API Add document sets, remove *Page methods from DMSDocument

* Add 2.0.0 changelog
* Update DMSInterface and DMSDocumentInterface removing *page and adding getDocumentSetsByPage to DMSInterface
* Update use documentation and update unit tests

This commit changes the relationship from Pages has_many Documents to Pages has_many DocumentSets which are many_many to Documents. The upload field has been upated to attach documents to a set instead of a page, the tests updated and the DMSInterface and DMSDocumentInterface updated to be less relevant to pages and more relevant to document sets.
This commit is contained in:
Robbie Averill 2017-05-02 14:49:41 +12:00
parent 9c5693dab0
commit e4bc553521
36 changed files with 750 additions and 681 deletions

5
CONTRIBUTING.md Normal file
View File

@ -0,0 +1,5 @@
# Contributing
Any open source product is only as good as the community behind it. You can participate by sharing code, ideas, or simply helping others. No matter what your skill level is, every contribution counts.
See our [high level overview](http://silverstripe.org/contributing-to-silverstripe) on silverstripe.org on how you can help out.

View File

@ -2,14 +2,10 @@
$config = Config::inst();
DMSSiteTreeExtension::show_documents_tab(); //show the Documents tab on all pages
DMSSiteTreeExtension::no_documents_tab(); //and don't exclude it from any pages
DMSDocumentAddController::add_allowed_extensions(); //add an array of additional allowed extensions
define('DMS_DIR', 'dms');
define('DMS_DIR', basename(__DIR__));
if (!file_exists(BASE_PATH . DIRECTORY_SEPARATOR . DMS_DIR)) {
user_error("DMS directory named incorrectly. Please install the DMS module into a folder named: ".DMS_DIR);
user_error('DMS directory named incorrectly. Please install the DMS module into a folder named: ' . DMS_DIR);
}
CMSMenu::remove_menu_item('DMSDocumentAddController');

View File

@ -5,11 +5,16 @@ After: framework/routes#coreroutes
Director:
rules:
'dmsdocument/$ID' : 'DMSDocument_Controller'
SiteTree:
extensions:
- DMSSiteTreeExtension
# Whether to show the document sets tab in the CMS for the page type this extension is applied to
documents_enabled: true
HtmlEditorField_Toolbar:
extensions:
- DocumentHtmlEditorFieldToolbar
DMSDocument_versions:
enable_versions: true

View File

@ -126,15 +126,25 @@ class DMS implements DMSInterface
// TODO: Implement getByFullTextSearch() method.
}
/**
* Returns a list of Document objects associated with a Page
* @param $page SiteTree to fetch the associated Documents from
* @param bool $showEmbargoed Boolean that specifies if embargoed documents should be included in results
* @return DataList Document list associated with the Page
*/
public function getByPage($page, $showEmbargoed = false)
public function getByPage(SiteTree $page, $showEmbargoed = false)
{
// TODO: Implement getByPage() method.
/** @var ArrayList $documents */
$documents = $page->getAllDocuments();
if (!$showEmbargoed) {
foreach ($documents as $document) {
if ($document->isEmbargoed()) {
$documents->remove($document);
}
}
}
return $documents;
}
public function getDocumentSetsByPage(SiteTree $page)
{
return $page->getDocumentSets();
}
/**

View File

@ -5,7 +5,6 @@
*/
class DMSDocumentAddController extends LeftAndMain
{
private static $url_segment = 'pages/adddocument';
private static $url_priority = 60;
private static $required_permission_codes = 'CMS_ACCESS_AssetAdmin';
@ -13,7 +12,13 @@ class DMSDocumentAddController extends LeftAndMain
private static $tree_class = 'SiteTree';
private static $session_namespace = 'CMSMain';
public static $allowed_extensions = array();
/**
* Allowed file upload extensions, will be merged with `$allowed_extensions` from {@link File}
*
* @config
* @var array
*/
private static $allowed_extensions = array();
private static $allowed_actions = array(
'getEditForm',
@ -22,27 +27,10 @@ class DMSDocumentAddController extends LeftAndMain
'documentlist'
);
/**
* Add an array of additional allowed extensions
* @static
* @param $array
*/
public static function add_allowed_extensions($array = null)
{
if (empty($array)) {
return;
}
if (is_array($array)) {
self::$allowed_extensions = $array;
} else {
self::$allowed_extensions = array($array);
}
}
/**
* Custom currentPage() method to handle opening the 'root' folder
*
* @return
* @return SiteTree
*/
public function currentPage()
{
@ -61,12 +49,27 @@ class DMSDocumentAddController extends LeftAndMain
/**
* Return fake-ID "root" if no ID is found (needed to upload files into the root-folder)
*
* @return int
*/
public function currentPageID()
{
return ($result = parent::currentPageID()) === null ? 0 : $result;
}
/**
* Get the current document set, if a document set ID was provided
*
* @return DMSDocumentSet
*/
public function getCurrentDocumentSet()
{
if ($id = $this->getRequest()->getVar('dsid')) {
return DMSDocumentSet::get()->byid($id);
}
return singleton('DMSDocumentSet');
}
/**
* @return Form
* @todo what template is used here? AssetAdmin_UploadContent.ss doesn't seem to be used anymore
@ -75,9 +78,13 @@ class DMSDocumentAddController extends LeftAndMain
{
Requirements::javascript(FRAMEWORK_DIR . '/javascript/AssetUploadField.js');
Requirements::css(FRAMEWORK_DIR . '/css/AssetUploadField.css');
Requirements::css(DMS_DIR.'/css/DMSMainCMS.css');
Requirements::css(DMS_DIR . '/css/DMSMainCMS.css');
/** @var SiteTree $page */
$page = $this->currentPage();
/** @var DMSDocumentSet $documentSet */
$documentSet = $this->getCurrentDocumentSet();
$uploadField = DMSUploadField::create('AssetUploadField', '');
$uploadField->setConfig('previewMaxWidth', 40);
$uploadField->setConfig('previewMaxHeight', 30);
@ -87,31 +94,34 @@ class DMSDocumentAddController extends LeftAndMain
$uploadField->addExtraClass('ss-assetuploadfield');
$uploadField->removeExtraClass('ss-uploadfield');
$uploadField->setTemplate('AssetUploadField');
$uploadField->setRecord($page);
$uploadField->setRecord($documentSet);
$uploadField->getValidator()->setAllowedExtensions(
array_filter(array_merge(Config::inst()->get('File', 'allowed_extensions'), self::$allowed_extensions))
);
$uploadField->getValidator()->setAllowedExtensions($this->getAllowedExtensions());
$exts = $uploadField->getValidator()->getAllowedExtensions();
asort($exts);
$backlink = $this->Backlink();
$done = "
<a class=\"ss-ui-button ss-ui-action-constructive cms-panel-link ui-corner-all\" href=\"".$backlink."\">
Done!
<a class=\"ss-ui-button ss-ui-action-constructive cms-panel-link ui-corner-all\" href=\"" . $backlink . "\">
" . _t('UploadField.DONE', 'DONE') . "
</a>";
$addExistingField = new DMSDocumentAddExistingField('AddExisting', 'Add Existing');
$addExistingField->setRecord($page);
$addExistingField = new DMSDocumentAddExistingField(
'AddExisting',
_t('DMSDocumentAddExistingField.ADDEXISTING', 'Add Existing')
);
$addExistingField->setRecord($documentSet);
$form = new Form(
$this,
'getEditForm',
new FieldList(
new TabSet(
'Main',
_t('DMSDocumentAddController.MAINTAB', 'Main'),
new Tab(
'From your computer',
_t('UploadField.FROMCOMPUTER', 'From your computer'),
new HiddenField('ID', false, $page->ID),
new HiddenField('DSID', false, $documentSet->ID),
$uploadField,
new LiteralField(
'AllowedExtensions',
@ -123,7 +133,7 @@ class DMSDocumentAddController extends LeftAndMain
)
),
new Tab(
'From the CMS',
_t('UploadField.FROMCMS', 'From the CMS'),
$addExistingField
)
)
@ -136,17 +146,6 @@ class DMSDocumentAddController extends LeftAndMain
$form->Backlink = $backlink;
// Don't use AssetAdmin_EditForm, as it assumes a different panel structure
$form->setTemplate($this->getTemplatesWithSuffix('_EditForm'));
/*$form->Fields()->push(
new LiteralField(
'BackLink',
sprintf(
'<a href="%s" class="backlink ss-ui-button cms-panel-link" data-icon="back">%s</a>',
Controller::join_links(singleton('AssetAdmin')->Link('show'), $folder ? $folder->ID : 0),
_t('AssetAdmin.BackToFolder', 'Back to folder')
)
)
);*/
//$form->loadDataFrom($folder);
return $form;
}
@ -170,26 +169,40 @@ class DMSDocumentAddController extends LeftAndMain
}
$items->push(new ArrayData(array(
'Title' => 'Add Document',
'Title' => _t('DMSDocumentSet.ADDDOCUMENTBUTTON', 'Add Document'),
'Link' => $this->Link()
)));
return $items;
}
/**
* Returns the link to be used to return the user after uploading a document. If a document set ID (dsid) is present
* then it will be redirected back to the page that owns the document set. @todo redirect back to the document set
*
* If no document set ID is present then we assume that it has been added from the model admin, so redirect back to
* that instead.
*
* @return string
*/
public function Backlink()
{
$pageID = $this->currentPageID();
return Controller::join_links(singleton('CMSPagesController')->Link(), 'edit/show', $pageID);
if (!$this->getRequest()->getVar('dsid')) {
$modelAdmin = new DMSDocumentAdmin;
$modelAdmin->init();
return $modelAdmin->Link();
}
return Controller::join_links(singleton('CMSPagesController')->Link(), 'edit/show', $this->currentPageID());
}
public function documentautocomplete()
{
$term = (isset($_GET['term'])) ? $_GET['term'] : '';
$term_sql = Convert::raw2sql($term);
$term = (string) $this->getRequest()->getVar('term');
$termSql = Convert::raw2sql($term);
$data = DMSDocument::get()
->where(
"(\"ID\" LIKE '%".$term_sql."%' OR \"Filename\" LIKE '%".$term_sql."%' OR \"Title\" LIKE '%".$term_sql."%')"
'("ID" LIKE \'%' . $termSql . '%\' OR "Filename" LIKE \'%' . $termSql . '%\''
. ' OR "Title" LIKE \'%' . $termSql . '%\')'
)
->sort('ID ASC')
->limit(20);
@ -202,22 +215,24 @@ class DMSDocumentAddController extends LeftAndMain
);
}
return json_encode($return);
return Convert::raw2json($return);
}
public function linkdocument()
{
$return = array('error' => _t('UploadField.FIELDNOTSET', 'Could not add document to page'));
$return = array('error' => 'testing');
return Convert::raw2json($return);
$page = $this->currentPage();
if (!empty($page)) {
$document = DataObject::get_by_id('DMSDocument', (int) $_GET['documentID']);
$document = DMSDocument::get()->byId($this->getRequest()->getVar('documentID'));
// @todo add sets
$document->addPage($page);
$buttonText = '<button class="ss-uploadfield-item-edit ss-ui-button ui-corner-all"'
. ' title="Edit this document" data-icon="pencil">'
. 'Edit<span class="toggle-details"><span class="toggle-details-icon"></span></span></button>';
. ' title="' . _t('DMSDocument.EDITDOCUMENT', 'Edit this document') . '" data-icon="pencil">'
. _t('DMSDocument.EDIT', 'Edit') . '<span class="toggle-details">'
. '<span class="toggle-details-icon"></span></span></button>';
// Collect all output data.
$return = array(
@ -232,21 +247,27 @@ class DMSDocumentAddController extends LeftAndMain
);
}
return json_encode($return);
return Convert::raw2json($return);
}
/**
* Returns HTML representing a list of documents that are associated with the given page ID, across all document
* sets.
*
* @return string HTML
*/
public function documentlist()
{
if (!isset($_GET['pageID'])) {
if (!$this->getRequest()->getVar('pageID')) {
return $this->httpError(400);
}
$page = SiteTree::get()->byId($_GET['pageID']);
$page = SiteTree::get()->byId($this->getRequest()->getVar('pageID'));
if ($page && $page->Documents()->count() > 0) {
if ($page && $page->getAllDocuments()->count() > 0) {
$list = '<ul>';
foreach ($page->Documents() as $document) {
foreach ($page->getAllDocuments() as $document) {
$list .= sprintf(
'<li><a class="add-document" data-document-id="%s">%s</a></li>',
$document->ID,
@ -264,4 +285,20 @@ class DMSDocumentAddController extends LeftAndMain
_t('DMSDocumentAddController.NODOCUMENTS', 'There are no documents attached to the selected page.')
);
}
/**
* Get an array of allowed file upload extensions, merged with {@link File} and extra configuration from this
* class
*
* @return array
*/
public function getAllowedExtensions()
{
return array_filter(
array_merge(
(array) Config::inst()->get('File', 'allowed_extensions'),
(array) $this->config()->get('allowed_extensions')
)
);
}
}

View File

@ -3,11 +3,11 @@
class DMSGridFieldAddNewButton extends GridFieldAddNewButton implements GridField_HTMLProvider
{
/**
* The page ID that the document should be attached to. Used in the GridField for Documents in a Page.
* The document set ID that the document should be attached to
*
* @var int
*/
protected $pageId;
protected $documentSetId;
/**
* Overriding the parent method to change the template that the DMS add button will be rendered with
@ -30,8 +30,8 @@ class DMSGridFieldAddNewButton extends GridFieldAddNewButton implements GridFiel
}
$link = singleton('DMSDocumentAddController')->Link();
if ($this->getPageId()) {
$link = Controller::join_links($link, '?ID=' . $this->getPageId());
if ($this->getDocumentSetId()) {
$link = Controller::join_links($link, '?dsid=' . $this->getDocumentSetId());
}
$data = new ArrayData(array(
@ -45,24 +45,24 @@ class DMSGridFieldAddNewButton extends GridFieldAddNewButton implements GridFiel
}
/**
* Set the page ID that this document should be attached to
* Set the document set ID that this document should be attached to
*
* @param int $id
* @return $this
*/
public function setPageId($id)
public function setDocumentSetId($id)
{
$this->pageId = $id;
$this->documentSetId = $id;
return $this;
}
/**
* Get the page ID that this document should be attached to
* Get the document set ID that this document should be attached to
*
* @return int
*/
public function getPageId()
public function getDocumentSetId()
{
return $this->pageId;
return $this->documentSetId;
}
}

View File

@ -57,7 +57,7 @@ class DMSGridFieldDeleteAction extends GridFieldDeleteAction implements
}
// Add a class to the field to if it is the last gridfield in the list
$numberOfRelations = $record->Pages()->Count();
$numberOfRelations = $record->getRelatedPages()->count();
$field
// Add a new class for custom JS to handle the delete action
->addExtraClass('dms-delete')

View File

@ -13,7 +13,7 @@ class DMSGridFieldDetailForm_ItemRequest extends GridFieldDetailForm_ItemRequest
//add a data attribute specifying how many pages this document is referenced on
if ($record = $this->record) {
$numberOfPageRelations = $record->Pages()->Count();
$numberOfPageRelations = $record->getRelatedPages()->Count();
$relations = new ShortCodeRelationFinder();
$numberOfInlineRelations = $relations->findPageCount($record->ID);

View File

@ -18,17 +18,12 @@ class DMSUploadField extends UploadField
"upload",
);
/**
* The temporary folder name to store files in during upload
* @var string
*/
protected $folderName = 'DMSTemporaryUploads';
public function __construct($name, $title = null, SS_List $items = null)
{
parent::__construct($name, $title, $items);
//set default DMS replace template to false
$this->setConfig('useDMSReplaceTemplate', 0);
}
/**
* Override the default behaviour of the UploadField and take the uploaded file (uploaded to assets) and
* add it into the DMS storage, deleting the old/uploaded file.
@ -48,9 +43,12 @@ class DMSUploadField extends UploadField
// Otherwise create it
$doc = $dms->storeDocument($file);
$file->delete();
// Relate to the underlying page being edited.
// Not applicable when editing the document itself and replacing it.
$doc->addPage($record);
}
// Relate to the underlying document set being edited.
// Not applicable when editing the document itself and replacing it, or uploading from the ModelAdmin
if ($record instanceof DMSDocumentSet) {
$record->Documents()->add($doc);
}
return $doc;
@ -61,17 +59,6 @@ class DMSUploadField extends UploadField
return true;
}
public function isDisabled()
{
return (parent::isDisabled() || !$this->isSaveable());
}
public function isSaveable()
{
return (!empty($this->getRecord()->ID));
}
/**
* Action to handle upload of a single file
*
@ -80,6 +67,10 @@ class DMSUploadField extends UploadField
*/
public function upload(SS_HTTPRequest $request)
{
if ($recordId = $request->postVar('ID')) {
$this->setRecord(DMSDocumentSet::get()->byId($recordId));
}
if ($this->isDisabled() || $this->isReadonly()) {
return $this->httpError(403);
}
@ -110,13 +101,13 @@ class DMSUploadField extends UploadField
if (!$return['error'] && $this->relationAutoSetting && $record && $record->exists()) {
$tooManyFiles = false;
// Some relationships allow many files to be attached.
if ($this->getConfig('allowedMaxFileNumber') && ($record->has_many($name) || $record->many_many($name))) {
if ($this->getConfig('allowedMaxFileNumber') && ($record->hasMany($name) || $record->manyMany($name))) {
if (!$record->isInDB()) {
$record->write();
}
$tooManyFiles = $record->{$name}()->count() >= $this->getConfig('allowedMaxFileNumber');
// has_one only allows one file at any given time.
} elseif ($record->has_one($name)) {
} elseif ($record->hasOne($name)) {
$tooManyFiles = $record->{$name}() && $record->{$name}()->exists();
}
@ -147,7 +138,7 @@ class DMSUploadField extends UploadField
// Get the uploaded file into a new file object.
try {
$this->upload->loadIntoFile($tmpfile, $fileObject, $this->folderName);
$this->upload->loadIntoFile($tmpfile, $fileObject, $this->getFolderName());
} catch (Exception $e) {
// we shouldn't get an error here, but just in case
$return['error'] = $e->getMessage();
@ -155,7 +146,7 @@ class DMSUploadField extends UploadField
if (!$return['error']) {
if ($this->upload->isError()) {
$return['error'] = implode(' '.PHP_EOL, $this->upload->getErrors());
$return['error'] = implode(' ' . PHP_EOL, $this->upload->getErrors());
} else {
$file = $this->upload->getFile();
@ -198,10 +189,10 @@ class DMSUploadField extends UploadField
// Replace the download template with a new one only when access the upload field through a GridField.
// Needs to be enabled through setConfig('downloadTemplateName', 'ss-dmsuploadfield-downloadtemplate');
Requirements::javascript('dms/javascript/DMSUploadField_downloadtemplate.js');
Requirements::javascript(DMS_DIR . '/javascript/DMSUploadField_downloadtemplate.js');
// In the add dialog, add the addtemplate into the set of file that load.
Requirements::javascript('dms/javascript/DMSUploadField_addtemplate.js');
Requirements::javascript(DMS_DIR . '/javascript/DMSUploadField_addtemplate.js');
return $fields;
}
@ -305,4 +296,26 @@ class DMSUploadField extends UploadField
user_error("Invalid value for UploadField::fileEditValidator", E_USER_ERROR);
}
/**
* Set the folder name to store DMS files in
*
* @param string $folderName
* @return $this
*/
public function setFolderName($folderName)
{
$this->folderName = (string) $folderName;
return $this;
}
/**
* Get the folder name for storing the document
*
* @return string
*/
public function getFolderName()
{
return $this->folderName;
}
}

View File

@ -8,9 +8,14 @@ class DMSUploadField_ItemHandler extends UploadField_ItemHandler
'EditForm',
);
/**
* Gets a DMS document by its ID
*
* @return DMSDocument
*/
public function getItem()
{
return DataObject::get_by_id('DMSDocument', $this->itemID);
return DMSDocument::get()->byId($this->itemID);
}
/**

View File

@ -5,136 +5,56 @@
*/
class DMSSiteTreeExtension extends DataExtension
{
private static $belongs_many_many = array(
'Documents' => 'DMSDocument'
private static $has_many = array(
'DocumentSets' => 'DMSDocumentSet'
);
private static $noDocumentsList = array();
private static $showDocumentsList = array();
/**
* Do not show the documents tab on the array of pages set here
* @static
* @param $mixed Array of page types to not show the Documents tab on
*/
public static function no_documents_tab($array = array())
{
if (empty($array)) {
return;
}
if (is_array($array)) {
self::$noDocumentsList = $array;
} else {
self::$noDocumentsList = array($array);
}
}
/**
* Only show the documents tab on the list of pages set here. Any pages set in the no_documents_tab array will
* still not be shown. If this isn't called, or if it is called with an empty array, all pages will get
* Document tabs.
* @static
* @param $array Array of page types to show the Documents tab on
*/
public static function show_documents_tab($array = array())
{
if (empty($array)) {
return;
}
if (is_array($array)) {
self::$showDocumentsList = $array;
} else {
self::$showDocumentsList = array($array);
}
}
public function updateCMSFields(FieldList $fields)
{
// Prevent certain pages from having a Document tab in the CMS
if (in_array($this->owner->ClassName, self::$noDocumentsList)) {
// Ability to disable document sets for a Page
if (!$this->owner->config()->get('documents_enabled')) {
return;
}
if (count(self::$showDocumentsList) > 0 && !in_array($this->owner->ClassName, self::$showDocumentsList)) {
return;
}
// Javascript to customize the grid field for the DMS document (overriding entwine
// in FRAMEWORK_DIR.'/javascript/GridField.js'
Requirements::javascript(DMS_DIR.'/javascript/DMSGridField.js');
Requirements::css(DMS_DIR.'/css/DMSMainCMS.css');
// Javascript for the link editor pop-up in TinyMCE
Requirements::javascript(DMS_DIR."/javascript/DocumentHtmlEditorFieldToolbar.js");
// Document listing
$gridFieldConfig = GridFieldConfig::create()->addComponents(
new GridFieldToolbarHeader(),
new GridFieldFilterHeader(),
new GridFieldSortableHeader(),
new GridFieldOrderableRows('DocumentSort'),
new GridFieldDataColumns(),
new GridFieldEditButton(),
new DMSGridFieldDeleteAction(), //special delete dialog to handle custom behaviour of unlinking and deleting
new GridFieldDetailForm()
//GridFieldLevelup::create($folder->ID)->setLinkSpec('admin/assets/show/%d')
);
if (class_exists('GridFieldPaginatorWithShowAll')) {
$paginatorComponent = new GridFieldPaginatorWithShowAll(15);
} else {
$paginatorComponent = new GridFieldPaginator(15);
}
$gridFieldConfig->addComponent($paginatorComponent);
if (class_exists('GridFieldSortableRows')) {
$sortableComponent = new GridFieldSortableRows('DocumentSort');
//setUsePagenation method removed from newer version of SortableGridField.
if (method_exists($sortableComponent, 'setUsePagination')) {
$sortableComponent->setUsePagination(false)->setForceRedraw(true);
}
$gridFieldConfig->addComponent($sortableComponent);
}
// HACK: Create a singleton of DMSDocument to ensure extensions are applied before we try to get display fields.
singleton('DMSDocument');
$gridFieldConfig->getComponentByType('GridFieldDataColumns')
->setDisplayFields(Config::inst()->get('DMSDocument', 'display_fields'))
->setFieldCasting(array('LastChanged'=>"Datetime->Ago"))
->setFieldFormatting(
array(
'FilenameWithoutID'=>'<a target=\'_blank\' class=\'file-url\' href=\'$Link\'>$FilenameWithoutID</a>'
)
);
//override delete functionality with this class
$gridFieldConfig->getComponentByType('GridFieldDetailForm')
->setItemRequestClass('DMSGridFieldDetailForm_ItemRequest');
$gridField = GridField::create(
'Documents',
'Document Sets',
false,
$this->owner->Documents()->Sort('DocumentSort'),
$gridFieldConfig
$this->owner->DocumentSets(), //->Sort('DocumentSort'),
new GridFieldConfig_RelationEditor
);
$gridField->addExtraClass('documents');
$gridField->addExtraClass('documentsets');
$gridFieldConfig->addComponent(
$addNewButton = new DMSGridFieldAddNewButton,
'GridFieldExportButton'
$fields->addFieldToTab(
'Root.Document Sets (' . $this->owner->DocumentSets()->Count() . ')',
$gridField
);
$addNewButton->setPageId($this->owner->ID);
$fields->addFieldToTab('Root.Documents (' . $this->owner->Documents()->Count() . ')', $gridField);
}
/**
* Enforce sorting for frontend
* Get a list of document sets for the owner page
*
* @return ArrayList
*/
public function PageDocuments()
public function getDocumentSets()
{
return $this->owner->getManyManyComponents('Documents')->sort('DocumentSort');
return $this->owner->DocumentSets();
}
/**
* Get a list of all documents from all document sets for the owner page
*
* @return ArrayList
*/
public function getAllDocuments()
{
$documents = ArrayList::create();
foreach ($this->getDocumentSets() as $documentSet) {
/** @var DocumentSet $documentSet */
$documents->merge($documentSet->getDocuments());
}
return $documents;
}
public function onBeforeDelete()
@ -160,7 +80,7 @@ class DMSSiteTreeExtension extends DataExtension
public function onBeforePublish()
{
$embargoedDocuments = $this->owner->Documents()->filter('EmbargoedUntilPublished', true);
$embargoedDocuments = $this->owner->getAllDocuments()->filter('EmbargoedUntilPublished', true);
if ($embargoedDocuments->Count() > 0) {
foreach ($embargoedDocuments as $doc) {
$doc->EmbargoedUntilPublished = false;
@ -169,8 +89,14 @@ class DMSSiteTreeExtension extends DataExtension
}
}
/**
* Returns the title of the page with the total number of documents it has associated with it across
* all document sets
*
* @return string
*/
public function getTitleWithNumberOfDocuments()
{
return $this->owner->Title . ' (' . $this->owner->Documents()->Count() . ')';
return $this->owner->Title . ' (' . $this->owner->getAllDocuments()->count() . ')';
}
}

View File

@ -6,58 +6,6 @@
*/
interface DMSDocumentInterface
{
/**
* Deletes the DMSDocument, its underlying file, as well as any tags related to this DMSDocument.
*
* @todo Can't be applied to classes which already implement the DataObjectInterface (naming conflict)
*
* @abstract
* @return null
*/
// function delete();
/**
* Associates this DMSDocument with a Page. This method does nothing if the association already exists.
* This could be a simple wrapper around $myDoc->Pages()->add($myPage) to add a many_many relation
* @abstract
* @param $pageObject Page object to associate this DMSDocument with
* @return null
*/
public function addPage($pageObject);
/**
* 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
*/
public function addPages($pageIDs);
/**
* Removes the association between this DMSDocument and a Page. This method does nothing if the association does
* not exist.
* @abstract
* @param $pageObject Page object to remove the association to
* @return mixed
*/
public function removePage($pageObject);
/**
* Returns a list of the Page objects associated with this DMSDocument
* @abstract
* @return DataList
*/
public function getPages();
/**
* Removes all associated Pages from the DMSDocument
* @abstract
* @return null
*/
public function removeAllPages();
/**
* Adds a metadata tag to the DMSDocument. The tag has a category and a value.
* Each category can have multiple values by default. So: addTag("fruit","banana") addTag("fruit", "apple") will
@ -67,7 +15,6 @@ interface DMSDocumentInterface
* 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)
* @abstract
* @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)
@ -78,7 +25,6 @@ interface DMSDocumentInterface
/**
* 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
@ -89,7 +35,6 @@ interface DMSDocumentInterface
* Removes a tag from the DMSDocument. 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.
* @abstract
* @param $category Category to remove (required)
* @param null $value Value to remove (optional)
* @return null
@ -98,14 +43,12 @@ interface DMSDocumentInterface
/**
* Deletes all tags associated with this DMSDocument.
* @abstract
* @return null
*/
public function removeAllTags();
/**
* Returns a link to download this DMSDocument from the DMS store
* @abstract
* @return String
*/
public function getLink();
@ -142,14 +85,12 @@ interface DMSDocumentInterface
* Hides the DMSDocument, 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 DMSDocuments that have not yet gone live.
* @abstract
* @return null
*/
public function embargoIndefinitely();
/**
* Returns if this is DMSDocument is embargoed or expired.
* @abstract
* @return bool True or False depending on whether this DMSDocument is embargoed or expired
*/
public function isHidden();
@ -157,7 +98,6 @@ interface DMSDocumentInterface
/**
* Returns if this is DMSDocument is embargoed.
* @abstract
* @return bool True or False depending on whether this DMSDocument is embargoed
*/
public function isEmbargoed();
@ -165,7 +105,6 @@ interface DMSDocumentInterface
/**
* Hides the DMSDocument, so it does not show up when getByPage($myPage) is called. Automatically un-hides the
* DMSDocument at a specific date.
* @abstract
* @param $datetime String date time value when this DMSDocument should expire
* @return null
*/
@ -179,21 +118,18 @@ interface DMSDocumentInterface
/**
* Clears any previously set embargos, so the DMSDocument always shows up in all queries.
* @abstract
* @return null
*/
public function clearEmbargo();
/**
* Returns if this is DMSDocument is expired.
* @abstract
* @return bool True or False depending on whether this DMSDocument is expired
*/
public function isExpired();
/**
* Hides the DMSDocument at a specific date, so it does not show up when getByPage($myPage) is called.
* @abstract
* @param $datetime String date time value when this DMSDocument should expire
* @return null
*/
@ -201,7 +137,6 @@ interface DMSDocumentInterface
/**
* Clears any previously set expiry.
* @abstract
* @return null
*/
public function clearExpiry();
@ -211,7 +146,6 @@ interface DMSDocumentInterface
/**
* Returns a DataList of all previous Versions of this DMSDocument (check the LastEdited date of each
* object to find the correct one)
* @abstract
* @return DataList List of DMSDocument objects
*/
public function getVersions();

View File

@ -57,11 +57,19 @@ interface DMSInterface
/**
* Returns a list of Document objects associated with a Page
* @abstract
* @param $page SiteTree to fetch the associated Documents from
* Returns a list of Document objects associated with a Page via intermediary document sets
*
* @param SiteTree $page SiteTree to fetch the associated Documents from
* @param bool $showEmbargoed Boolean that specifies if embargoed documents should be included in results
* @return DataList Document list associated with the Page
* @return ArrayList Document list associated with the Page
*/
public function getByPage($page, $showEmbargoed = false);
public function getByPage(SiteTree $page, $showEmbargoed = false);
/**
* Returns a list of Document Set objects associated with a Page
*
* @param SiteTree $page SiteTree to fetch the associated Document Sets from
* @return ArrayList Document list associated with the Page
*/
public function getDocumentSetsByPage(SiteTree $page);
}

View File

@ -43,20 +43,17 @@ class DMSDocument extends DataObject implements DMSDocumentInterface
"CanEditType" => "Enum('LoggedInUsers, OnlyTheseUsers', 'LoggedInUsers')",
);
private static $belongs_many_many = array(
'Sets' => 'DMSDocumentSet'
);
private static $many_many = array(
'Pages' => 'SiteTree',
'RelatedDocuments' => 'DMSDocument',
'Tags' => 'DMSTag',
'ViewerGroups' => 'Group',
'EditorGroups' => 'Group',
);
private static $many_many_extraFields = array(
'Pages' => array(
'DocumentSort' => 'Int'
)
);
private static $display_fields = array(
'ID' => 'ID',
'Title' => 'Title',
@ -82,7 +79,7 @@ class DMSDocument extends DataObject implements DMSDocumentInterface
'Filename' => 'Filename',
'Title' => 'Title',
'ViewCount' => 'ViewCount',
'getPages.count' => 'Page Use'
'getRelatedPages.count' => 'Page Use'
);
/**
@ -217,103 +214,6 @@ class DMSDocument extends DataObject implements DMSDocumentInterface
return $this->canView();
}
/**
* Associates this document with a Page. This method does nothing if the
* association already exists.
*
* This could be a simple wrapper around $myDoc->Pages()->add($myPage) to
* add a many_many relation.
*
* @param SiteTree $pageObject Page object to associate this Document with
*
* @return DMSDocument
*/
public function addPage($pageObject)
{
$this->Pages()->add($pageObject);
DB::query(
"UPDATE \"DMSDocument_Pages\" SET \"DocumentSort\"=\"DocumentSort\"+1"
. " WHERE \"SiteTreeID\" = $pageObject->ID"
);
return $this;
}
/**
* 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.
*
* @param array $pageIDs
*
* @return DMSDocument
*/
public function addPages($pageIDs)
{
foreach ($pageIDs as $id) {
$pageObject = DataObject::get_by_id("SiteTree", $id);
if ($pageObject && $pageObject->exists()) {
$this->addPage($pageObject);
}
}
return $this;
}
/**
* Removes the association between this Document and a Page. This method
* does nothing if the association does not exist.
*
* @param SiteTree $pageObject Page object to remove the association to
*
* @return DMSDocument
*/
public function removePage($pageObject)
{
$this->Pages()->remove($pageObject);
return $this;
}
/**
* @see getPages()
*
* @return DataList
*/
public function Pages()
{
$pages = $this->getManyManyComponents('Pages');
$this->extend('updatePages', $pages);
return $pages;
}
/**
* Returns a list of the Page objects associated with this Document.
*
* @return DataList
*/
public function getPages()
{
return $this->Pages();
}
/**
* Removes all associated Pages from the DMSDocument
*
* @return DMSDocument
*/
public function removeAllPages()
{
$this->Pages()->removeAll();
return $this;
}
/**
* Increase ViewCount by 1, without update any other record fields such as
* LastEdited.
@ -842,8 +742,6 @@ class DMSDocument extends DataObject implements DMSDocumentInterface
}
}
$this->removeAllPages();
// get rid of any versions have saved for this DMSDocument, too
if (DMSDocument_versions::$enable_versions) {
$versions = $this->getVersions();
@ -855,11 +753,9 @@ class DMSDocument extends DataObject implements DMSDocumentInterface
}
}
parent::delete();
return parent::delete();
}
/**
* Relate an existing file on the filesystem to the document.
*
@ -1060,7 +956,7 @@ class DMSDocument extends DataObject implements DMSDocumentInterface
$pagesGrid = GridField::create(
'Pages',
_t('DMSDocument.RelatedPages', 'Related Pages'),
$this->Pages(),
$this->getRelatedPages(),
$gridFieldConfig
);
@ -1321,7 +1217,7 @@ class DMSDocument extends DataObject implements DMSDocumentInterface
);
//count the number of pages this document is published on
$publishedOnCount = $this->Pages()->Count();
$publishedOnCount = $this->getRelatedPages()->count();
$publishedOnValue = "$publishedOnCount pages";
if ($publishedOnCount == 1) {
$publishedOnValue = "$publishedOnCount page";
@ -1419,6 +1315,26 @@ class DMSDocument extends DataObject implements DMSDocumentInterface
return $documents;
}
/**
* Get a list of related pages for this document by going through the associated document sets
*
* @return ArrayList
*/
public function getRelatedPages()
{
$pages = ArrayList::create();
foreach ($this->Sets() as $documentSet) {
/** @var DocumentSet $documentSet */
$pages->add($documentSet->Page());
}
$pages->removeDuplicates();
$this->extend('updateRelatedPages', $pages);
return $pages;
}
/**
* Get a GridField for managing related documents
*

View File

@ -0,0 +1,123 @@
<?php
/**
* A document set is attached to Pages, and contains many DMSDocuments
*/
class DMSDocumentSet extends DataObject
{
private static $db = array(
'Title' => 'Varchar(255)'
);
private static $has_one = array(
'Page' => 'SiteTree'
);
private static $many_many = array(
'Documents' => 'DMSDocument'
);
/**
* Retrieve a list of the documents in this set. An extension hook is provided before the result is returned.
*
* You can attach an extension to this event:
*
* <code>
* public function updateDocuments($document)
* {
* // do something
* }
* </code>
*
* @return DataList
*/
public function getDocuments()
{
$documents = $this->Documents();
$this->extend('updateDocuments', $documents);
return $documents;
}
/**
* Put the "documents" list into the main tab instead of its own tab, and replace the default "Add Document" button
* with a customised button for DMS documents
*
* @return FieldList
*/
public function getCMSFields()
{
$this->beforeUpdateCMSFields(function (FieldList $fields) {
// Javascript to customize the grid field for the DMS document (overriding entwine
// in FRAMEWORK_DIR.'/javascript/GridField.js'
Requirements::javascript(DMS_DIR . '/javascript/DMSGridField.js');
Requirements::css(DMS_DIR . '/css/DMSMainCMS.css');
// Javascript for the link editor pop-up in TinyMCE
Requirements::javascript(DMS_DIR . '/javascript/DocumentHtmlEditorFieldToolbar.js');
// Document listing
$gridFieldConfig = GridFieldConfig::create()
->addComponents(
new GridFieldToolbarHeader(),
new GridFieldFilterHeader(),
new GridFieldSortableHeader(),
// new GridFieldOrderableRows('DocumentSort'),
new GridFieldDataColumns(),
new GridFieldEditButton(),
new DMSGridFieldDeleteAction(), //special delete dialog to handle custom behaviour of unlinking and deleting
new GridFieldDetailForm()
//GridFieldLevelup::create($folder->ID)->setLinkSpec('admin/assets/show/%d')
);
if (class_exists('GridFieldPaginatorWithShowAll')) {
$paginatorComponent = new GridFieldPaginatorWithShowAll(15);
} else {
$paginatorComponent = new GridFieldPaginator(15);
}
$gridFieldConfig->addComponent($paginatorComponent);
if (class_exists('GridFieldSortableRows')) {
$sortableComponent = new GridFieldSortableRows('DocumentSort');
//setUsePagenation method removed from newer version of SortableGridField.
if (method_exists($sortableComponent, 'setUsePagination')) {
$sortableComponent->setUsePagination(false)->setForceRedraw(true);
}
$gridFieldConfig->addComponent($sortableComponent);
}
// HACK: Create a singleton of DMSDocument to ensure extensions are applied before we try to get display fields.
singleton('DMSDocument');
$gridFieldConfig->getComponentByType('GridFieldDataColumns')
->setDisplayFields(Config::inst()->get('DMSDocument', 'display_fields'))
->setFieldCasting(array('LastChanged'=>"Datetime->Ago"))
->setFieldFormatting(
array(
'FilenameWithoutID' => '<a target=\'_blank\' class=\'file-url\' href=\'$Link\'>$FilenameWithoutID</a>'
)
);
//override delete functionality with this class
$gridFieldConfig->getComponentByType('GridFieldDetailForm')
->setItemRequestClass('DMSGridFieldDetailForm_ItemRequest');
$gridField = GridField::create(
'Documents',
false,
$this->Documents(),//->Sort('DocumentSort'),
$gridFieldConfig
);
$gridField->addExtraClass('documents');
$gridFieldConfig->addComponent(
$addNewButton = new DMSGridFieldAddNewButton,
'GridFieldExportButton'
);
$addNewButton->setDocumentSetId($this->ID);
$fields->removeByName('Documents');
$fields->addFieldToTab('Root.Main', $gridField);
});
return parent::getCMSFields();
}
}

View File

@ -0,0 +1,30 @@
# 2.0.0 (unreleased)
## Document sets
Documents now belong to "sets", which are attached to Pages. A Page can have many Document Sets, and a Set has a
many_many relationship with Documents.
When upgrading from 1.x to 2.x you will need to migrate the relationships from your Pages to Documents so support
having a Document Set intermediary (@todo Add a build task for this).
## API changes
* `DMSSiteTreeExtension::no_documents_tab` removed, use YAML configuration `MyPage.documents_enabled: false` instead
* `DMSSiteTreeExtension::show_documents_tab` removed, use YAML configuration `MyPage.documents_enabled: true` instead
* `DMSSiteTreeExtension::PageDocuments` removed, use `DMSSiteTreeExtension::getDocumentSets` instead
* `DMSSiteTreeExtension::getDocuments` removed, use `DMSSiteTreeExtension::getAllDocuments` instead
* `DMSDocument::addPage` removed, use document sets instead
* `DMSDocument::addPages` removed, use document sets instead
* `DMSDocument::removePage` removed, use document sets instead
* `DMSDocument::removeAllPages` removed, use document sets instead
* `DMSDocument::getPages` removed, use `DMSDocument::getRelatedPages` instead
* `DMSDocumentInterface` has had the page manipulation methods removed, as above
* `DMSDocumentAddController::add_allowed_extensions` removed, use YAML configuration `DMSDocumentAddController::allowed_extensions` instead
* `DMSInterface` (and `DMS`) are stricter in the `getByPage` method, enforcing a `SiteTree` type hint
* New method `DMSInterface::getDocumentSetsByPage` (and in `DMS`)
## Template changes
The default template entry point is now `DocumentSets.ss` (previously `Documents.ss`). As well as this change,
`Documents.ss` has been renamed to `DocumentSet.ss`.

View File

@ -1,3 +1,30 @@
# Configuration
The file location is set via the `DMS::$dmsFolder` static, and points to a location in the webroot.
## Enable/disable documents/sets for a specific page type
If you don't need documents/document sets for a specific page type you can disable this with YAML configuration:
```yaml
MyPageType:
documents_enabled: false
```
Likewise, you could override a previously set configuration value by setting this back to `true` in a configuration
file with a higher precedence.
## Allowed extensions for DMS documents
By default the allowed extensions for DMS documents will come from the UploadField's allowed extesions list, and will
have a customised list of extensions for DMS merged in. The base `allowed_extensions` is a site-wide configuration
setting. [See here for information](https://docs.silverstripe.org/en/3/developer_guides/forms/field_types/uploadfield/#limit-the-allowed-filetypes) on changing this.
To add extra allowed file extensions purely for DMS documents, you can update the YAML configuration property:
```yaml
DMSDocumentAddController:
allowed_extensions:
- php
- php5
```

View File

@ -1,18 +1,35 @@
# Creating documents
Create by relative path:
The following examples will allow you to create a DMS document in the system without associating it to a document set.
## Create by relative path
```php
$dms = DMS::getDMSInstance();
$dms = DMS::inst();
$doc = $dms->storeDocument('assets/myfile.pdf');
```
Create from an existing `File` record:
## Create from an existing `File` record
```php
$dms = DMS::getDMSInstance();
$dms = DMS::inst();
$file = File::get()->byID(99);
$doc = $dms->storeDocument($file);
```
Note: Both operations copy the existing file.
## Associate to a document set
If you need to associate a document to a set once it has already been created, you can use the ORM relationship from
SiteTree to access the document sets, or you can simply access the document set directly:
```php
// Add document to the first set in my page
$firstSetInPage = $myPage->DocumentSets()->first();
$firstSetInPage->add($doc);
// Add document to a specific document set
$docSet = DMSDocumentSet::get()->byId(123);
$docSet->add($doc);
```

View File

@ -5,7 +5,7 @@
You can use `DMSDocument::getLink` to retrieve the secure route to download a DMS document:
```php
$dms = DMS::getDMSInstance();
$dms = DMS::inst();
$docs = $dms->getByTag('priority', 'important')->First();
$link = $doc->getLink();
```

View File

@ -14,3 +14,7 @@
## CMS user help
* TBC
### Changelogs
* [2.0.0 (unreleased)](changelogs/2.0.0.md)

View File

@ -1,10 +1,35 @@
# Manage page relations
To find documents by a Page:
Documents are associated to pages via "document sets". You can retrieve document sets for a page, then retrieve
documents that belong to those sets. You can still retrieve all documents for a page if you want to.
## Get document sets for a page
```php
$dms = DMS::getDMSInstance();
$page = SiteTree::get()->filter('URLSegment', 'home')->first();
/** @var DataList $docs */
$docs = $dms->getByPage($page);
$dms = DMS::inst();
$sets = $dms->getDocumentSetsByPage($myPage);
```
You can also request sets directly from the SiteTree instance:
```php
$sets = $page->getDocumentSets();
```
## Get all related documents for a page
`DMS::getByPage` will exclude currently embargoed documents by default. To include embargoed documents as well
add `true` as the second argument.
```php
$dms = DMS::inst();
$documents = $dms->getByPage($myPage);
$documentsIncludingEmbargoed = $dms->getByPage($myPage, true);
```
You can also request this directly from the SiteTree instance:
```php
$documents = $myPage->getAllDocuments();
```

View File

@ -4,7 +4,7 @@ Add a simple include to any of your `.ss` templates to display the DMSDocuments
the current page on the front-end.
```
<% include Documents %>
<% include DocumentSets %>
```
You can fine tune the HTML markup or display behaviour of any of the templates in `/dms/templates/Includes` to change

View File

@ -9,6 +9,8 @@ en:
TYPE: 'File type'
URL: URL
DMSDocument:
EDIT: Edit
EDITDOCUMENT: Edit this document
PLURALNAME: Documents
RelatedPages: 'Related Pages'
RelatedReferences: 'Related References'
@ -16,9 +18,14 @@ en:
Versions: Versions
DOWNLOAD: "Download {title}"
LASTCHANGED: "Last changed: {date}"
DMSDocumentSet:
ADDDOCUMENTBUTTON: Add Document
ADDDOCUMENTSBUTTON: Add Documents
PLURALNAME: Document Sets
SINGULARNAME: Document Set
DMSTag:
PLURALNAME: 'D M S Tags'
SINGULARNAME: 'D M S Tag'
PLURALNAME: 'DMS Tags'
SINGULARNAME: 'DMS Tag'
FileIFrameField:
ATTACHONCESAVED2: 'Files can be attached once you have saved the record for the first time.'
GridAction:
@ -29,8 +36,10 @@ en:
DeletePermissionsFailure: 'No delete permissions'
UploadField:
ATTACHFILE: 'Attach a file'
DONE: Done!
DROPFILE: 'drop a file'
FIELDNOTSET: 'File information not found'
FROMCMS: From the CMS
FROMCOMPUTER: 'From your computer'
FROMCOMPUTERINFO: 'Upload from your computer'
MAXNUMBEROFFILES: 'Max number of {count} file(s) exceeded.'
@ -38,6 +47,14 @@ en:
MENUTITLE: 'Edit Page'
NODOCUMENTS: 'There are no documents attached to the selected page.'
RELATEDDOCUMENTS: 'Related Documents'
MAINTAB: Main
DMSDocument_versions:
PLURALNAME: 'D M S Document_versionss'
SINGULARNAME: 'D M S Document_versions'
PLURALNAME: 'DMS Document_versionss'
SINGULARNAME: 'DMS Document_versions'
DMSDocumentAddExistingField:
ADDEXISTING: Add Existing
AUTOCOMPLETE: Search by ID or filename
CHOOSESET: Choose Document Set
EDITDOCUMENTDETAILS: Edit Document Details
LINKADOCUMENT: Link a Document
SELECTED: Selected Document

View File

@ -1,40 +1,24 @@
<div id="document" class="ss-add field treedropdown searchable">
<div class="document-add-existing <% if useFieldContext %>field<% else %>link-editor-context<% end_if %>">
<% if useFieldContext %>
<h3>
<% else %>
<div>
<% end_if %>
<div class="document-add-existing <% if $useFieldContext %>field<% else %>link-editor-context<% end_if %>">
<% if $useFieldContext %><h3><% else %><div><% end_if %>
<span class="step-label">
<% if useFieldContext %>
<% if $useFieldContext %>
<span class="flyout">1</span><span class="arrow"></span>
<strong class="title">Link a Document</strong>
<% else %>
<strong class="title"><%t DMSDocumentAddExistingField.LINKADOCUMENT "Link a Document" %></strong>
<% end_if %>
</span>
<% if useFieldContext %>
</h3>
<% else %>
</div>
<% end_if %>
<% if $useFieldContext %></h3><% else %></div><% end_if %>
<% if useFieldContext %>
<% else %>
<label>Link a Document</label>
<% if not $useFieldContext %>
<label><%t DMSDocumentAddExistingField.LINKADOCUMENT "Link a Document" %></label>
<div class="middleColumn">
<% end_if %>
<input class="document-autocomplete text" type="text" placeholder="Search by ID or filename" />
<!-- <span>or Add from page</span> -->
<input class="document-autocomplete text" type="text" placeholder="<%t DMSDocumentAddExistingField.AUTOCOMPLETE "Search by ID or filename" %>" />
$fieldByName(PageSelector)
<div class="document-list"></div>
<% if useFieldContext %>
<% else %>
<% if not $useFieldContext %>
</div>
<% end_if %>
</div>
<div class="ss-assetuploadfield <% if useFieldContext %>field<% else %>link-editor-context<% end_if %>">
@ -42,23 +26,18 @@
<span class="step-label">
<% if useFieldContext %>
<span class="flyout">2</span><span class="arrow"></span>
<strong class="title">Edit Document Details</strong>
<strong class="title"><%t DMSDocumentAddExistingField.EDITDOCUMENTDETAILS "Edit Document Details" %></strong>
<% else %>
<label>Selected Document</label>
<label><%t DMSDocumentAddExistingField.SELECTED "Selected Document" %></label>
<% end_if %>
</span>
</div>
<!-- <div class="fileOverview"></div> -->
<% if useFieldContext %>
<% else %>
<% if not $useFieldContext %>
<div class="middleColumn">
<% end_if %>
<ul class="files ss-uploadfield-files ss-add-files"></ul>
<% if useFieldContext %>
<% else %>
<% if not $useFieldContext %>
</div>
<% end_if %>
</div>
</div>

View File

@ -12,7 +12,7 @@
<div class="clear"><!-- --></div>
</label>
<div class="ss-uploadfield-item-actions">
<% if Top.isDisabled || Top.isReadonly %>
<% if $Top.isDisabled || $Top.isReadonly %>
<% else %>
$UploadFieldFileButtons
<% end_if %>
@ -25,11 +25,11 @@
<% end_loop %>
<% end_if %>
</ul>
<% if isDisabled || isReadonly %>
<% if isSaveable %>
<% if $isDisabled || $isReadonly %>
<% if $isSaveable %>
<% else %>
<div class="ss-uploadfield-item">
<em><% _t('FileIFrameField.ATTACHONCESAVED2', 'Files can be attached once you have saved the record for the first time.') %></em>
<em><%t FileIFrameField.ATTACHONCESAVED2 "Files can be attached once you have saved the record for the first time." %></em>
</div>
<% end_if %>
<% else %>
@ -41,8 +41,8 @@
<label class="ss-uploadfield-item-name"><b>
<% _t('UploadField.ATTACHFILE', 'Attach a file') %>
</b></label>
<label class="ss-uploadfield-fromcomputer ss-ui-button ui-corner-all" title="<% _t('UploadField.FROMCOMPUTERINFO', 'Upload from your computer') %>" data-icon="drive-upload">
<% _t('UploadField.FROMCOMPUTER', 'From your computer') %>
<label class="ss-uploadfield-fromcomputer ss-ui-button ui-corner-all" title="<%t UploadField.FROMCOMPUTERINFO 'Upload from your computer' %>" data-icon="drive-upload">
<%t UploadField.FROMCOMPUTER 'From your computer' %>
<input id="$id" name="$getName" class="$extraClass ss-uploadfield-fromcomputer-fileinput" data-config="$configString" type="file"<% if $multiple %> multiple="multiple"<% end_if %> />
</label>
<div class="clear"><!-- --></div>

View File

@ -0,0 +1,11 @@
<% if $getDocuments %>
<div class="documentsets-set">
<% if $Title %>
<h3>$Title</h3>
<% end_if %>
<% loop $getDocuments %>
<% include Document %>
<% end_loop %>
</div>
<% end_if %>

View File

@ -0,0 +1,7 @@
<% if $getDocumentSets %>
<div class="documentsets">
<% loop $getDocumentSets %>
<% include DocumentSet %>
<% end_loop %>
</div>
<% end_if %>

View File

@ -1,8 +0,0 @@
<% if $PageDocuments %>
<div class="documents">
<h3><%t DMSDocument.PLURALNAME "Documents" %></h3>
<% loop $PageDocuments %>
<% include Document %>
<% end_loop %>
</div>
<% end_if %>

View File

@ -0,0 +1,35 @@
<?php
class DMSDocumentSetTest extends SapphireTest
{
protected static $fixture_file = 'dmstest.yml';
/**
* Ensure that getDocuments is extensible
*/
public function testGetDocumentsIsExtensible()
{
DMSDocumentSet::add_extension('StubRelatedDocumentExtension');
$set = new DMSDocumentSet;
$documents = $set->getDocuments();
$this->assertCount(1, $documents);
$this->assertSame('Extended', $documents->first()->Filename);
}
public function testRelations()
{
$s1 = $this->objFromFixture('SiteTree', 's1');
$s2 = $this->objFromFixture('SiteTree', 's2');
$s4 = $this->objFromFixture('SiteTree', 's4');
$ds1 = $this->objFromFixture('DMSDocumentSet', 'ds1');
$ds2 = $this->objFromFixture('DMSDocumentSet', 'ds2');
$ds3 = $this->objFromFixture('DMSDocumentSet', 'ds3');
$this->assertCount(0, $s4->getDocumentSets(), 'Page 4 has no document sets associated');
$this->assertCount(2, $s1->getDocumentSets(), 'Page 1 has 2 document sets');
$this->assertEquals(array($ds1->ID, $ds2->ID), $s1->getDocumentSets()->column('ID'));
}
}

View File

@ -19,150 +19,6 @@ class DMSDocumentTest extends SapphireTest
self::$is_running_test = $this->originalIsRunningTest;
}
public function testPageRelations()
{
$s1 = $this->objFromFixture('SiteTree', 's1');
$s2 = $this->objFromFixture('SiteTree', 's2');
$s3 = $this->objFromFixture('SiteTree', 's3');
$s4 = $this->objFromFixture('SiteTree', 's4');
$s5 = $this->objFromFixture('SiteTree', 's5');
$s6 = $this->objFromFixture('SiteTree', 's6');
$d1 = $this->objFromFixture('DMSDocument', 'd1');
$pages = $d1->Pages();
$pagesArray = $pages->toArray();
$this->assertEquals($pagesArray[0]->ID, $s1->ID, 'Page 1 associated correctly');
$this->assertEquals($pagesArray[1]->ID, $s2->ID, 'Page 2 associated correctly');
$this->assertEquals($pagesArray[2]->ID, $s3->ID, 'Page 3 associated correctly');
$this->assertEquals($pagesArray[3]->ID, $s4->ID, 'Page 4 associated correctly');
$this->assertEquals($pagesArray[4]->ID, $s5->ID, 'Page 5 associated correctly');
$this->assertEquals($pagesArray[5]->ID, $s6->ID, 'Page 6 associated correctly');
}
public function testAddPageRelation()
{
$s1 = $this->objFromFixture('SiteTree', 's1');
$s2 = $this->objFromFixture('SiteTree', 's2');
$s3 = $this->objFromFixture('SiteTree', 's3');
$doc = new DMSDocument();
$doc->Filename = 'test file';
$doc->Folder = '0';
$doc->write();
$doc->addPage($s1);
$doc->addPage($s2);
$doc->addPage($s3);
$pages = $doc->Pages();
$pagesArray = $pages->toArray();
$this->assertEquals($pagesArray[0]->ID, $s1->ID, 'Page 1 associated correctly');
$this->assertEquals($pagesArray[1]->ID, $s2->ID, 'Page 2 associated correctly');
$this->assertEquals($pagesArray[2]->ID, $s3->ID, 'Page 3 associated correctly');
$doc->removePage($s1);
$pages = $doc->Pages();
$pagesArray = $pages->toArray(); // Page 1 is missing
$this->assertEquals($pagesArray[0]->ID, $s2->ID, 'Page 2 still associated correctly');
$this->assertEquals($pagesArray[1]->ID, $s3->ID, 'Page 3 still associated correctly');
$documents = $s2->Documents();
$documentsArray = $documents->toArray();
$this->assertDOSContains(
array(
array('Filename' => $doc->Filename)
),
$documentsArray,
'Document associated with page'
);
$doc->removeAllPages();
$pages = $doc->Pages();
$this->assertEquals($pages->Count(), 0, 'All pages removed');
$documents = $s2->Documents();
$documentsArray = $documents->toArray();
$this->assertNotContains($doc, $documentsArray, 'Document no longer associated with page');
}
public function testDeletingPageWithAssociatedDocuments()
{
$s1 = $this->objFromFixture('SiteTree', 's1');
$s2 = $this->objFromFixture('SiteTree', 's2');
$s2->publish('Stage', 'Live');
$s2ID = $s2->ID;
$doc = new DMSDocument();
$doc->Filename = 'delete test file';
$doc->Folder = '0';
$doc->write();
$doc->addPage($s1);
$doc->addPage($s2);
$s1->delete();
$documents = DataObject::get("DMSDocument", "\"Filename\" = 'delete test file'", false);
$this->assertEquals(
$documents->Count(),
'1',
"Deleting one of the associated page doesn't affect the single document we created"
);
$s2->delete();
$documents = DataObject::get("DMSDocument", "\"Filename\" = 'delete test file'");
$this->assertEquals(
$documents->Count(),
'1',
"Deleting a page from draft stage doesn't delete the associated docs,"
. "even if it's the last page they're associated with"
);
$s2 = Versioned::get_one_by_stage('SiteTree', 'Live', sprintf('"SiteTree"."ID" = %d', $s2ID));
$s2->doDeleteFromLive();
$documents = DataObject::get("DMSDocument", "\"Filename\" = 'delete test file'");
$this->assertEquals(
$documents->Count(),
'0',
'However, deleting the live version of the last page that a document is '
. 'associated with causes that document to be deleted as well'
);
}
public function testUnpublishPageWithAssociatedDocuments()
{
$s2 = $this->objFromFixture('SiteTree', 's2');
$s2->publish('Stage', 'Live');
$s2ID = $s2->ID;
$doc = new DMSDocument();
$doc->Filename = 'delete test file';
$doc->Folder = '0';
$doc->write();
$doc->addPage($s2);
$s2->doDeleteFromLive();
$documents = DataObject::get("DMSDocument", "\"Filename\" = 'delete test file'");
$this->assertEquals(
$documents->Count(),
'1',
"Deleting a page from live stage doesn't delete the associated docs,"
. "even if it's the last page they're associated with"
);
$s2 = Versioned::get_one_by_stage('SiteTree', 'Stage', sprintf('"SiteTree"."ID" = %d', $s2ID));
$s2->delete();
$documents = DataObject::get("DMSDocument", "\"Filename\" = 'delete test file'");
$this->assertEquals(
$documents->Count(),
'0',
'However, deleting the draft version of the last page that a document is '
. 'associated with causes that document to be deleted as well'
);
}
public function testDefaultDownloadBehabiourCMSFields()
{
$document = singleton('DMSDocument');
@ -364,4 +220,15 @@ class DMSDocumentTest extends SapphireTest
$doc3 = $this->objFromFixture('DMSDocument', 'doc-anyone');
$this->assertEquals('', $doc3->getPermissionDeniedReason());
}
/**
* Ensure that all pages that a document belongs to (via many document sets) can be retrieved in one list
*/
public function testGetRelatedPages()
{
$document = $this->objFromFixture('DMSDocument', 'd1');
$result = $document->getRelatedPages();
$this->assertCount(3, $result, 'Document 1 is related to 3 Pages');
$this->assertSame(array('s1', 's2', 's3'), $result->column('URLSegment'));
}
}

View File

@ -161,7 +161,7 @@ class DMSEmbargoTest extends SapphireTest
$doc->Folder = "0";
$dID = $doc->write();
$doc->addPage($s1);
$s1->getDocumentSets()->first()->getDocuments()->add($doc);
$s1->publish('Stage', 'Live');
$s1->doPublish();

View File

@ -24,24 +24,24 @@ class DMSGridFieldAddNewButtonTest extends SapphireTest
}
/**
* Test that when no page ID is present then it is not added to the URL for "add document"
* Test that when no document set ID is present then it is not added to the URL for "add document"
*/
public function testNoPageIdInAddUrlWhenNotProvided()
{
$fragments = $this->button->getHTMLFragments($this->gridField);
$result = array_pop($fragments)->getValue();
$this->assertNotContains('?ID', $result);
$this->assertNotContains('?dsid', $result);
}
/**
* Test that when a page ID is provided, it is added onto the "add document" link
* Test that when a document set ID is provided, it is added onto the "add document" link
*/
public function testPageIdAddedToLinkWhenProvided()
{
$this->button->setPageId(123);
$this->button->setDocumentSetId(123);
$fragments = $this->button->getHTMLFragments($this->gridField);
$result = array_pop($fragments)->getValue();
$this->assertContains('?ID=123', $result);
$this->assertContains('?dsid=123', $result);
}
}

View File

@ -1,7 +1,7 @@
<?php
class DMSTest extends FunctionalTest
{
protected $usesDatabase = true;
protected static $fixture_file = 'dmstest.yml';
/**
* Stub PDF files for testing
@ -16,6 +16,11 @@ class DMSTest extends FunctionalTest
public static $dmsFolderOld;
public static $dmsFolderSizeOld;
/**
* @var DMS
*/
protected $dms;
public function setUp()
{
parent::setUp();
@ -28,6 +33,8 @@ class DMSTest extends FunctionalTest
//clear out the test folder (in case a broken test doesn't delete it)
$this->delete(BASE_PATH . DIRECTORY_SEPARATOR . 'dms-assets-test-1234');
$this->dms = DMS::inst();
}
public function tearDown()
@ -82,10 +89,8 @@ class DMSTest extends FunctionalTest
public function testDMSStorage()
{
$dms = DMS::inst();
$file = self::$testFile;
$document = $dms->storeDocument($file);
$document = $this->dms->storeDocument($file);
$this->assertNotNull($document, "Document object created");
$this->assertTrue(
@ -100,13 +105,11 @@ class DMSTest extends FunctionalTest
public function testDMSFolderSpanning()
{
DMS::$dmsFolderSize = 5;
$dms = DMS::inst();
$file = self::$testFile;
$documents = array();
for ($i = 0; $i <= 16; $i++) {
$document = $dms->storeDocument($file);
$document = $this->dms->storeDocument($file);
$this->assertNotNull($document, "Document object created on run number: $i");
$this->assertTrue(file_exists($document->getFullPath()));
$documents[] = $document;
@ -131,10 +134,8 @@ class DMSTest extends FunctionalTest
public function testReplaceDocument()
{
$dms = DMS::inst();
// Store the first document
$document = $dms->storeDocument(self::$testFile);
$document = $this->dms->storeDocument(self::$testFile);
$document->Title = "My custom title";
$document->Description = "My custom description";
$document->write();
@ -158,4 +159,39 @@ class DMSTest extends FunctionalTest
$this->assertEquals("My custom title", $document->Title, "Custom title not modified");
$this->assertEquals("My custom description", $document->Description, "Custom description not modified");
}
/**
* Test that documents can be returned by a given page
*/
public function testGetByPageWithoutEmbargoes()
{
$pageWithEmbargoes = $this->objFromFixture('SiteTree', 's3');
$documents = $this->dms->getByPage($pageWithEmbargoes);
// Fixture: 3 documents in set, 1 is embargoed
$this->assertCount(2, $documents, 'Embargoed documents are excluded by default');
$this->assertContainsOnlyInstancesOf('DMSDocument', $documents);
}
/**
* Test that embargoed documents are excluded from getByPage
*/
public function testGetByPageWithEmbargoedDocuments()
{
$pageWithEmbargoes = $this->objFromFixture('SiteTree', 's3');
$documents = $this->dms->getByPage($pageWithEmbargoes, true);
// Fixture: 3 documents in set, 1 is embargoed
$this->assertCount(3, $documents, 'Embargoed documents can be included');
$this->assertContainsOnlyInstancesOf('DMSDocument', $documents);
}
/**
* Test that document sets can be retrieved for a given page
*/
public function testGetDocumentSetsByPage()
{
$page = $this->objFromFixture('SiteTree', 's1');
$sets = $this->dms->getDocumentSetsByPage($page);
$this->assertCount(2, $sets);
$this->assertContainsOnlyInstancesOf('DMSDocumentSet', $sets);
}
}

View File

@ -3,16 +3,38 @@
class StubRelatedDocumentExtension extends DataExtension implements TestOnly
{
/**
* Push a fixed array entry into the datalist for extensibility testing
* For method {@link DMSDocument::getRelatedDocuments}
*
* @param ArrayList $relatedDocuments
* @return ArrayList
*/
public function updateRelatedDocuments(ArrayList $relatedDocuments)
public function updateRelatedDocuments($relatedDocuments)
{
$relatedDocuments->push($this->getFakeDocument());
return $relatedDocuments;
}
/**
* For method {@link DMSDocumentSet::getDocuments}
*
* @param ArrayList $relatedDocuments
* @return ArrayList
*/
public function updateDocuments($documents)
{
$documents->push($this->getFakeDocument());
return $documents;
}
/**
* Return a dummy document for testing purposes
*
* @return DMSDocument
*/
protected function getFakeDocument()
{
$fakeDocument = new DMSDocument;
$fakeDocument->Filename = 'Extended';
$relatedDocuments->push($fakeDocument);
return $relatedDocuments;
return $fakeDocument;
}
}

View File

@ -1,7 +1,11 @@
DMSDocumentSet:
ds1:
Title: Document Set 1
SiteTree:
s1:
Title: testPage1
URLSegment: s1
DocumentSets: =>DMSDocumentSet.ds1
s2:
Title: testPage2
URLSegment: s2

View File

@ -1,21 +1,21 @@
SiteTree:
s1:
Title: testPage1
Title: testPage1 has document sets
URLSegment: s1
s2:
Title: testPage2
Title: testPage2 has document sets
URLSegment: s2
s3:
Title: testPage3
Title: testPage3 has document sets with embargoed docs
URLSegment: s3
s4:
Title: testPage4
Title: testPage4 has no sets
URLSegment: s4
s5:
Title: testPage5
Title: testPage5 has no sets
URLSegment: s5
s6:
Title: testPage6
Title: testPage6 has no sets
URLSegment: s6
DMSTag:
t1:
@ -43,17 +43,30 @@ Group:
cable-guy:
Code: cable-guy
Title: "Cable Guy"
DMSDocumentSet:
ds1:
Title: Document Set 1
Page: =>SiteTree.s1
ds2:
Title: Document Set 2
Page: =>SiteTree.s1
ds3:
Title: Document Set 3
Page: =>SiteTree.s2
ds4:
Title: Set containing embargoed documents
Page: =>SiteTree.s3
DMSDocument:
d1:
Filename: test-file-file-doesnt-exist-1
Folder: 5
Tags: =>DMSTag.t1, =>DMSTag.t2, =>DMSTag.t3, =>DMSTag.t4
Pages: =>SiteTree.s1, =>SiteTree.s2, =>SiteTree.s3, =>SiteTree.s4, =>SiteTree.s5, =>SiteTree.s6
Sets: =>DMSDocumentSet.ds1, =>DMSDocumentSet.ds2, =>DMSDocumentSet.ds3, =>DMSDocumentSet.ds4
d2:
Filename: test-file-file-doesnt-exist-2
Folder: 5
Tags: =>DMSTag.t5, =>DMSTag.t6
Pages: =>SiteTree.s5, =>SiteTree.s6
Sets: =>DMSDocumentSet.ds1, =>DMSDocumentSet.ds2, =>DMSDocumentSet.ds3, =>DMSDocumentSet.ds4
document_with_relations:
Filename: file-with-relations
Folder: 5
@ -80,6 +93,11 @@ DMSDocument:
Pages: =>SiteTree.s1, =>SiteTree.s2, =>SiteTree.s3, =>SiteTree.s4, =>SiteTree.s5, =>SiteTree.s6
ViewerGroups: =>Group.content-author
EditorGroups: =>Group.content-author
embargoed_document1:
Filename: foobar
EmbargoedIndefinitely: true
Folder: 5
Sets: =>DMSDocumentSet.ds4
Member:
editor:
Name: editor