Merge pull request #124 from robbieaverill/feature/tagging

API Remove DMSTag, replace with extension for taxonomy module
This commit is contained in:
Franco Springveldt 2017-05-18 22:49:44 +12:00 committed by GitHub
commit e8d46a90a5
28 changed files with 318 additions and 774 deletions

View File

@ -19,10 +19,10 @@ Additionally, documents are stored and managed as part of a page instead of away
* Relation of documents to other documents * Relation of documents to other documents
* Management and upload of documents within a page context in the CMS * Management and upload of documents within a page context in the CMS
* Metadata management through the powerful `GridField` and `UploadField` core APIs * Metadata management through the powerful `GridField` and `UploadField` core APIs
* Configurable tags for documents
* Download via SilverStripe controller (rather than filesystem URLs) * Download via SilverStripe controller (rather than filesystem URLs)
* Access control based on PHP logic, and page relations * Access control based on PHP logic, and page relations
* Replacement of existing files * Replacement of existing files
* Tagging via the [taxonomy module](https://github.com/silverstripe/silverstripe-taxonomy) if installed
## Documentation ## Documentation
@ -32,11 +32,11 @@ For information on configuring and using this module, please see [the documentat
* PHP 5.3 with the "fileinfo" module (or alternatively the "whereis" and "file" Unix commands) * PHP 5.3 with the "fileinfo" module (or alternatively the "whereis" and "file" Unix commands)
* SilverStripe framework/CMS ^3.5 * SilverStripe framework/CMS ^3.5
* [Taxonomy](https://github.com/silverstripe/silverstripe-taxonomy) ^1.2 (for tagging)
* (optional) [Pagination of Documents in the CMS](https://github.com/silverstripe-big-o/gridfieldpaginatorwithshowall) * (optional) [Pagination of Documents in the CMS](https://github.com/silverstripe-big-o/gridfieldpaginatorwithshowall)
* (optional) [Sorting of Documents in the CMS](https://github.com/silverstripe-big-o/SortableGridField) * (optional) [Sorting of Documents in the CMS](https://github.com/silverstripe-big-o/SortableGridField)
* (optional) [Full text search of Documents](https://github.com/silverstripe-big-o/silverstripe-fulltextsearch) * (optional) [Full text search of Documents](https://github.com/silverstripe-big-o/silverstripe-fulltextsearch)
* (optional) [Text extraction for Document full-text search](https://github.com/silverstripe-big-o/silverstripe-textextraction) * (optional) [Text extraction for Document full-text search](https://github.com/silverstripe-big-o/silverstripe-textextraction)
* (optional) [Tags](https://github.com/tubbs/silverstripe-dms-simple-tags)
## Contributing ## Contributing

View File

@ -1,16 +1,16 @@
DMSDocument: DMSDocument:
searchable_fields: searchable_fields:
Title: Title:
title: "Document title matches ..." title: Document title matches
Description: Description:
title: "Document summary matches ..." title: Document summary matches
CreatedByID: CreatedByID:
title: 'Document created by ...' title: Document created by
field: 'ListboxField' field: ListboxField
filter: 'ExactMatchFilter' filter: ExactMatchFilter
LastEditedByID: LastEditedByID:
title: 'Document last changed by ...' title: Document last changed by
field: 'ListboxField' field: ListboxField
filter: 'ExactMatchFilter' filter: ExactMatchFilter
Filename: Filename:
title: 'File name' title: File name

25
_config/taxonomy.yml Normal file
View File

@ -0,0 +1,25 @@
---
Name: dmstaxonomy
Only:
moduleexists: taxonomy
---
TaxonomyType:
extensions:
- DMSTaxonomyTypeExtension
DMSDocument:
extensions:
- DMSDocumentTaxonomyExtension
# Add query builder filter for tags
searchable_fields:
Tags.ID:
title: Document has tags
field: ListboxField
filter: ExactMatchFilter
DMSTaxonomyTypeExtension:
# Referenced to filter taxonomy terms for DMS Documents.
# To change, update the default_records array too.
default_record_name: Document
default_records:
- Document

View File

@ -121,27 +121,6 @@ class DMS extends Object implements DMSInterface
return $doc; return $doc;
} }
/**
*
* Returns a number of Document objects based on the a search by tags. You can search by category alone,
* by tag value alone, or by both. I.e:
*
* <code>
* getByTag("fruits", null);
* getByTag(null, "banana");
* getByTag("fruits", "banana");
* </code>
*
* @param null $category The metadata category to search for
* @param null $value The metadata value to search for
* @param bool $showEmbargoed Boolean that specifies if embargoed documents should be included in results
* @return DocumentInterface
*/
public function getByTag($category = null, $value = null, $showEmbargoed = false)
{
// TODO: Implement getByTag() method.
}
/** /**
* Returns a number of Document objects that match a full-text search of the Documents and their contents * Returns a number of Document objects that match a full-text search of the Documents and their contents
* (if contents is searchable and compatible search module is installed - e.g. FullTextSearch module) * (if contents is searchable and compatible search module is installed - e.g. FullTextSearch module)

View File

@ -1,121 +0,0 @@
<?php
/**
* This class is a {@link GridField} component that adds a delete a DMS document.
*
* This component also supports unlinking a relation instead of deleting the object. By default it unlinks, but if
* this is the last reference to a specific document, it warns the user that continuing with the operation will
* delete the document completely.
*
* <code>
* $action = new GridFieldDeleteAction(); // delete objects permanently
* </code>
*
* @package dms
* @subpackage cms
*/
class DMSGridFieldDeleteAction extends GridFieldDeleteAction implements
GridField_ColumnProvider,
GridField_ActionProvider
{
/**
*
* @param GridField $gridField
* @param DataObject $record
* @param string $columnName
* @return string - the HTML for the column
*/
public function getColumnContent($gridField, $record, $columnName)
{
if ($this->removeRelation) {
$field = GridField_FormAction::create(
$gridField,
'UnlinkRelation' . $record->ID,
false,
"unlinkrelation",
array('RecordID' => $record->ID)
)
->addExtraClass('gridfield-button-unlink')
->setAttribute('title', _t('GridAction.UnlinkRelation', "Unlink"))
->setAttribute('data-icon', 'chain--minus');
} else {
if (!$record->canDelete()) {
return '';
}
$field = GridField_FormAction::create(
$gridField,
'DeleteRecord' . $record->ID,
false,
"deleterecord",
array('RecordID' => $record->ID)
)
->addExtraClass('gridfield-button-delete')
->setAttribute('title', _t('GridAction.Delete', "Delete"))
->setAttribute('data-icon', 'cross-circle')
->setDescription(_t('GridAction.DELETE_DESCRIPTION', 'Delete'));
}
// Add a class to the field to if it is the last gridfield in the list
$numberOfRelations = $record->getRelatedPages()->count();
$field
// Add a new class for custom JS to handle the delete action
->addExtraClass('dms-delete')
// Add the number of pages attached to this field as a data-attribute
->setAttribute('data-pages-count', $numberOfRelations)
// Remove the base gridfield behaviour
->removeExtraClass('gridfield-button-delete');
// Set a class telling JS what kind of warning to display when clicking the delete button
if ($numberOfRelations > 1) {
$field->addExtraClass('dms-delete-link-only');
} else {
$field->addExtraClass('dms-delete-last-warning');
}
// Set a class to show if the document is hidden
if ($record->isHidden()) {
$field->addExtraClass('dms-document-hidden');
}
return $field->Field();
}
/**
* Handle the actions and apply any changes to the GridField
*
* @param GridField $gridField
* @param string $actionName
* @param mixed $arguments
* @param array $data - form data
* @return void
* @throws ValidationException If the current user doesn't have permission to delete the record
*/
public function handleAction(GridField $gridField, $actionName, $arguments, $data)
{
if ($actionName == 'deleterecord' || $actionName == 'unlinkrelation') {
$item = $gridField->getList()->byID($arguments['RecordID']);
if (!$item) {
return;
}
if ($actionName == 'deleterecord' && !$item->canDelete()) {
throw new ValidationException(
_t('GridFieldAction_Delete.DeletePermissionsFailure', "No delete permissions"),
0
);
}
$delete = false;
if ($item->getRelatedPages()->count() <= 1) {
$delete = true;
}
// Remove the relation
$gridField->getList()->remove($item);
if ($delete) {
// Delete the document
$item->delete();
}
}
}
}

View File

@ -48,7 +48,7 @@ class DMSUploadField extends UploadField
// Relate to the underlying document set being edited. // Relate to the underlying document set being edited.
// Not applicable when editing the document itself and replacing it, or uploading from the ModelAdmin // Not applicable when editing the document itself and replacing it, or uploading from the ModelAdmin
if ($record instanceof DMSDocumentSet) { if ($record instanceof DMSDocumentSet) {
$record->Documents()->add($doc, array('BelongsToSet' => 1)); $record->Documents()->add($doc, array('ManuallyAdded' => 1));
} }
return $doc; return $doc;

View File

@ -0,0 +1,55 @@
<?php
class DMSDocumentTaxonomyExtension extends DataExtension
{
private static $many_many = array(
'Tags' => 'TaxonomyTerm'
);
/**
* Push an autocomplete dropdown for the available tags in documents
*
* @param FieldList $fields
*/
public function updateCMSFields(FieldList $fields)
{
$tags = $this->getAllTagsMap();
$tagField = ListboxField::create('Tags', _t('DMSDocumentTaxonomyExtension.TAGS', 'Tags'))
->setMultiple(true)
->setSource($tags);
if (empty($tags)) {
$tagField->setAttribute('data-placeholder', _t('DMSDocumentTaxonomyExtension.NOTAGS', 'No tags found'));
}
$fields->insertAfter('Description', $tagField);
}
/**
* Return an array of all the available tags that a document can use. Will return a list containing a taxonomy
* term's entire hierarchy, e.g. "Photo > Attribute > Density > High"
*
* @return array
*/
public function getAllTagsMap()
{
$tags = TaxonomyTerm::get()->filter(
'Type.Name:ExactMatch',
Config::inst()->get('DMSTaxonomyTypeExtension', 'default_record_name')
);
$map = array();
foreach ($tags as $tag) {
$nameParts = array($tag->Name);
$currentTag = $tag;
while ($currentTag->Parent() && $currentTag->Parent()->exists()) {
array_unshift($nameParts, $currentTag->Parent()->Name);
$currentTag = $currentTag->Parent();
}
$map[$tag->ID] = implode(' > ', $nameParts);
}
return $map;
}
}

View File

@ -0,0 +1,28 @@
<?php
/**
* Creates default taxonomy type records if they don't exist already
*/
class DMSTaxonomyTypeExtension extends DataExtension
{
/**
* Create default taxonomy type records. Add records via YAML configuration (see taxonomy.yml):
*
* <code>
* DMSTaxonomyTypeExtension:
* default_records:
* - Document
* - PrivateDocument
* </code>
*/
public function requireDefaultRecords()
{
$records = (array) Config::inst()->get(get_class($this), 'default_records');
foreach ($records as $name) {
$type = TaxonomyType::get()->filter('Name', $name)->first();
if (!$type) {
$type = TaxonomyType::create(array('Name' => $name));
$type->write();
}
}
}
}

View File

@ -6,47 +6,6 @@
*/ */
interface DMSDocumentInterface interface DMSDocumentInterface
{ {
/**
* 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
* 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
*/
public function addTag($category, $value, $multiValue = true);
/**
* Fetches all tags associated with this DMSDocument within a given category. If a value is specified this method
* tries to fetch that specific tag.
* @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
*/
public function getTagsList($category, $value = null);
/**
* 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.
* @param $category Category to remove (required)
* @param null $value Value to remove (optional)
* @return null
*/
public function removeTag($category, $value = null);
/**
* Deletes all tags associated with this DMSDocument.
* @return null
*/
public function removeAllTags();
/** /**
* Returns a link to download this DMSDocument from the DMS store * Returns a link to download this DMSDocument from the DMS store
* @return String * @return String

View File

@ -27,24 +27,6 @@ interface DMSInterface
*/ */
public function storeDocument($file); public function storeDocument($file);
/**
*
* Returns a number of Document objects based on the a search by tags. You can search by category alone,
* by tag value alone, or by both. I.e:
*
* <code>
* getByTag("fruits", null);
* getByTag(null, "banana");
* getByTag("fruits", "banana");
* </code>
* @abstract
* @param null $category The metadata category to search for
* @param null $value The metadata value to search for
* @param bool $showEmbargoed Boolean that specifies if embargoed documents should be included in results
* @return DMSDocumentInterface
*/
public function getByTag($category = null, $value = null, $showEmbargoed = false);
/** /**
* Returns a number of Document objects that match a full-text search of the Documents and their contents * Returns a number of Document objects that match a full-text search of the Documents and their contents
* (if contents is searchable and compatible search module is installed - e.g. FullTextSearch module) * (if contents is searchable and compatible search module is installed - e.g. FullTextSearch module)

View File

@ -17,7 +17,6 @@
* @property Enum CanEditType Enum('LoggedInUsers, OnlyTheseUsers', 'LoggedInUsers') * @property Enum CanEditType Enum('LoggedInUsers, OnlyTheseUsers', 'LoggedInUsers')
* *
* @method ManyManyList RelatedDocuments * @method ManyManyList RelatedDocuments
* @method ManyManyList Tags
* @method ManyManyList ViewerGroups * @method ManyManyList ViewerGroups
* @method ManyManyList EditorGroups * @method ManyManyList EditorGroups
* *
@ -56,7 +55,6 @@ class DMSDocument extends DataObject implements DMSDocumentInterface
private static $many_many = array( private static $many_many = array(
'RelatedDocuments' => 'DMSDocument', 'RelatedDocuments' => 'DMSDocument',
'Tags' => 'DMSTag',
'ViewerGroups' => 'Group', 'ViewerGroups' => 'Group',
'EditorGroups' => 'Group', 'EditorGroups' => 'Group',
); );
@ -65,7 +63,7 @@ class DMSDocument extends DataObject implements DMSDocumentInterface
'ID' => 'ID', 'ID' => 'ID',
'Title' => 'Title', 'Title' => 'Title',
'FilenameWithoutID' => 'Filename', 'FilenameWithoutID' => 'Filename',
'LastEdited' => 'LastEdited' 'LastEdited' => 'Last Edited'
); );
private static $singular_name = 'Document'; private static $singular_name = 'Document';
@ -244,173 +242,6 @@ class DMSDocument extends DataObject implements DMSDocumentInterface
return $this; return $this;
} }
/**
* 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 string $category of a metadata category to add (required)
* @param string $value of a metadata value to add (required)
* @param bool $multiValue Boolean that determines if the category is
* multi-value or single-value (optional)
*
* @return DMSDocument
*/
public function addTag($category, $value, $multiValue = true)
{
if ($multiValue) {
//check for a duplicate tag, don't add the duplicate
$currentTag = $this->Tags()->filter(array('Category' => $category, 'Value' => $value));
if ($currentTag->Count() == 0) {
//multi value tag
$tag = new DMSTag();
$tag->Category = $category;
$tag->Value = $value;
$tag->MultiValue = true;
$tag->write();
$tag->Documents()->add($this);
} else {
//add the relation between the tag and document
foreach ($currentTag as $tagObj) {
$tagObj->Documents()->add($this);
}
}
} else {
//single value tag
$currentTag = $this->Tags()->filter(array('Category' => $category));
$tag = null;
if ($currentTag->Count() == 0) {
//create the single-value tag
$tag = new DMSTag();
$tag->Category = $category;
$tag->Value = $value;
$tag->MultiValue = false;
$tag->write();
} else {
//update the single value tag
$tag = $currentTag->first();
$tag->Value = $value;
$tag->MultiValue = false;
$tag->write();
}
// regardless of whether we created a new tag or are just updating an
// existing one, add the relation
$tag->Documents()->add($this);
}
return $this;
}
/**
* @param string $category
* @param string $value
*
* @return DataList
*/
protected function getTagsObjects($category, $value = null)
{
$valueFilter = array("Category" => $category);
if (!empty($value)) {
$valueFilter['Value'] = $value;
}
$tags = $this->Tags()->filter($valueFilter);
return $tags;
}
/**
* Fetches all tags associated with this DMSDocument within a given
* category. If a value is specified this method tries to fetch that
* specific tag.
*
* @param string $category metadata category to get
* @param string $value value of the tag to get
*
* @return array Strings of all the tags or null if there is no match found
*/
public function getTagsList($category, $value = null)
{
$tags = $this->getTagsObjects($category, $value);
$returnArray = null;
if ($tags->Count() > 0) {
$returnArray = array();
foreach ($tags as $t) {
$returnArray[] = $t->Value;
}
}
return $returnArray;
}
/**
* 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 string $category Category to remove
* @param string $value Value to remove
*
* @return DMSDocument
*/
public function removeTag($category, $value = null)
{
$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
if ($documentList->Count() == 0) {
$t->delete();
}
}
}
return $this;
}
/**
* Deletes all tags associated with this Document.
*
* @return DMSDocument
*/
public function removeAllTags()
{
$allTags = $this->Tags();
foreach ($allTags as $tag) {
$documentlist = $tag->Documents();
$documentlist->remove($this);
if ($tag->Documents()->Count() == 0) {
$tag->delete();
}
}
return $this;
}
/** /**
* Returns a link to download this document from the DMS store. * Returns a link to download this document from the DMS store.
* Alternatively a basic javascript alert will be shown should the user not have view permissions. An extension * Alternatively a basic javascript alert will be shown should the user not have view permissions. An extension
@ -717,17 +548,13 @@ class DMSDocument extends DataObject implements DMSDocumentInterface
} }
/** /**
* Deletes the DMSDocument, its underlying file, as well as any tags related * Deletes the DMSDocument and its underlying file. Also calls the parent DataObject's delete method in
* to this DMSDocument. Also calls the parent DataObject's delete method in
* order to complete an cascade. * order to complete an cascade.
* *
* @return void * @return void
*/ */
public function delete() public function delete()
{ {
// remove tags
$this->removeAllTags();
// delete the file (and previous versions of files) // delete the file (and previous versions of files)
$filesToDelete = array(); $filesToDelete = array();
$storageFolder = $this->getStorageFolder(); $storageFolder = $this->getStorageFolder();
@ -1383,6 +1210,9 @@ class DMSDocument extends DataObject implements DMSDocumentInterface
// Ensure that current document doesn't get returned in the autocompleter // Ensure that current document doesn't get returned in the autocompleter
$addExisting->setSearchList($this->getRelatedDocumentsForAutocompleter()); $addExisting->setSearchList($this->getRelatedDocumentsForAutocompleter());
// Restrict search fields to specific fields only
$addExisting->setSearchFields(array('Title', 'Filename'));
$this->extend('updateRelatedDocumentsGridField', $gridField); $this->extend('updateRelatedDocumentsGridField', $gridField);
return $gridField; return $gridField;

View File

@ -26,7 +26,9 @@ class DMSDocumentSet extends DataObject
private static $many_many_extraFields = array( private static $many_many_extraFields = array(
'Documents' => array( 'Documents' => array(
'BelongsToSet' => 'Boolean(1)', // Flag indicating if a document was added directly to a set - in which case it is set - or added via the query-builder. // Flag indicating if a document was added directly to a set - in which case it is set - or added
// via the query-builder.
'ManuallyAdded' => 'Boolean(1)',
), ),
); );
@ -89,7 +91,7 @@ class DMSDocumentSet extends DataObject
new GridFieldDataColumns(), new GridFieldDataColumns(),
new GridFieldEditButton(), new GridFieldEditButton(),
// Special delete dialog to handle custom behaviour of unlinking and deleting // Special delete dialog to handle custom behaviour of unlinking and deleting
new DMSGridFieldDeleteAction(), new GridFieldDeleteAction(true),
new GridFieldDetailForm() new GridFieldDetailForm()
); );
@ -115,7 +117,7 @@ class DMSDocumentSet extends DataObject
->setFieldFormatting( ->setFieldFormatting(
array( array(
'FilenameWithoutID' => '<a target=\'_blank\' class=\'file-url\' href=\'$Link\'>$FilenameWithoutID</a>', 'FilenameWithoutID' => '<a target=\'_blank\' class=\'file-url\' href=\'$Link\'>$FilenameWithoutID</a>',
'BelongsToSet' => function ($value) { 'ManuallyAdded' => function ($value) {
if ($value) { if ($value) {
return _t('DMSDocumentSet.MANUAL', 'Manually'); return _t('DMSDocumentSet.MANUAL', 'Manually');
} }
@ -197,24 +199,24 @@ class DMSDocumentSet extends DataObject
$dmsDocFields = $doc->scaffoldSearchFields(array('fieldClasses' => true)); $dmsDocFields = $doc->scaffoldSearchFields(array('fieldClasses' => true));
$membersMap = Member::get()->map('ID', 'Name')->toArray(); $membersMap = Member::get()->map('ID', 'Name')->toArray();
asort($membersMap); asort($membersMap);
foreach ($dmsDocFields as $field) { foreach ($dmsDocFields as $field) {
// Apply field customisations where necessary if ($field instanceof ListboxField) {
if (in_array($field->getName(), array('CreatedByID', 'LastEditedByID', 'LastEditedByID'))) { $map = ($field->getName() === 'Tags__ID') ? $doc->getAllTagsMap() : $membersMap;
/** @var ListboxField $field */ $field->setMultiple(true)->setSource($map);
$field->setMultiple(true)->setSource($membersMap);
} }
} }
$keyValPairs = JsonField::create('KeyValuePairs', $dmsDocFields->toArray()); $keyValPairs = JsonField::create('KeyValuePairs', $dmsDocFields->toArray());
// Now lastly add the sort fields // Now lastly add the sort fields
$sortedBy = FieldGroup::create('SortedBy', array( $sortedBy = FieldGroup::create('SortedBy', array(
DropdownField::create('SortBy', '', array( DropdownField::create('SortBy', '', array(
'LastEdited' => 'Last changed', 'LastEdited' => 'Last changed',
'Created' => 'Created', 'Created' => 'Created',
'Title' => 'Document title', 'Title' => 'Document title',
), 'LastEdited'), ), 'LastEdited'),
DropdownField::create('SortByDirection', '', $this->dbObject('SortByDirection')->enumValues(), 'DESC'), DropdownField::create('SortByDirection', '', $this->dbObject('SortByDirection')->enumValues(), 'DESC'),
)); ));
$sortedBy->setTitle(_t('DMSDocumentSet.SORTED_BY', 'Sort the document set by:')); $sortedBy->setTitle(_t('DMSDocumentSet.SORTED_BY', 'Sort the document set by:'));
$fields->addFieldsToTab('Root.QueryBuilder', array($keyValPairs, $sortedBy)); $fields->addFieldsToTab('Root.QueryBuilder', array($keyValPairs, $sortedBy));
@ -230,40 +232,65 @@ class DMSDocumentSet extends DataObject
/** /**
* Retrieve a list of the documents in this set. An extension hook is provided before the result is returned. * Retrieve a list of the documents in this set. An extension hook is provided before the result is returned.
*
* @return ArrayList|null
*/ */
public function saveLinkedDocuments() public function saveLinkedDocuments()
{ {
// Documents that belong to just this set. if (empty($this->KeyValuePairs) || !$this->isChanged('KeyValuePairs')) {
/** @var ManyManyList $originals */ return;
$originals = $this->Documents();
if (!(empty($this->KeyValuePairs)) && $this->isChanged('KeyValuePairs')) {
$keyValuesPair = Convert::json2array($this->KeyValuePairs);
/** @var DMSDocument $dmsDoc */
$dmsDoc = singleton('DMSDocument');
$context = $dmsDoc->getDefaultSearchContext();
$sortBy = $this->SortBy ? $this->SortBy : 'LastEdited';
$sortByDirection = $this->SortByDirection ? $this->SortByDirection : 'DESC';
$sortedBy = sprintf('%s %s', $sortBy, $sortByDirection);
/** @var DataList $documents */
$documents = $context->getResults($keyValuesPair, $sortedBy);
$now = SS_Datetime::now()->Rfc2822();
$documents = $documents->where(
"\"EmbargoedIndefinitely\" = 0 AND ".
" \"EmbargoedUntilPublished\" = 0 AND ".
"(\"EmbargoedUntilDate\" IS NULL OR " .
"(\"EmbargoedUntilDate\" IS NOT NULL AND '{$now}' >= \"EmbargoedUntilDate\")) AND " .
"\"ExpireAtDate\" IS NULL OR (\"ExpireAtDate\" IS NOT NULL AND '{$now}' < \"ExpireAtDate\")"
);
// Remove all BelongsToSet as the rules have changed
$originals->removeByFilter('"BelongsToSet" = 0');
foreach ($documents as $document) {
$originals->add($document, array('BelongsToSet' => 0));
}
} }
$keyValuesPair = Convert::json2array($this->KeyValuePairs);
/** @var DMSDocument $dmsDoc */
$dmsDoc = singleton('DMSDocument');
$context = $dmsDoc->getDefaultSearchContext();
$sortBy = $this->SortBy ? $this->SortBy : 'LastEdited';
$sortByDirection = $this->SortByDirection ? $this->SortByDirection : 'DESC';
$sortedBy = sprintf('%s %s', $sortBy, $sortByDirection);
/** @var DataList $documents */
$documents = $context->getResults($keyValuesPair, $sortedBy);
$documents = $this->addEmbargoConditions($documents);
$documents = $this->addQueryBuilderSearchResults($documents);
}
/**
* Add embargo date conditions to a search query
*
* @param DataList $documents
* @return DataList
*/
protected function addEmbargoConditions(DataList $documents)
{
$now = SS_Datetime::now()->Rfc2822();
return $documents->where(
"\"EmbargoedIndefinitely\" = 0 AND "
. " \"EmbargoedUntilPublished\" = 0 AND "
. "(\"EmbargoedUntilDate\" IS NULL OR "
. "(\"EmbargoedUntilDate\" IS NOT NULL AND '{$now}' >= \"EmbargoedUntilDate\")) AND "
. "\"ExpireAtDate\" IS NULL OR (\"ExpireAtDate\" IS NOT NULL AND '{$now}' < \"ExpireAtDate\")"
);
}
/**
* Remove all ManuallyAdded = 0 original results and add in the new documents returned by the search context
*
* @param DataList $documents
* @return DataList
*/
protected function addQueryBuilderSearchResults(DataList $documents)
{
/** @var ManyManyList $originals Documents that belong to just this set. */
$originals = $this->Documents();
$originals->removeByFilter('"ManuallyAdded" = 0');
foreach ($documents as $document) {
$originals->add($document, array('ManuallyAdded' => 0));
}
return $originals;
} }
/** /**
@ -275,7 +302,7 @@ class DMSDocumentSet extends DataObject
{ {
return array_merge( return array_merge(
(array) DMSDocument::create()->config()->get('display_fields'), (array) DMSDocument::create()->config()->get('display_fields'),
array('BelongsToSet' => _t('DMSDocumentSet.ADDEDMETHOD', 'Added')) array('ManuallyAdded' => _t('DMSDocumentSet.ADDEDMETHOD', 'Added'))
); );
} }
} }

View File

@ -1,18 +0,0 @@
<?php
/**
* Hold a set of metadata category/value tags associated with a DMSDocument
*
* @package dms
*/
class DMSTag extends DataObject
{
private static $db = array(
'Category' => 'Varchar(1024)',
'Value' => 'Varchar(1024)',
'MultiValue' => 'Boolean(1)'
);
private static $belongs_many_many = array(
'Documents' => 'DMSDocument'
);
}

View File

@ -11,7 +11,8 @@
"require": { "require": {
"silverstripe/framework": "^3.5", "silverstripe/framework": "^3.5",
"silverstripe/cms": "^3.5", "silverstripe/cms": "^3.5",
"silverstripe-australia/gridfieldextensions": "^1.1.0" "silverstripe-australia/gridfieldextensions": "^1.1.0",
"silverstripe/taxonomy": "1.2.x-dev"
}, },
"extra": { "extra": {
"branch-alias": { "branch-alias": {

View File

@ -6,7 +6,8 @@ Documents now belong to "sets", which are attached to Pages. A Page can have man
many_many relationship with Documents. 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 to support When upgrading from 1.x to 2.x you will need to migrate the relationships from your Pages to Documents to support
having a Document Set intermediary (@todo Add a build task for this). having a Document Set intermediary. [See here](../migration/document-sets.md) for an example build task to help with
this process.
## API changes ## API changes
@ -29,6 +30,9 @@ having a Document Set intermediary (@todo Add a build task for this).
* `DMS::transform_file_to_file_path` made non-static, use `DMS::inst()->transformFileToFilePath()` instead * `DMS::transform_file_to_file_path` made non-static, use `DMS::inst()->transformFileToFilePath()` instead
* `DMS::create_storage_folder` made non-static, use `DMS::inst()->createStorageFolder()` instead * `DMS::create_storage_folder` made non-static, use `DMS::inst()->createStorageFolder()` instead
* `DMS::get_storage_folder` made non-static, use `DMS::inst()->getStorageFolder()` instead * `DMS::get_storage_folder` made non-static, use `DMS::inst()->getStorageFolder()` instead
* `DMSDocument::addTag`, `::getTagsList`, `::removeTag` and `::removeAllTags` removed from the `DMSDocument` and `DMSDocumentInterface`. Please use the ORM relationship created by applying the `DMSDocumentTaxonomyExtension` extension to `DMSDocument` instead.
* `DMSInterface::getByTag` removed from `DMSInterface` and `DMS`. Use ORM relationships from applying `DMSDocumentTaxonomyExtension` to `DMSDocument` instead.
* `DMSGridFieldDeleteAction` removed
## Template changes ## Template changes

View File

@ -8,7 +8,6 @@
* [Creating documents](creating-documents.md) * [Creating documents](creating-documents.md)
* [Download documents](download-documents.md) * [Download documents](download-documents.md)
* [Manage page relations](manage-page-relations.md) * [Manage page relations](manage-page-relations.md)
* [Manage tags](manage-tags.md)
* [Manage related documents](manage-related-documents.md) * [Manage related documents](manage-related-documents.md)
* [Building frontend assets](building-frontend-assets.md) * [Building frontend assets](building-frontend-assets.md)

View File

@ -1,32 +0,0 @@
# Manage Tags
## Find documents by tag
```php
$dms = DMS::getDMSInstance();
$docs = $dms->getByTag('priority', 'important');
```
## Add tag to existing document
```php
$doc = DMSDocument::get()->byID(99);
$doc->addTag('priority', 'low');
```
## Supports multiple values for tags
```php
$doc->addTag('category', 'keyboard');
$doc->addTag('category', 'input device');
```
## Removing tags
Removing tags is abstracted as well.
```php
$doc->removeTag('category', 'keyboard');
$doc->removeTag('category', 'input device');
$doc->removeAllTags();
```

View File

@ -1,125 +1,15 @@
(function ($) { (function ($) {
"use strict"; "use strict";
$.entwine('ss', function ($) { $.entwine('ss', function ($) {
$('.ss-gridfield-item a.file-url').entwine({
$('#SectionID ul li').entwine({ onclick: function (e) {
onadd: function () { //make sure the download link doesn't trigger a gridfield edit dialog
this.addClass('ui-button ss-ui-button ui-corner-all ui-state-default ui-widget ui-button-text-only'); window.open(this.attr('href'), '_blank');
this.parents('ul').removeClass('ui-tabs-nav');
}
});
$('#SectionID input[type=radio]').entwine({
onadd: function () {
// Checks to see what radio button is selected
if (this.is(':checked')) {
this.change();
}
},
onchange: function (e) {
// Remove selected class from radio buttons
$('#SectionID').find('li').removeClass('selected');
//If radio button is checked then add the selected class
if (this.is(':checked')) {
this.parent('li').addClass('selected');
}
}
});
$('.ss-gridfield .action.dms-delete').entwine({
onclick: function (e) {
//work out how many pages are left attached to this document
var pagesCount = this.data('pages-count');
var pagesCountAfterDeletion = pagesCount - 1;
var addS = 's';
if (pagesCountAfterDeletion === 1) {
addS = '';
}
//display an appropriate message
var message = '';
if (this.hasClass('dms-delete-last-warning')) {
message = "Permanently delete this document?\n\nWarning: this document is attached only to this page, deleting it here will delete it permanently.";
}
if (this.hasClass('dms-delete-link-only')) {
message = "Unlink this document from this page?\n\nNote: it will remain attached to "+pagesCountAfterDeletion+" other page"+addS+".";
}
if (!confirm(message)) {
e.preventDefault();
return false;
} else {
//user says "okay", so go ahead and do the action
this._super(e);
}
}
});
$('.ss-gridfield .dms-document-hidden').entwine({
onadd: function () {
this.closest('tr').addClass('dms-document-hidden-row');
}
});
$('.cms-content-actions.south .ss-ui-action-destructive').entwine({
confirmBeforeDelete: function () {
var deleteButtons = $('button.dms-delete[data-pages-count=1]');
//we have page with DMSDocuments on it, and we have documents that only exist on this page
if (deleteButtons.length > 0) {
var message = "Are you sure you want to delete this page? Deleting this page will delete "+deleteButtons.length;
if (deleteButtons.length === 1) {
message += " document that is associated only with this page. This document is:\n\n";
} else {
message += " documents that are associated only with this page. These documents are:\n\n";
}
//create a list of documents and their IDs
deleteButtons.each(function () {
var tr = $(this).closest('tr');
message += tr.find('.col-ID').text() +' - '+ tr.find('.col-Title').text() +"\n";
});
if (!confirm(message)) {
return false;
}
}
return true;
}
});
$('#Form_EditForm_action_deletefromlive').entwine({
onclick: function (e) {
if (this.confirmBeforeDelete()) {
this._super(e);
} else {
return false;
}
}
});
$('#Form_EditForm_action_delete').entwine({
onclick: function (e) {
if (this.confirmBeforeDelete()) {
this._super(e);
} else {
return false;
}
}
});
$('.ss-gridfield-item a.file-url').entwine({
onclick: function (e) {
//make sure the download link doesn't trigger a gridfield edit dialog
window.open(this.attr('href'), '_blank');
e.preventDefault();
return false;
}
});
});
e.preventDefault();
return false;
}
});
});
}(jQuery)); }(jQuery));

View File

@ -27,9 +27,9 @@ en:
GRIDFIELD_NOTICE: Managing documents will be available once you have created this document set. GRIDFIELD_NOTICE: Managing documents will be available once you have created this document set.
PLURALNAME: Document Sets PLURALNAME: Document Sets
SINGULARNAME: Document Set SINGULARNAME: Document Set
DMSTag: DMSDocumentTaxonomyExtension:
PLURALNAME: 'DMS Tags' TAGS: Tags
SINGULARNAME: 'DMS Tag' NOTAGS: No tags found
FileIFrameField: FileIFrameField:
ATTACHONCESAVED2: 'Files can be attached once you have saved the record for the first time.' ATTACHONCESAVED2: 'Files can be attached once you have saved the record for the first time.'
GridAction: GridAction:

View File

@ -13,9 +13,6 @@ es:
RelatedPages: 'Páginas relacionadas' RelatedPages: 'Páginas relacionadas'
RelatedReferences: 'Referencias relacionadas' RelatedReferences: 'Referencias relacionadas'
SINGULARNAME: Documento SINGULARNAME: Documento
DMSTag:
PLURALNAME: 'Etiquetas del Sistema documental'
SINGULARNAME: 'Etiqueta del Sistema documental'
FileIFrameField: FileIFrameField:
ATTACHONCESAVED2: 'Archivos pueden ser adjuntados una vez que guardes por primera vez.' ATTACHONCESAVED2: 'Archivos pueden ser adjuntados una vez que guardes por primera vez.'
GridAction: GridAction:

View File

@ -13,9 +13,6 @@ nl:
RelatedPages: 'Gerelateerde paginas' RelatedPages: 'Gerelateerde paginas'
RelatedReferences: 'Gerelateerde referenties' RelatedReferences: 'Gerelateerde referenties'
SINGULARNAME: 'Document' SINGULARNAME: 'Document'
DMSTag:
PLURALNAME: 'D M S Tags'
SINGULARNAME: 'D M S Tag'
FileIFrameField: FileIFrameField:
ATTACHONCESAVED2: 'Bestanden kunnen worden toegevoegd na het voor het eerst opslaan' ATTACHONCESAVED2: 'Bestanden kunnen worden toegevoegd na het voor het eerst opslaan'
GridAction: GridAction:

View File

@ -68,7 +68,6 @@ class DMSDocumentSetTest extends SapphireTest
/** @var GridFieldConfig $config */ /** @var GridFieldConfig $config */
$config = $gridField->getConfig(); $config = $gridField->getConfig();
$this->assertNotNull($config->getComponentByType('DMSGridFieldDeleteAction'));
$this->assertNotNull($addNew = $config->getComponentByType('DMSGridFieldAddNewButton')); $this->assertNotNull($addNew = $config->getComponentByType('DMSGridFieldAddNewButton'));
$this->assertSame($set->ID, $addNew->getDocumentSetId()); $this->assertSame($set->ID, $addNew->getDocumentSetId());
@ -96,14 +95,14 @@ class DMSDocumentSetTest extends SapphireTest
$displayFields = $document->getDocumentDisplayFields(); $displayFields = $document->getDocumentDisplayFields();
$this->assertContains('Apple', $displayFields); $this->assertContains('Apple', $displayFields);
$this->assertContains('Orange', $displayFields); $this->assertContains('Orange', $displayFields);
$this->assertArrayHasKey('BelongsToSet', $displayFields); $this->assertArrayHasKey('ManuallyAdded', $displayFields);
$this->assertContains('Added', $displayFields); $this->assertContains('Added', $displayFields);
} }
/** /**
* Tests to ensure that the callback for formatting BelongsToSet will return a nice label for the user * Tests to ensure that the callback for formatting ManuallyAdded will return a nice label for the user
*/ */
public function testNiceFormattingForBelongsToSetInGridField() public function testNiceFormattingForManuallyAddedInGridField()
{ {
$fieldFormatting = $this->objFromFixture('DMSDocumentSet', 'ds1') $fieldFormatting = $this->objFromFixture('DMSDocumentSet', 'ds1')
->getCMSFields() ->getCMSFields()
@ -112,11 +111,11 @@ class DMSDocumentSetTest extends SapphireTest
->getComponentByType('GridFieldDataColumns') ->getComponentByType('GridFieldDataColumns')
->getFieldFormatting(); ->getFieldFormatting();
$this->assertArrayHasKey('BelongsToSet', $fieldFormatting); $this->assertArrayHasKey('ManuallyAdded', $fieldFormatting);
$this->assertTrue(is_callable($fieldFormatting['BelongsToSet'])); $this->assertTrue(is_callable($fieldFormatting['ManuallyAdded']));
$this->assertSame('Manually', $fieldFormatting['BelongsToSet'](1)); $this->assertSame('Manually', $fieldFormatting['ManuallyAdded'](1));
$this->assertSame('Query Builder', $fieldFormatting['BelongsToSet'](0)); $this->assertSame('Query Builder', $fieldFormatting['ManuallyAdded'](0));
} }
/** /**

View File

@ -84,6 +84,7 @@ class DMSDocumentTest extends SapphireTest
$this->assertNotContains('test-file-file-doesnt-exist-1', $jsonResult); $this->assertNotContains('test-file-file-doesnt-exist-1', $jsonResult);
$this->assertContains('test-file-file-doesnt-exist-2', $jsonResult); $this->assertContains('test-file-file-doesnt-exist-2', $jsonResult);
$this->assertEquals(array('Title', 'Filename'), $autocompleter->getSearchFields());
} }
/** /**

View File

@ -1,122 +0,0 @@
<?php
class DMSTagTest extends SapphireTest
{
protected $usesDatabase = true;
public function tearDownOnce()
{
self::$is_running_test = true;
$d = DataObject::get("DMSDocument");
foreach ($d as $d1) {
$d1->delete();
}
$t = DataObject::get("DMSTag");
foreach ($t as $t1) {
$t1->delete();
}
self::$is_running_test = $this->originalIsRunningTest;
}
public function testAddingTags()
{
$doc = new DMSDocument();
$doc->Filename = "test file";
$doc->Folder = "0";
$doc->write();
$doc->addTag("fruit", "banana");
$doc->addTag("fruit", "orange");
$doc->addTag("fruit", "apple");
$doc->addTag("company", "apple");
$doc->addTag("company", "SilverStripe");
$fruits = $doc->getTagsList("fruit");
$this->assertNotNull($fruits, "Something returned for fruit tags");
$this->assertEquals(count($fruits), 3, "3 fruit tags returned");
$this->assertTrue(in_array("banana", $fruits), "correct fruit tags returned");
//sneakily create another document and link one of the tags to that, too
$doc2 = new DMSDocument();
$doc2->Filename = "sneaky file";
$doc2->Folder = "0";
$doc2->write();
$doc2->addTag("fruit", "banana");
$fruits = $doc2->getTagsList("fruit");
$this->assertNotNull($fruits, "Something returned for fruit tags");
$this->assertEquals(count($fruits), 1, "Only 1 fruit tags returned");
//tidy up by deleting all tags from doc 1 (But the banana fruit tag should remain)
$doc->removeAllTags();
//banana fruit remains
$fruits = $doc2->getTagsList("fruit");
$this->assertNotNull($fruits, "Something returned for fruit tags");
$this->assertEquals(count($fruits), 1, "Only 1 fruit tags returned");
$tags = DataObject::get("DMSTag");
$this->assertEquals($tags->Count(), 1, "A single DMS tag objects remain after deletion of all tags on doc1");
//delete all tags off doc2 to complete the tidy up
$doc2->removeAllTags();
$tags = DataObject::get("DMSTag");
$this->assertEquals($tags->Count(), 0, "No DMS tag objects remain after deletion");
}
public function testRemovingTags()
{
$doc = new DMSDocument();
$doc->Filename = "test file";
$doc->Folder = "0";
$doc->write();
$doc->addTag("fruit", "banana");
$doc->addTag("fruit", "orange");
$doc->addTag("fruit", "apple");
$doc->addTag("company", "apple");
$doc->addTag("company", "SilverStripe");
$companies = $doc->getTagsList("company");
$this->assertNotNull($companies, "Companies returned before deletion");
$this->assertEquals(count($companies), 2, "Two companies returned before deletion");
//delete an entire category
$doc->removeTag("company");
$companies = $doc->getTagsList("company");
$this->assertNull($companies, "All companies deleted");
$fruit = $doc->getTagsList("fruit");
$this->assertEquals(count($fruit), 3, "Three fruits returned before deletion");
//delete a single tag
$doc->removeTag("fruit", "apple");
$fruit = $doc->getTagsList("fruit");
$this->assertEquals(count($fruit), 2, "Two fruits returned after deleting one");
//delete a single tag
$doc->removeTag("fruit", "orange");
$fruit = $doc->getTagsList("fruit");
$this->assertEquals(count($fruit), 1, "One fruits returned after deleting two");
//nothing happens when deleting tag that doesn't exist
$doc->removeTag("fruit", "jellybean");
$fruit = $doc->getTagsList("fruit");
$this->assertEquals(count($fruit), 1, "One fruits returned after attempting to delete non-existent fruit");
//delete the last fruit
$doc->removeTag("fruit", "banana");
$fruit = $doc->getTagsList("fruit");
$this->assertNull($fruit, "All fruits deleted");
$tags = DataObject::get("DMSTag");
$this->assertEquals($tags->Count(), 0, "No DMS tag objects remain after deletion");
}
}

View File

@ -26,25 +26,6 @@ SiteTree:
s9: s9:
Title: testPage9 Title: testPage9
URLSegment: s9 URLSegment: s9
DMSTag:
t1:
Category: tag1
Value: tag1value
t2:
Category: tag2
Value: tag2value
t3:
Category: tag3
Value: tag3value
t4:
Category: tag4
Value: tag4value
t5:
Category: tag5
Value: tag5value
t6:
Category: tag6
Value: tag6value
Group: Group:
content-author: content-author:
Code: content-author Code: content-author
@ -85,13 +66,11 @@ DMSDocument:
d1: d1:
Filename: test-file-file-doesnt-exist-1 Filename: test-file-file-doesnt-exist-1
Folder: 5 Folder: 5
Tags: =>DMSTag.t1, =>DMSTag.t2, =>DMSTag.t3, =>DMSTag.t4
Sets: =>DMSDocumentSet.ds1, =>DMSDocumentSet.ds2, =>DMSDocumentSet.ds3, =>DMSDocumentSet.ds4 Sets: =>DMSDocumentSet.ds1, =>DMSDocumentSet.ds2, =>DMSDocumentSet.ds3, =>DMSDocumentSet.ds4
d2: d2:
Filename: test-file-file-doesnt-exist-2 Filename: test-file-file-doesnt-exist-2
Folder: 5 Folder: 5
Title: File That Doesn't Exist (Title) Title: File That Doesn't Exist (Title)
Tags: =>DMSTag.t5, =>DMSTag.t6
Sets: =>DMSDocumentSet.ds1, =>DMSDocumentSet.ds2, =>DMSDocumentSet.ds3, =>DMSDocumentSet.ds4 Sets: =>DMSDocumentSet.ds1, =>DMSDocumentSet.ds2, =>DMSDocumentSet.ds3, =>DMSDocumentSet.ds4
document_with_relations: document_with_relations:
Filename: file-with-relations Filename: file-with-relations
@ -102,20 +81,17 @@ DMSDocument:
CanViewType: LoggedInUsers CanViewType: LoggedInUsers
CanEditType: LoggedInUsers CanEditType: LoggedInUsers
Folder: 5 Folder: 5
Tags: =>DMSTag.t1, =>DMSTag.t2, =>DMSTag.t3, =>DMSTag.t4
Sets: =>DMSDocumentSet.ds1, =>DMSDocumentSet.ds2, =>DMSDocumentSet.ds3, =>DMSDocumentSet.ds4 Sets: =>DMSDocumentSet.ds1, =>DMSDocumentSet.ds2, =>DMSDocumentSet.ds3, =>DMSDocumentSet.ds4
doc-anyone: doc-anyone:
FileName: doc-anyone FileName: doc-anyone
CanViewType: Anyone CanViewType: Anyone
Folder: 5 Folder: 5
Tags: =>DMSTag.t1, =>DMSTag.t2, =>DMSTag.t3, =>DMSTag.t4
Sets: =>DMSDocumentSet.ds1, =>DMSDocumentSet.ds2, =>DMSDocumentSet.ds3, =>DMSDocumentSet.ds4 Sets: =>DMSDocumentSet.ds1, =>DMSDocumentSet.ds2, =>DMSDocumentSet.ds3, =>DMSDocumentSet.ds4
doc-only-these-users: doc-only-these-users:
FileName: doc-only-these-users FileName: doc-only-these-users
CanViewType: OnlyTheseUsers CanViewType: OnlyTheseUsers
CanEditType: OnlyTheseUsers CanEditType: OnlyTheseUsers
Folder: 5 Folder: 5
Tags: =>DMSTag.t1, =>DMSTag.t2, =>DMSTag.t3, =>DMSTag.t4
Sets: =>DMSDocumentSet.ds1, =>DMSDocumentSet.ds2, =>DMSDocumentSet.ds3, =>DMSDocumentSet.ds4 Sets: =>DMSDocumentSet.ds1, =>DMSDocumentSet.ds2, =>DMSDocumentSet.ds3, =>DMSDocumentSet.ds4
ViewerGroups: =>Group.content-author ViewerGroups: =>Group.content-author
EditorGroups: =>Group.content-author EditorGroups: =>Group.content-author
@ -138,19 +114,19 @@ DMSDocument:
Filename: extradoc1 Filename: extradoc1
Folder: 5 Folder: 5
Sets: =>DMSDocumentSet.ds6 Sets: =>DMSDocumentSet.ds6
BelongsToSet: 1 ManuallyAdded: 1
CreatedByID: 2 CreatedByID: 2
extraDoc2: extraDoc2:
Filename: extradoc2 Filename: extradoc2
Folder: 5 Folder: 5
CreatedByID: 2 CreatedByID: 2
BelongsToSet: 0 ManuallyAdded: 0
extraDoc3: extraDoc3:
Filename: extradoc3 Filename: extradoc3
Folder: 5 Folder: 5
BelongsToSet: 0 ManuallyAdded: 0
CreatedByID: 2 CreatedByID: 2
docSaveLinkedDocuments1: docSaveLinkedDocuments1:
Filename: saveLinkedDocument1 Filename: saveLinkedDocument1
Folder: 5 Folder: 5
Sets: =>DMSDocumentSet.dsSaveLinkedDocuments Sets: =>DMSDocumentSet.dsSaveLinkedDocuments

View File

@ -0,0 +1,29 @@
<?php
class DMSDocumentTaxonomyExtensionTest extends SapphireTest
{
protected static $fixture_file = 'DMSDocumentTaxonomyExtensionTest.yml';
public function setUp()
{
parent::setUp();
if (!class_exists('TaxonomyType')) {
$this->markTestSkipped('This test requires silverstripe/taxonomy ^1.2 to be installed. Skipping.');
}
}
/**
* Ensure that appropriate tags by taxonomy type are returned, and that their hierarchy is displayd in the title
*/
public function testGetAllTagsMap()
{
$extension = new DMSDocumentTaxonomyExtension;
$result = $extension->getAllTagsMap();
$this->assertContains('Subject > Mathematics', $result);
$this->assertContains('Subject', $result);
$this->assertContains('Subject > Science > Chemistry', $result);
$this->assertNotContains('Physical Education', $result);
}
}

View File

@ -0,0 +1,30 @@
TaxonomyType:
document:
Name: Document
not_a_document:
Name: Not A Document
TaxonomyTerm:
subject:
Name: Subject
Type: =>TaxonomyType.document
maths:
Name: Mathematics
Parent: =>TaxonomyTerm.subject
Type: =>TaxonomyType.document
science:
Name: Science
Parent: =>TaxonomyTerm.subject
Type: =>TaxonomyType.document
chemistry:
Name: Chemistry
Parent: =>TaxonomyTerm.science
Type: =>TaxonomyType.document
chemistry_level_1:
Name: Level 1
Parent: =>TaxonomyTerm.chemistry
Type: =>TaxonomyType.document
# Not applicable to DMS documents, but here for testing edge cases
physed:
Name: Physical Education
Type: =>TaxonomyType.not_a_document

View File

@ -0,0 +1,29 @@
<?php
class DMSTaxonomyTypeExtensionTest extends SapphireTest
{
protected $usesDatabase = true;
protected $requiredExtensions = array(
'TaxonomyType' => array('DMSTaxonomyTypeExtension')
);
/**
* Ensure that the configurable list of default records are created
*/
public function testDefaultRecordsAreCreated()
{
Config::inst()->update('DMSTaxonomyTypeExtension', 'default_records', array('Food', 'Beverage', 'Books'));
TaxonomyType::create()->requireDefaultRecords();
$this->assertDOSContains(
array(
array('Name' => 'Food'),
array('Name' => 'Beverage'),
array('Name' => 'Books'),
),
TaxonomyType::get()
);
}
}