mirror of
https://github.com/silverstripe/silverstripe-cms
synced 2024-10-22 08:05:56 +02:00
Merge pull request #1291 from open-sausages/pulls/4.0/dbfile-file-dataobject
API CMS Implementation of RFC-1 Asset Abstraction (DataObject Refactoring)
This commit is contained in:
commit
b487d59d02
@ -1,4 +1,7 @@
|
||||
<?php
|
||||
|
||||
use SilverStripe\Filesystem\Storage\AssetNameGenerator;
|
||||
|
||||
/**
|
||||
* AssetAdmin is the 'file store' section of the CMS.
|
||||
* It provides an interface for manipulating the File and Folder objects in the system.
|
||||
@ -35,15 +38,8 @@ class AssetAdmin extends LeftAndMain implements PermissionProvider{
|
||||
'addfolder',
|
||||
'delete',
|
||||
'AddForm',
|
||||
'DeleteItemsForm',
|
||||
'SearchForm',
|
||||
'getsubtree',
|
||||
'movemarked',
|
||||
'removefile',
|
||||
'savefile',
|
||||
'deleteUnusedThumbnails' => 'ADMIN',
|
||||
'doSync',
|
||||
'filter',
|
||||
'getsubtree'
|
||||
);
|
||||
|
||||
/**
|
||||
@ -62,13 +58,11 @@ class AssetAdmin extends LeftAndMain implements PermissionProvider{
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up the controller, in particular, re-sync the File database with the assets folder./
|
||||
* Set up the controller
|
||||
*/
|
||||
public function init() {
|
||||
parent::init();
|
||||
|
||||
// Create base folder if it doesnt exist already
|
||||
if(!file_exists(ASSETS_PATH)) Filesystem::makeFolder(ASSETS_PATH);
|
||||
|
||||
Requirements::javascript(CMS_DIR . "/javascript/AssetAdmin.js");
|
||||
Requirements::javascript(CMS_DIR . '/javascript/CMSMain.GridField.js');
|
||||
@ -210,20 +204,6 @@ JS
|
||||
$addFolderBtn = '';
|
||||
}
|
||||
|
||||
if($folder->canEdit()) {
|
||||
$syncButton = new LiteralField(
|
||||
'SyncButton',
|
||||
sprintf(
|
||||
'<a class="ss-ui-button ss-ui-action ui-button-text-icon-primary ss-ui-button-ajax font-icon-sync" data-icon="arrow-circle-double" title="%s" href="%s">%s</a>',
|
||||
_t('AssetAdmin.FILESYSTEMSYNCTITLE', 'Update the CMS database entries of files on the filesystem. Useful when new files have been uploaded outside of the CMS, e.g. through FTP.'),
|
||||
$this->Link('doSync'),
|
||||
_t('AssetAdmin.FILESYSTEMSYNC','Sync files')
|
||||
)
|
||||
);
|
||||
} else {
|
||||
$syncButton = null;
|
||||
}
|
||||
|
||||
// Move existing fields to a "details" tab, unless they've already been tabbed out through extensions.
|
||||
// Required to keep Folder->getCMSFields() simple and reuseable,
|
||||
// without any dependencies into AssetAdmin (e.g. useful for "add folder" views).
|
||||
@ -249,7 +229,6 @@ JS
|
||||
// the button shouldn't be available. Adding empty values into a ComposteField breaks template rendering.
|
||||
$actionButtonsComposite = CompositeField::create()->addExtraClass('cms-actions-row');
|
||||
if($addFolderBtn) $actionButtonsComposite->push($addFolderBtn);
|
||||
if($syncButton) $actionButtonsComposite->push($syncButton);
|
||||
|
||||
// Add the upload field for new media
|
||||
if($currentPageID = $this->currentPageID()){
|
||||
@ -266,9 +245,8 @@ JS
|
||||
$uploadField->removeExtraClass('ss-uploadfield');
|
||||
$uploadField->setTemplate('AssetUploadField');
|
||||
|
||||
if($folder->exists() && $folder->getFilename()) {
|
||||
// The Upload class expects a folder relative *within* assets/
|
||||
$path = preg_replace('/^' . ASSETS_DIR . '\//', '', $folder->getFilename());
|
||||
if($folder->exists()) {
|
||||
$path = $folder->getFilename();
|
||||
$uploadField->setFolderName($path);
|
||||
} else {
|
||||
$uploadField->setFolderName('/'); // root of the assets
|
||||
@ -288,7 +266,6 @@ JS
|
||||
$gridField
|
||||
));
|
||||
|
||||
$treeField = new LiteralField('Tree', '');
|
||||
// Tree view
|
||||
$fields->addFieldsToTab('Root.TreeView', array(
|
||||
clone $actionsComposite,
|
||||
@ -320,8 +297,6 @@ JS
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
$fields->setForm($form);
|
||||
$form->setTemplate($this->getTemplatesWithSuffix('_EditForm'));
|
||||
// TODO Can't merge $FormAttributes in template at the moment
|
||||
@ -353,8 +328,12 @@ JS
|
||||
$className = $this->stat('tree_class');
|
||||
|
||||
$record = DataObject::get_by_id($className, $data['ID']);
|
||||
if($record && !$record->canDelete()) return Security::permissionFailure();
|
||||
if(!$record || !$record->ID) throw new SS_HTTPResponse_Exception("Bad record ID #" . (int)$data['ID'], 404);
|
||||
if($record && !$record->canDelete()) {
|
||||
return Security::permissionFailure();
|
||||
}
|
||||
if(!$record || !$record->ID) {
|
||||
throw new SS_HTTPResponse_Exception("Bad record ID #" . (int)$data['ID'], 404);
|
||||
}
|
||||
$parentID = $record->ParentID;
|
||||
$record->delete();
|
||||
$this->setCurrentPageID(null);
|
||||
@ -373,8 +352,12 @@ JS
|
||||
$context = singleton('File')->getDefaultSearchContext();
|
||||
|
||||
// Namespace fields, for easier detection if a search is present
|
||||
foreach($context->getFields() as $field) $field->setName(sprintf('q[%s]', $field->getName()));
|
||||
foreach($context->getFilters() as $filter) $filter->setFullName(sprintf('q[%s]', $filter->getFullName()));
|
||||
foreach($context->getFields() as $field) {
|
||||
$field->setName(sprintf('q[%s]', $field->getName()));
|
||||
}
|
||||
foreach($context->getFilters() as $filter) {
|
||||
$filter->setFullName(sprintf('q[%s]', $filter->getFullName()));
|
||||
}
|
||||
|
||||
// Customize fields
|
||||
$dateHeader = HeaderField::create('q[Date]', _t('CMSSearch.FILTERDATEHEADING', 'Date'), 4);
|
||||
@ -389,12 +372,12 @@ JS
|
||||
);
|
||||
$context->addField($dateGroup);
|
||||
$appCategories = array(
|
||||
'image' => _t('AssetAdmin.AppCategoryImage', 'Image'),
|
||||
'archive' => _t('AssetAdmin.AppCategoryArchive', 'Archive', 'A collection of files'),
|
||||
'audio' => _t('AssetAdmin.AppCategoryAudio', 'Audio'),
|
||||
'mov' => _t('AssetAdmin.AppCategoryVideo', 'Video'),
|
||||
'document' => _t('AssetAdmin.AppCategoryDocument', 'Document'),
|
||||
'flash' => _t('AssetAdmin.AppCategoryFlash', 'Flash', 'The fileformat'),
|
||||
'zip' => _t('AssetAdmin.AppCategoryArchive', 'Archive', 'A collection of files'),
|
||||
'doc' => _t('AssetAdmin.AppCategoryDocument', 'Document')
|
||||
'image' => _t('AssetAdmin.AppCategoryImage', 'Image'),
|
||||
'video' => _t('AssetAdmin.AppCategoryVideo', 'Video'),
|
||||
);
|
||||
$context->addField(
|
||||
$typeDropdown = new DropdownField(
|
||||
@ -444,7 +427,6 @@ JS
|
||||
}
|
||||
|
||||
public function AddForm() {
|
||||
$folder = singleton('Folder');
|
||||
$form = CMSForm::create(
|
||||
$this,
|
||||
'AddForm',
|
||||
@ -475,7 +457,9 @@ JS
|
||||
$class = $this->stat('tree_class');
|
||||
|
||||
// check create permissions
|
||||
if(!singleton($class)->canCreate()) return Security::permissionFailure($this);
|
||||
if(!singleton($class)->canCreate()) {
|
||||
return Security::permissionFailure($this);
|
||||
}
|
||||
|
||||
// check addchildren permissions
|
||||
if(
|
||||
@ -485,44 +469,40 @@ JS
|
||||
&& $data['ParentID']
|
||||
) {
|
||||
$parentRecord = DataObject::get_by_id($class, $data['ParentID']);
|
||||
if(
|
||||
$parentRecord->hasMethod('canAddChildren')
|
||||
&& !$parentRecord->canAddChildren()
|
||||
) return Security::permissionFailure($this);
|
||||
if($parentRecord->hasMethod('canAddChildren') && !$parentRecord->canAddChildren()) {
|
||||
return Security::permissionFailure($this);
|
||||
}
|
||||
} else {
|
||||
$parentRecord = null;
|
||||
}
|
||||
|
||||
$parent = (isset($data['ParentID']) && is_numeric($data['ParentID'])) ? (int)$data['ParentID'] : 0;
|
||||
$name = (isset($data['Name'])) ? basename($data['Name']) : _t('AssetAdmin.NEWFOLDER',"NewFolder");
|
||||
if(!$parentRecord || !$parentRecord->ID) $parent = 0;
|
||||
// Check parent
|
||||
$parentID = $parentRecord && $parentRecord->ID
|
||||
? (int)$parentRecord->ID
|
||||
: 0;
|
||||
// Build filename
|
||||
$filename = isset($data['Name'])
|
||||
? basename($data['Name'])
|
||||
: _t('AssetAdmin.NEWFOLDER',"NewFolder");
|
||||
if($parentRecord && $parentRecord->ID) {
|
||||
$filename = $parentRecord->getFilename() . '/' . $filename;
|
||||
}
|
||||
|
||||
// Get the folder to be created
|
||||
if($parentRecord && $parentRecord->ID) $filename = $parentRecord->FullPath . $name;
|
||||
else $filename = ASSETS_PATH . '/' . $name;
|
||||
|
||||
// Actually create
|
||||
if(!file_exists(ASSETS_PATH)) {
|
||||
mkdir(ASSETS_PATH);
|
||||
// Ensure name is unique
|
||||
foreach($this->getNameGenerator($filename) as $filename) {
|
||||
if(! File::find($filename) ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$record = new Folder();
|
||||
$record->ParentID = $parent;
|
||||
// Create record
|
||||
$record = Folder::create();
|
||||
$record->ParentID = $parentID;
|
||||
$record->Name = $record->Title = basename($filename);
|
||||
|
||||
// Ensure uniqueness
|
||||
$i = 2;
|
||||
$baseFilename = substr($record->Filename, 0, -1) . '-';
|
||||
while(file_exists($record->FullPath)) {
|
||||
$record->Filename = $baseFilename . $i . '/';
|
||||
$i++;
|
||||
}
|
||||
|
||||
$record->Name = $record->Title = basename($record->Filename);
|
||||
$record->write();
|
||||
|
||||
mkdir($record->FullPath);
|
||||
chmod($record->FullPath, Filesystem::config()->file_create_mask);
|
||||
|
||||
if($parentRecord) {
|
||||
return $this->redirect(Controller::join_links($this->Link('show'), $parentRecord->ID));
|
||||
@ -531,6 +511,17 @@ JS
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an asset renamer for the given filename.
|
||||
*
|
||||
* @param string $filename Path name
|
||||
* @return AssetNameGenerator
|
||||
*/
|
||||
protected function getNameGenerator($filename){
|
||||
return Injector::inst()
|
||||
->createWithArgs('AssetNameGenerator', array($filename));
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom currentPage() method to handle opening the 'root' folder
|
||||
*/
|
||||
@ -560,117 +551,6 @@ JS
|
||||
return $this->getSiteTreeFor($this->stat('tree_class'), null, 'ChildFolders', 'numChildFolders');
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------------//
|
||||
|
||||
// Data saving handlers
|
||||
|
||||
/**
|
||||
* Can be queried with an ajax request to trigger the filesystem sync. It returns a FormResponse status message
|
||||
* to display in the CMS
|
||||
*/
|
||||
public function doSync() {
|
||||
$message = Filesystem::sync();
|
||||
$this->response->addHeader('X-Status', rawurlencode($message));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* #################################
|
||||
* Garbage collection.
|
||||
* #################################
|
||||
*/
|
||||
|
||||
/**
|
||||
* Removes all unused thumbnails from the file store
|
||||
* and returns the status of the process to the user.
|
||||
*/
|
||||
public function deleteunusedthumbnails($request) {
|
||||
// Protect against CSRF on destructive action
|
||||
if(!SecurityToken::inst()->checkRequest($request)) return $this->httpError(400);
|
||||
|
||||
$count = 0;
|
||||
$thumbnails = $this->getUnusedThumbnails();
|
||||
|
||||
if($thumbnails) {
|
||||
foreach($thumbnails as $thumbnail) {
|
||||
unlink(ASSETS_PATH . "/" . $thumbnail);
|
||||
$count++;
|
||||
}
|
||||
}
|
||||
|
||||
$message = _t(
|
||||
'AssetAdmin.THUMBSDELETED',
|
||||
'{count} unused thumbnails have been deleted',
|
||||
array('count' => $count)
|
||||
);
|
||||
$this->response->addHeader('X-Status', rawurlencode($message));
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates array containg all unused thumbnails.
|
||||
*
|
||||
* Array is created in three steps:
|
||||
* 1. Scan assets folder and retrieve all thumbnails
|
||||
* 2. Scan all HTMLField in system and retrieve thumbnails from them.
|
||||
* 3. Count difference between two sets (array_diff)
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function getUnusedThumbnails() {
|
||||
$allThumbnails = array();
|
||||
$usedThumbnails = array();
|
||||
$dirIterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator(ASSETS_PATH));
|
||||
$classes = ClassInfo::subclassesFor('SiteTree');
|
||||
|
||||
if($dirIterator) {
|
||||
foreach($dirIterator as $file) {
|
||||
if($file->isFile()) {
|
||||
if(strpos($file->getPathname(), '_resampled') !== false) {
|
||||
$pathInfo = pathinfo($file->getPathname());
|
||||
if(in_array(strtolower($pathInfo['extension']), array('jpeg', 'jpg', 'jpe', 'png', 'gif'))) {
|
||||
$path = str_replace('\\','/', $file->getPathname());
|
||||
$allThumbnails[] = substr($path, strpos($path, '/assets/') + 8);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if($classes) {
|
||||
foreach($classes as $className) {
|
||||
$SNG_class = singleton($className);
|
||||
$objects = DataObject::get($className);
|
||||
|
||||
if($objects !== NULL) {
|
||||
foreach($objects as $object) {
|
||||
foreach($SNG_class->db() as $fieldName => $fieldType) {
|
||||
if($fieldType == 'HTMLText') {
|
||||
$url1 = HTTP::findByTagAndAttribute($object->$fieldName,array('img' => 'src'));
|
||||
|
||||
if($url1 != NULL) {
|
||||
$usedThumbnails[] = substr($url1[0], strpos($url1[0], '/assets/') + 8);
|
||||
}
|
||||
|
||||
if($object->latestPublished > 0) {
|
||||
$object = Versioned::get_latest_version($className, $object->ID);
|
||||
$url2 = HTTP::findByTagAndAttribute($object->$fieldName, array('img' => 'src'));
|
||||
|
||||
if($url2 != NULL) {
|
||||
$usedThumbnails[] = substr($url2[0], strpos($url2[0], '/assets/') + 8);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return array_diff($allThumbnails, $usedThumbnails);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $unlinked
|
||||
* @return ArrayList
|
||||
|
@ -1764,10 +1764,12 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
|
||||
}
|
||||
|
||||
/**
|
||||
* Rewrite a file URL on this page, after its been renamed. Triggers the onRenameLinkedAsset action on extensions.
|
||||
* Rewrites any linked images on this page.
|
||||
* Non-image files should be linked via shortcodes
|
||||
* Triggers the onRenameLinkedAsset action on extensions.
|
||||
* TODO: This doesn't work for HTMLText fields on other tables.
|
||||
*/
|
||||
public function rewriteFileURL($old, $new) {
|
||||
$fields = $this->inheritedDatabaseFields();
|
||||
public function rewriteFileLinks() {
|
||||
// Update the content without actually creating a new version
|
||||
foreach(array("SiteTree_Live", "SiteTree") as $table) {
|
||||
// Published site
|
||||
@ -1777,15 +1779,20 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
|
||||
)->record();
|
||||
$origPublished = $published;
|
||||
|
||||
foreach($fields as $fieldName => $fieldType) {
|
||||
if ($fieldType != 'HTMLText') continue;
|
||||
foreach($this->db() as $fieldName => $fieldType) {
|
||||
// Skip if non HTML or if empty
|
||||
if ($fieldType !== 'HTMLText' || empty($published[$fieldName])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Regenerate content
|
||||
$content = Image::regenerate_html_links($published[$fieldName]);
|
||||
if($content === $published[$fieldName]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// TODO: This doesn't work for HTMLText fields on other tables.
|
||||
if(isset($published[$fieldName])) {
|
||||
$published[$fieldName] = str_replace($old, $new, $published[$fieldName], $numReplaced);
|
||||
if($numReplaced) {
|
||||
$query = sprintf('UPDATE "%s" SET "%s" = ? WHERE "ID" = ?', $table, $fieldName);
|
||||
DB::prepared_query($query, array($published[$fieldName], $this->ID));
|
||||
DB::prepared_query($query, array($content, $this->ID));
|
||||
|
||||
// Tell static caching to update itself
|
||||
if($table == 'SiteTree_Live') {
|
||||
@ -1796,8 +1803,6 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the pages that depend on this page. This includes virtual pages, pages that link to it, etc.
|
||||
|
@ -14,7 +14,8 @@ class SiteTreeFileExtension extends DataExtension {
|
||||
ReadonlyField::create(
|
||||
'BackLinkCount',
|
||||
_t('AssetTableField.BACKLINKCOUNT', 'Used on:'),
|
||||
$this->BackLinkTracking()->Count() . ' ' . _t('AssetTableField.PAGES', 'page(s)'))
|
||||
$this->BackLinkTracking()->Count() . ' ' . _t('AssetTableField.PAGES', 'page(s)')
|
||||
)
|
||||
->addExtraClass('cms-description-toggle')
|
||||
->setDescription($this->BackLinkHTMLList()),
|
||||
'LastEdited'
|
||||
@ -27,19 +28,21 @@ class SiteTreeFileExtension extends DataExtension {
|
||||
* @return String
|
||||
*/
|
||||
public function BackLinkHTMLList() {
|
||||
$html = '<em>' . _t('SiteTreeFileExtension.BACKLINK_LIST_DESCRIPTION', 'This list shows all pages where the file has been added through a WYSIWYG editor.') . '</em>';
|
||||
$html = '<em>' . _t(
|
||||
'SiteTreeFileExtension.BACKLINK_LIST_DESCRIPTION',
|
||||
'This list shows all pages where the file has been added through a WYSIWYG editor.'
|
||||
) . '</em>';
|
||||
$html .= '<ul>';
|
||||
|
||||
foreach ($this->BackLinkTracking() as $backLink) {
|
||||
$listItem = '<li>';
|
||||
|
||||
// Add the page link
|
||||
$listItem .= '<a href="' . $backLink->Link() . '" target="_blank">' . Convert::raw2xml($backLink->MenuTitle) . '</a> – ';
|
||||
|
||||
// Add the CMS link
|
||||
$listItem .= '<a href="' . $backLink->CMSEditLink() . '">' . _t('SiteTreeFileExtension.EDIT', 'Edit') . '</a>';
|
||||
|
||||
$html .= $listItem . '</li>';
|
||||
// Add the page link and CMS link
|
||||
$html .= sprintf(
|
||||
'<li><a href="%s" target="_blank">%s</a> – <a href="%s">%s</a></li>',
|
||||
Convert::raw2att($backLink->Link()),
|
||||
Convert::raw2xml($backLink->MenuTitle),
|
||||
Convert::raw2att($backLink->CMSEditLink()),
|
||||
_t('SiteTreeFileExtension.EDIT', 'Edit')
|
||||
);
|
||||
}
|
||||
|
||||
return $html .= '</ul>';
|
||||
@ -54,31 +57,13 @@ class SiteTreeFileExtension extends DataExtension {
|
||||
* @param string $limit
|
||||
* @return ManyManyList
|
||||
*/
|
||||
public function BackLinkTracking($filter = null, $sort = null, $join = null, $limit = null) {
|
||||
if($filter !== null || $sort !== null || $join !== null || $limit !== null) {
|
||||
Deprecation::notice('4.0', 'The $filter, $sort, $join and $limit parameters for
|
||||
SiteTreeFileExtension::BackLinkTracking() have been deprecated.
|
||||
Please manipluate the returned list directly.', Deprecation::SCOPE_GLOBAL);
|
||||
}
|
||||
|
||||
public function BackLinkTracking() {
|
||||
if(class_exists("Subsite")){
|
||||
$rememberSubsiteFilter = Subsite::$disable_subsite_filter;
|
||||
Subsite::disable_subsite_filter(true);
|
||||
}
|
||||
|
||||
if($filter || $sort || $join || $limit) {
|
||||
Deprecation::notice('4.0', 'The $filter, $sort, $join and $limit parameters for
|
||||
SiteTreeFileExtension::BackLinkTracking() have been deprecated.
|
||||
Please manipluate the returned list directly.', Deprecation::SCOPE_GLOBAL);
|
||||
}
|
||||
|
||||
$links = $this->owner->getManyManyComponents('BackLinkTracking');
|
||||
if($this->owner->ID) {
|
||||
$links = $links
|
||||
->where($filter)
|
||||
->sort($sort)
|
||||
->limit($limit);
|
||||
}
|
||||
$this->owner->extend('updateBackLinkTracking', $links);
|
||||
|
||||
if(class_exists("Subsite")){
|
||||
@ -115,7 +100,9 @@ class SiteTreeFileExtension extends DataExtension {
|
||||
// This will syncLinkTracking on draft
|
||||
Versioned::reading_stage('Stage');
|
||||
$brokenPages = DataObject::get('SiteTree')->byIDs($brokenPageIDs);
|
||||
foreach($brokenPages as $brokenPage) $brokenPage->write();
|
||||
foreach($brokenPages as $brokenPage) {
|
||||
$brokenPage->write();
|
||||
}
|
||||
|
||||
// This will syncLinkTracking on published
|
||||
Versioned::reading_stage('Live');
|
||||
@ -131,22 +118,23 @@ class SiteTreeFileExtension extends DataExtension {
|
||||
/**
|
||||
* Rewrite links to the $old file to now point to the $new file.
|
||||
*
|
||||
* @uses SiteTree->rewriteFileURL()
|
||||
*
|
||||
* @param String $old File path relative to the webroot
|
||||
* @param String $new File path relative to the webroot
|
||||
* @uses SiteTree->rewriteFileID()
|
||||
*/
|
||||
public function updateLinks($old, $new) {
|
||||
if(class_exists('Subsite')) Subsite::disable_subsite_filter(true);
|
||||
public function updateLinks() {
|
||||
if(class_exists('Subsite')) {
|
||||
Subsite::disable_subsite_filter(true);
|
||||
}
|
||||
|
||||
$pages = $this->owner->BackLinkTracking();
|
||||
|
||||
$summary = "";
|
||||
if($pages) {
|
||||
foreach($pages as $page) $page->rewriteFileURL($old,$new);
|
||||
foreach($pages as $page) {
|
||||
$page->rewriteFileLinks();
|
||||
}
|
||||
}
|
||||
|
||||
if(class_exists('Subsite')) Subsite::disable_subsite_filter(false);
|
||||
if(class_exists('Subsite')) {
|
||||
Subsite::disable_subsite_filter(false);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -12,13 +12,13 @@
|
||||
* referenced in any HTMLText fields, and two booleans to indicate if there are any broken links. Call
|
||||
* augmentSyncLinkTracking to update those fields with any changes to those fields.
|
||||
*
|
||||
* @property SiteTree owner
|
||||
* @property SiteTree $owner
|
||||
*
|
||||
* @property bool HasBrokenFile
|
||||
* @property bool HasBrokenLink
|
||||
* @property bool $HasBrokenFile
|
||||
* @property bool $HasBrokenLink
|
||||
*
|
||||
* @method ManyManyList LinkTracking List of site pages linked on this page.
|
||||
* @method ManyManyList ImageTracking List of Images linked on this page.
|
||||
* @method ManyManyList LinkTracking() List of site pages linked on this page.
|
||||
* @method ManyManyList ImageTracking() List of Images linked on this page.
|
||||
*/
|
||||
class SiteTreeLinkTracking extends DataExtension {
|
||||
|
||||
@ -110,22 +110,25 @@ class SiteTreeLinkTracking extends DataExtension {
|
||||
|
||||
// Add file tracking for image references
|
||||
if($images = $htmlValue->getElementsByTagName('img')) foreach($images as $img) {
|
||||
if($image = File::find($path = urldecode(Director::makeRelative($img->getAttribute('src'))))) {
|
||||
// {@see HtmlEditorField} for data-fileid source
|
||||
$fileID = $img->getAttribute('data-fileid');
|
||||
if(!$fileID) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Assuming a local file is linked, check if it's valid
|
||||
if($image = File::get()->byID($fileID)) {
|
||||
$linkedFiles[] = $image->ID;
|
||||
} else {
|
||||
if(substr($path, 0, strlen(ASSETS_DIR) + 1) == ASSETS_DIR . '/') {
|
||||
$record->HasBrokenFile = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update the "LinkTracking" many_many
|
||||
if($record->ID && $record->manyManyComponent('LinkTracking') && $tracker = $record->LinkTracking()) {
|
||||
$tracker->removeByFilter(sprintf(
|
||||
'"FieldName" = \'%s\' AND "%s" = %d',
|
||||
$fieldName,
|
||||
$tracker->getForeignKey(),
|
||||
$record->ID
|
||||
if($record->ID && $record->manyManyComponent('LinkTracking') && ($tracker = $record->LinkTracking())) {
|
||||
$tracker->removeByFilter(array(
|
||||
sprintf('"FieldName" = ? AND "%s" = ?', $tracker->getForeignKey())
|
||||
=> array($fieldName, $record->ID)
|
||||
));
|
||||
|
||||
if($linkedPages) foreach($linkedPages as $item) {
|
||||
@ -134,12 +137,10 @@ class SiteTreeLinkTracking extends DataExtension {
|
||||
}
|
||||
|
||||
// Update the "ImageTracking" many_many
|
||||
if($record->ID && $record->manyManyComponent('ImageTracking') && $tracker = $record->ImageTracking()) {
|
||||
$tracker->removeByFilter(sprintf(
|
||||
'"FieldName" = \'%s\' AND "%s" = %d',
|
||||
$fieldName,
|
||||
$tracker->getForeignKey(),
|
||||
$record->ID
|
||||
if($record->ID && $record->manyManyComponent('ImageTracking') && ($tracker = $record->ImageTracking())) {
|
||||
$tracker->removeByFilter(array(
|
||||
sprintf('"FieldName" = ? AND "%s" = ?', $tracker->getForeignKey())
|
||||
=> array($fieldName, $record->ID)
|
||||
));
|
||||
|
||||
if($linkedFiles) foreach($linkedFiles as $item) {
|
||||
|
@ -1,25 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* @package cms
|
||||
* @subpackage assets
|
||||
*/
|
||||
class FilesystemSyncTask extends BuildTask {
|
||||
|
||||
protected $title = "Sync Files & Images assets";
|
||||
|
||||
protected $description = "The Files & Images system in SilverStripe maintains its own database
|
||||
of the contents of the assets/ folder. This action will update that database, and
|
||||
should be called whenever files are added to the assets/ folder from outside
|
||||
SilverStripe, for example, if an author uploads files via FTP.";
|
||||
|
||||
public function run($request) {
|
||||
if(isset($_GET['folderID'])) {
|
||||
$folderID = $_GET['folderID'];
|
||||
} else {
|
||||
$folderID = null;
|
||||
}
|
||||
|
||||
echo Filesystem::sync($folderID, !($request->getVar('skipSyncLinkTracking'))) . "\n";
|
||||
}
|
||||
|
||||
}
|
@ -32,7 +32,7 @@ So that I can link to a external website or a page on my site
|
||||
And I fill in the "internal" dropdown with "Details"
|
||||
And I wait for 1 second
|
||||
And I select "youranchor" from "Form_EditorToolbarLinkForm_AnchorSelector"
|
||||
And I press the "Insert link" button
|
||||
And I press the "Insert" button
|
||||
Then the "Content" HTML field should contain "<a href="[sitetree_link,id=3]#youranchor">awesome</a>"
|
||||
# Required to avoid "unsaved changes" browser dialog
|
||||
Then I press the "Save draft" button
|
||||
@ -53,8 +53,8 @@ So that I can link to a external website or a page on my site
|
||||
When I press the "Insert Link" button
|
||||
When I select the "Download a file" radio button
|
||||
And I attach the file "testfile.jpg" to "file[Uploads][]" with HTML5
|
||||
And I press the "Insert link" button
|
||||
Then the "Content" HTML field should contain "<a href="[file_link,id=3]" target="_blank">awesome</a>"
|
||||
And I press the "Insert" button
|
||||
Then the "Content" HTML field should contain "<a href="[file_link,id=4]" target="_blank">awesome</a>"
|
||||
# Required to avoid "unsaved changes" browser dialog
|
||||
Then I press the "Save draft" button
|
||||
|
||||
@ -64,7 +64,7 @@ So that I can link to a external website or a page on my site
|
||||
When I press the "Insert Link" button
|
||||
When I select the "Anchor on this page" radio button
|
||||
And I select "myanchor" from "Form_EditorToolbarLinkForm_AnchorSelector"
|
||||
And I press the "Insert link" button
|
||||
And I press the "Insert" button
|
||||
Then the "Content" HTML field should contain "<a href="#myanchor">awesome</a>"
|
||||
# Required to avoid "unsaved changes" browser dialog
|
||||
Then I press the "Save draft" button
|
||||
@ -77,7 +77,7 @@ So that I can link to a external website or a page on my site
|
||||
Then the "Form_EditorToolbarLinkForm_external" field should contain "http://silverstripe.org"
|
||||
# This doesn't seem to suffer from that issue
|
||||
When I fill in "http://google.com" for "URL"
|
||||
And I press the "Insert link" button
|
||||
And I press the "Insert" button
|
||||
Then the "Content" HTML field should contain "<a href="http://google.com">awesome</a>"
|
||||
# Required to avoid "unsaved changes" browser dialog
|
||||
Then I press the "Save draft" button
|
||||
|
@ -18,7 +18,7 @@ Feature: Insert an image into a page
|
||||
When I click "add by URL" in the ".ss-uploadfield-item-info" element
|
||||
And I fill in "RemoteURL" with "http://www.silverstripe.org/themes/ssv3/img/ss_logo.png"
|
||||
And I press the "Add url" button
|
||||
Then I should see "ss_logo.png (www.silverstripe.org)" in the ".ss-assetuploadfield span.name" element
|
||||
Then I should see "ss_logo.png" in the ".ss-assetuploadfield span.name" element
|
||||
|
||||
When I press the "Insert" button
|
||||
Then the "Content" HTML field should contain "ss_logo.png"
|
||||
@ -31,9 +31,9 @@ Feature: Insert an image into a page
|
||||
And I attach the file "testfile.jpg" to "AssetUploadField" with HTML5
|
||||
# TODO Delay previous step until upload succeeded
|
||||
And I wait for 2 seconds
|
||||
Then there should be a file "assets/Uploads/testfile.jpg"
|
||||
Then there should be a file "assets/Uploads/59de0c841f/testfile.jpg"
|
||||
When I press the "Insert" button
|
||||
Then the "Content" HTML field should contain "testfile.jpg"
|
||||
Then the "Content" HTML field should contain "testfile__Resampled.jpg"
|
||||
# Required to avoid "unsaved changed" browser dialog
|
||||
Then I press the "Save draft" button
|
||||
|
||||
@ -45,10 +45,10 @@ Feature: Insert an image into a page
|
||||
# TODO Delay previous step until upload succeeded
|
||||
And I wait for 2 seconds
|
||||
# Note change in default behaviour from 3.1, respect default Upload.replaceFile=false
|
||||
Then there should be a file "assets/Uploads/file1.jpg"
|
||||
And there should be a file "assets/Uploads/file1-v2.jpg"
|
||||
Then there should be a file "assets/Uploads/3d0ef6ec37/file1.jpg"
|
||||
And there should be a file "assets/Uploads/3d0ef6ec37/file1-v2.jpg"
|
||||
When I press the "Insert" button
|
||||
Then the "Content" HTML field should contain "file1-v2.jpg"
|
||||
Then the "Content" HTML field should contain "file1-v2__Resampled.jpg"
|
||||
# Required to avoid "unsaved changed" browser dialog
|
||||
Then I press the "Save draft" button
|
||||
|
||||
@ -57,7 +57,7 @@ Feature: Insert an image into a page
|
||||
And I fill in the "ParentID" dropdown with "folder1"
|
||||
And I click on "file1" in the "Files" table
|
||||
When I press the "Insert" button
|
||||
Then the "Content" HTML field should contain "file1.jpg"
|
||||
Then the "Content" HTML field should contain "file1__Resampled.jpg"
|
||||
# Required to avoid "unsaved changed" browser dialog
|
||||
Then I press the "Save draft" button
|
||||
|
||||
@ -68,7 +68,7 @@ Feature: Insert an image into a page
|
||||
And I press the "Edit" button
|
||||
When I fill in "Alternative text (alt)" with "My alt"
|
||||
And I press the "Insert" button
|
||||
Then the "Content" HTML field should contain "file1.jpg"
|
||||
Then the "Content" HTML field should contain "file1__Resampled.jpg"
|
||||
And the "Content" HTML field should contain "My alt"
|
||||
# Required to avoid "unsaved changed" browser dialog
|
||||
Then I press the "Save draft" button
|
||||
@ -76,14 +76,14 @@ Feature: Insert an image into a page
|
||||
# TODO This needs to support using drag handles, as we no longer have 'Width' or 'Height' input fields
|
||||
@todo
|
||||
Scenario: I can edit dimensions of an existing image
|
||||
Given the "page" "About us" contains "<img src=assets/folder1/file1.jpg>"
|
||||
Given the "page" "About us" contains "<img src=assets/folder1/3d0ef6ec37/file1.jpg>"
|
||||
And I reload the current page
|
||||
When I highlight "<img src=assets/folder1/file1.jpg>" in the "Content" HTML field
|
||||
When I highlight "<img src=assets/folder1/3d0ef6ec37/file1.jpg>" in the "Content" HTML field
|
||||
And I press the "Insert Media" button
|
||||
Then I should see "file1.jpg"
|
||||
When I fill in "Width" with "10"
|
||||
When I fill in "Height" with "20"
|
||||
And I press the "Insert" button
|
||||
Then the "Content" HTML field should contain "<img src=assets/folder1/file1.jpg width=10 height=20>"
|
||||
Then the "Content" HTML field should contain "<img src=assets/folder1/3d0ef6ec37/file1.jpg width=10 height=20>"
|
||||
# Required to avoid "unsaved changed" browser dialog
|
||||
Then I press the "Save draft" button
|
||||
|
@ -6,7 +6,7 @@ Feature: Manage files
|
||||
|
||||
Background:
|
||||
Given a "image" "assets/folder1/file1.jpg" was created "2012-01-01 12:00:00"
|
||||
And a "image" "assets/folder1/folder1.1/file2.jpg" was created "2010-01-01 12:00:00"
|
||||
And a "image" "assets/folder1/folder1-1/file2.jpg" was created "2010-01-01 12:00:00"
|
||||
And a "folder" "assets/folder2"
|
||||
And I am logged in with "ADMIN" permissions
|
||||
And I go to "/admin/assets"
|
||||
@ -21,7 +21,7 @@ Feature: Manage files
|
||||
Scenario: I can list files in a folder
|
||||
Given I click on "folder1" in the "Files" table
|
||||
Then the "folder1" table should contain "file1"
|
||||
And the "folder1" table should not contain "file1.1"
|
||||
And the "folder1" table should not contain "file1-1"
|
||||
|
||||
Scenario: I can upload a file to a folder
|
||||
Given I click on "folder1" in the "Files" table
|
||||
|
@ -8,40 +8,52 @@ class FileLinkTrackingTest extends SapphireTest {
|
||||
|
||||
public function setUp() {
|
||||
parent::setUp();
|
||||
AssetStoreTest_SpyStore::activate('FileLinkTrackingTest');
|
||||
$this->logInWithPermission('ADMIN');
|
||||
|
||||
if(!file_exists(ASSETS_PATH)) mkdir(ASSETS_PATH);
|
||||
$fh = fopen(ASSETS_PATH . '/testscript-test-file.pdf', "w");
|
||||
fwrite($fh, str_repeat('x',1000000));
|
||||
fclose($fh);
|
||||
// Write file contents
|
||||
$files = File::get()->exclude('ClassName', 'Folder');
|
||||
foreach($files as $file) {
|
||||
$destPath = AssetStoreTest_SpyStore::getLocalPath($file);
|
||||
Filesystem::makeFolder(dirname($destPath));
|
||||
file_put_contents($destPath, str_repeat('x', 1000000));
|
||||
}
|
||||
|
||||
// Since we can't hard-code IDs, manually inject image tracking shortcode
|
||||
$imageID = $this->idFromFixture('Image', 'file1');
|
||||
$page = $this->objFromFixture('Page', 'page1');
|
||||
$page->Content = sprintf(
|
||||
'<p><img src="/assets/FileLinkTrackingTest/55b443b601/testscript-test-file.jpg" data-fileid="%d" /></p>',
|
||||
$imageID
|
||||
);
|
||||
$page->write();
|
||||
}
|
||||
|
||||
public function tearDown() {
|
||||
AssetStoreTest_SpyStore::reset();
|
||||
parent::tearDown();
|
||||
$testFiles = array(
|
||||
'/testscript-test-file.pdf',
|
||||
'/renamed-test-file.pdf',
|
||||
'/renamed-test-file-second-time.pdf',
|
||||
);
|
||||
foreach($testFiles as $file) {
|
||||
if(file_exists(ASSETS_PATH . $file)) unlink(ASSETS_PATH . $file);
|
||||
}
|
||||
}
|
||||
|
||||
public function testFileRenameUpdatesDraftAndPublishedPages() {
|
||||
$page = $this->objFromFixture('Page', 'page1');
|
||||
$this->assertTrue($page->doPublish());
|
||||
$this->assertContains('<img src="assets/testscript-test-file.pdf"',
|
||||
DB::prepared_query("SELECT \"Content\" FROM \"SiteTree_Live\" WHERE \"ID\" = ?", array($page->ID))->value());
|
||||
$this->assertContains(
|
||||
'<img src="/assets/FileLinkTrackingTest/55b443b601/testscript-test-file.jpg"',
|
||||
DB::prepared_query("SELECT \"Content\" FROM \"SiteTree_Live\" WHERE \"ID\" = ?", array($page->ID))->value()
|
||||
);
|
||||
|
||||
$file = $this->objFromFixture('File', 'file1');
|
||||
$file->Name = 'renamed-test-file.pdf';
|
||||
$file = $this->objFromFixture('Image', 'file1');
|
||||
$file->Name = 'renamed-test-file.jpg';
|
||||
$file->write();
|
||||
|
||||
$this->assertContains('<img src="assets/renamed-test-file.pdf"',
|
||||
DB::prepared_query("SELECT \"Content\" FROM \"SiteTree\" WHERE \"ID\" = ?", array($page->ID))->value());
|
||||
$this->assertContains('<img src="assets/renamed-test-file.pdf"',
|
||||
DB::prepared_query("SELECT \"Content\" FROM \"SiteTree_Live\" WHERE \"ID\" = ?", array($page->ID))->value());
|
||||
$this->assertContains(
|
||||
'<img src="/assets/FileLinkTrackingTest/55b443b601/renamed-test-file.jpg"',
|
||||
DB::prepared_query("SELECT \"Content\" FROM \"SiteTree\" WHERE \"ID\" = ?", array($page->ID))->value()
|
||||
);
|
||||
$this->assertContains(
|
||||
'<img src="/assets/FileLinkTrackingTest/55b443b601/renamed-test-file.jpg"',
|
||||
DB::prepared_query("SELECT \"Content\" FROM \"SiteTree_Live\" WHERE \"ID\" = ?", array($page->ID))->value()
|
||||
);
|
||||
}
|
||||
|
||||
public function testFileLinkRewritingOnVirtualPages() {
|
||||
@ -56,15 +68,19 @@ class FileLinkTrackingTest extends SapphireTest {
|
||||
$svp->doPublish();
|
||||
|
||||
// Rename the file
|
||||
$file = $this->objFromFixture('File', 'file1');
|
||||
$file->Name = 'renamed-test-file.pdf';
|
||||
$file = $this->objFromFixture('Image', 'file1');
|
||||
$file->Name = 'renamed-test-file.jpg';
|
||||
$file->write();
|
||||
|
||||
// Verify that the draft and publish virtual pages both have the corrected link
|
||||
$this->assertContains('<img src="assets/renamed-test-file.pdf"',
|
||||
DB::prepared_query("SELECT \"Content\" FROM \"SiteTree\" WHERE \"ID\" = ?", array($svp->ID))->value());
|
||||
$this->assertContains('<img src="assets/renamed-test-file.pdf"',
|
||||
DB::prepared_query("SELECT \"Content\" FROM \"SiteTree_Live\" WHERE \"ID\" = ?", array($svp->ID))->value());
|
||||
$this->assertContains(
|
||||
'<img src="/assets/FileLinkTrackingTest/55b443b601/renamed-test-file.jpg"',
|
||||
DB::prepared_query("SELECT \"Content\" FROM \"SiteTree\" WHERE \"ID\" = ?", array($svp->ID))->value()
|
||||
);
|
||||
$this->assertContains(
|
||||
'<img src="/assets/FileLinkTrackingTest/55b443b601/renamed-test-file.jpg"',
|
||||
DB::prepared_query("SELECT \"Content\" FROM \"SiteTree_Live\" WHERE \"ID\" = ?", array($svp->ID))->value()
|
||||
);
|
||||
}
|
||||
|
||||
public function testLinkRewritingOnAPublishedPageDoesntMakeItEditedOnDraft() {
|
||||
@ -74,8 +90,8 @@ class FileLinkTrackingTest extends SapphireTest {
|
||||
$this->assertFalse($page->getIsModifiedOnStage());
|
||||
|
||||
// Rename the file
|
||||
$file = $this->objFromFixture('File', 'file1');
|
||||
$file->Name = 'renamed-test-file.pdf';
|
||||
$file = $this->objFromFixture('Image', 'file1');
|
||||
$file->Name = 'renamed-test-file.jpg';
|
||||
$file->write();
|
||||
|
||||
// Caching hack
|
||||
@ -89,25 +105,30 @@ class FileLinkTrackingTest extends SapphireTest {
|
||||
public function testTwoFileRenamesInARowWork() {
|
||||
$page = $this->objFromFixture('Page', 'page1');
|
||||
$this->assertTrue($page->doPublish());
|
||||
$this->assertContains('<img src="assets/testscript-test-file.pdf"',
|
||||
$this->assertContains(
|
||||
'<img src="/assets/FileLinkTrackingTest/55b443b601/testscript-test-file.jpg"',
|
||||
DB::prepared_query("SELECT \"Content\" FROM \"SiteTree_Live\" WHERE \"ID\" = ?", array($page->ID))->value());
|
||||
|
||||
// Rename the file twice
|
||||
$file = $this->objFromFixture('File', 'file1');
|
||||
$file->Name = 'renamed-test-file.pdf';
|
||||
$file = $this->objFromFixture('Image', 'file1');
|
||||
$file->Name = 'renamed-test-file.jpg';
|
||||
$file->write();
|
||||
|
||||
// TODO Workaround for bug in DataObject->getChangedFields(), which returns stale data,
|
||||
// and influences File->updateFilesystem()
|
||||
$file = DataObject::get_by_id('File', $file->ID);
|
||||
$file->Name = 'renamed-test-file-second-time.pdf';
|
||||
$file->Name = 'renamed-test-file-second-time.jpg';
|
||||
$file->write();
|
||||
|
||||
// Confirm that the correct image is shown in both the draft and live site
|
||||
$this->assertContains('<img src="assets/renamed-test-file-second-time.pdf"',
|
||||
DB::prepared_query("SELECT \"Content\" FROM \"SiteTree\" WHERE \"ID\" = ?", array($page->ID))->value());
|
||||
$this->assertContains('<img src="assets/renamed-test-file-second-time.pdf"',
|
||||
DB::prepared_query("SELECT \"Content\" FROM \"SiteTree_Live\" WHERE \"ID\" = ?", array($page->ID))->value());
|
||||
$this->assertContains(
|
||||
'<img src="/assets/FileLinkTrackingTest/55b443b601/renamed-test-file-second-time.jpg"',
|
||||
DB::prepared_query("SELECT \"Content\" FROM \"SiteTree\" WHERE \"ID\" = ?", array($page->ID))->value()
|
||||
);
|
||||
$this->assertContains(
|
||||
'<img src="/assets/FileLinkTrackingTest/55b443b601/renamed-test-file-second-time.jpg"',
|
||||
DB::prepared_query("SELECT \"Content\" FROM \"SiteTree_Live\" WHERE \"ID\" = ?", array($page->ID))->value()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,10 +1,12 @@
|
||||
# These need to come first so that SiteTree has the link meta-data written.
|
||||
File:
|
||||
Image:
|
||||
file1:
|
||||
Filename: assets/testscript-test-file.pdf
|
||||
FileFilename: testscript-test-file.jpg
|
||||
FileHash: 55b443b60176235ef09801153cca4e6da7494a0c
|
||||
Name: testscript-test-file.jpg
|
||||
|
||||
Page:
|
||||
page1:
|
||||
Title: page1
|
||||
URLSegment: page1
|
||||
Content: <p><img src="assets/testscript-test-file.pdf" /></p>
|
||||
Content: '<p><img src="/assets/FileLinkTrackingTest/55b443b601/testscript-test-file.jpg" /></p>'
|
||||
|
@ -4,6 +4,25 @@ class SiteTreeHtmlEditorFieldTest extends FunctionalTest {
|
||||
|
||||
protected static $use_draft_site = true;
|
||||
|
||||
public function setUp() {
|
||||
parent::setUp();
|
||||
AssetStoreTest_SpyStore::activate('SiteTreeHtmlEditorFieldTest');
|
||||
$this->logInWithPermission('ADMIN');
|
||||
|
||||
// Write file contents
|
||||
$files = File::get()->exclude('ClassName', 'Folder');
|
||||
foreach($files as $file) {
|
||||
$destPath = AssetStoreTest_SpyStore::getLocalPath($file);
|
||||
Filesystem::makeFolder(dirname($destPath));
|
||||
file_put_contents($destPath, str_repeat('x', 1000000));
|
||||
}
|
||||
}
|
||||
|
||||
public function tearDown() {
|
||||
AssetStoreTest_SpyStore::reset();
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
public function testLinkTracking() {
|
||||
$sitetree = $this->objFromFixture('SiteTree', 'home');
|
||||
$editor = new HtmlEditorField('Content');
|
||||
@ -103,20 +122,23 @@ class SiteTreeHtmlEditorFieldTest extends FunctionalTest {
|
||||
public function testImageTracking() {
|
||||
$sitetree = $this->objFromFixture('SiteTree', 'home');
|
||||
$editor = new HtmlEditorField('Content');
|
||||
$fileID = $this->idFromFixture('Image', 'example_image');
|
||||
$file = $this->objFromFixture('Image', 'example_image');
|
||||
|
||||
$editor->setValue('<img src="assets/example.jpg" />');
|
||||
$editor->setValue(sprintf('<img src="%s" data-fileid="%d" />', $file->getURL(), $file->ID));
|
||||
$editor->saveInto($sitetree);
|
||||
$sitetree->write();
|
||||
$this->assertEquals(
|
||||
array($fileID => $fileID), $sitetree->ImageTracking()->getIDList(), 'Inserted images are tracked.'
|
||||
array($file->ID => $file->ID),
|
||||
$sitetree->ImageTracking()->getIDList(),
|
||||
'Inserted images are tracked.'
|
||||
);
|
||||
|
||||
$editor->setValue(null);
|
||||
$editor->saveInto($sitetree);
|
||||
$sitetree->write();
|
||||
$this->assertEquals (
|
||||
array(), $sitetree->ImageTracking()->getIDList(), 'Tracked images are deleted when removed.'
|
||||
$this->assertEmpty(
|
||||
$sitetree->ImageTracking()->getIDList(),
|
||||
'Tracked images are deleted when removed.'
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -8,8 +8,12 @@ SiteTree:
|
||||
|
||||
File:
|
||||
example_file:
|
||||
FileFilename: example.pdf
|
||||
FileHash: 55b443b60176235ef09801153cca4e6da7494a0c
|
||||
Name: example.pdf
|
||||
|
||||
Image:
|
||||
example_image:
|
||||
FileFilename: example.jpg
|
||||
FileHash: 55b443b60176235ef09801153cca4e6da7494a0c
|
||||
Name: example.jpg
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
class SiteTreeLinkTrackingTest extends SapphireTest {
|
||||
|
||||
function isBroken($content) {
|
||||
protected function isBroken($content) {
|
||||
$parser = new SiteTreeLinkTracking_Parser();
|
||||
$htmlValue = Injector::inst()->create('HTMLValue', $content);
|
||||
$links = $parser->process($htmlValue);
|
||||
@ -11,7 +11,7 @@ class SiteTreeLinkTrackingTest extends SapphireTest {
|
||||
return $links[0]['Broken'];
|
||||
}
|
||||
|
||||
function testParser() {
|
||||
public function testParser() {
|
||||
$this->assertTrue($this->isBroken('<a href="[sitetree_link,id=123]">link</a>'));
|
||||
$this->assertTrue($this->isBroken('<a href="[sitetree_link,id=123]#no-such-anchor">link</a>'));
|
||||
$this->assertTrue($this->isBroken('<a href="[file_link,id=123]">link</a>'));
|
||||
@ -34,14 +34,14 @@ class SiteTreeLinkTrackingTest extends SapphireTest {
|
||||
$this->assertFalse($this->isBroken("<a href=\"[file_link,id=$file->ID]\">link</a>"));
|
||||
}
|
||||
|
||||
function highlight($content) {
|
||||
protected function highlight($content) {
|
||||
$page = new Page();
|
||||
$page->Content = $content;
|
||||
$page->write();
|
||||
return $page->Content;
|
||||
}
|
||||
|
||||
function testHighlighter() {
|
||||
public function testHighlighter() {
|
||||
$content = $this->highlight('<a href="[sitetree_link,id=123]" class="existing-class">link</a>');
|
||||
$this->assertEquals(substr_count($content, 'ss-broken'), 1, 'A ss-broken class is added to the broken link.');
|
||||
$this->assertEquals(substr_count($content, 'existing-class'), 1, 'Existing class is not removed.');
|
||||
@ -60,4 +60,16 @@ class SiteTreeLinkTrackingTest extends SapphireTest {
|
||||
$this->assertEquals(substr_count($content, 'existing-class'), 1, 'Existing class is not removed.');
|
||||
}
|
||||
|
||||
public function testHasBrokenFile() {
|
||||
$this->assertTrue($this->pageIsBrokenFile('<img src="someurl.jpg" data-fileid="99999999" />'));
|
||||
$this->assertFalse($this->pageIsBrokenFile('<img src="someurl.jpg" />'));
|
||||
}
|
||||
|
||||
protected function pageIsBrokenFile($content) {
|
||||
$page = new Page();
|
||||
$page->Content = $content;
|
||||
$page->write();
|
||||
return $page->HasBrokenFile;
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user