mirror of
https://github.com/silverstripe/silverstripe-dms
synced 2024-10-22 12:05:56 +00:00
Merge pull request #106 from robbieaverill/feature/related-documents
NEW Relate documents to other documents and tidy up documentation
This commit is contained in:
commit
bbe9e35d99
1
.gitattributes
vendored
1
.gitattributes
vendored
@ -1,3 +1,4 @@
|
|||||||
|
/docs export-ignore
|
||||||
/tests export-ignore
|
/tests export-ignore
|
||||||
/.gitignore export-ignore
|
/.gitignore export-ignore
|
||||||
/.travis.yml export-ignore
|
/.travis.yml export-ignore
|
||||||
|
113
README.md
113
README.md
@ -3,25 +3,20 @@
|
|||||||
[![Build status](https://travis-ci.org/silverstripe/silverstripe-dms.png?branch=master)](https://travis-ci.org/silverstripe/silverstripe-dms)
|
[![Build status](https://travis-ci.org/silverstripe/silverstripe-dms.png?branch=master)](https://travis-ci.org/silverstripe/silverstripe-dms)
|
||||||
[![Code quality](https://scrutinizer-ci.com/g/silverstripe/silverstripe-dms/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/silverstripe/silverstripe-dms/?branch=master)
|
[![Code quality](https://scrutinizer-ci.com/g/silverstripe/silverstripe-dms/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/silverstripe/silverstripe-dms/?branch=master)
|
||||||
[![Code coverage](https://codecov.io/gh/silverstripe/silverstripe-dms/branch/master/graph/badge.svg)](https://codecov.io/gh/silverstripe/silverstripe-dms)
|
[![Code coverage](https://codecov.io/gh/silverstripe/silverstripe-dms/branch/master/graph/badge.svg)](https://codecov.io/gh/silverstripe/silverstripe-dms)
|
||||||
|
![Helpful Robot](https://img.shields.io/badge/helpfulrobot-52-yellow.svg?style=flat)
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
The module adds a new `DMSDocument` model which allows management
|
The module adds a new `DMSDocument` model which allows management of large amounts of files, and their relations to
|
||||||
of large amounts of files, and their relations to pages.
|
pages. In contrast to the `File` model built into SilverStripe core, it aims to wrap storage and access concerns in
|
||||||
In contrast to the `File` model built into SilverStripe core,
|
a generic API. This allows more fine-grained control over how the documents are managed and exposed through the website.
|
||||||
it aims to wrap storage and access concerns in a generic API,
|
|
||||||
which allows more fine-grained control over how the documents are
|
|
||||||
managed and exposed through the website.
|
|
||||||
|
|
||||||
Additionally, documents are stored and managed as part of a page instead of
|
Additionally, documents are stored and managed as part of a page instead of away in a separate assets store.
|
||||||
away in a separate assets store.
|
|
||||||
|
|
||||||
Read more about the DMS module in this [blog post on silverstripe.org](http://www.silverstripe.org/document-management-system-module)
|
## Features
|
||||||
|
|
||||||
Features:
|
|
||||||
|
|
||||||
* Relation of documents to pages
|
* Relation of documents to pages
|
||||||
|
* 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
|
* Configurable tags for documents
|
||||||
@ -29,30 +24,11 @@ Features:
|
|||||||
* 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
|
||||||
|
|
||||||
## Documents on the Filesystem
|
## Documentation
|
||||||
|
|
||||||
While the DMS architecture allows for remote storage of files,
|
For information on configuring and using this module, please see [the documentation section](docs/en/index.md).
|
||||||
the default implementation (the `DMS` class) stores them locally.
|
|
||||||
Relations to pages and tags are persisted as many-many relationships
|
|
||||||
through the SilverStripe ORM.
|
|
||||||
|
|
||||||
File locations in this implementation are structured into
|
## Requirements
|
||||||
subfolders, in order to avoid exceeding filesystem limits.
|
|
||||||
The file name is a composite based on its database ID
|
|
||||||
and the original file name. The exact location shouldn't
|
|
||||||
be relied on by custom logic, but rather retrieved through
|
|
||||||
the API (`DMSDocument->getLink()`).
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
dms-assets/
|
|
||||||
0/
|
|
||||||
1234~myfile.pdf
|
|
||||||
1/
|
|
||||||
2345~myotherfile.pdf
|
|
||||||
|
|
||||||
|
|
||||||
### Requirements
|
|
||||||
|
|
||||||
* 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)
|
||||||
* (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)
|
||||||
@ -61,75 +37,6 @@ Example:
|
|||||||
* (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)
|
* (optional) [Tags](https://github.com/tubbs/silverstripe-dms-simple-tags)
|
||||||
|
|
||||||
### Configuration
|
|
||||||
|
|
||||||
The file location is set via the `DMS::$dmsFolder` static, and points to a location in the webroot.
|
|
||||||
|
|
||||||
### Usage
|
|
||||||
|
|
||||||
Add a simple include to any of your .ss templates to display the DMSDocuments associated with
|
|
||||||
the current page on the front-end.
|
|
||||||
|
|
||||||
<% include Documents %>
|
|
||||||
|
|
||||||
#### Create Documents
|
|
||||||
|
|
||||||
Create by relative path:
|
|
||||||
|
|
||||||
$dms = DMS::getDMSInstance();
|
|
||||||
$doc = $dms->storeDocument('assets/myfile.pdf');
|
|
||||||
|
|
||||||
Create from an existing `File` record:
|
|
||||||
|
|
||||||
$dms = DMS::getDMSInstance();
|
|
||||||
$file = File::get()->byID(99);
|
|
||||||
$doc = $dms->storeDocument($file);
|
|
||||||
|
|
||||||
Note: Both operations copy the existing file.
|
|
||||||
|
|
||||||
#### Download Documents
|
|
||||||
|
|
||||||
$dms = DMS::getDMSInstance();
|
|
||||||
$docs = $dms->getByTag('priority', 'important')->First();
|
|
||||||
$link = $doc->getLink();
|
|
||||||
|
|
||||||
// Set default download behavior ('open' or 'download'). 'download' is the system default
|
|
||||||
// Attempt to open the file in the browser
|
|
||||||
Config::inst()->update('DMSDocument', 'default_download_behaviour', 'open');
|
|
||||||
|
|
||||||
Or in you config.yml:
|
|
||||||
|
|
||||||
DMSDocument:
|
|
||||||
default_download_behaviour: open
|
|
||||||
|
|
||||||
#### Manage Page Relations
|
|
||||||
|
|
||||||
// Find documents by page
|
|
||||||
$dms = DMS::getDMSInstance();
|
|
||||||
$page = SiteTree::get()->filter('URLSegment', 'home')->First();
|
|
||||||
$docs = $dms->getByPage($page);
|
|
||||||
|
|
||||||
// Add documents to page
|
|
||||||
|
|
||||||
#### Manage Tags
|
|
||||||
|
|
||||||
// Find documents by tag
|
|
||||||
$dms = DMS::getDMSInstance();
|
|
||||||
$docs = $dms->getByTag('priority', 'important');
|
|
||||||
|
|
||||||
// Add tag to existing document
|
|
||||||
$doc = Document::get()->byID(99);
|
|
||||||
$doc->addTag('priority', 'low');
|
|
||||||
|
|
||||||
// Supports multiple values for tags
|
|
||||||
$doc->addTag('category', 'keyboard');
|
|
||||||
$doc->addTag('category', 'input device');
|
|
||||||
|
|
||||||
// Removing tags is abstracted as well
|
|
||||||
$doc->removeTag('category', 'keyboard');
|
|
||||||
$doc->removeTag('category', 'input device');
|
|
||||||
$doc->removeAllTags();
|
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
### Translations
|
### Translations
|
||||||
|
@ -23,6 +23,7 @@ class DMSDocument extends DataObject implements DMSDocumentInterface
|
|||||||
|
|
||||||
private static $many_many = array(
|
private static $many_many = array(
|
||||||
'Pages' => 'SiteTree',
|
'Pages' => 'SiteTree',
|
||||||
|
'RelatedDocuments' => 'DMSDocument',
|
||||||
'Tags' => 'DMSTag'
|
'Tags' => 'DMSTag'
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -1012,19 +1013,19 @@ class DMSDocument extends DataObject implements DMSDocumentInterface
|
|||||||
$versionsGridFieldConfig
|
$versionsGridFieldConfig
|
||||||
);
|
);
|
||||||
$extraTasks .= '<li class="ss-ui-button" data-panel="find-versions">Versions</li>';
|
$extraTasks .= '<li class="ss-ui-button" data-panel="find-versions">Versions</li>';
|
||||||
//$extraFields = $versionsGrid->addExtraClass('find-versions');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$fields->add(new LiteralField(
|
$fields->add(new LiteralField(
|
||||||
'BottomTaskSelection',
|
'BottomTaskSelection',
|
||||||
'<div id="Actions" class="field actions"><label class="left">Actions</label><ul>'.
|
'<div id="Actions" class="field actions"><label class="left">Actions</label><ul>'
|
||||||
'<li class="ss-ui-button" data-panel="embargo">Embargo</li>'.
|
. '<li class="ss-ui-button" data-panel="embargo">Embargo</li>'
|
||||||
'<li class="ss-ui-button" data-panel="expiry">Expiry</li>'.
|
. '<li class="ss-ui-button" data-panel="expiry">Expiry</li>'
|
||||||
'<li class="ss-ui-button" data-panel="replace">Replace</li>'.
|
. '<li class="ss-ui-button" data-panel="replace">Replace</li>'
|
||||||
'<li class="ss-ui-button" data-panel="find-usage">Usage</li>'.
|
. '<li class="ss-ui-button" data-panel="find-usage">Usage</li>'
|
||||||
'<li class="ss-ui-button" data-panel="find-references">References</li>'.
|
. '<li class="ss-ui-button" data-panel="find-references">References</li>'
|
||||||
$extraTasks.
|
. '<li class="ss-ui-button" data-panel="find-relateddocuments">Related Documents</li>'
|
||||||
'</ul></div>'
|
. $extraTasks
|
||||||
|
. '</ul></div>'
|
||||||
));
|
));
|
||||||
|
|
||||||
$embargoValue = 'None';
|
$embargoValue = 'None';
|
||||||
@ -1076,27 +1077,13 @@ class DMSDocument extends DataObject implements DMSDocumentInterface
|
|||||||
// These are toggled on and off via the Actions Buttons above
|
// These are toggled on and off via the Actions Buttons above
|
||||||
// exit('hit');
|
// exit('hit');
|
||||||
$actionsPanel = FieldGroup::create(
|
$actionsPanel = FieldGroup::create(
|
||||||
|
FieldGroup::create($embargo, $embargoDatetime)->addExtraClass('embargo'),
|
||||||
FieldGroup::create(
|
FieldGroup::create($expiry, $expiryDatetime)->addExtraClass('expiry'),
|
||||||
$embargo,
|
FieldGroup::create($uploadField)->addExtraClass('replace'),
|
||||||
$embargoDatetime
|
FieldGroup::create($pagesGrid)->addExtraClass('find-usage'),
|
||||||
)->addExtraClass('embargo'),
|
FieldGroup::create($referencesGrid)->addExtraClass('find-references'),
|
||||||
FieldGroup::create(
|
FieldGroup::create($versionsGrid)->addExtraClass('find-versions'),
|
||||||
$expiry,
|
FieldGroup::create($this->getRelatedDocumentsGridField())->addExtraClass('find-relateddocuments')
|
||||||
$expiryDatetime
|
|
||||||
)->addExtraClass('expiry'),
|
|
||||||
FieldGroup::create(
|
|
||||||
$uploadField
|
|
||||||
)->addExtraClass('replace'),
|
|
||||||
FieldGroup::create(
|
|
||||||
$pagesGrid
|
|
||||||
)->addExtraClass('find-usage'),
|
|
||||||
FieldGroup::create(
|
|
||||||
$referencesGrid
|
|
||||||
)->addExtraClass('find-references'),
|
|
||||||
FieldGroup::create(
|
|
||||||
$versionsGrid
|
|
||||||
)->addExtraClass('find-versions')
|
|
||||||
);
|
);
|
||||||
|
|
||||||
$actionsPanel->setName("ActionsPanel");
|
$actionsPanel->setName("ActionsPanel");
|
||||||
@ -1104,22 +1091,6 @@ class DMSDocument extends DataObject implements DMSDocumentInterface
|
|||||||
|
|
||||||
$fields->push($actionsPanel);
|
$fields->push($actionsPanel);
|
||||||
|
|
||||||
// $fields->add(FieldGroup::create(
|
|
||||||
// FieldGroup::create(
|
|
||||||
// $embargo,
|
|
||||||
// $embargoDatetime
|
|
||||||
// )->addExtraClass('embargo'),
|
|
||||||
// FieldGroup::create(
|
|
||||||
// $expiry,
|
|
||||||
// $expiryDatetime
|
|
||||||
// )->addExtraClass('expiry'),
|
|
||||||
// $uploadField->addExtraClass('replace'),
|
|
||||||
// $pagesGrid->addExtraClass('find-usage'),
|
|
||||||
// $referencesGrid->addExtraClass('find-references'),
|
|
||||||
// $extraFields
|
|
||||||
// )->setName("ActionsPanel")->addExtraClass('dmsupload ss-uploadfield'));
|
|
||||||
|
|
||||||
|
|
||||||
$this->extend('updateCMSFields', $fields);
|
$this->extend('updateCMSFields', $fields);
|
||||||
|
|
||||||
return $fields;
|
return $fields;
|
||||||
@ -1313,4 +1284,42 @@ class DMSDocument extends DataObject implements DMSDocumentInterface
|
|||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a data list of documents related to this document
|
||||||
|
*
|
||||||
|
* @return DataList
|
||||||
|
*/
|
||||||
|
public function getRelatedDocuments()
|
||||||
|
{
|
||||||
|
$documents = $this->RelatedDocuments();
|
||||||
|
|
||||||
|
$this->extend('updateRelatedDocuments', $documents);
|
||||||
|
|
||||||
|
return $documents;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a GridField for managing related documents
|
||||||
|
*
|
||||||
|
* @return GridField
|
||||||
|
*/
|
||||||
|
protected function getRelatedDocumentsGridField()
|
||||||
|
{
|
||||||
|
$gridField = GridField::create(
|
||||||
|
'RelatedDocuments',
|
||||||
|
_t('DMSDocument.RELATEDDOCUMENTS', 'Related Documents'),
|
||||||
|
$this->RelatedDocuments(),
|
||||||
|
new GridFieldConfig_RelationEditor
|
||||||
|
);
|
||||||
|
|
||||||
|
$gridField->getConfig()->removeComponentsByType('GridFieldAddNewButton');
|
||||||
|
// Move the autocompleter to the left
|
||||||
|
$gridField->getConfig()->removeComponentsByType('GridFieldAddExistingAutocompleter');
|
||||||
|
$gridField->getConfig()->addComponent(new GridFieldAddExistingAutocompleter('buttons-before-left'));
|
||||||
|
|
||||||
|
$this->extend('updateRelatedDocumentsGridField', $gridField);
|
||||||
|
|
||||||
|
return $gridField;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
0
docs/en/_manifest_exclude
Normal file
0
docs/en/_manifest_exclude
Normal file
3
docs/en/configuration.md
Normal file
3
docs/en/configuration.md
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# Configuration
|
||||||
|
|
||||||
|
The file location is set via the `DMS::$dmsFolder` static, and points to a location in the webroot.
|
18
docs/en/creating-documents.md
Normal file
18
docs/en/creating-documents.md
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
# Creating documents
|
||||||
|
|
||||||
|
Create by relative path:
|
||||||
|
|
||||||
|
```php
|
||||||
|
$dms = DMS::getDMSInstance();
|
||||||
|
$doc = $dms->storeDocument('assets/myfile.pdf');
|
||||||
|
```
|
||||||
|
|
||||||
|
Create from an existing `File` record:
|
||||||
|
|
||||||
|
```php
|
||||||
|
$dms = DMS::getDMSInstance();
|
||||||
|
$file = File::get()->byID(99);
|
||||||
|
$doc = $dms->storeDocument($file);
|
||||||
|
```
|
||||||
|
|
||||||
|
Note: Both operations copy the existing file.
|
18
docs/en/documents-on-the-filesystem.md
Normal file
18
docs/en/documents-on-the-filesystem.md
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
# Documents on the Filesystem
|
||||||
|
|
||||||
|
While the DMS architecture allows for remote storage of files, the default implementation (the `DMS` class)
|
||||||
|
stores them locally. Relations to pages and tags are persisted as many-many relationships through the SilverStripe ORM.
|
||||||
|
|
||||||
|
File locations in this implementation are structured into subfolders, in order to avoid exceeding filesystem limits.
|
||||||
|
The file name is a composite based on its database ID and the original file name. The exact location shouldn't be
|
||||||
|
relied on by custom logic, but rather retrieved through the API method `DMSDocument::getLink`.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```
|
||||||
|
dms-assets/
|
||||||
|
0/
|
||||||
|
1234~myfile.pdf
|
||||||
|
1/
|
||||||
|
2345~myotherfile.pdf
|
||||||
|
```
|
28
docs/en/download-documents.md
Normal file
28
docs/en/download-documents.md
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
# Download documents
|
||||||
|
|
||||||
|
## Get the download link
|
||||||
|
|
||||||
|
You can use `DMSDocument::getLink` to retrieve the secure route to download a DMS document:
|
||||||
|
|
||||||
|
```php
|
||||||
|
$dms = DMS::getDMSInstance();
|
||||||
|
$docs = $dms->getByTag('priority', 'important')->First();
|
||||||
|
$link = $doc->getLink();
|
||||||
|
```
|
||||||
|
|
||||||
|
## Default download behaviour
|
||||||
|
|
||||||
|
The default download behaviour is "download" which will force the browser to download the document. You
|
||||||
|
can select "open" as an option in the document's settings in the CMS individually, or you can change the global
|
||||||
|
default value with configuration:
|
||||||
|
|
||||||
|
```php
|
||||||
|
Config::inst()->update('DMSDocument', 'default_download_behaviour', 'open');
|
||||||
|
```
|
||||||
|
|
||||||
|
Or in YAML:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
DMSDocument:
|
||||||
|
default_download_behaviour: open
|
||||||
|
```
|
16
docs/en/index.md
Normal file
16
docs/en/index.md
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
# DMS documentation
|
||||||
|
|
||||||
|
## Development
|
||||||
|
|
||||||
|
* [Configuration](configuration.md)
|
||||||
|
* [Documents on the filesystem](documents-on-the-filesystem.md)
|
||||||
|
* [Use in templates](use-in-templates.md)
|
||||||
|
* [Creating documents](creating-documents.md)
|
||||||
|
* [Download documents](download-documents.md)
|
||||||
|
* [Manage page relations](manage-page-relations.md)
|
||||||
|
* [Manage tags](manage-tags.md)
|
||||||
|
* [Manage related documents](manage-related-documents.md)
|
||||||
|
|
||||||
|
## CMS user help
|
||||||
|
|
||||||
|
* TBC
|
10
docs/en/manage-page-relations.md
Normal file
10
docs/en/manage-page-relations.md
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
# Manage page relations
|
||||||
|
|
||||||
|
To find documents by a Page:
|
||||||
|
|
||||||
|
```php
|
||||||
|
$dms = DMS::getDMSInstance();
|
||||||
|
$page = SiteTree::get()->filter('URLSegment', 'home')->first();
|
||||||
|
/** @var DataList $docs */
|
||||||
|
$docs = $dms->getByPage($page);
|
||||||
|
```
|
50
docs/en/manage-related-documents.md
Normal file
50
docs/en/manage-related-documents.md
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
# Manage related documents
|
||||||
|
|
||||||
|
You can relate documents to each other using the GridField under "Related Documents" in the CMS.
|
||||||
|
|
||||||
|
## Add related documents
|
||||||
|
|
||||||
|
You can use the model relationship `DMSDocument::RelatedDocuments` to modify the DataList and save as required:
|
||||||
|
|
||||||
|
```php
|
||||||
|
$parentDocument = DMSDocument::get()->byId(123);
|
||||||
|
|
||||||
|
$relatedDocument = DMSDocument::get()->byId(234);
|
||||||
|
|
||||||
|
$parentDocument->RelatedDocuments()->add($relatedDocument);
|
||||||
|
```
|
||||||
|
|
||||||
|
Using the relationship method directly will skip the extension hook available in `getRelatedDocuments` (see below).
|
||||||
|
|
||||||
|
## Modifying the related documents list
|
||||||
|
|
||||||
|
If you need to modify the related documents DataList returned by the ORM, use the `updateRelatedDocuments` extension
|
||||||
|
hook provided by `DMSDocument::getRelatedDocuments`:
|
||||||
|
|
||||||
|
```php
|
||||||
|
# MyExtension is an extension applied to DMSDocument
|
||||||
|
class MyExtension extends DataExtension
|
||||||
|
{
|
||||||
|
public function updateRelatedDocuments($relatedDocuments)
|
||||||
|
{
|
||||||
|
foreach ($relatedDocuments as $document) {
|
||||||
|
// Add square brackets around the description
|
||||||
|
$document->Description = '[' . $document->Description . ']';
|
||||||
|
}
|
||||||
|
return $relatedDocuments;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Retrieving related documents
|
||||||
|
|
||||||
|
To retrieve a DataList of related documents you can either use `getRelatedDocuments` or the ORM relationship method
|
||||||
|
`RelatedDocuments` directly. The former will allow extensions to modify the list, whereas the latter will not.
|
||||||
|
|
||||||
|
```php
|
||||||
|
$relatedDocuments = $document->getRelatedDocuments();
|
||||||
|
|
||||||
|
foreach ($relatedDocuments as $relatedDocument) {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
```
|
32
docs/en/manage-tags.md
Normal file
32
docs/en/manage-tags.md
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
# 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();
|
||||||
|
```
|
11
docs/en/use-in-templates.md
Normal file
11
docs/en/use-in-templates.md
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
# Use in templates
|
||||||
|
|
||||||
|
Add a simple include to any of your `.ss` templates to display the DMSDocuments associated with
|
||||||
|
the current page on the front-end.
|
||||||
|
|
||||||
|
```
|
||||||
|
<% include Documents %>
|
||||||
|
```
|
||||||
|
|
||||||
|
You can fine tune the HTML markup or display behaviour of any of the templates in `/dms/templates/Includes` to change
|
||||||
|
the way documents will be displayed in your project.
|
@ -14,6 +14,8 @@ en:
|
|||||||
RelatedReferences: 'Related References'
|
RelatedReferences: 'Related References'
|
||||||
SINGULARNAME: Document
|
SINGULARNAME: Document
|
||||||
Versions: Versions
|
Versions: Versions
|
||||||
|
DOWNLOAD: "Download {title}"
|
||||||
|
LASTCHANGED: "Last changed: {date}"
|
||||||
DMSTag:
|
DMSTag:
|
||||||
PLURALNAME: 'D M S Tags'
|
PLURALNAME: 'D M S Tags'
|
||||||
SINGULARNAME: 'D M S Tag'
|
SINGULARNAME: 'D M S Tag'
|
||||||
@ -35,6 +37,7 @@ en:
|
|||||||
DMSDocumentAddController:
|
DMSDocumentAddController:
|
||||||
MENUTITLE: 'Edit Page'
|
MENUTITLE: 'Edit Page'
|
||||||
NODOCUMENTS: 'There are no documents attached to the selected page.'
|
NODOCUMENTS: 'There are no documents attached to the selected page.'
|
||||||
|
RELATEDDOCUMENTS: 'Related Documents'
|
||||||
DMSDocument_versions:
|
DMSDocument_versions:
|
||||||
PLURALNAME: 'D M S Document_versionss'
|
PLURALNAME: 'D M S Document_versionss'
|
||||||
SINGULARNAME: 'D M S Document_versions'
|
SINGULARNAME: 'D M S Document_versions'
|
||||||
|
@ -1,19 +1,18 @@
|
|||||||
<% if isHidden != true %>
|
<% if $isHidden != true %>
|
||||||
<div class="document $Extension">
|
<div class="document $Extension">
|
||||||
<% if Title %>
|
<% if $Title %>
|
||||||
<h4><a href="$Link" title="Download $Title">$Title</a></h4>
|
<h4><a href="$Link" title="<%t DMSDocument.DOWNLOAD "Download {title}" title=$Title %>">$Title</a></h4>
|
||||||
<% else %>
|
<% else %>
|
||||||
<h4><a href="$Link" title="Download $FilenameWithoutID">$FilenameWithoutID</a></h4>
|
<h4><a href="$Link" title="<%t DMSDocument.DOWNLOAD "Download {title}" title=$FilenameWithoutID %>">$FilenameWithoutID</a></h4>
|
||||||
<% end_if %>
|
<% end_if %>
|
||||||
|
|
||||||
<p class='details'>
|
<p class="details"><% include DocumentDetails %></p>
|
||||||
<strong>$FilenameWithoutID</strong>
|
<% if $Description %>
|
||||||
| $Extension
|
<p>$DescriptionWithLineBreak</p>
|
||||||
| $FileSizeFormatted
|
<% end_if %>
|
||||||
| Last Changed: $LastChanged.Nice
|
|
||||||
</p>
|
<% if $getRelatedDocuments %>
|
||||||
<% if Description %>
|
<% include RelatedDocuments %>
|
||||||
<p>$DescriptionWithLineBreak</p>
|
<% end_if %>
|
||||||
<% end_if %>
|
</div>
|
||||||
</div>
|
|
||||||
<% end_if %>
|
<% end_if %>
|
4
templates/Includes/DocumentDetails.ss
Normal file
4
templates/Includes/DocumentDetails.ss
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<strong>$FilenameWithoutID</strong>
|
||||||
|
| $Extension
|
||||||
|
| $FileSizeFormatted
|
||||||
|
| <%t DMSDocument.LASTCHANGED "Last changed: {date}" date=$LastChanged.Nice %>
|
@ -1,4 +1,8 @@
|
|||||||
<% if PageDocuments %>
|
<% if $PageDocuments %>
|
||||||
<div><h3>Documents:</h3></div>
|
<div class="documents">
|
||||||
<% loop PageDocuments %><% include Document %><% end_loop %>
|
<h3><%t DMSDocument.PLURALNAME "Documents" %></h3>
|
||||||
|
<% loop $PageDocuments %>
|
||||||
|
<% include Document %>
|
||||||
|
<% end_loop %>
|
||||||
|
</div>
|
||||||
<% end_if %>
|
<% end_if %>
|
||||||
|
14
templates/Includes/RelatedDocuments.ss
Normal file
14
templates/Includes/RelatedDocuments.ss
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<h5><%t DMSDocument.RELATED_DOCUMENTS "Related documents" %></h5>
|
||||||
|
|
||||||
|
<ul class="documents-relateddocuments">
|
||||||
|
<% loop $getRelatedDocuments %>
|
||||||
|
<li>
|
||||||
|
<% if $Title %>
|
||||||
|
<a href="$Link" title="<%t DMSDocument.DOWNLOAD "Download {title}" title=$Title %>">$Title</a>
|
||||||
|
<% else %>
|
||||||
|
<a href="$Link" title="<%t DMSDocument.DOWNLOAD "Download {title}" title=$FilenameWithoutID %>">$FilenameWithoutID</a>
|
||||||
|
<% end_if %>
|
||||||
|
<span class="documents-relateddocuments-documentdetails"><% include DocumentDetails %></span>
|
||||||
|
</li>
|
||||||
|
<% end_loop %>
|
||||||
|
</ul>
|
@ -1,17 +1,17 @@
|
|||||||
<?php
|
<?php
|
||||||
class DMSDocumentTest extends SapphireTest
|
class DMSDocumentTest extends SapphireTest
|
||||||
{
|
{
|
||||||
protected static $fixture_file = "dmstest.yml";
|
protected static $fixture_file = 'dmstest.yml';
|
||||||
|
|
||||||
public function tearDownOnce()
|
public function tearDownOnce()
|
||||||
{
|
{
|
||||||
self::$is_running_test = true;
|
self::$is_running_test = true;
|
||||||
|
|
||||||
$d = DataObject::get("DMSDocument");
|
$d = DataObject::get('DMSDocument');
|
||||||
foreach ($d as $d1) {
|
foreach ($d as $d1) {
|
||||||
$d1->delete();
|
$d1->delete();
|
||||||
}
|
}
|
||||||
$t = DataObject::get("DMSTag");
|
$t = DataObject::get('DMSTag');
|
||||||
foreach ($t as $t1) {
|
foreach ($t as $t1) {
|
||||||
$t1->delete();
|
$t1->delete();
|
||||||
}
|
}
|
||||||
@ -32,12 +32,12 @@ class DMSDocumentTest extends SapphireTest
|
|||||||
|
|
||||||
$pages = $d1->Pages();
|
$pages = $d1->Pages();
|
||||||
$pagesArray = $pages->toArray();
|
$pagesArray = $pages->toArray();
|
||||||
$this->assertEquals($pagesArray[0]->ID, $s1->ID, "Page 1 associated correctly");
|
$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[1]->ID, $s2->ID, 'Page 2 associated correctly');
|
||||||
$this->assertEquals($pagesArray[2]->ID, $s3->ID, "Page 3 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[3]->ID, $s4->ID, 'Page 4 associated correctly');
|
||||||
$this->assertEquals($pagesArray[4]->ID, $s5->ID, "Page 5 associated correctly");
|
$this->assertEquals($pagesArray[4]->ID, $s5->ID, 'Page 5 associated correctly');
|
||||||
$this->assertEquals($pagesArray[5]->ID, $s6->ID, "Page 6 associated correctly");
|
$this->assertEquals($pagesArray[5]->ID, $s6->ID, 'Page 6 associated correctly');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testAddPageRelation()
|
public function testAddPageRelation()
|
||||||
@ -47,8 +47,8 @@ class DMSDocumentTest extends SapphireTest
|
|||||||
$s3 = $this->objFromFixture('SiteTree', 's3');
|
$s3 = $this->objFromFixture('SiteTree', 's3');
|
||||||
|
|
||||||
$doc = new DMSDocument();
|
$doc = new DMSDocument();
|
||||||
$doc->Filename = "test file";
|
$doc->Filename = 'test file';
|
||||||
$doc->Folder = "0";
|
$doc->Folder = '0';
|
||||||
$doc->write();
|
$doc->write();
|
||||||
|
|
||||||
$doc->addPage($s1);
|
$doc->addPage($s1);
|
||||||
@ -57,15 +57,15 @@ class DMSDocumentTest extends SapphireTest
|
|||||||
|
|
||||||
$pages = $doc->Pages();
|
$pages = $doc->Pages();
|
||||||
$pagesArray = $pages->toArray();
|
$pagesArray = $pages->toArray();
|
||||||
$this->assertEquals($pagesArray[0]->ID, $s1->ID, "Page 1 associated correctly");
|
$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[1]->ID, $s2->ID, 'Page 2 associated correctly');
|
||||||
$this->assertEquals($pagesArray[2]->ID, $s3->ID, "Page 3 associated correctly");
|
$this->assertEquals($pagesArray[2]->ID, $s3->ID, 'Page 3 associated correctly');
|
||||||
|
|
||||||
$doc->removePage($s1);
|
$doc->removePage($s1);
|
||||||
$pages = $doc->Pages();
|
$pages = $doc->Pages();
|
||||||
$pagesArray = $pages->toArray(); // Page 1 is missing
|
$pagesArray = $pages->toArray(); // Page 1 is missing
|
||||||
$this->assertEquals($pagesArray[0]->ID, $s2->ID, "Page 2 still associated correctly");
|
$this->assertEquals($pagesArray[0]->ID, $s2->ID, 'Page 2 still associated correctly');
|
||||||
$this->assertEquals($pagesArray[1]->ID, $s3->ID, "Page 3 still associated correctly");
|
$this->assertEquals($pagesArray[1]->ID, $s3->ID, 'Page 3 still associated correctly');
|
||||||
|
|
||||||
$documents = $s2->Documents();
|
$documents = $s2->Documents();
|
||||||
$documentsArray = $documents->toArray();
|
$documentsArray = $documents->toArray();
|
||||||
@ -74,16 +74,16 @@ class DMSDocumentTest extends SapphireTest
|
|||||||
array('Filename' => $doc->Filename)
|
array('Filename' => $doc->Filename)
|
||||||
),
|
),
|
||||||
$documentsArray,
|
$documentsArray,
|
||||||
"Document associated with page"
|
'Document associated with page'
|
||||||
);
|
);
|
||||||
|
|
||||||
$doc->removeAllPages();
|
$doc->removeAllPages();
|
||||||
$pages = $doc->Pages();
|
$pages = $doc->Pages();
|
||||||
$this->assertEquals($pages->Count(), 0, "All pages removed");
|
$this->assertEquals($pages->Count(), 0, 'All pages removed');
|
||||||
|
|
||||||
$documents = $s2->Documents();
|
$documents = $s2->Documents();
|
||||||
$documentsArray = $documents->toArray();
|
$documentsArray = $documents->toArray();
|
||||||
$this->assertNotContains($doc, $documentsArray, "Document no longer associated with page");
|
$this->assertNotContains($doc, $documentsArray, 'Document no longer associated with page');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testDeletingPageWithAssociatedDocuments()
|
public function testDeletingPageWithAssociatedDocuments()
|
||||||
@ -94,8 +94,8 @@ class DMSDocumentTest extends SapphireTest
|
|||||||
$s2ID = $s2->ID;
|
$s2ID = $s2->ID;
|
||||||
|
|
||||||
$doc = new DMSDocument();
|
$doc = new DMSDocument();
|
||||||
$doc->Filename = "delete test file";
|
$doc->Filename = 'delete test file';
|
||||||
$doc->Folder = "0";
|
$doc->Folder = '0';
|
||||||
$doc->write();
|
$doc->write();
|
||||||
|
|
||||||
$doc->addPage($s1);
|
$doc->addPage($s1);
|
||||||
@ -125,8 +125,8 @@ class DMSDocumentTest extends SapphireTest
|
|||||||
$this->assertEquals(
|
$this->assertEquals(
|
||||||
$documents->Count(),
|
$documents->Count(),
|
||||||
'0',
|
'0',
|
||||||
"However, deleting the live version of the last page that a document is "
|
'However, deleting the live version of the last page that a document is '
|
||||||
."associated with causes that document to be deleted as well"
|
. 'associated with causes that document to be deleted as well'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -137,8 +137,8 @@ class DMSDocumentTest extends SapphireTest
|
|||||||
$s2ID = $s2->ID;
|
$s2ID = $s2->ID;
|
||||||
|
|
||||||
$doc = new DMSDocument();
|
$doc = new DMSDocument();
|
||||||
$doc->Filename = "delete test file";
|
$doc->Filename = 'delete test file';
|
||||||
$doc->Folder = "0";
|
$doc->Folder = '0';
|
||||||
$doc->write();
|
$doc->write();
|
||||||
|
|
||||||
$doc->addPage($s2);
|
$doc->addPage($s2);
|
||||||
@ -158,8 +158,8 @@ class DMSDocumentTest extends SapphireTest
|
|||||||
$this->assertEquals(
|
$this->assertEquals(
|
||||||
$documents->Count(),
|
$documents->Count(),
|
||||||
'0',
|
'0',
|
||||||
"However, deleting the draft version of the last page that a document is "
|
'However, deleting the draft version of the last page that a document is '
|
||||||
."associated with causes that document to be deleted as well"
|
. 'associated with causes that document to be deleted as well'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -175,4 +175,66 @@ class DMSDocumentTest extends SapphireTest
|
|||||||
$cmsFields = $document->getCMSFields();
|
$cmsFields = $document->getCMSFields();
|
||||||
$this->assertEquals('download', $cmsFields->dataFieldByName('DownloadBehavior')->Value());
|
$this->assertEquals('download', $cmsFields->dataFieldByName('DownloadBehavior')->Value());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensure that related documents can be retrieved for a given DMS document
|
||||||
|
*/
|
||||||
|
public function testRelatedDocuments()
|
||||||
|
{
|
||||||
|
$document = $this->objFromFixture('DMSDocument', 'document_with_relations');
|
||||||
|
$this->assertGreaterThan(0, $document->RelatedDocuments()->count());
|
||||||
|
$this->assertEquals(
|
||||||
|
array('test-file-file-doesnt-exist-1', 'test-file-file-doesnt-exist-2'),
|
||||||
|
$document->getRelatedDocuments()->column('Filename')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test the extensibility of getRelatedDocuments
|
||||||
|
*/
|
||||||
|
public function testGetRelatedDocumentsIsExtensible()
|
||||||
|
{
|
||||||
|
DMSDocument::add_extension('StubRelatedDocumentExtension');
|
||||||
|
|
||||||
|
$emptyDocument = new DMSDocument;
|
||||||
|
$relatedDocuments = $emptyDocument->getRelatedDocuments();
|
||||||
|
|
||||||
|
$this->assertCount(1, $relatedDocuments);
|
||||||
|
$this->assertSame('Extended', $relatedDocuments->first()->Filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensure that the DMS Document CMS actions contains a grid field for managing related documents
|
||||||
|
*/
|
||||||
|
public function testDocumentHasCmsFieldForManagingRelatedDocuments()
|
||||||
|
{
|
||||||
|
$document = $this->objFromFixture('DMSDocument', 'document_with_relations');
|
||||||
|
|
||||||
|
$documentFields = $document->getCMSFields();
|
||||||
|
/** @var FieldGroup $actions */
|
||||||
|
$actions = $documentFields->fieldByName('ActionsPanel');
|
||||||
|
|
||||||
|
$gridField = null;
|
||||||
|
foreach ($actions->getChildren() as $child) {
|
||||||
|
/** @var FieldGroup $child */
|
||||||
|
if ($gridField = $child->fieldByName('RelatedDocuments')) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$this->assertInstanceOf('GridField', $gridField);
|
||||||
|
|
||||||
|
/** @var GridFieldConfig $gridFieldConfig */
|
||||||
|
$gridFieldConfig = $gridField->getConfig();
|
||||||
|
|
||||||
|
$this->assertNotNull(
|
||||||
|
'GridFieldAddExistingAutocompleter',
|
||||||
|
$addExisting = $gridFieldConfig->getComponentByType('GridFieldAddExistingAutocompleter'),
|
||||||
|
'Related documents GridField has an "add existing" autocompleter'
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertNull(
|
||||||
|
$gridFieldConfig->getComponentByType('GridFieldAddNewButton'),
|
||||||
|
'Related documents GridField does not have an "add new" button'
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
18
tests/Stub/StubRelatedDocumentExtension.php
Normal file
18
tests/Stub/StubRelatedDocumentExtension.php
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
class StubRelatedDocumentExtension extends DataExtension implements TestOnly
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Push a fixed array entry into the datalist for extensibility testing
|
||||||
|
*
|
||||||
|
* @param ArrayList $relatedDocuments
|
||||||
|
* @return ArrayList
|
||||||
|
*/
|
||||||
|
public function updateRelatedDocuments(ArrayList $relatedDocuments)
|
||||||
|
{
|
||||||
|
$fakeDocument = new DMSDocument;
|
||||||
|
$fakeDocument->Filename = 'Extended';
|
||||||
|
$relatedDocuments->push($fakeDocument);
|
||||||
|
return $relatedDocuments;
|
||||||
|
}
|
||||||
|
}
|
@ -38,12 +38,16 @@ DMSTag:
|
|||||||
Value: tag6value
|
Value: tag6value
|
||||||
DMSDocument:
|
DMSDocument:
|
||||||
d1:
|
d1:
|
||||||
Filename: test-file-file-doesnt-exist
|
Filename: test-file-file-doesnt-exist-1
|
||||||
Folder: 5
|
Folder: 5
|
||||||
Tags: =>DMSTag.t1, =>DMSTag.t2, =>DMSTag.t3, =>DMSTag.t4
|
Tags: =>DMSTag.t1, =>DMSTag.t2, =>DMSTag.t3, =>DMSTag.t4
|
||||||
Pages: =>SiteTree.s1, =>SiteTree.s2, =>SiteTree.s3, =>SiteTree.s4, =>SiteTree.s5, =>SiteTree.s6
|
Pages: =>SiteTree.s1, =>SiteTree.s2, =>SiteTree.s3, =>SiteTree.s4, =>SiteTree.s5, =>SiteTree.s6
|
||||||
d2:
|
d2:
|
||||||
Filename: test-file-file-doesnt-exist
|
Filename: test-file-file-doesnt-exist-2
|
||||||
Folder: 5
|
Folder: 5
|
||||||
Tags: =>DMSTag.t5, =>DMSTag.t6
|
Tags: =>DMSTag.t5, =>DMSTag.t6
|
||||||
Pages: =>SiteTree.s5, =>SiteTree.s6
|
Pages: =>SiteTree.s5, =>SiteTree.s6
|
||||||
|
document_with_relations:
|
||||||
|
Filename: file-with-relations
|
||||||
|
Folder: 5
|
||||||
|
RelatedDocuments: =>DMSDocument.d1, =>DMSDocument.d2
|
||||||
|
Loading…
x
Reference in New Issue
Block a user