API Remove filesystem sync

API support new asset abstraction
BUG Fix file link tracking for new asset abstraction
This commit is contained in:
Damian Mooyman 2015-10-15 11:08:52 +13:00
parent bcc19df231
commit 2bd9d00da0
13 changed files with 271 additions and 361 deletions

View File

@ -1,4 +1,7 @@
<?php <?php
use SilverStripe\Filesystem\Storage\AssetNameGenerator;
/** /**
* AssetAdmin is the 'file store' section of the CMS. * AssetAdmin is the 'file store' section of the CMS.
* It provides an interface for manipulating the File and Folder objects in the system. * 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', 'addfolder',
'delete', 'delete',
'AddForm', 'AddForm',
'DeleteItemsForm',
'SearchForm', 'SearchForm',
'getsubtree', 'getsubtree'
'movemarked',
'removefile',
'savefile',
'deleteUnusedThumbnails' => 'ADMIN',
'doSync',
'filter',
); );
/** /**
@ -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() { public function init() {
parent::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/AssetAdmin.js");
Requirements::javascript(CMS_DIR . '/javascript/CMSMain.GridField.js'); Requirements::javascript(CMS_DIR . '/javascript/CMSMain.GridField.js');
@ -210,20 +204,6 @@ JS
$addFolderBtn = ''; $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. // Move existing fields to a "details" tab, unless they've already been tabbed out through extensions.
// Required to keep Folder->getCMSFields() simple and reuseable, // Required to keep Folder->getCMSFields() simple and reuseable,
// without any dependencies into AssetAdmin (e.g. useful for "add folder" views). // 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. // the button shouldn't be available. Adding empty values into a ComposteField breaks template rendering.
$actionButtonsComposite = CompositeField::create()->addExtraClass('cms-actions-row'); $actionButtonsComposite = CompositeField::create()->addExtraClass('cms-actions-row');
if($addFolderBtn) $actionButtonsComposite->push($addFolderBtn); if($addFolderBtn) $actionButtonsComposite->push($addFolderBtn);
if($syncButton) $actionButtonsComposite->push($syncButton);
// Add the upload field for new media // Add the upload field for new media
if($currentPageID = $this->currentPageID()){ if($currentPageID = $this->currentPageID()){
@ -266,9 +245,8 @@ JS
$uploadField->removeExtraClass('ss-uploadfield'); $uploadField->removeExtraClass('ss-uploadfield');
$uploadField->setTemplate('AssetUploadField'); $uploadField->setTemplate('AssetUploadField');
if($folder->exists() && $folder->getFilename()) { if($folder->exists()) {
// The Upload class expects a folder relative *within* assets/ $path = $folder->getFilename();
$path = preg_replace('/^' . ASSETS_DIR . '\//', '', $folder->getFilename());
$uploadField->setFolderName($path); $uploadField->setFolderName($path);
} else { } else {
$uploadField->setFolderName('/'); // root of the assets $uploadField->setFolderName('/'); // root of the assets
@ -287,8 +265,7 @@ JS
new HiddenField('ID'), new HiddenField('ID'),
$gridField $gridField
)); ));
$treeField = new LiteralField('Tree', '');
// Tree view // Tree view
$fields->addFieldsToTab('Root.TreeView', array( $fields->addFieldsToTab('Root.TreeView', array(
clone $actionsComposite, clone $actionsComposite,
@ -320,8 +297,6 @@ JS
); );
} }
$fields->setForm($form); $fields->setForm($form);
$form->setTemplate($this->getTemplatesWithSuffix('_EditForm')); $form->setTemplate($this->getTemplatesWithSuffix('_EditForm'));
// TODO Can't merge $FormAttributes in template at the moment // TODO Can't merge $FormAttributes in template at the moment
@ -353,8 +328,12 @@ JS
$className = $this->stat('tree_class'); $className = $this->stat('tree_class');
$record = DataObject::get_by_id($className, $data['ID']); $record = DataObject::get_by_id($className, $data['ID']);
if($record && !$record->canDelete()) return Security::permissionFailure(); if($record && !$record->canDelete()) {
if(!$record || !$record->ID) throw new SS_HTTPResponse_Exception("Bad record ID #" . (int)$data['ID'], 404); return Security::permissionFailure();
}
if(!$record || !$record->ID) {
throw new SS_HTTPResponse_Exception("Bad record ID #" . (int)$data['ID'], 404);
}
$parentID = $record->ParentID; $parentID = $record->ParentID;
$record->delete(); $record->delete();
$this->setCurrentPageID(null); $this->setCurrentPageID(null);
@ -373,8 +352,12 @@ JS
$context = singleton('File')->getDefaultSearchContext(); $context = singleton('File')->getDefaultSearchContext();
// Namespace fields, for easier detection if a search is present // Namespace fields, for easier detection if a search is present
foreach($context->getFields() as $field) $field->setName(sprintf('q[%s]', $field->getName())); foreach($context->getFields() as $field) {
foreach($context->getFilters() as $filter) $filter->setFullName(sprintf('q[%s]', $filter->getFullName())); $field->setName(sprintf('q[%s]', $field->getName()));
}
foreach($context->getFilters() as $filter) {
$filter->setFullName(sprintf('q[%s]', $filter->getFullName()));
}
// Customize fields // Customize fields
$dateHeader = HeaderField::create('q[Date]', _t('CMSSearch.FILTERDATEHEADING', 'Date'), 4); $dateHeader = HeaderField::create('q[Date]', _t('CMSSearch.FILTERDATEHEADING', 'Date'), 4);
@ -389,12 +372,12 @@ JS
); );
$context->addField($dateGroup); $context->addField($dateGroup);
$appCategories = array( $appCategories = array(
'image' => _t('AssetAdmin.AppCategoryImage', 'Image'), 'archive' => _t('AssetAdmin.AppCategoryArchive', 'Archive', 'A collection of files'),
'audio' => _t('AssetAdmin.AppCategoryAudio', 'Audio'), 'audio' => _t('AssetAdmin.AppCategoryAudio', 'Audio'),
'mov' => _t('AssetAdmin.AppCategoryVideo', 'Video'), 'document' => _t('AssetAdmin.AppCategoryDocument', 'Document'),
'flash' => _t('AssetAdmin.AppCategoryFlash', 'Flash', 'The fileformat'), 'flash' => _t('AssetAdmin.AppCategoryFlash', 'Flash', 'The fileformat'),
'zip' => _t('AssetAdmin.AppCategoryArchive', 'Archive', 'A collection of files'), 'image' => _t('AssetAdmin.AppCategoryImage', 'Image'),
'doc' => _t('AssetAdmin.AppCategoryDocument', 'Document') 'video' => _t('AssetAdmin.AppCategoryVideo', 'Video'),
); );
$context->addField( $context->addField(
$typeDropdown = new DropdownField( $typeDropdown = new DropdownField(
@ -444,7 +427,6 @@ JS
} }
public function AddForm() { public function AddForm() {
$folder = singleton('Folder');
$form = CMSForm::create( $form = CMSForm::create(
$this, $this,
'AddForm', 'AddForm',
@ -475,7 +457,9 @@ JS
$class = $this->stat('tree_class'); $class = $this->stat('tree_class');
// check create permissions // check create permissions
if(!singleton($class)->canCreate()) return Security::permissionFailure($this); if(!singleton($class)->canCreate()) {
return Security::permissionFailure($this);
}
// check addchildren permissions // check addchildren permissions
if( if(
@ -485,44 +469,40 @@ JS
&& $data['ParentID'] && $data['ParentID']
) { ) {
$parentRecord = DataObject::get_by_id($class, $data['ParentID']); $parentRecord = DataObject::get_by_id($class, $data['ParentID']);
if( if($parentRecord->hasMethod('canAddChildren') && !$parentRecord->canAddChildren()) {
$parentRecord->hasMethod('canAddChildren') return Security::permissionFailure($this);
&& !$parentRecord->canAddChildren() }
) return Security::permissionFailure($this);
} else { } else {
$parentRecord = null; $parentRecord = null;
} }
$parent = (isset($data['ParentID']) && is_numeric($data['ParentID'])) ? (int)$data['ParentID'] : 0; // Check parent
$name = (isset($data['Name'])) ? basename($data['Name']) : _t('AssetAdmin.NEWFOLDER',"NewFolder"); $parentID = $parentRecord && $parentRecord->ID
if(!$parentRecord || !$parentRecord->ID) $parent = 0; ? (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 // Get the folder to be created
if($parentRecord && $parentRecord->ID) $filename = $parentRecord->FullPath . $name;
else $filename = ASSETS_PATH . '/' . $name;
// Actually create // Ensure name is unique
if(!file_exists(ASSETS_PATH)) { foreach($this->getNameGenerator($filename) as $filename) {
mkdir(ASSETS_PATH); if(! File::find($filename) ) {
break;
}
} }
$record = new Folder(); // Create record
$record->ParentID = $parent; $record = Folder::create();
$record->ParentID = $parentID;
$record->Name = $record->Title = basename($filename); $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(); $record->write();
mkdir($record->FullPath);
chmod($record->FullPath, Filesystem::config()->file_create_mask);
if($parentRecord) { if($parentRecord) {
return $this->redirect(Controller::join_links($this->Link('show'), $parentRecord->ID)); 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 * Custom currentPage() method to handle opening the 'root' folder
*/ */
@ -560,117 +551,6 @@ JS
return $this->getSiteTreeFor($this->stat('tree_class'), null, 'ChildFolders', 'numChildFolders'); 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 * @param bool $unlinked
* @return ArrayList * @return ArrayList

View File

@ -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) { public function rewriteFileLinks() {
$fields = $this->inheritedDatabaseFields();
// Update the content without actually creating a new version // Update the content without actually creating a new version
foreach(array("SiteTree_Live", "SiteTree") as $table) { foreach(array("SiteTree_Live", "SiteTree") as $table) {
// Published site // Published site
@ -1777,23 +1779,26 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
)->record(); )->record();
$origPublished = $published; $origPublished = $published;
foreach($fields as $fieldName => $fieldType) { foreach($this->db() as $fieldName => $fieldType) {
if ($fieldType != 'HTMLText') continue; // Skip if non HTML or if empty
if ($fieldType !== 'HTMLText' || empty($published[$fieldName])) {
continue;
}
// TODO: This doesn't work for HTMLText fields on other tables. // Regenerate content
if(isset($published[$fieldName])) { $content = Image::regenerate_html_links($published[$fieldName]);
$published[$fieldName] = str_replace($old, $new, $published[$fieldName], $numReplaced); if($content === $published[$fieldName]) {
if($numReplaced) { continue;
$query = sprintf('UPDATE "%s" SET "%s" = ? WHERE "ID" = ?', $table, $fieldName); }
DB::prepared_query($query, array($published[$fieldName], $this->ID));
$query = sprintf('UPDATE "%s" SET "%s" = ? WHERE "ID" = ?', $table, $fieldName);
// Tell static caching to update itself DB::prepared_query($query, array($content, $this->ID));
if($table == 'SiteTree_Live') {
$publishedClass = $origPublished['ClassName']; // Tell static caching to update itself
$origPublishedObj = new $publishedClass($origPublished); if($table == 'SiteTree_Live') {
$this->invokeWithExtensions('onRenameLinkedAsset', $origPublishedObj); $publishedClass = $origPublished['ClassName'];
} $origPublishedObj = new $publishedClass($origPublished);
} $this->invokeWithExtensions('onRenameLinkedAsset', $origPublishedObj);
} }
} }
} }

View File

@ -14,9 +14,10 @@ class SiteTreeFileExtension extends DataExtension {
ReadonlyField::create( ReadonlyField::create(
'BackLinkCount', 'BackLinkCount',
_t('AssetTableField.BACKLINKCOUNT', 'Used on:'), _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()), ->addExtraClass('cms-description-toggle')
->setDescription($this->BackLinkHTMLList()),
'LastEdited' 'LastEdited'
); );
} }
@ -27,19 +28,21 @@ class SiteTreeFileExtension extends DataExtension {
* @return String * @return String
*/ */
public function BackLinkHTMLList() { 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>'; $html .= '<ul>';
foreach ($this->BackLinkTracking() as $backLink) { foreach ($this->BackLinkTracking() as $backLink) {
$listItem = '<li>'; // Add the page link and CMS link
$html .= sprintf(
// Add the page link '<li><a href="%s" target="_blank">%s</a> &ndash; <a href="%s">%s</a></li>',
$listItem .= '<a href="' . $backLink->Link() . '" target="_blank">' . Convert::raw2xml($backLink->MenuTitle) . '</a> &ndash; '; Convert::raw2att($backLink->Link()),
Convert::raw2xml($backLink->MenuTitle),
// Add the CMS link Convert::raw2att($backLink->CMSEditLink()),
$listItem .= '<a href="' . $backLink->CMSEditLink() . '">' . _t('SiteTreeFileExtension.EDIT', 'Edit') . '</a>'; _t('SiteTreeFileExtension.EDIT', 'Edit')
);
$html .= $listItem . '</li>';
} }
return $html .= '</ul>'; return $html .= '</ul>';
@ -54,31 +57,13 @@ class SiteTreeFileExtension extends DataExtension {
* @param string $limit * @param string $limit
* @return ManyManyList * @return ManyManyList
*/ */
public function BackLinkTracking($filter = null, $sort = null, $join = null, $limit = null) { public function BackLinkTracking() {
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);
}
if(class_exists("Subsite")){ if(class_exists("Subsite")){
$rememberSubsiteFilter = Subsite::$disable_subsite_filter; $rememberSubsiteFilter = Subsite::$disable_subsite_filter;
Subsite::disable_subsite_filter(true); 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'); $links = $this->owner->getManyManyComponents('BackLinkTracking');
if($this->owner->ID) {
$links = $links
->where($filter)
->sort($sort)
->limit($limit);
}
$this->owner->extend('updateBackLinkTracking', $links); $this->owner->extend('updateBackLinkTracking', $links);
if(class_exists("Subsite")){ if(class_exists("Subsite")){
@ -115,7 +100,9 @@ class SiteTreeFileExtension extends DataExtension {
// This will syncLinkTracking on draft // This will syncLinkTracking on draft
Versioned::reading_stage('Stage'); Versioned::reading_stage('Stage');
$brokenPages = DataObject::get('SiteTree')->byIDs($brokenPageIDs); $brokenPages = DataObject::get('SiteTree')->byIDs($brokenPageIDs);
foreach($brokenPages as $brokenPage) $brokenPage->write(); foreach($brokenPages as $brokenPage) {
$brokenPage->write();
}
// This will syncLinkTracking on published // This will syncLinkTracking on published
Versioned::reading_stage('Live'); 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. * Rewrite links to the $old file to now point to the $new file.
* *
* @uses SiteTree->rewriteFileURL() * @uses SiteTree->rewriteFileID()
*
* @param String $old File path relative to the webroot
* @param String $new File path relative to the webroot
*/ */
public function updateLinks($old, $new) { public function updateLinks() {
if(class_exists('Subsite')) Subsite::disable_subsite_filter(true); if(class_exists('Subsite')) {
Subsite::disable_subsite_filter(true);
}
$pages = $this->owner->BackLinkTracking(); $pages = $this->owner->BackLinkTracking();
$summary = "";
if($pages) { 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);
}
} }
} }

View File

@ -12,13 +12,13 @@
* referenced in any HTMLText fields, and two booleans to indicate if there are any broken links. Call * 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. * augmentSyncLinkTracking to update those fields with any changes to those fields.
* *
* @property SiteTree owner * @property SiteTree $owner
* *
* @property bool HasBrokenFile * @property bool $HasBrokenFile
* @property bool HasBrokenLink * @property bool $HasBrokenLink
* *
* @method ManyManyList LinkTracking List of site pages 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. * @method ManyManyList ImageTracking() List of Images linked on this page.
*/ */
class SiteTreeLinkTracking extends DataExtension { class SiteTreeLinkTracking extends DataExtension {
@ -110,22 +110,25 @@ class SiteTreeLinkTracking extends DataExtension {
// Add file tracking for image references // Add file tracking for image references
if($images = $htmlValue->getElementsByTagName('img')) foreach($images as $img) { 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; $linkedFiles[] = $image->ID;
} else { } else {
if(substr($path, 0, strlen(ASSETS_DIR) + 1) == ASSETS_DIR . '/') { $record->HasBrokenFile = true;
$record->HasBrokenFile = true;
}
} }
} }
// Update the "LinkTracking" many_many // Update the "LinkTracking" many_many
if($record->ID && $record->manyManyComponent('LinkTracking') && $tracker = $record->LinkTracking()) { if($record->ID && $record->manyManyComponent('LinkTracking') && ($tracker = $record->LinkTracking())) {
$tracker->removeByFilter(sprintf( $tracker->removeByFilter(array(
'"FieldName" = \'%s\' AND "%s" = %d', sprintf('"FieldName" = ? AND "%s" = ?', $tracker->getForeignKey())
$fieldName, => array($fieldName, $record->ID)
$tracker->getForeignKey(),
$record->ID
)); ));
if($linkedPages) foreach($linkedPages as $item) { if($linkedPages) foreach($linkedPages as $item) {
@ -134,12 +137,10 @@ class SiteTreeLinkTracking extends DataExtension {
} }
// Update the "ImageTracking" many_many // Update the "ImageTracking" many_many
if($record->ID && $record->manyManyComponent('ImageTracking') && $tracker = $record->ImageTracking()) { if($record->ID && $record->manyManyComponent('ImageTracking') && ($tracker = $record->ImageTracking())) {
$tracker->removeByFilter(sprintf( $tracker->removeByFilter(array(
'"FieldName" = \'%s\' AND "%s" = %d', sprintf('"FieldName" = ? AND "%s" = ?', $tracker->getForeignKey())
$fieldName, => array($fieldName, $record->ID)
$tracker->getForeignKey(),
$record->ID
)); ));
if($linkedFiles) foreach($linkedFiles as $item) { if($linkedFiles) foreach($linkedFiles as $item) {

View File

@ -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";
}
}

View File

@ -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 fill in the "internal" dropdown with "Details"
And I wait for 1 second And I wait for 1 second
And I select "youranchor" from "Form_EditorToolbarLinkForm_AnchorSelector" 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>" Then the "Content" HTML field should contain "<a href="[sitetree_link,id=3]#youranchor">awesome</a>"
# Required to avoid "unsaved changes" browser dialog # Required to avoid "unsaved changes" browser dialog
Then I press the "Save draft" button 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 press the "Insert Link" button
When I select the "Download a file" radio button When I select the "Download a file" radio button
And I attach the file "testfile.jpg" to "file[Uploads][]" with HTML5 And I attach the file "testfile.jpg" to "file[Uploads][]" with HTML5
And I press the "Insert link" button And I press the "Insert" button
Then the "Content" HTML field should contain "<a href="[file_link,id=3]" target="_blank">awesome</a>" Then the "Content" HTML field should contain "<a href="[file_link,id=4]" target="_blank">awesome</a>"
# Required to avoid "unsaved changes" browser dialog # Required to avoid "unsaved changes" browser dialog
Then I press the "Save draft" button 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 press the "Insert Link" button
When I select the "Anchor on this page" radio button When I select the "Anchor on this page" radio button
And I select "myanchor" from "Form_EditorToolbarLinkForm_AnchorSelector" 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>" Then the "Content" HTML field should contain "<a href="#myanchor">awesome</a>"
# Required to avoid "unsaved changes" browser dialog # Required to avoid "unsaved changes" browser dialog
Then I press the "Save draft" button 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" Then the "Form_EditorToolbarLinkForm_external" field should contain "http://silverstripe.org"
# This doesn't seem to suffer from that issue # This doesn't seem to suffer from that issue
When I fill in "http://google.com" for "URL" 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>" Then the "Content" HTML field should contain "<a href="http://google.com">awesome</a>"
# Required to avoid "unsaved changes" browser dialog # Required to avoid "unsaved changes" browser dialog
Then I press the "Save draft" button Then I press the "Save draft" button

View File

@ -18,7 +18,7 @@ Feature: Insert an image into a page
When I click "add by URL" in the ".ss-uploadfield-item-info" element 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 fill in "RemoteURL" with "http://www.silverstripe.org/themes/ssv3/img/ss_logo.png"
And I press the "Add url" button 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 When I press the "Insert" button
Then the "Content" HTML field should contain "ss_logo.png" 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 And I attach the file "testfile.jpg" to "AssetUploadField" with HTML5
# TODO Delay previous step until upload succeeded # TODO Delay previous step until upload succeeded
And I wait for 2 seconds 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 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 # Required to avoid "unsaved changed" browser dialog
Then I press the "Save draft" button 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 # TODO Delay previous step until upload succeeded
And I wait for 2 seconds And I wait for 2 seconds
# Note change in default behaviour from 3.1, respect default Upload.replaceFile=false # Note change in default behaviour from 3.1, respect default Upload.replaceFile=false
Then there should be a file "assets/Uploads/file1.jpg" Then there should be a file "assets/Uploads/3d0ef6ec37/file1.jpg"
And there should be a file "assets/Uploads/file1-v2.jpg" And there should be a file "assets/Uploads/3d0ef6ec37/file1-v2.jpg"
When I press the "Insert" button 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 # Required to avoid "unsaved changed" browser dialog
Then I press the "Save draft" button 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 fill in the "ParentID" dropdown with "folder1"
And I click on "file1" in the "Files" table And I click on "file1" in the "Files" table
When I press the "Insert" button 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 # Required to avoid "unsaved changed" browser dialog
Then I press the "Save draft" button Then I press the "Save draft" button
@ -68,7 +68,7 @@ Feature: Insert an image into a page
And I press the "Edit" button And I press the "Edit" button
When I fill in "Alternative text (alt)" with "My alt" When I fill in "Alternative text (alt)" with "My alt"
And I press the "Insert" button 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" And the "Content" HTML field should contain "My alt"
# Required to avoid "unsaved changed" browser dialog # Required to avoid "unsaved changed" browser dialog
Then I press the "Save draft" button 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 This needs to support using drag handles, as we no longer have 'Width' or 'Height' input fields
@todo @todo
Scenario: I can edit dimensions of an existing image 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 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 And I press the "Insert Media" button
Then I should see "file1.jpg" Then I should see "file1.jpg"
When I fill in "Width" with "10" When I fill in "Width" with "10"
When I fill in "Height" with "20" When I fill in "Height" with "20"
And I press the "Insert" button 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 # Required to avoid "unsaved changed" browser dialog
Then I press the "Save draft" button Then I press the "Save draft" button

View File

@ -6,7 +6,7 @@ Feature: Manage files
Background: Background:
Given a "image" "assets/folder1/file1.jpg" was created "2012-01-01 12:00:00" 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 a "folder" "assets/folder2"
And I am logged in with "ADMIN" permissions And I am logged in with "ADMIN" permissions
And I go to "/admin/assets" And I go to "/admin/assets"
@ -21,7 +21,7 @@ Feature: Manage files
Scenario: I can list files in a folder Scenario: I can list files in a folder
Given I click on "folder1" in the "Files" table Given I click on "folder1" in the "Files" table
Then the "folder1" table should contain "file1" 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 Scenario: I can upload a file to a folder
Given I click on "folder1" in the "Files" table Given I click on "folder1" in the "Files" table

View File

@ -8,40 +8,52 @@ class FileLinkTrackingTest extends SapphireTest {
public function setUp() { public function setUp() {
parent::setUp(); parent::setUp();
AssetStoreTest_SpyStore::activate('FileLinkTrackingTest');
$this->logInWithPermission('ADMIN'); $this->logInWithPermission('ADMIN');
if(!file_exists(ASSETS_PATH)) mkdir(ASSETS_PATH); // Write file contents
$fh = fopen(ASSETS_PATH . '/testscript-test-file.pdf', "w"); $files = File::get()->exclude('ClassName', 'Folder');
fwrite($fh, str_repeat('x',1000000)); foreach($files as $file) {
fclose($fh); $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() { public function tearDown() {
AssetStoreTest_SpyStore::reset();
parent::tearDown(); 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() { public function testFileRenameUpdatesDraftAndPublishedPages() {
$page = $this->objFromFixture('Page', 'page1'); $page = $this->objFromFixture('Page', 'page1');
$this->assertTrue($page->doPublish()); $this->assertTrue($page->doPublish());
$this->assertContains('<img src="assets/testscript-test-file.pdf"', $this->assertContains(
DB::prepared_query("SELECT \"Content\" FROM \"SiteTree_Live\" WHERE \"ID\" = ?", array($page->ID))->value()); '<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 = $this->objFromFixture('Image', 'file1');
$file->Name = 'renamed-test-file.pdf'; $file->Name = 'renamed-test-file.jpg';
$file->write(); $file->write();
$this->assertContains('<img src="assets/renamed-test-file.pdf"', $this->assertContains(
DB::prepared_query("SELECT \"Content\" FROM \"SiteTree\" WHERE \"ID\" = ?", array($page->ID))->value()); '<img src="/assets/FileLinkTrackingTest/55b443b601/renamed-test-file.jpg"',
$this->assertContains('<img src="assets/renamed-test-file.pdf"', DB::prepared_query("SELECT \"Content\" FROM \"SiteTree\" WHERE \"ID\" = ?", array($page->ID))->value()
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_Live\" WHERE \"ID\" = ?", array($page->ID))->value()
);
} }
public function testFileLinkRewritingOnVirtualPages() { public function testFileLinkRewritingOnVirtualPages() {
@ -56,15 +68,19 @@ class FileLinkTrackingTest extends SapphireTest {
$svp->doPublish(); $svp->doPublish();
// Rename the file // Rename the file
$file = $this->objFromFixture('File', 'file1'); $file = $this->objFromFixture('Image', 'file1');
$file->Name = 'renamed-test-file.pdf'; $file->Name = 'renamed-test-file.jpg';
$file->write(); $file->write();
// Verify that the draft and publish virtual pages both have the corrected link // Verify that the draft and publish virtual pages both have the corrected link
$this->assertContains('<img src="assets/renamed-test-file.pdf"', $this->assertContains(
DB::prepared_query("SELECT \"Content\" FROM \"SiteTree\" WHERE \"ID\" = ?", array($svp->ID))->value()); '<img src="/assets/FileLinkTrackingTest/55b443b601/renamed-test-file.jpg"',
$this->assertContains('<img src="assets/renamed-test-file.pdf"', DB::prepared_query("SELECT \"Content\" FROM \"SiteTree\" WHERE \"ID\" = ?", array($svp->ID))->value()
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_Live\" WHERE \"ID\" = ?", array($svp->ID))->value()
);
} }
public function testLinkRewritingOnAPublishedPageDoesntMakeItEditedOnDraft() { public function testLinkRewritingOnAPublishedPageDoesntMakeItEditedOnDraft() {
@ -74,8 +90,8 @@ class FileLinkTrackingTest extends SapphireTest {
$this->assertFalse($page->getIsModifiedOnStage()); $this->assertFalse($page->getIsModifiedOnStage());
// Rename the file // Rename the file
$file = $this->objFromFixture('File', 'file1'); $file = $this->objFromFixture('Image', 'file1');
$file->Name = 'renamed-test-file.pdf'; $file->Name = 'renamed-test-file.jpg';
$file->write(); $file->write();
// Caching hack // Caching hack
@ -89,25 +105,30 @@ class FileLinkTrackingTest extends SapphireTest {
public function testTwoFileRenamesInARowWork() { public function testTwoFileRenamesInARowWork() {
$page = $this->objFromFixture('Page', 'page1'); $page = $this->objFromFixture('Page', 'page1');
$this->assertTrue($page->doPublish()); $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()); DB::prepared_query("SELECT \"Content\" FROM \"SiteTree_Live\" WHERE \"ID\" = ?", array($page->ID))->value());
// Rename the file twice // Rename the file twice
$file = $this->objFromFixture('File', 'file1'); $file = $this->objFromFixture('Image', 'file1');
$file->Name = 'renamed-test-file.pdf'; $file->Name = 'renamed-test-file.jpg';
$file->write(); $file->write();
// TODO Workaround for bug in DataObject->getChangedFields(), which returns stale data, // TODO Workaround for bug in DataObject->getChangedFields(), which returns stale data,
// and influences File->updateFilesystem() // and influences File->updateFilesystem()
$file = DataObject::get_by_id('File', $file->ID); $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(); $file->write();
// Confirm that the correct image is shown in both the draft and live site // 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"', $this->assertContains(
DB::prepared_query("SELECT \"Content\" FROM \"SiteTree\" WHERE \"ID\" = ?", array($page->ID))->value()); '<img src="/assets/FileLinkTrackingTest/55b443b601/renamed-test-file-second-time.jpg"',
$this->assertContains('<img src="assets/renamed-test-file-second-time.pdf"', DB::prepared_query("SELECT \"Content\" FROM \"SiteTree\" WHERE \"ID\" = ?", array($page->ID))->value()
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_Live\" WHERE \"ID\" = ?", array($page->ID))->value()
);
} }
} }

View File

@ -1,10 +1,12 @@
# These need to come first so that SiteTree has the link meta-data written. # These need to come first so that SiteTree has the link meta-data written.
File: Image:
file1: file1:
Filename: assets/testscript-test-file.pdf FileFilename: testscript-test-file.jpg
FileHash: 55b443b60176235ef09801153cca4e6da7494a0c
Name: testscript-test-file.jpg
Page: Page:
page1: page1:
Title: page1 Title: page1
URLSegment: 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>'

View File

@ -4,6 +4,25 @@ class SiteTreeHtmlEditorFieldTest extends FunctionalTest {
protected static $use_draft_site = true; 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() { public function testLinkTracking() {
$sitetree = $this->objFromFixture('SiteTree', 'home'); $sitetree = $this->objFromFixture('SiteTree', 'home');
$editor = new HtmlEditorField('Content'); $editor = new HtmlEditorField('Content');
@ -102,21 +121,24 @@ class SiteTreeHtmlEditorFieldTest extends FunctionalTest {
public function testImageTracking() { public function testImageTracking() {
$sitetree = $this->objFromFixture('SiteTree', 'home'); $sitetree = $this->objFromFixture('SiteTree', 'home');
$editor = new HtmlEditorField('Content'); $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); $editor->saveInto($sitetree);
$sitetree->write(); $sitetree->write();
$this->assertEquals ( $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->setValue(null);
$editor->saveInto($sitetree); $editor->saveInto($sitetree);
$sitetree->write(); $sitetree->write();
$this->assertEquals ( $this->assertEmpty(
array(), $sitetree->ImageTracking()->getIDList(), 'Tracked images are deleted when removed.' $sitetree->ImageTracking()->getIDList(),
'Tracked images are deleted when removed.'
); );
} }

View File

@ -8,8 +8,12 @@ SiteTree:
File: File:
example_file: example_file:
FileFilename: example.pdf
FileHash: 55b443b60176235ef09801153cca4e6da7494a0c
Name: example.pdf Name: example.pdf
Image: Image:
example_image: example_image:
FileFilename: example.jpg
FileHash: 55b443b60176235ef09801153cca4e6da7494a0c
Name: example.jpg Name: example.jpg

View File

@ -2,7 +2,7 @@
class SiteTreeLinkTrackingTest extends SapphireTest { class SiteTreeLinkTrackingTest extends SapphireTest {
function isBroken($content) { protected function isBroken($content) {
$parser = new SiteTreeLinkTracking_Parser(); $parser = new SiteTreeLinkTracking_Parser();
$htmlValue = Injector::inst()->create('HTMLValue', $content); $htmlValue = Injector::inst()->create('HTMLValue', $content);
$links = $parser->process($htmlValue); $links = $parser->process($htmlValue);
@ -11,7 +11,7 @@ class SiteTreeLinkTrackingTest extends SapphireTest {
return $links[0]['Broken']; 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]">link</a>'));
$this->assertTrue($this->isBroken('<a href="[sitetree_link,id=123]#no-such-anchor">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>')); $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>")); $this->assertFalse($this->isBroken("<a href=\"[file_link,id=$file->ID]\">link</a>"));
} }
function highlight($content) { protected function highlight($content) {
$page = new Page(); $page = new Page();
$page->Content = $content; $page->Content = $content;
$page->write(); $page->write();
return $page->Content; return $page->Content;
} }
function testHighlighter() { public function testHighlighter() {
$content = $this->highlight('<a href="[sitetree_link,id=123]" class="existing-class">link</a>'); $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, '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.'); $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.'); $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;
}
} }