2012-07-16 03:06:35 +02:00
< ? php
2012-07-16 08:21:48 +02:00
class DMSDocument extends DataObject implements DMSDocumentInterface {
2013-08-22 13:38:26 +02:00
private static $db = array (
2012-07-26 02:46:03 +02:00
" Filename " => " Varchar(255) " , // eg. 3469~2011-energysaving-report.pdf
" Folder " => " Varchar(255) " , // eg. 0
" Title " => 'Varchar(1024)' , // eg. "Energy Saving Report for Year 2011, New Zealand LandCorp"
2012-07-25 08:08:49 +02:00
" Description " => 'Text' ,
2012-08-30 01:12:45 +02:00
" ViewCount " => 'Int' ,
2012-08-13 04:14:31 +02:00
" LastChanged " => 'SS_DateTime' , //when this document's file was created or last replaced (small changes like updating title don't count)
2012-08-14 07:26:26 +02:00
" EmbargoedIndefinitely " => 'Boolean(false)' ,
2012-08-13 04:14:31 +02:00
" EmbargoedUntilPublished " => 'Boolean(false)' ,
" EmbargoedUntilDate " => 'SS_DateTime' ,
" ExpireAtDate " => 'SS_DateTime'
2012-07-16 08:21:48 +02:00
);
2012-07-16 03:06:35 +02:00
2013-08-22 13:38:26 +02:00
private static $many_many = array (
2012-07-25 05:11:06 +02:00
'Pages' => 'SiteTree' ,
'Tags' => 'DMSTag'
2012-07-17 07:58:33 +02:00
);
2012-08-21 22:57:40 +02:00
2013-08-22 13:38:26 +02:00
private static $many_many_extraFields = array (
2012-08-21 22:57:40 +02:00
'Pages' => array (
'DocumentSort' => 'Int'
),
);
2013-08-22 13:38:26 +02:00
private static $display_fields = array (
2012-07-27 05:38:42 +02:00
'ID' => 'ID' ,
'Title' => 'Title' ,
2012-08-01 00:51:41 +02:00
'FilenameWithoutID' => 'Filename' ,
2012-07-27 05:38:42 +02:00
'LastChanged' => 'LastChanged'
);
2012-07-17 07:58:33 +02:00
2013-08-22 13:38:26 +02:00
private static $singular_name = 'Document' ;
2012-08-28 00:16:12 +02:00
2013-08-22 13:38:26 +02:00
private static $plural_name = 'Documents' ;
2013-04-18 03:02:32 +02:00
public function canView ( $member = null ) {
2013-11-14 03:00:16 +01:00
$canView = false ;
2013-04-18 03:02:32 +02:00
if ( $member == null ) $member = Member :: currentUser ();
if ( $member -> ID ){
2013-11-14 03:00:16 +01:00
$canView = true ;
2013-04-18 03:02:32 +02:00
}
2013-11-14 03:00:16 +01:00
$this -> extend ( 'canView' , $canView );
return $canView ;
2013-04-18 03:02:32 +02:00
}
public function canEdit ( $member = null ) {
2013-11-14 03:00:16 +01:00
$canEdit = $this -> canView ();
$this -> extend ( 'canEdit' , $canEdit );
return $canEdit ;
2013-04-18 03:02:32 +02:00
}
public function canCreate ( $member = null ) {
2013-11-14 03:00:16 +01:00
$canCreate = $this -> canView ();
$this -> extend ( 'canCreate' , $canCreate );
return $canCreate ;
2013-04-18 03:02:32 +02:00
}
public function canDelete ( $member = null ) {
2013-11-14 03:00:16 +01:00
$canDelete = $this -> canView ();
$this -> extend ( 'canDelete' , $canDelete );
return $canDelete ;
2013-04-18 03:02:32 +02:00
}
2012-08-28 00:16:12 +02:00
2012-07-16 03:06:35 +02:00
/**
2012-07-16 05:32:28 +02:00
* Associates this document with a Page . This method does nothing if the association already exists .
2012-07-27 02:22:27 +02:00
* This could be a simple wrapper around $myDoc -> Pages () -> add ( $myPage ) to add a many_many relation
2012-07-16 05:32:28 +02:00
* @ param $pageObject Page object to associate this Document with
* @ return null
2012-07-16 03:06:35 +02:00
*/
2012-07-16 08:21:48 +02:00
function addPage ( $pageObject ) {
2012-07-17 07:58:33 +02:00
$this -> Pages () -> add ( $pageObject );
2012-09-21 06:10:16 +02:00
2013-03-11 15:20:55 +01:00
DB :: query ( " UPDATE \" DMSDocument_Pages \" SET \" DocumentSort \" = \" DocumentSort \" +1 WHERE \" SiteTreeID \" = $pageObject->ID " );
2012-07-16 08:21:48 +02:00
}
2012-07-27 02:22:27 +02:00
/**
* Associates this DMSDocument with a set of Pages . This method loops through a set of page ids , and then associates this
* DMSDocument with the individual Page with the each page id in the set
* @ abstract
* @ param $pageIDs array of page ids used for the page objects associate this DMSDocument with
* @ return null
*/
function addPages ( $pageIDs ){
foreach ( $pageIDs as $id ){
$pageObject = DataObject :: get_by_id ( " SiteTree " , $id );
if ( $pageObject && $pageObject -> exists ()) {
$this -> addPage ( $pageObject );
}
}
}
2012-07-16 05:32:28 +02:00
/**
* Removes the association between this Document and a Page . This method does nothing if the association does not exist .
* @ param $pageObject Page object to remove the association to
* @ return mixed
*/
2012-07-16 08:21:48 +02:00
function removePage ( $pageObject ) {
2012-07-17 07:58:33 +02:00
$this -> Pages () -> remove ( $pageObject );
2012-07-16 08:21:48 +02:00
}
2012-07-16 05:32:28 +02:00
2012-08-31 01:09:43 +02:00
function Pages () {
$pages = $this -> getManyManyComponents ( 'Pages' );
$this -> extend ( 'updatePages' , $pages );
return $pages ;
}
2012-07-16 05:32:28 +02:00
/**
* Returns a list of the Page objects associated with this Document
* @ return DataList
*/
2012-07-16 08:21:48 +02:00
function getPages () {
2012-08-31 01:09:43 +02:00
return $this -> Pages ();
2012-07-17 07:58:33 +02:00
}
/**
* Removes all associated Pages from the DMSDocument
* @ return null
*/
function removeAllPages () {
$this -> Pages () -> removeAll ();
2012-07-16 08:21:48 +02:00
}
2012-07-16 03:06:35 +02:00
2012-08-30 01:12:45 +02:00
/**
* increase ViewCount by 1 , without update any other record fields such as LastEdited
*/
2012-08-30 16:07:23 +02:00
function trackView (){
2012-08-30 01:12:45 +02:00
if ( $this -> ID > 0 ) {
$count = $this -> ViewCount + 1 ;
DB :: query ( " UPDATE \" DMSDocument \" SET \" ViewCount \" =' $count ' WHERE \" ID \" = { $this -> ID } " );
}
}
2012-07-25 08:08:49 +02:00
2012-07-16 03:06:35 +02:00
/**
2012-07-16 05:32:28 +02:00
* Adds a metadata tag to the Document . The tag has a category and a value .
* Each category can have multiple values by default . So : addTag ( " fruit " , " banana " ) addTag ( " fruit " , " apple " ) will add two items .
* However , if the third parameter $multiValue is set to 'false' , then all updates to a category only ever update a single value . So :
* addTag ( " fruit " , " banana " ) addTag ( " fruit " , " apple " ) would result in a single metadata tag : fruit -> apple .
* Can could be implemented as a key / value store table ( although it is more like category / value , because the
* same category can occur multiple times )
* @ param $category String of a metadata category to add ( required )
* @ param $value String of a metadata value to add ( required )
* @ param bool $multiValue Boolean that determines if the category is multi - value or single - value ( optional )
* @ return null
2012-07-16 03:06:35 +02:00
*/
2012-07-16 08:21:48 +02:00
function addTag ( $category , $value , $multiValue = true ) {
2012-07-17 07:58:33 +02:00
if ( $multiValue ) {
//check for a duplicate tag, don't add the duplicate
2012-07-25 08:56:43 +02:00
$currentTag = $this -> Tags () -> filter ( array ( 'Category' => $category , 'Value' => $value ));
if ( $currentTag -> Count () == 0 ) {
2012-07-17 07:58:33 +02:00
//multi value tag
$tag = new DMSTag ();
$tag -> Category = $category ;
$tag -> Value = $value ;
2012-07-27 01:26:30 +02:00
$tag -> MultiValue = true ;
2012-07-17 07:58:33 +02:00
$tag -> write ();
2012-07-25 08:56:43 +02:00
$tag -> Documents () -> add ( $this );
2012-07-27 01:26:30 +02:00
} else {
//add the relation between the tag and document
foreach ( $currentTag as $tagObj ) {
$tagObj -> Documents () -> add ( $this );
}
2012-07-17 07:58:33 +02:00
}
} else {
//single value tag
2012-07-25 08:56:43 +02:00
$currentTag = $this -> Tags () -> filter ( array ( 'Category' => $category ));
2012-07-27 01:26:30 +02:00
$tag = null ;
if ( $currentTag -> Count () == 0 ) {
2012-07-17 07:58:33 +02:00
//create the single-value tag
$tag = new DMSTag ();
$tag -> Category = $category ;
$tag -> Value = $value ;
2012-07-27 01:26:30 +02:00
$tag -> MultiValue = false ;
2012-07-17 07:58:33 +02:00
$tag -> write ();
} else {
//update the single value tag
$tag = $currentTag -> first ();
$tag -> Value = $value ;
2012-07-27 01:26:30 +02:00
$tag -> MultiValue = false ;
2012-07-17 07:58:33 +02:00
$tag -> write ();
}
2012-07-27 01:26:30 +02:00
//regardless of whether we created a new tag or are just updating an existing one, add the relation
$tag -> Documents () -> add ( $this );
2012-07-17 07:58:33 +02:00
}
2012-07-16 08:21:48 +02:00
}
2012-07-16 03:06:35 +02:00
2012-07-27 01:26:30 +02:00
protected function getTagsObjects ( $category , $value = null ) {
$valueFilter = array ( " Category " => $category );
if ( ! empty ( $value )) $valueFilter [ 'Value' ] = $value ;
$tags = $this -> Tags () -> filter ( $valueFilter );
return $tags ;
}
2012-07-25 08:56:43 +02:00
/**
* Fetches all tags associated with this DMSDocument within a given category . If a value is specified this method
* tries to fetch that specific tag .
* @ abstract
* @ param $category String of the metadata category to get
* @ param null $value String of the value of the tag to get
* @ return array of Strings of all the tags or null if there is no match found
*/
2012-07-30 02:33:01 +02:00
function getTagsList ( $category , $value = null ) {
2012-07-27 01:26:30 +02:00
$tags = $this -> getTagsObjects ( $category , $value );
2012-07-25 08:56:43 +02:00
//convert DataList into array of Values
$returnArray = null ;
2012-07-27 01:26:30 +02:00
if ( $tags -> Count () > 0 ) {
2012-07-25 08:56:43 +02:00
$returnArray = array ();
2012-07-27 01:26:30 +02:00
foreach ( $tags as $t ) {
2012-07-25 08:56:43 +02:00
$returnArray [] = $t -> Value ;
}
}
return $returnArray ;
}
2012-07-16 05:32:28 +02:00
/**
* Removes a tag from the Document . If you only set a category , then all values in that category are deleted .
* If you specify both a category and a value , then only that single category / value pair is deleted .
* Nothing happens if the category or the value do not exist .
* @ param $category Category to remove ( required )
* @ param null $value Value to remove ( optional )
* @ return null
*/
2012-07-16 08:21:48 +02:00
function removeTag ( $category , $value = null ) {
2012-07-27 01:26:30 +02:00
$tags = $this -> getTagsObjects ( $category , $value );
if ( $tags -> Count () > 0 ) {
foreach ( $tags as $t ) {
$documentList = $t -> Documents ();
//remove the relation between the tag and the document
$documentList -> remove ( $this );
//delete the entire tag if it has no relations left
2012-07-27 02:36:45 +02:00
if ( $documentList -> Count () == 0 ) $t -> delete ();
2012-07-27 01:26:30 +02:00
}
}
2012-07-16 08:21:48 +02:00
}
2012-07-16 05:32:28 +02:00
/**
* Deletes all tags associated with this Document .
* @ return null
*/
2012-07-16 08:21:48 +02:00
function removeAllTags () {
2012-07-27 01:26:30 +02:00
$allTags = $this -> Tags ();
foreach ( $allTags as $tag ) {
2012-07-27 02:36:45 +02:00
$documentlist = $tag -> Documents ();
$documentlist -> remove ( $this );
2012-07-27 01:26:30 +02:00
if ( $tag -> Documents () -> Count () == 0 ) $tag -> delete ();
}
2012-07-16 08:21:48 +02:00
}
2012-07-16 05:32:28 +02:00
/**
* Returns a multi - dimensional array containing all Tags associated with this Document . The array has the
* following structure :
* $twoDimensionalArray = new array (
2012-07-16 08:21:48 +02:00
* array ( 'fruit' , 'banana' ),
* array ( 'fruit' , 'apple' )
* );
2012-07-16 05:32:28 +02:00
* @ return array Multi - dimensional array of tags
*/
2012-07-16 08:21:48 +02:00
function getAllTags () {
// TODO: Implement getAllTags() method.
}
2012-07-16 03:06:35 +02:00
2012-07-16 05:32:28 +02:00
/**
* Returns a link to download this document from the DMS store
* @ return String
*/
2012-08-22 23:20:48 +02:00
function getLink () {
2012-07-27 07:38:20 +02:00
return Controller :: join_links ( Director :: baseURL (), 'dmsdocument/' . $this -> ID );
2012-07-16 08:21:48 +02:00
}
2012-07-16 03:06:35 +02:00
2012-09-07 00:53:47 +02:00
/**
2012-10-10 02:44:02 +02:00
* The dummy function for the { @ see : getLink ()}
2012-09-07 00:53:47 +02:00
* RSSFeed need the data object to have a function called Link ()
*/
function Link (){
return $this -> getLink ();
}
2012-07-16 05:32:28 +02:00
/**
* Hides the document , so it does not show up when getByPage ( $myPage ) is called
* ( without specifying the $showEmbargoed = true parameter ) . This is similar to expire , except that this method
* should be used to hide documents that have not yet gone live .
* @ return null
*/
2012-08-14 07:26:26 +02:00
function embargoIndefinitely ( $write = true ) {
$this -> EmbargoedIndefinitely = true ;
2012-08-13 08:28:15 +02:00
if ( $write ) $this -> write ();
2012-08-13 04:14:31 +02:00
}
/**
* Hides the document until any page it is linked to is published
* @ return null
*/
2012-08-13 08:28:15 +02:00
function embargoUntilPublished ( $write = true ) {
2012-08-13 04:14:31 +02:00
$this -> EmbargoedUntilPublished = true ;
2012-08-13 08:28:15 +02:00
if ( $write ) $this -> write ();
2012-08-13 04:14:31 +02:00
}
/**
* Returns if this is Document is embargoed or expired .
2012-08-15 03:18:29 +02:00
* Also , returns if the document should be displayed on the front - end . Respecting the current reading mode
* of the site and the embargo status .
* I . e . if a document is embargoed until published , then it should still show up in draft mode .
2012-08-13 04:14:31 +02:00
* @ return bool True or False depending on whether this document is embargoed
*/
function isHidden () {
2012-08-15 03:18:29 +02:00
$hidden = $this -> isEmbargoed () || $this -> isExpired ();
$readingMode = Versioned :: get_reading_mode ();
if ( $readingMode == " Stage.Stage " && $this -> EmbargoedUntilPublished == true ) $hidden = false ;
return $hidden ;
2012-07-16 08:21:48 +02:00
}
2012-07-16 05:32:28 +02:00
/**
* Returns if this is Document is embargoed .
* @ return bool True or False depending on whether this document is embargoed
*/
2012-07-16 08:21:48 +02:00
function isEmbargoed () {
2012-08-14 01:35:41 +02:00
if ( is_object ( $this -> EmbargoedUntilDate )) $this -> EmbargoedUntilDate = $this -> EmbargoedUntilDate -> Value ;
2012-08-13 04:14:31 +02:00
$embargoed = false ;
2012-08-14 07:26:26 +02:00
if ( $this -> EmbargoedIndefinitely ) $embargoed = true ;
2012-08-13 04:14:31 +02:00
elseif ( $this -> EmbargoedUntilPublished ) $embargoed = true ;
2012-08-14 01:35:41 +02:00
elseif ( ! empty ( $this -> EmbargoedUntilDate ) && SS_Datetime :: now () -> Value < $this -> EmbargoedUntilDate ) $embargoed = true ;
2012-08-13 04:14:31 +02:00
return $embargoed ;
2012-07-16 08:21:48 +02:00
}
2012-07-16 05:32:28 +02:00
/**
* Hides the document , so it does not show up when getByPage ( $myPage ) is called . Automatically un - hides the
* Document at a specific date .
* @ param $datetime String date time value when this Document should expire
* @ return null
*/
2012-08-13 08:28:15 +02:00
function embargoUntilDate ( $datetime , $write = true ) {
2012-08-30 16:05:56 +02:00
$this -> EmbargoedUntilDate = DBField :: create_field ( 'SS_Datetime' , $datetime ) -> Format ( 'Y-m-d H:i:s' );
2012-08-13 08:28:15 +02:00
if ( $write ) $this -> write ();
2012-07-16 08:21:48 +02:00
}
2012-07-16 05:32:28 +02:00
/**
* Clears any previously set embargos , so the Document always shows up in all queries .
* @ return null
*/
2012-08-13 08:28:15 +02:00
function clearEmbargo ( $write = true ) {
2012-08-14 07:26:26 +02:00
$this -> EmbargoedIndefinitely = false ;
2012-08-13 04:14:31 +02:00
$this -> EmbargoedUntilPublished = false ;
$this -> EmbargoedUntilDate = null ;
2012-08-13 08:28:15 +02:00
if ( $write ) $this -> write ();
2012-07-16 08:21:48 +02:00
}
2012-07-16 05:32:28 +02:00
/**
* Returns if this is Document is expired .
* @ return bool True or False depending on whether this document is expired
*/
2012-07-16 08:21:48 +02:00
function isExpired () {
2012-08-14 01:35:41 +02:00
if ( is_object ( $this -> ExpireAtDate )) $this -> ExpireAtDate = $this -> ExpireAtDate -> Value ;
2012-08-13 04:14:31 +02:00
$expired = false ;
2012-08-14 01:35:41 +02:00
if ( ! empty ( $this -> ExpireAtDate ) && SS_Datetime :: now () -> Value >= $this -> ExpireAtDate ) $expired = true ;
2012-08-13 04:14:31 +02:00
return $expired ;
2012-07-16 08:21:48 +02:00
}
2012-07-16 05:32:28 +02:00
/**
* Hides the document at a specific date , so it does not show up when getByPage ( $myPage ) is called .
* @ param $datetime String date time value when this Document should expire
* @ return null
*/
2012-08-13 08:28:15 +02:00
function expireAtDate ( $datetime , $write = true ) {
2012-08-30 16:05:56 +02:00
$this -> ExpireAtDate = DBField :: create_field ( 'SS_Datetime' , $datetime ) -> Format ( 'Y-m-d H:i:s' );
2012-08-13 08:28:15 +02:00
if ( $write ) $this -> write ();
2012-07-16 08:21:48 +02:00
}
2012-07-16 05:32:28 +02:00
/**
* Clears any previously set expiry .
* @ return null
*/
2012-08-13 08:28:15 +02:00
function clearExpiry ( $write = true ) {
2012-08-13 04:14:31 +02:00
$this -> ExpireAtDate = null ;
2012-08-13 08:28:15 +02:00
if ( $write ) $this -> write ();
2012-07-16 08:21:48 +02:00
}
2012-07-16 05:32:28 +02:00
/**
* Returns a DataList of all previous Versions of this document ( check the LastEdited date of each
* object to find the correct one )
* @ return DataList List of Document objects
*/
2012-07-16 08:21:48 +02:00
function getVersions () {
2012-11-21 06:24:46 +01:00
if ( ! DMSDocument_versions :: $enable_versions ) user_error ( " DMSDocument versions are disabled " , E_USER_WARNING );
return DMSDocument_versions :: get_versions ( $this );
2012-07-16 08:21:48 +02:00
}
2012-07-16 05:32:28 +02:00
2012-07-17 03:52:46 +02:00
/**
* Returns the full filename of the document stored in this object
* @ return string
*/
function getFullPath () {
2013-01-07 17:31:04 +01:00
if ( $this -> Filename ) {
return DMS :: get_dms_path () . DIRECTORY_SEPARATOR . $this -> Folder . DIRECTORY_SEPARATOR . $this -> Filename ;
} else {
return false ;
}
2012-07-17 03:52:46 +02:00
}
2012-07-17 07:58:33 +02:00
/**
* Deletes the DMSDocument , its underlying file , as well as any tags related to this DMSDocument . Also calls the
* parent DataObject ' s delete method .
*/
function delete () {
//remove tags
$this -> removeAllTags ();
2012-08-01 05:00:43 +02:00
//delete the file (and previous versions of files)
$filesToDelete = array ();
2012-08-07 01:14:23 +02:00
$storageFolder = DMS :: get_dms_path () . DIRECTORY_SEPARATOR . DMS :: get_storage_folder ( $this -> ID );
2012-08-13 04:14:31 +02:00
if ( file_exists ( $storageFolder )) {
if ( $handle = opendir ( $storageFolder )) { //Open directory
//List files in the directory
2012-11-21 05:15:23 +01:00
while ( false !== ( $entry = readdir ( $handle ))) { //only delete if filename starts the the relevant ID
if ( strpos ( $entry , $this -> ID . '~' ) === 0 ) $filesToDelete [] = $entry ;
2012-08-13 04:14:31 +02:00
}
closedir ( $handle );
2012-08-01 05:00:43 +02:00
2012-08-13 04:14:31 +02:00
//delete all this files that have the id of this document
foreach ( $filesToDelete as $file ) {
$filePath = $storageFolder . DIRECTORY_SEPARATOR . $file ;
if ( is_file ( $filePath )) unlink ( $filePath );
}
2012-08-01 05:00:43 +02:00
}
}
2012-07-17 07:58:33 +02:00
$this -> removeAllPages ();
2012-11-21 06:24:46 +01:00
//get rid of any versions have saved for this DMSDocument, too
if ( DMSDocument_versions :: $enable_versions ) {
$versions = $this -> getVersions ();
if ( $versions -> Count () > 0 ) {
foreach ( $versions as $v ) {
$v -> delete ();
}
}
}
2012-07-17 07:58:33 +02:00
//delete the dataobject
parent :: delete ();
}
2012-11-21 06:24:46 +01:00
2012-08-06 15:46:05 +02:00
/**
* Relate an existing file on the filesystem to the document .
* Copies the file to the new destination , as defined in { @ link get_DMS_path ()} .
*
* @ param String Path to file , relative to webroot .
*/
2012-07-27 04:02:46 +02:00
function storeDocument ( $filePath ) {
if ( empty ( $this -> ID )) user_error ( " Document must be written to database before it can store documents " , E_USER_ERROR );
//calculate all the path to copy the file to
$fromFilename = basename ( $filePath );
$toFilename = $this -> ID . '~' . $fromFilename ; //add the docID to the start of the Filename
2012-08-07 01:14:23 +02:00
$toFolder = DMS :: get_storage_folder ( $this -> ID );
$toPath = DMS :: get_dms_path () . DIRECTORY_SEPARATOR . $toFolder . DIRECTORY_SEPARATOR . $toFilename ;
DMS :: create_storage_folder ( DMS :: get_dms_path () . DIRECTORY_SEPARATOR . $toFolder );
2012-07-27 04:02:46 +02:00
//copy the file into place
$fromPath = BASE_PATH . DIRECTORY_SEPARATOR . $filePath ;
2012-11-21 06:24:46 +01:00
//version the existing file (copy it to a new "very specific" filename
if ( DMSDocument_versions :: $enable_versions ) {
DMSDocument_versions :: create_version ( $this );
2012-11-22 01:47:59 +01:00
} else { //otherwise delete the old document file
$oldPath = $this -> getFullPath ();
2013-01-07 17:31:04 +01:00
if ( file_exists ( $oldPath )) unlink ( $oldPath );
2012-11-21 06:24:46 +01:00
}
2012-07-27 04:02:46 +02:00
copy ( $fromPath , $toPath ); //this will overwrite the existing file (if present)
//write the filename of the stored document
$this -> Filename = $toFilename ;
$this -> Folder = $toFolder ;
2012-09-20 05:28:44 +02:00
$extension = pathinfo ( $this -> Filename , PATHINFO_EXTENSION );
if ( empty ( $this -> Title )) $this -> Title = basename ( $filePath , '.' . $extension ); //don't overwrite existing document titles
2012-07-27 04:02:46 +02:00
$this -> LastChanged = SS_Datetime :: now () -> Rfc2822 ();
$this -> write ();
return $this ;
}
2012-07-27 01:26:30 +02:00
/**
* Takes a File object or a String ( path to a file ) and copies it into the DMS , replacing the original document file
* but keeping the rest of the document unchanged .
* @ param $file File object , or String that is path to a file to store
* @ return DMSDocumentInstance Document object that we replaced the file in
*/
function replaceDocument ( $file ) {
2012-08-07 01:14:23 +02:00
$filePath = DMS :: transform_file_to_file_path ( $file );
2012-07-27 04:02:46 +02:00
$doc = $this -> storeDocument ( $filePath ); //replace the document
return $doc ;
2012-07-27 01:26:30 +02:00
}
2012-07-30 08:31:23 +02:00
/**
* Return the type of file for the given extension
* on the current file name .
*
* @ return string
*/
static function get_file_type ( $ext ) {
$types = array (
'gif' => 'GIF image - good for diagrams' ,
'jpg' => 'JPEG image - good for photos' ,
'jpeg' => 'JPEG image - good for photos' ,
'png' => 'PNG image - good general-purpose format' ,
'ico' => 'Icon image' ,
'tiff' => 'Tagged image format' ,
'doc' => 'Word document' ,
'xls' => 'Excel spreadsheet' ,
'zip' => 'ZIP compressed file' ,
'gz' => 'GZIP compressed file' ,
'dmg' => 'Apple disk image' ,
'pdf' => 'Adobe Acrobat PDF file' ,
'mp3' => 'MP3 audio file' ,
'wav' => 'WAV audo file' ,
'avi' => 'AVI video file' ,
'mpg' => 'MPEG video file' ,
'mpeg' => 'MPEG video file' ,
'js' => 'Javascript file' ,
'css' => 'CSS file' ,
'html' => 'HTML file' ,
'htm' => 'HTML file'
);
return isset ( $types [ $ext ]) ? $types [ $ext ] : $ext ;
}
2012-08-01 00:51:41 +02:00
function getFilenameWithoutID () {
2012-07-30 08:31:23 +02:00
$filenameParts = explode ( '~' , $this -> Filename );
$filename = array_pop ( $filenameParts );
return $filename ;
}
2012-11-12 22:42:43 +01:00
/**
* Returns the Description field with HTML < br > tags added when there is a line break
* @ return string Description
*/
2012-11-12 22:51:42 +01:00
function getDescriptionWithLineBreak () {
2012-11-12 22:42:43 +01:00
return nl2br ( $this -> getField ( 'Description' ));
}
2012-07-30 08:31:23 +02:00
2012-07-27 07:38:20 +02:00
function getCMSFields () {
2012-07-31 08:16:54 +02:00
//include JS to handling showing and hiding of bottom "action" tabs
2012-11-20 22:22:40 +01:00
Requirements :: javascript ( DMS_DIR . '/javascript/DMSDocumentCMSFields.js' );
Requirements :: css ( DMS_DIR . '/css/DMSDocumentCMSFields.css' );
2012-07-30 08:31:23 +02:00
2012-07-31 08:16:54 +02:00
$fields = new FieldList (); //don't use the automatic scaffolding, it is slow and unnecessary here
2012-07-30 08:31:23 +02:00
2012-11-21 06:24:46 +01:00
$extraTasks = '' ; //additional text to inject into the list of tasks at the bottom of a DMSDocument CMSfield
$extraFields = FormField :: create ( 'Empty' );
2012-08-21 04:46:01 +02:00
//get list of shortcode page relations
$relationFinder = new ShortCodeRelationFinder ();
$relationList = $relationFinder -> getList ( $this -> ID );
$fieldsTop = $this -> getFieldsForFile ( $relationList -> count ());
2012-07-31 08:16:54 +02:00
$fields -> add ( $fieldsTop );
$fields -> add ( new TextField ( 'Title' , 'Title' ));
$fields -> add ( new TextareaField ( 'Description' , 'Description' ));
2012-07-30 08:31:23 +02:00
2012-07-31 04:24:35 +02:00
//create upload field to replace document
2012-08-06 20:53:34 +02:00
$uploadField = new DMSUploadField ( 'ReplaceFile' , 'Replace file' );
$uploadField -> setConfig ( 'allowedMaxFileNumber' , 1 );
2013-01-02 17:28:25 +01:00
$uploadField -> setConfig ( 'downloadTemplateName' , 'ss-dmsuploadfield-downloadtemplate' );
2012-08-06 20:53:34 +02:00
$uploadField -> setRecord ( $this );
2012-07-31 08:16:54 +02:00
$gridFieldConfig = GridFieldConfig :: create () -> addComponents (
2012-08-06 15:46:05 +02:00
new GridFieldToolbarHeader (),
2012-07-31 08:16:54 +02:00
new GridFieldSortableHeader (),
new GridFieldDataColumns (),
new GridFieldPaginator ( 30 ),
2012-08-03 02:18:55 +02:00
//new GridFieldEditButton(),
2012-07-31 08:16:54 +02:00
new GridFieldDetailForm ()
);
2012-08-06 15:46:05 +02:00
$gridFieldConfig -> getComponentByType ( 'GridFieldDataColumns' )
-> setDisplayFields ( array (
'Title' => 'Title' ,
'ClassName' => 'Page Type' ,
'ID' => 'Page ID'
))
-> setFieldFormatting ( array (
'Title' => sprintf (
'<a class=\"cms-panel-link\" href=\"%s/$ID\">$Title</a>' ,
singleton ( 'CMSPageEditController' ) -> Link ( 'show' )
)
));
2012-07-31 08:16:54 +02:00
$pagesGrid = GridField :: create (
'Pages' ,
2012-08-06 15:46:05 +02:00
_t ( 'DMSDocument.RelatedPages' , 'Related Pages' ),
2012-07-31 08:16:54 +02:00
$this -> Pages (),
$gridFieldConfig
);
2012-08-21 04:21:23 +02:00
$referencesGrid = GridField :: create (
'References' ,
_t ( 'DMSDocument.RelatedReferences' , 'Related References' ),
2012-08-21 04:46:01 +02:00
$relationList ,
2012-08-21 04:21:23 +02:00
$gridFieldConfig
);
2012-11-21 06:24:46 +01:00
if ( DMSDocument_versions :: $enable_versions ) {
$versionsGridFieldConfig = GridFieldConfig :: create () -> addComponents (
new GridFieldToolbarHeader (),
new GridFieldSortableHeader (),
new GridFieldDataColumns (),
new GridFieldPaginator ( 30 )
);
$versionsGridFieldConfig -> getComponentByType ( 'GridFieldDataColumns' ) -> setDisplayFields ( Config :: inst () -> get ( 'DMSDocument_versions' , 'display_fields' ))
-> setFieldCasting ( array ( 'LastChanged' => " Datetime->Ago " ))
-> setFieldFormatting ( array ( 'FilenameWithoutID' => '<a target=\'_blank\' class=\'file-url\' href=\'$Link\'>$FilenameWithoutID</a>' ));
$versionsGrid = GridField :: create (
'Versions' ,
_t ( 'DMSDocument.Versions' , 'Versions' ),
$this -> getVersions (),
$versionsGridFieldConfig
);
$extraTasks .= '<li class="ss-ui-button" data-panel="find-versions">Versions</li>' ;
$extraFields = $versionsGrid -> addExtraClass ( 'find-versions' );
}
2012-08-13 06:41:43 +02:00
$fields -> add ( new LiteralField ( 'BottomTaskSelection' ,
'<div id="Actions" class="field actions"><label class="left">Actions</label><ul>' .
'<li class="ss-ui-button" data-panel="embargo">Embargo</li>' .
2012-08-13 08:28:15 +02:00
'<li class="ss-ui-button" data-panel="expiry">Expiry</li>' .
'<li class="ss-ui-button" data-panel="replace">Replace</li>' .
2012-11-21 06:24:46 +01:00
'<li class="ss-ui-button" data-panel="find-usage">Usage</li>' .
'<li class="ss-ui-button" data-panel="find-references">References</li>' .
$extraTasks .
2012-08-13 06:41:43 +02:00
'</ul></div>' ));
2012-08-13 08:28:15 +02:00
2012-08-14 05:05:23 +02:00
$embargoValue = 'None' ;
2012-08-14 07:26:26 +02:00
if ( $this -> EmbargoedIndefinitely ) $embargoValue = 'Indefinitely' ;
2012-08-14 05:05:23 +02:00
elseif ( $this -> EmbargoedUntilPublished ) $embargoValue = 'Published' ;
elseif ( ! empty ( $this -> EmbargoedUntilDate )) $embargoValue = 'Date' ;
2012-08-14 07:26:26 +02:00
$embargo = new OptionsetField ( 'Embargo' , 'Embargo' , array ( 'None' => 'None' , 'Published' => 'Hide document until page is published' , 'Indefinitely' => 'Hide document indefinitely' , 'Date' => 'Hide until set date' ), $embargoValue );
2012-08-14 00:03:31 +02:00
$embargoDatetime = DatetimeField :: create ( 'EmbargoedUntilDate' , '' );
2012-08-16 06:18:41 +02:00
$embargoDatetime -> getDateField () -> setConfig ( 'showcalendar' , true ) -> setConfig ( 'dateformat' , 'dd-MM-yyyy' ) -> setConfig ( 'datavalueformat' , 'dd-MM-yyyy' );
2012-08-13 08:28:15 +02:00
2012-08-14 05:05:23 +02:00
$expiryValue = 'None' ;
if ( ! empty ( $this -> ExpireAtDate )) $expiryValue = 'Date' ;
$expiry = new OptionsetField ( 'Expiry' , 'Expiry' , array ( 'None' => 'None' , 'Date' => 'Set document to expire on' ), $expiryValue );
2012-08-14 00:03:31 +02:00
$expiryDatetime = DatetimeField :: create ( 'ExpireAtDate' , '' );
2012-08-16 06:18:41 +02:00
$expiryDatetime -> getDateField () -> setConfig ( 'showcalendar' , true ) -> setConfig ( 'dateformat' , 'dd-MM-yyyy' ) -> setConfig ( 'datavalueformat' , 'dd-MM-yyyy' );
2012-08-08 02:17:20 +02:00
// This adds all the actions details into a group.
// Embargo, History, etc to go in here
// These are toggled on and off via the Actions Buttons above
2012-08-13 08:28:15 +02:00
$fields -> add ( FieldGroup :: create (
FieldGroup :: create (
$embargo ,
$embargoDatetime
) -> addExtraClass ( 'embargo' ),
FieldGroup :: create (
$expiry ,
$expiryDatetime
) -> addExtraClass ( 'expiry' ),
2012-08-13 06:41:43 +02:00
$uploadField -> addExtraClass ( 'replace' ),
2012-08-21 04:21:23 +02:00
$pagesGrid -> addExtraClass ( 'find-usage' ),
2012-11-21 06:24:46 +01:00
$referencesGrid -> addExtraClass ( 'find-references' ),
$extraFields
2012-08-13 08:28:15 +02:00
) -> setName ( " ActionsPanel " ) -> addExtraClass ( 'dmsupload ss-uploadfield' ));
2012-08-21 01:31:53 +02:00
$this -> extend ( 'updateCMSFields' , $fields );
2012-07-30 08:31:23 +02:00
return $fields ;
}
2012-08-13 08:28:15 +02:00
function onBeforeWrite () {
parent :: onBeforeWrite ();
2012-08-14 01:35:41 +02:00
if ( isset ( $this -> Embargo )) {
//set the embargo options from the OptionSetField created in the getCMSFields method
//do not write after clearing the embargo (write happens automatically)
2012-08-14 05:05:23 +02:00
$savedDate = $this -> EmbargoedUntilDate ;
$this -> clearEmbargo ( false ); //clear all previous settings and re-apply them on save
2012-08-13 08:28:15 +02:00
2012-08-14 05:05:23 +02:00
if ( $this -> Embargo == 'Published' ) $this -> embargoUntilPublished ( false );
2012-08-14 07:26:26 +02:00
if ( $this -> Embargo == 'Indefinitely' ) $this -> embargoIndefinitely ( false );
2012-08-14 05:05:23 +02:00
if ( $this -> Embargo == 'Date' ) $this -> embargoUntilDate ( $savedDate , false );
2012-08-14 01:35:41 +02:00
}
2012-08-14 01:44:12 +02:00
if ( isset ( $this -> Expiry )) {
2012-08-14 05:05:23 +02:00
if ( $this -> Expiry == 'Date' ) $this -> expireAtDate ( $this -> ExpireAtDate , false );
2012-08-14 01:35:41 +02:00
else $this -> clearExpiry ( false ); //clear all previous settings
}
2012-08-13 08:28:15 +02:00
}
2012-07-30 08:31:23 +02:00
/**
* Return the relative URL of an icon for the file type ,
* based on the { @ link appCategory ()} value .
2012-08-03 02:18:55 +02:00
* Images are searched for in " dms/images/app_icons/ " .
2012-07-30 08:31:23 +02:00
*
* @ return String
*/
function Icon ( $ext ) {
2012-11-20 22:22:40 +01:00
if ( ! Director :: fileExists ( DMS_DIR . " /images/app_icons/ { $ext } _32.png " )) {
2012-07-30 08:31:23 +02:00
$ext = File :: get_app_category ( $ext );
}
2012-11-20 22:22:40 +01:00
if ( ! Director :: fileExists ( DMS_DIR . " /images/app_icons/ { $ext } _32.png " )) {
2012-07-30 08:31:23 +02:00
$ext = " generic " ;
}
2012-11-20 22:22:40 +01:00
return DMS_DIR . " /images/app_icons/ { $ext } _32.png " ;
2012-07-30 08:31:23 +02:00
}
2012-08-01 04:13:07 +02:00
/**
* Return the extension of the file associated with the document
*/
2012-08-22 23:20:48 +02:00
function getExtension () {
2012-08-01 04:42:31 +02:00
return strtolower ( pathinfo ( $this -> Filename , PATHINFO_EXTENSION ));
}
2012-08-22 23:20:48 +02:00
function getSize () {
$size = $this -> getAbsoluteSize ();
return ( $size ) ? File :: format_size ( $size ) : false ;
}
2012-08-01 04:42:31 +02:00
/**
* Return the size of the file associated with the document
*/
2012-08-22 23:20:48 +02:00
function getAbsoluteSize () {
2012-10-15 01:01:44 +02:00
return file_exists ( $this -> getFullPath ()) ? filesize ( $this -> getFullPath ()) : null ;
2012-08-01 04:42:31 +02:00
}
2012-09-03 07:25:21 +02:00
/**
* An alias to DMSDocument :: getSize ()
*/
2012-08-01 04:42:31 +02:00
function getFileSizeFormatted (){
2012-09-03 07:25:21 +02:00
return $this -> getSize ();
2012-08-01 04:13:07 +02:00
}
2012-07-30 08:31:23 +02:00
/**
* @ return FieldList
*/
2012-08-21 04:46:01 +02:00
protected function getFieldsForFile ( $relationListCount ) {
2012-08-27 05:07:01 +02:00
$extension = $this -> getExtension ();
2012-07-30 08:31:23 +02:00
$previewField = new LiteralField ( " ImageFull " ,
" <img id='thumbnailImage' class='thumbnail-preview' src=' { $this -> Icon ( $extension ) } ?r= " . rand ( 1 , 100000 ) . " ' alt=' { $this -> Title } ' /> \n "
);
//count the number of pages this document is published on
$publishedOnCount = $this -> Pages () -> Count ();
$publishedOnValue = " $publishedOnCount pages " ;
if ( $publishedOnCount == 1 ) $publishedOnValue = " $publishedOnCount page " ;
2012-08-21 04:46:01 +02:00
$relationListCountValue = " $relationListCount pages " ;
if ( $relationListCount == 1 ) $relationListCountValue = " $relationListCount page " ;
2012-07-30 08:31:23 +02:00
$fields = new FieldGroup (
$filePreview = CompositeField :: create (
CompositeField :: create (
$previewField
) -> setName ( " FilePreviewImage " ) -> addExtraClass ( 'cms-file-info-preview' ),
CompositeField :: create (
CompositeField :: create (
new ReadonlyField ( " ID " , " ID number " . ':' , $this -> ID ),
new ReadonlyField ( " FileType " , _t ( 'AssetTableField.TYPE' , 'File type' ) . ':' , self :: get_file_type ( $extension )),
2012-08-01 07:59:22 +02:00
new ReadonlyField ( " Size " , _t ( 'AssetTableField.SIZE' , 'File size' ) . ':' , $this -> getFileSizeFormatted ()),
2012-07-30 08:31:23 +02:00
$urlField = new ReadonlyField ( 'ClickableURL' , _t ( 'AssetTableField.URL' , 'URL' ),
2012-08-27 05:07:01 +02:00
sprintf ( '<a href="%s" target="_blank" class="file-url">%s</a>' , $this -> getLink (), $this -> getLink ())
2012-07-30 08:31:23 +02:00
),
2012-08-01 00:51:41 +02:00
new ReadonlyField ( " FilenameWithoutIDField " , " Filename " . ':' , $this -> getFilenameWithoutID ()),
2012-07-30 08:31:23 +02:00
new DateField_Disabled ( " Created " , _t ( 'AssetTableField.CREATED' , 'First uploaded' ) . ':' , $this -> Created ),
new DateField_Disabled ( " LastEdited " , _t ( 'AssetTableField.LASTEDIT' , 'Last changed' ) . ':' , $this -> LastEdited ),
new DateField_Disabled ( " LastChanged " , _t ( 'AssetTableField.LASTCHANGED' , 'Last replaced' ) . ':' , $this -> LastChanged ),
2012-08-21 04:46:01 +02:00
new ReadonlyField ( " PublishedOn " , " Published on " . ':' , $publishedOnValue ),
2012-11-22 03:48:33 +01:00
new ReadonlyField ( " ReferencedOn " , " Referenced on " . ':' , $relationListCountValue ),
new ReadonlyField ( " ViewCount " , " View count " . ':' , $this -> ViewCount )
2012-07-30 08:31:23 +02:00
)
) -> setName ( " FilePreviewData " ) -> addExtraClass ( 'cms-file-info-data' )
) -> setName ( " FilePreview " ) -> addExtraClass ( 'cms-file-info' )
);
2012-07-31 08:16:54 +02:00
$fields -> setName ( 'FileP' );
2012-07-30 08:31:23 +02:00
$urlField -> dontEscape = true ;
2012-07-27 07:38:20 +02:00
return $fields ;
}
2012-07-31 04:26:29 +02:00
/**
2012-08-01 03:43:46 +02:00
* Takes a file and adds it to the DMSDocument storage , replacing the current file .
2012-07-31 04:26:29 +02:00
* @ param $file File to ingest
*/
function ingestFile ( $file ) {
2012-07-31 04:44:00 +02:00
$this -> replaceDocument ( $file );
$file -> delete ();
2012-07-31 04:26:29 +02:00
}
2012-08-14 08:36:52 +02:00
2012-07-27 07:38:20 +02:00
}
2013-10-11 12:58:13 +02:00
class DMSDocument_Controller extends Controller {
2012-08-07 08:19:58 +02:00
2012-08-13 04:14:31 +02:00
static $testMode = false ; //mode to switch for testing. Does not return document download, just document URL
2013-10-03 08:29:21 +02:00
private static $allowed_actions = array (
2012-07-27 07:38:20 +02:00
'index'
);
2013-10-11 12:58:13 +02:00
public function init () {
Versioned :: choose_site_stage ();
parent :: init ();
}
2012-08-07 08:19:58 +02:00
/**
* Returns the document object from the request object ' s ID parameter .
* Returns null , if no document found
*/
protected function getDocumentFromID ( $request ) {
2012-08-13 04:14:31 +02:00
$doc = null ;
2012-11-21 06:24:46 +01:00
$id = Convert :: raw2sql ( $request -> param ( 'ID' ));
if ( strpos ( $id , 'version' ) === 0 ) { //versioned document
$id = str_replace ( 'version' , '' , $id );
$doc = DataObject :: get_by_id ( 'DMSDocument_versions' , $id );
$this -> extend ( 'updateVersionFromID' , $doc , $request );
} else { //normal document
$doc = DataObject :: get_by_id ( 'DMSDocument' , $id );
$this -> extend ( 'updateDocumentFromID' , $doc , $request );
}
2012-08-13 04:14:31 +02:00
return $doc ;
2012-08-07 08:19:58 +02:00
}
2012-07-27 07:38:20 +02:00
/**
* Access the file download without redirecting user , so we can block direct access to documents .
*/
function index ( SS_HTTPRequest $request ) {
2012-08-07 08:19:58 +02:00
$doc = $this -> getDocumentFromID ( $request );
2012-07-27 07:38:20 +02:00
if ( ! empty ( $doc )) {
$canView = false ;
//Runs through all pages that this page links to and sets canView to true if the user can view ONE of these pages
2012-11-21 06:24:46 +01:00
if ( method_exists ( $doc , 'Pages' )) {
$pages = $doc -> Pages ();
if ( $pages -> Count () > 0 ) {
foreach ( $pages as $page ) {
if ( $page -> CanView ()) {
$canView = true ; //just one canView is enough to know that we can view the file
break ;
}
2012-07-27 07:38:20 +02:00
}
2012-11-21 06:24:46 +01:00
} else {
//if the document isn't on any page, then allow viewing of the document (because there is no canView() to consult)
$canView = true ;
2012-07-27 07:38:20 +02:00
}
}
2012-08-13 04:14:31 +02:00
// check for embargo or expiry
if ( $doc -> isHidden ()) $canView = false ;
2012-07-27 07:38:20 +02:00
2012-11-21 06:24:46 +01:00
//admins can always download any document, even if otherwise hidden
$member = Member :: currentUser ();
if ( $member && Permission :: checkMember ( $member , 'ADMIN' )) $canView = true ;
2012-07-27 07:38:20 +02:00
if ( $canView ) {
$path = $doc -> getFullPath ();
if ( is_file ( $path ) ) {
$fileBin = trim ( `whereis file` );
if ( function_exists ( 'finfo_file' ) ) {
// discover the mime type properly
$finfo = finfo_open ( FILEINFO_MIME_TYPE );
$mime = finfo_file ( $finfo , $path );
}
else if ( is_executable ( $fileBin ) ) {
// try to use the system tool
$mime = `$fileBin -i -b $path` ;
$mime = explode ( ';' , $mime );
$mime = trim ( $mime [ 0 ]);
}
else {
// make do with what we have
2012-08-27 05:07:01 +02:00
$ext = $doc -> getExtension ();
2012-07-27 07:38:20 +02:00
if ( $ext == 'pdf' ) {
$mime = 'application/pdf' ;
} elseif ( $ext == 'html' || $ext == 'htm' ) {
$mime = 'text/html' ;
} else {
$mime = 'application/octet-stream' ;
}
}
2012-08-13 04:14:31 +02:00
if ( self :: $testMode ) return $path ;
2012-10-10 02:44:02 +02:00
//if a DMSDocument can be downloaded and all the permissions/privileges has passed,
2012-08-30 01:12:45 +02:00
//its ViewCount should be increased by 1 just before the browser sending the file to front.
2012-08-30 16:07:23 +02:00
$doc -> trackView ();
2012-08-30 01:12:45 +02:00
2012-07-27 07:38:20 +02:00
header ( 'Content-Type: ' . $mime );
header ( 'Content-Length: ' . filesize ( $path ), null );
2012-08-01 03:23:20 +02:00
if ( ! empty ( $mime ) && $mime != " text/html " ) header ( 'Content-Disposition: attachment; filename="' . $doc -> getFilenameWithoutID () . '"' );
2012-07-27 07:38:20 +02:00
header ( 'Content-transfer-encoding: 8bit' );
header ( 'Expires: 0' );
header ( 'Pragma: cache' );
header ( 'Cache-Control: private' );
flush ();
readfile ( $path );
exit ;
}
}
}
2012-08-13 04:14:31 +02:00
if ( self :: $testMode ) return 'This asset does not exist.' ;
2012-07-27 07:38:20 +02:00
$this -> httpError ( 404 , 'This asset does not exist.' );
}
2012-07-30 02:33:01 +02:00
2012-07-27 07:38:20 +02:00
}