mirror of
https://github.com/silverstripe/silverstripe-dms
synced 2024-10-22 14:05:56 +02:00
API-CHANGE: dms shortcode system
This commit is contained in:
parent
80c3f3f757
commit
bac9666597
@ -1,3 +1,6 @@
|
||||
<?php
|
||||
Object::add_extension('SiteTree','DMSSiteTreeExtension');
|
||||
CMSMenu::remove_menu_item('DMSDocumentAddController');
|
||||
Object::add_extension('HtmlEditorField_Toolbar','DocumentHtmlEditorFieldToolbar');
|
||||
CMSMenu::remove_menu_item('DMSDocumentAddController');
|
||||
|
||||
ShortcodeParser::get('default')->register('dms_document_link', array('DMSDocument_Controller', 'dms_link_shortcode_handler'));
|
@ -519,12 +519,21 @@ class DMSDocument extends DataObject implements DMSDocumentInterface {
|
||||
$gridFieldConfig
|
||||
);
|
||||
|
||||
$relationFinder = new ShortCodeRelationFinder();
|
||||
$referencesGrid = GridField::create(
|
||||
'References',
|
||||
_t('DMSDocument.RelatedReferences', 'Related References'),
|
||||
$relationFinder->getList($this->ID),
|
||||
$gridFieldConfig
|
||||
);
|
||||
|
||||
$fields->add(new LiteralField('BottomTaskSelection',
|
||||
'<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="expiry">Expiry</li>'.
|
||||
'<li class="ss-ui-button" data-panel="replace">Replace</li>'.
|
||||
'<li class="ss-ui-button" data-panel="find-usage">Find usage</li>'.
|
||||
'<li class="ss-ui-button" data-panel="find-references">Find references</li>'.
|
||||
'</ul></div>'));
|
||||
|
||||
$embargoValue = 'None';
|
||||
@ -554,7 +563,8 @@ class DMSDocument extends DataObject implements DMSDocumentInterface {
|
||||
$expiryDatetime
|
||||
)->addExtraClass('expiry'),
|
||||
$uploadField->addExtraClass('replace'),
|
||||
$pagesGrid->addExtraClass('find-usage')
|
||||
$pagesGrid->addExtraClass('find-usage'),
|
||||
$referencesGrid->addExtraClass('find-references')
|
||||
)->setName("ActionsPanel")->addExtraClass('dmsupload ss-uploadfield'));
|
||||
|
||||
$this->extend('updateCMSFields', $fields);
|
||||
@ -775,5 +785,32 @@ class DMSDocument_Controller extends Controller {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Handles dms_document_link shortcode
|
||||
* @return string
|
||||
*/
|
||||
public static function dms_link_shortcode_handler($arguments, $content = null, $parser = null) {
|
||||
$linkText = null;
|
||||
if(isset($arguments['id']) && is_numeric($arguments['id'])) {
|
||||
// get the document object
|
||||
$document = DataObject::get_by_id('DMSDocument', Convert::raw2sql($arguments['id']));
|
||||
if ($document && !$document->isHidden()) {
|
||||
if( $content ) {
|
||||
$linkText = sprintf('<a href="%s">%s</a>', $document->Link(), $parser->parse($content));
|
||||
} else {
|
||||
$extension = $document->getFileExt();
|
||||
$size = "data:{size:'{$document->getFileSizeFormatted()}'}";
|
||||
$linkText = $document->getDownloadLink()."\" class=\"$size documentLink $extension";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!$linkText) {
|
||||
$errorPage = DataObject::get_one('ErrorPage', '"ErrorCode" = \'404\'');
|
||||
if ($errorPage) $linkText = $errorPage->Link();
|
||||
}
|
||||
return $linkText;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -10,6 +10,9 @@ class DMSSiteTreeExtension extends DataExtension {
|
||||
Requirements::javascript('dms/javascript/DMSGridField.js');
|
||||
Requirements::css('dms/css/DMSMainCMS.css');
|
||||
|
||||
//javascript for the link editor pop-up in TinyMCE
|
||||
Requirements::javascript("dms/javascript/DocumentHtmlEditorFieldToolbar.js");
|
||||
|
||||
// Document listing
|
||||
$gridFieldConfig = GridFieldConfig::create()->addComponents(
|
||||
new GridFieldToolbarHeader(),
|
||||
|
@ -1,6 +1,9 @@
|
||||
<?php
|
||||
|
||||
class DMSDocumentAddExistingField extends CompositeField {
|
||||
|
||||
public $useFieldContext = true;
|
||||
|
||||
function __construct($name, $title = null) {
|
||||
$this->name = $name;
|
||||
$this->title = ($title === null) ? $name : $title;
|
||||
@ -41,6 +44,14 @@ class DMSDocumentAddExistingField extends CompositeField {
|
||||
|
||||
return $this->renderWith('DMSDocumentAddExistingField');
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets or unsets the use of the "field" class in the template. The "field" class adds Javascript behaviour
|
||||
* that causes unwelcome hiding side-effects when this Field is used within the link editor pop-up
|
||||
*/
|
||||
public function setUseFieldClass($use = false) {
|
||||
$this->useFieldContext = $use;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
40
code/cms/DocumentHtmlEditorFieldToolbar.php
Normal file
40
code/cms/DocumentHtmlEditorFieldToolbar.php
Normal file
@ -0,0 +1,40 @@
|
||||
<?php
|
||||
/**
|
||||
* Extends the original toolbar with document picking capability - modified lines are commented.
|
||||
*/
|
||||
|
||||
|
||||
class DocumentHtmlEditorFieldToolbar extends Extension {
|
||||
|
||||
function updateLinkForm(Form $form) {
|
||||
$linkType = null;
|
||||
$fieldList = null;
|
||||
$fields = $form->Fields();//->fieldByName('Heading');
|
||||
foreach($fields as $field) {
|
||||
$linkType = ($field->fieldByName('LinkType'));
|
||||
$fieldList = $field;
|
||||
if ($linkType) break; //break once we have the object
|
||||
}
|
||||
|
||||
$source = $linkType->getSource();
|
||||
$source['document'] = 'Download a document';
|
||||
$linkType->setSource($source);
|
||||
|
||||
$addExistingField = new DMSDocumentAddExistingField('AddExisting', 'Add Existing');
|
||||
$addExistingField->setForm($form);
|
||||
$addExistingField->setUseFieldClass(false);
|
||||
$fieldList->insertAfter($addExistingField,'Locale');
|
||||
|
||||
// Requirements::javascript(SAPPHIRE_DIR . "/thirdparty/behaviour/behaviour.js");
|
||||
// Requirements::javascript(SAPPHIRE_DIR . "/javascript/tiny_mce_improvements.js");
|
||||
//
|
||||
// // create additional field, rebase to 'documents' directory
|
||||
// $documents = new TreeDropdownField('document', 'Document', 'File', 'ID', 'DocumentDropdownTitle', true);
|
||||
// $documents->setSearchFunction(array($this, 'documentSearchCallback'));
|
||||
// $baseFolder = Folder::find_or_make(Document::$directory);
|
||||
// $documents->setTreeBaseID($baseFolder->ID);
|
||||
|
||||
|
||||
//return $form;
|
||||
}
|
||||
}
|
76
code/tools/ShortCodeRelationFinder.php
Normal file
76
code/tools/ShortCodeRelationFinder.php
Normal file
@ -0,0 +1,76 @@
|
||||
<?php
|
||||
/**
|
||||
* Finds {@link DataObject} instances using certain shortcodes
|
||||
* by fulltext-querying only fields which are capable of parsing shortcodes.
|
||||
* Effectively the reverse of "link tracking",
|
||||
* which updates this relation on write rather than fetching it on demand.
|
||||
*
|
||||
* Doesn't scale to millions of pages due to triggering a potentially unindexed LIKE
|
||||
* search across dozens of columns and tables - but for a couple of hundred pages
|
||||
* and occasionally use its a feasible solution.
|
||||
*/
|
||||
class ShortCodeRelationFinder {
|
||||
|
||||
/**
|
||||
* @var String Regex matching a {@link DBField} class name which is shortcode capable.
|
||||
*
|
||||
* This should really look for implementors of a ShortCodeParseable interface,
|
||||
* but we can't extend the core Text and HTMLText class
|
||||
* on existing like SiteTree.Content for this.
|
||||
*/
|
||||
protected $fieldSpecRegex = '/^(HTMLText)/';
|
||||
|
||||
/**
|
||||
* @param String Shortcode index number to find
|
||||
* @return array IDs
|
||||
*/
|
||||
function findPageIDs($number) {
|
||||
$list = $this->getList($number);
|
||||
$found = $list->column();
|
||||
return $found;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return DataList
|
||||
*/
|
||||
function getList($number) {
|
||||
$list = DataList::create('SiteTree');
|
||||
$where = array();
|
||||
$fields = $this->getShortCodeFields('SiteTree');
|
||||
foreach($fields as $ancClass => $ancFields) {
|
||||
foreach($ancFields as $ancFieldName => $ancFieldSpec) {
|
||||
if ($ancClass != "SiteTree") $list = $list->leftJoin($ancClass,'"'.$ancClass.'"."ID" = "SiteTree"."ID"');
|
||||
$where[] = "\"$ancClass\".\"$ancFieldName\" LIKE '%[dms_document_link,id=$number]%'"; //."%s" LIKE ""',
|
||||
}
|
||||
}
|
||||
|
||||
$list->where(implode(' OR ',$where));
|
||||
return $list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a filtered list of fields which could contain shortcodes.
|
||||
*
|
||||
* @param String
|
||||
* @return Array Map of class names to an array of field names on these classes.
|
||||
*/
|
||||
function getShortcodeFields($class) {
|
||||
$fields = array();
|
||||
$ancestry = array_values(ClassInfo::dataClassesFor($class));
|
||||
|
||||
foreach($ancestry as $ancestor) {
|
||||
if(ClassInfo::classImplements($ancestor, 'TestOnly')) continue;
|
||||
|
||||
$ancFields = DataObject::custom_database_fields($ancestor);
|
||||
if($ancFields) foreach($ancFields as $ancFieldName => $ancFieldSpec) {
|
||||
if(preg_match($this->fieldSpecRegex, $ancFieldSpec)) {
|
||||
if(!@$fields[$ancestor]) $fields[$ancestor] = array();
|
||||
$fields[$ancestor][$ancFieldName] = $ancFieldSpec;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
}
|
@ -26,6 +26,17 @@
|
||||
}
|
||||
}
|
||||
);
|
||||
},
|
||||
selectdocument: function(documentID, documentName) {
|
||||
if (typeof(documentID) !== "undefined") {
|
||||
//substitute the ID for the full document name, if no name is present
|
||||
if (typeof(documentName) === "undefined") {
|
||||
documentName = documentID;
|
||||
}
|
||||
$('.ss-add-files').html('<div class="selected-document" data-document-id="'+documentID+'">'+documentName+'</div>');
|
||||
} else {
|
||||
$('.ss-add-files').html('');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@ -36,10 +47,12 @@
|
||||
source: 'admin/pages/adddocument/documentautocomplete',
|
||||
select: function(event, ui) {
|
||||
if(ui.item) {
|
||||
var document_id = ui.item.value;
|
||||
|
||||
$(this).closest('.document-add-existing').adddocument(document_id);
|
||||
$(this).val('');
|
||||
if (self.closest('.document-add-existing').hasClass('link-editor-context')) {
|
||||
$(this).closest('.document-add-existing').selectdocument(ui.item.value, ui.item.label);
|
||||
} else {
|
||||
$(this).closest('.document-add-existing').adddocument(ui.item.value);
|
||||
$(this).val('');
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
@ -78,12 +91,19 @@
|
||||
|
||||
$('.document-add-existing a.add-document').entwine({
|
||||
onclick: function(event) {
|
||||
var document_id = $(this).data('document-id');
|
||||
var document_id = this.data('document-id');
|
||||
var dae = this.closest('.document-add-existing');
|
||||
|
||||
$(this).closest('.document-add-existing').adddocument(document_id);
|
||||
$(this).closest('.document-list').hide();
|
||||
$(this).closest('.document-add-existing').find('input.document-autocomplete').prop('disabled', false);
|
||||
|
||||
if (dae.hasClass('link-editor-context')) {
|
||||
dae.selectdocument(document_id, this.text());
|
||||
} else {
|
||||
dae.adddocument(document_id);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
107
javascript/DocumentHtmlEditorFieldToolbar.js
Normal file
107
javascript/DocumentHtmlEditorFieldToolbar.js
Normal file
@ -0,0 +1,107 @@
|
||||
(function($) {
|
||||
"use strict";
|
||||
|
||||
$.entwine('ss', function($) {
|
||||
|
||||
$('form.htmleditorfield-linkform input[name=LinkType]').entwine({
|
||||
onchange: function(e) {
|
||||
this._super(e);
|
||||
|
||||
var form = $('form.htmleditorfield-linkform');
|
||||
var show = false;
|
||||
|
||||
if (this.attr('value') === 'document') {
|
||||
if (this.is(':checked')) {
|
||||
show = true;
|
||||
}
|
||||
}
|
||||
|
||||
//hide or show the additional document link addition tool
|
||||
if (show) {
|
||||
form.find('.ss-add.ss-upload').show();
|
||||
} else {
|
||||
form.find('.ss-add.ss-upload').hide();
|
||||
}
|
||||
},
|
||||
onadd: function(e){
|
||||
this.change();
|
||||
}
|
||||
});
|
||||
|
||||
$('form.htmleditorfield-linkform').entwine({
|
||||
insertLink: function() {
|
||||
var href, target = null;
|
||||
var checkedValue = this.find(':input[name=LinkType]:checked').val();
|
||||
if (checkedValue === 'document') {
|
||||
href = '[dms_document_link,id=' + this.find('.selected-document').data('document-id') + ']';
|
||||
|
||||
// Determine target
|
||||
if(this.find(':input[name=TargetBlank]').is(':checked')) target = '_blank';
|
||||
|
||||
var attributes = {
|
||||
href : href,
|
||||
target : target,
|
||||
title : this.find('.selected-document').text() //title is the text of the selected document
|
||||
};
|
||||
|
||||
this.modifySelection(function(ed){
|
||||
ed.insertLink(attributes);
|
||||
});
|
||||
|
||||
this.updateFromEditor();
|
||||
return false;
|
||||
} else {
|
||||
this._super();
|
||||
}
|
||||
},
|
||||
getCurrentLink: function() {
|
||||
var selectedEl = this.getSelection(), href = "", target = "", title = "", action = "insert", style_class = "";
|
||||
var linkDataSource = null;
|
||||
if(selectedEl.length) {
|
||||
if(selectedEl.is('a')) {
|
||||
linkDataSource = selectedEl;
|
||||
} else {
|
||||
linkDataSource = selectedEl = selectedEl.parents('a:first');
|
||||
}
|
||||
}
|
||||
if(linkDataSource && linkDataSource.length) this.modifySelection(function(ed){
|
||||
ed.selectNode(linkDataSource[0]);
|
||||
});
|
||||
|
||||
// Is anchor not a link
|
||||
if (!linkDataSource.attr('href')) linkDataSource = null;
|
||||
|
||||
if (linkDataSource) {
|
||||
href = linkDataSource.attr('href');
|
||||
target = linkDataSource.attr('target');
|
||||
title = linkDataSource.attr('title');
|
||||
style_class = linkDataSource.attr('class');
|
||||
href = this.getEditor().cleanLink(href, linkDataSource);
|
||||
action = "update";
|
||||
}
|
||||
|
||||
//match a document or call the regular link handling
|
||||
if(href.match(/^\[dms_document_link(\s*|%20|,)?id=([0-9]+)\]?$/i)) {
|
||||
var returnArray = {
|
||||
LinkType: 'document',
|
||||
DocumentID: RegExp.$2,
|
||||
Description: title
|
||||
};
|
||||
|
||||
//show the selected document
|
||||
$('.document-add-existing').selectdocument(returnArray.DocumentID,returnArray.Description);
|
||||
|
||||
//select the correct radio button
|
||||
$('form.htmleditorfield-linkform input[name=LinkType][value=document]').click();
|
||||
|
||||
return returnArray;
|
||||
} else {
|
||||
$('.document-add-existing').selectdocument(); //clear the selected document
|
||||
$('form.htmleditorfield-linkform .ss-add.ss-upload').hide();
|
||||
return this._super();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
}(jQuery));
|
@ -1,9 +1,13 @@
|
||||
<div class="ss-add ss-upload">
|
||||
<div class="document-add-existing field">
|
||||
<div class="document-add-existing <% if useFieldContext %>field<% else %>link-editor-context<% end_if %>">
|
||||
|
||||
<h3>
|
||||
<span class="step-label">
|
||||
<% if useFieldContext %>
|
||||
<span class="flyout">1</span><span class="arrow"></span>
|
||||
<% else %>
|
||||
<span class="flyout">3</span><span class="arrow"></span>
|
||||
<% end_if %>
|
||||
<span class="title">Link a Document</span>
|
||||
</span>
|
||||
</h3>
|
||||
@ -21,8 +25,14 @@
|
||||
<div class="ss-assetuploadfield">
|
||||
<h3>
|
||||
<span class="step-label">
|
||||
<% if useFieldContext %>
|
||||
<span class="flyout">2</span><span class="arrow"></span>
|
||||
<span class="title">Edit Document Details</span>
|
||||
<% else %>
|
||||
<span class="flyout">4</span><span class="arrow"></span>
|
||||
<span class="title">Selected Document</span>
|
||||
<% end_if %>
|
||||
|
||||
</span>
|
||||
</h3>
|
||||
<!-- <div class="fileOverview"></div> -->
|
||||
|
35
tests/ShortCodeRelationFinderTest.php
Normal file
35
tests/ShortCodeRelationFinderTest.php
Normal file
@ -0,0 +1,35 @@
|
||||
<?php
|
||||
class ShortCodeRelationFinderTest extends SapphireTest {
|
||||
|
||||
static $fixture_file = array(
|
||||
'dms/tests/dmstest.yml'
|
||||
);
|
||||
|
||||
function testFindInRate() {
|
||||
$d1 = $this->objFromFixture('DMSDocument', 'd1');
|
||||
$d2 = $this->objFromFixture('DMSDocument', 'd2');
|
||||
|
||||
$page1 = new SiteTree();
|
||||
$page1->Content = 'Condition: <a title="document test 1" href="[dms_document_link,id='.$d1->ID.']">';
|
||||
$page1ID = $page1->write();
|
||||
|
||||
$page2 = new SiteTree();
|
||||
$page2->Content = 'Condition: <a title="document test 2" href="[dms_document_link,id='.$d2->ID.']">';
|
||||
$page2ID = $page2->write();
|
||||
|
||||
$page3 = new CDSHolder();
|
||||
$page3->TermsAndConditions = 'Condition: <a title="document test 1" href="[dms_document_link,id='.$d1->ID.']">';
|
||||
$page3ID = $page3->write();
|
||||
|
||||
$finder = new ShortCodeRelationFinder();
|
||||
|
||||
$ids = $finder->findPageIDs('UnknownShortcode');
|
||||
$this->assertEquals(0, count($ids));
|
||||
|
||||
$ids = $finder->findPageIDs($d1->ID);
|
||||
$this->assertNotContains($page2ID, $ids);
|
||||
$this->assertContains($page1ID, $ids);
|
||||
$this->assertContains($page3ID, $ids);
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user