From 79da01513e72fed3f2fb899ad6cd9feac1f41bc6 Mon Sep 17 00:00:00 2001 From: Franco Springveldt Date: Wed, 14 Jun 2017 11:05:19 +1200 Subject: [PATCH] FIX document and documentset permissions --- code/cms/DMSDocumentAddController.php | 25 +++++++ code/cms/DMSDocumentAdmin.php | 4 +- code/cms/DMSGridFieldEditButton.php | 26 +++++++ code/extensions/DMSSiteTreeExtension.php | 9 +++ code/model/DMSDocument.php | 44 +++++++++--- code/model/DMSDocumentSet.php | 68 +++++++++++++++++- tests/DMSDocumentSetTest.php | 23 ++++++ tests/DMSDocumentTest.php | 89 ++++++++++++++++++++---- tests/cms/DMSDocumentAdminTest.php | 41 ++++++----- 9 files changed, 290 insertions(+), 39 deletions(-) create mode 100755 code/cms/DMSGridFieldEditButton.php diff --git a/code/cms/DMSDocumentAddController.php b/code/cms/DMSDocumentAddController.php index 13bb0ab..2554be8 100644 --- a/code/cms/DMSDocumentAddController.php +++ b/code/cms/DMSDocumentAddController.php @@ -316,4 +316,29 @@ class DMSDocumentAddController extends LeftAndMain ) ); } + + /** + * Overrides the parent method to allow users with access to DMS admin to access this controller + * + * @param Member $member + * @return bool + */ + public function canView($member = null) + { + if (!$member || !(is_a($member, 'Member')) || is_numeric($member)) { + $member = Member::currentUser(); + } + + if ($member && + Permission::checkMember( + $member, + array( + 'CMS_ACCESS_DMSDocumentAdmin', + ) + ) + ) { + return true; + } + return parent::canView($member); + } } diff --git a/code/cms/DMSDocumentAdmin.php b/code/cms/DMSDocumentAdmin.php index 5bbb353..10fe0a8 100644 --- a/code/cms/DMSDocumentAdmin.php +++ b/code/cms/DMSDocumentAdmin.php @@ -18,7 +18,6 @@ class DMSDocumentAdmin extends ModelAdmin parent::init(); Requirements::javascript(DMS_DIR . '/javascript/DMSGridField.js'); } - /** * Remove the default "add" button and replace it with a customised version for DMS * @@ -45,6 +44,9 @@ class DMSDocumentAdmin extends ModelAdmin { $gridFieldConfig = $gridField->getConfig(); + $gridFieldConfig->removeComponentsByType('GridFieldEditButton'); + $gridFieldConfig->addComponent(new DMSGridFieldEditButton(), 'GridFieldDeleteAction'); + if ($this->modelClass === 'DMSDocument') { $gridFieldConfig->removeComponentsByType('GridFieldAddNewButton'); $gridFieldConfig->addComponent( diff --git a/code/cms/DMSGridFieldEditButton.php b/code/cms/DMSGridFieldEditButton.php new file mode 100755 index 0000000..1519907 --- /dev/null +++ b/code/cms/DMSGridFieldEditButton.php @@ -0,0 +1,26 @@ + Controller::join_links($gridField->Link('item'), $record->ID, 'edit') + )); + + $template = $record->canEdit() ? 'GridFieldEditButton' : 'GridFieldViewButton'; + + return $data->renderWith($template); + } +} diff --git a/code/extensions/DMSSiteTreeExtension.php b/code/extensions/DMSSiteTreeExtension.php index 7f32914..a7e873e 100644 --- a/code/extensions/DMSSiteTreeExtension.php +++ b/code/extensions/DMSSiteTreeExtension.php @@ -16,6 +16,15 @@ class DMSSiteTreeExtension extends DataExtension return; } + // Hides the DocumentSets tab if the user has no permisions + if (!Permission::checkMember( + Member::currentUser(), + array('ADMIN', 'CMS_ACCESS_DMSDocumentAdmin') + ) + ) { + return; + } + $gridField = GridField::create( 'Document Sets', false, diff --git a/code/model/DMSDocument.php b/code/model/DMSDocument.php index 615215a..b94b7b0 100644 --- a/code/model/DMSDocument.php +++ b/code/model/DMSDocument.php @@ -116,7 +116,6 @@ class DMSDocument extends DataObject implements DMSDocumentInterface if (!$this->CanViewType || $this->CanViewType == 'Anyone') { return true; } - if ($member && Permission::checkMember($member, array( 'ADMIN', 'SITETREE_EDIT_ALL', @@ -198,6 +197,13 @@ class DMSDocument extends DataObject implements DMSDocumentInterface } } + // Do early admin check + if ($member && + Permission::checkMember($member, array('CMS_ACCESS_DMSDocumentAdmin')) + ) { + return true; + } + return $this->canEdit($member); } @@ -220,7 +226,7 @@ class DMSDocument extends DataObject implements DMSDocumentInterface } } - return $this->canView(); + return $this->canEdit($member); } /** @@ -838,8 +844,6 @@ class DMSDocument extends DataObject implements DMSDocumentInterface $this->addActionPanelTask('find-versions', 'Versions'); } - $fields->add(LiteralField::create('BottomTaskSelection', $this->getActionTaskHtml())); - $embargoValue = 'None'; if ($this->EmbargoedIndefinitely) { $embargoValue = 'Indefinitely'; @@ -894,12 +898,19 @@ class DMSDocument extends DataObject implements DMSDocumentInterface FieldGroup::create($uploadField)->addExtraClass('replace'), FieldGroup::create($pagesGrid)->addExtraClass('find-usage'), FieldGroup::create($referencesGrid)->addExtraClass('find-references'), - FieldGroup::create($versionsGrid)->addExtraClass('find-versions'), - FieldGroup::create($this->getRelatedDocumentsGridField())->addExtraClass('find-relateddocuments'), FieldGroup::create($this->getPermissionsActionPanel())->addExtraClass('permissions') ); - $actionsPanel->setName("ActionsPanel"); + if ($this->canEdit()) { + $actionsPanel->push(FieldGroup::create($versionsGrid)->addExtraClass('find-versions')); + $actionsPanel->push( + FieldGroup::create($this->getRelatedDocumentsGridField())->addExtraClass('find-relateddocuments') + ); + } else { + $this->removeActionPanelTask('find-relateddocuments')->removeActionPanelTask('find-versions'); + } + $fields->add(LiteralField::create('BottomTaskSelection', $this->getActionTaskHtml())); + $actionsPanel->setName('ActionsPanel'); $actionsPanel->addExtraClass('dmsdocument-actionspanel'); $fields->push($actionsPanel); @@ -1204,6 +1215,10 @@ class DMSDocument extends DataObject implements DMSDocumentInterface new GridFieldConfig_RelationEditor ); + $gridFieldConfig = $gridField->getConfig(); + $gridFieldConfig->removeComponentsByType('GridFieldEditButton'); + $gridFieldConfig->addComponent(new DMSGridFieldEditButton(), 'GridFieldDeleteAction'); + $gridField->getConfig()->removeComponentsByType('GridFieldAddNewButton'); // Move the autocompleter to the left $gridField->getConfig()->removeComponentsByType('GridFieldAddExistingAutocompleter'); @@ -1219,7 +1234,6 @@ class DMSDocument extends DataObject implements DMSDocumentInterface $addExisting->setResultsFormat('$Filename'); $this->extend('updateRelatedDocumentsGridField', $gridField); - return $gridField; } @@ -1323,4 +1337,18 @@ class DMSDocument extends DataObject implements DMSDocumentInterface return $html; } + + /** + * Removes an "action panel" tasks + * + * @param string $panelKey + * @return $this + */ + public function removeActionPanelTask($panelKey) + { + if (array_key_exists($panelKey, $this->actionTasks)) { + unset($this->actionTasks[$panelKey]); + } + return $this; + } } diff --git a/code/model/DMSDocumentSet.php b/code/model/DMSDocumentSet.php index 2fbbaa2..f902439 100644 --- a/code/model/DMSDocumentSet.php +++ b/code/model/DMSDocumentSet.php @@ -97,7 +97,7 @@ class DMSDocumentSet extends DataObject new GridFieldFilterHeader(), new GridFieldSortableHeader(), new GridFieldDataColumns(), - new GridFieldEditButton(), + new DMSGridFieldEditButton(), // Special delete dialog to handle custom behaviour of unlinking and deleting new GridFieldDeleteAction(true), new GridFieldDetailForm() @@ -121,6 +121,13 @@ class DMSDocumentSet extends DataObject $fields->fieldByName('Root.Main.PageID')->setTitle(_t('DMSDocumentSet.SHOWONPAGE', 'Show on page')); } + // Don't show which page this is if we're already editing within a page context + if (Controller::curr() instanceof CMSPageEditController) { + $fields->removeByName('PageID'); + } else { + $fields->fieldByName('Root.Main.PageID')->setTitle(_t('DMSDocumentSet.SHOWONPAGE', 'Show on page')); + } + $gridFieldConfig->getComponentByType('GridFieldDataColumns') ->setDisplayFields($self->getDocumentDisplayFields()) ->setFieldCasting(array('LastEdited' => 'Datetime->Ago')) @@ -348,4 +355,63 @@ class DMSDocumentSet extends DataObject } return $result; } + + public function canView($member = null) + { + $extended = $this->extendedCan(__FUNCTION__, $member); + if ($extended !== null) { + return $extended; + } + return $this->getGlobalPermission($member); + } + + public function canCreate($member = null) + { + $extended = $this->extendedCan(__FUNCTION__, $member); + if ($extended !== null) { + return $extended; + } + return $this->getGlobalPermission($member); + } + + public function canEdit($member = null) + { + $extended = $this->extendedCan(__FUNCTION__, $member); + if ($extended !== null) { + return $extended; + } + return $this->getGlobalPermission($member); + } + + public function canDelete($member = null) + { + $extended = $this->extendedCan(__FUNCTION__, $member); + if ($extended !== null) { + return $extended; + } + return $this->getGlobalPermission($member); + } + + /** + * Checks if a then given (or logged in) member is either an ADMIN, SITETREE_EDIT_ALL or has access + * to the DMSDocumentAdmin module, in which case permissions is granted. + * + * @param Member $member + * @return bool + */ + public function getGlobalPermission(Member $member = null) + { + if (!$member || !(is_a($member, 'Member')) || is_numeric($member)) { + $member = Member::currentUser(); + } + + $result = ($member && + Permission::checkMember( + $member, + array('ADMIN', 'SITETREE_EDIT_ALL', 'CMS_ACCESS_DMSDocumentAdmin') + ) + ); + + return (bool) $result; + } } diff --git a/tests/DMSDocumentSetTest.php b/tests/DMSDocumentSetTest.php index 0c46409..c088618 100644 --- a/tests/DMSDocumentSetTest.php +++ b/tests/DMSDocumentSetTest.php @@ -244,4 +244,27 @@ class DMSDocumentSetTest extends SapphireTest $fields = $set->getCMSFields(); $this->assertNull($fields->fieldByName('Root.Main.PageID')); } + + /** + * Tests all crud permissions + */ + public function testPermissions() + { + if ($member = Member::currentUser()) { + $member->logout(); + } + + $set = $this->objFromFixture('DMSDocumentSet', 'ds1'); + + $this->assertFalse($set->canCreate()); + $this->assertFalse($set->canDelete()); + $this->assertFalse($set->canEdit()); + $this->assertFalse($set->canView()); + + $this->logInWithPermission('CMS_ACCESS_DMSDocumentAdmin'); + $this->assertTrue($set->canCreate()); + $this->assertTrue($set->canDelete()); + $this->assertTrue($set->canEdit()); + $this->assertTrue($set->canView()); + } } diff --git a/tests/DMSDocumentTest.php b/tests/DMSDocumentTest.php index d7f712d..19dae1c 100644 --- a/tests/DMSDocumentTest.php +++ b/tests/DMSDocumentTest.php @@ -49,7 +49,9 @@ class DMSDocumentTest extends SapphireTest public function testDocumentHasCmsFieldForManagingRelatedDocuments() { $document = $this->objFromFixture('DMSDocument', 'document_with_relations'); - $gridField = $this->getGridFieldFromDocument($document); + $gridField = $this->getRelatedDocumentsGridField($document); + $this->assertInstanceOf('GridField', $gridField); + $gridFieldConfig = $gridField->getConfig(); $this->assertNotNull( @@ -64,13 +66,25 @@ class DMSDocumentTest extends SapphireTest ); } + /** + * Ensures that the DMS Document CMS Related and Versions fields are removed if user can't edit + */ + public function testDocumentHasNoCMSFieldsForManagingRelatedDocumentsIfCantEdit() + { + $this->logInWithPermission('another-user'); + $document = $this->objFromFixture('DMSDocument', 'doc-only-these-users'); + $gridField = $this->getRelatedDocumentsGridField($document); + $this->assertNull($gridField); + } + /** * Ensure that the related documents list does not include the current document itself */ public function testGetRelatedDocumentsForAutocompleter() { $document = $this->objFromFixture('DMSDocument', 'd1'); - $gridField = $this->getGridFieldFromDocument($document); + $gridField = $this->getRelatedDocumentsGridField($document); + $this->assertInstanceOf('GridField', $gridField); $config = $gridField->getConfig(); @@ -90,7 +104,7 @@ class DMSDocumentTest extends SapphireTest /** * @return GridField */ - protected function getGridFieldFromDocument(DMSDocument $document) + protected function getRelatedDocumentsGridField(DMSDocument $document) { $documentFields = $document->getCMSFields(); /** @var FieldGroup $actions */ @@ -103,7 +117,6 @@ class DMSDocumentTest extends SapphireTest break; } } - $this->assertInstanceOf('GridField', $gridField); return $gridField; } @@ -121,6 +134,19 @@ class DMSDocumentTest extends SapphireTest $this->assertContains('
  • init(); @@ -22,23 +22,32 @@ class DMSDocumentAdminTest extends FunctionalTest $form = $modelAdmin->getEditForm(); $gridFieldConfig = $form->Fields()->first()->getConfig(); - // Our button is an instance of the original, so is returned when asking for the original - $addNewButtons = $gridFieldConfig->getComponentsByType('GridFieldAddNewButton'); - foreach ($addNewButtons as $key => $addNewButton) { - if ($addNewButton instanceof DMSGridFieldAddNewButton) { - // Remove our version for testing's sake - $addNewButtons->remove($addNewButton); - } - } - - $this->assertCount(0, $addNewButtons, 'Original add new button is removed'); - $this->assertInstanceOf( - 'DMSGridFieldAddNewButton', - $gridFieldConfig->getComponentByType('DMSGridFieldAddNewButton'), - 'Model admin for documents contains customised DMS add new button' + $replacements = array( + 'GridFieldAddNewButton'=>'DMSGridFieldAddNewButton', + 'GridFieldEditButton'=>'DMSGridFieldEditButton' ); + + foreach ($replacements as $oldClass => $newClass) { + // Our button is an instance of the original, so is returned when asking for the original + $newButtons = $gridFieldConfig->getComponentsByType($oldClass); + foreach ($newButtons as $key => $newButton) { + if ($newButton instanceof $newClass) { + // Remove our version for testing's sake + $newButtons->remove($newButton); + } + } + + $this->assertCount(0, $newButtons, 'Original button is removed'); + $this->assertInstanceOf( + $newClass, + $gridFieldConfig->getComponentByType($newClass), + "Model admin for documents contains customised {$newClass} button" + ); + } } + + /** * Quick check to ensure that the ModelAdmin endpoint is working */