diff --git a/_config.php b/_config.php index 5d06b200..1d2d5bc9 100644 --- a/_config.php +++ b/_config.php @@ -17,4 +17,37 @@ Director::addRules(50, array( )); CMSMenu::populate_menu(); + +HtmlEditorConfig::get('cms')->setOptions(array( + 'mode' => 'none', + 'language' => i18n::get_tinymce_lang(), + 'content_css' => 'cms/css/editor.css, '.(SSViewer::current_theme() ? THEMES_DIR . "/" . SSViewer::current_theme() : project()) . "/css/editor.css", + + 'body_class' => 'typography', + 'document_base_url' => Director::absoluteBaseURL(), + + 'urlconverter_callback' => "nullConverter", + 'setupcontent_callback' => "sapphiremce_setupcontent", + 'cleanup_callback' => "sapphiremce_cleanup", + + 'template_templates' => array( + array( 'title' => "Three column", 'src' => "assets/snippet.html", 'description' => "A simple 3 column layout" ) + ), + + 'use_native_selects' => true, // fancy selects are bug as of SS 2.3.0 + 'valid_elements' => "+a[id|rel|rev|dir|tabindex|accesskey|type|name|href|target|title|class],-strong/-b[class],-em/-i[class],-strike[class],-u[class],#p[id|dir|class|align],-ol[class],-ul[class],-li[class],br,img[id|dir|longdesc|usemap|class|src|border|alt=|title|width|height|align],-sub[class],-sup[class],-blockquote[dir|class],-table[border=0|cellspacing|cellpadding|width|height|class|align|summary|dir|id|style],-tr[id|dir|class|rowspan|width|height|align|valign|bgcolor|background|bordercolor|style],tbody[id|class|style],thead[id|class|style],tfoot[id|class|style],-td[id|dir|class|colspan|rowspan|width|height|align|valign|scope|style],-th[id|dir|class|colspan|rowspan|width|height|align|valign|scope|style],caption[id|dir|class],-div[id|dir|class|align|style],-span[class|align],-pre[class|align],address[class|align],-h1[id|dir|class|align],-h2[id|dir|class|align],-h3[id|dir|class|align],-h4[id|dir|class|align],-h5[id|dir|class|align],-h6[id|dir|class|align],hr[class],dd[id|class|title|dir],dl[id|class|title|dir],dt[id|class|title|dir]", + 'extended_valid_elements' => "img[class|src|alt|title|hspace|vspace|width|height|align|onmouseover|onmouseout|name|usemap],iframe[src|name|width|height|align|frameborder|marginwidth|marginheight|scrolling],object[width|height|data|type],param[name|value],map[class|name|id],area[shape|coords|href|target|alt]" +)); + + +HtmlEditorConfig::get('cms')->disablePlugins('blockquote'); +HtmlEditorConfig::get('cms')->enablePlugins('media', '../../tinymce_ssbuttons', 'fullscreen'); + +HtmlEditorConfig::get('cms')->insertButtonsBefore('formatselect', 'styleselect'); +HtmlEditorConfig::get('cms')->insertButtonsBefore('advcode', 'ssimage', 'ssflash', 'sslink', 'unlink', 'anchor', 'separator' ); +HtmlEditorConfig::get('cms')->insertButtonsAfter ('advcode', 'fullscreen', 'separator'); + +HtmlEditorConfig::get('cms')->removeButtons('tablecontrols'); +HtmlEditorConfig::get('cms')->addButtonsToLine(3, 'tablecontrols'); + ?> diff --git a/code/CMSMain.php b/code/CMSMain.php index bd00be28..ff36da37 100644 --- a/code/CMSMain.php +++ b/code/CMSMain.php @@ -40,7 +40,6 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr 'dialog', 'duplicate', 'duplicatewithchildren', - 'filtersitetree', 'getpagecount', 'getversion', 'publishall', @@ -56,7 +55,9 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr 'EditForm', 'AddPageOptionsForm', 'SiteTreeAsUL', - 'getshowdeletedsubtree' + 'getshowdeletedsubtree', + 'getfilteredsubtree', + 'batchactions' ); /** @@ -75,13 +76,12 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr return array( 'Title' => _t('CMSMain.TITLEOPT', 'Title', 0, 'The dropdown title in CMSMain left SiteTreeFilterOptions'), 'MenuTitle' => _t('CMSMain.MENUTITLEOPT', 'Navigation Label', 0, 'The dropdown title in CMSMain left SiteTreeFilterOptions'), - 'ClassName' => _t('CMSMain.PAGETYPEOPT', 'Page Type', 0, "The dropdown title in CMSMain left SiteTreeFilterOptions"), 'Status' => _t('CMSMain.STATUSOPT', 'Status', 0, "The dropdown title in CMSMain left SiteTreeFilterOptions"), 'MetaDescription' => _t('CMSMain.METADESCOPT', 'Description', 0, "The dropdown title in CMSMain left SiteTreeFilterOptions"), 'MetaKeywords' => _t('CMSMain.METAKEYWORDSOPT', 'Keywords', 0, "The dropdown title in CMSMain left SiteTreeFilterOptions") ); } - + public function init() { parent::init(); @@ -110,18 +110,14 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr $spellcheckSpec = array(); foreach($spellcheckLangs as $lang => $title) $spellcheckSpec[] = "{$title}={$lang}"; + HtmlEditorConfig::get('cms')->setOption('spellchecker_languages', '+' . implode(',', $spellcheckSpec)); + + // @todo Do we need this - I'm pretty sure not, since HtmlEditorField#Field() will include it on being called. + // The only time you might need it is if you are creating an textarea.htmlfield yourself, in which case bad things are going to happen now we've moved configuration // We don't want this showing up in every ajax-response, it should always be present in a CMS-environment if(!Director::is_ajax()) { Requirements::javascript(MCE_ROOT . "tiny_mce_src.js"); - Requirements::javascriptTemplate(CMS_DIR . "/javascript/tinymce.template.js", array( - "ContentCSS" => (SSViewer::current_theme() ? THEMES_DIR . "/" . SSViewer::current_theme() : project()) . "/css/editor.css", - "BaseURL" => Director::absoluteBaseURL(), - "Lang" => i18n::get_tinymce_lang(), - 'SpellcheckLangs' => '+' . implode(',', $spellcheckSpec) - )); } - // Always block the HtmlEditorField.js otherwise it will be sent with an ajax request - Requirements::block(SAPPHIRE_DIR . '/javascript/HtmlEditorField.js'); Requirements::javascript(CMS_DIR . '/javascript/CMSMain.js'); Requirements::javascript(CMS_DIR . '/javascript/CMSMain_left.js'); @@ -186,7 +182,18 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr return $tree; } + + public function getfilteredsubtree() { + // Get the tree + $tree = $this->getSiteTreeFor($this->stat('tree_class'), $_REQUEST['ID'], null, 'cmsMainMarkingFilterFunction'); + // Trim off the outer tag + $tree = ereg_replace('^[ \t\r\n]*]*>','', $tree); + $tree = ereg_replace(']*>[ \t\r\n]*$','', $tree); + + return $tree; + } + /** * Returns the SiteTree columns that can be filtered using the the Site Tree Search button as a DataObjectSet */ @@ -205,44 +212,11 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr $dateField = new CalendarDateField('SiteTreeFilterDate'); return $dateField->Field(); } - - /** - * Returns a filtered Site Tree - */ - public function filtersitetree() { - // Pre-cache sitetree version numbers for querying efficiency - Versioned::prepopulate_versionnumber_cache("SiteTree", "Stage"); - Versioned::prepopulate_versionnumber_cache("SiteTree", "Live"); - - $className = 'SiteTree'; - $rootID = null; - $obj = $rootID ? $this->getRecord($rootID) : singleton($className); - $obj->setMarkingFilterFunction('cmsMainMarkingFilterFunction'); - $obj->markPartialTree(); - - if($p = $this->currentPage()) $obj->markToExpose($p); - - // getChildrenAsUL is a flexible and complex way of traversing the tree - $siteTree = $obj->getChildrenAsUL("", ' - "
  • ID\" class=\"" . $child->CMSTreeClasses($extraArg) . "\">" . - "Link(),0,-1), "show", $child->ID) . "\" " . (($child->canEdit() || $child->canAddChildren()) ? "" : "class=\"disabled\"") . " title=\"' . _t('LeftAndMain.PAGETYPE') . '".$child->class."\" >" . - ($child->TreeTitle()) . - "" -' - ,$this, true); - - // Wrap the root if needs be. - - if(!$rootID) { - $rootLink = $this->Link() . '0'; - $siteTree = ""; - } - - return $siteTree; - - } + public function SiteTreeFilterPageTypeField() { + $types = SiteTree::page_type_classes(); array_unshift($types, 'All'); + $optionsetField = new DropdownField('ClassName', 'ClassName', array_combine($types, $types), 'Any'); + return $optionsetField->Field(); + } public function generateDataTreeHints() { $classes = ClassInfo::subclassesFor( $this->stat('tree_class') ); @@ -975,54 +949,12 @@ JS; 'DialogType' => 'alert' ))->renderWith('Dialog'); } - + /** - * Publishes a number of items. - * Called by AJAX + * Batch Actions Handler */ - public function publishitems() { - // This method can't be called without ajax. - if(!Director::is_ajax()) { - Director::redirectBack(); - return; - } - - $ids = split(' *, *', $this->requestParams['csvIDs']); - - $notifications = array(); - - $idList = array(); - - // make sure all the ids are numeric. - // Add all the children to the list of IDs if they are missing - foreach($ids as $id) { - $brokenPageList = ''; - if(is_numeric($id)) { - $record = DataObject::get_by_id($this->stat('tree_class'), $id); - - if($record) { - if($record && !$record->canPublish()) return Security::permissionFailure($this); - - // Publish this page - $record->doPublish(); - - // Now make sure the 'changed' icon is removed - $publishedRecord = DataObject::get_by_id($this->stat('tree_class'), $id); - $JS_title = Convert::raw2js($publishedRecord->TreeTitle()); - FormResponse::add("\$('sitetree').setNodeTitle($id, '$JS_title');"); - FormResponse::add("$('Form_EditForm').reloadIfSetTo($record->ID);"); - $record->destroy(); - unset($record); - } - } - } - - if (sizeof($ids) > 1) $message = sprintf(_t('CMSMain.PAGESPUB', "%d pages published "), sizeof($ids)); - else $message = sprintf(_t('CMSMain.PAGEPUB', "%d page published "), sizeof($ids)); - - FormResponse::add('statusMessage("'.$message.'","good");'); - - return FormResponse::respond(); + function batchactions() { + return new CMSBatchActionHandler($this, 'batchactions'); } /** @@ -1045,97 +977,10 @@ JS; } /** - * Delete a number of items. - * This code supports notification + * Returns a list of batch actions */ - public function deleteitems() { - // This method can't be called without ajax. - if(!Director::is_ajax()) { - Director::redirectBack(); - return; - } - - $ids = split(' *, *', $_REQUEST['csvIDs']); - - $notifications = array(); - - $idList = array(); - - // make sure all the ids are numeric. - // Add all the children to the list of IDs if they are missing - foreach($ids as $id) { - $brokenPageList = ''; - if(is_numeric($id)) { - $record = DataObject::get_by_id($this->stat('tree_class'), $id); - - if($record) { - if($record && !$record->canDelete()) return Security::permissionFailure($this); - - // add all the children for this record if they are not already in the list - // this check is a little slower but will prevent circular dependencies - // (should they exist, which they probably shouldn't) from causing - // the function to not terminate - $children = $record->AllChildren(); - - if( $children ) - foreach( $children as $child ) - if( array_search( $child->ID, $ids ) !== FALSE ) - $ids[] = $child->ID; - - if($record->hasMethod('BackLinkTracking')) { - $brokenPages = $record->BackLinkTracking(); - foreach($brokenPages as $brokenPage) { - $brokenPageList .= "
  • " . $brokenPage->Breadcrumbs(3, true) . "
  • "; - $brokenPage->HasBrokenLink = true; - $notifications[$brokenPage->OwnerID][] = $brokenPage; - $brokenPage->writeWithoutVersion(); - } - } - - $oldID = $record->ID; - $record->delete(); - $record->destroy(); - - // DataObject::delete_by_id($this->stat('tree_class'), $id); - - // check to see if the record exists on the live site, if it doesn't remove the tree node - $liveRecord = Versioned::get_one_by_stage( $this->stat('tree_class'), 'Live', "\"{$this->stat('tree_class')}\".\"ID\"={$id}"); - - if($liveRecord) { - $liveRecord->IsDeletedFromStage = true; - $title = Convert::raw2js($liveRecord->TreeTitle()); - FormResponse::add("$('sitetree').setNodeTitle($oldID, '$title');"); - FormResponse::add("$('Form_EditForm').reloadIfSetTo($oldID);"); - } else { - FormResponse::add("var node = $('sitetree').getTreeNodeByIdx('$id');"); - FormResponse::add("if(node && node.parentTreeNode) node.parentTreeNode.removeTreeNode(node);"); - FormResponse::add("$('Form_EditForm').reloadIfSetTo($oldID);"); - } - } - } - } - - if($notifications) foreach($notifications as $memberID => $pages) { - if(class_exists('Page_BrokenLinkEmail')) { - $email = new Page_BrokenLinkEmail(); - $email->populateTemplate(new ArrayData(array( - "Recipient" => DataObject::get_by_id("Member", $memberID), - "BrokenPages" => new DataObjectSet($pages), - ))); - $email->debug(); - $email->send(); - } - } - - if (sizeof($ids) > 1) $message = sprintf(_t('CMSMain.PAGESDEL', "%d pages deleted "), sizeof($ids)); - else $message = sprintf(_t('CMSMain.PAGEDEL', "%d page deleted "), sizeof($ids)); - if(isset($brokenPageList) && $brokenPageList != '') { - $message .= _t('CMSMain.NOWBROKEN'," The following pages now have broken links:")."" . _t('CMSMain.NOWBROKEN2',"Their owners have been emailed and they will fix up those pages."); - } - - FormResponse::add('statusMessage("'.$message.'","good");'); - - return FormResponse::respond(); + function BatchActionList() { + return $this->batchactions()->batchActionList(); } /** @@ -1435,6 +1280,7 @@ $filterCache = array(); // TODO: Find way to put this in a class function cmsMainMarkingFilterFunction($node) { + global $filterCache; // Expand all nodes // $node->markingFinished(); @@ -1458,6 +1304,11 @@ function cmsMainMarkingFilterFunction($node) { $failed_filter = true; } } + // Check the ClassName + if (!empty($_REQUEST['ClassName']) && $_REQUEST['ClassName'] != 'Any') { + if ($node->ClassName != $_REQUEST['ClassName']) $failed_filter = true; + } + // Now check if a specified Criteria attribute matches foreach (CMSMain::T_SiteTreeFilterOptions() as $key => $value) { diff --git a/code/LeftAndMain.php b/code/LeftAndMain.php index 07c8ca8e..c2acea8b 100644 --- a/code/LeftAndMain.php +++ b/code/LeftAndMain.php @@ -153,6 +153,9 @@ class LeftAndMain extends Controller { // Audit logging hook if(empty($_REQUEST['executeForm']) && !Director::is_ajax()) $this->extend('accessedCMS'); + + // Override HtmlEditorField's config with our own + HtmlEditorconfig::set_active('cms'); Requirements::css(CMS_DIR . '/css/typography.css'); Requirements::css(CMS_DIR . '/css/layout.css'); @@ -465,10 +468,18 @@ class LeftAndMain extends Controller { * @param $childrenMethod The method to call to get the children of the tree. For example, * Children, AllChildrenIncludingDeleted, or AllHistoricalChildren */ - function getSiteTreeFor($className, $rootID = null, - $childrenMethod = "AllChildrenIncludingDeleted") { + function getSiteTreeFor($className, $rootID = null, $childrenMethod = null, $filterFunction = null) { + // Default childrenMethod + if (!$childrenMethod) $childrenMethod = 'AllChildrenIncludingDeleted'; + + // Get the tree root $obj = $rootID ? $this->getRecord($rootID) : singleton($className); + + // Mark the nodes of the tree to return + if ($filterFunction) $obj->setMarkingFilterFunction($filterFunction); $obj->markPartialTree(30, $this, $childrenMethod); + + // Ensure current page is exposed if($p = $this->currentPage()) $obj->markToExpose($p); // getChildrenAsUL is a flexible and complex way of traversing the tree diff --git a/css/cms_left.css b/css/cms_left.css index 6d90ea38..73155920 100644 --- a/css/cms_left.css +++ b/css/cms_left.css @@ -374,14 +374,15 @@ ul.tree span.untranslated a:visited { } #left form.actionparams div.SearchCriteria { - width: 40%; + width: 28%; overflow: hidden; float: left; } -#left form.actionparams input.SearchCriteria { - width: 43%; +#left form.actionparams input.SearchCriteria, #left form.actionparams #InputSiteTreeFilterClassName select { + width: 60%; float: left; + margin: 0; } #left form.actionparams #InputSiteTreeFilterDate .calendar { margin-left: -96px; diff --git a/css/layout.css b/css/layout.css index 0b2bd9b4..f677ce23 100644 --- a/css/layout.css +++ b/css/layout.css @@ -357,17 +357,25 @@ body.stillLoading select { padding-left: 5px; position: relative; } - #SearchBox #SiteTreeSearchTerm { - padding: 1px 0 2px 0; + #SearchControls { + float: left; + position: relative; + margin-top:2px; + } + #SearchControls label { + display: none; + } + #SearchControls select#SiteTreeFilterAddCriteria { + width: 8.8em; + padding:1px 0; margin:0; } - #searchIndicator { display: none; width: 16px; height: 16px; background: #EFEFEF url(../images/network-save.gif) no-repeat; position: absolute; - left: 145px; + left: 95px; top: 2px; } #searchIndicator.loading { @@ -379,13 +387,6 @@ body.stillLoading select { padding-left: 5px; margin-bottom: 4px; } - #addCriteria { - float: left; - width: 100%; - } - #addCriteria label { - display: none; - } #sitetree_ul, ul#sitetree { padding: 3px 0 0 3px; clear: left; diff --git a/javascript/CMSMain_left.js b/javascript/CMSMain_left.js index c47b2d4f..cc588e80 100755 --- a/javascript/CMSMain_left.js +++ b/javascript/CMSMain_left.js @@ -129,9 +129,9 @@ ShowDeletedPagesAction.prototype = { onclick : function() { if(this.checked) { - SiteTreeHandlers.loadTree_url = SiteTreeHandlers.controller_url + '/getshowdeletedsubtree'; + $('sitetree').setCustomURL(SiteTreeHandlers.controller_url+'/getshowdeletedsubtree'); } else { - SiteTreeHandlers.loadTree_url = SiteTreeHandlers.controller_url + '/getsubtree'; + $('sitetree').clearCustomURL(); } // We can't update the tree while it's draggable; it gets b0rked. @@ -144,13 +144,8 @@ ShowDeletedPagesAction.prototype = { var indicator = $('checkboxActionIndicator'); indicator.style.display = 'block'; - var url = SiteTreeHandlers.loadTree_url + '?ID=0&ajax=1'; - if($('LangSelector')) url += "&locale=" + $('LangSelector').value; - - var request = new Ajax.Request(url, { - onSuccess: function(response) { - $('sitetree').innerHTML = response.responseText; - SiteTree.applyTo($('sitetree')); + $('sitetree').reload({ + onSuccess: function() { if(__makeDraggableAfterUpdate) $('sitetree').makeDraggable(); indicator.style.display = 'none'; }, @@ -161,6 +156,107 @@ ShowDeletedPagesAction.prototype = { } } +/** + * Show only drafts checkbox click action + */ +showonlydrafts = Class.create(); +showonlydrafts.applyTo('#publishpage_show_drafts'); +showonlydrafts.prototype = { + onclick : function() { + if(this.checked) { + $('sitetree').setCustomURL(SiteTreeHandlers.controller_url+'/getfilteredsubtree', {Status:'Saved'}); + } else { + $('sitetree').clearCustomURL(); + } + + $('sitetree').reload({ + onSuccess: function() { + statusMessage(ss.i18n._t('CMSMAIN.FILTEREDTREE'),'good'); + }, + onFailure: function(response) { + errorMessage(ss.i18n.sprintf( + ss.i18n._t('CMSMAIN.ERRORFILTERPAGES'), + response.responseText + )); + } + }); + } +} + +/** + * Control the site tree filter + */ +SiteTreeFilterForm = Class.create(); +SiteTreeFilterForm.applyTo('form#search_options'); +SiteTreeFilterForm.prototype = { + initialize: function() { + var self = this; + Form.getElements(this).each(function(el){ + if (el.type == 'submit') el.onclick = function(){self.clicked = $F(this); console.log(self.clicked)}; + }); + }, + onsubmit: function() { + var filters = $H(); + + if (this.clicked == 'Search') { + Form.getElements(this).each(function(el){ + if (el.type == 'text') { + if ($F(el)) filters[el.name] = $F(el); + } + else if (el.type == 'select-one') { + if ($F(el) && $F(el) != 'All') filters[el.name] = $F(el); + } + }); + } + else { + Form.getElements(this).each(function(el){ + if (el.type == 'text') $(el).clear(); + else if (el.type == 'select-one') el.value = 'All'; + }); + document.getElementsBySelector('.SearchCriteriaContainer', this).each(function(el){ + Element.hide(el); + }) + } + + if (filters.keys().length) { + // Set new URL + $('sitetree').setCustomURL(SiteTreeHandlers.controller_url + '/getfilteredsubtree', filters); + + // Disable checkbox tree controls that currently don't work with search. + // @todo: Make them work together + if ($('sitetree').isDraggable) $('sitetree').stopBeingDraggable(); + document.getElementsBySelector('.checkboxAboveTree input[type=checkbox]').each(function(el){ + el.value = false; el.disabled = true; + }) + } + else { + // Reset URL to default + $('sitetree').clearCustomURL(); + + // Enable checkbox tree controls + document.getElementsBySelector('.checkboxAboveTree input[type=checkbox]').each(function(el){ + el.disabled = false; + }) + } + + $('SiteTreeSearchButton').className = $('SiteTreeSearchClearButton').className = 'hidden'; + $('searchIndicator').className = 'loading'; + + $('sitetree').reload({ + onSuccess : function(response) { + $('SiteTreeSearchButton').className = $('SiteTreeSearchClearButton').className = 'action'; + $('searchIndicator').className = ''; + statusMessage('Filtered tree','good'); + }, + onFailure : function(response) { + errorMessage('Could not filter site tree
    ' + response.responseText); + } + }); + + return false; + } +} + /** * Add Criteria Drop-down onchange action which allows more criteria to be shown */ @@ -210,37 +306,6 @@ batchactionsclass.prototype = { } } -/** - * Show only drafts checkbox click action - */ -showonlydrafts = Class.create(); -showonlydrafts.applyTo('#publishpage_show_drafts'); -showonlydrafts.prototype = { - onclick : function() { - if (0 == $('SiteTreeIsFiltered').value) { - // Show all items in Site Tree again - new Ajax.Request( 'admin/filterSiteTree?Status=Saved&ajax=1', { - onSuccess: function( response ) { - $('sitetree_ul').innerHTML = response.responseText; - Behaviour.apply($('sitetree_ul')); - $('SiteTreeIsFiltered').value = 1; - $('batchactions').multiselectTransform(); - statusMessage(ss.i18n._t('CMSMAIN.FILTEREDTREE'),'good'); - }, - onFailure : function(response) { - errorMessage(ss.i18n.sprintf( - ss.i18n._t('CMSMAIN.ERRORFILTERPAGES'), - response.responseText - )); - } - }); - } else { - batchActionGlobals.unfilterSiteTree(); - } - } -} - - // batchActionGlobals is needed because calls to observeMethod doesn't seem to preserve instance variables so a Prototype can't be used batchActionGlobals = { selectedNodes: { }, @@ -337,25 +402,39 @@ batchActionGlobals = { * Publish selected pages action */ publishpage = Class.create(); -publishpage.applyTo('#Form_PublishItemsForm'); +publishpage.applyTo('#batchactions_options'); publishpage.prototype = { onsubmit : function() { csvIDs = batchActionGlobals.getCsvIds(); if(csvIDs) { + var optionEl = $('choose_batch_action').options[$('choose_batch_action').selectedIndex]; + var actionText = optionEl.text; + var optionParams = eval(optionEl.className); + var ingText = optionParams.doingText; + + // Confirmation + if(!confirm("You have " + batchActionGlobals.count + " pages selected.\n\nDo your really want to " + actionText.toLowerCase() + "?")) { + return false; + } + this.elements.csvIDs.value = csvIDs; + + // Select form submission URL + this.action = $('choose_batch_action').value; - statusMessage(ss.i18n._t('CMSMAIN.PUBLISHINGPAGES')); + // Loading indicator + statusMessage(ingText); + $('batchactions_go').className = 'loading'; - // Put an AJAXY loading icon on the button - $('Form_PublishItemsForm_action_publishitems').className = 'loading'; + // Submit form Ajax.SubmitForm(this, null, { onSuccess : function(response) { Ajax.Evaluator(response); - $('Form_PublishItemsForm_action_publishitems').className = ''; + $('batchactions_go').className = ''; treeactions.closeSelection($('batchactions')); }, onFailure : function(response) { - errorMessage(ss.i18n._t('CMSMAIN.ERRORPUBLISHING'), response); + errorMessage('Error ' + ingText, response); } }); } else { @@ -450,4 +529,4 @@ TreeContextMenu = { treeNode.onOrderChanged(sortedChildren,treeNode); } -}; \ No newline at end of file +}; diff --git a/javascript/LeftAndMain.js b/javascript/LeftAndMain.js index 407ffb10..57d90a09 100644 --- a/javascript/LeftAndMain.js +++ b/javascript/LeftAndMain.js @@ -886,4 +886,28 @@ function hideIndicator(id) { setInterval(function() { new Ajax.Request("Security/ping"); -}, 5*60*1000); \ No newline at end of file +}, 5*60*1000); + +/** + * Find and enable TinyMCE on all htmleditor fields + * Pulled in from old tinymce.template.js + */ + +function nullConverter(url) { + return url; +} + +Behaviour.register({ + 'textarea.htmleditor' : { + initialize : function() { + tinyMCE.execCommand("mceAddControl", true, this.id); + this.isChanged = function() { + return tinyMCE.getInstanceById(this.id).isDirty(); + } + this.resetChanged = function() { + inst = tinyMCE.getInstanceById(this.id); + inst.startContent = tinymce.trim(inst.getContent({format : 'raw', no_events : 1})); + } + } + } +}) diff --git a/javascript/LeftAndMain_left.js b/javascript/LeftAndMain_left.js index e999f5ad..ff2c9ca8 100755 --- a/javascript/LeftAndMain_left.js +++ b/javascript/LeftAndMain_left.js @@ -27,6 +27,48 @@ var TreeContextMenu = null; */ TreeAPI = Class.create(); TreeAPI.prototype = { + + setCustomURL: function(url, arguments) { + this.customURL = url; + this.customArguments = $H(arguments); + }, + + clearCustomURL: function() { + this.customURL = this.customArguments = null; + }, + + url: function(args) { + var args = $H(args).merge(this.customArguments); + + var url = this.customURL ? this.customURL : SiteTreeHandlers.loadTree_url; + url = url + (url.match(/\?/) ? '&' : '?') + args.toQueryString(); + + console.log('Loading tree from ' + url); + return url; + }, + + reload: function(options) { + this.innerHTML = 'Loading...'; + + var args = {ajax:1, ID:0}; + if ($('Form_EditForm_Locale')) args.local = $('Form_EditForm_Locale').value; + + url = this.url(args); + + var self = this; + new Ajax.Request(url, { + onSuccess: function(response){ + self.innerHTML = response.responseText; + self.castAsTreeNode(self.firstChild); + if (options.onSuccess) options.onSuccess(response); + }, + onFailure: function(response){ + errorMessage('error loading tree', response); + if (options.onError) options.onError(response); + } + }); + }, + /** * Perform the given code on the each tree node with the given index. * There could be more than one :-) @@ -177,11 +219,11 @@ TreeNodeAPI.prototype = { this.cuedNewNodes[this.cuedNewNodes.length] = node; } - - var url = SiteTreeHandlers.loadTree_url; - url += (url.match(/\?/)) ? '&' : '?'; - url += 'ajax=1&ID=' + this.getIdx(); - if($('Form_EditForm_Locale')) url += "&locale=" + $('Form_EditForm_Locale').value; + var args = {ajax:1, ID:this.getIdx()}; + if ($('Form_EditForm_Locale')) args.local = $('Form_EditForm_Locale').value; + + url = this.tree.url(args); + new Ajax.Request(url, { onSuccess : this.installSubtree.bind(this), onFailure : this.showSubtreeLoadingError @@ -481,30 +523,3 @@ ReorganiseAction.prototype = { } } } - -/** - * Control the site tree filter - */ -SiteTreeFilterForm = Class.create(); -SiteTreeFilterForm.applyTo('form#search_options'); -SiteTreeFilterForm.prototype = { - onsubmit: function() { - $('SiteTreeSearchButton').className = 'hidden'; - $('searchIndicator').className = 'loading'; - Ajax.SubmitForm(this, null, { - onSuccess : function(response) { - $('SiteTreeIsFiltered').value = 1; - $('SiteTreeSearchButton').className = ''; - $('searchIndicator').className = ''; - $('sitetree_ul').innerHTML = response.responseText; - Behaviour.apply($('sitetree_ul')); - statusMessage('Filtered tree','good'); - }, - onFailure : function(response) { - errorMessage('Could not filter site tree
    ' + response.responseText); - } - }); - - return false; - } -} diff --git a/javascript/WidgetAreaEditor.js b/javascript/WidgetAreaEditor.js index 9d6a2fa7..31fdfcf7 100644 --- a/javascript/WidgetAreaEditor.js +++ b/javascript/WidgetAreaEditor.js @@ -1,7 +1,7 @@ -WidgetAreaEditor = Class.create(); -WidgetAreaEditor.applyTo('div.WidgetAreaEditor'); +WidgetAreaEditorClass = Class.create(); +WidgetAreaEditorClass.applyTo('div.WidgetAreaEditor'); -WidgetAreaEditor.prototype = { +WidgetAreaEditorClass.prototype = { initialize: function() { UsedWidget.applyToChildren($('WidgetAreaEditor_usedWidgets'), 'div.Widget'); diff --git a/javascript/tinymce.template.js b/javascript/tinymce.template.js deleted file mode 100755 index ffef4da9..00000000 --- a/javascript/tinymce.template.js +++ /dev/null @@ -1,63 +0,0 @@ -function nullConverter(url) { - return url; -} - -/** - * TinyMCE initialisation template. - * $ variables are replaced by string search & replace. It's pretty crude. - */ -// Prevents "Error: 'tinyMCE' is undefined" error in IE7 on Newsletter Recipient import. -if((typeof tinyMCE != 'undefined')) { - tinyMCE.init({ - mode : "none", - language: "$Lang", - width: "100%", - auto_resize : false, - theme : "advanced", - content_css : "cms/css/editor.css, $ContentCSS", - body_class : 'typography', - document_base_url: "$BaseURL", - urlconverter_callback : "nullConverter", - - setupcontent_callback : "sapphiremce_setupcontent", - cleanup_callback : "sapphiremce_cleanup", - - theme_advanced_layout_manager: "SimpleLayout", - theme_advanced_toolbar_location : "top", - theme_advanced_toolbar_align : "left", - theme_advanced_toolbar_parent : "right", - plugins : "media,contextmenu,table,emotions,paste,../../tinymce_ssbuttons,../../tinymce_advcode,spellchecker,fullscreen", - blockquote_clear_tag : "p", - table_inline_editing : true, - theme_advanced_buttons1 : "bold,italic,underline,strikethrough,separator,justifyleft,justifycenter,justifyright,justifyfull,styleselect,formatselect,separator,bullist,numlist,outdent,indent,blockquote,hr,charmap", - theme_advanced_buttons2 : "undo,redo,separator,cut,copy,paste,pastetext,pasteword,spellchecker,separator,ssimage,ssflash,sslink,unlink,anchor,separator,advcode,fullscreen,separator,search,replace,selectall,visualaid", - theme_advanced_buttons3 : "tablecontrols", - spellchecker_languages : "$SpellcheckLangs", - - template_templates : [ - { title : "Three column", src : "assets/snippet.html", description : "A simple 3 column layout"}, - ], - - safari_warning : false, - relative_urls : true, - verify_html : true, - use_native_selects : true, // fancy selects are bug as of SS 2.3.0 - valid_elements : "+a[id|rel|rev|dir|tabindex|accesskey|type|name|href|target|title|class],-strong/-b[class],-em/-i[class],-strike[class],-u[class],#p[id|dir|class|align],-ol[class],-ul[class],-li[class],br,img[id|dir|longdesc|usemap|class|src|border|alt=|title|width|height|align],-sub[class],-sup[class],-blockquote[dir|class],-table[border=0|cellspacing|cellpadding|width|height|class|align|summary|dir|id|style],-tr[id|dir|class|rowspan|width|height|align|valign|bgcolor|background|bordercolor|style],tbody[id|class|style],thead[id|class|style],tfoot[id|class|style],-td[id|dir|class|colspan|rowspan|width|height|align|valign|scope|style],-th[id|dir|class|colspan|rowspan|width|height|align|valign|scope|style],caption[id|dir|class],-div[id|dir|class|align|style],-span[class|align],-pre[class|align],address[class|align],-h1[id|dir|class|align],-h2[id|dir|class|align],-h3[id|dir|class|align],-h4[id|dir|class|align],-h5[id|dir|class|align],-h6[id|dir|class|align],hr[class],dd[id|class|title|dir],dl[id|class|title|dir],dt[id|class|title|dir]", - extended_valid_elements : "img[class|src|alt|title|hspace|vspace|width|height|align|onmouseover|onmouseout|name|usemap],iframe[src|name|width|height|align|frameborder|marginwidth|marginheight|scrolling],object[width|height|data|type],param[name|value],map[class|name|id],area[shape|coords|href|target|alt]" - }); -} - -Behaviour.register({ - 'textarea.htmleditor' : { - initialize : function() { - tinyMCE.execCommand("mceAddControl", true, this.id); - this.isChanged = function() { - return tinyMCE.getInstanceById(this.id).isDirty(); - } - this.resetChanged = function() { - inst = tinyMCE.getInstanceById(this.id); - inst.startContent = tinymce.trim(inst.getContent({format : 'raw', no_events : 1})); - } - } - } -}) diff --git a/lang/en_US.php b/lang/en_US.php index 93499386..9bfb1ece 100755 --- a/lang/en_US.php +++ b/lang/en_US.php @@ -150,7 +150,7 @@ $lang['en_US']['CMSMain']['WORKTODO'] = 'You have work to do on these %d $lang['en_US']['CMSMain_dialog.ss']['BUTTONNOTFOUND'] = 'Couldn\'t find the button name'; $lang['en_US']['CMSMain_dialog.ss']['NOLINKED'] = 'Can\'t find window.linkedObject to send the button click back to the main window'; $lang['en_US']['CMSMain_left.ss']['ADDEDNOTPUB'] = 'Added to the draft site and not published yet'; -$lang['en_US']['CMSMain_left.ss']['ADDSEARCHCRITERIA'] = 'Add Criteria...'; +$lang['en_US']['CMSMain_left.ss']['ADDSEARCHCRITERIA'] = 'Add Criteria'; $lang['en_US']['CMSMain_left.ss']['BATCHACTIONS'] = array( 'Batch Actions', PR_HIGH @@ -177,6 +177,7 @@ $lang['en_US']['CMSMain_left.ss']['NEW'] = 'new'; $lang['en_US']['CMSMain_left.ss']['OPENBOX'] = 'click to open this box'; $lang['en_US']['CMSMain_left.ss']['PAGEVERSIONH'] = 'Page Version History'; $lang['en_US']['CMSMain_left.ss']['PUBLISHCONFIRM'] = 'Publish the selected pages'; +$lang['en_US']['CMSMain_left.ss']['CLEAR'] = 'Clear'; $lang['en_US']['CMSMain_left.ss']['SEARCH'] = array( 'Search', PR_HIGH diff --git a/templates/Includes/CMSMain_left.ss b/templates/Includes/CMSMain_left.ss index d78659c4..7a4d7c0a 100755 --- a/templates/Includes/CMSMain_left.ss +++ b/templates/Includes/CMSMain_left.ss @@ -29,15 +29,19 @@ + + <% control SiteTreeFilterOptions %> <% end_control %> -
    +
    +
     
    + +
    - -
    - + + +
    + +
    + +
    - - <% if IsTranslatableEnabled %> -
    - Language: $LangSelector +
    + +
    - <% end_if %> +
    + + +
    +
    + + <% if IsTranslatableEnabled %> +
    + Language: $LangSelector +
    + <% end_if %> +
    $SiteTreeAsUL diff --git a/tests/CMSMainTest.php b/tests/CMSMainTest.php index 918e09fe..e8ed96ae 100644 --- a/tests/CMSMainTest.php +++ b/tests/CMSMainTest.php @@ -21,8 +21,7 @@ class CMSMainTest extends FunctionalTest { $response->getBody() ); - $response = $this->post('admin/cms/publishitems', array('csvIDs' => '1,2', 'ajax' => 1)); - + $response = Director::test("admin/cms/batchactions/publish", array('csvIDs' => '1,2', 'ajax' => 1), $session); $this->assertContains('setNodeTitle(1, \'Page 1\');', $response->getBody()); $this->assertContains('setNodeTitle(2, \'Page 2\');', $response->getBody());