From 489a62abb2a4ae3810b8a59a34b8fc5c4f8ed27f Mon Sep 17 00:00:00 2001 From: Robbie Averill Date: Wed, 17 May 2017 10:58:51 +1200 Subject: [PATCH] API Remove DMSGridFieldDeleteAction, add query builder filter for taxonomy terms --- README.md | 1 + _config/querybuilder.yml | 18 ++-- _config/taxonomy.yml | 6 ++ code/cms/DMSGridFieldDeleteAction.php | 121 ----------------------- code/cms/DMSUploadField.php | 2 +- code/model/DMSDocument.php | 5 +- code/model/DMSDocumentSet.php | 117 ++++++++++++++--------- composer.json | 3 +- docs/en/changelogs/2.0.0.md | 1 + javascript/DMSGridField.js | 132 +++----------------------- tests/DMSDocumentSetTest.php | 15 ++- tests/DMSDocumentTest.php | 1 + tests/dmstest.yml | 6 +- 13 files changed, 118 insertions(+), 310 deletions(-) delete mode 100644 code/cms/DMSGridFieldDeleteAction.php diff --git a/README.md b/README.md index 2da4f52..40476ea 100644 --- a/README.md +++ b/README.md @@ -32,6 +32,7 @@ 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) * 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) [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) diff --git a/_config/querybuilder.yml b/_config/querybuilder.yml index 1814ba2..be986aa 100644 --- a/_config/querybuilder.yml +++ b/_config/querybuilder.yml @@ -1,16 +1,16 @@ DMSDocument: searchable_fields: Title: - title: "Document title matches ..." + title: Document title matches Description: - title: "Document summary matches ..." + title: Document summary matches CreatedByID: - title: 'Document created by ...' - field: 'ListboxField' - filter: 'ExactMatchFilter' + title: Document created by + field: ListboxField + filter: ExactMatchFilter LastEditedByID: - title: 'Document last changed by ...' - field: 'ListboxField' - filter: 'ExactMatchFilter' + title: Document last changed by + field: ListboxField + filter: ExactMatchFilter Filename: - title: 'File name' \ No newline at end of file + title: File name diff --git a/_config/taxonomy.yml b/_config/taxonomy.yml index 071d4f0..2b45fd5 100644 --- a/_config/taxonomy.yml +++ b/_config/taxonomy.yml @@ -10,6 +10,12 @@ TaxonomyType: 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. diff --git a/code/cms/DMSGridFieldDeleteAction.php b/code/cms/DMSGridFieldDeleteAction.php deleted file mode 100644 index edbba46..0000000 --- a/code/cms/DMSGridFieldDeleteAction.php +++ /dev/null @@ -1,121 +0,0 @@ - - * $action = new GridFieldDeleteAction(); // delete objects permanently - * - * - * @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(); - } - } - } -} diff --git a/code/cms/DMSUploadField.php b/code/cms/DMSUploadField.php index 89b65f1..7f64f25 100644 --- a/code/cms/DMSUploadField.php +++ b/code/cms/DMSUploadField.php @@ -48,7 +48,7 @@ class DMSUploadField extends UploadField // 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, array('BelongsToSet' => 1)); + $record->Documents()->add($doc, array('ManuallyAdded' => 1)); } return $doc; diff --git a/code/model/DMSDocument.php b/code/model/DMSDocument.php index aba289f..523bc92 100644 --- a/code/model/DMSDocument.php +++ b/code/model/DMSDocument.php @@ -63,7 +63,7 @@ class DMSDocument extends DataObject implements DMSDocumentInterface 'ID' => 'ID', 'Title' => 'Title', 'FilenameWithoutID' => 'Filename', - 'LastEdited' => 'LastEdited' + 'LastEdited' => 'Last Edited' ); private static $singular_name = 'Document'; @@ -1210,6 +1210,9 @@ class DMSDocument extends DataObject implements DMSDocumentInterface // Ensure that current document doesn't get returned in the autocompleter $addExisting->setSearchList($this->getRelatedDocumentsForAutocompleter()); + // Restrict search fields to specific fields only + $addExisting->setSearchFields(array('Title', 'Filename')); + $this->extend('updateRelatedDocumentsGridField', $gridField); return $gridField; diff --git a/code/model/DMSDocumentSet.php b/code/model/DMSDocumentSet.php index 65e53fc..e0cb159 100644 --- a/code/model/DMSDocumentSet.php +++ b/code/model/DMSDocumentSet.php @@ -26,7 +26,9 @@ class DMSDocumentSet extends DataObject private static $many_many_extraFields = 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 GridFieldEditButton(), // Special delete dialog to handle custom behaviour of unlinking and deleting - new DMSGridFieldDeleteAction(), + new GridFieldDeleteAction(true), new GridFieldDetailForm() ); @@ -115,7 +117,7 @@ class DMSDocumentSet extends DataObject ->setFieldFormatting( array( 'FilenameWithoutID' => '$FilenameWithoutID', - 'BelongsToSet' => function ($value) { + 'ManuallyAdded' => function ($value) { if ($value) { return _t('DMSDocumentSet.MANUAL', 'Manually'); } @@ -197,24 +199,24 @@ class DMSDocumentSet extends DataObject $dmsDocFields = $doc->scaffoldSearchFields(array('fieldClasses' => true)); $membersMap = Member::get()->map('ID', 'Name')->toArray(); asort($membersMap); + foreach ($dmsDocFields as $field) { - // Apply field customisations where necessary - if (in_array($field->getName(), array('CreatedByID', 'LastEditedByID', 'LastEditedByID'))) { - /** @var ListboxField $field */ - $field->setMultiple(true)->setSource($membersMap); + if ($field instanceof ListboxField) { + $map = ($field->getName() === 'Tags__ID') ? $doc->getAllTagsMap() : $membersMap; + $field->setMultiple(true)->setSource($map); } } $keyValPairs = JsonField::create('KeyValuePairs', $dmsDocFields->toArray()); // Now lastly add the sort fields $sortedBy = FieldGroup::create('SortedBy', array( - DropdownField::create('SortBy', '', array( - 'LastEdited' => 'Last changed', - 'Created' => 'Created', - 'Title' => 'Document title', - ), 'LastEdited'), - DropdownField::create('SortByDirection', '', $this->dbObject('SortByDirection')->enumValues(), 'DESC'), - )); + DropdownField::create('SortBy', '', array( + 'LastEdited' => 'Last changed', + 'Created' => 'Created', + 'Title' => 'Document title', + ), 'LastEdited'), + DropdownField::create('SortByDirection', '', $this->dbObject('SortByDirection')->enumValues(), 'DESC'), + )); $sortedBy->setTitle(_t('DMSDocumentSet.SORTED_BY', 'Sort the document set by:')); $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. - * - * @return ArrayList|null */ public function saveLinkedDocuments() { - // Documents that belong to just this set. - /** @var ManyManyList $originals */ - $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)); - } + if (empty($this->KeyValuePairs) || !$this->isChanged('KeyValuePairs')) { + return; } + + $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( (array) DMSDocument::create()->config()->get('display_fields'), - array('BelongsToSet' => _t('DMSDocumentSet.ADDEDMETHOD', 'Added')) + array('ManuallyAdded' => _t('DMSDocumentSet.ADDEDMETHOD', 'Added')) ); } } diff --git a/composer.json b/composer.json index d751f94..4d61c7d 100644 --- a/composer.json +++ b/composer.json @@ -11,7 +11,8 @@ "require": { "silverstripe/framework": "^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": { "branch-alias": { diff --git a/docs/en/changelogs/2.0.0.md b/docs/en/changelogs/2.0.0.md index 88fd6cb..22c879b 100644 --- a/docs/en/changelogs/2.0.0.md +++ b/docs/en/changelogs/2.0.0.md @@ -32,6 +32,7 @@ this process. * `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 diff --git a/javascript/DMSGridField.js b/javascript/DMSGridField.js index f7e1890..35543aa 100644 --- a/javascript/DMSGridField.js +++ b/javascript/DMSGridField.js @@ -1,125 +1,15 @@ (function ($) { - "use strict"; + "use strict"; - $.entwine('ss', function ($) { - - $('#SectionID ul li').entwine({ - onadd: function () { - this.addClass('ui-button ss-ui-button ui-corner-all ui-state-default ui-widget ui-button-text-only'); - 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; - } - }); - - }); + $.entwine('ss', function ($) { + $('.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; + } + }); + }); }(jQuery)); diff --git a/tests/DMSDocumentSetTest.php b/tests/DMSDocumentSetTest.php index af7080c..4204db8 100644 --- a/tests/DMSDocumentSetTest.php +++ b/tests/DMSDocumentSetTest.php @@ -68,7 +68,6 @@ class DMSDocumentSetTest extends SapphireTest /** @var GridFieldConfig $config */ $config = $gridField->getConfig(); - $this->assertNotNull($config->getComponentByType('DMSGridFieldDeleteAction')); $this->assertNotNull($addNew = $config->getComponentByType('DMSGridFieldAddNewButton')); $this->assertSame($set->ID, $addNew->getDocumentSetId()); @@ -96,14 +95,14 @@ class DMSDocumentSetTest extends SapphireTest $displayFields = $document->getDocumentDisplayFields(); $this->assertContains('Apple', $displayFields); $this->assertContains('Orange', $displayFields); - $this->assertArrayHasKey('BelongsToSet', $displayFields); + $this->assertArrayHasKey('ManuallyAdded', $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') ->getCMSFields() @@ -112,11 +111,11 @@ class DMSDocumentSetTest extends SapphireTest ->getComponentByType('GridFieldDataColumns') ->getFieldFormatting(); - $this->assertArrayHasKey('BelongsToSet', $fieldFormatting); - $this->assertTrue(is_callable($fieldFormatting['BelongsToSet'])); + $this->assertArrayHasKey('ManuallyAdded', $fieldFormatting); + $this->assertTrue(is_callable($fieldFormatting['ManuallyAdded'])); - $this->assertSame('Manually', $fieldFormatting['BelongsToSet'](1)); - $this->assertSame('Query Builder', $fieldFormatting['BelongsToSet'](0)); + $this->assertSame('Manually', $fieldFormatting['ManuallyAdded'](1)); + $this->assertSame('Query Builder', $fieldFormatting['ManuallyAdded'](0)); } /** diff --git a/tests/DMSDocumentTest.php b/tests/DMSDocumentTest.php index b0278a5..6973d35 100644 --- a/tests/DMSDocumentTest.php +++ b/tests/DMSDocumentTest.php @@ -84,6 +84,7 @@ class DMSDocumentTest extends SapphireTest $this->assertNotContains('test-file-file-doesnt-exist-1', $jsonResult); $this->assertContains('test-file-file-doesnt-exist-2', $jsonResult); + $this->assertEquals(array('Title', 'Filename'), $autocompleter->getSearchFields()); } /** diff --git a/tests/dmstest.yml b/tests/dmstest.yml index 6e0d457..dab68b0 100644 --- a/tests/dmstest.yml +++ b/tests/dmstest.yml @@ -114,17 +114,17 @@ DMSDocument: Filename: extradoc1 Folder: 5 Sets: =>DMSDocumentSet.ds6 - BelongsToSet: 1 + ManuallyAdded: 1 CreatedByID: 2 extraDoc2: Filename: extradoc2 Folder: 5 CreatedByID: 2 - BelongsToSet: 0 + ManuallyAdded: 0 extraDoc3: Filename: extradoc3 Folder: 5 - BelongsToSet: 0 + ManuallyAdded: 0 CreatedByID: 2 docSaveLinkedDocuments1: Filename: saveLinkedDocument1