Merged from branches/2.3

git-svn-id: svn://svn.silverstripe.com/silverstripe/open/modules/sapphire/trunk@76846 467b73ca-7a2a-4603-9d3b-597d59a354a9
This commit is contained in:
Sean Harvey 2009-05-14 05:26:47 +00:00
parent 5b0ee9678f
commit b1018f5864
9 changed files with 307 additions and 24 deletions

View File

@ -102,12 +102,6 @@ class Image extends File {
$url = $this->URL();
$title = ($this->Title) ? $this->Title : $this->Filename;
// remove file path and extension
// that accessware.co.nz complains about this
if (preg_match("/([^\/.]*)\..{1,6}$/", $title, $matches)) {
$title = $matches[1];
}
return "<img src=\"$url\" alt=\"$title\" />";
}
}

View File

@ -696,12 +696,12 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
// if page can't be edited, don't grant delete permissions
if(!$this->canEdit($member)) return false;
$children = $this->AllChildren();
if($children) foreach($children as $child) {
if(!$child->canDelete($member)) return false;
}
return $this->stat('can_create') != false;
}
@ -1337,8 +1337,8 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
$labels['ShowInMenus'] =_t('SiteTree.SHOWINMENUS', "Show in menus?");
$labels['ShowInSearch'] = _t('SiteTree.SHOWINSEARCH', "Show in search?");
$labels['ProvideComments'] = _t('SiteTree.ALLOWCOMMENTS', "Allow comments on this page?");
$labels['ViewersGroup'] = _t('SiteTree.GROUP', "Group");
$labels['EditorsGroup'] = _t('SiteTree.GROUP');
$labels['ViewerGroups'] = _t('SiteTree.VIEWERGROUPS', "Viewer Groups");
$labels['EditorGroups'] = _t('SiteTree.EDITORGROUPS', "Editor Groups");
$labels['URLSegment'] = _t('SiteTree.URLSegment', 'URL Segment', PR_MEDIUM, 'URL for this page');
$labels['Content'] = _t('SiteTree.Content', 'Content', PR_MEDIUM, 'Main HTML Content for a page');
$labels['HomepageForDomain'] = _t('SiteTree.HomepageForDomain', 'Hompage for this domain');
@ -1533,6 +1533,13 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
return $result;
}
function doDeleteFromLive() {
$origStage = Versioned::current_stage();
Versioned::reading_stage('Live');
$this->delete();
Versioned::reading_stage($origStage);
}
/**
* Check if this page is new - that is, if it has yet to have been written

View File

@ -357,6 +357,10 @@ class Folder extends File {
),
new HiddenField("ID")
);
if(!$this->canEdit()) {
$fields->removeFieldFromTab("Root", "Upload");
}
$this->extend('updateCMSFields', $fields);

249
forms/HtmlEditorConfig.php Normal file
View File

@ -0,0 +1,249 @@
<?php
/**
* A PHP version of TinyMCE's configuration, to allow various parameters to be configured on a site or section basis
*
* There can be multiple HtmlEditorConfig's, which should always be created / accessed using HtmlEditorConfig::get. You can then set
* the currently active config using set_active. Whichever config is active when HtmlEditorField#Field is called wins.
*
* @author "Hamish Friedlander" <hamish@silverstripe.com>
*/
class HtmlEditorConfig {
static $configs = array();
static $current = null;
/**
* Get the HtmlEditorConfig object for the given identifier. This is a correct way to get an HtmlEditorConfig instance - do not call 'new'
* @param $identifier string - the identifier for the config set
* @return HtmlEditorConfig - the configuration object. This will be created if it does not yet exist for that identifier
*/
static function get($identifier = 'default') {
if (!array_key_exists($identifier, self::$configs)) self::$configs[$identifier] = new HtmlEditorConfig();
return self::$configs[$identifier];
}
/**
* Set the currently active configuration object
* @param $identifier string - the identifier for the config set
* @return null
*/
static function set_active($identifier = null) {
self::$current = $identifier;
}
/**
* Get the currently active configuration object
* @return HtmlEditorConfig - the active configuration object
*/
static function get_active() {
$identifier = self::$current ? self::$current : 'default';
return self::get($identifier);
}
/**
* Holder for all TinyMCE settings _except_ plugins and buttons
*/
protected $settings = array(
'mode' => "specific_textareas",
'editor_selector' => "htmleditor",
'width' => "100%",
'auto_resize' => false,
'theme' => "advanced",
'theme_advanced_layout_manager' => "SimpleLayout",
'theme_advanced_toolbar_location' => "top",
'theme_advanced_toolbar_align' => "left",
'theme_advanced_toolbar_parent' => "right",
'blockquote_clear_tag' => "p",
'table_inline_editing' => true,
'safari_warning' => false,
'relative_urls' => true,
'verify_html' => true
);
/**
* Holder list of enabled plugins
*/
protected $plugins = array(
'blockquote', 'contextmenu', 'table', 'emotions', 'paste', '../../tinymce_advcode', 'spellchecker'
);
/**
* Holder list of buttons, organised by line
*/
protected $buttons = array(
1 => array('bold','italic','underline','strikethrough','separator','justifyleft','justifycenter','justifyright','justifyfull','formatselect','separator','bullist','numlist','outdent','indent','blockquote','hr','charmap'),
2 => array('undo','redo','separator','cut','copy','paste','pastetext','pasteword','spellchecker','separator','advcode','search','replace','selectall','visualaid','separator','tablecontrols'),
3 => array()
);
/**
* Get the current value of an option
* @param $k string - The key of the option to get
* @return mixed - The value of the specified option
*/
function getOption($k) {
return $this->settings[$k];
}
/**
* Set the value of one option
* @param $k string - The key of the option to set
* @param $v mixed - The value of the option to set
* @return mixed - $v returned for chaining
*/
function setOption($k,$v) {
return $this->settings[$k] = $v;
}
/**
* Set multiple options
* @param $a array - The options to set, as keys and values of the array
* @return null
*/
function setOptions($a) {
foreach ($a as $k=>$v) {
$this->settings[$k] = $v;
}
}
/**
* Enable one or several plugins. Will maintain unique list if already enabled plugin is re-passed
* @param[0..] a string, or several strings, or a single array of strings - The plugins to enable
* @return null
*/
function enablePlugins() {
$plugins = func_get_args();
if (is_array($plugins[0])) $plugins = $plugins[0];
foreach ($plugins as $plugin) {
if (!in_array($plugin, $this->plugins)) $this->plugins[] = $plugin;
}
}
/**
* Enable one or several plugins. Will properly handle being passed a plugin that is already disabled
* @param[0..] a string, or several strings, or a single array of strings - The plugins to disable
* @return null
*/
function disablePlugins() {
$plugins = func_get_args();
if (is_array($plugins[0])) $plugins = $plugins[0];
foreach ($plugins as $plugin) {
if (($idx = array_search($plugin, $this->plugins)) !== false) {
array_splice($this->plugins, $idx, 1);
continue;
}
}
}
/**
* Totally re-set the buttons on a given line
* @param[0] integer from 1..3 - The line number to redefine
* @param[1..] a string or several strings, or a single array of strings - The button names to make this line contain
* @return null
*/
function setButtonsForLine() {
if (func_num_args() == 2) {
list($line, $buttons) = func_get_args();
}
else {
$buttons = func_get_args();
$line = array_shift($buttons);
}
$this->buttons[$line] = is_array($buttons) ? $buttons : array($buttons);
}
/**
* Add buttons to the end of a line
* @param[0] integer from 1..3
* @param[1..] a string, or several strings, or a single array of strings - The button names to add to the end of this line
* @return null
*/
function addButtonsToLine() {
$inserts = func_get_args();
$line = array_shift($inserts);
if (is_array($inserts[0])) $inserts = $inserts[0];
foreach ($inserts as $button) {
$this->buttons[$line][] = $button;
}
}
/**
* Internal function for adding and removing buttons related to another button
* @param $name string - the name of the button to modify
* @param $offset integer - the offset relative to that button to perform an array_splice at - 0 for before $name, 1 for after
* @param $del integer - the number of buttons to remove at the position given by index(string) + offset
* @param $add mixed - an array or single item to insert at the position given by index(string) + offset, or null for no insertion
* @return boolean - true if $name matched a button, false otherwise
*/
protected function modifyButtons($name, $offset, $del=0, $add=null) {
foreach ($this->buttons as &$buttons) {
if (($idx = array_search($name, $buttons)) !== false) {
if ($add) array_splice($buttons, $idx+$offset, $del, $add);
else array_splice($buttons, $idx+$offset, $del, $add);
return true;
}
}
return false;
}
/**
* Insert buttons before the first occurance of another button
* @param[0] string - the name of the button to insert other buttons before
* @param[1..] a string, or several strings, or a single array of strings - the button names to insert before that button
* @return boolean - true if insertion occured, false if it did not (because the given button name was not found)
*/
function insertButtonsBefore() {
$inserts = func_get_args();
$before = array_shift($inserts);
return $this->modifyButtons($before, 0, 0, $inserts);
}
/**
* Insert buttons after the first occurance of another button
* @param[0] string - the name of the button to insert other buttons after
* @param[1..] a string, or several strings, or a single array of strings - the button names to insert after that button
* @return boolean - true if insertion occured, false if it did not (because the given button name was not found)
*/
function insertButtonsAfter() {
$inserts = func_get_args();
$after = array_shift($inserts);
return $this->modifyButtons($after, 1, 0, $inserts);
}
/**
* Remove the first occurance of buttons
* @param[0..] string - the name of the buttons to remove
* @return null
*/
function removeButtons() {
$removes = func_get_args();
foreach ($removes as $button) {
$this->modifyButtons($button, 0, 1);
}
}
/**
* Generate the javascript that will set tinyMCE's configuration to that of the current settings of this object
* @return string - the javascript
*/
function generateJS() {
$config = $this->settings;
$config['plugins'] = implode(',', $this->plugins);
foreach ($this->buttons as $i=>$buttons) {
$config['theme_advanced_buttons'.$i] = implode(',', $buttons);
}
return "
if((typeof tinyMCE != 'undefined')) {
tinyMCE.init(" . Convert::raw2json($config) . ");
}";
}
}

View File

@ -20,13 +20,12 @@ class HtmlEditorField extends TextareaField {
$this->extraClass = 'typography';
}
/**
* Returns the a <textarea> field with tinymce="true" set on it
*/
function Field() {
Requirements::javascript(MCE_ROOT . "tiny_mce_src.js");
Requirements::javascript(SAPPHIRE_DIR . '/javascript/HtmlEditorField.js');
Requirements::customScript(HtmlEditorConfig::get_active()->generateJS(), 'htmlEditorConfig');
// Don't allow unclosed tags - they will break the whole application ;-)
$cleanVal = $this->value;

View File

@ -936,24 +936,21 @@ JS
$items = $this->customSourceItems;
} else {
$dataQuery = $this->getCsvQuery();
$records = $dataQuery->execute();
$sourceClass = $this->sourceClass;
$dataobject = new $sourceClass();
$items = $dataobject->buildDataObjectSet($records, 'DataObjectSet');
}
if($items && $items->count()) foreach($items as $item) {
// create a TableListField_Item to support resolving of
// relation-fields in dot notation via TableListField_Item->Fields()
if($item) $fieldItems->push(new TableListField_Item($item, $this));
$items = $dataQuery->execute();
}
// temporary override to adjust TableListField_Item behaviour
$this->setFieldFormatting(array());
$this->fieldList = $csvColumns;
if($fieldItems) {
foreach($fieldItems as $fieldItem) {
if($items) {
foreach($items as $item) {
if(is_array($item)) {
$className = isset($item['RecordClassName']) ? $item['RecordClassName'] : $item['ClassName'];
$item = new $className($item);
}
$fieldItem = new TableListField_Item($item, $this);
$fields = $fieldItem->Fields();
$columnData = array();
if($fields) foreach($fields as $field) {
@ -973,6 +970,10 @@ JS
}
$fileData .= implode($separator, $columnData);
$fileData .= "\n";
$item->destroy();
unset($item);
unset($fieldItem);
}
$numColumns = count($columnData);

View File

@ -13,6 +13,7 @@ class VersionedTest extends SapphireTest {
// Get all items, ignoring deleted
$remainingPages = DataObject::get("SiteTree", "ParentID = 0");
// Check that page 3 has gone
$this->assertNotNull($remainingPages);
$this->assertEquals(array("Page 1", "Page 2"), $remainingPages->column('Title'));
// Get all including deleted

View File

@ -10,6 +10,7 @@ class VirtualPageTest extends SapphireTest {
function testEditingSourcePageUpdatesVirtualPages() {
$master = $this->objFromFixture('Page', 'master');
$master->Title = "New title";
$master->MenuTitle = "New menutitle";
$master->Content = "<p>New content</p>";
$master->write();
@ -18,6 +19,8 @@ class VirtualPageTest extends SapphireTest {
$this->assertEquals("New title", $vp1->Title);
$this->assertEquals("New title", $vp2->Title);
$this->assertEquals("New menutitle", $vp1->MenuTitle);
$this->assertEquals("New menutitle", $vp2->MenuTitle);
$this->assertEquals("<p>New content</p>", $vp1->Content);
$this->assertEquals("<p>New content</p>", $vp2->Content);
}
@ -29,6 +32,7 @@ class VirtualPageTest extends SapphireTest {
function testPublishingSourcePagePublishesVirtualPages() {
$master = $this->objFromFixture('Page', 'master');
$master->Title = "New title";
$master->MenuTitle = "New menutitle";
$master->Content = "<p>New content</p>";
$master->write();
$master->doPublish();
@ -42,9 +46,31 @@ class VirtualPageTest extends SapphireTest {
$this->assertEquals("New title", $vp1->Title);
$this->assertEquals("New title", $vp2->Title);
$this->assertEquals("New menutitle", $vp1->MenuTitle);
$this->assertEquals("New menutitle", $vp2->MenuTitle);
$this->assertEquals("<p>New content</p>", $vp1->Content);
$this->assertEquals("<p>New content</p>", $vp2->Content);
Versioned::reading_stage("Stage");
}
/**
* Test that virtual pages get the content from the master page when they are created.
*/
function testNewVirtualPagesGrabTheContentFromTheirMaster() {
$vp = new VirtualPage();
$vp->write();
$vp->CopyContentFromID = $this->idFromFixture('Page', 'master');
$vp->write();
$this->assertEquals("My Page", $vp->Title);
$this->assertEquals("My Page Nav", $vp->MenuTitle);
$vp->CopyContentFromID = $this->idFromFixture('Page', 'master2');
$vp->write();
$this->assertEquals("My Other Page", $vp->Title);
$this->assertEquals("My Other Page Nav", $vp->MenuTitle);
}
}

View File

@ -1,8 +1,10 @@
Page:
master:
Title: My Page
MenuTitle: My Page Nav
master2:
Title: My Other Page
MenuTitle: My Other Page Nav
holder:
Title: Sub-pages
VirtualPage: