Moved CMS module to open source path
git-svn-id: svn://svn.silverstripe.com/silverstripe/open/modules/cms/trunk@39000 467b73ca-7a2a-4603-9d3b-597d59a354a9
19
_config.php
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
Director::addRules(50, array(
|
||||||
|
'processes/$Action/$ID/$Batch' => 'BatchProcess_Controller',
|
||||||
|
'silverstripe/' => '->admin/',
|
||||||
|
'cms/' => '->admin/',
|
||||||
|
'admin/security/$Action/$ID/$OtherID' => 'SecurityAdmin',
|
||||||
|
'admin/help/$Action/$ID' => 'CMSHelp',
|
||||||
|
'admin/newsletter/$Action/$ID' => 'NewsletterAdmin',
|
||||||
|
'admin/reports/$Action/$ID' => 'ReportAdmin',
|
||||||
|
'admin/assets/$Action/$ID' => 'AssetAdmin',
|
||||||
|
'admin/ReportField/$Action/$ID/$Type/$OtherID' => 'ReportField_Controller',
|
||||||
|
'admin/bulkload/$Action/$ID/$OtherID' => 'BulkLoaderAdmin',
|
||||||
|
'admin/$Action/$ID/$OtherID' => 'CMSMain',
|
||||||
|
'unsubscribe/$Email/$MailingList' => 'Unsubscribe_Controller',
|
||||||
|
'membercontrolpanel/$Email' => 'MemberControlPanel'
|
||||||
|
));
|
||||||
|
|
||||||
|
?>
|
660
code/AssetAdmin.php
Executable file
@ -0,0 +1,660 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AssetAdmin is the 'file store' section of the CMS.
|
||||||
|
* It provides an interface for maniupating the File and Folder objects in the system.
|
||||||
|
*/
|
||||||
|
class AssetAdmin extends LeftAndMain {
|
||||||
|
static $tree_class = "File";
|
||||||
|
|
||||||
|
public function Link($action=null) {
|
||||||
|
if(!$action) $action = "index";
|
||||||
|
return "admin/assets/$action/" . $this->currentPageID();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return fake-ID "root" if no ID is found (needed to upload files into the root-folder)
|
||||||
|
*/
|
||||||
|
public function currentPageID() {
|
||||||
|
if(isset($_REQUEST['ID']) && is_numeric($_REQUEST['ID'])) {
|
||||||
|
return $_REQUEST['ID'];
|
||||||
|
} elseif (is_numeric($this->urlParams['ID'])) {
|
||||||
|
return $this->urlParams['ID'];
|
||||||
|
} elseif(is_numeric(Session::get("{$this->class}.currentPage"))) {
|
||||||
|
return Session::get("{$this->class}.currentPage");
|
||||||
|
} else {
|
||||||
|
return "root";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set up the controller, in particular, re-sync the File database with the assets folder./
|
||||||
|
*/
|
||||||
|
function init() {
|
||||||
|
parent::init();
|
||||||
|
|
||||||
|
// needed for MemberTableField (Requirements not determined before Ajax-Call)
|
||||||
|
Requirements::javascript("sapphire/javascript/ComplexTableField.js");
|
||||||
|
Requirements::css("jsparty/greybox/greybox.css");
|
||||||
|
Requirements::css("sapphire/css/ComplexTableField.css");
|
||||||
|
|
||||||
|
Requirements::javascript("cms/javascript/AssetAdmin.js");
|
||||||
|
Requirements::javascript("cms/javascript/AssetAdmin_left.js");
|
||||||
|
Requirements::javascript("cms/javascript/AssetAdmin_right.js");
|
||||||
|
|
||||||
|
// Requirements::javascript('sapphire/javascript/TableListField.js');
|
||||||
|
|
||||||
|
// Include the right JS]
|
||||||
|
// Hayden: This didn't appear to be used at all
|
||||||
|
/*$fileList = new FileList("Form_EditForm_Files", null);
|
||||||
|
$fileList->setClick_AjaxLoad('admin/assets/getfile/', 'Form_SubForm');
|
||||||
|
$fileList->FieldHolder();*/
|
||||||
|
|
||||||
|
Requirements::javascript("jsparty/greybox/AmiJS.js");
|
||||||
|
Requirements::javascript("jsparty/greybox/greybox.js");
|
||||||
|
Requirements::css("jsparty/greybox/greybox.css");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display the upload form. Returns an iframe tag that will show admin/assets/uploadiframe.
|
||||||
|
*/
|
||||||
|
function getUploadIframe() {
|
||||||
|
return <<<HTML
|
||||||
|
<iframe name="AssetAdmin_upload" src="admin/assets/uploadiframe/{$this->urlParams['ID']}" id="AssetAdmin_upload" border="0" style="border-style: none; width: 100%; height: 200px">
|
||||||
|
</iframe>
|
||||||
|
HTML;
|
||||||
|
}
|
||||||
|
|
||||||
|
function index() {
|
||||||
|
File::sync();
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show the content of the upload iframe. The form is specified by a template.
|
||||||
|
*/
|
||||||
|
function uploadiframe() {
|
||||||
|
Requirements::clear();
|
||||||
|
|
||||||
|
Requirements::javascript("jsparty/prototype.js");
|
||||||
|
Requirements::javascript("jsparty/loader.js");
|
||||||
|
Requirements::javascript("jsparty/behaviour.js");
|
||||||
|
Requirements::javascript("jsparty/prototype_improvements.js");
|
||||||
|
Requirements::javascript("jsparty/layout_helpers.js");
|
||||||
|
Requirements::javascript("cms/javascript/LeftAndMain.js");
|
||||||
|
Requirements::javascript("jsparty/multifile/multifile.js");
|
||||||
|
Requirements::css("jsparty/multifile/multifile.css");
|
||||||
|
Requirements::css("cms/css/typography.css");
|
||||||
|
Requirements::css("cms/css/layout.css");
|
||||||
|
Requirements::css("cms/css/cms_left.css");
|
||||||
|
Requirements::css("cms/css/cms_right.css");
|
||||||
|
|
||||||
|
if(isset($data['ID']) && $data['ID'] != 'root') $folder = DataObject::get_by_id("Folder", $data['ID']);
|
||||||
|
else $folder = singleton('Folder');
|
||||||
|
|
||||||
|
$canUpload = $folder->userCanEdit();
|
||||||
|
|
||||||
|
return array( 'CanUpload' => $canUpload );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the form object shown in the uploadiframe.
|
||||||
|
*/
|
||||||
|
function UploadForm() {
|
||||||
|
|
||||||
|
return new Form($this,'UploadForm', new FieldSet(
|
||||||
|
new HiddenField("ID", "", $this->currentPageID()),
|
||||||
|
// needed because the button-action is triggered outside the iframe
|
||||||
|
new HiddenField("action_doUpload", "", "1"),
|
||||||
|
new FileField("Files[0]" , "Choose file "),
|
||||||
|
new LiteralField('UploadButton',"
|
||||||
|
<input type='submit' value='Upload Files Listed Below' name='action_upload' id='Form_UploadForm_action_upload' class='action' />
|
||||||
|
"),
|
||||||
|
new LiteralField('MultifileCode',"
|
||||||
|
<p>Files ready to upload:</p>
|
||||||
|
<div id='Form_UploadForm_FilesList'></div>
|
||||||
|
<script>
|
||||||
|
var multi_selector = new MultiSelector($('Form_UploadForm_FilesList'), null, $('Form_UploadForm_action_upload'));
|
||||||
|
multi_selector.addElement($('Form_UploadForm_Files-0'));
|
||||||
|
</script>
|
||||||
|
")
|
||||||
|
), new FieldSet(
|
||||||
|
));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method processes the results of the UploadForm.
|
||||||
|
* It will save the uploaded files to /assets/ and create new File objects as required.
|
||||||
|
*/
|
||||||
|
function doUpload($data, $form) {
|
||||||
|
foreach($data['Files'] as $param => $files) {
|
||||||
|
foreach($files as $key => $value) {
|
||||||
|
$processedFiles[$key][$param] = $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if($data['ID'] && $data['ID'] != 'root') $folder = DataObject::get_by_id("Folder", $data['ID']);
|
||||||
|
else $folder = singleton('Folder');
|
||||||
|
|
||||||
|
$warnFiles = array();
|
||||||
|
$fileSizeWarnings = '';
|
||||||
|
|
||||||
|
foreach($processedFiles as $file) {
|
||||||
|
if($file['tmp_name']) {
|
||||||
|
// check that the file can be uploaded and isn't too large
|
||||||
|
|
||||||
|
$extensionIndex = strripos( $file['name'], '.' );
|
||||||
|
$extension = strtolower( substr( $file['name'], $extensionIndex + 1 ) );
|
||||||
|
|
||||||
|
if( $extensionIndex !== FALSE )
|
||||||
|
list( $maxSize, $warnSize ) = File::getMaxFileSize( $extension );
|
||||||
|
else
|
||||||
|
list( $maxSize, $warnSize ) = File::getMaxFileSize();
|
||||||
|
|
||||||
|
// check that the file is not too large or that the current user is an administrator
|
||||||
|
if( $this->can('AdminCMS') || ( File::allowedFileType( $extension ) && (!isset($maxsize) || $file['size'] < $maxSize)))
|
||||||
|
$newFiles[] = $folder->addUploadToFolder($file);
|
||||||
|
elseif( !File::allowedFileType( $extension ) ) {
|
||||||
|
$fileSizeWarnings .= "alert( 'Only administrators can upload $extension files.' );";
|
||||||
|
} else {
|
||||||
|
if( $file['size'] > 1048576 )
|
||||||
|
$fileSize = "" . ceil( $file['size'] / 1048576 ) . "MB";
|
||||||
|
elseif( $file['size'] > 1024 )
|
||||||
|
$fileSize = "" . ceil( $file['size'] / 1024 ) . "KB";
|
||||||
|
else
|
||||||
|
$fileSize = "" . ceil( $file['size'] ) . "B";
|
||||||
|
|
||||||
|
|
||||||
|
$fileSizeWarnings .= "alert( '\\'" . $file['name'] . "\\' is too large ($fileSize). Files of this type cannot be larger than $warnSize ' );";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if($newFiles) {
|
||||||
|
$numFiles = sizeof($newFiles);
|
||||||
|
$statusMessage = "Uploaded $numFiles files";
|
||||||
|
$status = "good";
|
||||||
|
} else {
|
||||||
|
$statusMessage = "There was nothing to upload";
|
||||||
|
$status = "";
|
||||||
|
}
|
||||||
|
echo <<<HTML
|
||||||
|
<script type="text/javascript">
|
||||||
|
var form = parent.document.getElementById('Form_EditForm');
|
||||||
|
form.getPageFromServer(form.elements.ID.value);
|
||||||
|
parent.statusMessage("{$statusMessage}","{$status}");
|
||||||
|
$fileSizeWarnings
|
||||||
|
parent.document.getElementById('sitetree').getTreeNodeByIdx( "{$folder->ID}" ).getElementsByTagName('a')[0].className += ' contents';
|
||||||
|
</script>
|
||||||
|
HTML;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Needs to be overridden to make sure an ID with value "0" is still valid (rootfolder)
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the form that displays the details of a folder, including a file list and fields for editing the folder name.
|
||||||
|
*/
|
||||||
|
function getEditForm($id) {
|
||||||
|
if($id && $id != "root") {
|
||||||
|
$record = DataObject::get_by_id("File", $id);
|
||||||
|
} else {
|
||||||
|
$record = singleton("Folder");
|
||||||
|
}
|
||||||
|
|
||||||
|
$fileList = new AssetTableField(
|
||||||
|
$this,
|
||||||
|
"Files",
|
||||||
|
"File",
|
||||||
|
array("Title" => "Title", "LinkedURL" => "Filename"),
|
||||||
|
""
|
||||||
|
);
|
||||||
|
$fileList->setFolder($record);
|
||||||
|
$fileList->setPopupCaption("View/Edit Asset");
|
||||||
|
|
||||||
|
if($record) {
|
||||||
|
$nameField = ($id != "root") ? new TextField("Name") : new HiddenField("Name");
|
||||||
|
$fields = new FieldSet(
|
||||||
|
new HiddenField("Title"),
|
||||||
|
$nameField,
|
||||||
|
new TabSet("Root",
|
||||||
|
new Tab("Files",
|
||||||
|
$fileList
|
||||||
|
),
|
||||||
|
new Tab("Details",
|
||||||
|
new ReadonlyField("URL"),
|
||||||
|
new ReadonlyField("ClassName", "Type"),
|
||||||
|
new ReadonlyField("Created", "First Uploaded"),
|
||||||
|
new ReadonlyField("LastEdited", "Last Updated")
|
||||||
|
),
|
||||||
|
new Tab("Upload",
|
||||||
|
new LiteralField("UploadIframe",
|
||||||
|
$this->getUploadIframe()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
new HiddenField("ID")
|
||||||
|
);
|
||||||
|
|
||||||
|
$actions = new FieldSet();
|
||||||
|
|
||||||
|
if( $record->userCanEdit() ) {
|
||||||
|
$actions = new FieldSet(
|
||||||
|
new FormAction('deletemarked',"Delete files"),
|
||||||
|
new FormAction('movemarked',"Move files..."),
|
||||||
|
new FormAction('save',"Save")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$form = new Form($this, "EditForm", $fields, $actions);
|
||||||
|
if($record->ID) {
|
||||||
|
$form->loadDataFrom($record);
|
||||||
|
} else {
|
||||||
|
$form->loadDataFrom(array(
|
||||||
|
"ID" => "root",
|
||||||
|
"URL" => Director::absoluteBaseURL() . 'assets/',
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// @todo: These workflow features aren't really appropriate for all projects
|
||||||
|
if( Member::currentUser()->_isAdmin() && project() == 'mot' ) {
|
||||||
|
$fields->addFieldsToTab( 'Root.Workflow', new DropdownField("Owner", "Owner", Member::map() ) );
|
||||||
|
$fields->addFieldsToTab( 'Root.Workflow', new TreeMultiselectField("CanUse", "Content usable by") );
|
||||||
|
$fields->addFieldsToTab( 'Root.Workflow', new TreeMultiselectField("CanEdit", "Content modifiable by") );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( !$record->userCanEdit() )
|
||||||
|
$form->makeReadonly();
|
||||||
|
|
||||||
|
return $form;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the form used to specify options for the "move marked" action.
|
||||||
|
*/
|
||||||
|
public function MoveMarkedOptionsForm() {
|
||||||
|
$folderDropdown = new TreeDropdownField("DestFolderID", "Move files to", "Folder");
|
||||||
|
$folderDropdown->setFilterFunction(create_function('$obj', 'return $obj->class == "Folder";'));
|
||||||
|
|
||||||
|
return new CMSActionOptionsForm($this, "MoveMarkedOptionsForm", new FieldSet(
|
||||||
|
new HiddenField("ID"),
|
||||||
|
new HiddenField("FileIDs"),
|
||||||
|
$folderDropdown
|
||||||
|
),
|
||||||
|
new FieldSet(
|
||||||
|
new FormAction("movemarked", "Move marked files")
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Perform the "move marked" action.
|
||||||
|
* Called by ajax, with a JavaScript return.
|
||||||
|
*/
|
||||||
|
public function movemarked() {
|
||||||
|
if($_REQUEST['DestFolderID'] && is_numeric($_REQUEST['DestFolderID'])) {
|
||||||
|
$destFolderID = $_REQUEST['DestFolderID'];
|
||||||
|
$fileList = "'" . ereg_replace(' *, *',"','",trim(addslashes($_REQUEST['FileIDs']))) . "'";
|
||||||
|
$numFiles = 0;
|
||||||
|
|
||||||
|
if($fileList != "''") {
|
||||||
|
$files = DataObject::get("File", "`File`.ID IN ($fileList)");
|
||||||
|
if($files) {
|
||||||
|
foreach($files as $file) {
|
||||||
|
$file->ParentID = $destFolderID;
|
||||||
|
$file->write();
|
||||||
|
$numFiles++;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
user_error("No files in $fileList could be found!", E_USER_ERROR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
echo <<<JS
|
||||||
|
statusMessage("Moved $numFiles files");
|
||||||
|
JS;
|
||||||
|
} else {
|
||||||
|
user_error("Bad data: $_REQUEST[DestFolderID]", E_USER_ERROR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the form used to specify options for the "delete marked" action.
|
||||||
|
* In actual fact, this form only has hidden fields and the button is auto-clickd without the
|
||||||
|
* form being displayed; it's just the most consistent way of providing this information to the
|
||||||
|
* CMS.
|
||||||
|
*/
|
||||||
|
public function DeleteMarkedOptionsForm() {
|
||||||
|
return new CMSActionOptionsForm($this, "DeleteMarkedOptionsForm", new FieldSet(
|
||||||
|
new HiddenField("ID"),
|
||||||
|
new HiddenField("FileIDs")
|
||||||
|
),
|
||||||
|
new FieldSet(
|
||||||
|
new FormAction("deletemarked", "Delete marked files")
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Perform the "delete marked" action.
|
||||||
|
* Called by ajax, with a JavaScript return.
|
||||||
|
*/
|
||||||
|
public function deletemarked() {
|
||||||
|
$fileList = "'" . ereg_replace(' *, *',"','",trim(addslashes($_REQUEST['FileIDs']))) . "'";
|
||||||
|
$numFiles = 0;
|
||||||
|
$folderID = 0;
|
||||||
|
$deleteList = '';
|
||||||
|
$brokenPageList = '';
|
||||||
|
|
||||||
|
if($fileList != "''") {
|
||||||
|
$files = DataObject::get("File", "`File`.ID IN ($fileList)");
|
||||||
|
if($files) {
|
||||||
|
foreach($files as $file) {
|
||||||
|
if( !$folderID )
|
||||||
|
$folderID = $file->ParentID;
|
||||||
|
|
||||||
|
// $deleteList .= "\$('Form_EditForm_Files').removeById($file->ID);\n";
|
||||||
|
$file->delete();
|
||||||
|
$numFiles++;
|
||||||
|
}
|
||||||
|
if($brokenPages = Notifications::getItems("BrokenLink")) {
|
||||||
|
$brokenPageList = " These pages now have broken links:</ul>";
|
||||||
|
foreach($brokenPages as $brokenPage) {
|
||||||
|
$brokenPageList .= "<li style="font-size: 65%">" . $brokenPage->Breadcrumbs(3, true) . "</li>";
|
||||||
|
}
|
||||||
|
$brokenPageList .= "</ul>";
|
||||||
|
Notifications::notifyByEmail("BrokenLink", "Page_BrokenLinkEmail");
|
||||||
|
}
|
||||||
|
|
||||||
|
if( $folderID ) {
|
||||||
|
$remaining = DB::query("SELECT COUNT(*) FROM `File` WHERE `ParentID`=$folderID")->value();
|
||||||
|
|
||||||
|
if( !$remaining )
|
||||||
|
$deleteList = "Element.removeClassName(\$('sitetree').getTreeNodeByIdx( '$folderID' ).getElementsByTagName('a')[0],'contents');";
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
user_error("No files in $fileList could be found!", E_USER_ERROR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
echo <<<JS
|
||||||
|
$deleteList
|
||||||
|
$('Form_EditForm').getPageFromServer($('Form_EditForm_ID').value);
|
||||||
|
statusMessage("Deleted $numFiles files.$brokenPageList");
|
||||||
|
JS;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the content to be placed in Form_SubForm when editing a file.
|
||||||
|
* Called using ajax.
|
||||||
|
*/
|
||||||
|
public function getfile() {
|
||||||
|
SSViewer::setOption('rewriteHashlinks', false);
|
||||||
|
|
||||||
|
// bdc: only try to return something if user clicked on an object
|
||||||
|
if (is_object($this->getSubForm($this->urlParams['ID']))) {
|
||||||
|
return $this->getSubForm($this->urlParams['ID'])->formHtmlContent();
|
||||||
|
}
|
||||||
|
else return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Action handler for the save button on the file subform.
|
||||||
|
* Saves the file
|
||||||
|
*/
|
||||||
|
public function savefile($data, $form) {
|
||||||
|
$record = DataObject::get_by_id("File", $data['ID']);
|
||||||
|
$form->saveInto($record);
|
||||||
|
$record->write();
|
||||||
|
$title = Convert::raw2js($record->Title);
|
||||||
|
$name = Convert::raw2js($record->Name);
|
||||||
|
echo <<<JS
|
||||||
|
statusMessage('Saved file #$data[ID]');
|
||||||
|
$('record-$data[ID]').getElementsByTagName('td')[1].innerHTML = "$title";
|
||||||
|
$('record-$data[ID]').getElementsByTagName('td')[2].innerHTML = "$name";
|
||||||
|
JS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the entire site tree as a nested set of ULs
|
||||||
|
|
||||||
|
*/
|
||||||
|
public function SiteTreeAsUL() {
|
||||||
|
$obj = singleton('Folder');
|
||||||
|
$obj->setMarkingFilter("ClassName", "Folder");
|
||||||
|
$obj->markPartialTree();
|
||||||
|
|
||||||
|
if($p = $this->currentPage()) $obj->markToExpose($p);
|
||||||
|
|
||||||
|
// getChildrenAsUL is a flexible and complex way of traversing the tree
|
||||||
|
|
||||||
|
$siteTree = $obj->getChildrenAsUL("",
|
||||||
|
|
||||||
|
' "<li id=\"record-$child->ID\" class=\"$child->class" . $child->markingClasses() . ($extraArg->isCurrentPage($child) ? " current" : "") . "\">" . ' .
|
||||||
|
|
||||||
|
' "<a href=\"" . Director::link(substr($extraArg->Link(),0,-1), "show", $child->ID) . "\" class=\"" . ($child->hasChildren() ? " contents" : "") . "\" >" . $child->Title . "</a>" ',
|
||||||
|
|
||||||
|
$this, true);
|
||||||
|
|
||||||
|
|
||||||
|
// Wrap the root if needs be.
|
||||||
|
|
||||||
|
$rootLink = $this->Link() . 'show/root';
|
||||||
|
|
||||||
|
if(!isset($rootID)) $siteTree = "<ul id=\"sitetree\" class=\"tree unformatted\"><li id=\"record-root\" class=\"Root\"><a href=\"$rootLink\">http://www.yoursite.com/assets</a>"
|
||||||
|
|
||||||
|
. $siteTree . "</li></ul>";
|
||||||
|
|
||||||
|
|
||||||
|
return $siteTree;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a subtree of items underneat the given folder.
|
||||||
|
*/
|
||||||
|
public function getsubtree() {
|
||||||
|
$obj = DataObject::get_by_id("Folder", $_REQUEST['ID']);
|
||||||
|
$obj->setMarkingFilter("ClassName", "Folder");
|
||||||
|
$obj->markPartialTree();
|
||||||
|
|
||||||
|
$results = $obj->getChildrenAsUL("",
|
||||||
|
|
||||||
|
' "<li id=\"record-$child->ID\" class=\"$child->class" . $child->markingClasses() . ($extraArg->isCurrentPage($child) ? " current" : "") . "\">" . ' .
|
||||||
|
|
||||||
|
' "<a href=\"" . Director::link(substr($extraArg->Link(),0,-1), "show", $child->ID) . "\" >" . $child->Title . "</a>" ',
|
||||||
|
|
||||||
|
$this, true);
|
||||||
|
|
||||||
|
return substr(trim($results), 4,-5);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------------------//
|
||||||
|
|
||||||
|
// Data saving handlers
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a new folder and return its details suitable for ajax.
|
||||||
|
*/
|
||||||
|
public function addfolder() {
|
||||||
|
$parent = ($_REQUEST['ParentID'] && is_numeric($_REQUEST['ParentID'])) ? $_REQUEST['ParentID'] : 0;
|
||||||
|
|
||||||
|
if($parent) {
|
||||||
|
$parentObj = DataObject::get_by_id("File", $parent);
|
||||||
|
if(!$parentObj || !$parentObj->ID) $parent = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
$p = new Folder();
|
||||||
|
$p->ParentID = $parent;
|
||||||
|
$p->Title = "NewFolder";
|
||||||
|
|
||||||
|
$p->Name = "NewFolder";
|
||||||
|
|
||||||
|
// Get the folder to be created
|
||||||
|
if(isset($parentObj->ID)) $filename = $parentObj->FullPath . $p->Name;
|
||||||
|
else $filename = '../assets/' . $p->Name;
|
||||||
|
|
||||||
|
// Ensure uniqueness
|
||||||
|
$i = 2;
|
||||||
|
$baseFilename = $filename . '-';
|
||||||
|
while(file_exists($filename)) {
|
||||||
|
$filename = $baseFilename . $i;
|
||||||
|
$p->Name = $p->Title = basename($filename);
|
||||||
|
$i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Actually create
|
||||||
|
mkdir($filename);
|
||||||
|
chmod($filename, 02775);
|
||||||
|
|
||||||
|
$p->write();
|
||||||
|
|
||||||
|
|
||||||
|
return $this->returnItemToUser($p);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the given tree item to the client.
|
||||||
|
* If called by ajax, this will be some javascript commands.
|
||||||
|
* Otherwise, it will redirect back.
|
||||||
|
*/
|
||||||
|
public function returnItemToUser($p) {
|
||||||
|
if($_REQUEST['ajax']) {
|
||||||
|
$parentID = (int)$p->ParentID;
|
||||||
|
return <<<JS
|
||||||
|
tree = $('sitetree');
|
||||||
|
|
||||||
|
var newNode = tree.createTreeNode($p->ID, "$p->Title", "$p->class");
|
||||||
|
|
||||||
|
tree.getTreeNodeByIdx($parentID).appendTreeNode(newNode);
|
||||||
|
|
||||||
|
newNode.selectTreeNode();
|
||||||
|
JS;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
Director::redirectBack();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete a folder
|
||||||
|
*/
|
||||||
|
public function deletefolder() {
|
||||||
|
$script = '';
|
||||||
|
$ids = split(' *, *', $_REQUEST['csvIDs']);
|
||||||
|
|
||||||
|
foreach($ids as $id) {
|
||||||
|
|
||||||
|
if(is_numeric($id)) {
|
||||||
|
|
||||||
|
$record = DataObject::get_by_id($this->stat('tree_class'), $id);
|
||||||
|
|
||||||
|
if(!$record)
|
||||||
|
|
||||||
|
Debug::message( "Record appears to be null" );
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*if($record->hasMethod('BackLinkTracking')) {
|
||||||
|
$brokenPages = $record->BackLinkTracking();
|
||||||
|
|
||||||
|
foreach($brokenPages as $brokenPage) {
|
||||||
|
|
||||||
|
$brokenPageList .= "<li style=\"font-size: 65%\">" . $brokenPage->Breadcrumbs(3, true) . "</li>";
|
||||||
|
|
||||||
|
$brokenPage->HasBrokenLink = true;
|
||||||
|
|
||||||
|
$notifications[$brokenPage->OwnerID][] = $brokenPage;
|
||||||
|
|
||||||
|
$brokenPage->write();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}*/
|
||||||
|
|
||||||
|
$record->delete();
|
||||||
|
$record->destroy();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// DataObject::delete_by_id($this->stat('tree_class'), $id);
|
||||||
|
|
||||||
|
$script .= $this->deleteTreeNodeJS($record);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*if($notifications) foreach($notifications as $memberID => $pages) {
|
||||||
|
|
||||||
|
$email = new Page_BrokenLinkEmail();
|
||||||
|
|
||||||
|
$email->populateTemplate(new ArrayData(array(
|
||||||
|
|
||||||
|
"Recipient" => DataObject::get_by_id("Member", $memberID),
|
||||||
|
|
||||||
|
"BrokenPages" => new DataObjectSet($pages),
|
||||||
|
|
||||||
|
)));
|
||||||
|
|
||||||
|
$email->debug();
|
||||||
|
|
||||||
|
$email->send();
|
||||||
|
|
||||||
|
}*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
$s = (sizeof($ids) > 1) ? "s" :"";
|
||||||
|
|
||||||
|
$message = sizeof($ids) . " folder$s deleted.";
|
||||||
|
//
|
||||||
|
if(isset($brokenPageList)) $message .= " The following pages now have broken links:<ul>" . addslashes($brokenPageList) . "</ul>Their owners have been emailed and they will fix up those pages.";
|
||||||
|
$script .= "statusMessage('$message');";
|
||||||
|
echo $script;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function removefile(){
|
||||||
|
if($fileID = $this->urlParams['ID']){
|
||||||
|
$file = DataObject::get_by_id('File', $fileID);
|
||||||
|
$file->delete();
|
||||||
|
$file->destroy();
|
||||||
|
|
||||||
|
if(Director::is_ajax()) {
|
||||||
|
echo <<<JS
|
||||||
|
$('Form_EditForm_Files').removeFile($fileID);
|
||||||
|
statusMessage('removed file', 'good');
|
||||||
|
JS;
|
||||||
|
}else{
|
||||||
|
Director::redirectBack();
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
user_error("AssetAdmin::removefile: Bad parameters: File=$fileID", E_USER_ERROR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function save($urlParams, $form) {
|
||||||
|
// Don't save the root folder - there's no database record
|
||||||
|
if($_REQUEST['ID'] == 'root') {
|
||||||
|
FormResponse::status_message("Saved", "good");
|
||||||
|
return FormResponse::respond();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
$form->dataFieldByName('Title')->value = $form->dataFieldByName('Name')->value;
|
||||||
|
|
||||||
|
return parent::save($urlParams, $form);
|
||||||
|
}
|
||||||
|
}
|
130
code/AssetTableField.php
Executable file
@ -0,0 +1,130 @@
|
|||||||
|
<?php
|
||||||
|
class AssetTableField extends ComplexTableField {
|
||||||
|
|
||||||
|
protected $folder;
|
||||||
|
|
||||||
|
protected $template = "AssetTableField";
|
||||||
|
|
||||||
|
function __construct($controller, $name, $sourceClass, $fieldList, $detailFormFields, $sourceFilter = "", $sourceSort = "", $sourceJoin = "") {
|
||||||
|
|
||||||
|
parent::__construct($controller, $name, $sourceClass, $fieldList, $detailFormFields, $sourceFilter, $sourceSort, $sourceJoin);
|
||||||
|
|
||||||
|
$this->sourceSort = "Title";
|
||||||
|
$this->Markable = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setFolder($folder) {
|
||||||
|
$this->folder = $folder;
|
||||||
|
$this->sourceFilter .= ($this->sourceFilter) ? " AND " : "";
|
||||||
|
$this->sourceFilter .= " ParentID = '" . $folder->ID . "' AND ClassName <> 'Folder'";
|
||||||
|
}
|
||||||
|
|
||||||
|
function Folder() {
|
||||||
|
return $this->folder;
|
||||||
|
}
|
||||||
|
|
||||||
|
function sourceID() {
|
||||||
|
return $this->folder->ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
function DetailForm() {
|
||||||
|
$ID = (isset($_REQUEST['ctf']['ID'])) ? Convert::raw2xml($_REQUEST['ctf']['ID']) : null;
|
||||||
|
$childID = (isset($_REQUEST['ctf']['childID'])) ? Convert::raw2xml($_REQUEST['ctf']['childID']) : null;
|
||||||
|
$childClass = (isset($_REQUEST['fieldName'])) ? Convert::raw2xml($_REQUEST['fieldName']) : null;
|
||||||
|
$methodName = (isset($_REQUEST['methodName'])) ? $_REQUEST['methodName'] : null;
|
||||||
|
|
||||||
|
if(!$childID) {
|
||||||
|
user_error("AssetTableField::DetailForm Please specify a valid ID");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if($childID) {
|
||||||
|
$childData = DataObject::get_by_id("File", $childID);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!$childData) {
|
||||||
|
user_error("AssetTableField::DetailForm No record found");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if($childData->ParentID) {
|
||||||
|
$folder = DataObject::get_by_id('File', $childData->ParentID );
|
||||||
|
} else {
|
||||||
|
$folder = singleton('Folder');
|
||||||
|
}
|
||||||
|
|
||||||
|
$urlLink = "<div class='field readonly'>";
|
||||||
|
$urlLink .= "<label class='left'>URL</label>";
|
||||||
|
$urlLink .= "<span class='readonly'><a href='{$childData->Link()}'>{$childData->RelativeLink()}</a></span>";
|
||||||
|
$urlLink .= "</div>";
|
||||||
|
|
||||||
|
$detailFormFields = new FieldSet(
|
||||||
|
new TabSet("BottomRoot",
|
||||||
|
new Tab("Main",
|
||||||
|
new TextField("Title"),
|
||||||
|
new TextField("Name", "Filename"),
|
||||||
|
new LiteralField("AbsoluteURL", $urlLink),
|
||||||
|
new ReadonlyField("FileType", "Type"),
|
||||||
|
new ReadonlyField("Size", "Size", $childData->getSize()),
|
||||||
|
new DropdownField("OwnerID", "Owner", Member::mapInCMSGroups( $folder->CanEdit() ) ),
|
||||||
|
new DateField_Disabled("Created", "First uploaded"),
|
||||||
|
new DateField_Disabled("LastEdited", "Last changed")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
if(is_a($childData,'Image')) {
|
||||||
|
$big = $childData->URL;
|
||||||
|
$thumbnail = $childData->getFormattedImage('AssetLibraryPreview')->URL;
|
||||||
|
|
||||||
|
$detailFormFields->addFieldToTab("BottomRoot.Main",
|
||||||
|
new ReadonlyField("Dimensions"),
|
||||||
|
"Created"
|
||||||
|
);
|
||||||
|
|
||||||
|
$detailFormFields->addFieldToTab("BottomRoot",
|
||||||
|
new Tab("Image",
|
||||||
|
new LiteralField("ImageFull",
|
||||||
|
"<img src='{$thumbnail}' alt='{$childData->Name}' />"
|
||||||
|
)
|
||||||
|
),
|
||||||
|
'Main'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if($childData && $childData->hasMethod('BackLinkTracking')) {
|
||||||
|
$links = $childData->BackLinkTracking();
|
||||||
|
if($links->exists()) {
|
||||||
|
foreach($links as $link) {
|
||||||
|
$backlinks[] = "<li><a href=\"admin/show/$link->ID\">" . $link->Breadcrumbs(null,true). "</a></li>";
|
||||||
|
}
|
||||||
|
$backlinks = "<div style=\"clear:left\">The following pages link to this file:<ul>" . implode("",$backlinks) . "</ul>";
|
||||||
|
}
|
||||||
|
if(!isset($backlinks)) $backlinks = "<p>This file hasn't been linked to from any pages.</p>";
|
||||||
|
$detailFormFields->addFieldToTab("BottomRoot.Links", new LiteralField("Backlinks", $backlinks));
|
||||||
|
}
|
||||||
|
|
||||||
|
// the ID field confuses the Controller-logic in finding the right view for ReferencedField
|
||||||
|
$detailFormFields->removeByName('ID');
|
||||||
|
// add a namespaced ID instead thats "converted" by saveComplexTableField()
|
||||||
|
$detailFormFields->push(new HiddenField("ctf[childID]","",$childID));
|
||||||
|
$detailFormFields->push(new HiddenField("ctf[ClassName]","",$this->sourceClass));
|
||||||
|
|
||||||
|
$readonly = ($this->methodName == "show");
|
||||||
|
$form = new ComplexTableField_Popup($this, "DetailForm", $detailFormFields, $this->sourceClass, $readonly);
|
||||||
|
|
||||||
|
if (is_numeric($childID)) {
|
||||||
|
if ($methodName == "show" || $methodName == "edit") {
|
||||||
|
$form->loadDataFrom($childData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if( !$folder->userCanEdit() || $methodName == "show") {
|
||||||
|
$form->makeReadonly();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $form;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
?>
|
67
code/BulkLoader.php
Executable file
@ -0,0 +1,67 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
abstract class BulkLoader extends ViewableData {
|
||||||
|
/**
|
||||||
|
* Override this on subclasses to give the specific functions names
|
||||||
|
*/
|
||||||
|
static $title = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a human-readable name for this object.
|
||||||
|
* It defaults to the class name can be overridden by setting the static variable $title
|
||||||
|
*/
|
||||||
|
function Title() {
|
||||||
|
if($title = $this->stat('title')) return $title;
|
||||||
|
else return $this->class;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process every record in the file
|
||||||
|
* @param filename The name of the CSV file to process
|
||||||
|
* @param preview If true, we'll just output a summary of changes but not actually do anything
|
||||||
|
*
|
||||||
|
* @returns A DataObjectSet containing a list of all the reuslst
|
||||||
|
*/
|
||||||
|
function processAll($filename, $preview = false) {
|
||||||
|
// TODO
|
||||||
|
// Get the first record out of the CSV and store it as headers
|
||||||
|
// Get each record out of the CSV
|
||||||
|
// Remap the record so that it's keyed by headers
|
||||||
|
// Pass it to $this->processRecord, and get the results
|
||||||
|
// Put the results inside an ArrayData and push that onto a DataObjectSet for returning
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*----------------------------------------------------------------------------------------
|
||||||
|
* Next, we have some abstract functions that let subclasses say what kind of batch operation they're
|
||||||
|
* going to do
|
||||||
|
*----------------------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a FieldSet containing all the options for this form; this
|
||||||
|
* doesn't include the actual upload field itself
|
||||||
|
*/
|
||||||
|
abstract function getOptionFields();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process a single record from the CSV file.
|
||||||
|
* @param record An map of the CSV data, keyed by the header field
|
||||||
|
* @param preview
|
||||||
|
*
|
||||||
|
* @returns A 2 value array.
|
||||||
|
* - The first element should be "add", "edit" or "", depending on the operation performed in response to this record
|
||||||
|
* - The second element is a free-text string that can optionally provide some more information about what changes have
|
||||||
|
* been made
|
||||||
|
*/
|
||||||
|
abstract function processRecord($record, $preview = false);
|
||||||
|
|
||||||
|
/*----------------------------------------------------------------------------------------
|
||||||
|
* Next, we have a library of helper functions (Brian to build as necessary)
|
||||||
|
*----------------------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
123
code/BulkLoaderAdmin.php
Executable file
@ -0,0 +1,123 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class to provide batch-update facilities to CMS users.
|
||||||
|
* The BulkLoaderAdmin class provides an interface for accessing all of the subclasses of BulkLoader,
|
||||||
|
* each of which defines a particular bulk loading operation.
|
||||||
|
*
|
||||||
|
* This code was originally developed for Per Week in collaboration with Brian Calhoun.
|
||||||
|
*/
|
||||||
|
class BulkLoaderAdmin extends LeftAndMain {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialisation method called before accessing any functionality that BulkLoaderAdmin has to offer
|
||||||
|
*/
|
||||||
|
public function init() {
|
||||||
|
Requirements::javascript('cms/javascript/BulkLoaderAdmin.js');
|
||||||
|
|
||||||
|
parent::init();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Link function to tell us how to get back to this controller.
|
||||||
|
*/
|
||||||
|
public function Link($action = null) {
|
||||||
|
return "admin/bulkload/$action";
|
||||||
|
}
|
||||||
|
|
||||||
|
public function BulkLoaders() {
|
||||||
|
$items = ClassInfo::subclassesFor("BulkLoader");
|
||||||
|
array_shift($items);
|
||||||
|
|
||||||
|
foreach($items as $item) {
|
||||||
|
$itemObjects[] = new $item();
|
||||||
|
}
|
||||||
|
|
||||||
|
return new DataObjectSet($itemObjects);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the form shown when we first click on a loader on the left.
|
||||||
|
* Provides all the options, a file upload, and an option to proceed
|
||||||
|
*/
|
||||||
|
public function getEditForm($className = null) {
|
||||||
|
if(is_subclass_of($className, 'BulkLoader')) {
|
||||||
|
$loader = new $className();
|
||||||
|
|
||||||
|
$fields = $loader->getOptionFields();
|
||||||
|
if(!$fields) $fields = new FieldSet();
|
||||||
|
|
||||||
|
$fields->push(new FileField("File", "CSV File"));
|
||||||
|
$fields->push(new HiddenField('LoaderClass', '', $loader->class));
|
||||||
|
|
||||||
|
return new Form($this, "EditForm",
|
||||||
|
$fields,
|
||||||
|
new FieldSet(
|
||||||
|
new FormAction('preview', "Preview")
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function preview() {
|
||||||
|
$className = $_REQUEST['LoaderClass'];
|
||||||
|
if(is_subclass_of($className, 'BulkLoader')) {
|
||||||
|
$loader = new $className();
|
||||||
|
|
||||||
|
$results = $loader->processAll($_FILES['File']['tmp_name'], false);
|
||||||
|
|
||||||
|
return $this->customise(array(
|
||||||
|
"Message" => "Press continue to load this data in",
|
||||||
|
"Results" => $results,
|
||||||
|
"ConfirmForm" => $this->getConfirmFormFor($loader, $file),
|
||||||
|
))->renderWith("BulkLoaderAdmin_preview");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a confirmation form for the given file/class
|
||||||
|
* Will copy the file to a suitable temporary location
|
||||||
|
* @param loader A BulkLoader object
|
||||||
|
* @param file The name of the temp file
|
||||||
|
*/
|
||||||
|
public function getConfirmFormFor($loader, $file) {
|
||||||
|
$tmpFile = tempnam(TEMP_FOLDER,'batch-upload-');
|
||||||
|
copy($file,$tmpFile);
|
||||||
|
|
||||||
|
return new Form($this, "ConfirmForm", new FieldSet(
|
||||||
|
new HiddenField("File", "", $tmpFile),
|
||||||
|
new HiddenField("LoaderClass", "", $loader->class)
|
||||||
|
), new FieldSet(
|
||||||
|
new FormAction('process', 'Confirm bulk load')
|
||||||
|
));
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Stub to return the form back after pressing the button.
|
||||||
|
*/
|
||||||
|
public function ConfirmForm() {
|
||||||
|
$className = $_REQUEST['LoaderClass'];
|
||||||
|
return $this->getConfirmFormFor(new $className(), $_REQUEST['File']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process the data and display the final "finished" message
|
||||||
|
*/
|
||||||
|
public function process() {
|
||||||
|
$className = $_REQUEST['LoaderClass'];
|
||||||
|
if(is_subclass_of($className, 'BulkLoader')) {
|
||||||
|
$loader = new $className();
|
||||||
|
|
||||||
|
$results = $loader->processAll($_REQUEST['Filename'], true);
|
||||||
|
|
||||||
|
return $this->customise(array(
|
||||||
|
"Message" => "This data has been loaded in",
|
||||||
|
"Results" => $results,
|
||||||
|
"ConfirmForm" => " ",
|
||||||
|
))->renderWith("BulkLoaderAdmin_preview");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
17
code/CMSActionOptionsForm.php
Executable file
@ -0,0 +1,17 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A special kind of form used to make the action dialogs that appear just underneath the top-right
|
||||||
|
* buttons in the CMS
|
||||||
|
*/
|
||||||
|
class CMSActionOptionsForm extends Form {
|
||||||
|
function FormAttributes() {
|
||||||
|
return "class=\"actionparams\" style=\"display:none\" " . parent::FormAttributes();
|
||||||
|
}
|
||||||
|
function FormName() {
|
||||||
|
$action = $this->actions->First()->Name();
|
||||||
|
return "{$action}_options";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
8
code/CMSHelp.php
Executable file
@ -0,0 +1,8 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
class CMSHelp extends Controller {
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
1132
code/CMSMain.php
Normal file
@ -0,0 +1,1132 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The main "content" area of the CMS.
|
||||||
|
* This class creates a 2-frame layout - left-tree and right-form - to sit beneath the main
|
||||||
|
* admin menu.
|
||||||
|
* @todo Create some base classes to contain the generic functionality that will be replicated.
|
||||||
|
*/
|
||||||
|
class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionProvider {
|
||||||
|
static $tree_class = "SiteTree";
|
||||||
|
static $subitem_class = "Member";
|
||||||
|
|
||||||
|
public function init() {
|
||||||
|
parent::init();
|
||||||
|
|
||||||
|
Requirements::javascript(MCE_ROOT . "tiny_mce_src.js");
|
||||||
|
Requirements::javascript("jsparty/tiny_mce_improvements.js");
|
||||||
|
Requirements::javascript("jsparty/hover.js");
|
||||||
|
Requirements::javascript("jsparty/scriptaculous/controls.js");
|
||||||
|
|
||||||
|
Requirements::javascript("cms/javascript/MemberList.js");
|
||||||
|
Requirements::javascript("cms/javascript/CMSMain.js");
|
||||||
|
Requirements::javascript("cms/javascript/CMSMain_left.js");
|
||||||
|
Requirements::javascript("cms/javascript/CMSMain_right.js");
|
||||||
|
Requirements::javascript("cms/javascript/ForumAdmin.js");
|
||||||
|
|
||||||
|
|
||||||
|
Requirements::javascript("cms/javascript/SideTabs.js");
|
||||||
|
Requirements::javascript("cms/javascript/TaskList.js");
|
||||||
|
Requirements::javascript("cms/javascript/CommentList.js");
|
||||||
|
Requirements::javascript("cms/javascript/SideReports.js");
|
||||||
|
Requirements::javascript("sapphire/javascript/UpdateURL.js");
|
||||||
|
Requirements::javascript("sapphire/javascript/UniqueFields.js");
|
||||||
|
Requirements::javascript("sapphire/javascript/RedirectorPage.js");
|
||||||
|
Requirements::css("sapphire/css/FieldEditor.css");
|
||||||
|
|
||||||
|
Requirements::javascript("sapphire/javascript/FieldEditor.js");
|
||||||
|
Requirements::javascript("sapphire/javascript/TableListField.js");
|
||||||
|
Requirements::javascript("sapphire/javascript/TableField.js");
|
||||||
|
Requirements::javascript("sapphire/javascript/ComplexTableField.js");
|
||||||
|
Requirements::javascript("survey/javascript/SurveyResponses.js");
|
||||||
|
Requirements::javascript("survey/javascript/FormResponses.js");
|
||||||
|
Requirements::javascript( 'sapphire/javascript/DataReport.js' );
|
||||||
|
Requirements::css("survey/css/SurveyFilter.css");
|
||||||
|
|
||||||
|
Requirements::javascript("sapphire/javascript/SelectionGroup.js");
|
||||||
|
|
||||||
|
// For Parents
|
||||||
|
Requirements::javascript("parents/javascript/NotifyMembers.js");
|
||||||
|
|
||||||
|
// For Tourism
|
||||||
|
Requirements::css("tourism/css/SurveyCMSMain.css");
|
||||||
|
Requirements::javascript("tourism/javascript/QuotasReport.js");
|
||||||
|
|
||||||
|
// For Mikano
|
||||||
|
Requirements::javascript("sapphire/javascript/ReportField.js");
|
||||||
|
|
||||||
|
// For Ptraining
|
||||||
|
Requirements::javascript("ptraining/javascript/BookingList.js");
|
||||||
|
|
||||||
|
// For Forum
|
||||||
|
Requirements::javascript("forum/javascript/ForumAccess.js");
|
||||||
|
|
||||||
|
// For Gallery
|
||||||
|
Requirements::javascript('gallery/javascript/GalleryPage_CMS.js');
|
||||||
|
|
||||||
|
Requirements::css(project() . "/css/typography.css");
|
||||||
|
|
||||||
|
// HACK ALERT:
|
||||||
|
// We need a better way of including all of the CSS that *might* be used by this application.
|
||||||
|
// Perhaps the ajax responses can include some instructions to go get more CSS / JavaScript?
|
||||||
|
if(Director::fileExists('mot')) {
|
||||||
|
Requirements::css("mot/css/WorkflowWidget.css");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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::javascriptTemplate("cms/javascript/tinymce.template.js", array(
|
||||||
|
"ContentCSS" => project() . "/css/editor.css",
|
||||||
|
"BaseURL" => Director::absoluteBaseURL(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------------------//
|
||||||
|
// Main controllers
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------------------//
|
||||||
|
// Main UI components
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the entire site tree as a nested set of ULs
|
||||||
|
*/
|
||||||
|
public function SiteTreeAsUL() {
|
||||||
|
$this->generateDataTreeHints();
|
||||||
|
$this->generateTreeStylingJS();
|
||||||
|
|
||||||
|
return $this->getSiteTreeFor("SiteTree");
|
||||||
|
}
|
||||||
|
|
||||||
|
public function generateDataTreeHints() {
|
||||||
|
$classes = ClassInfo::subclassesFor( $this->stat('tree_class') );
|
||||||
|
|
||||||
|
$def['Root'] = array();
|
||||||
|
|
||||||
|
foreach($classes as $class) {
|
||||||
|
$obj = singleton($class);
|
||||||
|
if($obj instanceof HiddenClass) continue;
|
||||||
|
|
||||||
|
$allowedChildren = $obj->allowedChildren();
|
||||||
|
if($allowedChildren != "none") $def[$class]['allowedChildren'] = $allowedChildren;
|
||||||
|
$def[$class]['defaultChild'] = $obj->defaultChild();
|
||||||
|
$def[$class]['defaultParent'] = isset(DataObject::get_by_url($obj->defaultParent())->ID) ? DataObject::get_by_url($obj->defaultParent())->ID : null;
|
||||||
|
|
||||||
|
if(is_array($allowedChildren)) foreach($allowedChildren as $allowedChild) {
|
||||||
|
$def[$allowedChild]['allowedParents'][] = $class;
|
||||||
|
}
|
||||||
|
|
||||||
|
if($obj->stat('can_be_root')) {
|
||||||
|
$def['Root']['allowedChildren'][] = $class;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Put data hints into a script tag at the top
|
||||||
|
Requirements::customScript("siteTreeHints = " . $this->jsDeclaration($def) . ";");
|
||||||
|
}
|
||||||
|
|
||||||
|
public function generateTreeStylingJS() {
|
||||||
|
$classes = ClassInfo::subclassesFor('SiteTree');
|
||||||
|
foreach($classes as $class) {
|
||||||
|
$obj = singleton($class);
|
||||||
|
if($obj instanceof HiddenClass) continue;
|
||||||
|
if($icon = $obj->stat('icon')) $iconInfo[$class] = $icon;
|
||||||
|
}
|
||||||
|
$iconInfo['BrokenLink'] = 'cms/images/treeicons/brokenlink';
|
||||||
|
|
||||||
|
|
||||||
|
$js = "var _TREE_ICONS = [];\n";
|
||||||
|
|
||||||
|
|
||||||
|
foreach($iconInfo as $class => $icon) {
|
||||||
|
// SiteTree::$icon can be set to array($icon, $option)
|
||||||
|
// $option can be "file" or "folder" to force the icon to always be the file or the folder form
|
||||||
|
$option = null;
|
||||||
|
if(is_array($icon)) list($icon, $option) = $icon;
|
||||||
|
|
||||||
|
$fileImage = ($option == "folder") ? $icon . '-openfolder.gif' : $icon . '-file.gif';
|
||||||
|
$openFolderImage = $icon . '-openfolder.gif';
|
||||||
|
if(!Director::fileExists($openFolderImage) || $option = "file") $openFolderImage = $fileImage;
|
||||||
|
$closedFolderImage = $icon . '-closedfolder.gif';
|
||||||
|
if(!Director::fileExists($closedFolderImage) || $option = "file") $closedFolderImage = $fileImage;
|
||||||
|
|
||||||
|
$js .= <<<JS
|
||||||
|
_TREE_ICONS['$class'] = {
|
||||||
|
fileIcon: '$fileImage',
|
||||||
|
openFolderIcon: '$openFolderImage',
|
||||||
|
closedFolderIcon: '$closedFolderImage'
|
||||||
|
};
|
||||||
|
|
||||||
|
JS;
|
||||||
|
}
|
||||||
|
|
||||||
|
Requirements::customScript($js);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a javascript instanciation of this array
|
||||||
|
*/
|
||||||
|
protected function jsDeclaration($array) {
|
||||||
|
if(is_array($array)) {
|
||||||
|
$object = false;
|
||||||
|
foreach(array_keys($array) as $key) {
|
||||||
|
if(!is_numeric($key)) {
|
||||||
|
$object = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if($object) {
|
||||||
|
foreach($array as $k => $v) {
|
||||||
|
$parts[] = "$k : " . $this->jsDeclaration($v);
|
||||||
|
}
|
||||||
|
return " {\n " . implode(", \n", $parts) . " }\n";
|
||||||
|
} else {
|
||||||
|
foreach($array as $part) $parts[] = $this->jsDeclaration($part);
|
||||||
|
return " [ " . implode(", ", $parts) . " ]\n";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return "'" . addslashes($array) . "'";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Populates an array of classes in the CMS which allows the
|
||||||
|
* user to change the page type.
|
||||||
|
*/
|
||||||
|
public function PageTypes() {
|
||||||
|
$classes = ClassInfo::getValidSubClasses();
|
||||||
|
array_shift($classes);
|
||||||
|
$result = new DataObjectSet();
|
||||||
|
$kill_ancestors[] = null;
|
||||||
|
|
||||||
|
// figure out if there are any classes we don't want to appear
|
||||||
|
foreach($classes as $class) {
|
||||||
|
$instance = singleton($class);
|
||||||
|
|
||||||
|
// do any of the progeny want to hide an ancestor?
|
||||||
|
if ($ancestor_to_hide = $instance->stat('hide_ancestor')){
|
||||||
|
// note for killing later
|
||||||
|
$kill_ancestors[] = $ancestor_to_hide;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If any of the descendents don't want any of the elders to show up, cruelly render the elders surplus to requirements.
|
||||||
|
if ($kill_ancestors) {
|
||||||
|
foreach ($kill_ancestors as $mark) {
|
||||||
|
// unset from $classes
|
||||||
|
unset($classes[$mark]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach($classes as $class) {
|
||||||
|
$instance = singleton($class);
|
||||||
|
if($instance instanceof HiddenClass) continue;
|
||||||
|
|
||||||
|
if( !$instance->canCreate() ) continue;
|
||||||
|
|
||||||
|
// skip this type if it is restricted
|
||||||
|
if($instance->stat('need_permission') && !$this->can( singleton($class)->stat('need_permission') ) ) continue;
|
||||||
|
|
||||||
|
$addAction = $instance->uninherited('add_action', true);
|
||||||
|
if(!$addAction) $addAction = "a $class";
|
||||||
|
|
||||||
|
$result->push(new ArrayData(array(
|
||||||
|
"ClassName" => $class,
|
||||||
|
"AddAction" => "Create $addAction",
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a databsae record to be managed by the CMS
|
||||||
|
*/
|
||||||
|
public function getRecord($id) {
|
||||||
|
|
||||||
|
$treeClass = $this->stat('tree_class');
|
||||||
|
|
||||||
|
if($id && is_numeric($id)) {
|
||||||
|
$record = DataObject::get_one( $treeClass, "`$treeClass`.ID = $id");
|
||||||
|
|
||||||
|
if(!$record) {
|
||||||
|
// $record = Versioned::get_one_by_stage($treeClass, "Live", "`$treeClass`.ID = $id");
|
||||||
|
Versioned::reading_stage('Live');
|
||||||
|
singleton($treeClass)->flushCache();
|
||||||
|
$record = DataObject::get_one( $treeClass, "`$treeClass`.ID = $id");
|
||||||
|
if($record) {
|
||||||
|
$record->DeletedFromStage = true;
|
||||||
|
} else {
|
||||||
|
Versioned::reading_stage(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $record;
|
||||||
|
|
||||||
|
} else if(substr($id,0,3) == 'new') {
|
||||||
|
return $this->getNewItem($id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getEditForm($id) {
|
||||||
|
$record = $this->getRecord($id);
|
||||||
|
|
||||||
|
if($record) {
|
||||||
|
if($record->DeletedFromStage) $record->Status = "Removed from the draft site";
|
||||||
|
|
||||||
|
$fields = $record->getCMSFields($this);
|
||||||
|
$fields->push($idField = new HiddenField("ID"));
|
||||||
|
$fields->push($liveURLField = new HiddenField("LiveURLSegment"));
|
||||||
|
$fields->push($stageURLField = new HiddenField("StageURLSegment"));
|
||||||
|
|
||||||
|
/*if( substr($record->ID, 0, 3 ) == 'new' )*/
|
||||||
|
$fields->push(new HiddenField('Sort','', $record->Sort ));
|
||||||
|
|
||||||
|
$idField->setValue($id);
|
||||||
|
|
||||||
|
if($record->ID && is_numeric( $record->ID ) ) $liveURLField->setValue(DB::query("SELECT URLSegment FROM SiteTree_Live WHERE ID = $id")->value());
|
||||||
|
if(!$record->DeletedFromStage) $stageURLField->setValue($record->URLSegment);
|
||||||
|
|
||||||
|
// getAllCMSActions can be used to completely redefine the action list
|
||||||
|
if($record->hasMethod('getAllCMSActions')) {
|
||||||
|
$actions = $record->getAllCMSActions();
|
||||||
|
} else {
|
||||||
|
$actions = new FieldSet();
|
||||||
|
|
||||||
|
if($record->DeletedFromStage) {
|
||||||
|
if($record->can('CMSEdit')) {
|
||||||
|
$actions->push(new FormAction('revert','Restore'));
|
||||||
|
$actions->push(new FormAction('deletefromlive','Delete from the published site'));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if($record->hasMethod('getCMSActions')) {
|
||||||
|
$extraActions = $record->getCMSActions();
|
||||||
|
if($extraActions) foreach($extraActions as $action) $actions->push($action);
|
||||||
|
}
|
||||||
|
|
||||||
|
if($record->canEdit()) {
|
||||||
|
$actions->push(new FormAction('save','Save draft'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$form = new Form($this, "EditForm", $fields, $actions);
|
||||||
|
$form->loadDataFrom($record);
|
||||||
|
$form->disableDefaultAction();
|
||||||
|
|
||||||
|
if(!$record->canEdit() || $record->DeletedFromStage) $form->makeReadonly();
|
||||||
|
|
||||||
|
return $form;
|
||||||
|
} else if($id) {
|
||||||
|
return new Form($this, "EditForm", new FieldSet(
|
||||||
|
new LabelField("This page doesn't exist")), new FieldSet());
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------------------//
|
||||||
|
// Data saving handlers
|
||||||
|
|
||||||
|
|
||||||
|
public function addpage() {
|
||||||
|
$className = $_REQUEST['PageType'] ? $_REQUEST['PageType'] : "Page";
|
||||||
|
$parent = $_REQUEST['ParentID'] ? $_REQUEST['ParentID'] : 0;
|
||||||
|
$suffix = $_REQUEST['Suffix'] ? "-" . $_REQUEST['Suffix'] : null;
|
||||||
|
|
||||||
|
|
||||||
|
if(is_numeric($parent)) $parentObj = DataObject::get_by_id("SiteTree", $parent);
|
||||||
|
if(!$parentObj || !$parentObj->ID) $parent = 0;
|
||||||
|
|
||||||
|
$p = $this->getNewItem("new-$className-$parent".$suffix );
|
||||||
|
// $p->write();
|
||||||
|
|
||||||
|
$p->CheckedPublicationDifferences = $p->AddedToStage = true;
|
||||||
|
return $this->returnItemToUser($p);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getNewItem($id, $setID = true) {
|
||||||
|
list($dummy, $className, $parentID, $suffix) = explode('-',$id);
|
||||||
|
$newItem = new $className();
|
||||||
|
|
||||||
|
if( !$suffix ) {
|
||||||
|
$sessionTag = "NewItems." . $parentID . "." . $className;
|
||||||
|
if(Session::get($sessionTag)) {
|
||||||
|
$suffix = '-' . Session::get($sessionTag);
|
||||||
|
Session::set($sessionTag, Session::get($sessionTag) + 1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
Session::set($sessionTag, 1);
|
||||||
|
|
||||||
|
$id = $id . $suffix;
|
||||||
|
}
|
||||||
|
|
||||||
|
$newItem->Title = "New $className";
|
||||||
|
$newItem->URLSegment = "new-" . strtolower($className);
|
||||||
|
$newItem->ClassName = $className;
|
||||||
|
$newItem->ParentID = $parentID;
|
||||||
|
|
||||||
|
if($newItem->fieldExists('Sort')) {
|
||||||
|
$newItem->Sort = DB::query("SELECT MAX(Sort) FROM SiteTree WHERE ParentID = '" . Convert::raw2sql($parentID) . "'")->value() + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( Member::currentUser() )
|
||||||
|
$newItem->OwnerID = Member::currentUser()->ID;
|
||||||
|
|
||||||
|
if($setID) $newItem->ID = $id;
|
||||||
|
|
||||||
|
return $newItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function Link($action = null) {
|
||||||
|
return "admin/$action";
|
||||||
|
}
|
||||||
|
|
||||||
|
public function publish($urlParams, $form) {
|
||||||
|
$id = $_REQUEST['ID'];
|
||||||
|
$record = DataObject::get_by_id("SiteTree", $id);
|
||||||
|
$form->saveInto($record);
|
||||||
|
$this->performPublish($record);
|
||||||
|
|
||||||
|
return $this->tellBrowserAboutPublicationChange($record, "Published '$record->Title' successfully");
|
||||||
|
}
|
||||||
|
|
||||||
|
public function deletefromlive($urlParams, $form) {
|
||||||
|
$id = $_REQUEST['ID'];
|
||||||
|
Versioned::reading_stage('Live');
|
||||||
|
$record = DataObject::get_by_id("SiteTree", $id);
|
||||||
|
|
||||||
|
// before deleting the records, get the descendants of this tree
|
||||||
|
if($record) {
|
||||||
|
$descendantIDs = $record->getDescendantIDList('SiteTree');
|
||||||
|
|
||||||
|
// then delete them from the live site too
|
||||||
|
foreach( $descendantIDs as $descID )
|
||||||
|
if( $descendant = DataObject::get_by_id('SiteTree', $descID) ) {
|
||||||
|
$descendant->delete();
|
||||||
|
$descendantsRemoved++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// delete the record
|
||||||
|
$record->delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
Versioned::reading_stage('Stage');
|
||||||
|
|
||||||
|
if($descendantsRemoved) {
|
||||||
|
$descRemoved = " and $descendantsRemoved descendants";
|
||||||
|
}
|
||||||
|
|
||||||
|
$title = Convert::raw2js($record->Title);
|
||||||
|
FormResponse::add($this->deleteTreeNodeJS($record));
|
||||||
|
FormResponse::status_message("Deleted '$title'$descRemoved from live site", 'good');
|
||||||
|
|
||||||
|
return FormResponse::respond();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Actually perform the publication step
|
||||||
|
*/
|
||||||
|
public function performPublish($record) {
|
||||||
|
$record->AssignedToID = 0;
|
||||||
|
$record->RequestedByID = 0;
|
||||||
|
$record->Status = "Published";
|
||||||
|
//$record->PublishedByID = Member::currentUser()->ID;
|
||||||
|
$record->write();
|
||||||
|
$record->publish("Stage", "Live");
|
||||||
|
|
||||||
|
// Fix the sort order for this page's siblings
|
||||||
|
DB::query("UPDATE SiteTree_Live
|
||||||
|
INNER JOIN SiteTree ON SiteTree_Live.ID = SiteTree.ID
|
||||||
|
SET SiteTree_Live.Sort = SiteTree.Sort
|
||||||
|
WHERE SiteTree_Live.ParentID = " . sprintf('%d', $record->ParentID));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function revert($urlParams, $form) {
|
||||||
|
$id = $_REQUEST['ID'];
|
||||||
|
|
||||||
|
Versioned::reading_stage('Live');
|
||||||
|
$obj = DataObject::get_by_id("SiteTree", $id);
|
||||||
|
Versioned::reading_stage('Stage');
|
||||||
|
$obj->publish("Live", "Stage");
|
||||||
|
|
||||||
|
$title = Convert::raw2js($obj->Title);
|
||||||
|
FormResponse::get_page($id);
|
||||||
|
FormResponse::add("$('sitetree').setNodeTitle($id, '$title');");
|
||||||
|
FormResponse::status_message("Restored '$title' successfully",'good');
|
||||||
|
|
||||||
|
return FormResponse::respond();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function delete($urlParams, $form) {
|
||||||
|
$id = $_REQUEST['ID'];
|
||||||
|
$record = DataObject::get_one("SiteTree", "SiteTree.ID = $id");
|
||||||
|
$record->delete();
|
||||||
|
Director::redirectBack();
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------------------//
|
||||||
|
// Workflow handlers
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send this page on to another user for review
|
||||||
|
*/
|
||||||
|
function submit() {
|
||||||
|
|
||||||
|
$page = DataObject::get_by_id("SiteTree", $_REQUEST['ID']);
|
||||||
|
$recipient = DataObject::get_by_id("Member", $_REQUEST['RecipientID']);
|
||||||
|
if(!$recipient) user_error("CMSMain::submit() Can't find recipient #$_REQUEST[RecipientID]", E_USER_ERROR);
|
||||||
|
|
||||||
|
$comment = new WorkflowPageComment();
|
||||||
|
$comment->Comment = $_REQUEST['Message'];
|
||||||
|
$comment->PageID = $page->ID;
|
||||||
|
$comment->AuthorID = Member::currentUserID();
|
||||||
|
$comment->Recipient = $recipient;
|
||||||
|
$comment->Action = $_REQUEST['Status'];
|
||||||
|
$comment->write();
|
||||||
|
|
||||||
|
$emailData = $page->customise(array(
|
||||||
|
"Message" => $_REQUEST['Message'],
|
||||||
|
"Recipient" => $recipient,
|
||||||
|
"Sender" => Member::currentUser(),
|
||||||
|
"ApproveLink" => "admin/approve/$page->ID",
|
||||||
|
"EditLink" => "admin/show/$page->ID",
|
||||||
|
"StageLink" => "$page->URLSegment/?stage=Stage",
|
||||||
|
));
|
||||||
|
|
||||||
|
$email = new Page_WorkflowSubmitEmail();
|
||||||
|
$email->populateTemplate($emailData);
|
||||||
|
$email->send();
|
||||||
|
|
||||||
|
$page->AssignedToID = $recipient->ID;
|
||||||
|
$page->RequestedByID = Member::currentUserID();
|
||||||
|
$page->Status = $_REQUEST['Status'];
|
||||||
|
$page->writeWithoutVersion();
|
||||||
|
|
||||||
|
FormResponse::status_message("Sent to $recipient->FirstName $recipient->Surname for approval.","good");
|
||||||
|
|
||||||
|
return FormResponse::respond();
|
||||||
|
}
|
||||||
|
|
||||||
|
function getpagemembers() {
|
||||||
|
$relationName = $_REQUEST['SecurityLevel'];
|
||||||
|
$pageID = $this->urlParams['ID'];
|
||||||
|
$page = DataObject::get_by_id('SiteTree',$pageID);
|
||||||
|
if($page) {
|
||||||
|
foreach($page->$relationName() as $editorGroup) $groupIDs[] = $editorGroup->ID;
|
||||||
|
if($groupIDs) {
|
||||||
|
$groupList = implode(", ", $groupIDs);
|
||||||
|
$members = DataObject::get("Member","","",
|
||||||
|
"INNER JOIN `Group_Members` ON `Group_Members`.MemberID = `Member`.ID AND `Group_Members`.GroupID IN ($groupList)");
|
||||||
|
}
|
||||||
|
|
||||||
|
if($members) {
|
||||||
|
|
||||||
|
if( $page->RequestedByID )
|
||||||
|
$members->shift( $page->RequestedBy() );
|
||||||
|
|
||||||
|
foreach($members as $editor) {
|
||||||
|
$options .= "<option value=\"$editor->ID\">$editor->FirstName $editor->Surname ($editor->Email)</option>";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$options = "<option>(no-one available)</option>";
|
||||||
|
}
|
||||||
|
|
||||||
|
return <<<HTML
|
||||||
|
<label class="left">Send to</label>
|
||||||
|
<select name="RecipientID">$options</select>
|
||||||
|
HTML;
|
||||||
|
} else {
|
||||||
|
user_error("CMSMain::getpagemembers() Cannot find page #$pageID", E_USER_ERROR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getMembersByGroup() {
|
||||||
|
|
||||||
|
$group = DataObject::get_by_id("Group", $this->urlParams['ID']);
|
||||||
|
if($group){
|
||||||
|
$memberList = new MemberList('Users', $group);
|
||||||
|
$memberList->setController($this);
|
||||||
|
return $memberList->renderWith('MemberList');
|
||||||
|
}else{
|
||||||
|
return user_error("CMSMain::getpagemembers() Cannot find Group #$group->ID", E_USER_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function addmember() {
|
||||||
|
SecurityAdmin::addmember($this->stat('subitem_class'));
|
||||||
|
}
|
||||||
|
|
||||||
|
function tasklist() {
|
||||||
|
$tasks = DataObject::get("Page", "AssignedToID = " . Member::currentUserID(), "Created DESC");
|
||||||
|
if($tasks) {
|
||||||
|
$data = new ArrayData(array(
|
||||||
|
"Tasks" => $tasks,
|
||||||
|
"Message" => "You have work to do on these <b>" . $tasks->Count() . "</b> pages.",
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
$data = new ArrayData(array(
|
||||||
|
"Message" => "You have nothing assigned to you.",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
return $data->renderWith("TaskList");
|
||||||
|
}
|
||||||
|
|
||||||
|
function waitingon() {
|
||||||
|
$tasks = DataObject::get("Page", "RequestedByID = " . Member::currentUserID(), "Created DESC");
|
||||||
|
if($tasks) {
|
||||||
|
$data = new ArrayData(array(
|
||||||
|
"Tasks" => $tasks,
|
||||||
|
"Message" => "You are waiting on other people to work on these <b>" . $tasks->Count() . "</b> pages.",
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
$data = new ArrayData(array(
|
||||||
|
"Message" => "You aren't waiting on anybody.",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
return $data->renderWith("WaitingOn");
|
||||||
|
}
|
||||||
|
|
||||||
|
function comments() {
|
||||||
|
if($this->urlParams['ID']) {
|
||||||
|
$comments = DataObject::get("WorkflowPageComment", "PageID = " . $this->urlParams['ID'], "Created DESC");
|
||||||
|
$data = new ArrayData(array(
|
||||||
|
"Comments" => $comments,
|
||||||
|
));
|
||||||
|
return $data->renderWith("CommentList");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a dropdown for selecting reports
|
||||||
|
*/
|
||||||
|
function ReportSelector() {
|
||||||
|
$reports = ClassInfo::subclassesFor("SideReport");
|
||||||
|
|
||||||
|
$options[""] = "(Choose a report)";
|
||||||
|
foreach($reports as $report) {
|
||||||
|
if($report != 'SideReport') $options[$report] = singleton($report)->title();
|
||||||
|
}
|
||||||
|
return new DropdownField("ReportSelector","Report",$options);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Get the content for a side report
|
||||||
|
*/
|
||||||
|
function sidereport() {
|
||||||
|
$reportClass = $this->urlParams['ID'];
|
||||||
|
$report = new $reportClass();
|
||||||
|
return $report->getHTML();
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Get the versions of the current page
|
||||||
|
*/
|
||||||
|
function versions() {
|
||||||
|
$pageID = $this->urlParams['ID'];
|
||||||
|
$page = $this->getRecord($pageID);
|
||||||
|
if($page) {
|
||||||
|
$versions = $page->allVersions($_REQUEST['unpublished'] ? "" : "`SiteTree_versions`.WasPublished = 1");
|
||||||
|
return array(
|
||||||
|
'Versions' => $versions,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return "Can't find page #$pageID";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Roll a page back to a previous version
|
||||||
|
*/
|
||||||
|
function rollback() {
|
||||||
|
if($_REQUEST['Version']) {
|
||||||
|
$record = $this->performRollback($_REQUEST['ID'], $_REQUEST['Version']);
|
||||||
|
echo "Rolled back to version #$_REQUEST[Version]. New version number is #$record->Version";
|
||||||
|
} else {
|
||||||
|
$record = $this->performRollback($_REQUEST['ID'], "Live");
|
||||||
|
echo "Rolled back to published version. New version number is #$record->Version";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function unpublish() {
|
||||||
|
$SQL_id = Convert::raw2sql($_REQUEST['ID']);
|
||||||
|
|
||||||
|
$page = DataObject::get_by_id("SiteTree", $SQL_id);
|
||||||
|
$page->deleteFromStage('Live');
|
||||||
|
$page->flushCache();
|
||||||
|
|
||||||
|
$page = DataObject::get_by_id("SiteTree", $SQL_id);
|
||||||
|
$page->Status = "Unpublished";
|
||||||
|
$page->write();
|
||||||
|
|
||||||
|
return $this->tellBrowserAboutPublicationChange($page, "Removed '$page->Title' from the published site");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a few pieces of information about a change to a page
|
||||||
|
* - Send the new status message
|
||||||
|
* - Update the action buttons
|
||||||
|
* - Update the treenote
|
||||||
|
* - Send a status message
|
||||||
|
*/
|
||||||
|
function tellBrowserAboutPublicationChange($page, $statusMessage) {
|
||||||
|
|
||||||
|
$JS_title = Convert::raw2js($page->TreeTitle());
|
||||||
|
|
||||||
|
$JS_stageURL = Convert::raw2js(DB::query("SELECT URLSegment FROM SiteTree WHERE ID = $page->ID")->value());
|
||||||
|
$JS_liveURL = Convert::raw2js(DB::query("SELECT URLSegment FROM SiteTree_Live WHERE ID = $page->ID")->value());
|
||||||
|
FormResponse::add($this->getActionUpdateJS($page));
|
||||||
|
FormResponse::update_status($page->Status);
|
||||||
|
FormResponse::add("\$('sitetree').setNodeTitle($page->ID, '$JS_title')");
|
||||||
|
FormResponse::status_message($statusMessage, 'good');
|
||||||
|
FormResponse::add("$('Form_EditForm').elements.StageURLSegment.value = '$JS_stageURL'");
|
||||||
|
FormResponse::add("$('Form_EditForm').elements.LiveURLSegment.value = '$JS_liveURL'");
|
||||||
|
FormResponse::add("$('Form_EditForm').notify('PagePublished', $('Form_EditForm').elements.ID.value);");
|
||||||
|
|
||||||
|
return FormResponse::respond();
|
||||||
|
}
|
||||||
|
|
||||||
|
function performRollback($id, $version) {
|
||||||
|
$record = DataObject::get_by_id($this->stat('tree_class'), $id);
|
||||||
|
$record->publish($version, "Stage", true);
|
||||||
|
$record->AssignedToID = 0;
|
||||||
|
$record->RequestedByID = 0;
|
||||||
|
$record->Status = "Saved (update)";
|
||||||
|
$record->writeWithoutVersion();
|
||||||
|
return $record;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getversion() {
|
||||||
|
$id = $this->urlParams['ID'];
|
||||||
|
$version = $this->urlParams['OtherID'];
|
||||||
|
$record = Versioned::get_version("SiteTree", $id, $version);
|
||||||
|
|
||||||
|
if($record) {
|
||||||
|
$fields = $record->getCMSFields($this);
|
||||||
|
$fields->removeByName("Status");
|
||||||
|
|
||||||
|
$fields->push(new HiddenField("ID"));
|
||||||
|
$fields->push(new HiddenField("Version"));
|
||||||
|
$fields->insertBefore(new HeaderField("You are viewing version #$version, created " . $record->obj('LastEdited')->Ago()), "Root");
|
||||||
|
|
||||||
|
$actions = new FieldSet(
|
||||||
|
new FormAction("email", "Email"),
|
||||||
|
new FormAction("print", "Print"),
|
||||||
|
new FormAction("rollback", "Roll back to this version")
|
||||||
|
);
|
||||||
|
|
||||||
|
// encode the message to appear in the body of the email
|
||||||
|
$archiveURL = Director::absoluteBaseURL() . $record->URLSegment . '?archiveDate=' . $record->obj('LastEdited')->URLDate();
|
||||||
|
$archiveEmailMessage = urlencode( $this->customise( array( 'ArchiveDate' => $record->obj('LastEdited'), 'ArchiveURL' => $archiveURL ) )->renderWith( 'ViewArchivedEmail' ) );
|
||||||
|
|
||||||
|
$archiveEmailMessage = preg_replace( '/\+/', '%20', $archiveEmailMessage );
|
||||||
|
|
||||||
|
$fields->push( new HiddenField( 'ArchiveEmailMessage', '', $archiveEmailMessage ) );
|
||||||
|
$fields->push( new HiddenField( 'ArchiveEmailSubject', '', preg_replace( '/\+/', '%20', urlencode( 'Archived version of ' . $record->Title ) ) ) );
|
||||||
|
$fields->push( new HiddenField( 'ArchiveURL', '', $archiveURL ) );
|
||||||
|
|
||||||
|
$form = new Form($this, "EditForm", $fields, $actions);
|
||||||
|
$form->loadDataFrom($record);
|
||||||
|
$form->loadDataFrom(array(
|
||||||
|
"ID" => $id,
|
||||||
|
"Version" => $version,
|
||||||
|
));
|
||||||
|
$form->makeReadonly();
|
||||||
|
|
||||||
|
$templateData = $this->customise(array(
|
||||||
|
"EditForm" => $form
|
||||||
|
));
|
||||||
|
|
||||||
|
SSViewer::setOption('rewriteHashlinks', false);
|
||||||
|
$result = $templateData->renderWith($this->class . '_right');
|
||||||
|
$parts = split('</?form[^>]*>', $result);
|
||||||
|
return $parts[sizeof($parts)-2];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function compareversions() {
|
||||||
|
$id = $this->urlParams['ID'];
|
||||||
|
$version1 = $_REQUEST['From'];
|
||||||
|
$version2 = $_REQUEST['To'];
|
||||||
|
|
||||||
|
if( $version1 > $version2 ) {
|
||||||
|
$toVersion = $version1;
|
||||||
|
$fromVersion = $version2;
|
||||||
|
} else {
|
||||||
|
$toVersion = $version2;
|
||||||
|
$fromVersion = $version1;
|
||||||
|
}
|
||||||
|
|
||||||
|
$page = DataObject::get_by_id("SiteTree", $id);
|
||||||
|
$record = $page->compareVersions($fromVersion, $toVersion);
|
||||||
|
if($record) {
|
||||||
|
$fields = $record->getCMSFields($this);
|
||||||
|
$fields->push(new HiddenField("ID"));
|
||||||
|
$fields->push(new HiddenField("Version"));
|
||||||
|
$fields->insertBefore(new HeaderField("You are comparing versions #$fromVersion and #$toVersion"), "Root");
|
||||||
|
|
||||||
|
$actions = new FieldSet();
|
||||||
|
|
||||||
|
$form = new Form($this, "EditForm", $fields, $actions);
|
||||||
|
$form->loadDataFrom($record);
|
||||||
|
$form->loadDataFrom(array(
|
||||||
|
"ID" => $id,
|
||||||
|
"Version" => $version,
|
||||||
|
));
|
||||||
|
$form->makeReadonly();
|
||||||
|
foreach($form->Fields()->dataFields() as $field) {
|
||||||
|
$field->dontEscape = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->sendFormToBrowser(array(
|
||||||
|
"EditForm" => $form
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function sendFormToBrowser($templateData) {
|
||||||
|
if(Director::is_ajax()) {
|
||||||
|
SSViewer::setOption('rewriteHashlinks', false);
|
||||||
|
$result = $this->customise($templateData)->renderWith($this->class . '_right');
|
||||||
|
$parts = split('</?form[^>]*>', $result);
|
||||||
|
return $parts[sizeof($parts)-2];
|
||||||
|
} else {
|
||||||
|
return array(
|
||||||
|
"Right" => $this->customise($templateData)->renderWith($this->class . '_right'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function dialog() {
|
||||||
|
Requirements::clear();
|
||||||
|
|
||||||
|
$buttons = new DataObjectSet;
|
||||||
|
if($_REQUEST['Buttons']) foreach($_REQUEST['Buttons'] as $button) {
|
||||||
|
list($name, $title) = explode(',',$button,2);
|
||||||
|
$buttons->push(new ArrayData(array(
|
||||||
|
"Name" => $name,
|
||||||
|
"Title" => $title,
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
return array(
|
||||||
|
"Message" => htmlentities($_REQUEST['Message']),
|
||||||
|
"Buttons" => $buttons,
|
||||||
|
"Modal" => $_REQUEST['Modal'] ? true : false,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function savedialog() {
|
||||||
|
Requirements::clear();
|
||||||
|
Requirements::css('cms/css/dialog.css');
|
||||||
|
Requirements::javascript('jsparty/prototype.js');
|
||||||
|
Requirements::javascript('jsparty/behaviour.js');
|
||||||
|
Requirements::javascript('jsparty/prototype_improvements.js');
|
||||||
|
Requirements::javascript('cms/javascript/dialog.js');
|
||||||
|
|
||||||
|
$message = "You have unsaved changes. Would you like to save them?";
|
||||||
|
$buttons = "<button name=\"save\">Save changes</button><button name=\"discard\">Discard changes</button><button name=\"cancel\">Stay on this page</button>";
|
||||||
|
|
||||||
|
return $this->customise( array(
|
||||||
|
'Message' => $message,
|
||||||
|
'Buttons' => $buttons,
|
||||||
|
'DialogType' => 'alert'
|
||||||
|
))->renderWith( 'Dialog' );
|
||||||
|
}
|
||||||
|
|
||||||
|
function canceldraftchangesdialog() {
|
||||||
|
Requirements::clear();
|
||||||
|
Requirements::css('cms/css/dialog.css');
|
||||||
|
Requirements::javascript('jsparty/prototype.js');
|
||||||
|
Requirements::javascript('jsparty/behaviour.js');
|
||||||
|
Requirements::javascript('jsparty/prototype_improvements.js');
|
||||||
|
Requirements::javascript('cms/javascript/dialog.js');
|
||||||
|
|
||||||
|
$message = "Do you really want to copy the published content to the stage site?";
|
||||||
|
$buttons = "<button name=\"OK\">OK</button><button name=\"Cancel\">Cancel</button>";
|
||||||
|
|
||||||
|
return $this->customise( array(
|
||||||
|
'Message' => $message,
|
||||||
|
'Buttons' => $buttons,
|
||||||
|
'DialogType' => 'alert'
|
||||||
|
))->renderWith('Dialog');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete a number of items.
|
||||||
|
* This code supports notification
|
||||||
|
*/
|
||||||
|
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) Debug::message( "Can't find record #$id" );
|
||||||
|
|
||||||
|
if($record) {
|
||||||
|
|
||||||
|
// 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 .= "<li style=\"font-size: 65%\">" . $brokenPage->Breadcrumbs(3, true) . "</li>";
|
||||||
|
$brokenPage->HasBrokenLink = true;
|
||||||
|
$notifications[$brokenPage->OwnerID][] = $brokenPage;
|
||||||
|
$brokenPage->writeWithoutVersion();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$record->delete();
|
||||||
|
$record->destroy();
|
||||||
|
|
||||||
|
// DataObject::delete_by_id($this->stat('tree_class'), $id);
|
||||||
|
$record->CheckedPublicationDifferences = $record->DeletedFromStage = true;
|
||||||
|
|
||||||
|
// check to see if the record exists on the live site, if it doesn't remove the tree node
|
||||||
|
// $_REQUEST['showqueries'] = 1 ;
|
||||||
|
$liveRecord = Versioned::get_one_by_stage( $this->stat('tree_class'), 'Live', "`{$this->stat('tree_class')}`.`ID`={$id}");
|
||||||
|
|
||||||
|
if($liveRecord) {
|
||||||
|
$title = Convert::raw2js($record->TreeTitle());
|
||||||
|
FormResponse::add("$('sitetree').setNodeTitle($record->OldID, '$title');");
|
||||||
|
FormResponse::add("$('Form_EditForm').reloadIfSetTo($record->OldID);");
|
||||||
|
} else {
|
||||||
|
FormResponse::add("var node = $('sitetree').getTreeNodeByIdx('$id');");
|
||||||
|
FormResponse::add("if(node.parentTreeNode) node.parentTreeNode.removeTreeNode(node);");
|
||||||
|
FormResponse::add("$('Form_EditForm').reloadIfSetTo($record->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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$s = sizeof($ids) > 1 ? "s" : "";
|
||||||
|
$message = sizeof($ids) . " page$s deleted.";
|
||||||
|
if($brokenPageList != '') {
|
||||||
|
$message .= " The following pages now have broken links:<ul>" . addslashes($brokenPageList) . "</ul>Their owners have been emailed and they will fix up those pages.";
|
||||||
|
}
|
||||||
|
|
||||||
|
FormResponse::status_message($message);
|
||||||
|
|
||||||
|
return FormResponse::respond();
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildbrokenlinks() {
|
||||||
|
if($this->urlParams['ID']) {
|
||||||
|
$newPageSet[] = DataObject::get_by_id("Page", $this->urlParams['ID']);
|
||||||
|
} else {
|
||||||
|
$pages = DataObject::get("Page");
|
||||||
|
foreach($pages as $page) $newPageSet[] = $page;
|
||||||
|
$pages = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$content = new HtmlEditorField('Content');
|
||||||
|
$download = new HtmlEditorField('Download');
|
||||||
|
|
||||||
|
foreach($newPageSet as $i => $page) {
|
||||||
|
$page->HasBrokenLink = 0;
|
||||||
|
$page->HasBrokenFile = 0;
|
||||||
|
|
||||||
|
$lastUsage = (memory_get_usage() - $lastPoint);
|
||||||
|
$lastPoint = memory_get_usage();
|
||||||
|
$content->setValue($page->Content);
|
||||||
|
$content->saveInto($page);
|
||||||
|
|
||||||
|
$download->setValue($page->Download);
|
||||||
|
$download->saveInto($page);
|
||||||
|
|
||||||
|
echo "<li>$page->Title (link:$page->HasBrokenLink, file:$page->HasBrokenFile)";
|
||||||
|
|
||||||
|
$page->writeWithoutVersion();
|
||||||
|
$page->destroy();
|
||||||
|
$newPageSet[$i] = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function AddPageOptionsForm() {
|
||||||
|
$pageTypes = array();
|
||||||
|
|
||||||
|
foreach( $this->PageTypes() as $arrayData ) {
|
||||||
|
$pageTypes[$arrayData->getField('ClassName')] = $arrayData->getField('AddAction');
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Form($this, "AddPageOptionsForm", new FieldSet(
|
||||||
|
new HiddenField("ParentID"),
|
||||||
|
new DropdownField("PageType", "", $pageTypes)
|
||||||
|
// "Page to copy" => new TreeDropdownField("DuplicateSection", "", "SiteTree"),
|
||||||
|
),
|
||||||
|
new FieldSet(
|
||||||
|
new FormAction("addpage", "Go")
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function to get page count
|
||||||
|
*/
|
||||||
|
function getpagecount() {
|
||||||
|
ini_set('max_execution_time',300);
|
||||||
|
$excludePages = split(" *, *", $_GET['exclude']);
|
||||||
|
|
||||||
|
$pages = DataObject::get("SiteTree", "ParentID = 0");
|
||||||
|
foreach($pages as $page) $pageArr[] = $page;
|
||||||
|
|
||||||
|
while(list($i,$page) = each($pageArr)) {
|
||||||
|
if(!in_array($page->URLSegment, $excludePages)) {
|
||||||
|
if($children = $page->AllChildren()) {
|
||||||
|
foreach($children as $child) $pageArr[] = $child;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if(!$_GET['onlywithcontent'] || strlen(Convert::xml2raw($page->Content)) > 100) {
|
||||||
|
echo "<li>" . $page->Breadcrumbs(null, true) . "</li>";
|
||||||
|
$count++;
|
||||||
|
} else {
|
||||||
|
echo "<li style=\"color: #777\">" . $page->Breadcrumbs(null, true) . " - no content</li>";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "<p>Total pages: $count</p>";
|
||||||
|
}
|
||||||
|
|
||||||
|
function publishall() {
|
||||||
|
ini_set("memory_limit","100M");
|
||||||
|
ini_set('max_execution_time', 300);
|
||||||
|
|
||||||
|
if(isset($_POST['confirm'])) {
|
||||||
|
$pages = DataObject::get("SiteTree");
|
||||||
|
$count = 0;
|
||||||
|
foreach($pages as $page) {
|
||||||
|
$this->performPublish($page);
|
||||||
|
$page->destroy();
|
||||||
|
unset($page);
|
||||||
|
$count++;
|
||||||
|
echo "<li>$count";
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "Done: Published $count pages";
|
||||||
|
|
||||||
|
} else {
|
||||||
|
echo <<<HTML
|
||||||
|
<h1>"Publish All" functionality</h1>
|
||||||
|
<p>Pressing this button will do the equivalent of going to every page and pressing "publish". It's
|
||||||
|
intended to be used after there have been massive edits of the content, such as when the site was
|
||||||
|
first built.</p>
|
||||||
|
<form method="post" action="publishall">
|
||||||
|
<input type="submit" name="confirm" value="Please publish every page in the site, copying content stage to live" />
|
||||||
|
</form>
|
||||||
|
HTML;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function restorepage() {
|
||||||
|
if($id = $this->urlParams['ID']) {
|
||||||
|
$restoredPage = Versioned::get_latest_version("SiteTree", $id);
|
||||||
|
$restoredPage->ID = $restoredPage->RecordID;
|
||||||
|
if(!DB::query("SELECT ID FROM SiteTree WHERE ID = $restoredPage->ID")->value()) {
|
||||||
|
DB::query("INSERT INTO SiteTree SET ID = $restoredPage->ID");
|
||||||
|
}
|
||||||
|
$restoredPage->forceChange();
|
||||||
|
$restoredPage->writeWithoutVersion();
|
||||||
|
Debug::show($restoredPage);
|
||||||
|
} else {
|
||||||
|
echo "visit restorepage/(ID)";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function duplicate() {
|
||||||
|
if(($id = $this->urlParams['ID']) && is_numeric($id)) {
|
||||||
|
$page = DataObject::get_by_id("SiteTree", $id);
|
||||||
|
|
||||||
|
$newPage = $page->duplicate();
|
||||||
|
|
||||||
|
return $this->returnItemToUser($newPage);
|
||||||
|
} else {
|
||||||
|
user_error("CMSMain::duplicate() Bad ID: '$id'", E_USER_WARNING);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// HACK HACK HACK - Dont remove without telling simon ;-)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is only used by parents inc.
|
||||||
|
* TODO Work out a better way of handling control to the individual page objects.
|
||||||
|
*/
|
||||||
|
function sethottip($data,$form) {
|
||||||
|
$page = DataObject::get_by_id("SiteTree", $_REQUEST['ID']);
|
||||||
|
return $page->sethottip($data,$form);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* This is only used by parents inc.
|
||||||
|
* TODO Work out a better way of handling control to the individual page objects.
|
||||||
|
*/
|
||||||
|
function notifyInvitation($data,$form) {
|
||||||
|
$page = DataObject::get_by_id("SiteTree", $_REQUEST['ID']);
|
||||||
|
return $page->notifyInvitation($data,$form);
|
||||||
|
}
|
||||||
|
function testInvitation($data,$form) {
|
||||||
|
$page = DataObject::get_by_id("SiteTree", $_REQUEST['ID']);
|
||||||
|
return $page->testInvitation($data,$form);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this as an action handler for custom CMS buttons.
|
||||||
|
*/
|
||||||
|
function callPageMethod($data, $form) {
|
||||||
|
$methodName = $form->buttonClicked()->extraData();
|
||||||
|
$record = $this->CurrentPage();
|
||||||
|
return $record->$methodName($data, $form);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provide the permission codes used by LeftAndMain.
|
||||||
|
* Can't put it on LeftAndMain since that's an abstract base class.
|
||||||
|
*/
|
||||||
|
function providePermissions() {
|
||||||
|
$classes = ClassInfo::subclassesFor('LeftAndMain');
|
||||||
|
|
||||||
|
foreach($classes as $class) {
|
||||||
|
$perms["CMS_ACCESS_" . $class] = "Access to $class in CMS";
|
||||||
|
}
|
||||||
|
return $perms;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
795
code/Diff.php
Executable file
@ -0,0 +1,795 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// difflib.php
|
||||||
|
//
|
||||||
|
// A PHP diff engine for phpwiki.
|
||||||
|
//
|
||||||
|
// Copyright (C) 2000, 2001 Geoffrey T. Dairiki <dairiki@dairiki.org>
|
||||||
|
// You may copy this code freely under the conditions of the GPL.
|
||||||
|
//
|
||||||
|
|
||||||
|
// FIXME: possibly remove assert()'s for production version?
|
||||||
|
|
||||||
|
// PHP3 does not have assert()
|
||||||
|
define('USE_ASSERTS', function_exists('assert'));
|
||||||
|
|
||||||
|
class _DiffOp {
|
||||||
|
var $type;
|
||||||
|
var $orig;
|
||||||
|
var $final;
|
||||||
|
|
||||||
|
function reverse() {
|
||||||
|
trigger_error("pure virtual", E_USER_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
function norig() {
|
||||||
|
return $this->orig ? sizeof($this->orig) : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function nfinal() {
|
||||||
|
return $this->final ? sizeof($this->final) : 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _DiffOp_Copy extends _DiffOp {
|
||||||
|
var $type = 'copy';
|
||||||
|
|
||||||
|
function _DiffOp_Copy ($orig, $final = false) {
|
||||||
|
if (!is_array($final))
|
||||||
|
$final = $orig;
|
||||||
|
$this->orig = $orig;
|
||||||
|
$this->final = $final;
|
||||||
|
}
|
||||||
|
|
||||||
|
function reverse() {
|
||||||
|
return new _DiffOp_Copy($this->final, $this->orig);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _DiffOp_Delete extends _DiffOp {
|
||||||
|
var $type = 'delete';
|
||||||
|
|
||||||
|
function _DiffOp_Delete ($lines) {
|
||||||
|
$this->orig = $lines;
|
||||||
|
$this->final = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function reverse() {
|
||||||
|
return new _DiffOp_Add($this->orig);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _DiffOp_Add extends _DiffOp {
|
||||||
|
var $type = 'add';
|
||||||
|
|
||||||
|
function _DiffOp_Add ($lines) {
|
||||||
|
$this->final = $lines;
|
||||||
|
$this->orig = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function reverse() {
|
||||||
|
return new _DiffOp_Delete($this->final);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _DiffOp_Change extends _DiffOp {
|
||||||
|
var $type = 'change';
|
||||||
|
|
||||||
|
function _DiffOp_Change ($orig, $final) {
|
||||||
|
$this->orig = $orig;
|
||||||
|
$this->final = $final;
|
||||||
|
}
|
||||||
|
|
||||||
|
function reverse() {
|
||||||
|
return new _DiffOp_Change($this->final, $this->orig);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class used internally by Diff to actually compute the diffs.
|
||||||
|
*
|
||||||
|
* The algorithm used here is mostly lifted from the perl module
|
||||||
|
* Algorithm::Diff (version 1.06) by Ned Konz, which is available at:
|
||||||
|
* http://www.perl.com/CPAN/authors/id/N/NE/NEDKONZ/Algorithm-Diff-1.06.zip
|
||||||
|
*
|
||||||
|
* More ideas are taken from:
|
||||||
|
* http://www.ics.uci.edu/~eppstein/161/960229.html
|
||||||
|
*
|
||||||
|
* Some ideas are (and a bit of code) are from from analyze.c, from GNU
|
||||||
|
* diffutils-2.7, which can be found at:
|
||||||
|
* ftp://gnudist.gnu.org/pub/gnu/diffutils/diffutils-2.7.tar.gz
|
||||||
|
*
|
||||||
|
* Finally, some ideas (subdivision by NCHUNKS > 2, and some optimizations)
|
||||||
|
* are my own.
|
||||||
|
*
|
||||||
|
* @author Geoffrey T. Dairiki
|
||||||
|
* @access private
|
||||||
|
*/
|
||||||
|
class _DiffEngine
|
||||||
|
{
|
||||||
|
function diff ($from_lines, $to_lines) {
|
||||||
|
$n_from = sizeof($from_lines);
|
||||||
|
$n_to = sizeof($to_lines);
|
||||||
|
|
||||||
|
$this->xchanged = $this->ychanged = array();
|
||||||
|
$this->xv = $this->yv = array();
|
||||||
|
$this->xind = $this->yind = array();
|
||||||
|
unset($this->seq);
|
||||||
|
unset($this->in_seq);
|
||||||
|
unset($this->lcs);
|
||||||
|
|
||||||
|
// Skip leading common lines.
|
||||||
|
for ($skip = 0; $skip < $n_from && $skip < $n_to; $skip++) {
|
||||||
|
if ($from_lines[$skip] != $to_lines[$skip])
|
||||||
|
break;
|
||||||
|
$this->xchanged[$skip] = $this->ychanged[$skip] = false;
|
||||||
|
}
|
||||||
|
// Skip trailing common lines.
|
||||||
|
$xi = $n_from; $yi = $n_to;
|
||||||
|
for ($endskip = 0; --$xi > $skip && --$yi > $skip; $endskip++) {
|
||||||
|
if ($from_lines[$xi] != $to_lines[$yi])
|
||||||
|
break;
|
||||||
|
$this->xchanged[$xi] = $this->ychanged[$yi] = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ignore lines which do not exist in both files.
|
||||||
|
for ($xi = $skip; $xi < $n_from - $endskip; $xi++)
|
||||||
|
$xhash[$from_lines[$xi]] = 1;
|
||||||
|
for ($yi = $skip; $yi < $n_to - $endskip; $yi++) {
|
||||||
|
$line = $to_lines[$yi];
|
||||||
|
if ( ($this->ychanged[$yi] = empty($xhash[$line])) )
|
||||||
|
continue;
|
||||||
|
$yhash[$line] = 1;
|
||||||
|
$this->yv[] = $line;
|
||||||
|
$this->yind[] = $yi;
|
||||||
|
}
|
||||||
|
for ($xi = $skip; $xi < $n_from - $endskip; $xi++) {
|
||||||
|
$line = $from_lines[$xi];
|
||||||
|
if ( ($this->xchanged[$xi] = empty($yhash[$line])) )
|
||||||
|
continue;
|
||||||
|
$this->xv[] = $line;
|
||||||
|
$this->xind[] = $xi;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the LCS.
|
||||||
|
$this->_compareseq(0, sizeof($this->xv), 0, sizeof($this->yv));
|
||||||
|
|
||||||
|
// Merge edits when possible
|
||||||
|
$this->_shift_boundaries($from_lines, $this->xchanged, $this->ychanged);
|
||||||
|
$this->_shift_boundaries($to_lines, $this->ychanged, $this->xchanged);
|
||||||
|
|
||||||
|
// Compute the edit operations.
|
||||||
|
$edits = array();
|
||||||
|
$xi = $yi = 0;
|
||||||
|
while ($xi < $n_from || $yi < $n_to) {
|
||||||
|
USE_ASSERTS && assert($yi < $n_to || $this->xchanged[$xi]);
|
||||||
|
USE_ASSERTS && assert($xi < $n_from || $this->ychanged[$yi]);
|
||||||
|
|
||||||
|
// Skip matching "snake".
|
||||||
|
$copy = array();
|
||||||
|
while ( $xi < $n_from && $yi < $n_to
|
||||||
|
&& !$this->xchanged[$xi] && !$this->ychanged[$yi]) {
|
||||||
|
$copy[] = $from_lines[$xi++];
|
||||||
|
++$yi;
|
||||||
|
}
|
||||||
|
if ($copy)
|
||||||
|
$edits[] = new _DiffOp_Copy($copy);
|
||||||
|
|
||||||
|
// Find deletes & adds.
|
||||||
|
$delete = array();
|
||||||
|
while ($xi < $n_from && $this->xchanged[$xi])
|
||||||
|
$delete[] = $from_lines[$xi++];
|
||||||
|
|
||||||
|
$add = array();
|
||||||
|
while ($yi < $n_to && $this->ychanged[$yi])
|
||||||
|
$add[] = $to_lines[$yi++];
|
||||||
|
|
||||||
|
if ($delete && $add)
|
||||||
|
$edits[] = new _DiffOp_Change($delete, $add);
|
||||||
|
elseif ($delete)
|
||||||
|
$edits[] = new _DiffOp_Delete($delete);
|
||||||
|
elseif ($add)
|
||||||
|
$edits[] = new _DiffOp_Add($add);
|
||||||
|
}
|
||||||
|
return $edits;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Divide the Largest Common Subsequence (LCS) of the sequences
|
||||||
|
* [XOFF, XLIM) and [YOFF, YLIM) into NCHUNKS approximately equally
|
||||||
|
* sized segments.
|
||||||
|
*
|
||||||
|
* Returns (LCS, PTS). LCS is the length of the LCS. PTS is an
|
||||||
|
* array of NCHUNKS+1 (X, Y) indexes giving the diving points between
|
||||||
|
* sub sequences. The first sub-sequence is contained in [X0, X1),
|
||||||
|
* [Y0, Y1), the second in [X1, X2), [Y1, Y2) and so on. Note
|
||||||
|
* that (X0, Y0) == (XOFF, YOFF) and
|
||||||
|
* (X[NCHUNKS], Y[NCHUNKS]) == (XLIM, YLIM).
|
||||||
|
*
|
||||||
|
* This function assumes that the first lines of the specified portions
|
||||||
|
* of the two files do not match, and likewise that the last lines do not
|
||||||
|
* match. The caller must trim matching lines from the beginning and end
|
||||||
|
* of the portions it is going to specify.
|
||||||
|
*/
|
||||||
|
function _diag ($xoff, $xlim, $yoff, $ylim, $nchunks) {
|
||||||
|
$flip = false;
|
||||||
|
|
||||||
|
if ($xlim - $xoff > $ylim - $yoff) {
|
||||||
|
// Things seems faster (I'm not sure I understand why)
|
||||||
|
// when the shortest sequence in X.
|
||||||
|
$flip = true;
|
||||||
|
list ($xoff, $xlim, $yoff, $ylim)
|
||||||
|
= array( $yoff, $ylim, $xoff, $xlim);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($flip)
|
||||||
|
for ($i = $ylim - 1; $i >= $yoff; $i--)
|
||||||
|
$ymatches[$this->xv[$i]][] = $i;
|
||||||
|
else
|
||||||
|
for ($i = $ylim - 1; $i >= $yoff; $i--)
|
||||||
|
$ymatches[$this->yv[$i]][] = $i;
|
||||||
|
|
||||||
|
$this->lcs = 0;
|
||||||
|
$this->seq[0]= $yoff - 1;
|
||||||
|
$this->in_seq = array();
|
||||||
|
$ymids[0] = array();
|
||||||
|
|
||||||
|
$numer = $xlim - $xoff + $nchunks - 1;
|
||||||
|
$x = $xoff;
|
||||||
|
for ($chunk = 0; $chunk < $nchunks; $chunk++) {
|
||||||
|
if ($chunk > 0)
|
||||||
|
for ($i = 0; $i <= $this->lcs; $i++)
|
||||||
|
$ymids[$i][$chunk-1] = $this->seq[$i];
|
||||||
|
|
||||||
|
$x1 = $xoff + (int)(($numer + ($xlim-$xoff)*$chunk) / $nchunks);
|
||||||
|
for ( ; $x < $x1; $x++) {
|
||||||
|
$line = $flip ? $this->yv[$x] : $this->xv[$x];
|
||||||
|
if (empty($ymatches[$line]))
|
||||||
|
continue;
|
||||||
|
$matches = $ymatches[$line];
|
||||||
|
reset($matches);
|
||||||
|
while (list ($junk, $y) = each($matches))
|
||||||
|
if (empty($this->in_seq[$y])) {
|
||||||
|
$k = $this->_lcs_pos($y);
|
||||||
|
USE_ASSERTS && assert($k > 0);
|
||||||
|
$ymids[$k] = $ymids[$k-1];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
while (list ($junk, $y) = each($matches)) {
|
||||||
|
if ($y > $this->seq[$k-1]) {
|
||||||
|
USE_ASSERTS && assert($y < $this->seq[$k]);
|
||||||
|
// Optimization: this is a common case:
|
||||||
|
// next match is just replacing previous match.
|
||||||
|
$this->in_seq[$this->seq[$k]] = false;
|
||||||
|
$this->seq[$k] = $y;
|
||||||
|
$this->in_seq[$y] = 1;
|
||||||
|
}
|
||||||
|
else if (empty($this->in_seq[$y])) {
|
||||||
|
$k = $this->_lcs_pos($y);
|
||||||
|
USE_ASSERTS && assert($k > 0);
|
||||||
|
$ymids[$k] = $ymids[$k-1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$seps[] = $flip ? array($yoff, $xoff) : array($xoff, $yoff);
|
||||||
|
$ymid = $ymids[$this->lcs];
|
||||||
|
for ($n = 0; $n < $nchunks - 1; $n++) {
|
||||||
|
$x1 = $xoff + (int)(($numer + ($xlim - $xoff) * $n) / $nchunks);
|
||||||
|
$y1 = $ymid[$n] + 1;
|
||||||
|
$seps[] = $flip ? array($y1, $x1) : array($x1, $y1);
|
||||||
|
}
|
||||||
|
$seps[] = $flip ? array($ylim, $xlim) : array($xlim, $ylim);
|
||||||
|
|
||||||
|
return array($this->lcs, $seps);
|
||||||
|
}
|
||||||
|
|
||||||
|
function _lcs_pos ($ypos) {
|
||||||
|
$end = $this->lcs;
|
||||||
|
if ($end == 0 || $ypos > $this->seq[$end]) {
|
||||||
|
$this->seq[++$this->lcs] = $ypos;
|
||||||
|
$this->in_seq[$ypos] = 1;
|
||||||
|
return $this->lcs;
|
||||||
|
}
|
||||||
|
|
||||||
|
$beg = 1;
|
||||||
|
while ($beg < $end) {
|
||||||
|
$mid = (int)(($beg + $end) / 2);
|
||||||
|
if ( $ypos > $this->seq[$mid] )
|
||||||
|
$beg = $mid + 1;
|
||||||
|
else
|
||||||
|
$end = $mid;
|
||||||
|
}
|
||||||
|
|
||||||
|
USE_ASSERTS && assert($ypos != $this->seq[$end]);
|
||||||
|
|
||||||
|
$this->in_seq[$this->seq[$end]] = false;
|
||||||
|
$this->seq[$end] = $ypos;
|
||||||
|
$this->in_seq[$ypos] = 1;
|
||||||
|
return $end;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Find LCS of two sequences.
|
||||||
|
*
|
||||||
|
* The results are recorded in the vectors $this->{x,y}changed[], by
|
||||||
|
* storing a 1 in the element for each line that is an insertion
|
||||||
|
* or deletion (ie. is not in the LCS).
|
||||||
|
*
|
||||||
|
* The subsequence of file 0 is [XOFF, XLIM) and likewise for file 1.
|
||||||
|
*
|
||||||
|
* Note that XLIM, YLIM are exclusive bounds.
|
||||||
|
* All line numbers are origin-0 and discarded lines are not counted.
|
||||||
|
*/
|
||||||
|
function _compareseq ($xoff, $xlim, $yoff, $ylim) {
|
||||||
|
// Slide down the bottom initial diagonal.
|
||||||
|
while ($xoff < $xlim && $yoff < $ylim
|
||||||
|
&& $this->xv[$xoff] == $this->yv[$yoff]) {
|
||||||
|
++$xoff;
|
||||||
|
++$yoff;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Slide up the top initial diagonal.
|
||||||
|
while ($xlim > $xoff && $ylim > $yoff
|
||||||
|
&& $this->xv[$xlim - 1] == $this->yv[$ylim - 1]) {
|
||||||
|
--$xlim;
|
||||||
|
--$ylim;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($xoff == $xlim || $yoff == $ylim)
|
||||||
|
$lcs = 0;
|
||||||
|
else {
|
||||||
|
// This is ad hoc but seems to work well.
|
||||||
|
//$nchunks = sqrt(min($xlim - $xoff, $ylim - $yoff) / 2.5);
|
||||||
|
//$nchunks = max(2,min(8,(int)$nchunks));
|
||||||
|
$nchunks = min(7, $xlim - $xoff, $ylim - $yoff) + 1;
|
||||||
|
list ($lcs, $seps)
|
||||||
|
= $this->_diag($xoff,$xlim,$yoff, $ylim,$nchunks);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($lcs == 0) {
|
||||||
|
// X and Y sequences have no common subsequence:
|
||||||
|
// mark all changed.
|
||||||
|
while ($yoff < $ylim)
|
||||||
|
$this->ychanged[$this->yind[$yoff++]] = 1;
|
||||||
|
while ($xoff < $xlim)
|
||||||
|
$this->xchanged[$this->xind[$xoff++]] = 1;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Use the partitions to split this problem into subproblems.
|
||||||
|
reset($seps);
|
||||||
|
$pt1 = $seps[0];
|
||||||
|
while ($pt2 = next($seps)) {
|
||||||
|
$this->_compareseq ($pt1[0], $pt2[0], $pt1[1], $pt2[1]);
|
||||||
|
$pt1 = $pt2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Adjust inserts/deletes of identical lines to join changes
|
||||||
|
* as much as possible.
|
||||||
|
*
|
||||||
|
* We do something when a run of changed lines include a
|
||||||
|
* line at one end and has an excluded, identical line at the other.
|
||||||
|
* We are free to choose which identical line is included.
|
||||||
|
* `compareseq' usually chooses the one at the beginning,
|
||||||
|
* but usually it is cleaner to consider the following identical line
|
||||||
|
* to be the "change".
|
||||||
|
*
|
||||||
|
* This is extracted verbatim from analyze.c (GNU diffutils-2.7).
|
||||||
|
*/
|
||||||
|
function _shift_boundaries ($lines, &$changed, $other_changed) {
|
||||||
|
$i = 0;
|
||||||
|
$j = 0;
|
||||||
|
|
||||||
|
USE_ASSERTS && assert('sizeof($lines) == sizeof($changed)');
|
||||||
|
$len = sizeof($lines);
|
||||||
|
$other_len = sizeof($other_changed);
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
/*
|
||||||
|
* Scan forwards to find beginning of another run of changes.
|
||||||
|
* Also keep track of the corresponding point in the other file.
|
||||||
|
*
|
||||||
|
* Throughout this code, $i and $j are adjusted together so that
|
||||||
|
* the first $i elements of $changed and the first $j elements
|
||||||
|
* of $other_changed both contain the same number of zeros
|
||||||
|
* (unchanged lines).
|
||||||
|
* Furthermore, $j is always kept so that $j == $other_len or
|
||||||
|
* $other_changed[$j] == false.
|
||||||
|
*/
|
||||||
|
while ($j < $other_len && $other_changed[$j])
|
||||||
|
$j++;
|
||||||
|
|
||||||
|
while ($i < $len && ! $changed[$i]) {
|
||||||
|
USE_ASSERTS && assert('$j < $other_len && ! $other_changed[$j]');
|
||||||
|
$i++; $j++;
|
||||||
|
while ($j < $other_len && $other_changed[$j])
|
||||||
|
$j++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($i == $len)
|
||||||
|
break;
|
||||||
|
|
||||||
|
$start = $i;
|
||||||
|
|
||||||
|
// Find the end of this run of changes.
|
||||||
|
while (++$i < $len && $changed[$i])
|
||||||
|
continue;
|
||||||
|
|
||||||
|
do {
|
||||||
|
/*
|
||||||
|
* Record the length of this run of changes, so that
|
||||||
|
* we can later determine whether the run has grown.
|
||||||
|
*/
|
||||||
|
$runlength = $i - $start;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Move the changed region back, so long as the
|
||||||
|
* previous unchanged line matches the last changed one.
|
||||||
|
* This merges with previous changed regions.
|
||||||
|
*/
|
||||||
|
while ($start > 0 && $lines[$start - 1] == $lines[$i - 1]) {
|
||||||
|
$changed[--$start] = 1;
|
||||||
|
$changed[--$i] = false;
|
||||||
|
while ($start > 0 && $changed[$start - 1])
|
||||||
|
$start--;
|
||||||
|
USE_ASSERTS && assert('$j > 0');
|
||||||
|
while ($other_changed[--$j])
|
||||||
|
continue;
|
||||||
|
USE_ASSERTS && assert('$j >= 0 && !$other_changed[$j]');
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set CORRESPONDING to the end of the changed run, at the last
|
||||||
|
* point where it corresponds to a changed run in the other file.
|
||||||
|
* CORRESPONDING == LEN means no such point has been found.
|
||||||
|
*/
|
||||||
|
$corresponding = $j < $other_len ? $i : $len;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Move the changed region forward, so long as the
|
||||||
|
* first changed line matches the following unchanged one.
|
||||||
|
* This merges with following changed regions.
|
||||||
|
* Do this second, so that if there are no merges,
|
||||||
|
* the changed region is moved forward as far as possible.
|
||||||
|
*/
|
||||||
|
while ($i < $len && $lines[$start] == $lines[$i]) {
|
||||||
|
$changed[$start++] = false;
|
||||||
|
$changed[$i++] = 1;
|
||||||
|
while ($i < $len && $changed[$i])
|
||||||
|
$i++;
|
||||||
|
|
||||||
|
USE_ASSERTS && assert('$j < $other_len && ! $other_changed[$j]');
|
||||||
|
$j++;
|
||||||
|
if ($j < $other_len && $other_changed[$j]) {
|
||||||
|
$corresponding = $i;
|
||||||
|
while ($j < $other_len && $other_changed[$j])
|
||||||
|
$j++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while ($runlength != $i - $start);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If possible, move the fully-merged run of changes
|
||||||
|
* back to a corresponding run in the other file.
|
||||||
|
*/
|
||||||
|
while ($corresponding < $i) {
|
||||||
|
$changed[--$start] = 1;
|
||||||
|
$changed[--$i] = 0;
|
||||||
|
USE_ASSERTS && assert('$j > 0');
|
||||||
|
while ($other_changed[--$j])
|
||||||
|
continue;
|
||||||
|
USE_ASSERTS && assert('$j >= 0 && !$other_changed[$j]');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class representing a 'diff' between two sequences of strings.
|
||||||
|
*/
|
||||||
|
class Diff
|
||||||
|
{
|
||||||
|
var $edits;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
* Computes diff between sequences of strings.
|
||||||
|
*
|
||||||
|
* @param $from_lines array An array of strings.
|
||||||
|
* (Typically these are lines from a file.)
|
||||||
|
* @param $to_lines array An array of strings.
|
||||||
|
*/
|
||||||
|
function Diff($from_lines, $to_lines) {
|
||||||
|
$eng = new _DiffEngine;
|
||||||
|
$this->edits = $eng->diff($from_lines, $to_lines);
|
||||||
|
//$this->_check($from_lines, $to_lines);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compute reversed Diff.
|
||||||
|
*
|
||||||
|
* SYNOPSIS:
|
||||||
|
*
|
||||||
|
* $diff = new Diff($lines1, $lines2);
|
||||||
|
* $rev = $diff->reverse();
|
||||||
|
* @return object A Diff object representing the inverse of the
|
||||||
|
* original diff.
|
||||||
|
*/
|
||||||
|
function reverse () {
|
||||||
|
$rev = $this;
|
||||||
|
$rev->edits = array();
|
||||||
|
foreach ($this->edits as $edit) {
|
||||||
|
$rev->edits[] = $edit->reverse();
|
||||||
|
}
|
||||||
|
return $rev;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check for empty diff.
|
||||||
|
*
|
||||||
|
* @return bool True iff two sequences were identical.
|
||||||
|
*/
|
||||||
|
function isEmpty () {
|
||||||
|
foreach ($this->edits as $edit) {
|
||||||
|
if ($edit->type != 'copy')
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compute the length of the Longest Common Subsequence (LCS).
|
||||||
|
*
|
||||||
|
* This is mostly for diagnostic purposed.
|
||||||
|
*
|
||||||
|
* @return int The length of the LCS.
|
||||||
|
*/
|
||||||
|
function lcs () {
|
||||||
|
$lcs = 0;
|
||||||
|
foreach ($this->edits as $edit) {
|
||||||
|
if ($edit->type == 'copy')
|
||||||
|
$lcs += sizeof($edit->orig);
|
||||||
|
}
|
||||||
|
return $lcs;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the original set of lines.
|
||||||
|
*
|
||||||
|
* This reconstructs the $from_lines parameter passed to the
|
||||||
|
* constructor.
|
||||||
|
*
|
||||||
|
* @return array The original sequence of strings.
|
||||||
|
*/
|
||||||
|
function orig() {
|
||||||
|
$lines = array();
|
||||||
|
|
||||||
|
foreach ($this->edits as $edit) {
|
||||||
|
if ($edit->orig)
|
||||||
|
array_splice($lines, sizeof($lines), 0, $edit->orig);
|
||||||
|
}
|
||||||
|
return $lines;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the final set of lines.
|
||||||
|
*
|
||||||
|
* This reconstructs the $to_lines parameter passed to the
|
||||||
|
* constructor.
|
||||||
|
*
|
||||||
|
* @return array The sequence of strings.
|
||||||
|
*/
|
||||||
|
function finaltext() {
|
||||||
|
$lines = array();
|
||||||
|
|
||||||
|
foreach ($this->edits as $edit) {
|
||||||
|
if ($edit->final)
|
||||||
|
array_splice($lines, sizeof($lines), 0, $edit->final);
|
||||||
|
}
|
||||||
|
return $lines;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check a Diff for validity.
|
||||||
|
*
|
||||||
|
* This is here only for debugging purposes.
|
||||||
|
*/
|
||||||
|
function _check ($from_lines, $to_lines) {
|
||||||
|
if (serialize($from_lines) != serialize($this->orig()))
|
||||||
|
trigger_error("Reconstructed original doesn't match", E_USER_ERROR);
|
||||||
|
if (serialize($to_lines) != serialize($this->finaltext()))
|
||||||
|
trigger_error("Reconstructed final doesn't match", E_USER_ERROR);
|
||||||
|
|
||||||
|
$rev = $this->reverse();
|
||||||
|
if (serialize($to_lines) != serialize($rev->orig()))
|
||||||
|
trigger_error("Reversed original doesn't match", E_USER_ERROR);
|
||||||
|
if (serialize($from_lines) != serialize($rev->finaltext()))
|
||||||
|
trigger_error("Reversed final doesn't match", E_USER_ERROR);
|
||||||
|
|
||||||
|
|
||||||
|
$prevtype = 'none';
|
||||||
|
foreach ($this->edits as $edit) {
|
||||||
|
if ( $prevtype == $edit->type )
|
||||||
|
trigger_error("Edit sequence is non-optimal", E_USER_ERROR);
|
||||||
|
$prevtype = $edit->type;
|
||||||
|
}
|
||||||
|
|
||||||
|
$lcs = $this->lcs();
|
||||||
|
trigger_error("Diff okay: LCS = $lcs", E_USER_NOTICE);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static function compareHTML($from, $to) {
|
||||||
|
// First split up the content into words and tags
|
||||||
|
$set1 = self::getHTMLChunks($from);
|
||||||
|
$set2 = self::getHTMLChunks($to);
|
||||||
|
|
||||||
|
// Diff that
|
||||||
|
$diff = new Diff($set1, $set2);
|
||||||
|
|
||||||
|
// Go through everything, converting edited tags (and their content) into single chunks. Otherwise
|
||||||
|
// the generated HTML gets crusty
|
||||||
|
foreach($diff->edits as $edit) {
|
||||||
|
//echo "<li>$edit->type: " . htmlentities(implode(" " ,$edit->orig));
|
||||||
|
//if($edit->final) echo ' / ' . htmlentities(implode(" " ,$edit->final));
|
||||||
|
|
||||||
|
switch($edit->type) {
|
||||||
|
case 'copy':
|
||||||
|
$lookForTag = false;
|
||||||
|
$stuffFor[1] = $edit->orig;
|
||||||
|
$stuffFor[2] = $edit->orig;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'change':
|
||||||
|
$lookForTag = true;
|
||||||
|
$stuffFor[1] = $edit->orig;
|
||||||
|
$stuffFor[2] = $edit->final;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'add':
|
||||||
|
$lookForTag = true;
|
||||||
|
$stuffFor[1] = null;
|
||||||
|
$stuffFor[2] = $edit->final;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'delete':
|
||||||
|
$lookForTag = true;
|
||||||
|
$stuffFor[1] = $edit->orig;
|
||||||
|
$stuffFor[2] = null;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach($stuffFor as $listName => $chunks) {
|
||||||
|
if($chunks) {
|
||||||
|
foreach($chunks as $item) {
|
||||||
|
// $tagStack > 0 indicates that we should be tag-building
|
||||||
|
if($tagStack[$listName]) $rechunked[$listName][sizeof($rechunked[$listName])-1] .= ' ' . $item;
|
||||||
|
else $rechunked[$listName][] = $item;
|
||||||
|
|
||||||
|
if($lookForTag && $item[0] == "<" && substr($item,0,2) != "</") {
|
||||||
|
$tagStack[$listName] = 1;
|
||||||
|
} else if($tagStack[$listName]) {
|
||||||
|
if(substr($item,0,2) == "</") $tagStack[$listName]--;
|
||||||
|
else if($item[0] == "<") $tagStack[$listName]++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// echo "<li>" . htmlentities($item) . " -> " .$tagStack[$listName];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Diff the re-chunked data, turning it into maked up HTML
|
||||||
|
$diff = new Diff($rechunked[1], $rechunked[2]);
|
||||||
|
foreach($diff->edits as $edit) {
|
||||||
|
// echo "<li>$edit->type: " . htmlentities(implode(" " ,$edit->orig));
|
||||||
|
// if($edit->final) echo ' / ' . htmlentities(implode(" " ,$edit->final));
|
||||||
|
|
||||||
|
switch($edit->type) {
|
||||||
|
case 'copy':
|
||||||
|
$content .= " " . implode(" ", $edit->orig) . " ";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'change':
|
||||||
|
$content .= " <ins>" . implode(" ", $edit->final) . "</ins> ";
|
||||||
|
$content .= " <del>" . implode(" ", $edit->orig) . "</del> ";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'add':
|
||||||
|
$content .= " <ins>" . implode(" ", $edit->final) . "</ins> ";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'delete':
|
||||||
|
$content .= " <del>" . implode(" ", $edit->orig) . "</del> ";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// echo "<p>" . htmlentities($content) . "</p>";
|
||||||
|
return $content;
|
||||||
|
}
|
||||||
|
static function getHTMLChunks($content) {
|
||||||
|
$content = str_replace(array(" ","<", ">"),array(" "," <", "> "),$content);
|
||||||
|
$candidateChunks = split("[\t\r\n ]+", $content);
|
||||||
|
while(list($i,$item) = each($candidateChunks)) {
|
||||||
|
if($item[0] == "<") {
|
||||||
|
$newChunk = $item;
|
||||||
|
while($item[strlen($item)-1] != ">") {
|
||||||
|
list($i,$item) = each($candidateChunks);
|
||||||
|
$newChunk .= ' ' . $item;
|
||||||
|
}
|
||||||
|
$chunks[] = $newChunk;
|
||||||
|
} else {
|
||||||
|
$chunks[] = $item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $chunks;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* FIXME: bad name.
|
||||||
|
*/
|
||||||
|
class MappedDiff
|
||||||
|
extends Diff
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
*
|
||||||
|
* Computes diff between sequences of strings.
|
||||||
|
*
|
||||||
|
* This can be used to compute things like
|
||||||
|
* case-insensitve diffs, or diffs which ignore
|
||||||
|
* changes in white-space.
|
||||||
|
*
|
||||||
|
* @param $from_lines array An array of strings.
|
||||||
|
* (Typically these are lines from a file.)
|
||||||
|
*
|
||||||
|
* @param $to_lines array An array of strings.
|
||||||
|
*
|
||||||
|
* @param $mapped_from_lines array This array should
|
||||||
|
* have the same size number of elements as $from_lines.
|
||||||
|
* The elements in $mapped_from_lines and
|
||||||
|
* $mapped_to_lines are what is actually compared
|
||||||
|
* when computing the diff.
|
||||||
|
*
|
||||||
|
* @param $mapped_to_lines array This array should
|
||||||
|
* have the same number of elements as $to_lines.
|
||||||
|
*/
|
||||||
|
function MappedDiff($from_lines, $to_lines,
|
||||||
|
$mapped_from_lines, $mapped_to_lines) {
|
||||||
|
|
||||||
|
assert(sizeof($from_lines) == sizeof($mapped_from_lines));
|
||||||
|
assert(sizeof($to_lines) == sizeof($mapped_to_lines));
|
||||||
|
|
||||||
|
$this->Diff($mapped_from_lines, $mapped_to_lines);
|
||||||
|
|
||||||
|
$xi = $yi = 0;
|
||||||
|
// Optimizing loop invariants:
|
||||||
|
// http://phplens.com/lens/php-book/optimizing-debugging-php.php
|
||||||
|
for ($i = 0, $max = sizeof($this->edits); $i < $max; $i++) {
|
||||||
|
$orig = &$this->edits[$i]->orig;
|
||||||
|
if (is_array($orig)) {
|
||||||
|
$orig = array_slice($from_lines, $xi, sizeof($orig));
|
||||||
|
$xi += sizeof($orig);
|
||||||
|
}
|
||||||
|
|
||||||
|
$final = &$this->edits[$i]->final;
|
||||||
|
if (is_array($final)) {
|
||||||
|
$final = array_slice($to_lines, $yi, sizeof($final));
|
||||||
|
$yi += sizeof($final);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
?>
|
17
code/FileList.php
Executable file
@ -0,0 +1,17 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
class FileList extends TableListField {
|
||||||
|
// bdc: added sort by Title as default behaviour
|
||||||
|
protected $folder;
|
||||||
|
function __construct($name, $folder) {
|
||||||
|
$this->folder = $folder;
|
||||||
|
parent::__construct($name, "File", array("Title" => "Title", "LinkedURL" => "URL"), "", "Title");
|
||||||
|
$this->Markable = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function sourceItems() {
|
||||||
|
return DataObject::get("File", "ParentID = '" . $this->folder->ID . "' AND ClassName <> 'Folder'", "Title");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
650
code/GenericDataAdmin.php
Executable file
@ -0,0 +1,650 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides a common interface for searching, viewing and editing DataObjects.
|
||||||
|
* Extend the class to adjust functionality to your specific DataObjects.
|
||||||
|
*
|
||||||
|
* @var $data_type DataObject The base class
|
||||||
|
* @var $data_type_extra Array Additional DataObjects which are included in the search.
|
||||||
|
* @var $resultColumnts Array Columnnames shown in the result-table.
|
||||||
|
*/
|
||||||
|
abstract class GenericDataAdmin extends LeftAndMain {
|
||||||
|
|
||||||
|
public $filter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var FieldSet Specifies which {Actions} can be performed on a resultset,
|
||||||
|
* e.g. "Export" or "Send". The form contains the resultset as CSV for further processing.
|
||||||
|
* These actions can be extended in a subclassed constructor.
|
||||||
|
*/
|
||||||
|
protected $result_actions;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
static $data_type;
|
||||||
|
|
||||||
|
static $data_type_extra;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies which information should be listed in the results-box,
|
||||||
|
* either in "table"- or "list"-format (see {$result_format}).
|
||||||
|
*
|
||||||
|
* Format "table":
|
||||||
|
* array(
|
||||||
|
* 'AccountName' => 'AccountName'
|
||||||
|
* )
|
||||||
|
*
|
||||||
|
* Format "list":
|
||||||
|
* see {@DataObject->buildNestedUL}
|
||||||
|
*/
|
||||||
|
static $result_columns;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
* Either "table" or "list". List-format also supports second level results.
|
||||||
|
*/
|
||||||
|
static $result_format = "table";
|
||||||
|
|
||||||
|
static $csv_columns;
|
||||||
|
|
||||||
|
private $results;
|
||||||
|
|
||||||
|
function __construct() {
|
||||||
|
$this->result_actions = new FieldSet(
|
||||||
|
new FormAction("export","Export as CSV")
|
||||||
|
);
|
||||||
|
|
||||||
|
parent::__construct();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets Requirements and checks for Permissions.
|
||||||
|
* Subclass this function to add custom Requirements.
|
||||||
|
*/
|
||||||
|
function init() {
|
||||||
|
parent::init();
|
||||||
|
|
||||||
|
Requirements::javascript(MCE_ROOT . "tiny_mce_src.js");
|
||||||
|
Requirements::javascript("jsparty/tiny_mce_improvements.js");
|
||||||
|
|
||||||
|
Requirements::javascript("jsparty/hover.js");
|
||||||
|
Requirements::javascript("jsparty/scriptaculous/controls.js");
|
||||||
|
|
||||||
|
Requirements::javascript("cms/javascript/SecurityAdmin.js");
|
||||||
|
Requirements::javascript("cms/javascript/CMSMain_left.js");
|
||||||
|
|
||||||
|
Requirements::javascript("cms/javascript/GenericDataAdmin_left.js");
|
||||||
|
Requirements::javascript("cms/javascript/GenericDataAdmin_right.js");
|
||||||
|
Requirements::javascript("cms/javascript/SideTabs.js");
|
||||||
|
|
||||||
|
// 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::javascriptTemplate("cms/javascript/tinymce.template.js", array(
|
||||||
|
"ContentCSS" => project() . "/css/editor.css",
|
||||||
|
"BaseURL" => Director::absoluteBaseURL(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
Requirements::css("cms/css/GenericDataAdmin.css");
|
||||||
|
|
||||||
|
//For wrightgroup workshop
|
||||||
|
Requirements::css("writework/css/WorkshopCMSLayout.css");
|
||||||
|
}
|
||||||
|
|
||||||
|
function Link() {
|
||||||
|
$args = func_get_args();
|
||||||
|
return call_user_func_array( array( &$this, 'getLink' ), $args );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return String
|
||||||
|
*/
|
||||||
|
function DataTypeSingular() {
|
||||||
|
return singleton($this->stat('data_type'))->singular_name();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return String
|
||||||
|
*/
|
||||||
|
function DataTypePlural() {
|
||||||
|
return singleton($this->stat('data_type'))->plural_name();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Form
|
||||||
|
*/
|
||||||
|
function CreationForm() {
|
||||||
|
$plural_name = singleton($this->stat('data_type'))->plural_name();
|
||||||
|
$singular_name = singleton($this->stat('data_type'))->singular_name();
|
||||||
|
return new Form($this, 'CreationForm', new FieldSet(), new FieldSet(new FormAction("createRecord", "Create {$singular_name}")));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Form
|
||||||
|
*/
|
||||||
|
function EditForm() {
|
||||||
|
$id = isset($_REQUEST['ID']) ? $_REQUEST['ID'] : Session::get('currentPage');
|
||||||
|
if($id && DataObject::get_by_id($this->stat('data_type'), $id)) {
|
||||||
|
return $this->getEditForm($id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// legacy
|
||||||
|
function ExportForm() {
|
||||||
|
return $this->EditForm();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Form
|
||||||
|
*/
|
||||||
|
function SearchForm() {
|
||||||
|
|
||||||
|
$fields = $this->getSearchFields();
|
||||||
|
$actions = new FieldSet($action = new FormAction("getResults", "Go"));
|
||||||
|
|
||||||
|
$searchForm = new Form($this, "SearchForm", $fields, $actions);
|
||||||
|
$searchForm->loadDataFrom($_REQUEST);
|
||||||
|
return $searchForm;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines fields and actions for the given {$data_type}, and populates
|
||||||
|
* these fields with values from {$data_type} and any connected {$data_type_extra}.
|
||||||
|
* Adds default actions ("save" and "delete") if no custom actions are found.
|
||||||
|
* Returns an empty form if no fields or actions are found (on first load).
|
||||||
|
*
|
||||||
|
* @param $id Number
|
||||||
|
* @return Form
|
||||||
|
*/
|
||||||
|
function getEditForm($id) {
|
||||||
|
if($_GET['debug_profile']) Profiler::mark('getEditForm');
|
||||||
|
|
||||||
|
$genericData = DataObject::get_by_id($this->stat('data_type'), $id);
|
||||||
|
|
||||||
|
$fields = (method_exists($genericData, getCMSFields)) ? $genericData->getCMSFields() : new FieldSet();
|
||||||
|
|
||||||
|
if(!$fields->dataFieldByName('ID')) {
|
||||||
|
|
||||||
|
$fields->push($idField = new HiddenField("ID","ID",$id));
|
||||||
|
$idField->setValue($id);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(method_exists($genericData, getGenericStatus)){
|
||||||
|
$genericDataStatus = $genericData->getGenericStatus();
|
||||||
|
if($genericDataStatus){
|
||||||
|
$fields->push($dataStatusField = new ReadonlyField("GenericDataStatus", "", $genericDataStatus));
|
||||||
|
$dataStatusField -> dontEscape = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
$actions = (method_exists($genericData, getCMSActions)) ? $genericData->getCMSActions() : new FieldSet();
|
||||||
|
if(!$actions->fieldByName('action_save')) {
|
||||||
|
$actions->push(new FormAction('save', 'Save','ajaxAction-save'));
|
||||||
|
}
|
||||||
|
if(!$actions->fieldByName('action_delete')) {
|
||||||
|
$actions->push(new FormAction('delete', 'Delete','ajaxAction-delete'));
|
||||||
|
}
|
||||||
|
$form = new Form($this, "EditForm", $fields, $actions);
|
||||||
|
|
||||||
|
if($this->stat('data_type_extra')) {
|
||||||
|
foreach ($this->stat('data_type_extra') as $oneRelated) {
|
||||||
|
$oneExtra = $genericData-> $oneRelated();
|
||||||
|
if($oneExtra) {
|
||||||
|
$allFields = $oneExtra->getAllFields();
|
||||||
|
foreach ($allFields as $k => $v) {
|
||||||
|
$fieldname = $oneRelated . "[" . $k . "]";
|
||||||
|
$allFields[$fieldname] = $v;
|
||||||
|
unset ($allFields[$k]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$form->loadDataFrom($allFields);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$form->loadDataFrom($genericData);
|
||||||
|
$form->disableDefaultAction();
|
||||||
|
|
||||||
|
if($_GET['debug_profile']) Profiler::unmark('getEditForm');
|
||||||
|
return $form;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display the results of the search.
|
||||||
|
* @return String
|
||||||
|
*/
|
||||||
|
function Results() {
|
||||||
|
$ret = "";
|
||||||
|
|
||||||
|
$singular_name = singleton($this->stat('data_type'))->singular_name();
|
||||||
|
$plural_name = singleton($this->stat('data_type'))->plural_name();
|
||||||
|
$this->filter = array(
|
||||||
|
"ClassName" => $this->stat('data_type')
|
||||||
|
);
|
||||||
|
|
||||||
|
$results = $this->performSearch();
|
||||||
|
if($results) {
|
||||||
|
$name = ($results->Count() > 1) ? $plural_name : $singular_name;
|
||||||
|
$ret .= "<H2>{$results->Count()} {$name} found:</H2>";
|
||||||
|
|
||||||
|
switch($this->stat('result_format')) {
|
||||||
|
case 'table':
|
||||||
|
$ret .= $this->getResultTable($results);
|
||||||
|
break;
|
||||||
|
case 'list':
|
||||||
|
$ret .= $this->getResultList($results);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
$ret .= $this->getResultActionsForm($results);
|
||||||
|
} else {
|
||||||
|
if($this->hasMethod('isEmptySearch') && $this->isEmptySearch()) {
|
||||||
|
$ret .="<h3>Please choose some search criteria and press 'Go'.</h3>";
|
||||||
|
} else {
|
||||||
|
$ret .="<h3>Sorry, no {$plural_name} found by this search.</h3>";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getResults() {
|
||||||
|
if(Director::is_ajax()) {
|
||||||
|
echo $this->Results();
|
||||||
|
} else {
|
||||||
|
return $this->Results();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getResultList($results, $link = true) {
|
||||||
|
$listBody = $results->buildNestedUL($this->stat('result_columns'));
|
||||||
|
|
||||||
|
return <<<HTML
|
||||||
|
<div class="ResultList">
|
||||||
|
$listBody
|
||||||
|
</div>
|
||||||
|
HTML;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param $results
|
||||||
|
* @param $link Link the rows to their according result (evaluated by javascript)
|
||||||
|
* @return String Result-Table as HTML
|
||||||
|
*/
|
||||||
|
function getResultTable($results, $link = true) {
|
||||||
|
$tableHeader = $this->columnheader();
|
||||||
|
|
||||||
|
$tableBody = $this->columnbody($results);
|
||||||
|
|
||||||
|
return <<<HTML
|
||||||
|
<table class="ResultTable">
|
||||||
|
<thead>
|
||||||
|
$tableHeader
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
$tableBody
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
HTML;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function columnheader(){
|
||||||
|
$content = "";
|
||||||
|
foreach( array_keys($this->stat('result_columns')) as $field ) {
|
||||||
|
$content .= $this->htmlTableCell($field);
|
||||||
|
}
|
||||||
|
return $this->htmlTableRow($content);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function columnbody($results=null) {
|
||||||
|
// shouldn't be called here, but left in for legacy
|
||||||
|
if(!$results) {
|
||||||
|
$results = $this->performSearch();
|
||||||
|
}
|
||||||
|
|
||||||
|
$body = "";
|
||||||
|
if($results){
|
||||||
|
$i=0;
|
||||||
|
foreach($results as $result){
|
||||||
|
$i++;
|
||||||
|
$html = "";
|
||||||
|
foreach($this->stat('result_columns') as $field) {
|
||||||
|
$value = $this->buildResultFieldValue($result, $field);
|
||||||
|
$html .= $this->htmlTableCell($value, $this->Link("show", $result->ID), "show", true);
|
||||||
|
|
||||||
|
}
|
||||||
|
$evenOrOdd = ($i%2)?"even":"odd";
|
||||||
|
$row = $this->htmlTableRow($html, null, $evenOrOdd);
|
||||||
|
$body .= $row;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $body;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function listbody($results=null) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param $results Array
|
||||||
|
* @return String Form-Content
|
||||||
|
*/
|
||||||
|
function getResultActionsForm($results) {
|
||||||
|
$ret = "";
|
||||||
|
|
||||||
|
$csvValues = array();
|
||||||
|
foreach($results as $record) {
|
||||||
|
$csvValues[] = $record->ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
$form = new Form(
|
||||||
|
$this,
|
||||||
|
"ExportForm",
|
||||||
|
new FieldSet(
|
||||||
|
new HiddenField("csvIDs","csvIDs",implode(",",$csvValues))
|
||||||
|
),
|
||||||
|
$this->result_actions
|
||||||
|
);
|
||||||
|
|
||||||
|
$ret = <<<HTML
|
||||||
|
<div id="Form_ResultForm">
|
||||||
|
{$form->renderWith("Form")}
|
||||||
|
</div>
|
||||||
|
HTML;
|
||||||
|
|
||||||
|
return $ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param $result
|
||||||
|
* @param $field Mixed can be array: eg: array("FirstName", "Surname"), in which case two fields
|
||||||
|
* in database table should concatenate to one cell for report table.
|
||||||
|
* The field could be "Total->Nice" or "Order.Created->Date", intending to show its specific format.
|
||||||
|
* Caster then is "Nice" "Date" etc.
|
||||||
|
*/
|
||||||
|
protected function buildResultFieldValue($result, $field) {
|
||||||
|
if(is_array($field)) {
|
||||||
|
$i = 0;
|
||||||
|
foreach($field as $each) {
|
||||||
|
$value .= $i == 0 ? "" : "_";
|
||||||
|
$value .= $this->buildResultFieldValue($result, $each);
|
||||||
|
$i++;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
list ($field, $caster) = explode("->", $field);
|
||||||
|
if(preg_match('/^(.+)\.(.+)$/', $field, $matches)) {
|
||||||
|
$field = $matches[2];
|
||||||
|
}
|
||||||
|
|
||||||
|
if($caster) {
|
||||||
|
// When the intending value is Created.Date, the obj need to be casted as Datetime explicitely.
|
||||||
|
if ($field == "Created" || $field == "LastEdited") {
|
||||||
|
$created = Object::create('Datetime', $result->Created, "Created");
|
||||||
|
// $created->setVal();
|
||||||
|
$value = $created->val($caster);
|
||||||
|
} else // Dealing with other field like "Total->Nice", etc.
|
||||||
|
$value = $result->obj($field)->val($caster);
|
||||||
|
} else { // Simple field, no casting
|
||||||
|
$value = $result->val($field);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function htmlTableCell($value, $link = false, $class = "", $id = null) {
|
||||||
|
if($link) {
|
||||||
|
return "<td><a href=\"$link\" class=\"$class\" id=\"$id\">" . htmlentities($value) . "</a></td>";
|
||||||
|
} else {
|
||||||
|
return "<td>" . htmlentities($value) . "</td>";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function htmlTableRow($value, $link = null, $evenOrOdd = null) {
|
||||||
|
if ($link) {
|
||||||
|
return "<tr class=\"$evenOrOdd\"><a href=\"$link\">" . $value . "</a></tr>";
|
||||||
|
} else {
|
||||||
|
return "<tr class=\"$evenOrOdd\">" . $value . "</tr>";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exports a given set of comma-separated IDs (from a previous search-query, stored in a HiddenField).
|
||||||
|
* Uses {$csv_columns} if present, and falls back to {$result_columns}.
|
||||||
|
*/
|
||||||
|
function export() {
|
||||||
|
|
||||||
|
$now = Date("s-i-H");
|
||||||
|
$fileName = "export-$now.csv";
|
||||||
|
|
||||||
|
$csv_columns = ($this->stat('csv_columns')) ? array_values($this->stat('csv_columns')) : array_values($this->stat('result_columns'));
|
||||||
|
|
||||||
|
$fileData = "";
|
||||||
|
$fileData .= "\"" . implode("\";\"",$csv_columns) . "\"";
|
||||||
|
$fileData .= "\n";
|
||||||
|
|
||||||
|
$records = $this->performSearch();
|
||||||
|
if($records) {
|
||||||
|
foreach($records as $record) {
|
||||||
|
$columnData = array();
|
||||||
|
foreach($csv_columns as $column) {
|
||||||
|
$tmpColumnData = "\"" . str_replace("\"", "\"\"", $record->$column) . "\"";
|
||||||
|
$tmpColumnData = str_replace(array("\r", "\n"), "", $tmpColumnData);
|
||||||
|
$columnData[] = $tmpColumnData;
|
||||||
|
}
|
||||||
|
$fileData .= implode(",",$columnData);
|
||||||
|
$fileData .= "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
HTTP::sendFileToBrowser($fileData, $fileName);
|
||||||
|
} else {
|
||||||
|
user_error("No records found", E_USER_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save genetric data handler
|
||||||
|
*
|
||||||
|
* @return String Statusmessage
|
||||||
|
*/
|
||||||
|
function save($urlParams, $form) {
|
||||||
|
|
||||||
|
$className = $this->stat('data_type');
|
||||||
|
|
||||||
|
$id = $_REQUEST['ID'];
|
||||||
|
|
||||||
|
if(substr($id, 0, 3) != 'new') {
|
||||||
|
$generic = DataObject::get_one($className, "`$className`.ID = $id");
|
||||||
|
$generic->Status = "Saved (Update)";
|
||||||
|
} else {
|
||||||
|
$generic = new $className();
|
||||||
|
$generic->Status = "Saved (New)";
|
||||||
|
}
|
||||||
|
|
||||||
|
$form->saveInto($generic, true);
|
||||||
|
$id = $generic->write();
|
||||||
|
|
||||||
|
if($this->stat('data_type_extra')) {
|
||||||
|
foreach($this->stat('data_type_extra') as $oneRelated) {
|
||||||
|
$oneExtra = $generic->$oneRelated();
|
||||||
|
if($_REQUEST[$oneExtra->class]) {
|
||||||
|
foreach($_REQUEST[$oneExtra->class] as $field => $value) {
|
||||||
|
$oneExtra->setField($field, $value);
|
||||||
|
}
|
||||||
|
$oneExtra->write();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FormResponse::status_message('Saved', 'good');
|
||||||
|
FormResponse::update_status($generic->Status);
|
||||||
|
|
||||||
|
if (method_exists($this, "saveAfterCall")) {
|
||||||
|
$this->saveAfterCall($generic, $urlParams, $form);
|
||||||
|
}
|
||||||
|
|
||||||
|
return FormResponse::respond();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show a single record
|
||||||
|
*
|
||||||
|
* @return Array Editing Form
|
||||||
|
*/
|
||||||
|
function show() {
|
||||||
|
|
||||||
|
Session::set('currentPage', $this->urlParams['ID']);
|
||||||
|
|
||||||
|
$editForm = $this->getEditForm($this->urlParams['ID']);
|
||||||
|
|
||||||
|
if(Director::is_ajax()) {
|
||||||
|
return $editForm->formHtmlContent();
|
||||||
|
} else {
|
||||||
|
return array (
|
||||||
|
'EditForm' => $editForm
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a new DataObject
|
||||||
|
*
|
||||||
|
* @return String
|
||||||
|
*/
|
||||||
|
function createRecord() {
|
||||||
|
$baseClass = $this->stat('data_type');
|
||||||
|
$obj = new $baseClass();
|
||||||
|
$obj->write();
|
||||||
|
|
||||||
|
$editForm = $this->getEditForm($obj->ID);
|
||||||
|
|
||||||
|
return (Director::is_ajax()) ? $editForm->formHtmlContent() : array ('EditForm' => $editForm);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete a given Dataobjebt by ID
|
||||||
|
*
|
||||||
|
* @param $urlParams Array
|
||||||
|
* @param $form Form
|
||||||
|
* @return String
|
||||||
|
*/
|
||||||
|
function delete($urlParams, $form) {
|
||||||
|
$id = Convert::raw2sql($_REQUEST['ID']);
|
||||||
|
$obj = DataObject::get_by_id($this->stat('data_type'), $id);
|
||||||
|
if ($obj) {
|
||||||
|
$obj->delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
// clear session data
|
||||||
|
Session::clear('currentPage');
|
||||||
|
|
||||||
|
FormResponse::status_message('Successfully deleted', 'good');
|
||||||
|
FormResponse::add("$('Form_EditForm').deleteEffect();");
|
||||||
|
|
||||||
|
return FormResponse::respond();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getRelatedData() {
|
||||||
|
|
||||||
|
$relatedName = $_REQUEST['RelatedClass'];
|
||||||
|
$id = $_REQUEST[$relatedName]['ID'];
|
||||||
|
$baseClass = $this->stat('data_type');
|
||||||
|
$relatedClasses = singleton($baseClass)->stat('has_one');
|
||||||
|
if($id){
|
||||||
|
$relatedObject = DataObject::get_by_id($relatedClasses[$relatedName], $id);
|
||||||
|
$response .= <<<JS
|
||||||
|
$('$relatedName').unsetNewRelatedKey();
|
||||||
|
JS;
|
||||||
|
}
|
||||||
|
elseif($id !== '0'){ //in case of null;
|
||||||
|
$relatedObject = new $relatedClasses[$relatedName]();
|
||||||
|
if($parentID = $_REQUEST[$relatedName]['ParentID']){
|
||||||
|
$relatedObject->ParentID = $parentID;
|
||||||
|
}
|
||||||
|
$id = $relatedObject->write();
|
||||||
|
$response .= <<<JS
|
||||||
|
$('$relatedName').setNewRelatedKey($id);
|
||||||
|
JS;
|
||||||
|
}else{ // in case of 0
|
||||||
|
$relatedObject = new $relatedClasses[$relatedName]();
|
||||||
|
if($parentID = $_REQUEST[$relatedName]['ParentID']){
|
||||||
|
$relatedObject->ParentID = $parentID;
|
||||||
|
}
|
||||||
|
$response .= <<<JS
|
||||||
|
$('$relatedName').unsetNewRelatedKey();
|
||||||
|
JS;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(Director::is_ajax()) {
|
||||||
|
$fields = $_REQUEST[$relatedName];
|
||||||
|
|
||||||
|
$response .= <<<JS
|
||||||
|
var dataArray = new Array();
|
||||||
|
JS;
|
||||||
|
foreach ($fields as $k => $v) {
|
||||||
|
$JS_newKey = Convert::raw2js($relatedName . '[' . $k . ']');
|
||||||
|
$JS_newValue = Convert::raw2js($relatedObject-> $k);
|
||||||
|
$response .=<<<JS
|
||||||
|
dataArray['$JS_newKey'] = '$JS_newValue';
|
||||||
|
JS;
|
||||||
|
}
|
||||||
|
|
||||||
|
$response .=<<<JS
|
||||||
|
$('$relatedName').updateChildren(dataArray, true);
|
||||||
|
JS;
|
||||||
|
|
||||||
|
FormResponse::add($response);
|
||||||
|
}
|
||||||
|
|
||||||
|
return FormResponse::respond();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function updateRelatedKey() {
|
||||||
|
if(Director::is_ajax()) {
|
||||||
|
$funcName = "get" . $_REQUEST['RelatedClass'] . "Dropdown";
|
||||||
|
$relatedKeyDropdown = singleton($this->stat('data_type'))->$funcName();
|
||||||
|
$relatedKeyDropdown->extraClass = "relatedDataKey";
|
||||||
|
echo $relatedKeyDropdown->FieldHolder();
|
||||||
|
} else {
|
||||||
|
Director::redirectBack();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute a query based on {$filter} and build a DataObjectSet
|
||||||
|
* out of the results.
|
||||||
|
*
|
||||||
|
* @return DataObjectSet
|
||||||
|
*/
|
||||||
|
abstract function performSearch();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Form fields which trigger {getResults} and {peformSearch}.
|
||||||
|
* Provide HTML in the following format to get auto-collapsing "advanced search"-fields.
|
||||||
|
* <div id="BasicSearchFields"></div>
|
||||||
|
* <div class="ToggleAdvancedSearchFields" style="display:none"><a href="#">Show advanced options</a></div>
|
||||||
|
* <div id="AdvancedSearchFields"></div>
|
||||||
|
*
|
||||||
|
* @return FieldSet
|
||||||
|
*/
|
||||||
|
abstract function getSearchFields();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provide custom link.
|
||||||
|
*
|
||||||
|
* @return String
|
||||||
|
*/
|
||||||
|
abstract function getLink();
|
||||||
|
|
||||||
|
//abstract function create();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Legacy
|
||||||
|
*/
|
||||||
|
function AddForm() {
|
||||||
|
return $this->CreationForm();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
66
code/ImprintStats.php
Executable file
@ -0,0 +1,66 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
class ImprintStats extends ViewableData {
|
||||||
|
protected static $imprintID;
|
||||||
|
|
||||||
|
static function setID($id) {
|
||||||
|
ImprintStats::$imprintID = $id;
|
||||||
|
}
|
||||||
|
|
||||||
|
function forTemplate() {
|
||||||
|
$page = $this->CurrentPage();
|
||||||
|
|
||||||
|
if($page){
|
||||||
|
$description = str_replace(array("\n","\r"),"",$page->MetaDescription);
|
||||||
|
|
||||||
|
$title = Convert::raw2js($page->Title);
|
||||||
|
|
||||||
|
$level1 = $page->Level(1);
|
||||||
|
$level2 = $page->Level(2);
|
||||||
|
if($level1->URLSegment == 'modes') {
|
||||||
|
$level1 = $level2;
|
||||||
|
$level2 = $page->Level(3);
|
||||||
|
}
|
||||||
|
if($level2 && $level1->URLSegment == 'home') {
|
||||||
|
$level1 = $level2;
|
||||||
|
$level2 = $page->Level(3);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
$section = $level1->Title;
|
||||||
|
$service = $level2 ? $level2->Title : $section;
|
||||||
|
|
||||||
|
$fullURL = $page->Link();
|
||||||
|
|
||||||
|
$imprintID = ImprintStats::$imprintID;
|
||||||
|
|
||||||
|
return "<!-- Start: Nexsys Imprint Code (Copyright Nexsys Development Ltd 2002-2005) -->
|
||||||
|
<script src=\"cms/javascript/ImprintStats.js\" type=\"text/javascript\"></script>
|
||||||
|
<script type=\"text/javascript\">
|
||||||
|
//<![CDATA[
|
||||||
|
var NI_DESCRIPTION = \"$description\";
|
||||||
|
var NI_SECTION = \"$section\";
|
||||||
|
var NI_SERVICE = \"$service\";
|
||||||
|
var NI_TRIGGER = \"$trigger\";
|
||||||
|
var NI_AMOUNT = \"0\";
|
||||||
|
var NI_ADCAMPAIGN = \"\";
|
||||||
|
var NI_TITLE = \"$title\";
|
||||||
|
var NI_URL = \"$fullURL\";
|
||||||
|
var NI_BASKET_ADD = \"\";
|
||||||
|
var NI_BASKET_REMOVE = \"\";
|
||||||
|
var NI_PARAMETERS = \"\";
|
||||||
|
|
||||||
|
if (typeof(NI_IW) != \"undefined\") ni_TrackHit(\"imprint1.nexsysdev.net\", $imprintID, NI_DESCRIPTION, NI_SECTION, NI_SERVICE, NI_TRIGGER, NI_AMOUNT, NI_ADCAMPAIGN, NI_TITLE, NI_URL, 1, NI_BASKET_ADD, NI_BASKET_REMOVE, NI_PARAMETERS);
|
||||||
|
else document.write('<div style=\"position:absolute;width:1px;height:1px;overflow:hidden\"><img src=\"http://imprint1.nexsysdev.net/Hit.aspx?tv=1&sc=$imprintID&js=1\" width=\"1\" height=\"1\" style=\"border:0\" alt=\"Nexsys Imprint\"/></div>');
|
||||||
|
//]]>
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<noscript>
|
||||||
|
<div style=\"position:absolute;width:1px;height:1px;overflow:hidden\"><a href=\"http://www.nexsysdev.com\"><img src=\"http://imprint1.nexsysdev.net/Hit.aspx?tv=1&sc=55075&lo=&st=&sv=&tr=&ac=&js=0\" width=\"1\" height=\"1\" alt=\"Nexsys Imprint\" /></a></div>
|
||||||
|
</noscript>
|
||||||
|
<!-- End: Nexsys Imprint Code -->";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
761
code/LeftAndMain.php
Normal file
@ -0,0 +1,761 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* LeftAndMain is the parent class of all the two-pane views in the CMS.
|
||||||
|
* If you are wanting to add more areas to the CMS, you can do it by subclassing LeftAndMain.
|
||||||
|
*/
|
||||||
|
abstract class LeftAndMain extends Controller {
|
||||||
|
static $tree_class = null;
|
||||||
|
static $extra_menu_items = array(), $removed_menu_items = array(), $replaced_menu_items = array();
|
||||||
|
static $ForceReload;
|
||||||
|
|
||||||
|
function init() {
|
||||||
|
Director::set_site_mode('cms');
|
||||||
|
|
||||||
|
parent::init();
|
||||||
|
|
||||||
|
// Security check for LeftAndMain sub-class permissions
|
||||||
|
if(!Permission::check("CMS_ACCESS_$this->class")) {
|
||||||
|
// When access /admin/, we should try a redirect to another part of the admin rather than a
|
||||||
|
if($this->class == 'CMSMain') {
|
||||||
|
$menu = $this->MainMenu();
|
||||||
|
if(($first = $menu->First()) && $first->Link) {
|
||||||
|
Director::redirect($first->Link);
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
$messageSet = array(
|
||||||
|
'default' => "Enter your email address and password to access the CMS.",
|
||||||
|
'alreadyLoggedIn' => "I'm sorry, but you can't access that part of the CMS. If you want to log in as someone else, do so below",
|
||||||
|
'logInAgain' => "You have been logged out of the CMS. If you would like to log in again, enter a username and password below.",
|
||||||
|
);
|
||||||
|
|
||||||
|
Security::permissionFailure($this, $messageSet);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
Requirements::javascript("jsparty/prototype.js");
|
||||||
|
Requirements::javascript("jsparty/behaviour.js");
|
||||||
|
Requirements::javascript("jsparty/prototype_improvements.js");
|
||||||
|
Requirements::javascript("jsparty/loader.js");
|
||||||
|
|
||||||
|
Requirements::javascript("jsparty/layout_helpers.js");
|
||||||
|
Requirements::javascript("jsparty/tree/tree.js");
|
||||||
|
Requirements::css("jsparty/tree/tree.css");
|
||||||
|
Requirements::javascript("jsparty/scriptaculous/effects.js");
|
||||||
|
Requirements::javascript("jsparty/scriptaculous/dragdrop.js");
|
||||||
|
|
||||||
|
Requirements::javascript("jsparty/tabstrip/tabstrip.js");
|
||||||
|
Requirements::css("jsparty/tabstrip/tabstrip.css");
|
||||||
|
|
||||||
|
|
||||||
|
Requirements::css("jsparty/greybox/greybox.css");
|
||||||
|
Requirements::javascript("jsparty/greybox/AmiJS.js");
|
||||||
|
Requirements::javascript("jsparty/greybox/greybox.js");
|
||||||
|
|
||||||
|
Requirements::javascript("cms/javascript/LeftAndMain.js");
|
||||||
|
Requirements::javascript("cms/javascript/LeftAndMain_left.js");
|
||||||
|
Requirements::javascript("cms/javascript/LeftAndMain_right.js");
|
||||||
|
|
||||||
|
Requirements::javascript("jsparty/calendar/calendar.js");
|
||||||
|
Requirements::javascript("jsparty/calendar/lang/calendar-en.js");
|
||||||
|
Requirements::javascript("jsparty/calendar/calendar-setup.js");
|
||||||
|
Requirements::css("sapphire/css/CalendarDateField.css");
|
||||||
|
Requirements::css("jsparty/calendar/calendar-win2k-1.css");
|
||||||
|
|
||||||
|
Requirements::javascript('sapphire/javascript/Validator.js');
|
||||||
|
|
||||||
|
Requirements::css("sapphire/css/SubmittedFormReportField.css");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the current user can access the CMS
|
||||||
|
*/
|
||||||
|
function canAccessCMS() {
|
||||||
|
|
||||||
|
$member = Member::currentUser();
|
||||||
|
|
||||||
|
if($member) {
|
||||||
|
if($groups = $member->Groups()) {
|
||||||
|
foreach($groups as $group) if($group->CanCMS) return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the current user has administrative rights in the CMS
|
||||||
|
*/
|
||||||
|
function canAdminCMS() {
|
||||||
|
if($member = Member::currentUser()) return $member->isAdmin();
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------------------//
|
||||||
|
// Main controllers
|
||||||
|
|
||||||
|
/**
|
||||||
|
* You should implement a Link() function in your subclass of LeftAndMain,
|
||||||
|
* to point to the URL of that particular controller.
|
||||||
|
*/
|
||||||
|
abstract public function Link();
|
||||||
|
|
||||||
|
public function show($params) {
|
||||||
|
if($params['ID']) $this->setCurrentPageID($params['ID']);
|
||||||
|
if($params['OtherID'])
|
||||||
|
Session::set('currentMember', $params['OtherID']);
|
||||||
|
|
||||||
|
if(Director::is_ajax()) {
|
||||||
|
SSViewer::setOption('rewriteHashlinks', false);
|
||||||
|
return $this->EditForm()->formHtmlContent();
|
||||||
|
|
||||||
|
} else {
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function getitem() {
|
||||||
|
$this->setCurrentPageID($_REQUEST['ID']);
|
||||||
|
SSViewer::setOption('rewriteHashlinks', false);
|
||||||
|
// Changed 3/11/2006 to not use getLastFormIn because that didn't have _form_action, _form_name, etc.
|
||||||
|
|
||||||
|
$form = $this->EditForm();
|
||||||
|
if($form) return $form->formHtmlContent();
|
||||||
|
else return "";
|
||||||
|
}
|
||||||
|
public function getLastFormIn($html) {
|
||||||
|
$parts = split('</?form[^>]*>', $html);
|
||||||
|
return $parts[sizeof($parts)-2];
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------------------//
|
||||||
|
// Main UI components
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the main menu of the CMS. This is also used by init() to work out which sections the user
|
||||||
|
* has access to.
|
||||||
|
*/
|
||||||
|
public function MainMenu() {
|
||||||
|
// Don't accidentally return a menu if you're not logged in - it's used to determine access.
|
||||||
|
if(!Member::currentUserID()) return new DataObjectSet();
|
||||||
|
|
||||||
|
// Built-in modules
|
||||||
|
|
||||||
|
// array[0]: Name of the icon
|
||||||
|
// array[1]: URL to visi
|
||||||
|
// array[2]: The controller class for this menu, used to check permisssions. If blank, it's assumed that this is public, and always shown to
|
||||||
|
// users who have the rights to access some other part of the admin area.
|
||||||
|
$menuSrc = array(
|
||||||
|
"Site Content" => array("content", "admin/", "CMSMain"),
|
||||||
|
"Files & Images" => array("files", "admin/assets/", "AssetAdmin"),
|
||||||
|
"Newsletters" => array("newsletter", "admin/newsletter/", "NewsletterAdmin"),
|
||||||
|
"Reports" => array("report", "admin/reports/", "ReportAdmin"),
|
||||||
|
"Security" => array("security", "admin/security/", "SecurityAdmin"),
|
||||||
|
"Help" => array("help", "http://userhelp.silverstripe.com"),
|
||||||
|
);
|
||||||
|
|
||||||
|
if(!$this->hasReports()) unset($menuSrc['Reports']);
|
||||||
|
|
||||||
|
// Extra modules
|
||||||
|
if($removed = $this->stat('removed_menu_items')) {
|
||||||
|
foreach($removed as $remove) {
|
||||||
|
foreach($menuSrc as $k => $v) {
|
||||||
|
if($v[0] == $remove) {
|
||||||
|
unset($menuSrc[$k]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// replace menu items
|
||||||
|
if($replaced = $this->stat('replaced_menu_items') ) {
|
||||||
|
|
||||||
|
$newMenu = array();
|
||||||
|
|
||||||
|
reset( $menuSrc );
|
||||||
|
|
||||||
|
for( $i = 0; $i < count( $menuSrc ); $i++ ) {
|
||||||
|
$existing = current($menuSrc);
|
||||||
|
|
||||||
|
if( $replacement = $replaced[$existing[0]] ) {
|
||||||
|
$newMenu = array_merge( $newMenu, $replacement );
|
||||||
|
} else
|
||||||
|
$newMenu = array_merge( $newMenu, array( key( $menuSrc) => current( $menuSrc ) ) );
|
||||||
|
|
||||||
|
next( $menuSrc );
|
||||||
|
}
|
||||||
|
|
||||||
|
reset( $menuSrc );
|
||||||
|
|
||||||
|
$menuSrc = $newMenu;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extra modules
|
||||||
|
if($extra = $this->stat('extra_menu_items')) {
|
||||||
|
foreach($extra as $k => $v) {
|
||||||
|
if(!is_array($v)) $extra[$k] = array($k, $v, 'title' => $k);
|
||||||
|
else $extra[$k]['title'] = $k;
|
||||||
|
}
|
||||||
|
|
||||||
|
array_splice($menuSrc, count($menuSrc)-2, 0, $extra);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encode into DO set
|
||||||
|
$menu = new DataObjectSet();
|
||||||
|
$itemsWithPermission = 0;
|
||||||
|
foreach($menuSrc as $title => $menuItem) {
|
||||||
|
if(is_numeric($title) && isset($menuItem['title'])) $title = $menuItem['title'];
|
||||||
|
if(!isset($menuItem[2]) || Permission::check("CMS_ACCESS_$menuItem[2]")) {
|
||||||
|
// Count up the number of items that have specific permission settings
|
||||||
|
if(isset($menuItem[2])) $itemsWithPermission++;
|
||||||
|
|
||||||
|
$linkingmode = "";
|
||||||
|
if(!(strpos($this->Link(), $menuItem[1]) === false)) {
|
||||||
|
if($menuItem[0] == "content") {
|
||||||
|
if($this->Link() == "admin/")
|
||||||
|
$linkingmode = "current";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
$linkingmode = "current";
|
||||||
|
}
|
||||||
|
|
||||||
|
$menu->push(new ArrayData(array(
|
||||||
|
"Title" => Convert::raw2xml($title),
|
||||||
|
"Code" => $menuItem[0],
|
||||||
|
"Link" => $menuItem[1],
|
||||||
|
"LinkingMode" => $linkingmode
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only return a menu if there is at least one permission-requiring item. Otherwise, the menu will just be the "help" icon.
|
||||||
|
if($itemsWithPermission > 0) return $menu;
|
||||||
|
else return new DataObjectSet();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a list of appropriate templates for this class, with the given suffix
|
||||||
|
*/
|
||||||
|
protected function getTemplatesWithSuffix($suffix) {
|
||||||
|
$classes = array_reverse(ClassInfo::ancestry($this->class));
|
||||||
|
foreach($classes as $class) {
|
||||||
|
$templates[] = $class . $suffix;
|
||||||
|
if($class == 'LeftAndMain') break;
|
||||||
|
}
|
||||||
|
return $templates;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function Left() {
|
||||||
|
return $this->renderWith($this->getTemplatesWithSuffix('_left'));
|
||||||
|
}
|
||||||
|
public function Right() {
|
||||||
|
return $this->renderWith($this->getTemplatesWithSuffix('_right'));
|
||||||
|
}
|
||||||
|
public function RightBottom() {
|
||||||
|
if(SSViewer::hasTemplate($this->getTemplatesWithSuffix('_rightbottom'))) {
|
||||||
|
return $this->renderWith($this->getTemplatesWithSuffix('_rightbottom'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function getRecord($id, $className = null) {
|
||||||
|
if(!$className) $className = $this->stat('tree_class');
|
||||||
|
return DataObject::get_by_id($className, $rootID);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSiteTreeFor($className, $rootID = null) {
|
||||||
|
$obj = $rootID ? $this->getRecord($rootID) : singleton($className);
|
||||||
|
$obj->markPartialTree();
|
||||||
|
if($p = $this->currentPage()) $obj->markToExpose($p);
|
||||||
|
|
||||||
|
// getChildrenAsUL is a flexible and complex way of traversing the tree
|
||||||
|
$siteTree = $obj->getChildrenAsUL("", '
|
||||||
|
"<li id=\"record-$child->ID\" class=\"" . $child->CMSTreeClasses($extraArg) . "\">" .
|
||||||
|
"<a href=\"" . Director::link(substr($extraArg->Link(),0,-1), "show", $child->ID) . "\" " . (($child->canEdit() || $child->canAddChildren()) ? "" : "class=\"disabled\"") . ">" .
|
||||||
|
($child->TreeTitle()) .
|
||||||
|
"</a>"
|
||||||
|
'
|
||||||
|
,$this, true);
|
||||||
|
|
||||||
|
// Wrap the root if needs be.
|
||||||
|
|
||||||
|
if(!$rootID) {
|
||||||
|
$rootLink = $this->Link() . '0';
|
||||||
|
$siteTree = "<ul id=\"sitetree\" class=\"tree unformatted\"><li id=\"record-0\" class=\"Root nodelete\"><a href=\"$rootLink\">Site Content</a>"
|
||||||
|
. $siteTree . "</li></ul>";
|
||||||
|
}
|
||||||
|
|
||||||
|
return $siteTree;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getsubtree() {
|
||||||
|
$results = $this->getSiteTreeFor($this->stat('tree_class'), $_REQUEST['ID']);
|
||||||
|
return substr(trim($results), 4,-5);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allows you to returns a new data object to the tree (subclass of sitetree)
|
||||||
|
* and updates the tree via javascript.
|
||||||
|
*/
|
||||||
|
public function returnItemToUser($p) {
|
||||||
|
if(Director::is_ajax()) {
|
||||||
|
// Prepare the object for insertion.
|
||||||
|
$parentID = (int)$p->ParentID;
|
||||||
|
$id = $p->ID ? $p->ID : "new-$p->class-$p->ParentID";
|
||||||
|
$treeTitle = Convert::raw2js($p->TreeTitle());
|
||||||
|
$hasChildren = is_numeric( $id ) && $p->AllChildren() ? ' unexpanded' : '';
|
||||||
|
|
||||||
|
// Ensure there is definitly a node avaliable. if not, append to the home tree.
|
||||||
|
$response = <<<JS
|
||||||
|
var tree = $('sitetree');
|
||||||
|
var newNode = tree.createTreeNode("$id", "$treeTitle", "{$p->class}{$hasChildren}");
|
||||||
|
node = tree.getTreeNodeByIdx($parentID);
|
||||||
|
if(!node){ node = tree.getTreeNodeByIdx(0); }
|
||||||
|
node.open();
|
||||||
|
node.appendTreeNode(newNode);
|
||||||
|
newNode.selectTreeNode();
|
||||||
|
JS;
|
||||||
|
FormResponse::add($response);
|
||||||
|
}
|
||||||
|
|
||||||
|
return FormResponse::respond();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save page handler
|
||||||
|
*/
|
||||||
|
public function save($urlParams, $form) {
|
||||||
|
$className = $this->stat('tree_class');
|
||||||
|
$result = '';
|
||||||
|
|
||||||
|
$id = $_REQUEST['ID'];
|
||||||
|
if(substr($id,0,3) != 'new') {
|
||||||
|
$record = DataObject::get_one($className, "`$className`.ID = $id");
|
||||||
|
} else {
|
||||||
|
$record = $this->getNewItem($id, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// We don't want to save a new version if there are no changes
|
||||||
|
$dataFields_new = $form->Fields()->dataFields();
|
||||||
|
$dataFields_old = $record->getAllFields();
|
||||||
|
$changed = false;
|
||||||
|
$hasNonRecordFields = false;
|
||||||
|
foreach($dataFields_new as $datafield) {
|
||||||
|
// if the form has fields not belonging to the record
|
||||||
|
if(!isset($dataFields_old[$datafield->Name()])) {
|
||||||
|
$hasNonRecordFields = true;
|
||||||
|
}
|
||||||
|
// if field-values have changed
|
||||||
|
if(!isset($dataFields_old[$datafield->Name()]) || $dataFields_old[$datafield->Name()] != $datafield->dataValue()) {
|
||||||
|
$changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!$changed && !$hasNonRecordFields) {
|
||||||
|
// Tell the user we have saved even though we haven't, as not to confuse them
|
||||||
|
if(is_a($record, "Page")) {
|
||||||
|
$record->Status = "Saved (update)";
|
||||||
|
}
|
||||||
|
FormResponse::status_message("Saved", "good");
|
||||||
|
FormResponse::update_status($record->Status);
|
||||||
|
return FormResponse::respond();
|
||||||
|
}
|
||||||
|
|
||||||
|
$form->dataFieldByName('ID')->Value = 0;
|
||||||
|
|
||||||
|
if(isset($urlParams['Sort']) && is_numeric($urlParams['Sort'])) {
|
||||||
|
$record->Sort = $urlParams['Sort'];
|
||||||
|
}
|
||||||
|
|
||||||
|
// HACK: This should be turned into something more general
|
||||||
|
$originalClass = $record->ClassName;
|
||||||
|
$originalStatus = $record->Status;
|
||||||
|
|
||||||
|
$record->HasBrokenLink = 0;
|
||||||
|
$record->HasBrokenFile = 0;
|
||||||
|
|
||||||
|
$record->writeWithoutVersion();
|
||||||
|
|
||||||
|
// HACK: This should be turned into something more general
|
||||||
|
$originalURLSegment = $record->URLSegment;
|
||||||
|
|
||||||
|
$form->saveInto($record, true);
|
||||||
|
|
||||||
|
if(is_a($record, "Page")) {
|
||||||
|
$record->Status = ($record->Status == "New page" || $record->Status == "Saved (new)") ? "Saved (new)" : "Saved (update)";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// $record->write();
|
||||||
|
|
||||||
|
if(Director::is_ajax()) {
|
||||||
|
if($id != $record->ID) {
|
||||||
|
FormResponse::add("$('sitetree').setNodeIdx(\"$id\", \"$record->ID\");");
|
||||||
|
FormResponse::add("$('Form_EditForm').elements.ID.value = \"$record->ID\";");
|
||||||
|
}
|
||||||
|
|
||||||
|
$title = Convert::raw2js($record->TreeTitle());
|
||||||
|
|
||||||
|
if($added = DataObjectLog::getAdded('SiteTree')) {
|
||||||
|
foreach($added as $page) {
|
||||||
|
if($page->ID != $record->ID) $result .= $this->addTreeNodeJS($page);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if($deleted = DataObjectLog::getDeleted('SiteTree')) {
|
||||||
|
foreach($deleted as $page) {
|
||||||
|
if($page->ID != $record->ID) $result .= $this->deleteTreeNodeJS($page);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if($changed = DataObjectLog::getChanged('SiteTree')) {
|
||||||
|
foreach($changed as $page) {
|
||||||
|
if($page->ID != $record->ID) {
|
||||||
|
$title = Convert::raw2js($page->TreeTitle());
|
||||||
|
FormResponse::add("$('sitetree').setNodeTitle($page->ID, \"$title\")");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FormResponse::add("$('sitetree').setNodeTitle(\"$record->ID\", \"$title\");");
|
||||||
|
$message = "Saved.";
|
||||||
|
|
||||||
|
|
||||||
|
// Update the icon if the class has changed
|
||||||
|
if($originalClass != $record->ClassName) {
|
||||||
|
$record->setClassName( $record->ClassName );
|
||||||
|
$newClass = $record->ClassName;
|
||||||
|
$record = $record->newClassInstance( $newClass );
|
||||||
|
|
||||||
|
FormResponse::add("if(\$('sitetree').setNodeIcon) \$('sitetree').setNodeIcon($record->ID, '$originalClass', '$record->ClassName');");
|
||||||
|
}
|
||||||
|
|
||||||
|
// HACK: This should be turned into somethign more general
|
||||||
|
if( ($record->class == 'VirtualPage' && $originalURLSegment != $record->URLSegment) ||
|
||||||
|
($originalClass != $record->ClassName) || self::$ForceReload == true) {
|
||||||
|
FormResponse::add("$('Form_EditForm').getPageFromServer($record->ID);");
|
||||||
|
}
|
||||||
|
|
||||||
|
if( ($record->class != 'VirtualPage') && $originalURLSegment != $record->URLSegment) {
|
||||||
|
$message .= " Changed URL to '$record->URLSegment'";
|
||||||
|
FormResponse::add("\$('Form_EditForm').elements.URLSegment.value = \"$record->URLSegment\";");
|
||||||
|
FormResponse::add("\$('Form_EditForm_StageURLSegment').value = \"{$record->URLSegment}\";");
|
||||||
|
}
|
||||||
|
|
||||||
|
// After reloading action
|
||||||
|
if($originalStatus != $record->Status) {
|
||||||
|
$message .= " Status changed to '$record->Status'";
|
||||||
|
}
|
||||||
|
|
||||||
|
$record->write();
|
||||||
|
|
||||||
|
$result .= $this->getActionUpdateJS($record);
|
||||||
|
FormResponse::status_message($message, "good");
|
||||||
|
|
||||||
|
FormResponse::update_status($record->Status);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return FormResponse::respond();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a piece of javascript that will update the actions of the main form
|
||||||
|
*/
|
||||||
|
public function getActionUpdateJS($record) {
|
||||||
|
// Get the new action buttons
|
||||||
|
$tempForm = $this->getEditForm($record->ID);
|
||||||
|
$actionList = '';
|
||||||
|
foreach($tempForm->Actions() as $action) {
|
||||||
|
$actionList .= $action->Field() . ' ';
|
||||||
|
}
|
||||||
|
|
||||||
|
FormResponse::add("$('Form_EditForm').loadActionsFromString('" . Convert::raw2js($actionList) . "');");
|
||||||
|
|
||||||
|
return FormResponse::respond();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return JavaScript code to generate a tree node for the given page, if visible
|
||||||
|
*/
|
||||||
|
public function addTreeNodeJS($page, $select = false) {
|
||||||
|
$parentID = (int)$page->ParentID;
|
||||||
|
$title = Convert::raw2js($page->TreeTitle());
|
||||||
|
$response = <<<JS
|
||||||
|
var newNode = $('sitetree').createTreeNode($page->ID, \"$title\", \"$page->class\");
|
||||||
|
var parentNode = $('sitetree').getTreeNodeByIdx($parentID);
|
||||||
|
if(parentNode) parentNode.appendTreeNode(newNode);\n"
|
||||||
|
($selected ? "newNode.selectTreeNode();\n" : "") ;
|
||||||
|
JS;
|
||||||
|
FormResponse::add($response);
|
||||||
|
return FormResponse::respond();
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Return JavaScript code to remove a tree node for the given page, if it exists.
|
||||||
|
*/
|
||||||
|
public function deleteTreeNodeJS($page) {
|
||||||
|
$id = $page->ID ? $page->ID : $page->OldID;
|
||||||
|
$response = <<<JS
|
||||||
|
var node = $('sitetree').getTreeNodeByIdx($id);
|
||||||
|
if(node && node.parentTreeNode) node.parentTreeNode.removeTreeNode(node);
|
||||||
|
$('Form_EditForm').closeIfSetTo($id);
|
||||||
|
JS;
|
||||||
|
FormResponse::add($response);
|
||||||
|
return FormResponse::respond();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a static variable on this class which means the panel will be reloaded.
|
||||||
|
*/
|
||||||
|
static function ForceReload(){
|
||||||
|
self::$ForceReload = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ajax handler for updating the parent of a tree node
|
||||||
|
*/
|
||||||
|
public function ajaxupdateparent() {
|
||||||
|
$id = $_REQUEST['ID'];
|
||||||
|
$parentID = $_REQUEST['ParentID'];
|
||||||
|
if($parentID == 'root'){
|
||||||
|
$parentID = 0;
|
||||||
|
}
|
||||||
|
$_REQUEST['ajax'] = 1;
|
||||||
|
|
||||||
|
if(is_numeric($id) && is_numeric($parentID) && $id != $parentID) {
|
||||||
|
$node = DataObject::get_by_id($this->stat('tree_class'), $id);
|
||||||
|
if($node){
|
||||||
|
$node->ParentID = $parentID;
|
||||||
|
$node->Status = "Saved (update)";
|
||||||
|
$node->write();
|
||||||
|
|
||||||
|
if(is_numeric($_REQUEST['CurrentlyOpenPageID'])) {
|
||||||
|
$currentPage = DataObject::get_by_id($this->stat('tree_class'), $_REQUEST['CurrentlyOpenPageID']);
|
||||||
|
if($currentPage) {
|
||||||
|
$cleanupJS = $currentPage->cmsCleanup_parentChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FormResponse::status_message('saved', 'good');
|
||||||
|
FormResponse::add($cleanupJS);
|
||||||
|
|
||||||
|
}else{
|
||||||
|
FormResponse::status_message("Please Save Page: This page could not be upated because it hasn't been saved yet.","good");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return FormResponse::respond();
|
||||||
|
} else {
|
||||||
|
user_error("Error in ajaxupdateparent request; id=$id, parentID=$parentID", E_USER_ERROR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ajax handler for updating the order of a number of tree nodes
|
||||||
|
* $_GET[ID]: An array of node ids in the correct order
|
||||||
|
* $_GET[MovedNodeID]: The node that actually got moved
|
||||||
|
*/
|
||||||
|
public function ajaxupdatesort() {
|
||||||
|
$className = $this->stat('tree_class');
|
||||||
|
$counter = 0;
|
||||||
|
$js = '';
|
||||||
|
$_REQUEST['ajax'] = 1;
|
||||||
|
|
||||||
|
if(is_array($_REQUEST['ID'])) {
|
||||||
|
$movedNode = DataObject::get_by_id($className, $_REQUEST['MovedNodeID']);
|
||||||
|
|
||||||
|
foreach($_REQUEST['ID'] as $id) {
|
||||||
|
if($id == $movedNode->ID) {
|
||||||
|
$movedNode->Sort = ++$counter;
|
||||||
|
$movedNode->Status = "Saved (update)";
|
||||||
|
$movedNode->write();
|
||||||
|
|
||||||
|
$title = Convert::raw2js($movedNode->TreeTitle());
|
||||||
|
$js .="$('sitetree').setNodeTitle($movedNode->ID, \"$title\")\n";
|
||||||
|
|
||||||
|
// Nodes that weren't "actually moved" shouldn't be registered as having been edited; do a direct SQL update instead
|
||||||
|
} else if(is_numeric($id)) {
|
||||||
|
++$counter;
|
||||||
|
DB::query("UPDATE `$className` SET `Sort` = $counter WHERE `ID` = '$id'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Virtual pages require selected to be null if the page is the same.
|
||||||
|
FormResponse::add(
|
||||||
|
"if( $('sitetree').selected && $('sitetree').selected[0]){
|
||||||
|
var idx = $('sitetree').selected[0].getIdx();
|
||||||
|
if(idx){
|
||||||
|
$('Form_EditForm').getPageFromServer(idx);
|
||||||
|
}
|
||||||
|
}\n" . $js
|
||||||
|
);
|
||||||
|
FormResponse::status_message('saved', 'good');
|
||||||
|
} else {
|
||||||
|
FormResponse::error("Error in request");
|
||||||
|
}
|
||||||
|
|
||||||
|
return FormResponse::respond();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete a number of items
|
||||||
|
*/
|
||||||
|
public function deleteitems() {
|
||||||
|
$ids = split(' *, *', $_REQUEST['csvIDs']);
|
||||||
|
|
||||||
|
$script = "st = \$('sitetree'); \n";
|
||||||
|
foreach($ids as $id) {
|
||||||
|
if(is_numeric($id)) {
|
||||||
|
DataObject::delete_by_id($this->stat('tree_class'), $id);
|
||||||
|
$script .= "node = st.getTreeNodeByIdx($id); if(node) node.parentTreeNode.removeTreeNode(node); $('Form_EditForm').closeIfSetTo($id); \n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
FormResponse::add($script);
|
||||||
|
|
||||||
|
return FormResponse::respond();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function EditForm() {
|
||||||
|
$id = isset($_REQUEST['ID']) ? $_REQUEST['ID'] : $this->currentPageID();
|
||||||
|
if($id) return $this->getEditForm($id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function printable() {
|
||||||
|
$id = $_REQUEST['ID'] ? $_REQUEST['ID'] : $this->currentPageID();
|
||||||
|
|
||||||
|
if($id) $form = $this->getEditForm($id);
|
||||||
|
$form->transform(new PrintableTransformation());
|
||||||
|
$form->actions = null;
|
||||||
|
|
||||||
|
Requirements::clear();
|
||||||
|
Requirements::css('cms/css/LeftAndMain_printable.css');
|
||||||
|
return array(
|
||||||
|
"PrintForm" => $form
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function currentPageID() {
|
||||||
|
if(isset($_REQUEST['ID']) && is_numeric($_REQUEST['ID'])) {
|
||||||
|
return $_REQUEST['ID'];
|
||||||
|
} elseif (is_numeric($this->urlParams['ID'])) {
|
||||||
|
return $this->urlParams['ID'];
|
||||||
|
} elseif(Session::get("{$this->class}.currentPage")) {
|
||||||
|
return Session::get("{$this->class}.currentPage");
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setCurrentPageID($id) {
|
||||||
|
Session::set("{$this->class}.currentPage", $id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function currentPage() {
|
||||||
|
$id = $this->currentPageID();
|
||||||
|
if($id && is_numeric($id)) {
|
||||||
|
return DataObject::get_by_id($this->stat('tree_class'), $id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isCurrentPage(DataObject $page) {
|
||||||
|
return $page->ID == Session::get("{$this->class}.currentPage");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the CMS's HTML-editor toolbar
|
||||||
|
*/
|
||||||
|
public function EditorToolbar() {
|
||||||
|
return new HtmlEditorField_Toolbar($this, "EditorToolbar");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the version number of this application
|
||||||
|
*/
|
||||||
|
public function CMSVersion() {
|
||||||
|
$sapphireVersionFile = file_get_contents('../sapphire/silverstripe_version');
|
||||||
|
$jspartyVersionFile = file_get_contents('../jsparty/silverstripe_version');
|
||||||
|
$cmsVersionFile = file_get_contents('../cms/silverstripe_version');
|
||||||
|
|
||||||
|
if(strstr($sapphireVersionFile, "/sapphire/trunk")) {
|
||||||
|
$sapphireVersion = "trunk";
|
||||||
|
} else {
|
||||||
|
preg_match("/sapphire\/(?:(?:branches)|(?:tags))(?:\/rc)?\/([A-Za-z0-9._-]+)\/silverstripe_version/", $sapphireVersionFile, $matches);
|
||||||
|
$sapphireVersion = $matches[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
if(strstr($jspartyVersionFile, "/jsparty/trunk")) {
|
||||||
|
$jspartyVersion = "trunk";
|
||||||
|
} else {
|
||||||
|
preg_match("/jsparty\/(?:(?:branches)|(?:tags))(?:\/rc)?\/([A-Za-z0-9._-]+)\/silverstripe_version/", $jspartyVersionFile, $matches);
|
||||||
|
$jspartyVersion = $matches[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
if(strstr($cmsVersionFile, "/cms/trunk")) {
|
||||||
|
$cmsVersion = "trunk";
|
||||||
|
} else {
|
||||||
|
preg_match("/cms\/(?:(?:branches)|(?:tags))(?:\/rc)?\/([A-Za-z0-9._-]+)\/silverstripe_version/", $cmsVersionFile, $matches);
|
||||||
|
$cmsVersion = $matches[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
if($sapphireVersion == $jspartyVersion && $jspartyVersion == $cmsVersion) {
|
||||||
|
return $sapphireVersion;
|
||||||
|
} else {
|
||||||
|
return "cms: $cmsVersion, sapphire: $sapphireVersion, jsparty: $jspartyVersion";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The application name is customisable by calling
|
||||||
|
* LeftAndMain::setApplicationName("Something New")
|
||||||
|
*/
|
||||||
|
static $application_name = "SilverStripe CMS", $application_logo_text = "SilverStripe CMS";
|
||||||
|
static function setApplicationName($name, $logoText = null) {
|
||||||
|
self::$application_name = $name;
|
||||||
|
self::$application_logo_text = $logoText ? $logoText : $name;
|
||||||
|
}
|
||||||
|
function ApplicationName() {
|
||||||
|
return self::$application_name;
|
||||||
|
}
|
||||||
|
function ApplicationLogoText() {
|
||||||
|
return self::$application_logo_text;
|
||||||
|
}
|
||||||
|
|
||||||
|
static $application_logo = "cms/images/mainmenu/logo.gif", $application_logo_style = "";
|
||||||
|
|
||||||
|
static function setLogo($logo, $logoStyle) {
|
||||||
|
self::$application_logo = $logo;
|
||||||
|
self::$application_logo_style = $logoStyle;
|
||||||
|
self::$application_logo_text = "";
|
||||||
|
}
|
||||||
|
function LogoStyle() {
|
||||||
|
return "background-image: url(" . self::$application_logo . "); " . self::$application_logo_style;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if we have reports and need to display the Reports main menu item
|
||||||
|
*
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
function hasReports() {
|
||||||
|
$subclasses = ClassInfo::subclassesFor('Report');
|
||||||
|
foreach($subclasses as $class){
|
||||||
|
if($class != 'Report') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the base directory of the tiny_mce codebase
|
||||||
|
*/
|
||||||
|
function MceRoot() {
|
||||||
|
return MCE_ROOT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
271
code/MemberList.php
Executable file
@ -0,0 +1,271 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
class MemberList extends FormField {
|
||||||
|
protected $members;
|
||||||
|
protected $hidePassword;
|
||||||
|
protected $pageSize;
|
||||||
|
|
||||||
|
function __construct($name, $group, $members = null, $hidePassword = null, $pageLimit = 10) {
|
||||||
|
if($group) {
|
||||||
|
if(is_object($group)) $this->group = $group;
|
||||||
|
else $this->group = DataObject::get_by_id('Group',$group);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->pageSize = $pageLimit;
|
||||||
|
|
||||||
|
if($members) $this->members = $this->memberListWithGroupID($members, $group);
|
||||||
|
|
||||||
|
$this->hidePassword = $hidePassword;
|
||||||
|
|
||||||
|
parent::__construct(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
function memberListWithGroupID($members, $group) {
|
||||||
|
$newMembers = new DataObjectSet();
|
||||||
|
foreach($members as $member) {
|
||||||
|
$newMembers->push($member->customise(array("GroupID" => $group->ID)));
|
||||||
|
}
|
||||||
|
return $newMembers;
|
||||||
|
}
|
||||||
|
|
||||||
|
function FieldHolder() {
|
||||||
|
return $this->renderWith("MemberList");
|
||||||
|
}
|
||||||
|
function setGroup($group) {
|
||||||
|
$this->group = $group;
|
||||||
|
}
|
||||||
|
function setController($controller) {
|
||||||
|
$this->controller = $controller;
|
||||||
|
}
|
||||||
|
|
||||||
|
function GroupID() {
|
||||||
|
if( $this->group )
|
||||||
|
return $this->group->ID;
|
||||||
|
|
||||||
|
return '0';
|
||||||
|
}
|
||||||
|
|
||||||
|
function GetControllerName() {
|
||||||
|
return $this->controller->class;
|
||||||
|
}
|
||||||
|
function Members() {
|
||||||
|
/*if($this->members)
|
||||||
|
$members = $this->members;
|
||||||
|
else if($this->group) {
|
||||||
|
$members = $this->group->Members( $this->pageSize, 0 );
|
||||||
|
$allMembers = $this->group->Members();
|
||||||
|
|
||||||
|
if( $allMembers )
|
||||||
|
$total = $allMembers->Count();
|
||||||
|
} else
|
||||||
|
return null;*/
|
||||||
|
|
||||||
|
if( $this->members )
|
||||||
|
return $this->members;
|
||||||
|
|
||||||
|
if( !$baseGroup )
|
||||||
|
$baseGroup = $this->group->ID;
|
||||||
|
|
||||||
|
// Debug::message( $_REQUEST['MemberListOrderByField'] );
|
||||||
|
|
||||||
|
// construct the filter and sort
|
||||||
|
|
||||||
|
if( $_REQUEST['MemberListOrderByField'] )
|
||||||
|
$sort = "`" . $_REQUEST['MemberListOrderByField'] . "`" . addslashes( $_REQUEST['MemberListOrderByOrder'] );
|
||||||
|
|
||||||
|
$whereClauses = array();
|
||||||
|
|
||||||
|
$search = addslashes( $_REQUEST['MemberListSearch'] );
|
||||||
|
|
||||||
|
if( is_numeric( $_REQUEST['MemberListStart'] ) )
|
||||||
|
$limitClause = ( $_REQUEST['MemberListStart'] ) . ", {$this->pageSize}";
|
||||||
|
else
|
||||||
|
$limitClause = "0, {$this->pageSize}";
|
||||||
|
|
||||||
|
|
||||||
|
if( !empty($_REQUEST['MemberListSearch']) )
|
||||||
|
$whereClauses[] = "( `Email`='$search' OR `FirstName`='$search' OR `Surname`='$search' )";
|
||||||
|
|
||||||
|
if( is_numeric( $_REQUEST['MemberListBaseGroup'] ) )
|
||||||
|
$baseGroup = $_REQUEST['MemberListBaseGroup'];
|
||||||
|
|
||||||
|
$whereClauses[] = "`GroupID`='".$baseGroup."'";
|
||||||
|
$join = "INNER JOIN `Group_Members` ON `MemberID`=`Member`.`ID`";
|
||||||
|
|
||||||
|
// $_REQUEST['showqueries'] = 1;
|
||||||
|
|
||||||
|
$members = DataObject::get('Member', implode( ' AND ', $whereClauses ), $sort, $join, $limitClause );
|
||||||
|
|
||||||
|
// $_REQUEST['showqueries'] = 0;
|
||||||
|
|
||||||
|
if( is_numeric( $_REQUEST['MemberListGroup'] ) ) {
|
||||||
|
$baseMembers = new DataObjectSet();
|
||||||
|
|
||||||
|
if( $members )
|
||||||
|
foreach( $members as $member )
|
||||||
|
if( $member->inGroup( $_REQUEST['MemberListGroup'] ) )
|
||||||
|
$baseMembers->push( $member );
|
||||||
|
} else
|
||||||
|
$baseMembers = $members;
|
||||||
|
|
||||||
|
if($members){
|
||||||
|
$members->setPageLimits( $_REQUEST['MemberListStart'], $this->pageSize, $total );
|
||||||
|
|
||||||
|
$this->members = $this->memberListWithGroupID($members, $this->group);
|
||||||
|
}
|
||||||
|
return $this->members;
|
||||||
|
}
|
||||||
|
|
||||||
|
function FirstLink() {
|
||||||
|
if( !$_REQUEST['MemberListStart'] )
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return "admin/security/listmembers?MemberListStart=0{$this->filterString()}";
|
||||||
|
}
|
||||||
|
|
||||||
|
function filterString() {
|
||||||
|
|
||||||
|
foreach( $_REQUEST as $key => $value ) {
|
||||||
|
if( strpos( $key, 'MemberList' ) === 0 && $key != 'MemberListStart' )
|
||||||
|
$filterString .= "&$key=$value";
|
||||||
|
}
|
||||||
|
|
||||||
|
if( !$_REQUEST['MemberListBaseGroup'] )
|
||||||
|
$filterString .= '&MemberListBaseGroup=' . $this->group->ID;
|
||||||
|
|
||||||
|
return $filterString;
|
||||||
|
}
|
||||||
|
|
||||||
|
function MemberListStart() {
|
||||||
|
return $_REQUEST['MemberListStart'];
|
||||||
|
}
|
||||||
|
|
||||||
|
function PrevLink() {
|
||||||
|
if( !$_REQUEST['MemberListStart'] )
|
||||||
|
return null;
|
||||||
|
|
||||||
|
$prevStart = $_REQUEST['MemberListStart'] - $this->pageSize;
|
||||||
|
|
||||||
|
if( $prevStart < 0 )
|
||||||
|
$prevStart = 0;
|
||||||
|
|
||||||
|
return "admin/security/listmembers?MemberListStart=$prevStart{$this->filterString()}";
|
||||||
|
}
|
||||||
|
|
||||||
|
function NextLink() {
|
||||||
|
$total = $this->TotalMembers();
|
||||||
|
|
||||||
|
$lastStart = $total - ( $total % $this->pageSize );
|
||||||
|
|
||||||
|
if( $_REQUEST['MemberListStart'] >= $lastStart )
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return "admin/security/listmembers?MemberListStart={$this->pageSize}{$this->filterString()}";
|
||||||
|
}
|
||||||
|
|
||||||
|
function LastLink() {
|
||||||
|
|
||||||
|
$total = $this->TotalMembers();
|
||||||
|
|
||||||
|
$lastStart = $total - ( $total % $this->pageSize );
|
||||||
|
|
||||||
|
if( $_REQUEST['MemberListStart'] >= $lastStart )
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return "admin/security/listmembers?MemberListStart=$lastStart{$this->filterString()}";
|
||||||
|
}
|
||||||
|
|
||||||
|
function PageSize() {
|
||||||
|
return $this->pageSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
function FirstMember() {
|
||||||
|
return $_REQUEST['MemberListStart'] + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function LastMember() {
|
||||||
|
return $_REQUEST['MemberListStart'] + min( $_REQUEST['MemberListStart'] + $this->pageSize, $this->TotalMembers() - $_REQUEST['MemberListStart'] );
|
||||||
|
}
|
||||||
|
|
||||||
|
function TotalMembers() {
|
||||||
|
if($this->group) $members = $this->group->Members();
|
||||||
|
|
||||||
|
if( !$members )
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return $members->TotalItems();
|
||||||
|
}
|
||||||
|
|
||||||
|
function DontShowPassword(){
|
||||||
|
if( $this->hidePassword )
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return $this->controller->class=='CMSMain'||$this->controller->class=='NewsletterAdmin';
|
||||||
|
}
|
||||||
|
|
||||||
|
function AddRecordForm() {
|
||||||
|
if($this->DontShowPassword())
|
||||||
|
{
|
||||||
|
return new TabularStyle(new Form($this->controller,'AddRecordForm',
|
||||||
|
new FieldSet(
|
||||||
|
new TextField("FirstName", "First Name"),
|
||||||
|
new TextField("Surname", "Surname"),
|
||||||
|
new TextField("Email", "Email"),
|
||||||
|
new HiddenField("GroupID", null, $this->group->ID)
|
||||||
|
),
|
||||||
|
new FieldSet(
|
||||||
|
new FormAction("addmember", "Add")
|
||||||
|
)
|
||||||
|
));
|
||||||
|
|
||||||
|
} else {
|
||||||
|
return new TabularStyle(new Form($this->controller,'AddRecordForm',
|
||||||
|
new FieldSet(
|
||||||
|
new TextField("FirstName", "First Name"),
|
||||||
|
new TextField("Surname", "Surname"),
|
||||||
|
new TextField("Email", "Email"),
|
||||||
|
new TextField("Password", "Password"),
|
||||||
|
new HiddenField("GroupID", null, $this->group->ID)
|
||||||
|
),
|
||||||
|
new FieldSet(
|
||||||
|
new FormAction("addmember", "Add")
|
||||||
|
)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function SearchField() {
|
||||||
|
$field = new TextField( 'MemberListSearch', 'Search' );
|
||||||
|
return $field->FieldHolder();
|
||||||
|
}
|
||||||
|
|
||||||
|
function OrderByField() {
|
||||||
|
$fields = new FieldGroup( new DropdownField('MemberListOrderByField','', array(
|
||||||
|
'FirstName' => 'FirstName',
|
||||||
|
'Surname' => 'Surname',
|
||||||
|
'Email' => 'Email'
|
||||||
|
)),
|
||||||
|
new DropdownField('MemberListOrderByOrder','',array(
|
||||||
|
'ASC' => 'Ascending',
|
||||||
|
'DESC' => 'Descending'
|
||||||
|
)));
|
||||||
|
|
||||||
|
$field = new FieldGroup( new LabelField( 'Order by' ), $fields );
|
||||||
|
return $field->FieldHolder();
|
||||||
|
}
|
||||||
|
|
||||||
|
function GroupFilter() {
|
||||||
|
|
||||||
|
$groups = DataObject::get('Group');
|
||||||
|
|
||||||
|
$groupArray = array( '' => 'Any group' );
|
||||||
|
|
||||||
|
foreach( $groups as $group )
|
||||||
|
$groupArray[$group->ID] = $group->Title;
|
||||||
|
|
||||||
|
$field = new DropdownField('MemberListGroup','Filter by group',$groupArray );
|
||||||
|
return $field->FieldHolder();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
412
code/MemberTableField.php
Executable file
@ -0,0 +1,412 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Enhances {ComplexTableField} with the ability to list groups and given members.
|
||||||
|
* It is based around groups, so it deletes Members from a Group rather than from the entire system.
|
||||||
|
*
|
||||||
|
* In contrast to the original implementation, the URL-parameters "ParentClass" and "ParentID" are used
|
||||||
|
* to specify "Group" (hardcoded) and the GroupID-relation.
|
||||||
|
*
|
||||||
|
* Returns either:
|
||||||
|
* - provided members
|
||||||
|
* - members of a provided group
|
||||||
|
* - all members
|
||||||
|
* - members based on a search-query
|
||||||
|
*/
|
||||||
|
class MemberTableField extends ComplexTableField {
|
||||||
|
protected $members;
|
||||||
|
protected $hidePassword;
|
||||||
|
protected $pageSize;
|
||||||
|
protected $detailFormValidator;
|
||||||
|
protected $group;
|
||||||
|
|
||||||
|
protected $template = "MemberTableField";
|
||||||
|
|
||||||
|
static $data_class = "Member";
|
||||||
|
|
||||||
|
protected $permissions = array(
|
||||||
|
"add",
|
||||||
|
"edit",
|
||||||
|
"delete"
|
||||||
|
);
|
||||||
|
|
||||||
|
private static $addedPermissions = array();
|
||||||
|
|
||||||
|
private static $addedFields = array();
|
||||||
|
|
||||||
|
private static $addedCsvFields = array();
|
||||||
|
|
||||||
|
public static function addPermissions( $addingPermissionList ) {
|
||||||
|
self::$addedPermissions = $addingPermissionList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function addMembershipFields( $addingFieldList, $addingCsvFieldList = null ) {
|
||||||
|
self::$addedFields = $addingFieldList;
|
||||||
|
$addingCsvFieldList == null ? self::$addedCsvFields = $addingFieldList : self::$addedCsvFields = $addingCsvFieldList;
|
||||||
|
}
|
||||||
|
|
||||||
|
function __construct($controller, $name, $group, $members = null, $hidePassword = true, $pageLimit = 10) {
|
||||||
|
|
||||||
|
if($group) {
|
||||||
|
if(is_object($group)) {
|
||||||
|
$this->group = $group;
|
||||||
|
} else if(is_numeric($group)){
|
||||||
|
$this->group = DataObject::get_by_id('Group',$group);
|
||||||
|
}
|
||||||
|
} else if(is_numeric($_REQUEST['ctf'][$this->Name()]["ID"])) {
|
||||||
|
$this->group = DataObject::get_by_id('Group',$_REQUEST['ctf'][$this->Name()]["ID"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
$sourceClass = $this->stat("data_class");
|
||||||
|
|
||||||
|
foreach( self::$addedPermissions as $permission )
|
||||||
|
array_push( $this->permissions, $permission );
|
||||||
|
|
||||||
|
$fieldList = array(
|
||||||
|
"FirstName" => "Firstname",
|
||||||
|
"Surname" => "Surname",
|
||||||
|
"Email" => "Email"
|
||||||
|
);
|
||||||
|
|
||||||
|
$csvFieldList = $fieldList;
|
||||||
|
foreach( self::$addedCsvFields as $key => $value ) {
|
||||||
|
$csvFieldList[$key] = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach( self::$addedFields as $key => $value ) {
|
||||||
|
$fieldList[$key] = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!$hidePassword) {
|
||||||
|
$fieldList["Password"] = "Password";
|
||||||
|
}
|
||||||
|
|
||||||
|
if(isset($_REQUEST['ctf']['childID']) && $memberID = $_REQUEST['ctf']['childID']) {
|
||||||
|
$SNG_member = DataObject::get_by_id($this->stat("data_class"),$_REQUEST['ctf']['childID']);
|
||||||
|
} else {
|
||||||
|
$SNG_member = singleton(Object::getCustomClass($this->stat("data_class")));
|
||||||
|
}
|
||||||
|
$detailFormFields = $SNG_member->getCMSFields();
|
||||||
|
$this->detailFormValidator = $SNG_member->getValidator();
|
||||||
|
|
||||||
|
$this->pageSize = $pageLimit;
|
||||||
|
|
||||||
|
// Legacy: Use setCustomSourceItems() instead.
|
||||||
|
if($members) {
|
||||||
|
$this->customSourceItems = $this->memberListWithGroupID($members, $group);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->hidePassword = $hidePassword;
|
||||||
|
|
||||||
|
parent::__construct($controller, $name, $sourceClass, $fieldList, $detailFormFields);
|
||||||
|
|
||||||
|
Requirements::javascript("cms/javascript/MemberTableField.js");
|
||||||
|
|
||||||
|
// construct the filter and sort
|
||||||
|
if(isset($_REQUEST['MemberOrderByField'])) {
|
||||||
|
$this->sourceSort = "`" . Convert::raw2sql($_REQUEST['MemberOrderByField']) . "`" . Convert::raw2sql( $_REQUEST['MemberOrderByOrder'] );
|
||||||
|
}
|
||||||
|
|
||||||
|
// search
|
||||||
|
$search = isset($_REQUEST['MemberSearch']) ? Convert::raw2sql($_REQUEST['MemberSearch']) : null;
|
||||||
|
if(!empty($_REQUEST['MemberSearch'])) {
|
||||||
|
//$this->sourceFilter[] = "( `Email` LIKE '%$search%' OR `FirstName` LIKE '%$search%' OR `Surname` LIKE '%$search%' )";
|
||||||
|
$sourceF = "( ";
|
||||||
|
foreach( $fieldList as $k => $v )
|
||||||
|
$sourceF .= "`$k` LIKE '%$search%' OR ";
|
||||||
|
$this->sourceFilter[] = substr( $sourceF, 0, -3 ) . ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
// filter by groups
|
||||||
|
// TODO Not implemented yet
|
||||||
|
if(isset($_REQUEST['ctf'][$this->Name()]['GroupID']) && is_numeric($_REQUEST['ctf'][$this->Name()]['GroupID'])) {
|
||||||
|
$this->sourceFilter[] = "`GroupID`='{$_REQUEST['ctf'][$this->Name()]['GroupID']}'";
|
||||||
|
} elseif($this->group) {
|
||||||
|
//$this->sourceFilter[] = "`GroupID`='{$this->group->ID}'";
|
||||||
|
// If the table is not clean (without duplication), the total and navigation wil not work well, so uncheck the big line below
|
||||||
|
$this->sourceFilter[] = "`Group_Members`.`ID` IN (SELECT `ID` FROM `Group_Members` WHERE `GroupID`='{$this->group->ID}' GROUP BY `MemberID` HAVING MIN(`ID`))";
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->sourceJoin = " INNER JOIN `Group_Members` ON `MemberID`=`Member`.`ID`";
|
||||||
|
|
||||||
|
$this->setFieldListCsv( $csvFieldList );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overridden functions
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
function sourceID() {
|
||||||
|
return $this->group->ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
function AddLink() {
|
||||||
|
return "{$this->PopupBaseLink()}&methodName=add";
|
||||||
|
}
|
||||||
|
|
||||||
|
function DetailForm() {
|
||||||
|
$ID = Convert::raw2xml($_REQUEST['ctf']['ID']);
|
||||||
|
$childID = isset($_REQUEST['ctf']['childID']) ? Convert::raw2xml($_REQUEST['ctf']['childID']) : 0;
|
||||||
|
$childClass = Convert::raw2xml($_REQUEST['fieldName']);
|
||||||
|
$methodName = isset($_REQUEST['methodName']) ? $_REQUEST['methodName'] : '';
|
||||||
|
|
||||||
|
if($methodName == "add") {
|
||||||
|
$parentIdName = $this->getParentIdName($childClass,$this->getParentClass());
|
||||||
|
if(!$parentIdName) {
|
||||||
|
user_error("ComplexTableField::DetailForm() Dataobject does not seem to have an 'has-one'-relationship", E_USER_WARNING);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$this->detailFormFields->push(new HiddenField('parentClass'," ",$this->getParentClass()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// the ID field confuses the Controller-logic in finding the right view for ReferencedField
|
||||||
|
$this->detailFormFields->removeByName('ID');
|
||||||
|
|
||||||
|
$this->detailFormFields->push(new HiddenField("ctf[ID]"," ",$ID));
|
||||||
|
// add a namespaced ID instead thats "converted" by saveComplexTableField()
|
||||||
|
$this->detailFormFields->push(new HiddenField("ctf[childID]","",$childID));
|
||||||
|
$this->detailFormFields->push(new HiddenField("ClassName","",$this->sourceClass));
|
||||||
|
|
||||||
|
$form = new MemberTableField_Popup($this, "DetailForm", $this->detailFormFields, $this->sourceClass, $methodName == "show", $this->detailFormValidator);
|
||||||
|
|
||||||
|
if (is_numeric($childID)) {
|
||||||
|
if ($methodName == "show" || $methodName == "edit") {
|
||||||
|
$childData = DataObject::get_by_id($this->sourceClass, $childID);
|
||||||
|
$form->loadDataFrom($childData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($methodName == "show") {
|
||||||
|
$form->makeReadonly();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $form;
|
||||||
|
}
|
||||||
|
|
||||||
|
function SearchForm() {
|
||||||
|
$searchFields = new FieldGroup(
|
||||||
|
new TextField('MemberSearch', 'Search'),
|
||||||
|
new HiddenField("ctf[ID]",'',$this->group->ID),
|
||||||
|
new HiddenField('MemberFieldName','',$this->name),
|
||||||
|
new HiddenField('MemberDontShowPassword','',$this->hidePassword)
|
||||||
|
);
|
||||||
|
|
||||||
|
$orderByFields = new FieldGroup(
|
||||||
|
new LabelField('Order by'),
|
||||||
|
new FieldSet(
|
||||||
|
new DropdownField('MemberOrderByField','', array(
|
||||||
|
'FirstName' => 'FirstName',
|
||||||
|
'Surname' => 'Surname',
|
||||||
|
'Email' => 'Email'
|
||||||
|
)),
|
||||||
|
new DropdownField('MemberOrderByOrder','',array(
|
||||||
|
'ASC' => 'Ascending',
|
||||||
|
'DESC' => 'Descending'
|
||||||
|
))
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
$groups = DataObject::get('Group');
|
||||||
|
$groupArray = array('' => 'Any group');
|
||||||
|
foreach( $groups as $group ) {
|
||||||
|
$groupArray[$group->ID] = $group->Title;
|
||||||
|
}
|
||||||
|
$groupFields = new DropdownField('MemberGroup','Filter by group',$groupArray );
|
||||||
|
|
||||||
|
$actionFields = new LiteralField('MemberFilterButton','<input type="submit" name="MemberFilterButton" value="Filter" id="MemberFilterButton"/>');
|
||||||
|
|
||||||
|
$fieldContainer = new FieldGroup(
|
||||||
|
$searchFields,
|
||||||
|
// $orderByFields,
|
||||||
|
// $groupFields,
|
||||||
|
$actionFields
|
||||||
|
);
|
||||||
|
|
||||||
|
return $fieldContainer->FieldHolder();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add existing member to group rather than creating a new member
|
||||||
|
*/
|
||||||
|
function addtogroup() {
|
||||||
|
$data = $_REQUEST;
|
||||||
|
unset($data['ID']);
|
||||||
|
|
||||||
|
if(!is_numeric($data['ctf']['ID'])) {
|
||||||
|
FormResponse::status_messsage('Adding failed', 'bad');
|
||||||
|
}
|
||||||
|
|
||||||
|
$className = $this->stat('data_class');
|
||||||
|
$record = new $className();
|
||||||
|
|
||||||
|
$record->update($data);
|
||||||
|
$record->write();
|
||||||
|
|
||||||
|
// To Avoid duplication in the Group_Members table if the ComponentSet.php is not modified just uncomment le line below
|
||||||
|
|
||||||
|
//if( ! $record->isInGroup( $data['ctf']['ID'] ) )
|
||||||
|
$record->Groups()->add( $data['ctf']['ID'] );
|
||||||
|
|
||||||
|
$this->sourceItems();
|
||||||
|
|
||||||
|
// TODO add javascript to highlight added row (problem: might not show up due to sorting/filtering)
|
||||||
|
FormResponse::update_dom_id($this->id(), $this->renderWith($this->template), true);
|
||||||
|
FormResponse::status_message('Added member to group', 'good');
|
||||||
|
|
||||||
|
return FormResponse::respond();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Custom delete implementation:
|
||||||
|
* Remove member from group rather than from the database
|
||||||
|
*/
|
||||||
|
function delete() {
|
||||||
|
$groupID = Convert::raw2sql($_REQUEST["ctf"]["ID"]);
|
||||||
|
$memberID = Convert::raw2sql($_REQUEST["ctf"]["childID"]);
|
||||||
|
if(is_numeric($groupID) && is_numeric($memberID)) {
|
||||||
|
$member = DataObject::get_by_id('Member', $memberID);
|
||||||
|
$member->Groups()->remove($groupID);
|
||||||
|
} else {
|
||||||
|
user_error("MemberTableField::delete: Bad parameters: Group=$groupID, Member=$memberID", E_USER_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
return FormResponse::respond();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* #################################
|
||||||
|
* Utility Functions
|
||||||
|
* #################################
|
||||||
|
*/
|
||||||
|
function getParentClass() {
|
||||||
|
return "Group";
|
||||||
|
}
|
||||||
|
|
||||||
|
function getParentIdName($childClass,$parentClass){
|
||||||
|
return "GroupID";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* #################################
|
||||||
|
* Custom Functions
|
||||||
|
* #################################
|
||||||
|
*/
|
||||||
|
function memberListWithGroupID($members, $group) {
|
||||||
|
$newMembers = new DataObjectSet();
|
||||||
|
foreach($members as $member) {
|
||||||
|
$newMembers->push($member->customise(array("GroupID" => $group->ID)));
|
||||||
|
}
|
||||||
|
return $newMembers;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setGroup($group) {
|
||||||
|
$this->group = $group;
|
||||||
|
}
|
||||||
|
function setController($controller) {
|
||||||
|
$this->controller = $controller;
|
||||||
|
}
|
||||||
|
|
||||||
|
function GetControllerName() {
|
||||||
|
return $this->controller->class;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add existing member to group by name (with JS-autocompletion)
|
||||||
|
*/
|
||||||
|
function AddRecordForm() {
|
||||||
|
$fields = new FieldSet();
|
||||||
|
foreach($this->FieldList() as $fieldName=>$fieldTitle) {
|
||||||
|
$fields->push(new TextField($fieldName));
|
||||||
|
}
|
||||||
|
$fields->push(new HiddenField("ctf[ID]", null, $this->group->ID));
|
||||||
|
|
||||||
|
return new TabularStyle(new Form($this->controller,'AddRecordForm',
|
||||||
|
$fields,
|
||||||
|
new FieldSet(
|
||||||
|
new FormAction("addtogroup", "Add")
|
||||||
|
)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cached version for getting the appropraite members for this particular group.
|
||||||
|
*
|
||||||
|
* This includes getting inherited groups, such as groups under groups.
|
||||||
|
*/
|
||||||
|
function sourceItems(){
|
||||||
|
// Caching.
|
||||||
|
if($this->sourceItems) {
|
||||||
|
return $this->sourceItems;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup limits
|
||||||
|
$limitClause = "";
|
||||||
|
if(isset($_REQUEST['ctf'][$this->Name()]['start']) && is_numeric($_REQUEST['ctf'][$this->Name()]['start'])) {
|
||||||
|
$limitClause = ($_REQUEST['ctf'][$this->Name()]['start']) . ", {$this->pageSize}";
|
||||||
|
} else {
|
||||||
|
$limitClause = "0, {$this->pageSize}";
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// We use the group to get the members, as they already have the bulk of the look up functions
|
||||||
|
$start = isset($_REQUEST['ctf'][$this->Name()]['start']) ? $_REQUEST['ctf'][$this->Name()]['start'] : 0;
|
||||||
|
$this->sourceItems = $this->group->Members(
|
||||||
|
$this->pageSize, // limit
|
||||||
|
$start, // offset
|
||||||
|
$this->sourceFilter,
|
||||||
|
$this->sourceSort
|
||||||
|
);
|
||||||
|
$this->unpagedSourceItems = $this->group->Members( "", "", $this->sourceFilter, $this->sourceSort );
|
||||||
|
$this->totalCount = ($this->sourceItems) ? $this->sourceItems->TotalItems() : 0;
|
||||||
|
return $this->sourceItems;
|
||||||
|
}
|
||||||
|
|
||||||
|
function TotalCount() {
|
||||||
|
$this->sourceItems(); // Called for its side-effect of setting total count
|
||||||
|
return $this->totalCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class MemberTableField_Popup extends ComplexTableField_Popup {
|
||||||
|
function __construct($controller, $name, $fields, $sourceClass, $readonly=false, $validator = null) {
|
||||||
|
|
||||||
|
// DO NOT CHANGE THE ORDER OF THESE JS FILES. THESE ARE ONLY REQUIRED FOR THIS INSTANCE !!!11onetwo
|
||||||
|
|
||||||
|
parent::__construct($controller, $name, $fields, $sourceClass, $readonly, $validator);
|
||||||
|
|
||||||
|
Requirements::javascript("cms/javascript/MemberTableField.js");
|
||||||
|
Requirements::javascript("cms/javascript/MemberTableField_popup.js");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function saveComplexTableField() {
|
||||||
|
$id = Convert::raw2sql($_REQUEST['ctf']['childID']);
|
||||||
|
|
||||||
|
if (is_numeric($id)) {
|
||||||
|
$childObject = DataObject::get_by_id($this->sourceClass, $id);
|
||||||
|
} else {
|
||||||
|
$childObject = new $this->sourceClass();
|
||||||
|
}
|
||||||
|
$this->saveInto($childObject);
|
||||||
|
$childObject->write();
|
||||||
|
|
||||||
|
$childObject->Groups()->add($_REQUEST['ctf']['ID']);
|
||||||
|
|
||||||
|
// if ajax-call in an iframe, close window by javascript, else redirect to referrer
|
||||||
|
if(!Director::is_ajax()) {
|
||||||
|
Director::redirect(substr($_SERVER['REQUEST_URI'],0,strpos($_SERVER['REQUEST_URI'],"?")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
?>
|
93
code/Newsletter/BatchProcess.php
Executable file
@ -0,0 +1,93 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Create a process in session which is incremented to calls from the client
|
||||||
|
*/
|
||||||
|
class BatchProcess extends Object {
|
||||||
|
|
||||||
|
protected $objects;
|
||||||
|
protected $current;
|
||||||
|
protected $id;
|
||||||
|
protected $scriptOutput = true;
|
||||||
|
|
||||||
|
function __construct( $collection ) {
|
||||||
|
|
||||||
|
$this->current = 0;
|
||||||
|
|
||||||
|
if( $collection ) {
|
||||||
|
if( is_array( $collection ) )
|
||||||
|
$this->objects = $collection;
|
||||||
|
elseif( is_a( $collection, 'DataObjectSet' ) ) {
|
||||||
|
$this->objects = $collection->toArray();
|
||||||
|
|
||||||
|
} else
|
||||||
|
$this->objects = array( $collection );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function runToCompletion() {
|
||||||
|
$this->scriptOutput = false;
|
||||||
|
$this->current = 0;
|
||||||
|
$ignore = $this->next( count( $this->objects ) );
|
||||||
|
|
||||||
|
$this->complete();
|
||||||
|
}
|
||||||
|
|
||||||
|
function getID() {
|
||||||
|
return $this->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
function next() {
|
||||||
|
self::addProcess( $this );
|
||||||
|
return $this->id.':'.$this->current.'/'.count( $this->objects );
|
||||||
|
}
|
||||||
|
|
||||||
|
function start() {
|
||||||
|
$this->current = 0;
|
||||||
|
$this->id = self::generateID();
|
||||||
|
|
||||||
|
if( !$this->objects || count( $this->objects ) === 0 )
|
||||||
|
return $this->complete();
|
||||||
|
|
||||||
|
return $this->next();
|
||||||
|
}
|
||||||
|
|
||||||
|
function complete() {
|
||||||
|
self::removeProcess( $this );
|
||||||
|
}
|
||||||
|
|
||||||
|
static function generateID() {
|
||||||
|
return count(Session::get('BatchProcesses')) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static function addProcess( $process ) {
|
||||||
|
Session::set('BatchProcesses.' . ($process->getID() - 1), serialize($process));
|
||||||
|
}
|
||||||
|
|
||||||
|
static function removeProcess( $process ) {
|
||||||
|
Session::clear('BatchProcesses.' . ($process->getID() - 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class BatchProcess_Controller extends Controller {
|
||||||
|
|
||||||
|
function next() {
|
||||||
|
|
||||||
|
$processID = $this->urlParams['ID'];
|
||||||
|
|
||||||
|
if( !$processID ) {
|
||||||
|
return "ERROR:Could not continue process";
|
||||||
|
}
|
||||||
|
|
||||||
|
$process = unserialize(Session::get('BatchProcesses.' . ($this->urlParams['ID'] - 1)));
|
||||||
|
|
||||||
|
if( !$process ) {
|
||||||
|
return "ERROR:Could not continue process";
|
||||||
|
}
|
||||||
|
|
||||||
|
if( $this->urlParams['Batch'] )
|
||||||
|
return $process->next( $this->urlParams['Batch'] );
|
||||||
|
else
|
||||||
|
return $process->next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
46
code/Newsletter/BouncedList.php
Executable file
@ -0,0 +1,46 @@
|
|||||||
|
<?php
|
||||||
|
class BouncedList extends FormField {
|
||||||
|
|
||||||
|
protected $nlType;
|
||||||
|
|
||||||
|
function __construct( $name, $newsletterType ) {
|
||||||
|
parent::__construct( $name, '', null );
|
||||||
|
|
||||||
|
if( is_object( $newsletterType ) )
|
||||||
|
$this->nlType = $newsletterType;
|
||||||
|
else
|
||||||
|
$this->nlType = DataObject::get_by_id( 'NewsletterType', $newsletterType );
|
||||||
|
}
|
||||||
|
|
||||||
|
function setController($controller) {
|
||||||
|
$this->controller = $controller;
|
||||||
|
}
|
||||||
|
|
||||||
|
function FieldHolder() {
|
||||||
|
return $this->renderWith( 'NewsletterAdmin_BouncedList' );
|
||||||
|
}
|
||||||
|
|
||||||
|
function Entries() {
|
||||||
|
|
||||||
|
$id = $this->nlType->GroupID;
|
||||||
|
|
||||||
|
$bounceRecords = DataObject::get( 'Email_BounceRecord', "`GroupID`='$id'", null, "INNER JOIN `Group_Members` USING(`MemberID`)" );
|
||||||
|
|
||||||
|
//user_error($id, E_USER_ERROR );
|
||||||
|
|
||||||
|
if( !$bounceRecords )
|
||||||
|
return null;
|
||||||
|
|
||||||
|
foreach( $bounceRecords as $bounceRecord ) {
|
||||||
|
if( $bounceRecord ) {
|
||||||
|
$bouncedUsers[] = new ArrayData( array(
|
||||||
|
'Record' => $bounceRecord,
|
||||||
|
'Member' => DataObject::get_by_id( 'Member', $bounceRecord->MemberID )
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new DataObjectSet( $bouncedUsers );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
99
code/Newsletter/Newsletter.php
Executable file
@ -0,0 +1,99 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
class Newsletter extends DataObject {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a FieldSet with which to create the CMS editing form.
|
||||||
|
* You can use the extend() method of FieldSet to create customised forms for your other
|
||||||
|
* data objects.
|
||||||
|
*/
|
||||||
|
function getCMSFields($controller = null) {
|
||||||
|
require_once("forms/Form.php");
|
||||||
|
|
||||||
|
$group = DataObject::get_by_id("Group", $this->Parent()->GroupID);
|
||||||
|
|
||||||
|
$ret = new FieldSet(
|
||||||
|
new TabSet("Root",
|
||||||
|
$mailTab = new Tab("Mail",
|
||||||
|
new TextField("Subject", "Subject", $this->Subject),
|
||||||
|
new HtmlEditorField("Content", "Content")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
if( $this->Status != 'Draft' ) {
|
||||||
|
$mailTab->push( new ReadonlyField("SendDate", "Sent at", $this->SendDate) );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return $ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTitle() {
|
||||||
|
return $this->getField('Subject');
|
||||||
|
}
|
||||||
|
|
||||||
|
function getNewsletterType() {
|
||||||
|
return DataObject::get_by_id('NewsletterType', $this->ParentID);
|
||||||
|
}
|
||||||
|
|
||||||
|
static $db = array(
|
||||||
|
"Status" => "Enum('Draft, Send', 'Draft')",
|
||||||
|
"Content" => "HTMLText",
|
||||||
|
"Subject" => "Varchar(255)",
|
||||||
|
"SentDate" => "Datetime",
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
static $has_one = array(
|
||||||
|
"Parent" => "NewsletterType",
|
||||||
|
);
|
||||||
|
|
||||||
|
static $has_many = array(
|
||||||
|
"Recipients" => "Newsletter_Recipient",
|
||||||
|
);
|
||||||
|
|
||||||
|
static function newDraft( $parentID, $subject, $content ) {
|
||||||
|
if( is_numeric( $parentID ) ) {
|
||||||
|
$newsletter = new Newsletter();
|
||||||
|
$newsletter->Status = 'Draft';
|
||||||
|
$newsletter->Title = $newsletter->Subject = $subject;
|
||||||
|
$newsletter->ParentID = $parentID;
|
||||||
|
$newsletter->Content = $content;
|
||||||
|
$newsletter->write();
|
||||||
|
} else {
|
||||||
|
user_error( $parentID, E_USER_ERROR );
|
||||||
|
}
|
||||||
|
|
||||||
|
return $newsletter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Newsletter_Recipient extends DataObject {
|
||||||
|
static $db = array(
|
||||||
|
"ParentID" => "Int",
|
||||||
|
);
|
||||||
|
static $has_one = array(
|
||||||
|
"Member" => "Member",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
class Newsletter_Email extends Email_Template {
|
||||||
|
protected $nlType;
|
||||||
|
|
||||||
|
function __construct($nlType) {
|
||||||
|
$this->nlType = $nlType;
|
||||||
|
parent::__construct();
|
||||||
|
}
|
||||||
|
|
||||||
|
function setTemplate( $template ) {
|
||||||
|
$this->ss_template = $template;
|
||||||
|
}
|
||||||
|
|
||||||
|
function UnsubscribeLink(){
|
||||||
|
$emailAddr = $this->To();
|
||||||
|
$nlTypeID = $this->nlType->ID;
|
||||||
|
return Director::absoluteBaseURL()."unsubscribe/$emailAddr/$nlTypeID";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
97
code/Newsletter/NewsletterEmailProcess.php
Executable file
@ -0,0 +1,97 @@
|
|||||||
|
<?php
|
||||||
|
class NewsletterEmailProcess extends BatchProcess {
|
||||||
|
|
||||||
|
protected $subject;
|
||||||
|
protected $body;
|
||||||
|
protected $from;
|
||||||
|
protected $newsletter;
|
||||||
|
protected $nlType;
|
||||||
|
protected $messageID;
|
||||||
|
|
||||||
|
function __construct( $subject, $body, $from, $newsletter, $nlType, $messageID = null ) {
|
||||||
|
|
||||||
|
$this->subject = $subject;
|
||||||
|
$this->body = $body;
|
||||||
|
$this->from = $from;
|
||||||
|
$this->newsletter = $newsletter;
|
||||||
|
$this->nlType = $nlType;
|
||||||
|
$this->messageID = $messageID;
|
||||||
|
|
||||||
|
$groupID = $nlType->GroupID;
|
||||||
|
|
||||||
|
parent::__construct( DataObject::get( 'Member', "`GroupID`='$groupID'", null, "INNER JOIN `Group_Members` ON `MemberID`=`Member`.`ID`" ) );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function next( $count = 10 ) {
|
||||||
|
$max = $this->current + $count;
|
||||||
|
|
||||||
|
$max = $max < count( $this->objects ) ? $max : count( $this->objects );
|
||||||
|
|
||||||
|
while($this->current < $max) {
|
||||||
|
$index = $this->current++;
|
||||||
|
$member = $this->objects[$index];
|
||||||
|
|
||||||
|
// check to see if the user has unsubscribed from the mailing list
|
||||||
|
// TODO Join in the above query first
|
||||||
|
$unsubscribeRecord = DataObject::get_one('Member_UnsubscribeRecord', "`MemberID`='{$member->ID}' AND `NewsletterTypeID`='{$this->nlType->ID}'");
|
||||||
|
|
||||||
|
if( !$unsubscribeRecord ) {
|
||||||
|
|
||||||
|
$address = $member->Email;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Email Blacklisting Support
|
||||||
|
*/
|
||||||
|
if($member->BlacklistedEmail && Email_BlackList::isBlocked($this->to)){
|
||||||
|
$bounceRecord = new Email_BounceRecord();
|
||||||
|
$bounceRecord->BounceEmail = $member->Email;
|
||||||
|
$bounceRecord->BounceTime = date("Y-m-d H:i:s",time());
|
||||||
|
$bounceRecord->BounceMessage = "BlackListed Email";
|
||||||
|
$bounceRecord->MemberID = $member->ID;
|
||||||
|
$bounceRecord->write();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$e = new Newsletter_Email($this->nlType);
|
||||||
|
$e->setBody( $this->body );
|
||||||
|
$e->setSubject( $this->subject );
|
||||||
|
$e->setFrom( $this->from );
|
||||||
|
$e->setTemplate( $this->nlType->Template );
|
||||||
|
|
||||||
|
|
||||||
|
$e->populateTemplate( array( 'Member' => $member, 'FirstName' => $member->FirstName ) );
|
||||||
|
$this->sendToAddress( $e, $address, $this->messageID );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if( $this->current >= count( $this->objects ) )
|
||||||
|
return $this->complete();
|
||||||
|
else
|
||||||
|
return parent::next();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function sendToAddress( $email, $address, $messageID = null ) {
|
||||||
|
$email->setTo( $address );
|
||||||
|
$email->send( $messageID );
|
||||||
|
}
|
||||||
|
|
||||||
|
function complete() {
|
||||||
|
parent::complete();
|
||||||
|
|
||||||
|
if( $this->newsletter->SentDate )
|
||||||
|
$resent = true;
|
||||||
|
|
||||||
|
$this->newsletter->SentDate = 'now';
|
||||||
|
$this->newsletter->Status = 'Send';
|
||||||
|
$this->newsletter->write();
|
||||||
|
|
||||||
|
$message = "statusMessage('Sent " . count( $this->objects ) . " emails successfully','good');";
|
||||||
|
|
||||||
|
if( $resent )
|
||||||
|
return $message."resent_ok( '{$this->nlType->ID}', '{$this->newsletter->ID}' )";
|
||||||
|
else
|
||||||
|
return $message."draft_sent_ok( '{$this->nlType->ID}', '{$this->newsletter->ID}' )";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
57
code/Newsletter/NewsletterList.php
Executable file
@ -0,0 +1,57 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
class NewsletterList extends FormField {
|
||||||
|
function __construct($name, $mailtype, $status = "Draft") {
|
||||||
|
if(is_object($mailtype)) $this->mailType = $mailtype;
|
||||||
|
else $this->mailType = DataObject::get_by_id('NewsletterType',$mailtype);
|
||||||
|
$this->status = $status;
|
||||||
|
parent::__construct(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
function FieldHolder() {
|
||||||
|
return $this->renderWith("NewsletterList");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function setMailType($mailtype) {
|
||||||
|
$this->mailType = $mailtype;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setController($controller) {
|
||||||
|
$this->controller = $controller;
|
||||||
|
}
|
||||||
|
|
||||||
|
function Newsletters() {
|
||||||
|
return DataObject::get( 'Newsletter', "`ParentID`='{$this->mailType->ID}' AND `Status`='{$this->status}'" );
|
||||||
|
}
|
||||||
|
|
||||||
|
function DraftNewsletters() {
|
||||||
|
return $this->mailType->DraftNewsletters();
|
||||||
|
}
|
||||||
|
|
||||||
|
function SentNewsletters() {
|
||||||
|
return $this->mailType->SentNewsletters();
|
||||||
|
}
|
||||||
|
|
||||||
|
function Status() {
|
||||||
|
return $this->status;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* function AddRecordForm() {
|
||||||
|
return new TabularStyle(new Form($this->controller,'AddRecordForm',
|
||||||
|
new FieldSet(
|
||||||
|
new TextField("FirstName", "First Name"),
|
||||||
|
new TextField("Surname", "Surname"),
|
||||||
|
new TextField("Email", "Email"),
|
||||||
|
new TextField("Password", "Password"),
|
||||||
|
new HiddenField("GroupID", null, $this->group->ID)
|
||||||
|
),
|
||||||
|
new FieldSet(
|
||||||
|
new FormAction("addmember", "Add")
|
||||||
|
)
|
||||||
|
));
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
55
code/Newsletter/NewsletterType.php
Executable file
@ -0,0 +1,55 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
class NewsletterType extends DataObject {
|
||||||
|
|
||||||
|
static $db = array(
|
||||||
|
"Title" => "Varchar",
|
||||||
|
"Template" => "Varchar",
|
||||||
|
"FromEmail" => "Varchar",
|
||||||
|
"Sent" => "Datetime"
|
||||||
|
);
|
||||||
|
static $has_one = array(
|
||||||
|
"Parent" => "SiteTree",
|
||||||
|
"Group" => "Group",
|
||||||
|
);
|
||||||
|
static $has_many = array(
|
||||||
|
"Newsletters" => "Newsletter",
|
||||||
|
);
|
||||||
|
|
||||||
|
function DraftNewsletters() {
|
||||||
|
return DataObject::get("Newsletter","ParentID={$this->ID} AND Status ='Draft'");
|
||||||
|
}
|
||||||
|
|
||||||
|
function SentNewsletters() {
|
||||||
|
return DataObject::get("Newsletter","ParentID={$this->ID} AND Status ='Send'");
|
||||||
|
}
|
||||||
|
|
||||||
|
function Recipients() {
|
||||||
|
return DataObject::get("Member", "Group_Members.GroupID = {$this->GroupID}", "", "JOIN Group_Members on Group_Members.MemberID = Member.ID");
|
||||||
|
}
|
||||||
|
|
||||||
|
function delete() {
|
||||||
|
foreach( $this->Newsletters() as $newsletter )
|
||||||
|
$newsletter->delete();
|
||||||
|
|
||||||
|
parent::delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the group so the security section is also in sync with
|
||||||
|
* the curent newsletters.
|
||||||
|
*/
|
||||||
|
function onBeforeWrite(){
|
||||||
|
if($this->ID){
|
||||||
|
$group = $this->Group();
|
||||||
|
if($group->Title != "$this->Title"){
|
||||||
|
$group->Title = "Mailing List: " . $this->Title;
|
||||||
|
// Otherwise the code would have mailing list in it too :-(
|
||||||
|
$group->Code = SiteTree::generateURLSegment($this->Title);
|
||||||
|
$group->write();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
parent::onBeforeWrite();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
372
code/Newsletter/RecipientImportField.php
Executable file
@ -0,0 +1,372 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Displays a file upload field.
|
||||||
|
*/
|
||||||
|
class RecipientImportField extends FormField {
|
||||||
|
|
||||||
|
protected $memberGroup;
|
||||||
|
protected $memberClass;
|
||||||
|
protected $tableColumns;
|
||||||
|
protected $table;
|
||||||
|
protected $clientFileName;
|
||||||
|
protected $typeID;
|
||||||
|
|
||||||
|
static $column_types = array(
|
||||||
|
'Salutation' => array( 'title', 'salutation' ),
|
||||||
|
'FirstName' => array( 'firstname', 'christianname', 'givenname' ),
|
||||||
|
'Surname' => array( 'lastname','surname', 'familyname' ),
|
||||||
|
'Email' => array( 'email', 'emailaddress' ),
|
||||||
|
'Address' => array( 'address' ),
|
||||||
|
'PhoneNumber' => array('phone','phonenumber'),
|
||||||
|
'JobTitle' => array( 'jobtitle' ),
|
||||||
|
'Organisation' => array( 'organisation', 'organization' ),
|
||||||
|
'EmailType' => array( 'htmlorplaintext', 'emailtype' )
|
||||||
|
);
|
||||||
|
|
||||||
|
static $custom_set_fields = array();
|
||||||
|
|
||||||
|
public static function setCustomField( $getMapCode, $fieldName ) {
|
||||||
|
self::$custom_set_fields[] = array(
|
||||||
|
'Code' => $getMapCode,
|
||||||
|
'Field' => $fieldName
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function __construct( $name, $title, $memberGroup, $memberClass = 'Member', $form = null ) {
|
||||||
|
$this->memberGroup = $memberGroup;
|
||||||
|
$this->memberClass = $memberClass;
|
||||||
|
parent::__construct( $name, $title, null, $form );
|
||||||
|
}
|
||||||
|
|
||||||
|
function Field() {
|
||||||
|
$frameURL = Director::absoluteBaseURL() . 'admin/newsletter/displayfilefield/' . $this->typeID;
|
||||||
|
|
||||||
|
return "<iframe name=\"{$this->Name}\" frameborder=\"0\" class=\"RecipientImportField\" src=\"$frameURL\"></iframe>";
|
||||||
|
}
|
||||||
|
|
||||||
|
function setTypeID( $id ) {
|
||||||
|
$this->typeID = $id;
|
||||||
|
}
|
||||||
|
|
||||||
|
function CustomSetFields() {
|
||||||
|
$fields = new FieldSet();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
foreach( self::$custom_set_fields as $customField ) {
|
||||||
|
eval( '$map = ' . $customField['Code'] .';' );
|
||||||
|
|
||||||
|
$noneMap = array( 0 => '(Not set)' );
|
||||||
|
|
||||||
|
$map = $noneMap + $map;
|
||||||
|
|
||||||
|
$fields->push( new DropdownField( 'Set['.$customField['Field'].']', $customField['Field'], $map ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
return $fields;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns HTML to be displayed inside the IFrame
|
||||||
|
*/
|
||||||
|
static function fileupload() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the table of results to be displayed in the table of
|
||||||
|
* details loaded from the file
|
||||||
|
*/
|
||||||
|
function displaytable() {
|
||||||
|
|
||||||
|
// Check that a file was uploaded
|
||||||
|
|
||||||
|
$tempFile = fopen( $_FILES['ImportFile']['tmp_name'], 'r' );
|
||||||
|
|
||||||
|
// display some error if the file cannot be opened
|
||||||
|
|
||||||
|
$this->clientFileName = $_FILES['ImportFile']['name'];
|
||||||
|
|
||||||
|
while( ( $row = fgetcsv( $tempFile ) ) !== false ) {
|
||||||
|
|
||||||
|
if( !$this->tableColumns ) {
|
||||||
|
$this->parseTableHeader( $row );
|
||||||
|
} else {
|
||||||
|
$newRow = array();
|
||||||
|
$newSessionTableRow = array();
|
||||||
|
|
||||||
|
foreach( $row as $cell ) {
|
||||||
|
$newRow[] = $this->parseCSVCell( $cell );
|
||||||
|
$newSessionTableRow[] = $cell;
|
||||||
|
}
|
||||||
|
|
||||||
|
$cells = new DataObjectSet( $newRow );
|
||||||
|
$table[] = $cells->customise( array( 'Cells' => $cells ) );
|
||||||
|
|
||||||
|
$sessionTable[] = $newSessionTableRow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose( $tempFile );
|
||||||
|
|
||||||
|
$this->table = new DataObjectSet( $table );
|
||||||
|
|
||||||
|
Session::set("ImportFile.{$_REQUEST['ID']}", $sessionTable);
|
||||||
|
|
||||||
|
return $this->renderWith( 'Newsletter_RecipientImportField_Table' );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines what type each column is
|
||||||
|
*/
|
||||||
|
function parseTableHeader( $columns ) {
|
||||||
|
|
||||||
|
$columnSource = array_combine(
|
||||||
|
array_keys( self::$column_types ),
|
||||||
|
array_keys( self::$column_types )
|
||||||
|
);
|
||||||
|
|
||||||
|
$columnSource = array_merge( array( 'Unknown' => 'Unknown' ), $columnSource );
|
||||||
|
|
||||||
|
foreach( $columns as $cell ) {
|
||||||
|
$columnType = $this->getColumnType( $this->parseCSVCell( $cell )->Value() );
|
||||||
|
$this->tableColumns[] = new DropdownField( 'ImportFileColumns[' . (string)( $colCount++ ) . ']', '', $columnSource, $columnType );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseCSVCell( $cell ) {
|
||||||
|
return new RecipientImportField_Cell( $cell );
|
||||||
|
}
|
||||||
|
|
||||||
|
function getColumnType( $cell ) {
|
||||||
|
$cell = strtolower( $cell );
|
||||||
|
$escapedValue = preg_replace( '/[^a-z]/', '', $cell );
|
||||||
|
|
||||||
|
foreach( self::$column_types as $type => $aliases ) {
|
||||||
|
if( in_array( $escapedValue, $aliases ) )
|
||||||
|
return $type;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'Unknown';
|
||||||
|
}
|
||||||
|
|
||||||
|
function setController( $controller ) {
|
||||||
|
$this->controller = $controller;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set of table column headers
|
||||||
|
*/
|
||||||
|
function ColumnHeaders() {
|
||||||
|
return new DataObjectSet( $this->tableColumns );
|
||||||
|
}
|
||||||
|
|
||||||
|
function Rows() {
|
||||||
|
return $this->table;
|
||||||
|
}
|
||||||
|
|
||||||
|
function FileName() {
|
||||||
|
return $this->clientFileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
function TypeID() {
|
||||||
|
return $this->typeID;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class RecipientImportField_Cell extends ViewableData {
|
||||||
|
protected $value;
|
||||||
|
|
||||||
|
function __construct( $value ) {
|
||||||
|
$this->value = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function Value() {
|
||||||
|
return $this->value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class RecipientImportField_UploadForm extends Form {
|
||||||
|
function import( $data, $form ) {
|
||||||
|
$id = $data['ID'];
|
||||||
|
$mailType = DataObject::get_one("NewsletterType", "ID = $id");
|
||||||
|
if($mailType->GroupID)
|
||||||
|
$group = DataObject::get_one("Group", "ID = $mailType->GroupID");
|
||||||
|
|
||||||
|
$recipientField = new RecipientImportField("ImportFile","Import from file", $group );
|
||||||
|
$recipientField->setTypeID( $id );
|
||||||
|
|
||||||
|
// if the file is not valid, then return an error
|
||||||
|
if( empty( $_FILES ) || empty( $_FILES['ImportFile'] ) || $_FILES['ImportFile']['size'] == 0 )
|
||||||
|
return $this->customise( array( 'ID' => $id, "UploadForm" => $this->controller->UploadForm( $id ), 'ErrorMessage' => 'Please choose a CSV file to import' ) )->renderWith('Newsletter_RecipientImportField');
|
||||||
|
elseif( !$this->isValidCSV( $_FILES['ImportFile'] ) ) {
|
||||||
|
/*if( file_exists( $_FILES['ImportFile']['tmp_name'] ) ) unlink( $_FILES['ImportFile']['tmp_name'] );
|
||||||
|
unset( $_FILES['ImportFile'] );*/
|
||||||
|
return $this->customise( array( 'ID' => $id, "UploadForm" => $this->controller->UploadForm( $id ), 'ErrorMessage' => 'The selected file was not a CSV file' ) )->renderWith('Newsletter_RecipientImportField');
|
||||||
|
} else
|
||||||
|
return $recipientField->displaytable();
|
||||||
|
}
|
||||||
|
|
||||||
|
function isValidCSV( $file ) {
|
||||||
|
return preg_match( '/.*\.csv$/', $file['name'] ) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function confirm( $data, $form ) {
|
||||||
|
$id = $data['ID'];
|
||||||
|
$mailType = DataObject::get_one("NewsletterType", "ID = $id");
|
||||||
|
if($mailType->GroupID)
|
||||||
|
$group = DataObject::get_one("Group", "ID = $mailType->GroupID");
|
||||||
|
return $this->importMembers( $id, $group, $data['ImportFileColumns'], $data['Set'] );
|
||||||
|
}
|
||||||
|
|
||||||
|
function cancel( $data, $form ) {
|
||||||
|
$newForm = $this->controller->UploadForm( $data['ID'] );
|
||||||
|
return $newForm->forTemplate();
|
||||||
|
}
|
||||||
|
|
||||||
|
function action_import( $data, $form ) {
|
||||||
|
return $this->import( $data, $form );
|
||||||
|
}
|
||||||
|
|
||||||
|
function action_confirm( $data, $form ) {
|
||||||
|
return $this->confirm( $data, $form );
|
||||||
|
}
|
||||||
|
|
||||||
|
function action_cancel( $data, $form ) {
|
||||||
|
return $this->cancel( $data, $form );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Import members from the uploaded file
|
||||||
|
*/
|
||||||
|
protected function importMembers( $id, $group, $cols, $setFields, $primaryColType = 'Email' ) {
|
||||||
|
|
||||||
|
$startTime = time();
|
||||||
|
|
||||||
|
$importData = Session::set("ImportFile.{$id}");
|
||||||
|
|
||||||
|
$validColumns = array_keys( RecipientImportField::$column_types );
|
||||||
|
|
||||||
|
$columnMap = array_flip( $cols );
|
||||||
|
|
||||||
|
// Debug::show($columnMap);
|
||||||
|
|
||||||
|
// locate the primary column's index
|
||||||
|
$primaryColumn = /*array_search( $primaryColType, $validColumns );*/ $columnMap[$primaryColType];
|
||||||
|
|
||||||
|
// changed fields
|
||||||
|
$changedFields = array();
|
||||||
|
|
||||||
|
// intersect the list of valid columns with the column map to find the columns we need
|
||||||
|
$importColumns = array_intersect( $validColumns, $cols );
|
||||||
|
|
||||||
|
// statistics
|
||||||
|
$newMembers = 0;
|
||||||
|
$changedMembers = 0;
|
||||||
|
$skippedMembers = 0;
|
||||||
|
|
||||||
|
// the class that the imported members will become
|
||||||
|
$newMemberClass = Object::getCustomClass( 'Member' );
|
||||||
|
|
||||||
|
// for each row, add a new member or overwrite an existing member
|
||||||
|
foreach( $importData as $newMemberRow ) {
|
||||||
|
|
||||||
|
// skip rows with an empty value for the primary column
|
||||||
|
if( empty( $newMemberRow[$primaryColumn] ) ) {
|
||||||
|
$skippedMembers++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// remember to check if the user has unsubscribed
|
||||||
|
$trackChanges = true;
|
||||||
|
|
||||||
|
// TODO: Write DataObject::update
|
||||||
|
$member = $this->findMember( $newMemberRow[$primaryColumn] );
|
||||||
|
|
||||||
|
if( !$member ) {
|
||||||
|
$newMembers++;
|
||||||
|
$trackChanges = false;
|
||||||
|
$member = Object::create("Member");
|
||||||
|
} else {
|
||||||
|
// skip this member if the are unsubscribed
|
||||||
|
if( $member->Unsubscribed ) {
|
||||||
|
$skippedMembers++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( $member->class != $newMemberClass )
|
||||||
|
$member->setClassName( $newMemberClass );
|
||||||
|
|
||||||
|
$changedMembers++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// add each of the valid columns
|
||||||
|
foreach( $importColumns as $datum ) {
|
||||||
|
|
||||||
|
// perform any required conversions
|
||||||
|
$newValue = trim( $newMemberRow[$columnMap[$datum]] );
|
||||||
|
$oldValue = trim( $member->$datum );
|
||||||
|
|
||||||
|
// Debug::message( "$datum@{$columnMap[$datum]}" );
|
||||||
|
|
||||||
|
// track the modifications to the member data
|
||||||
|
if( $trackChanges && $newValue != $oldValue && $datum != $primaryColumn ) {
|
||||||
|
$changedFields[] = array(
|
||||||
|
$newMemberRow[$primaryColumn],
|
||||||
|
"$datum:\n$oldValue",
|
||||||
|
"$datum:\n$newValue"
|
||||||
|
);
|
||||||
|
|
||||||
|
$numChangedFields++;
|
||||||
|
}
|
||||||
|
|
||||||
|
$member->$datum = $newValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// set any set fields
|
||||||
|
if( $setFields )
|
||||||
|
foreach( $setFields as $fieldName => $fieldValue )
|
||||||
|
$member->$fieldName = $fieldValue;
|
||||||
|
|
||||||
|
// add member to group
|
||||||
|
$member->write();
|
||||||
|
$member->Groups()->add( $group->ID );
|
||||||
|
}
|
||||||
|
|
||||||
|
$numChangedFields = count( $changedFields );
|
||||||
|
$this->notifyChanges( $changedFields );
|
||||||
|
|
||||||
|
// TODO Refresh window
|
||||||
|
$customData = array(
|
||||||
|
'ID' => $id,
|
||||||
|
"UploadForm" => $this->controller->UploadForm( $id ),
|
||||||
|
'ImportMessage' => 'Imported new members',
|
||||||
|
'NewMembers' => (string)$newMembers,
|
||||||
|
'ChangedMembers' => (string)$changedMembers,
|
||||||
|
'ChangedFields' => (string)$numChangedFields,
|
||||||
|
'SkippedRecords' => (string)$skippedMembers,
|
||||||
|
'Time' => time() - $startTime
|
||||||
|
);
|
||||||
|
return $this->customise( $customData )->renderWith('Newsletter_RecipientImportField');
|
||||||
|
}
|
||||||
|
|
||||||
|
function findMember( $email ) {
|
||||||
|
$email = addslashes( $email );
|
||||||
|
return DataObject::get_one( 'Member', "`Email`='$email'" );
|
||||||
|
}
|
||||||
|
|
||||||
|
function notifyChanges( $changes ) {
|
||||||
|
$email = new Email( Email::getAdminEmail(), Email::getAdminEmail(), 'Changed Fields' );
|
||||||
|
|
||||||
|
$body = "";
|
||||||
|
|
||||||
|
foreach( $changes as $change ) {
|
||||||
|
$body .= "-------------------------------\n";
|
||||||
|
$body .= implode( ' ', $change ) . "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
$email->setBody( $body );
|
||||||
|
$email->send();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
254
code/Newsletter/SubscribeForm.php
Executable file
@ -0,0 +1,254 @@
|
|||||||
|
<?php
|
||||||
|
class SubscribeForm extends UserDefinedForm {
|
||||||
|
static $add_action = "a newsletter subscription form";
|
||||||
|
|
||||||
|
static $required_fields = array(
|
||||||
|
'Email address' => 'EditableEmailField(CustomParameter=Email,CanDelete=0)',
|
||||||
|
'First name' => 'EditableTextField(CustomParameter=FirstName)',
|
||||||
|
'Last name' => 'EditableTextField(CustomParameter=Surname)',
|
||||||
|
'Address' => 'EditableTextField(Rows=3,CustomParameter=Address)',
|
||||||
|
'Job title' => 'EditableTextField(CustomParameter=JobTitle)',
|
||||||
|
'Organisation' => 'EditableTextField(CustomParameter=Organisation)',
|
||||||
|
'Mail format' => 'EditableRadioField(Options=1:HTML,Options=0:Text__only,CustomParameter=HTMLEmail)'
|
||||||
|
);
|
||||||
|
|
||||||
|
static $db = array(
|
||||||
|
'Subscribe' => 'Boolean',
|
||||||
|
'AllNewsletters' => 'Boolean',
|
||||||
|
'Subject' => 'Varchar'
|
||||||
|
);
|
||||||
|
|
||||||
|
static $defaults = array(
|
||||||
|
"OnCompleteMessage" => "<p>Thanks, you have been added to our mailing list.</p>",
|
||||||
|
);
|
||||||
|
|
||||||
|
static $has_many = array(
|
||||||
|
'Newsletters' => 'NewsletterType'
|
||||||
|
);
|
||||||
|
|
||||||
|
function __construct( $data = null, $isSingleton = false ) {
|
||||||
|
parent::__construct( $data, $isSingleton );
|
||||||
|
|
||||||
|
if( $data || $isSingleton )
|
||||||
|
return;
|
||||||
|
|
||||||
|
$this->addDefaultFields();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function addDefaultFields() {
|
||||||
|
$f = $this->Fields();
|
||||||
|
|
||||||
|
// check that the required fields exist
|
||||||
|
$count = 1;
|
||||||
|
|
||||||
|
foreach( self::$required_fields as $defaultName => $typeString ) {
|
||||||
|
|
||||||
|
list( $type, $typeValue ) = $this->parseType( $typeString );
|
||||||
|
$newField = new $type();
|
||||||
|
$newField->Name = "Field" . (string)$count;
|
||||||
|
$newField->Title = $defaultName;
|
||||||
|
|
||||||
|
// TODO Map the field to a particular action
|
||||||
|
|
||||||
|
if( !empty( $typeValue ) ) {
|
||||||
|
$newField->prepopulate( $typeValue );
|
||||||
|
}
|
||||||
|
|
||||||
|
$newField->setField('ID', "new-" . $count);
|
||||||
|
|
||||||
|
$newField->Sort = $count;
|
||||||
|
$f->addWithoutWrite($newField);
|
||||||
|
$count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function Newsletters() {
|
||||||
|
$components = $this->getComponents('Newsletters');
|
||||||
|
return $components;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function customFormActions( $isReadonly = false ) {
|
||||||
|
|
||||||
|
$fields = parent::customFormActions( $isReadonly );
|
||||||
|
|
||||||
|
// get the newsletters in the system
|
||||||
|
$newsletterTypes = DataObject::get( 'NewsletterType' );
|
||||||
|
|
||||||
|
$availableNewsletters = array();
|
||||||
|
|
||||||
|
foreach( $this->Newsletters() as $subscribeTo )
|
||||||
|
$availableNewsletters[] = $subscribeTo->ID;
|
||||||
|
|
||||||
|
// create a checkbox for each newsletter
|
||||||
|
if($newsletterTypes && is_object($newsletterTypes)) {
|
||||||
|
foreach( $newsletterTypes as $newsletterType )
|
||||||
|
$nlCheckboxes[] = new CheckboxField( "CustomNewsletters[{$newsletterType->ID}]", $newsletterType->Title, in_array( $newsletterType->ID, $availableNewsletters ) );
|
||||||
|
}
|
||||||
|
$fields->push( new OptionsetField( 'AllNewsletters', '', array(
|
||||||
|
1 => 'All newsletters',
|
||||||
|
0 => 'Specific newsletters'
|
||||||
|
),
|
||||||
|
$this->AllNewsletters
|
||||||
|
));
|
||||||
|
|
||||||
|
$fields->push( new CompositeField( $nlCheckboxes ));
|
||||||
|
$fields->push( new TextField( 'Subject', 'Subject line on confirmation', $this->Subject ) );
|
||||||
|
|
||||||
|
return $fields;
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseType( $typeString ) {
|
||||||
|
if( preg_match('/^([A-Za-z_]+)\(([^)]+)\)$/', $typeString, $match ) )
|
||||||
|
return array( $match[1], $match[2] );
|
||||||
|
|
||||||
|
return array( $typeString, null );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public function processNewFormFields() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saves data related to any custom actions this form performs when submitted
|
||||||
|
*/
|
||||||
|
public function customFormSave( $data, $form ) {
|
||||||
|
|
||||||
|
$newsletters = $this->Newsletters();
|
||||||
|
|
||||||
|
$this->Subscribe = !empty( $data['Subscribe'] );
|
||||||
|
$this->AllNewsletters = !empty( $data['AllNewsletters'] );
|
||||||
|
$this->Subject = $data['Subject'];
|
||||||
|
// Note: $data['CustomNewsletters'] was changed to $_REQUEST['CustomNewsletters'] in order to fix
|
||||||
|
// to fix "'Specific newsletters' option in 'newsletter subscription form' page type does not work" bug
|
||||||
|
// See: http://www.silverstripe.com/bugs/flat/1675
|
||||||
|
if( !empty( $_REQUEST['CustomNewsletters'] ) ) {
|
||||||
|
/*foreach( $newsletters as $newsletter ) {
|
||||||
|
if( $data['CustomNewsletters'][$newsletter->ID] == 'on' ) {
|
||||||
|
$newsletters->add( $newsletter->ID );
|
||||||
|
} else {
|
||||||
|
$newsletters->remove( $newsletter->ID );
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
|
||||||
|
if( isset($_REQUEST['CustomNewsletters'][0]) )
|
||||||
|
unset( $_REQUEST['CustomNewsletters'][0] );
|
||||||
|
|
||||||
|
$newsletters->setByIDList( array_keys( $_REQUEST['CustomNewsletters'] ) );
|
||||||
|
} else {
|
||||||
|
$this->AllNewsletters = true;
|
||||||
|
$newsletters->removeAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SubscribeForm_SubscribeEmail extends Email_Template {
|
||||||
|
protected $to = '$Email';
|
||||||
|
protected $subject = '$Subject';
|
||||||
|
protected $ss_template = 'SubscribeEmail';
|
||||||
|
protected $from = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
class SubscribeForm_Controller extends UserDefinedForm_Controller {
|
||||||
|
|
||||||
|
function process( $data, $form ) {
|
||||||
|
|
||||||
|
// Add the user to the mailing list
|
||||||
|
$member = Object::create("Member");
|
||||||
|
|
||||||
|
// $_REQUEST['showqueries'] = 1;
|
||||||
|
|
||||||
|
// map the editables to the data
|
||||||
|
|
||||||
|
foreach( $this->Fields() as $editable ) {
|
||||||
|
|
||||||
|
$field = $editable->CustomParameter;
|
||||||
|
if( !$field )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Debug::message( $editable->Name . '->' . $field );
|
||||||
|
|
||||||
|
// if( $member->hasField( $field ) )
|
||||||
|
$member->$field = $data[$editable->Name];
|
||||||
|
}
|
||||||
|
|
||||||
|
// need to write the member record before adding the user to groups
|
||||||
|
$member->write();
|
||||||
|
|
||||||
|
$newsletters = array();
|
||||||
|
|
||||||
|
// Add member to the selected newsletters
|
||||||
|
if( $data['Newsletters'] ) foreach( $data['Newsletters'] as $listID ) {
|
||||||
|
|
||||||
|
if( !is_numeric( $listID ) )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// get the newsletter
|
||||||
|
$newsletter = DataObject::get_by_id( 'NewsletterType', $listID );
|
||||||
|
|
||||||
|
if( !$newsletter )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
$newsletters[] = $newsletter;
|
||||||
|
// Debug::show( $newsletter->GroupID );
|
||||||
|
|
||||||
|
$member->Groups()->add( $newsletter->GroupID );
|
||||||
|
}
|
||||||
|
|
||||||
|
// email the user with their details
|
||||||
|
$templateData = array(
|
||||||
|
'Email' => $member->Email,
|
||||||
|
'FirstName' => $member->FirstName,
|
||||||
|
'Newsletters' => new DataObjectSet( $newsletters ),
|
||||||
|
'UnsubscribeLink' => Director::baseURL() . 'unsubscribe/' . $member->Email
|
||||||
|
);
|
||||||
|
|
||||||
|
$email = new SubscribeForm_SubscribeEmail();
|
||||||
|
$email->setFrom( Email::getAdminEmail() );
|
||||||
|
$email->setSubject( $this->Subject );
|
||||||
|
|
||||||
|
$email->populateTemplate( $templateData );
|
||||||
|
$email->send();
|
||||||
|
|
||||||
|
$parentHTML = parent::process( $data, $form );
|
||||||
|
|
||||||
|
$templateData['Link'] = $data['Referrer'];
|
||||||
|
$templateData['UnsubscribeLink'] = Director::baseURL() . 'unsubscribe';
|
||||||
|
|
||||||
|
$custom = $this->customise(array(
|
||||||
|
"Content" => $this->customise( $templateData )->renderWith('SubscribeSubmission')
|
||||||
|
));
|
||||||
|
|
||||||
|
return $custom->renderWith('Page');
|
||||||
|
}
|
||||||
|
|
||||||
|
function Form() {
|
||||||
|
$form = parent::Form();
|
||||||
|
|
||||||
|
if( $this->AllNewsletters )
|
||||||
|
$newsletterList = DataObject::get('NewsletterType');
|
||||||
|
else
|
||||||
|
$newsletterList = $this->Newsletters();
|
||||||
|
|
||||||
|
$newsletters = array();
|
||||||
|
|
||||||
|
// Debug::show($newsletterList);
|
||||||
|
|
||||||
|
// get the newsletter types to display on the form
|
||||||
|
foreach( $newsletterList as $newsletter )
|
||||||
|
$newsletters[] = $newsletter;
|
||||||
|
|
||||||
|
// Debug::show( $newsletters );
|
||||||
|
|
||||||
|
$form->Fields()->push( new CheckboxSetField( 'Newsletters', 'Subscribe to lists', $newsletters ) );
|
||||||
|
|
||||||
|
$validator = $form->getValidator();
|
||||||
|
$validator->addRequiredField( 'Newsletters' );
|
||||||
|
$form->setValidator( $validator );
|
||||||
|
|
||||||
|
return $form;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
40
code/Newsletter/TemplateList.php
Executable file
@ -0,0 +1,40 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* This should extend DropdownField
|
||||||
|
*/
|
||||||
|
class TemplateList extends DropdownField {
|
||||||
|
|
||||||
|
protected $templatePath;
|
||||||
|
|
||||||
|
function __construct( $name, $title, $value, $path, $form = null ) {
|
||||||
|
$this->templatePath = $path;
|
||||||
|
parent::__construct( $name, $title, $this->getTemplates(), $value, $form );
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getTemplates() {
|
||||||
|
$templates = array( "" => "None" );
|
||||||
|
|
||||||
|
$absPath = Director::baseFolder();
|
||||||
|
if( $absPath{strlen($absPath)-1} != "/" )
|
||||||
|
$absPath .= "/";
|
||||||
|
|
||||||
|
$absPath .= $this->templatePath;
|
||||||
|
if(is_dir($absPath)) {
|
||||||
|
$templateDir = opendir( $absPath );
|
||||||
|
|
||||||
|
// read all files in the directory
|
||||||
|
while( ( $templateFile = readdir( $templateDir ) ) !== false ) {
|
||||||
|
// *.ss files are templates
|
||||||
|
if( preg_match( '/(.*)\.ss$/', $templateFile, $match ) )
|
||||||
|
$templates[$match[1]] = $match[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $templates;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setController( $controller ) {
|
||||||
|
$this->controller = $controller;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
178
code/Newsletter/Unsubscribe.php
Executable file
@ -0,0 +1,178 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Create a form that a user can use to unsubscribe from a mailing list
|
||||||
|
*/
|
||||||
|
class Unsubscribe_Controller extends Page_Controller {
|
||||||
|
function __construct($data = null) {
|
||||||
|
}
|
||||||
|
function RelativeLink($action = null) {
|
||||||
|
return "unsubscribe/$action";
|
||||||
|
}
|
||||||
|
|
||||||
|
function index() {
|
||||||
|
Session::clear("loggedInAs");
|
||||||
|
Requirements::css(project()."/css/form.css");
|
||||||
|
// if the email address is given
|
||||||
|
$emailAddress = addslashes( $this->urlParams['Email'] );
|
||||||
|
$mailingListID = addslashes( $this->urlParams['MailingList'] );
|
||||||
|
|
||||||
|
if(is_numeric($mailingListID)) {
|
||||||
|
$mailingList = DataObject::get_by_id("NewsletterType", $mailingListID);
|
||||||
|
}
|
||||||
|
|
||||||
|
// try to find the user
|
||||||
|
if($emailAddress)
|
||||||
|
$member = DataObject::get_one( 'Member', "`Email`='$emailAddress'" );
|
||||||
|
|
||||||
|
// if the email address and mailing list is given in the URL and both are valid,
|
||||||
|
// then unsubscribe the user
|
||||||
|
if( $member && $mailingList && $member->inGroup( $mailingList->GroupID ) ) {
|
||||||
|
$this->unsubscribeFromList( $member, $mailingList );
|
||||||
|
$url = "unsubscribe"."/done/".$member->Email."/".$mailingList->Title;
|
||||||
|
Director::redirect($url);
|
||||||
|
} elseif( $member ) {
|
||||||
|
$listForm = $this->MailingListForm( $member );
|
||||||
|
} else {
|
||||||
|
$listForm = $this->EmailAddressForm();
|
||||||
|
}
|
||||||
|
|
||||||
|
if($this->urlParams['Email'] == "done")
|
||||||
|
$listForm->sessionMessage("Thank you. You have been removed from the selected groups", "good");
|
||||||
|
|
||||||
|
return $this->customise( array( 'Content' => $listForm->forTemplate() ) )->renderWith('Page');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display a form with all the mailing lists that the user is subscribed to
|
||||||
|
*/
|
||||||
|
function MailingListForm( $member = null ) {
|
||||||
|
$email = $this->urlParams['Email'];
|
||||||
|
return new Unsubscribe_MailingListForm($this, 'MailingListForm', $member, $email);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display a form allowing the user to input their email address
|
||||||
|
*/
|
||||||
|
function EmailAddressForm() {
|
||||||
|
return new Unsubscribe_EmailAddressForm( $this, 'EmailAddressForm' );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show the lists for the user with the given email address
|
||||||
|
*/
|
||||||
|
function showlists( $data, $form ) {
|
||||||
|
$member = DataObject::get_one( 'Member', "`Email`='{$data['Email']}'" );
|
||||||
|
|
||||||
|
|
||||||
|
$mailingListForm = new Unsubscribe_MailingListForm( $this, 'MailingListForm', $member, $data['Email']);
|
||||||
|
|
||||||
|
return $this->customise( array( 'Content' => $mailingListForm->forTemplate() ) )->renderWith('Page');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unsubscribe the user from the given lists.
|
||||||
|
*/
|
||||||
|
function unsubscribe($data, $form) {
|
||||||
|
$email = $this->urlParams['Email'];
|
||||||
|
$member = DataObject::get_one( 'Member', "`Email`='$email'" );
|
||||||
|
if(!$member){
|
||||||
|
$member = DataObject::get_one('Member', "`EmailAddress` = '$email'");
|
||||||
|
}
|
||||||
|
|
||||||
|
if( $data['MailingLists'] ) {
|
||||||
|
foreach( array_keys( $data['MailingLists'] ) as $listID ){
|
||||||
|
|
||||||
|
$nlType = DataObject::get_by_id( 'NewsletterType', $listID );
|
||||||
|
$nlTypeTitles[]= $nlType->Title;
|
||||||
|
$this->unsubscribeFromList( $member, DataObject::get_by_id( 'NewsletterType', $listID ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
$sORp = (sizeof($nlTypeTitles)>1)?"newsletters ":"newsletter ";
|
||||||
|
//means single or plural
|
||||||
|
$nlTypeTitles = $sORp.implode(", ", $nlTypeTitles);
|
||||||
|
$url = "unsubscribe/done/".$member->Email."/".$nlTypeTitles;
|
||||||
|
Director::redirect($url);
|
||||||
|
} else {
|
||||||
|
$form->addErrorMessage('MailingLists', 'You need to select at least one mailing list to unsubscribe from.', 'bad');
|
||||||
|
Director::redirectBack();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function unsubscribeFromList( $member, $list ) {
|
||||||
|
// track unsubscriptions
|
||||||
|
$member->Groups()->remove( $list->GroupID );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Unsubscribe_MailingListForm extends Form {
|
||||||
|
|
||||||
|
protected $memberEmail;
|
||||||
|
|
||||||
|
function __construct( $controller, $name, $member, $email ) {
|
||||||
|
|
||||||
|
$this->memberEmail = $member->Email;
|
||||||
|
|
||||||
|
$fields = new FieldSet();
|
||||||
|
$actions = new FieldSet();
|
||||||
|
|
||||||
|
// get all the mailing lists for this user
|
||||||
|
$lists = $this->getMailingLists( $member );
|
||||||
|
|
||||||
|
if( $lists ) {
|
||||||
|
$fields->push( new LabelField( 'You are subscribed to the following lists:' ) );
|
||||||
|
|
||||||
|
foreach( $lists as $list ) {
|
||||||
|
$fields->push( new CheckboxField( "MailingLists[{$list->ID}]", $list->Title ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
$actions->push( new FormAction('unsubscribe', 'Unsubscribe' ) );
|
||||||
|
} else {
|
||||||
|
$fields->push( new LabelField( "I'm sorry, but $email doesn't appear to be in any of our mailing lists." ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
parent::__construct( $controller, $name, $fields, $actions );
|
||||||
|
}
|
||||||
|
|
||||||
|
function FormAction() {
|
||||||
|
return $this->controller->Link() . "{$this->memberEmail}?executeForm=" . $this->name;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getMailingLists( $member ) {
|
||||||
|
// get all the newsletter types that the member is subscribed to
|
||||||
|
return DataObject::get( 'NewsletterType', "`MemberID`='{$member->ID}'", null, "LEFT JOIN `Group_Members` USING(`GroupID`)" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Unsubscribe_EmailAddressForm extends Form {
|
||||||
|
|
||||||
|
function __construct( $controller, $name ) {
|
||||||
|
|
||||||
|
$fields = new FieldSet(
|
||||||
|
new EmailField( 'Email', 'Email address' )
|
||||||
|
);
|
||||||
|
|
||||||
|
$actions = new FieldSet(
|
||||||
|
new FormAction( 'showlists', 'Show lists' )
|
||||||
|
);
|
||||||
|
|
||||||
|
parent::__construct( $controller, $name, $fields, $actions );
|
||||||
|
}
|
||||||
|
|
||||||
|
function FormAction() {
|
||||||
|
return parent::FormAction() . ( $_REQUEST['showqueries'] ? '&showqueries=1' : '' );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Unsubscribe_Successful extends Form {
|
||||||
|
function __construct($controller, $name){
|
||||||
|
$fields = new FieldSet();
|
||||||
|
$actions = new FieldSet();
|
||||||
|
parent::__construct($controller, $name, $fields, $actions);
|
||||||
|
}
|
||||||
|
function setSuccessfulMessage($email, $newsletterTypes) {
|
||||||
|
Requirements::css(project() . "/css/form.css");
|
||||||
|
$this->setMessage("Thank you. $email will no longer receive the $newsletterTypes.", "good");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
49
code/Newsletter/UnsubscribedList.php
Executable file
@ -0,0 +1,49 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Displays a list of all members that have unsubscribed from the list
|
||||||
|
*/
|
||||||
|
class UnsubscribedList extends FormField {
|
||||||
|
|
||||||
|
protected $nlType;
|
||||||
|
|
||||||
|
function __construct( $name, $newsletterType ) {
|
||||||
|
parent::__construct( $name, '', null );
|
||||||
|
|
||||||
|
if( is_object( $newsletterType ) )
|
||||||
|
$this->nlType = $newsletterType;
|
||||||
|
else
|
||||||
|
$this->nlType = DataObject::get_by_id( 'NewsletterType', $newsletterType );
|
||||||
|
}
|
||||||
|
|
||||||
|
function FieldHolder() {
|
||||||
|
return $this->renderWith( 'NewsletterAdmin_UnsubscribedList' );
|
||||||
|
}
|
||||||
|
|
||||||
|
function Entries() {
|
||||||
|
|
||||||
|
$id = $this->nlType->ID;
|
||||||
|
|
||||||
|
$unsubscribeRecords = DataObject::get( 'Member_UnsubscribeRecord', "`NewsletterTypeID`='$id'" );
|
||||||
|
|
||||||
|
// user_error($id, E_USER_ERROR );
|
||||||
|
|
||||||
|
if( !$unsubscribeRecords )
|
||||||
|
return null;
|
||||||
|
|
||||||
|
foreach( $unsubscribeRecords as $unsubscribeRecord ) {
|
||||||
|
if( $unsubscribeRecord ) {
|
||||||
|
$unsubscribedUsers[] = new ArrayData( array(
|
||||||
|
'Record' => $unsubscribeRecord,
|
||||||
|
'Member' => DataObject::get_by_id( 'Member', $unsubscribeRecord->MemberID )
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new DataObjectSet( $unsubscribedUsers );
|
||||||
|
}
|
||||||
|
|
||||||
|
function setController($controller) {
|
||||||
|
$this->controller = $controller;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
686
code/NewsletterAdmin.php
Executable file
@ -0,0 +1,686 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
class NewsletterAdmin extends LeftAndMain {
|
||||||
|
static $subitem_class = "Member";
|
||||||
|
|
||||||
|
static $template_path = null; // defaults to (project)/templates/email
|
||||||
|
|
||||||
|
public function init() {
|
||||||
|
// Check permissions
|
||||||
|
// if(!Member::currentUser() || !Member::currentUser()->isAdmin()) Security::permissionFailure($this);
|
||||||
|
|
||||||
|
parent::init();
|
||||||
|
/*
|
||||||
|
if(!$this->can('AdminCMS')) {
|
||||||
|
$messageSet = array(
|
||||||
|
'default' => "Enter your email address and password to access the CMS.",
|
||||||
|
'alreadyLoggedIn' => "I'm sorry, but you can't access that part of the CMS. If you want to log in as someone else, do so below",
|
||||||
|
'logInAgain' => "You have been logged out of the CMS. If you would like to log in again, enter a username and password below.",
|
||||||
|
);
|
||||||
|
|
||||||
|
Security::permissionFailure($this, $messageSet);
|
||||||
|
return;
|
||||||
|
}*/
|
||||||
|
|
||||||
|
Requirements::javascript(MCE_ROOT . "tiny_mce_src.js");
|
||||||
|
Requirements::javascript("jsparty/tiny_mce_improvements.js");
|
||||||
|
|
||||||
|
Requirements::javascript("jsparty/hover.js");
|
||||||
|
Requirements::javascript("jsparty/scriptaculous/controls.js");
|
||||||
|
|
||||||
|
Requirements::javascript("cms/javascript/SecurityAdmin.js");
|
||||||
|
|
||||||
|
Requirements::javascript("cms/javascript/LeftAndMain_left.js");
|
||||||
|
Requirements::javascript("cms/javascript/LeftAndMain_right.js");
|
||||||
|
Requirements::javascript("cms/javascript/CMSMain_left.js");
|
||||||
|
|
||||||
|
Requirements::javascript("cms/javascript/NewsletterAdmin_left.js");
|
||||||
|
Requirements::javascript("cms/javascript/NewsletterAdmin_right.js");
|
||||||
|
Requirements::javascript("sapphire/javascript/ProgressBar.js");
|
||||||
|
|
||||||
|
// 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::javascriptTemplate("cms/javascript/tinymce.template.js", array(
|
||||||
|
"ContentCSS" => project() . "/css/editor.css",
|
||||||
|
"BaseURL" => Director::absoluteBaseURL(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// needed for MemberTableField (Requirements not determined before Ajax-Call)
|
||||||
|
Requirements::javascript("jsparty/greybox/AmiJS.js");
|
||||||
|
Requirements::javascript("jsparty/greybox/greybox.js");
|
||||||
|
Requirements::javascript("sapphire/javascript/TableListField.js");
|
||||||
|
Requirements::javascript("sapphire/javascript/TableField.js");
|
||||||
|
Requirements::javascript("sapphire/javascript/ComplexTableField.js");
|
||||||
|
Requirements::javascript("cms/javascript/MemberTableField.js");
|
||||||
|
Requirements::css("jsparty/greybox/greybox.css");
|
||||||
|
Requirements::css("sapphire/css/ComplexTableField.css");
|
||||||
|
}
|
||||||
|
|
||||||
|
public function remove() {
|
||||||
|
$ids = explode( ',', $_REQUEST['csvIDs'] );
|
||||||
|
|
||||||
|
foreach( $ids as $id ) {
|
||||||
|
if( preg_match( '/^mailtype_(\d+)$/', $id, $matches ) )
|
||||||
|
$record = DataObject::get_by_id( 'NewsletterType', $matches[1] );
|
||||||
|
else if( preg_match( '/^[a-z]+_\d+_(\d+)$/', $id, $matches ) )
|
||||||
|
$record = DataObject::get_by_id( 'Newsletter', $matches[1] );
|
||||||
|
|
||||||
|
if($record) {
|
||||||
|
$record->delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
FormResponse::add("removeTreeNodeByIdx(\$('sitetree'), '$id' );");
|
||||||
|
}
|
||||||
|
|
||||||
|
FormResponse::status_message('Deleted $count items','good');
|
||||||
|
|
||||||
|
return FormResponse::respond();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getformcontent(){
|
||||||
|
Session::set('currentPage', $_REQUEST['ID']);
|
||||||
|
Session::set('currentType', $_REQUEST['type']);
|
||||||
|
if($_REQUEST['otherID'])
|
||||||
|
Session::set('currentOtherID', $_REQUEST['otherID']);
|
||||||
|
SSViewer::setOption('rewriteHashlinks', false);
|
||||||
|
$result = $this->renderWith($this->class . "_right");
|
||||||
|
return $this->getLastFormIn($result);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function shownewsletter($params) {
|
||||||
|
return $this->showWithEditForm( $params, $this->getNewsletterEditForm( $params['ID'] ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
public function showtype($params) {
|
||||||
|
return $this->showWithEditForm( $params, $this->getNewsletterTypeEditForm( $params['ID'] ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
public function removenewsletter($params) {
|
||||||
|
if( !is_numeric( $params['ID'] ) )
|
||||||
|
return '';
|
||||||
|
|
||||||
|
$newsletter = DataObject::get_by_id( 'Newsletter', $params['ID'] );
|
||||||
|
$newsletter->delete();
|
||||||
|
return 'letter-' . $params['ID'];
|
||||||
|
}
|
||||||
|
|
||||||
|
private function showWithEditForm( $params, $editForm ) {
|
||||||
|
if(isset($params['ID'])) {
|
||||||
|
Session::set('currentPage', $params['ID']);
|
||||||
|
}
|
||||||
|
if(isset($params['OtherID'])) {
|
||||||
|
Session::set('currentMember', $params['OtherID']);
|
||||||
|
}
|
||||||
|
if(Director::is_ajax()) {
|
||||||
|
SSViewer::setOption('rewriteHashlinks', false);
|
||||||
|
return $editForm->formHtmlContent();
|
||||||
|
} else {
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getEditForm( $id ) {
|
||||||
|
return $this->getNewsletterTypeEditForm( $id );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the EditForm
|
||||||
|
*/
|
||||||
|
public function EditForm() {
|
||||||
|
if((isset($_REQUEST['ID']) && $_REQUEST['Type'] == 'Newsletter') || isset($_REQUEST['action_savenewsletter'])) {
|
||||||
|
return $this->NewsletterEditForm();
|
||||||
|
} else {
|
||||||
|
return $this->TypeEditForm();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function NewsletterEditForm() {
|
||||||
|
$id = $_REQUEST['ID'] ? $_REQUEST['ID'] : $this->currentPageID();
|
||||||
|
if(!is_numeric($id)) {
|
||||||
|
$id = 0;
|
||||||
|
}
|
||||||
|
return $this->getNewsletterEditForm($id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function TypeEditForm() {
|
||||||
|
$id = isset($_REQUEST['ID']) ? $_REQUEST['ID'] : $this->currentPageID();
|
||||||
|
if(!is_numeric($id)) {
|
||||||
|
$id = 0;
|
||||||
|
}
|
||||||
|
return $this->getNewsletterTypeEditForm($id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getNewsletterTypeEditForm($id) {
|
||||||
|
if(!is_numeric($id)) {
|
||||||
|
$id = Session::get('currentPage');
|
||||||
|
}
|
||||||
|
if( is_a( $id, 'NewsletterType' ) ) {
|
||||||
|
$mailType = $id;
|
||||||
|
$id = $mailType->ID;
|
||||||
|
} else {
|
||||||
|
if($id && is_numeric($id)) {
|
||||||
|
$mailType = DataObject::get_by_id( 'NewsletterType', $id );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(isset($mailType) && is_object($mailType) && $mailType->GroupID) {
|
||||||
|
$group = DataObject::get_one("Group", "ID = $mailType->GroupID");
|
||||||
|
}
|
||||||
|
|
||||||
|
if(isset($mailType)) {
|
||||||
|
$fields = new FieldSet(
|
||||||
|
new TextField("Title", "Newsletter Type"),
|
||||||
|
new TextField("FromEmail", "Send newsletters from"),
|
||||||
|
new TabSet("Root",
|
||||||
|
new Tab("Drafts",
|
||||||
|
$draftList = new NewsletterList("Draft", $mailType, "Draft")
|
||||||
|
),
|
||||||
|
new TabSet("Sent",
|
||||||
|
new Tab("Sent",
|
||||||
|
$sendList = new NewsletterList("Send", $mailType, "Send")
|
||||||
|
),
|
||||||
|
new Tab("Unsubscribed",
|
||||||
|
$unsubscribedList = new UnsubscribedList("Unsubscribed", $mailType)
|
||||||
|
),
|
||||||
|
new Tab("Bounced",
|
||||||
|
$bouncedList = new BouncedList("Bounced", $mailType )
|
||||||
|
)
|
||||||
|
),
|
||||||
|
new TabSet("Recipients",
|
||||||
|
new Tab( "Recipients",
|
||||||
|
$recipients = new MemberTableField(
|
||||||
|
$this,
|
||||||
|
"Recipients",
|
||||||
|
$group
|
||||||
|
)
|
||||||
|
),
|
||||||
|
new Tab( "Import",
|
||||||
|
$importField = new RecipientImportField("ImportFile","Import from file", $group )
|
||||||
|
)
|
||||||
|
),
|
||||||
|
new Tab("Template",
|
||||||
|
$templates = new TemplateList("Template","Template", $mailType->Template, self::template_path())
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
$draftList->setController($this);
|
||||||
|
$sendList->setController($this);
|
||||||
|
$recipients->setController($this);
|
||||||
|
$templates->setController($this);
|
||||||
|
$importField->setController($this);
|
||||||
|
$unsubscribedList->setController($this);
|
||||||
|
$bouncedList->setController($this);
|
||||||
|
|
||||||
|
$importField->setTypeID( $id );
|
||||||
|
|
||||||
|
$fields->push($idField = new HiddenField("ID"));
|
||||||
|
$fields->push( new HiddenField( "executeForm", "", "TypeEditForm" ) );
|
||||||
|
$idField->setValue($id);
|
||||||
|
// $actions = new FieldSet(new FormAction('adddraft', 'Add Draft'));
|
||||||
|
|
||||||
|
$actions = new FieldSet(new FormAction('save','Save'));
|
||||||
|
|
||||||
|
$form = new Form($this, "EditForm", $fields, $actions);
|
||||||
|
$form->loadDataFrom(array(
|
||||||
|
'Title' => $mailType->Title,
|
||||||
|
'FromEmail' => $mailType->FromEmail
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
$form = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $form;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
public function showmailinglist($params) {
|
||||||
|
return $this->showWithEditForm( $params, $this->getMailinglistEditForm( $params['ID'] ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getMailinglistEditForm($id) {
|
||||||
|
if(!is_numeric($id)) {
|
||||||
|
$id = $_SESSION['currentPage'];
|
||||||
|
}
|
||||||
|
if( is_a( $id, 'NewsletterType' ) ) {
|
||||||
|
$mailType = $id;
|
||||||
|
$id = $mailType->ID;
|
||||||
|
} else {
|
||||||
|
if($id && is_numeric($id)) {
|
||||||
|
$mailType = DataObject::get_by_id( 'NewsletterType', $id );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if($mailType->GroupID) {
|
||||||
|
$group = DataObject::get_one("Group", "ID = $mailType->GroupID");
|
||||||
|
}
|
||||||
|
|
||||||
|
if($mailType) {
|
||||||
|
$fields = new FieldSet(
|
||||||
|
new TabSet("Recipients",
|
||||||
|
new Tab( "Recipients",
|
||||||
|
$recipients = new MemberTableField(
|
||||||
|
$this,
|
||||||
|
"Recipients",
|
||||||
|
$group
|
||||||
|
)
|
||||||
|
),
|
||||||
|
new Tab( "Import",
|
||||||
|
$importField = new RecipientImportField("ImportFile","Import from file", $group )
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
$recipients->setController($this);
|
||||||
|
$importField->setController($this);
|
||||||
|
$importField->setTypeID( $id );
|
||||||
|
|
||||||
|
$fields->push($idField = new HiddenField("ID"));
|
||||||
|
$fields->push( new HiddenField( "executeForm", "", "TypeEditForm" ) );
|
||||||
|
$idField->setValue($id);
|
||||||
|
|
||||||
|
$form = new Form($this, "EditForm", $fields, new FieldSet());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reloads the list of recipients via ajax
|
||||||
|
*/
|
||||||
|
function getrecipientslist() {
|
||||||
|
if( $_REQUEST['ajax'] ) {
|
||||||
|
$newsletterType = DataObject::get_by_id('NewsletterType', $this->urlParams['ID'] );
|
||||||
|
$memberList = new MemberTableField($this, "Recipients", $newsletterType->Group() );
|
||||||
|
return $memberList->FieldHolder();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function template_path() {
|
||||||
|
if(self::$template_path) return self::$template_path;
|
||||||
|
else return self::$template_path = project() . '/templates/email';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function showdraft( $params ) {
|
||||||
|
return $this->showWithEditForm( $params, $this->getNewsletterEditForm( $params['ID'] ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getNewsletterEditForm($myId){
|
||||||
|
|
||||||
|
$email = DataObject::get_by_id("Newsletter", $myId);
|
||||||
|
if($email) {
|
||||||
|
|
||||||
|
$fields = $email->getCMSFields($this);
|
||||||
|
$fields->push($idField = new HiddenField("ID"));
|
||||||
|
$idField->setValue($myId);
|
||||||
|
$fields->push($typeField = new HiddenField("Type"));
|
||||||
|
$typeField->setValue('Newsletter');
|
||||||
|
//$fields->push(new HiddenField("executeForm", "", "EditForm") );
|
||||||
|
|
||||||
|
$actions = new FieldSet();
|
||||||
|
|
||||||
|
if( $email->SentDate )
|
||||||
|
$actions->push(new FormAction('send','Resend'));
|
||||||
|
else
|
||||||
|
$actions->push(new FormAction('send','Send'));
|
||||||
|
|
||||||
|
$actions->push(new FormAction('save','Save'));
|
||||||
|
|
||||||
|
$form = new Form($this, "EditForm", $fields, $actions);
|
||||||
|
$form->loadDataFrom($email);
|
||||||
|
|
||||||
|
if($email->Status != 'Draft') {
|
||||||
|
$form->makeReadonly();
|
||||||
|
}
|
||||||
|
|
||||||
|
// user_error( $form->FormAction(), E_USER_ERROR );
|
||||||
|
|
||||||
|
return $form;
|
||||||
|
} else {
|
||||||
|
user_error( 'Unknown Email ID: ' . $myId, E_USER_ERROR );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function SendProgressBar() {
|
||||||
|
$progressBar = new ProgressBar( 'SendProgressBar', 'Sending emails...' );
|
||||||
|
return $progressBar->FieldHolder();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function sendnewsletter( /*$data, $form = null*/ ) {
|
||||||
|
|
||||||
|
$id = $_REQUEST['ID'] ? $_REQUEST['ID'] : $_REQUEST['NewsletterID'];
|
||||||
|
|
||||||
|
if( !$id ) {
|
||||||
|
FormResponse::status_message('No newsletter specified','bad');
|
||||||
|
return FormResponse::respond();
|
||||||
|
}
|
||||||
|
|
||||||
|
$newsletter = DataObject::get_by_id("Newsletter", $id);
|
||||||
|
$nlType = $newsletter->getNewsletterType();
|
||||||
|
|
||||||
|
$e = new Newsletter_Email($nlType);
|
||||||
|
$e->Body = $body = $newsletter->Content;
|
||||||
|
$e->Subject = $subject = $newsletter->Subject;
|
||||||
|
|
||||||
|
// TODO Make this dynamic
|
||||||
|
|
||||||
|
if( $nlType && $nlType->FromEmail )
|
||||||
|
$e->From = $from = $nlType->FromEmail;
|
||||||
|
else
|
||||||
|
$e->From = $from = Email::getAdminEmail();
|
||||||
|
|
||||||
|
$e->To = $_REQUEST['TestEmail'];
|
||||||
|
$e->setTemplate( $nlType->Template );
|
||||||
|
|
||||||
|
$messageID = base64_encode( $newsletter->ID . '_' . date( 'd-m-Y H:i:s' ) );
|
||||||
|
|
||||||
|
switch($_REQUEST['SendType']) {
|
||||||
|
case "Test":
|
||||||
|
if($_REQUEST['TestEmail']) {
|
||||||
|
if( $nlType->Template ) {
|
||||||
|
self::sendToAddress( $e, $_REQUEST['TestEmail'], $messageID );
|
||||||
|
FormResponse::status_message('Sent test to ' . $_REQUEST['TestEmail'],'good');
|
||||||
|
} else {
|
||||||
|
FormResponse::status_message('No template selected','bad');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
FormResponse::status_message('Please enter an email address','bad');
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "List":
|
||||||
|
echo self::sendToList( $subject, $body, $from, $newsletter, $nlType, $messageID );
|
||||||
|
}
|
||||||
|
|
||||||
|
return FormResponse::respond();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static function sendToAddress( $email, $address, $messageID = null ) {
|
||||||
|
$email->To = $address;
|
||||||
|
$email->send();
|
||||||
|
}
|
||||||
|
|
||||||
|
static function sendToList( $subject, $body, $from, $newsletter, $nlType, $messageID = null ) {
|
||||||
|
|
||||||
|
$emailProcess = new NewsletterEmailProcess( $subject, $body, $from, $newsletter, $nlType, $messageID );
|
||||||
|
return $emailProcess->start();
|
||||||
|
|
||||||
|
/*$groupID = $nlType->GroupID;
|
||||||
|
|
||||||
|
$members = DataObject::get( 'Member', "`GroupID`='$groupID'", null, "INNER JOIN `Group_Members` ON `MemberID`=`Member`.`ID`" );
|
||||||
|
|
||||||
|
// user_error( $members, E_USER_ERROR );
|
||||||
|
|
||||||
|
if( !$members )
|
||||||
|
return "statusMessage('Unable to retrieve members from mailing list', 'bad' )";
|
||||||
|
|
||||||
|
foreach( $members as $member ) {
|
||||||
|
// check to see if the user has unsubscribed from the mailing list
|
||||||
|
$unsubscribeRecord = DataObject::get_one('Member_UnsubscribeRecord', "`MemberID`= {$member->ID} AND `NewsletterTypeID`={$nlType->ID}");
|
||||||
|
|
||||||
|
if( !$unsubscribeRecord ) {
|
||||||
|
$e = new Newsletter_Email($nlType);
|
||||||
|
$e->Body = $body;
|
||||||
|
$e->Subject =$subject;
|
||||||
|
$e->From = $from;
|
||||||
|
$e->setTemplate( $nlType->Template );
|
||||||
|
|
||||||
|
$e->populateTemplate( array( 'Member' => $member, 'FirstName' => $member->FirstName ) );
|
||||||
|
$this->sendToAddress( $e, $member->Email, $messageID );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if( $newsletter->Sent )
|
||||||
|
$resent = true;
|
||||||
|
|
||||||
|
$newsletter->Sent = 'now';
|
||||||
|
$newsletter->Status = 'Send';
|
||||||
|
$newsletter->write();
|
||||||
|
|
||||||
|
if( $resent )
|
||||||
|
return "resent_ok( '{$nlType->ID}', '{$newsletter->ID}' )";
|
||||||
|
else
|
||||||
|
return "draft_sent_ok( '{$nlType->ID}', '{$newsletter->ID}' )";*/
|
||||||
|
}
|
||||||
|
|
||||||
|
public function save($urlParams, $form) {
|
||||||
|
if( $_REQUEST['Type'] && $_REQUEST['Type'] == 'Newsletter' )
|
||||||
|
return $this->savenewsletter( $urlParams, $form );
|
||||||
|
|
||||||
|
$id = $_REQUEST['ID'];
|
||||||
|
$record = DataObject::get_one('NewsletterType', "`$className`.ID = $id");
|
||||||
|
|
||||||
|
// Is the template attached to the type, or the newsletter itself?
|
||||||
|
|
||||||
|
$record->Template = addslashes( $_REQUEST['Template'] );
|
||||||
|
|
||||||
|
$form->saveInto($record);
|
||||||
|
$record->write();
|
||||||
|
|
||||||
|
FormResponse::set_node_title("mailtype_$id", $record->Title);
|
||||||
|
FormResponse::status_message('Saved', 'good');
|
||||||
|
|
||||||
|
return FormResponse::respond();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function savenewsletter($urlParams, $form) {
|
||||||
|
|
||||||
|
$id = $_REQUEST['ID'];
|
||||||
|
$record = DataObject::get_one('Newsletter', "`$className`.ID = $id");
|
||||||
|
|
||||||
|
// Is the template attached to the type, or the newsletter itself?
|
||||||
|
$type = $record->getNewsletterType();
|
||||||
|
|
||||||
|
$record->Subject = $urlParams['Subject'];
|
||||||
|
$record->Content = $urlParams['Content'];
|
||||||
|
|
||||||
|
$record->write();
|
||||||
|
|
||||||
|
$id = 'draft_'.$record->ParentID.'_'.$record->ID;
|
||||||
|
|
||||||
|
FormResponse::set_node_title($id, $record->Title);
|
||||||
|
FormResponse::status_message('Saved', 'good');
|
||||||
|
|
||||||
|
return FormResponse::respond();
|
||||||
|
}
|
||||||
|
|
||||||
|
function NewsletterAdminSiteTree() {
|
||||||
|
return $this->getsitetree();
|
||||||
|
}
|
||||||
|
|
||||||
|
function getsitetree() {
|
||||||
|
return $this->renderWith('NewsletterAdmin_SiteTree');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function AddRecordForm() {
|
||||||
|
$m = new MemberTableField($this,"Members", $this->currentPageID());
|
||||||
|
return $m->AddRecordForm();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ajax autocompletion
|
||||||
|
*/
|
||||||
|
public function autocomplete() {
|
||||||
|
$fieldName = $this->urlParams['ID'];
|
||||||
|
$fieldVal = $_REQUEST[$fieldName];
|
||||||
|
|
||||||
|
$matches = DataObject::get("Member","$fieldName LIKE '" . addslashes($fieldVal) . "%'");
|
||||||
|
if($matches) {
|
||||||
|
echo "<ul>";
|
||||||
|
foreach($matches as $match) {
|
||||||
|
$data = $match->FirstName;
|
||||||
|
$data .= ",$match->Surname";
|
||||||
|
$data .= ",$match->Email";
|
||||||
|
$data .= ",$match->Password";
|
||||||
|
echo "<li>" . $match->$fieldName . "<span class=\"informal\">($match->FirstName $match->Surname, $match->Email)</span><span class=\"informal data\">$data</li>";
|
||||||
|
}
|
||||||
|
echo "</ul>";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function savemember() {
|
||||||
|
$data = $_REQUEST;
|
||||||
|
|
||||||
|
$className = $this->stat('subitem_class');
|
||||||
|
|
||||||
|
$id = $_REQUEST['ID'];
|
||||||
|
if($id == 'new') $id = null;
|
||||||
|
|
||||||
|
if($id) {
|
||||||
|
$record = DataObject::get_one($className, "`$className`.ID = $id");
|
||||||
|
} else {
|
||||||
|
// send out an email to notify the user that they have been subscribed
|
||||||
|
$record = new $className();
|
||||||
|
}
|
||||||
|
|
||||||
|
$record->update($data);
|
||||||
|
$record->ID = $id;
|
||||||
|
$record->write();
|
||||||
|
|
||||||
|
$record->Groups()->add($data['GroupID']);
|
||||||
|
|
||||||
|
$FirstName = Convert::raw2js($record->FirstName);
|
||||||
|
$Surname = Convert::raw2js($record->Surname);
|
||||||
|
$Email = Convert::raw2js($record->Email);
|
||||||
|
$Password = Convert::raw2js($record->Password);
|
||||||
|
$response = <<<JS
|
||||||
|
$('MemberList').setRecordDetails($record->ID, {
|
||||||
|
FirstName : "$FirstName",
|
||||||
|
Surname : "$Surname",
|
||||||
|
Email : "$Email"
|
||||||
|
});
|
||||||
|
$('MemberList').clearAddForm();
|
||||||
|
JS;
|
||||||
|
FormResponse::add($response);
|
||||||
|
FormResponse::status_message('Saved', 'good');
|
||||||
|
|
||||||
|
return FormResponse::respond();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function NewsletterTypes() {
|
||||||
|
return DataObject::get("NewsletterType","");
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addgroup() {
|
||||||
|
$parent = $_REQUEST['ParentID'] ? $_REQUEST['ParentID'] : 0;
|
||||||
|
$p = new Group();
|
||||||
|
$p->Title = "New Group";
|
||||||
|
$p->Code = "new-group";
|
||||||
|
$p->ParentID = $parent;
|
||||||
|
$p->write();
|
||||||
|
|
||||||
|
$this->returnItemToUser($p);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addtype( $params ) {
|
||||||
|
switch( $_REQUEST['PageType'] ) {
|
||||||
|
case 'type':
|
||||||
|
$form = $this->getNewsletterTypeEditForm( $this->newNewsletterType() );
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
$form = $this->getNewsletterEditForm( $this->newDraft( $_REQUEST['ParentID'] ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->showWithEditForm( $_REQUEST, $form );
|
||||||
|
}
|
||||||
|
|
||||||
|
public function adddraft( $data, $form ) {
|
||||||
|
$this->save( $data, $form );
|
||||||
|
$draftID = $this->newDraft( $_REQUEST['ID'] );
|
||||||
|
return $this->getNewsletterEditForm( $draftID );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new newsletter type
|
||||||
|
*/
|
||||||
|
private function newNewsletterType() {
|
||||||
|
// create a new group for the newsletter
|
||||||
|
$newGroup = new Group();
|
||||||
|
$newGroup->Title = "New mailing list";
|
||||||
|
$newGroup->Code = "new-mailing-list";
|
||||||
|
$newGroup->write();
|
||||||
|
|
||||||
|
// create the new type
|
||||||
|
$newsletterType = new NewsletterType();
|
||||||
|
$newsletterType->Title = 'New newsletter type';
|
||||||
|
$newsletterType->GroupID = $newGroup->ID;
|
||||||
|
$newsletterType->write();
|
||||||
|
|
||||||
|
// return the contents of the site tree
|
||||||
|
return $newsletterType;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function newDraft( $parentID ) {
|
||||||
|
if(!$parentID || !is_numeric( $parentID)) {
|
||||||
|
$parent = DataObject::get_one("NewsletterType");
|
||||||
|
$parentID = $parent->ID;
|
||||||
|
}
|
||||||
|
if( $parentID && is_numeric( $parentID ) ) {
|
||||||
|
$newsletter = new Newsletter();
|
||||||
|
$newsletter->Status = 'Draft';
|
||||||
|
$newsletter->Title = $newsletter->Subject = 'New draft newsletter';
|
||||||
|
$newsletter->ParentID = $parentID;
|
||||||
|
$newsletter->write();
|
||||||
|
} else {
|
||||||
|
user_error( "You must first create a newsletter type before creating a draft", E_USER_ERROR );
|
||||||
|
}
|
||||||
|
|
||||||
|
return $newsletter->ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function newmember() {
|
||||||
|
Session::clear('currentMember');
|
||||||
|
$newMemberForm = array(
|
||||||
|
"MemberForm" => $this->getMemberForm('new'),
|
||||||
|
);
|
||||||
|
|
||||||
|
if(Director::is_ajax()) {
|
||||||
|
SSViewer::setOption('rewriteHashlinks', false);
|
||||||
|
$customised = $this->customise($newMemberForm);
|
||||||
|
$result = $customised->renderWith($this->class . "_rightbottom");
|
||||||
|
$parts = split('</?form[^>]*>', $result);
|
||||||
|
echo $parts[1];
|
||||||
|
|
||||||
|
} else {
|
||||||
|
return $newMemberForm;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function EditedMember() {
|
||||||
|
if(Session::get('currentMember'))
|
||||||
|
return DataObject::get_by_id("Member", Session::get('currentMember'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function Link($action = null) {
|
||||||
|
if(!$action) $action = "index";
|
||||||
|
return "admin/newsletter/$action/" . $this->currentPageID();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function displayfilefield() {
|
||||||
|
|
||||||
|
$id = $this->urlParams['ID'];
|
||||||
|
|
||||||
|
return $this->customise( array( 'ID' => $id, "UploadForm" => $this->UploadForm() ) )->renderWith('Newsletter_RecipientImportField');
|
||||||
|
}
|
||||||
|
|
||||||
|
function UploadForm( $id = null ) {
|
||||||
|
|
||||||
|
if( !$id )
|
||||||
|
$id = $this->urlParams['ID'];
|
||||||
|
|
||||||
|
$fields = new FieldSet(
|
||||||
|
new FileField( "ImportFile", "" ),
|
||||||
|
//new HiddenField( "action_import", "", "1" ),
|
||||||
|
new HiddenField( "ID", "", $id )
|
||||||
|
);
|
||||||
|
|
||||||
|
$actions = new FieldSet(
|
||||||
|
new FormAction( "action_import", "Show contents" )
|
||||||
|
);
|
||||||
|
|
||||||
|
return new RecipientImportField_UploadForm( $this, "UploadForm", $fields, $actions );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
320
code/PageTypes/UserDefinedForm.php
Executable file
@ -0,0 +1,320 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
class UserDefinedForm extends Page {
|
||||||
|
static $add_action = "a contact form";
|
||||||
|
|
||||||
|
static $icon = "cms/images/treeicons/task";
|
||||||
|
|
||||||
|
// a list of groups that are permitted to create pages of this type.
|
||||||
|
/*static $can_create = array(
|
||||||
|
'Administrators'
|
||||||
|
);*/
|
||||||
|
|
||||||
|
static $need_permission = 'ADMIN';
|
||||||
|
|
||||||
|
static $db = array(
|
||||||
|
"EmailTo" => "Varchar",
|
||||||
|
"EmailOnSubmit" => "Boolean",
|
||||||
|
"SubmitButtonText" => "Varchar",
|
||||||
|
"OnCompleteMessage" => "HTMLText"
|
||||||
|
);
|
||||||
|
|
||||||
|
static $defaults = array(
|
||||||
|
"OnCompleteMessage" => "<p>Thanks, we've received your submission.</p>",
|
||||||
|
);
|
||||||
|
|
||||||
|
static $has_many = array(
|
||||||
|
"Fields" => "EditableFormField",
|
||||||
|
"Submissions" => "SubmittedForm"
|
||||||
|
);
|
||||||
|
|
||||||
|
protected $fields;
|
||||||
|
|
||||||
|
function getCMSFields($cms) {
|
||||||
|
$fields = parent::getCMSFields($cms);
|
||||||
|
|
||||||
|
$fields->addFieldToTab("Root.Form", new FieldEditor("Fields", "Fields", "", $this ));
|
||||||
|
$fields->addFieldToTab("Root.Submissions", new SubmittedFormReportField( "Reports", "Received Submissions", "", $this ) );
|
||||||
|
$fields->addFieldToTab("Root.Content.On complete", new HtmlEditorField( "OnCompleteMessage", "Show on completion",3,"",$this->OnCompleteMessage, $this ) );
|
||||||
|
|
||||||
|
return $fields;
|
||||||
|
}
|
||||||
|
|
||||||
|
function FilterForm() {
|
||||||
|
// Build fields
|
||||||
|
$fields = new FieldSet();
|
||||||
|
$required = array();
|
||||||
|
|
||||||
|
foreach( $this->Fields() as $field ) {
|
||||||
|
$fields->push( $field->getFilterField() );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build actions
|
||||||
|
$actions = new FieldSet(
|
||||||
|
new FormAction( "filter", "Submit" )
|
||||||
|
);
|
||||||
|
|
||||||
|
// set the name of the form
|
||||||
|
return new Form( $this, "Form", $fields, $actions );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter the submissions by the given criteria
|
||||||
|
*/
|
||||||
|
function filter( $data, $form ) {
|
||||||
|
|
||||||
|
$filterClause = array( "`SubmittedForm`.`ParentID` = '{$this->ID}'" );
|
||||||
|
|
||||||
|
$keywords = preg_split( '/\s+/', $data['FilterKeyword'] );
|
||||||
|
|
||||||
|
$keywordClauses = array();
|
||||||
|
|
||||||
|
// combine all keywords into one clause
|
||||||
|
foreach( $keywords as $keyword ) {
|
||||||
|
|
||||||
|
// escape %, \ and _ in the keyword. These have special meanings in a LIKE string
|
||||||
|
$keyword = preg_replace( '/([%_])/', '\\\\1', addslashes( $keyword ) );
|
||||||
|
|
||||||
|
$keywordClauses[] = "`Value` LIKE '%$keyword%'";
|
||||||
|
}
|
||||||
|
|
||||||
|
if( count( $keywordClauses ) > 0 ) {
|
||||||
|
$filterClause[] = "( " . implode( ' OR ', $keywordClauses ) . ")";
|
||||||
|
$searchQuery = 'keywords \'' . implode( "', '", $keywords ) . '\' ';
|
||||||
|
}
|
||||||
|
|
||||||
|
$fromDate = addslashes( $data['FilterFromDate'] );
|
||||||
|
$toDate = addslashes( $data['FilterToDate'] );
|
||||||
|
|
||||||
|
// use date objects to convert date to value expected by database
|
||||||
|
if( ereg('^([0-9]+)/([0-9]+)/([0-9]+)$', $fromDate, $parts) )
|
||||||
|
$fromDate = $parts[3] . '-' . $parts[2] . '-' . $parts[1];
|
||||||
|
|
||||||
|
if( ereg('^([0-9]+)/([0-9]+)/([0-9]+)$', $toDate, $parts) )
|
||||||
|
$toDate = $parts[3] . '-' . $parts[2] . '-' . $parts[1];
|
||||||
|
|
||||||
|
if( $fromDate ) {
|
||||||
|
$filterClause[] = "`SubmittedForm`.`Created` >= '$fromDate'";
|
||||||
|
$searchQuery .= 'from ' . $fromDate . ' ';
|
||||||
|
}
|
||||||
|
|
||||||
|
if( $toDate ) {
|
||||||
|
$filterClause[] = "`SubmittedForm`.`Created` <= '$toDate'";
|
||||||
|
$searchQuery .= 'to ' . $toDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
$submittedValues = DataObject::get( 'SubmittedFormField', implode( ' AND ', $filterClause ), "", "INNER JOIN `SubmittedForm` ON `SubmittedFormField`.`ParentID`=`SubmittedForm`.`ID`" );
|
||||||
|
|
||||||
|
if( !$submittedValues || $submittedValues->Count() == 0 )
|
||||||
|
return "No matching results found";
|
||||||
|
|
||||||
|
$submissions = $submittedValues->groupWithParents( 'ParentID', 'SubmittedForm' );
|
||||||
|
|
||||||
|
if( !$submissions || $submissions->Count() == 0 )
|
||||||
|
return "No matching results found";
|
||||||
|
|
||||||
|
return $submissions->customise(
|
||||||
|
array( 'Submissions' => $submissions )
|
||||||
|
)->renderWith( 'SubmittedFormReportField_Reports' );
|
||||||
|
}
|
||||||
|
|
||||||
|
function ReportFilterForm() {
|
||||||
|
return new SubmittedFormReportField_FilterForm( $this, 'ReportFilterForm' );
|
||||||
|
}
|
||||||
|
|
||||||
|
function delete() {
|
||||||
|
// remove all the fields associated with this page
|
||||||
|
foreach( $this->Fields() as $field )
|
||||||
|
$field->delete();
|
||||||
|
|
||||||
|
parent::delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function customFormActions( $isReadonly = false ) {
|
||||||
|
return new FieldSet( new TextField( "SubmitButtonText", "Text on submit button:", $this->SubmitButtonText ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Duplicate this UserDefinedForm page, and its form fields.
|
||||||
|
* Submissions, on the other hand, won't be duplicated.
|
||||||
|
*/
|
||||||
|
public function duplicate() {
|
||||||
|
$page = parent::duplicate();
|
||||||
|
foreach($this->Fields() as $field) {
|
||||||
|
$newField = $field->duplicate();
|
||||||
|
$newField->ParentID = $page->ID;
|
||||||
|
$newField->write();
|
||||||
|
}
|
||||||
|
return $page;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class UserDefinedForm_Controller extends Page_Controller {
|
||||||
|
|
||||||
|
function init() {
|
||||||
|
Requirements::javascript('jsparty/prototype-safe.js');
|
||||||
|
Requirements::javascript('jsparty/behaviour.js');
|
||||||
|
Requirements::javascript('mot/javascript/UserDefinedForm.js');
|
||||||
|
|
||||||
|
parent::init();
|
||||||
|
}
|
||||||
|
|
||||||
|
function Form() {
|
||||||
|
// Build fields
|
||||||
|
$fields = new FieldSet();
|
||||||
|
$required = array();
|
||||||
|
|
||||||
|
if( !$this->SubmitButtonText )
|
||||||
|
$this->SubmitButtonText = 'Submit';
|
||||||
|
|
||||||
|
foreach( $this->Fields() as $field ) {
|
||||||
|
$fields->push( $field->getFormField() );
|
||||||
|
if( $field->Required )
|
||||||
|
$required[] = $field->Name;
|
||||||
|
}
|
||||||
|
|
||||||
|
$fields->push( new HiddenField( "Referrer", "", $_SERVER['HTTP_REFERER'] ) );
|
||||||
|
|
||||||
|
// Build actions
|
||||||
|
$actions = new FieldSet(
|
||||||
|
new FormAction( "process", $this->SubmitButtonText )
|
||||||
|
);
|
||||||
|
|
||||||
|
// set the name of the form
|
||||||
|
$form = new Form( $this, "Form", $fields, $actions, new RequiredFields( $required ) );
|
||||||
|
$form->loadDataFrom($this->failover);
|
||||||
|
return $form;
|
||||||
|
}
|
||||||
|
|
||||||
|
function ReportFilterForm() {
|
||||||
|
return new SubmittedFormReportField_FilterForm( $this, 'ReportFilterForm' );
|
||||||
|
}
|
||||||
|
|
||||||
|
function process( $data, $form ) {
|
||||||
|
$submittedForm = new SubmittedForm();
|
||||||
|
$submittedForm->SubmittedBy = Member::currentUser();
|
||||||
|
$submittedForm->ParentID = $this->ID;
|
||||||
|
$submittedForm->Recipient = $this->EmailTo;
|
||||||
|
$submittedForm->write();
|
||||||
|
|
||||||
|
$values = array();
|
||||||
|
$recipientAddresses = array();
|
||||||
|
$sendCopy = false;
|
||||||
|
|
||||||
|
$submittedFields = new DataObjectSet();
|
||||||
|
foreach( $this->Fields() as $field ) {
|
||||||
|
$submittedField = new SubmittedFormField();
|
||||||
|
$submittedField->ParentID = $submittedForm->ID;
|
||||||
|
$submittedField->Name = $field->Name;
|
||||||
|
$submittedField->Title = $field->Title;
|
||||||
|
|
||||||
|
if( $field->hasMethod( 'getValueFromData' ) )
|
||||||
|
$submittedField->Value = $field->getValueFromData( $data );
|
||||||
|
else
|
||||||
|
$submittedField->Value = $data[$field->Name];
|
||||||
|
|
||||||
|
$submittedField->write();
|
||||||
|
$submittedFields->push($submittedField);
|
||||||
|
|
||||||
|
if(!empty( $data[$field->Name])){
|
||||||
|
// execute the appropriate functionality based on the form field.
|
||||||
|
switch($field->ClassName){
|
||||||
|
|
||||||
|
case "EditableEmailField" :
|
||||||
|
|
||||||
|
if($field->SendCopy){
|
||||||
|
$recipientAddresses[] = $data[$field->Name];
|
||||||
|
$sendCopy = true;
|
||||||
|
$values[$field->Title] = '<a style="white-space: nowrap" href="mailto:'.$data[$field->Name].'">'.$data[$field->Name].'</a>';
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "EditableFileField" :
|
||||||
|
|
||||||
|
// Returns a file type which we attach to the email.
|
||||||
|
$submittedfile = $field->createSubmittedField($data[$field->Name], $submittedForm);
|
||||||
|
$file = $submittedfile->UploadedFile();
|
||||||
|
|
||||||
|
$filename = $file->getFilename();
|
||||||
|
|
||||||
|
// Attach the file if its less than 1MB, provide a link if its over.
|
||||||
|
if($file->getAbsoluteSize() < 1024*1024*1){
|
||||||
|
$attachments[] = $file;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Always provide the link if present.
|
||||||
|
if($file->ID) {
|
||||||
|
$submittedField->Value = $values[$field->Title] = "<a href=\"". $filename ."\" title=\"". Director::absoluteBaseURL(). $filename. "\">Uploaded to: ". Director::absoluteBaseURL(). $filename . "</a>";
|
||||||
|
} else {
|
||||||
|
$submittedField->Value = $values[$field->Title] = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
}elseif( $field->hasMethod( 'getValueFromData' ) ) {
|
||||||
|
$values[$field->Title] = Convert::linkIfMatch($field->getValueFromData( $data ));
|
||||||
|
|
||||||
|
} else {
|
||||||
|
$values[$field->Title] = Convert::linkIfMatch($data[$field->Name]);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if( $this->EmailOnSubmit || $sendCopy ) {
|
||||||
|
$emailData = array(
|
||||||
|
"Recipient" => $this->EmailTo,
|
||||||
|
"Sender" => Member::currentUser(),
|
||||||
|
"Fields" => $submittedFields,
|
||||||
|
);
|
||||||
|
|
||||||
|
$email = new UserDefinedForm_SubmittedFormEmail($submittedFields);
|
||||||
|
$email->populateTemplate($emailData);
|
||||||
|
$email->setTo( $this->EmailTo );
|
||||||
|
$email->setSubject( $this->Title );
|
||||||
|
|
||||||
|
// add attachments to email (<1MB)
|
||||||
|
if($attachments){
|
||||||
|
foreach($attachments as $file){
|
||||||
|
$email->attachFile($filename,$filename);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$email->send();
|
||||||
|
|
||||||
|
// send to each of email fields
|
||||||
|
foreach( $recipientAddresses as $addr ) {
|
||||||
|
$email->setTo( $addr );
|
||||||
|
$email->send();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$custom = $this->customise(array(
|
||||||
|
"Content" => $this->customise( array( 'Link' => $data['Referrer'] ) )->renderWith('ReceivedFormSubmission'),
|
||||||
|
"Form" => " ",
|
||||||
|
));
|
||||||
|
|
||||||
|
return $custom->renderWith('Page');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class UserDefinedForm_SubmittedFormEmail extends Email_Template {
|
||||||
|
protected $ss_template = "SubmittedFormEmail";
|
||||||
|
protected $from = '$Sender.Email';
|
||||||
|
protected $to = '$Recipient.Email';
|
||||||
|
protected $subject = "Submission of form";
|
||||||
|
protected $data;
|
||||||
|
|
||||||
|
function __construct($values) {
|
||||||
|
parent::__construct();
|
||||||
|
|
||||||
|
$this->data = $values;
|
||||||
|
}
|
||||||
|
|
||||||
|
function Data() {
|
||||||
|
return $this->data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
129
code/ReportAdmin.php
Executable file
@ -0,0 +1,129 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
class ReportAdmin extends LeftAndMain {
|
||||||
|
static $subitem_class = "GrantObject";
|
||||||
|
|
||||||
|
static $template_path = null; // defaults to (project)/templates/email
|
||||||
|
|
||||||
|
public function init() {
|
||||||
|
parent::init();
|
||||||
|
|
||||||
|
Requirements::javascript(MCE_ROOT . "tiny_mce_src.js");
|
||||||
|
Requirements::javascript("jsparty/tiny_mce_improvements.js");
|
||||||
|
|
||||||
|
Requirements::javascript("jsparty/hover.js");
|
||||||
|
Requirements::javascript("jsparty/scriptaculous/controls.js");
|
||||||
|
|
||||||
|
Requirements::javascript("cms/javascript/SecurityAdmin.js");
|
||||||
|
|
||||||
|
Requirements::javascript("cms/javascript/LeftAndMain_left.js");
|
||||||
|
Requirements::javascript("cms/javascript/LeftAndMain_right.js");
|
||||||
|
Requirements::javascript("cms/javascript/CMSMain_left.js");
|
||||||
|
|
||||||
|
|
||||||
|
Requirements::javascript("cms/javascript/ReportAdmin_left.js");
|
||||||
|
Requirements::javascript("cms/javascript/ReportAdmin_right.js");
|
||||||
|
|
||||||
|
// TODO Find a better solution to integrate optional Requirements in a specific order
|
||||||
|
if(Director::fileExists("ecommerce/css/DataReportCMSMain.css")) {
|
||||||
|
Requirements::css("ecommerce/css/DataReportCMSMain.css");
|
||||||
|
}
|
||||||
|
if(Director::fileExists("ecommerce/css/DataReportCMSMain.css")) {
|
||||||
|
Requirements::javascript("ecommerce/javascript/DataReport.js");
|
||||||
|
}
|
||||||
|
if(Director::fileExists(project() . "/css/DataReportCMSMain.css")) {
|
||||||
|
Requirements::css(project() . "/css/DataReportCMSMain.css");
|
||||||
|
}
|
||||||
|
if(Director::fileExists(project() . "/css/DataReportCMSMain.css")) {
|
||||||
|
Requirements::javascript(project() . "/javascript/DataReport.js");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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::javascriptTemplate("cms/javascript/tinymce.template.js", array(
|
||||||
|
"ContentCSS" => project() . "/css/editor.css",
|
||||||
|
"BaseURL" => Director::absoluteBaseURL(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function Link($action = null) {
|
||||||
|
return "admin/reports/$action";
|
||||||
|
}
|
||||||
|
|
||||||
|
public function Reports(){
|
||||||
|
$allReports= ClassInfo::subclassesFor("Report");
|
||||||
|
foreach($allReports as $report) {
|
||||||
|
if($report != 'Report') $processedReports[] = new $report();
|
||||||
|
}
|
||||||
|
|
||||||
|
$reports = new DataObjectSet($processedReports);
|
||||||
|
return $reports;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function showreport($params) {
|
||||||
|
return $this->showWithEditForm( $params, $this->getReportEditForm( $params['ID'] ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function showWithEditForm( $params, $editForm ) {
|
||||||
|
if($params['ID']) {
|
||||||
|
Session::set('currentPage', $params['ID']);
|
||||||
|
}
|
||||||
|
if($params['OtherID']) {
|
||||||
|
Session::set('currentOtherID', $params['OtherID']);
|
||||||
|
}
|
||||||
|
|
||||||
|
if($_REQUEST['ajax']) {
|
||||||
|
SSViewer::setOption('rewriteHashlinks', false);
|
||||||
|
$result = $this->customise( array( 'EditForm' => $editForm ) )->renderWith($this->getTemplatesWithSuffix("_right"));
|
||||||
|
return $this->getLastFormIn($result);
|
||||||
|
} else {
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function EditForm() {
|
||||||
|
$id = $_REQUEST['ID'] ? $_REQUEST['ID'] : Session::get('currentPage');
|
||||||
|
|
||||||
|
$subclasses = ClassInfo::subclassesFor('Report');
|
||||||
|
|
||||||
|
foreach($subclasses as $class){
|
||||||
|
if($class != 'Report') {
|
||||||
|
$obj = new $class();
|
||||||
|
$ids[] = $obj->getOwnerID();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// bdc: do we have any subclasses?
|
||||||
|
if(sizeof($ids) > 0){
|
||||||
|
if($id && in_array($id, $ids)) return $this->getReportEditForm($id);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getReportEditForm($id){
|
||||||
|
if(is_numeric($id))
|
||||||
|
$page = DataObject::get_by_id("SiteTree", $id);
|
||||||
|
if($page) $reportClass = "Report_".$page->ClassName;
|
||||||
|
|
||||||
|
if(!$reportClass)
|
||||||
|
$reportClass = $id;
|
||||||
|
|
||||||
|
$obj = new $reportClass();
|
||||||
|
$fields = $obj->getCMSFields();
|
||||||
|
|
||||||
|
$fields->push($idField = new HiddenField("ID"));
|
||||||
|
$idField->setValue($id);
|
||||||
|
|
||||||
|
//$actions = new FieldSet(new FormAction('exporttocsv', 'Export to CVS'));
|
||||||
|
$actions = new FieldSet();
|
||||||
|
$form = new Form($this, "EditForm", $fields, $actions);
|
||||||
|
|
||||||
|
return $form;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
334
code/SecurityAdmin.php
Normal file
@ -0,0 +1,334 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
class SecurityAdmin extends LeftAndMain {
|
||||||
|
static $tree_class = "Group";
|
||||||
|
static $subitem_class = "Member";
|
||||||
|
|
||||||
|
public function init() {
|
||||||
|
// Check permissions
|
||||||
|
// if(!Member::currentUser() || !Member::currentUser()->isAdmin()) Security::permissionFailure($this);
|
||||||
|
|
||||||
|
parent::init();
|
||||||
|
|
||||||
|
Requirements::javascript("jsparty/hover.js");
|
||||||
|
Requirements::javascript("jsparty/scriptaculous/controls.js");
|
||||||
|
|
||||||
|
// needed for MemberTableField (Requirements not determined before Ajax-Call)
|
||||||
|
Requirements::javascript("sapphire/javascript/TableListField.js");
|
||||||
|
Requirements::javascript("sapphire/javascript/TableField.js");
|
||||||
|
Requirements::javascript("sapphire/javascript/ComplexTableField.js");
|
||||||
|
Requirements::javascript("cms/javascript/MemberTableField.js");
|
||||||
|
Requirements::css("jsparty/greybox/greybox.css");
|
||||||
|
Requirements::css("sapphire/css/ComplexTableField.css");
|
||||||
|
|
||||||
|
Requirements::javascript("cms/javascript/SecurityAdmin.js");
|
||||||
|
Requirements::javascript("cms/javascript/SecurityAdmin_left.js");
|
||||||
|
Requirements::javascript("cms/javascript/SecurityAdmin_right.js");
|
||||||
|
|
||||||
|
Requirements::javascript("jsparty/greybox/AmiJS.js");
|
||||||
|
Requirements::javascript("jsparty/greybox/greybox.js");
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getEditForm($id) {
|
||||||
|
$record = DataObject::get_by_id("Group", $id);
|
||||||
|
if($record) {
|
||||||
|
$fields = new FieldSet(
|
||||||
|
new TabSet("Root",
|
||||||
|
new Tab("Members",
|
||||||
|
new TextField("Title", "Group name"),
|
||||||
|
$memberList = new MemberTableField(
|
||||||
|
$this,
|
||||||
|
"Members",
|
||||||
|
$record
|
||||||
|
)
|
||||||
|
),
|
||||||
|
|
||||||
|
new Tab("Permissions",
|
||||||
|
new LiteralField("", "<p>This section is for advanced users only.
|
||||||
|
See <a href=\"http://doc.silverstripe.com/doku.php?id=permissions:codes\" target=\"_blank\">this page</a>
|
||||||
|
for more information.</p>"),
|
||||||
|
new TableField(
|
||||||
|
"Permissions",
|
||||||
|
"Permission",
|
||||||
|
array("Code" => "Code", "Arg" => "Optional ID"),
|
||||||
|
array("Code" => "PermissionDropdownField", "Arg" => "TextField"),
|
||||||
|
"GroupID", $id
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
$memberList->setController($this);
|
||||||
|
|
||||||
|
$fields->push($idField = new HiddenField("ID"));
|
||||||
|
$idField->setValue($id);
|
||||||
|
$actions = new FieldSet(
|
||||||
|
new FormAction('addmember','Add Member')
|
||||||
|
);
|
||||||
|
|
||||||
|
$actions->push(new FormAction('save','Save'));
|
||||||
|
|
||||||
|
$form = new Form($this, "EditForm", $fields, $actions);
|
||||||
|
$form->loadDataFrom($record);
|
||||||
|
return $form;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function AddRecordForm() {
|
||||||
|
$m = new MemberTableField(
|
||||||
|
$this,
|
||||||
|
"Members",
|
||||||
|
$this->currentPageID()
|
||||||
|
);
|
||||||
|
return $m->AddRecordForm();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ajax autocompletion
|
||||||
|
*/
|
||||||
|
public function autocomplete() {
|
||||||
|
$fieldName = $this->urlParams['ID'];
|
||||||
|
$fieldVal = $_REQUEST[$fieldName];
|
||||||
|
|
||||||
|
$matches = DataObject::get("Member","$fieldName LIKE '" . addslashes($fieldVal) . "%'");
|
||||||
|
if($matches) {
|
||||||
|
$result .= "<ul>";
|
||||||
|
foreach($matches as $match) {
|
||||||
|
|
||||||
|
$data = $match->FirstName;
|
||||||
|
$data .= ",$match->Surname";
|
||||||
|
$data .= ",$match->Email";
|
||||||
|
$data .= ",$match->Password";
|
||||||
|
$result .= "<li>" . $match->$fieldName . "<span class=\"informal\">($match->FirstName $match->Surname, $match->Email)</span><span class=\"informal data\">$data</span></li>";
|
||||||
|
}
|
||||||
|
$result .= "</ul>";
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getmember() {
|
||||||
|
Session::set('currentMember', $_REQUEST['ID']);
|
||||||
|
SSViewer::setOption('rewriteHashlinks', false);
|
||||||
|
$result = $this->renderWith($this->class . "_rightbottom");
|
||||||
|
$parts = split('</?form[^>]*>', $result);
|
||||||
|
echo $parts[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function MemberForm() {
|
||||||
|
$id = $_REQUEST['ID'] ? $_REQUEST['ID'] : Session::get('currentMember');
|
||||||
|
if($id)
|
||||||
|
return $this->getMemberForm($id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getMemberForm($id) {
|
||||||
|
if($id && $id != 'new') $record = DataObject::get_one("Member", "`Member`.ID = $id");
|
||||||
|
if($record || $id == 'new') {
|
||||||
|
$fields = new FieldSet(
|
||||||
|
new HiddenField('MemberListBaseGroup', '', $this->currentPageID() )
|
||||||
|
);
|
||||||
|
|
||||||
|
if( $extraFields = $record->getCMSFields() )
|
||||||
|
foreach( $extraFields as $extra )
|
||||||
|
$fields->push( $extra );
|
||||||
|
|
||||||
|
$fields->push($idField = new HiddenField("ID"));
|
||||||
|
$fields->push($groupIDField = new HiddenField("GroupID"));
|
||||||
|
|
||||||
|
|
||||||
|
$actions = new FieldSet();
|
||||||
|
$actions->push(new FormAction('savemember','Save'));
|
||||||
|
|
||||||
|
$form = new Form($this, "MemberForm", $fields, $actions);
|
||||||
|
if($record) $form->loadDataFrom($record);
|
||||||
|
|
||||||
|
$idField->setValue($id);
|
||||||
|
$groupIDField->setValue($this->currentPageID());
|
||||||
|
|
||||||
|
return $form;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function savemember() {
|
||||||
|
$data = $_REQUEST;
|
||||||
|
|
||||||
|
$className = $this->stat('subitem_class');
|
||||||
|
|
||||||
|
$id = $_REQUEST['ID'];
|
||||||
|
if($id == 'new') $id = null;
|
||||||
|
|
||||||
|
if($id) {
|
||||||
|
$record = DataObject::get_one($className, "`$className`.ID = $id");
|
||||||
|
} else {
|
||||||
|
$record = new $className();
|
||||||
|
}
|
||||||
|
|
||||||
|
$record->update($data);
|
||||||
|
$record->ID = $id;
|
||||||
|
$record->write();
|
||||||
|
|
||||||
|
$record->Groups()->add($data['GroupID']);
|
||||||
|
|
||||||
|
|
||||||
|
FormResponse::add("reloadMemberTableField();");
|
||||||
|
|
||||||
|
return FormResponse::respond();
|
||||||
|
}
|
||||||
|
|
||||||
|
function addmember($className=null) {
|
||||||
|
$data = $_REQUEST;
|
||||||
|
unset($data['ID']);
|
||||||
|
if($className == null);
|
||||||
|
$className = $this->stat('subitem_class');
|
||||||
|
$record = new $className();
|
||||||
|
|
||||||
|
$record->update($data);
|
||||||
|
$record->write();
|
||||||
|
if($data['GroupID'])
|
||||||
|
$record->Groups()->add($data['GroupID']);
|
||||||
|
|
||||||
|
FormResponse::add("reloadMemberTableField();");
|
||||||
|
|
||||||
|
return FormResponse::respond();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function removememberfromgroup() {
|
||||||
|
$groupID = $this->urlParams['ID'];
|
||||||
|
$memberID = $this->urlParams['OtherID'];
|
||||||
|
if(is_numeric($groupID) && is_numeric($memberID)) {
|
||||||
|
$member = DataObject::get_by_id('Member', $memberID);
|
||||||
|
$member->Groups()->remove($groupID);
|
||||||
|
FormResponse::add("reloadMemberTableField();");
|
||||||
|
|
||||||
|
} else {
|
||||||
|
user_error("SecurityAdmin::removememberfromgroup: Bad parameters: Group=$groupID, Member=$memberID", E_USER_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
return FormResponse::respond();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the entire site tree as a nested set of ULs
|
||||||
|
*/
|
||||||
|
public function SiteTreeAsUL() {
|
||||||
|
$className = "Group";
|
||||||
|
|
||||||
|
$obj = singleton($className);
|
||||||
|
|
||||||
|
// getChildrenAsUL is a flexible and complex way of traversing the tree
|
||||||
|
$siteTree = $obj->getChildrenAsUL("",
|
||||||
|
' "<li id=\"record-$child->ID\" class=\"$child->class " . ($child->Locked ? " nodelete" : "") . ' .
|
||||||
|
' ($extraArg->isCurrentPage($child) ? " current" : "") . "\">" . ' .
|
||||||
|
' "<a href=\"" . Director::link("admin", "show", $child->ID) . "\" >" . $child->Title . "</a>" ',$this);
|
||||||
|
|
||||||
|
$siteTree = "<ul id=\"sitetree\" class=\"tree unformatted\">" .
|
||||||
|
"<li id=\"record-0\" class=\"Root\">" .
|
||||||
|
"<a href=\"admin/security/show/0\" >Security groups</a>"
|
||||||
|
. $siteTree .
|
||||||
|
"</li>" .
|
||||||
|
"</ul>";
|
||||||
|
|
||||||
|
return $siteTree;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addgroup() {
|
||||||
|
$parent = $_REQUEST['ParentID'] ? $_REQUEST['ParentID'] : 0;
|
||||||
|
$p = new Group();
|
||||||
|
$p->Title = "New Group";
|
||||||
|
$p->Code = "new-group";
|
||||||
|
$p->ParentID = $parent;
|
||||||
|
$p->write();
|
||||||
|
return $this->returnItemToUser($p);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function newmember() {
|
||||||
|
Session::clear('currentMember');
|
||||||
|
$newMemberForm = array(
|
||||||
|
"MemberForm" => $this->getMemberForm('new'),
|
||||||
|
);
|
||||||
|
// This should be using FormResponse ;-)
|
||||||
|
if(Director::is_ajax()) {
|
||||||
|
SSViewer::setOption('rewriteHashlinks', false);
|
||||||
|
$customised = $this->customise($newMemberForm);
|
||||||
|
$result = $customised->renderWith($this->class . "_rightbottom");
|
||||||
|
$parts = split('</?form[^>]*>', $result);
|
||||||
|
return $parts[1];
|
||||||
|
|
||||||
|
} else {
|
||||||
|
return $newMemberForm;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function EditedMember() {
|
||||||
|
if(Session::get('currentMember'))
|
||||||
|
return DataObject::get_by_id("Member", Session::get('currentMember'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function Link($action = null) {
|
||||||
|
if(!$action) $action = "index";
|
||||||
|
return "admin/security/$action/" . $this->currentPageID();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function listmembers( $baseGroup = null ) {
|
||||||
|
|
||||||
|
if( !$baseGroup )
|
||||||
|
$baseGroup = $this->urlParams['ID'];
|
||||||
|
|
||||||
|
// Debug::message( $_REQUEST['MemberListOrderByField'] );
|
||||||
|
|
||||||
|
// construct the filter and sort
|
||||||
|
|
||||||
|
if( $_REQUEST['MemberListOrderByField'] )
|
||||||
|
$sort = "`" . $_REQUEST['MemberListOrderByField'] . "`" . addslashes( $_REQUEST['MemberListOrderByOrder'] );
|
||||||
|
|
||||||
|
$whereClauses = array();
|
||||||
|
|
||||||
|
$search = addslashes( $_REQUEST['MemberListSearch'] );
|
||||||
|
|
||||||
|
if( $_REQUEST['MemberListPage'] ) {
|
||||||
|
$pageSize = 10;
|
||||||
|
|
||||||
|
$limitClause = ( $_REQUEST['MemberListPage'] ) . ", $pageSize";
|
||||||
|
}
|
||||||
|
|
||||||
|
if( !empty($_REQUEST['MemberListSearch']) )
|
||||||
|
$whereClauses[] = "( `Email`='$search' OR `FirstName`='$search' OR `Surname`='$search' )";
|
||||||
|
|
||||||
|
if( is_numeric( $_REQUEST['MemberListBaseGroup'] ) ) {
|
||||||
|
$whereClauses[] = "`GroupID`='".$_REQUEST['MemberListBaseGroup']."'";
|
||||||
|
$join = "INNER JOIN `Group_Members` ON `MemberID`=`Member`.`ID`";
|
||||||
|
}
|
||||||
|
|
||||||
|
// $_REQUEST['showqueries'] = 1;
|
||||||
|
|
||||||
|
$members = DataObject::get('Member', implode( ' AND ', $whereClauses ), $sort, $join, $limitClause );
|
||||||
|
|
||||||
|
if( is_numeric( $_REQUEST['MemberListGroup'] ) ) {
|
||||||
|
$baseMembers = new DataObjectSet();
|
||||||
|
|
||||||
|
if( $members )
|
||||||
|
foreach( $members as $member )
|
||||||
|
if( $member->inGroup( $_REQUEST['MemberListGroup'] ) )
|
||||||
|
$baseMembers->push( $member );
|
||||||
|
} else
|
||||||
|
$baseMembers = $members;
|
||||||
|
|
||||||
|
$baseMembers = null;
|
||||||
|
|
||||||
|
// user_error( $_REQUEST['MemberListBaseGroup'], E_USER_ERROR );
|
||||||
|
|
||||||
|
$memberListField = new MemberTableField(
|
||||||
|
$this,
|
||||||
|
'MemberList',
|
||||||
|
$_REQUEST['MemberListBaseGroup'],
|
||||||
|
$baseMembers,
|
||||||
|
$_REQUEST['MemberListDontShowPassword']
|
||||||
|
);
|
||||||
|
|
||||||
|
return $memberListField->renderWith('MemberList_Table');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
64
code/SideReport.php
Executable file
@ -0,0 +1,64 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
abstract class SideReport extends Object {
|
||||||
|
abstract function records();
|
||||||
|
abstract function fieldsToShow();
|
||||||
|
abstract function title();
|
||||||
|
|
||||||
|
function getHTML() {
|
||||||
|
$records = $this->records();
|
||||||
|
$fieldsToShow = $this->fieldsToShow();
|
||||||
|
|
||||||
|
if($records) {
|
||||||
|
$result = "<ul class=\"$this->class\">\n";
|
||||||
|
|
||||||
|
foreach($records as $record) {
|
||||||
|
$result .= "<li>\n";
|
||||||
|
foreach($fieldsToShow as $fieldTitle => $fieldSource) {
|
||||||
|
$fieldName = ereg_replace('[^A-Za-z0-9]+','',$fieldTitle);
|
||||||
|
if(is_string($fieldSource)) {
|
||||||
|
$val = $record->$fieldSource;
|
||||||
|
} else {
|
||||||
|
$val = $record->val($fieldSource[0], $fieldSource[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$result .= "<a class=\"$fieldName\" href=\"admin/show/$record->ID\">$val</a>";
|
||||||
|
}
|
||||||
|
$result .= "\n</li>\n";
|
||||||
|
}
|
||||||
|
$result .= "</ul>\n";
|
||||||
|
} else {
|
||||||
|
$result = 'The '.$this->title().' report is empty.';
|
||||||
|
}
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SideReport_EmptyPages extends SideReport {
|
||||||
|
function title() {
|
||||||
|
return "Empty pages";
|
||||||
|
}
|
||||||
|
function records() {
|
||||||
|
return DataObject::get("SiteTree", "Content = '' OR Content IS NULL OR Content LIKE '<p></p>' OR Content LIKE '<p> </p>'", "Title");
|
||||||
|
}
|
||||||
|
function fieldsToShow() {
|
||||||
|
return array(
|
||||||
|
"Title" => array("NestedTitle", array("2")),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SideReport_RecentlyEdited extends SideReport {
|
||||||
|
function title() {
|
||||||
|
return "Pages edited in the last 2 weeks";
|
||||||
|
}
|
||||||
|
function records() {
|
||||||
|
return DataObject::get("SiteTree", "`SiteTree`.LastEdited > NOW() - INTERVAL 14 DAY", "`SiteTree`.`LastEdited` DESC");
|
||||||
|
}
|
||||||
|
function fieldsToShow() {
|
||||||
|
return array(
|
||||||
|
"Title" => array("NestedTitle", array("2")),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
87
code/StaticExporter.php
Executable file
@ -0,0 +1,87 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class lets you export a static copy of your site.
|
||||||
|
* It creates a huge number of folders each containing an index.html file.
|
||||||
|
* This preserves the URL naming format.
|
||||||
|
*/
|
||||||
|
class StaticExporter extends Controller {
|
||||||
|
function init() {
|
||||||
|
parent::init();
|
||||||
|
if(!$this->can('AdminCMS')) {
|
||||||
|
$messageSet = array(
|
||||||
|
'default' => "Enter your email address and password to access the CMS.",
|
||||||
|
'alreadyLoggedIn' => "I'm sorry, but you can't access that part of the CMS. If you want to log in as someone else, do so below",
|
||||||
|
'logInAgain' => "You have been logged out of the CMS. If you would like to log in again, enter a username and password below.",
|
||||||
|
);
|
||||||
|
|
||||||
|
Security::permissionFailure($this, $messageSet);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function Link($action = null) {
|
||||||
|
return "StaticExporter/$action";
|
||||||
|
}
|
||||||
|
|
||||||
|
function index() {
|
||||||
|
echo "<h1>Static exporter</h1>";
|
||||||
|
echo $this->StaticExportForm()->forTemplate();
|
||||||
|
}
|
||||||
|
|
||||||
|
function StaticExportForm() {
|
||||||
|
return new Form($this, 'StaticExportForm', new FieldSet(
|
||||||
|
new TextField('folder', 'Folder to export to'),
|
||||||
|
new TextField('baseurl', 'Base URL')
|
||||||
|
), new FieldSet(
|
||||||
|
new FormAction('export', 'Export to that folder')
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
function export() {
|
||||||
|
|
||||||
|
if($_REQUEST['baseurl']) {
|
||||||
|
$base = $_REQUEST['baseurl'];
|
||||||
|
if(substr($base,-1) != '/') $base .= '/';
|
||||||
|
Director::setBaseURL($base);
|
||||||
|
}
|
||||||
|
|
||||||
|
$folder = $_REQUEST['folder'];
|
||||||
|
if($folder && file_exists($folder)) {
|
||||||
|
$pages = DataObject::get("SiteTree");
|
||||||
|
foreach($pages as $page) {
|
||||||
|
$subfolder = "$folder/$page->URLSegment";
|
||||||
|
$contentfile = "$folder/$page->URLSegment/index.html";
|
||||||
|
|
||||||
|
echo "<li>$page->URLSegment/index.html";
|
||||||
|
|
||||||
|
// Make the folder
|
||||||
|
if(!file_exists($subfolder)) {
|
||||||
|
mkdir($subfolder);
|
||||||
|
chmod($subfolder,02775);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run the page
|
||||||
|
Requirements::clear();
|
||||||
|
$controllerClass = "{$page->class}_Controller";
|
||||||
|
if(class_exists($controllerClass)) {
|
||||||
|
$controller = new $controllerClass($page);
|
||||||
|
$pageContent = $controller->run( array() );
|
||||||
|
|
||||||
|
// Write to file
|
||||||
|
if($fh = fopen($contentfile, 'w')) {
|
||||||
|
fwrite($fh, $pageContent);
|
||||||
|
fclose($fh);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
echo "Please specify a folder that exists";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
133
code/ThumbnailStripField.php
Executable file
@ -0,0 +1,133 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides a strip of thumbnails showing all of the images in the system.
|
||||||
|
* It will be tied to a 'parent field' that will provide it with a filter by which to reduce the number
|
||||||
|
* of thumbnails displayed.
|
||||||
|
*/
|
||||||
|
class ThumbnailStripField extends FormField {
|
||||||
|
protected $parentField;
|
||||||
|
protected $updateMethod;
|
||||||
|
|
||||||
|
function __construct($name, $parentField, $updateMethod = "getimages") {
|
||||||
|
$this->parentField = $parentField;
|
||||||
|
$this->updateMethod = $updateMethod;
|
||||||
|
|
||||||
|
parent::__construct($name);
|
||||||
|
}
|
||||||
|
|
||||||
|
function ParentField() {
|
||||||
|
return $this->form->FormName() . '_' . $this->parentField;
|
||||||
|
}
|
||||||
|
|
||||||
|
function FieldHolder() {
|
||||||
|
Requirements::javascript('cms/javascript/ThumbnailStripField.js');
|
||||||
|
return $this->renderWith('ThumbnailStripField');
|
||||||
|
}
|
||||||
|
|
||||||
|
function Images() {
|
||||||
|
//return DataObject::get("Image", "Paretn);
|
||||||
|
}
|
||||||
|
|
||||||
|
function UpdateMethod() {
|
||||||
|
return $this->updateMethod;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Populate the Thumbnail strip field, by looking for a folder,
|
||||||
|
* and the descendants of this folder.
|
||||||
|
*/
|
||||||
|
function getimages() {
|
||||||
|
$result = '';
|
||||||
|
$folder = DataObject::get_by_id("Folder", $_GET['folderID']);
|
||||||
|
|
||||||
|
if( !$folder )
|
||||||
|
return 'This is not a folder';
|
||||||
|
|
||||||
|
$folderList = $folder->getDescendantIDList("Folder");
|
||||||
|
|
||||||
|
array_unshift($folderList, $folder->ID);
|
||||||
|
|
||||||
|
$images = DataObject::get("Image", "ParentID IN (" . implode(', ', $folderList) . ")");
|
||||||
|
|
||||||
|
if($images) {
|
||||||
|
$result .= '<ul>';
|
||||||
|
foreach($images as $image) {
|
||||||
|
$thumbnail = $image->getFormattedImage('StripThumbnail');
|
||||||
|
|
||||||
|
// Constrain the output image to a 600x600 square. This is passed to the destwidth/destheight in the class, which are then used to
|
||||||
|
// set width & height properties on the <img> tag inserted into the CMS. Resampling is done after save
|
||||||
|
$width = $image->Width;
|
||||||
|
$height = $image->Height;
|
||||||
|
if($width > 600) {
|
||||||
|
$height *= (600/$width);
|
||||||
|
$width = 600;
|
||||||
|
}
|
||||||
|
if($height > 600) {
|
||||||
|
$width *= (600/$height);
|
||||||
|
$height = 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
$result .=
|
||||||
|
'<li>' .
|
||||||
|
'<a href=" ' . $image->Filename . '">' .
|
||||||
|
'<img class="destwidth=' . round($width) . ',destheight=' . round($height) . '" src="'. $thumbnail->URL .'" alt="' . $image->Title . '" title="' . $image->Title . '" />' .
|
||||||
|
'</a>' .
|
||||||
|
'</li>';
|
||||||
|
}
|
||||||
|
$result .= '</ul>';
|
||||||
|
}else{
|
||||||
|
$result = "<h2> No images found in ". $folder->Title. "</h2>";
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function getflash() {
|
||||||
|
|
||||||
|
$folder = DataObject::get_by_id("Folder", $_GET['folderID']);
|
||||||
|
|
||||||
|
if( !$folder )
|
||||||
|
return 'This is not a folder';
|
||||||
|
|
||||||
|
$folderList = $folder->getDescendantIDList("Folder");
|
||||||
|
array_unshift($folderList, $folder->ID);
|
||||||
|
|
||||||
|
$width = Image::$strip_thumbnail_width - 10;
|
||||||
|
$height = Image::$strip_thumbnail_height - 10;
|
||||||
|
|
||||||
|
$flashObjects = DataObject::get("File", "ParentID IN (" . implode(', ', $folderList) . ") AND Filename LIKE '%.swf'");
|
||||||
|
|
||||||
|
if($flashObjects) {
|
||||||
|
$result .= '<ul>';
|
||||||
|
foreach($flashObjects as $flashObject) {
|
||||||
|
// doesn't work well because we can't stop/mute flash-files, AND IE does not bubble javascript-events
|
||||||
|
// over a flash-object grml
|
||||||
|
// $result .= <<<HTML
|
||||||
|
//<a href="$flashObject->URL">
|
||||||
|
// <object type="application/x-shockwave-flash" data="$flashObject->URL" width="$width" height="$height">
|
||||||
|
// <param name="movie" value="$flashObject->URL" />
|
||||||
|
// </object>
|
||||||
|
//</a>
|
||||||
|
//HTML;
|
||||||
|
$mceRoot = MCE_ROOT .
|
||||||
|
$result .= <<<HTML
|
||||||
|
<li>
|
||||||
|
<a href="$flashObject->URL">
|
||||||
|
<img src="{$mceRoot}themes/advanced/images/spacer_flash.jpg" alt="spacer" width="$width" height="$height" />
|
||||||
|
<br />
|
||||||
|
$flashObject->Name
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
HTML;
|
||||||
|
}
|
||||||
|
$result .= '</ul>';
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
397
code/sitefeatures/Akismet.php
Normal file
@ -0,0 +1,397 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Akismet anti-comment spam service
|
||||||
|
*
|
||||||
|
* The class in this package allows use of the {@link http://akismet.com Akismet} anti-comment spam service in any PHP5 application.
|
||||||
|
*
|
||||||
|
* This service performs a number of checks on submitted data and returns whether or not the data is likely to be spam.
|
||||||
|
*
|
||||||
|
* Please note that in order to use this class, you must have a vaild {@link http://wordpress.com/api-keys/ WordPress API key}. They are free for non/small-profit types and getting one will only take a couple of minutes.
|
||||||
|
*
|
||||||
|
* For commercial use, please {@link http://akismet.com/commercial/ visit the Akismet commercial licensing page}.
|
||||||
|
*
|
||||||
|
* Please be aware that this class is PHP5 only. Attempts to run it under PHP4 will most likely fail.
|
||||||
|
*
|
||||||
|
* See the Akismet class documentation page linked to below for usage information.
|
||||||
|
*
|
||||||
|
* @package Akismet
|
||||||
|
* @author Alex Potsides, {@link http://www.achingbrain.net http://www.achingbrain.net}
|
||||||
|
* @version 0.1
|
||||||
|
* @copyright Alex Potsides, {@link http://www.achingbrain.net http://www.achingbrain.net}
|
||||||
|
* @license http://www.gnu.org/licenses/gpl.html GNU General Public License
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Akismet PHP5 Class
|
||||||
|
*
|
||||||
|
* This class takes the functionality from the Akismet WordPress plugin written by {@link http://photomatt.net/ Matt Mullenweg} and allows it to be integrated into any PHP5 application or website.
|
||||||
|
*
|
||||||
|
* The original plugin is {@link http://akismet.com/download/ available on the Akismet website}.
|
||||||
|
*
|
||||||
|
* <b>Usage:</b>
|
||||||
|
* <code>
|
||||||
|
* $akismet = new Akismet('http://www.example.com/blog/', 'aoeu1aoue');
|
||||||
|
* $akismet->setCommentAuthor($name);
|
||||||
|
* $akismet->setCommentAuthorEmail($email);
|
||||||
|
* $akismet->setCommentAuthorURL($url);
|
||||||
|
* $akismet->setCommentContent($comment);
|
||||||
|
* $akismet->setPermalink('http://www.example.com/blog/alex/someurl/');
|
||||||
|
* if($akismet->isCommentSpam())
|
||||||
|
* // store the comment but mark it as spam (in case of a mis-diagnosis)
|
||||||
|
* else
|
||||||
|
* // store the comment normally
|
||||||
|
* </code>
|
||||||
|
*
|
||||||
|
* @package akismet
|
||||||
|
* @name Akismet
|
||||||
|
* @version 0.2
|
||||||
|
* @author Alex Potsides
|
||||||
|
* @link http://www.achingbrain.net/
|
||||||
|
*/
|
||||||
|
class Akismet
|
||||||
|
{
|
||||||
|
private $version = '0.2';
|
||||||
|
private $wordPressAPIKey;
|
||||||
|
private $blogURL;
|
||||||
|
private $comment;
|
||||||
|
private $apiPort;
|
||||||
|
private $akismetServer;
|
||||||
|
private $akismetVersion;
|
||||||
|
|
||||||
|
// This prevents some potentially sensitive information from being sent accross the wire.
|
||||||
|
private $ignore = array('HTTP_COOKIE',
|
||||||
|
'HTTP_X_FORWARDED_FOR',
|
||||||
|
'HTTP_X_FORWARDED_HOST',
|
||||||
|
'HTTP_MAX_FORWARDS',
|
||||||
|
'HTTP_X_FORWARDED_SERVER',
|
||||||
|
'REDIRECT_STATUS',
|
||||||
|
'SERVER_PORT',
|
||||||
|
'PATH',
|
||||||
|
'DOCUMENT_ROOT',
|
||||||
|
'SERVER_ADMIN',
|
||||||
|
'QUERY_STRING',
|
||||||
|
'PHP_SELF' );
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws Exception An exception is thrown if your API key is invalid.
|
||||||
|
* @param string Your WordPress API key.
|
||||||
|
* @param string $blogURL The URL of your blog.
|
||||||
|
*/
|
||||||
|
public function __construct($blogURL, $wordPressAPIKey)
|
||||||
|
{
|
||||||
|
$this->blogURL = $blogURL;
|
||||||
|
$this->wordPressAPIKey = $wordPressAPIKey;
|
||||||
|
|
||||||
|
// Set some default values
|
||||||
|
$this->apiPort = 80;
|
||||||
|
$this->akismetServer = 'rest.akismet.com';
|
||||||
|
$this->akismetVersion = '1.1';
|
||||||
|
|
||||||
|
// Start to populate the comment data
|
||||||
|
$this->comment['blog'] = $blogURL;
|
||||||
|
$this->comment['user_agent'] = $_SERVER['HTTP_USER_AGENT'];
|
||||||
|
$this->comment['referrer'] = $_SERVER['HTTP_REFERER'];
|
||||||
|
|
||||||
|
// This is necessary if the server PHP5 is running on has been set up to run PHP4 and
|
||||||
|
// PHP5 concurently and is actually running through a separate proxy al a these instructions:
|
||||||
|
// http://www.schlitt.info/applications/blog/archives/83_How_to_run_PHP4_and_PHP_5_parallel.html
|
||||||
|
// and http://wiki.coggeshall.org/37.html
|
||||||
|
// Otherwise the user_ip appears as the IP address of the PHP4 server passing the requests to the
|
||||||
|
// PHP5 one...
|
||||||
|
$this->comment['user_ip'] = $_SERVER['REMOTE_ADDR'] != getenv('SERVER_ADDR') ? $_SERVER['REMOTE_ADDR'] : getenv('HTTP_X_FORWARDED_FOR');
|
||||||
|
|
||||||
|
// Check to see if the key is valid
|
||||||
|
$response = $this->http_post('key=' . $this->wordPressAPIKey . '&blog=' . $this->blogURL, $this->akismetServer, '/' . $this->akismetVersion . '/verify-key');
|
||||||
|
|
||||||
|
if($response[1] != 'valid')
|
||||||
|
{
|
||||||
|
// Whoops, no it's not. Throw an exception as we can't proceed without a valid API key.
|
||||||
|
throw new Exception('Invalid API key. Please obtain one from http://wordpress.com/api-keys/');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function http_post($request, $host, $path)
|
||||||
|
{
|
||||||
|
$http_request = "POST " . $path . " HTTP/1.1\r\n";
|
||||||
|
$http_request .= "Host: " . $host . "\r\n";
|
||||||
|
$http_request .= "Content-Type: application/x-www-form-urlencoded; charset=utf-8\r\n";
|
||||||
|
$http_request .= "Content-Length: " . strlen($request) . "\r\n";
|
||||||
|
$http_request .= "User-Agent: Akismet PHP5 Class " . $this->version . " | Akismet/1.11\r\n";
|
||||||
|
$http_request .= "\r\n";
|
||||||
|
$http_request .= $request;
|
||||||
|
|
||||||
|
$socketWriteRead = new SocketWriteRead($host, $this->apiPort, $http_request);
|
||||||
|
$socketWriteRead->send();
|
||||||
|
|
||||||
|
return explode("\r\n\r\n", $socketWriteRead->getResponse(), 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Formats the data for transmission echo $sql;
|
||||||
|
private function getQueryString()
|
||||||
|
{
|
||||||
|
foreach($_SERVER as $key => $value)
|
||||||
|
{
|
||||||
|
if(!in_array($key, $this->ignore))
|
||||||
|
{
|
||||||
|
if($key == 'REMOTE_ADDR')
|
||||||
|
{
|
||||||
|
$this->comment[$key] = $this->comment['user_ip'];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$this->comment[$key] = $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$query_string = '';
|
||||||
|
|
||||||
|
foreach($this->comment as $key => $data)
|
||||||
|
{
|
||||||
|
@$query_string .= $key . '=' . urlencode(stripslashes($data)) . '&';
|
||||||
|
}
|
||||||
|
|
||||||
|
return $query_string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for spam.
|
||||||
|
*
|
||||||
|
* Uses the web service provided by {@link http://www.akismet.com Akismet} to see whether or not the submitted comment is spam. Returns a boolean value.
|
||||||
|
*
|
||||||
|
* @return bool True if the comment is spam, false if not
|
||||||
|
*/
|
||||||
|
public function isCommentSpam()
|
||||||
|
{
|
||||||
|
$response = $this->http_post($this->getQueryString(), $this->wordPressAPIKey . '.rest.akismet.com', '/' . $this->akismetVersion . '/comment-check');
|
||||||
|
|
||||||
|
return ($response[1] == 'true');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Submit spam that is incorrectly tagged as ham.
|
||||||
|
*
|
||||||
|
* Using this function will make you a good citizen as it helps Akismet to learn from its mistakes. This will improve the service for everybody.
|
||||||
|
*/
|
||||||
|
public function submitSpam()
|
||||||
|
{
|
||||||
|
$this->http_post($this->getQueryString(), $this->wordPressAPIKey . '.' . $this->akismetServer, '/' . $this->akismetVersion . '/submit-spam');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Submit ham that is incorrectly tagged as spam.
|
||||||
|
*
|
||||||
|
* Using this function will make you a good citizen as it helps Akismet to learn from its mistakes. This will improve the service for everybody.
|
||||||
|
*/
|
||||||
|
public function submitHam()
|
||||||
|
{
|
||||||
|
$this->http_post($this->getQueryString(), $this->wordPressAPIKey . '.' . $this->akismetServer, '/' . $this->akismetVersion . '/submit-ham');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* To override the user IP address when submitting spam/ham later on
|
||||||
|
*
|
||||||
|
* @param string $userip An IP address. Optional.
|
||||||
|
*/
|
||||||
|
public function setUserIP($userip)
|
||||||
|
{
|
||||||
|
$this->comment['user_ip'] = $userip;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* To override the referring page when submitting spam/ham later on
|
||||||
|
*
|
||||||
|
* @param string $referrer The referring page. Optional.
|
||||||
|
*/
|
||||||
|
public function setReferrer($referrer)
|
||||||
|
{
|
||||||
|
$this->comment['referrer'] = $referrer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A permanent URL referencing the blog post the comment was submitted to.
|
||||||
|
*
|
||||||
|
* @param string $permalink The URL. Optional.
|
||||||
|
*/
|
||||||
|
public function setPermalink($permalink)
|
||||||
|
{
|
||||||
|
$this->comment['permalink'] = $permalink;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The type of comment being submitted.
|
||||||
|
*
|
||||||
|
* May be blank, comment, trackback, pingback, or a made up value like "registration" or "wiki".
|
||||||
|
*/
|
||||||
|
public function setCommentType($commentType)
|
||||||
|
{
|
||||||
|
$this->comment['comment_type'] = $commentType;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name that the author submitted with the comment.
|
||||||
|
*/
|
||||||
|
public function setCommentAuthor($commentAuthor)
|
||||||
|
{
|
||||||
|
$this->comment['comment_author'] = $commentAuthor;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The email address that the author submitted with the comment.
|
||||||
|
*
|
||||||
|
* The address is assumed to be valid.
|
||||||
|
*/
|
||||||
|
public function setCommentAuthorEmail($authorEmail)
|
||||||
|
{
|
||||||
|
$this->comment['comment_author_email'] = $authorEmail;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The URL that the author submitted with the comment.
|
||||||
|
*/
|
||||||
|
public function setCommentAuthorURL($authorURL)
|
||||||
|
{
|
||||||
|
$this->comment['comment_author_url'] = $authorURL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The comment's body text.
|
||||||
|
*/
|
||||||
|
public function setCommentContent($commentBody)
|
||||||
|
{
|
||||||
|
$this->comment['comment_content'] = $commentBody;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defaults to 80
|
||||||
|
*/
|
||||||
|
public function setAPIPort($apiPort)
|
||||||
|
{
|
||||||
|
$this->apiPort = $apiPort;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defaults to rest.akismet.com
|
||||||
|
*/
|
||||||
|
public function setAkismetServer($akismetServer)
|
||||||
|
{
|
||||||
|
$this->akismetServer = $akismetServer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defaults to '1.1'
|
||||||
|
*/
|
||||||
|
public function setAkismetVersion($akismetVersion)
|
||||||
|
{
|
||||||
|
$this->akismetVersion = $akismetVersion;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility class used by Akismet
|
||||||
|
*
|
||||||
|
* This class is used by Akismet to do the actual sending and receiving of data. It opens a connection to a remote host, sends some data and the reads the response and makes it available to the calling program.
|
||||||
|
*
|
||||||
|
* The code that makes up this class originates in the Akismet WordPress plugin, which is {@link http://akismet.com/download/ available on the Akismet website}.
|
||||||
|
*
|
||||||
|
* N.B. It is not necessary to call this class directly to use the Akismet class. This is included here mainly out of a sense of completeness.
|
||||||
|
*
|
||||||
|
* @package akismet
|
||||||
|
* @name SocketWriteRead
|
||||||
|
* @version 0.1
|
||||||
|
* @author Alex Potsides
|
||||||
|
* @link http://www.achingbrain.net/
|
||||||
|
*/
|
||||||
|
class SocketWriteRead
|
||||||
|
{
|
||||||
|
private $host;
|
||||||
|
private $port;
|
||||||
|
private $request;
|
||||||
|
private $response;
|
||||||
|
private $responseLength;
|
||||||
|
private $errorNumber;
|
||||||
|
private $errorString;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $host The host to send/receive data.
|
||||||
|
* @param int $port The port on the remote host.
|
||||||
|
* @param string $request The data to send.
|
||||||
|
* @param int $responseLength The amount of data to read. Defaults to 1160 bytes.
|
||||||
|
*/
|
||||||
|
public function __construct($host, $port, $request, $responseLength = 1160)
|
||||||
|
{
|
||||||
|
$this->host = $host;
|
||||||
|
$this->port = $port;
|
||||||
|
$this->request = $request;
|
||||||
|
$this->responseLength = $responseLength;
|
||||||
|
$this->errorNumber = 0;
|
||||||
|
$this->errorString = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends the data to the remote host.
|
||||||
|
*
|
||||||
|
* @throws An exception is thrown if a connection cannot be made to the remote host.
|
||||||
|
*/
|
||||||
|
public function send()
|
||||||
|
{
|
||||||
|
$this->response = '';
|
||||||
|
|
||||||
|
$fs = fsockopen($this->host, $this->port, $this->errorNumber, $this->errorString, 3);
|
||||||
|
|
||||||
|
if($this->errorNumber != 0)
|
||||||
|
{
|
||||||
|
throw new Exception('Error connecting to host: ' . $this->host . ' Error number: ' . $this->errorNumber . ' Error message: ' . $this->errorString);
|
||||||
|
}
|
||||||
|
|
||||||
|
if($fs !== false)
|
||||||
|
{
|
||||||
|
@fwrite($fs, $this->request);
|
||||||
|
|
||||||
|
while(!feof($fs))
|
||||||
|
{
|
||||||
|
$this->response .= fgets($fs, $this->responseLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose($fs);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the server response text
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getResponse()
|
||||||
|
{
|
||||||
|
return $this->response;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the error number
|
||||||
|
*
|
||||||
|
* If there was no error, 0 will be returned.
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function getErrorNumner()
|
||||||
|
{
|
||||||
|
return $this->errorNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the error string
|
||||||
|
*
|
||||||
|
* If there was no error, an empty string will be returned.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getErrorString()
|
||||||
|
{
|
||||||
|
return $this->errorString;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
144
code/sitefeatures/PageComment.php
Executable file
@ -0,0 +1,144 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
class PageComment extends DataObject {
|
||||||
|
static $db = array(
|
||||||
|
"Name" => "Varchar",
|
||||||
|
"Comment" => "Text",
|
||||||
|
"IsSpam" => "Boolean",
|
||||||
|
);
|
||||||
|
|
||||||
|
static $has_one = array(
|
||||||
|
"Parent" => "SiteTree",
|
||||||
|
);
|
||||||
|
|
||||||
|
static $casting = array(
|
||||||
|
"RSSTitle" => "Varchar",
|
||||||
|
);
|
||||||
|
|
||||||
|
// Number of comments to show before paginating
|
||||||
|
static $comments_per_page = 10;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a link to this comment
|
||||||
|
* @return string link to this comment.
|
||||||
|
*/
|
||||||
|
function Link() {
|
||||||
|
return $this->Parent()->Link();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function DeleteLink() {
|
||||||
|
if(Permission::check('CMS_ACCESS_CMSMain')) {
|
||||||
|
return "PageComment/deletecomment/$this->ID";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function deletecomment() {
|
||||||
|
if(Permission::check('CMS_ACCESS_CMSMain')) {
|
||||||
|
$comment = DataObject::get_by_id("PageComment", $this->urlParams['ID']);
|
||||||
|
if($comment) {
|
||||||
|
$comment->delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(Director::is_ajax()) {
|
||||||
|
echo "";
|
||||||
|
} else {
|
||||||
|
Director::redirectBack();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function SpamLink() {
|
||||||
|
$member = Member::currentUser();
|
||||||
|
if(SSAkismet::isEnabled() && Permission::check('CMS_ACCESS_CMSMain') && !$this->getField('IsSpam')) {
|
||||||
|
return "PageComment/reportspam/$this->ID";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function HamLink() {
|
||||||
|
$member = Member::currentUser();
|
||||||
|
if(SSAkismet::isEnabled() && Permission::check('CMS_ACCESS_CMSMain') && $this->getField('IsSpam')) {
|
||||||
|
return "PageComment/reportham/$this->ID";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function SpamClass() {
|
||||||
|
if($this->getField('IsSpam')) {
|
||||||
|
return 'spam';
|
||||||
|
} else {
|
||||||
|
return 'notspam';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function reportspam() {
|
||||||
|
if(SSAkismet::isEnabled() && Permission::check('CMS_ACCESS_CMSMain')) {
|
||||||
|
$comment = DataObject::get_by_id("PageComment", $this->urlParams['ID']);
|
||||||
|
|
||||||
|
if($comment) {
|
||||||
|
try {
|
||||||
|
$akismet = new SSAkismet();
|
||||||
|
$akismet->setCommentAuthor($comment->getField('Name'));
|
||||||
|
$akismet->setCommentContent($comment->getField('Comment'));
|
||||||
|
|
||||||
|
$akismet->submitSpam();
|
||||||
|
} catch (Exception $e) {
|
||||||
|
// Akismet didn't work, most likely the service is down.
|
||||||
|
}
|
||||||
|
|
||||||
|
if(SSAkismet::getSaveSpam()) {
|
||||||
|
$comment->setField('IsSpam', true);
|
||||||
|
$comment->write();
|
||||||
|
} else {
|
||||||
|
$comment->delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(Director::is_ajax()) {
|
||||||
|
if(SSAkismet::getSaveSpam()) {
|
||||||
|
echo $comment->renderWith('PageCommentInterface_singlecomment');
|
||||||
|
} else {
|
||||||
|
echo '';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Director::redirectBack();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function reportham() {
|
||||||
|
if(SSAkismet::isEnabled() && Permission::check('CMS_ACCESS_CMSMain')) {
|
||||||
|
$comment = DataObject::get_by_id("PageComment", $this->urlParams['ID']);
|
||||||
|
|
||||||
|
if($comment) {
|
||||||
|
try {
|
||||||
|
$akismet = new SSAkismet();
|
||||||
|
$akismet->setCommentAuthor($comment->getField('Name'));
|
||||||
|
$akismet->setCommentContent($comment->getField('Comment'));
|
||||||
|
|
||||||
|
$akismet->submitHam();
|
||||||
|
} catch (Exception $e) {
|
||||||
|
// Akismet didn't work, most likely the service is down.
|
||||||
|
}
|
||||||
|
|
||||||
|
$comment->setField('IsSpam', false);
|
||||||
|
$comment->write();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(Director::is_ajax()) {
|
||||||
|
echo $comment->renderWith('PageCommentInterface_singlecomment');
|
||||||
|
} else {
|
||||||
|
Director::redirectBack();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function RSSTitle() {
|
||||||
|
return "Comment by '$this->Name' on " . $this->Parent()->Title;
|
||||||
|
}
|
||||||
|
function rss() {
|
||||||
|
$rss = new RSSFeed(DataObject::get("PageComment", "ParentID > 0", "Created DESC", "", 10), "home/", "Page comments", "", "RSSTitle", "Comment", "Name");
|
||||||
|
$rss->outputToBrowser();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
121
code/sitefeatures/PageCommentInterface.php
Executable file
@ -0,0 +1,121 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents an interface for viewing and adding page comments
|
||||||
|
* Create one, passing the page discussed to the constructor. It can then be
|
||||||
|
* inserted into a template.
|
||||||
|
*/
|
||||||
|
class PageCommentInterface extends ViewableData {
|
||||||
|
protected $controller, $methodName, $page;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new page comment interface
|
||||||
|
* @param controller The controller that the U
|
||||||
|
* @param methodName The method to return this PageCommentInterface object
|
||||||
|
* @param page The page that we're commenting on
|
||||||
|
*/
|
||||||
|
function __construct($controller, $methodName, $page) {
|
||||||
|
$this->controller = $controller;
|
||||||
|
$this->methodName = $methodName;
|
||||||
|
$this->page = $page;
|
||||||
|
}
|
||||||
|
|
||||||
|
function forTemplate() {
|
||||||
|
return $this->renderWith('PageCommentInterface');
|
||||||
|
}
|
||||||
|
|
||||||
|
function PostCommentForm() {
|
||||||
|
Requirements::javascript('jsparty/behaviour.js');
|
||||||
|
Requirements::javascript('jsparty/prototype.js');
|
||||||
|
Requirements::javascript('jsparty/scriptaculous/effects.js');
|
||||||
|
Requirements::javascript('cms/javascript/PageCommentInterface.js');
|
||||||
|
|
||||||
|
$form = new PageCommentInterface_Form($this->controller, $this->methodName . ".PostCommentForm", new FieldSet(
|
||||||
|
new HiddenField("ParentID", "ParentID", $this->page->ID),
|
||||||
|
new TextField("Name", "Your name"),
|
||||||
|
new TextareaField("Comment", "Comments")
|
||||||
|
|
||||||
|
), new FieldSet(
|
||||||
|
new FormAction("postcomment", "Post")
|
||||||
|
));
|
||||||
|
|
||||||
|
$form->loadDataFrom(array(
|
||||||
|
"Name" => Cookie::get("PageCommentInterface_Name"),
|
||||||
|
));
|
||||||
|
|
||||||
|
return $form;
|
||||||
|
}
|
||||||
|
|
||||||
|
function Comments() {
|
||||||
|
// Comment limits
|
||||||
|
if(isset($_GET['commentStart'])) {
|
||||||
|
$limit = (int)$_GET['commentStart'].",".PageComment::$comments_per_page;
|
||||||
|
} else {
|
||||||
|
$limit = "0,".PageComment::$comments_per_page;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(isset($_GET['showspam'])) {
|
||||||
|
$comments = DataObject::get("PageComment", "ParentID = '" . Convert::raw2sql($this->page->ID) . "'", "Created DESC", "", $limit);
|
||||||
|
} else {
|
||||||
|
$comments = DataObject::get("PageComment", "ParentID = '" . Convert::raw2sql($this->page->ID) . "' AND IsSpam = 0", "Created DESC", "", $limit);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(is_null($comments)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This allows us to use the normal 'start' GET variables as well (In the weird circumstance where you have paginated comments AND something else paginated)
|
||||||
|
$comments->setPaginationGetVar('commentStart');
|
||||||
|
|
||||||
|
return $comments;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class PageCommentInterface_Form extends Form {
|
||||||
|
function postcomment($data) {
|
||||||
|
// Spam filtering
|
||||||
|
if(SSAkismet::isEnabled()) {
|
||||||
|
try {
|
||||||
|
$akismet = new SSAkismet();
|
||||||
|
|
||||||
|
$akismet->setCommentAuthor($data['Name']);
|
||||||
|
$akismet->setCommentContent($data['Comment']);
|
||||||
|
|
||||||
|
if($akismet->isCommentSpam()) {
|
||||||
|
if(SSAkismet::getSaveSpam()) {
|
||||||
|
$comment = Object::create('PageComment');
|
||||||
|
$this->saveInto($comment);
|
||||||
|
$comment->setField("IsSpam", true);
|
||||||
|
$comment->write();
|
||||||
|
}
|
||||||
|
echo "<b>Spam detected!!</b><br /><br />";
|
||||||
|
echo "If you believe this was in error, please email ";
|
||||||
|
echo ereg_replace("@", " _(at)_", Email::getAdminEmail());
|
||||||
|
echo ".<br /><br />The message you posted was:<br /><br />";
|
||||||
|
echo $data['Comment'];
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} catch (Exception $e) {
|
||||||
|
// Akismet didn't work, continue without spam check
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Cookie::set("PageCommentInterface_Name", $data['Name']);
|
||||||
|
|
||||||
|
$comment = Object::create('PageComment');
|
||||||
|
$this->saveInto($comment);
|
||||||
|
$comment->IsSpam = false;
|
||||||
|
$comment->write();
|
||||||
|
|
||||||
|
if(Director::is_ajax()) {
|
||||||
|
echo $comment->renderWith('PageCommentInterface_singlecomment');
|
||||||
|
} else {
|
||||||
|
Director::redirectBack();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
26
code/sitefeatures/SSAkismet.php
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
<?php
|
||||||
|
class SSAkismet extends Akismet {
|
||||||
|
private static $apiKey;
|
||||||
|
private static $saveSpam = false;
|
||||||
|
|
||||||
|
static function setAPIKey($key) {
|
||||||
|
self::$apiKey = $key;
|
||||||
|
}
|
||||||
|
|
||||||
|
static function isEnabled() {
|
||||||
|
return (self::$apiKey != null) ? true : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static function setSaveSpam($save = true) {
|
||||||
|
SSAkismet::$saveSpam = $save;
|
||||||
|
}
|
||||||
|
|
||||||
|
static function getSaveSpam() {
|
||||||
|
return SSAkismet::$saveSpam;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __construct() {
|
||||||
|
parent::__construct(Director::absoluteBaseURL(), self::$apiKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
236
css/GenericDataAdmin.css
Executable file
@ -0,0 +1,236 @@
|
|||||||
|
.right form div.tab {
|
||||||
|
padding-top: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
form#Form_EditForm #Form_ExportForm fieldset {
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.leftcolumn {
|
||||||
|
margin: 0;
|
||||||
|
width: 46%;
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rightcolumn {
|
||||||
|
width: 46%;
|
||||||
|
margin: 0;
|
||||||
|
float: right;
|
||||||
|
clear: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab div.groupfield, .tab div.groupfield div.groupfield {
|
||||||
|
margin: 1.2em 0;
|
||||||
|
border: 1px solid #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab div.field {
|
||||||
|
margin: .3em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#left {
|
||||||
|
width:335px
|
||||||
|
}
|
||||||
|
#LeftPane {
|
||||||
|
background-color: #FFF;
|
||||||
|
border-left: 1px #ccc solid;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Generic form styling */
|
||||||
|
|
||||||
|
#LeftPane h3 {
|
||||||
|
font-size:1.2em;
|
||||||
|
letter-spacing:1px;
|
||||||
|
color:#4874C6;
|
||||||
|
}
|
||||||
|
|
||||||
|
#LeftPane form label.left {
|
||||||
|
font-size:1.1em;
|
||||||
|
font-weight:bold;
|
||||||
|
float:left;
|
||||||
|
width:100px;
|
||||||
|
padding-right:3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#LeftPane form div {
|
||||||
|
clear:left;
|
||||||
|
}
|
||||||
|
|
||||||
|
#LeftPane form div.field {
|
||||||
|
margin-bottom:5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#LeftPane form input {
|
||||||
|
padding:2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#LeftPane form input.text,
|
||||||
|
#LeftPane form select {
|
||||||
|
padding:2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#LeftPane form input.text {
|
||||||
|
width:145px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#LeftPane form div.field select {
|
||||||
|
width:152px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#LeftPane #AddForm {
|
||||||
|
padding: 5px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#Search_holder {
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
#Search_holder .ToggleAdvancedSearchFields {
|
||||||
|
margin: 3px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#Search_holder .Actions {
|
||||||
|
margin: 5px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#Search_holder h3 {
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#LeftPane #Form_CreationForm {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#LeftPane #Form_CreationForm p.Actions {
|
||||||
|
margin-left: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#SearchForm_holder {
|
||||||
|
overflow: auto; /* TODO */
|
||||||
|
padding: 5px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#SearchForm_holder #DateRange .calendardate,
|
||||||
|
#SearchForm_holder #DateRange label{
|
||||||
|
float:left;
|
||||||
|
clear:none;
|
||||||
|
padding:1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#SearchForm_holder #DateRange input {
|
||||||
|
width:65px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Result list layout */
|
||||||
|
div.ResultList {
|
||||||
|
padding: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.ResultList ul {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
list-style: none;
|
||||||
|
background:#eee;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.ResultList li {
|
||||||
|
padding: .1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.ResultList ul ul {
|
||||||
|
margin-left: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.ResultList li.odd {
|
||||||
|
background:#f5f5f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.ResultList li.even {
|
||||||
|
background:#eee;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Result table layout */
|
||||||
|
table.ResultTable table tbody tr.odd {
|
||||||
|
background:#f5f5f5;
|
||||||
|
}
|
||||||
|
table.ResultTable table tbody tr td {
|
||||||
|
padding:2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.ResultTable {
|
||||||
|
padding: 1em;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
table.ResultTable * {
|
||||||
|
border:0;
|
||||||
|
font-size:1em;
|
||||||
|
}
|
||||||
|
table.ResultTable td {
|
||||||
|
padding: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.ResultTable thead tr {
|
||||||
|
background:#eee;
|
||||||
|
}
|
||||||
|
table.ResultTable thead tr td {
|
||||||
|
font-weight:bold;
|
||||||
|
letter-spacing:1px;
|
||||||
|
font-size:1.1em;
|
||||||
|
}
|
||||||
|
table.ResultTable tbody tr {
|
||||||
|
background:#eee;
|
||||||
|
}
|
||||||
|
table.ResultTable tbody tr.even {
|
||||||
|
background:#f0f8ff;
|
||||||
|
}
|
||||||
|
table.ResultTable tbody tr.odd {
|
||||||
|
background:#fff;
|
||||||
|
}
|
||||||
|
table.ResultTable tbody tr td {
|
||||||
|
font-size:1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#Search_holder table.ResultTable tbody tr {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
#Form_export_action_export {
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#Form_EditForm_GenericDataStatus {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 500;
|
||||||
|
top: -70px;
|
||||||
|
right: -10px;
|
||||||
|
text-align: right;
|
||||||
|
width: 150px;
|
||||||
|
height: 38px;
|
||||||
|
|
||||||
|
padding: 10px;
|
||||||
|
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: bold;
|
||||||
|
|
||||||
|
border: 1px solid #cc9;
|
||||||
|
color: #660;
|
||||||
|
background-color: #F9F9E3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clear:after {
|
||||||
|
content: ".";
|
||||||
|
display: block;
|
||||||
|
height: 0;
|
||||||
|
clear: both;
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clear {display: inline-block;}
|
||||||
|
|
||||||
|
/* Hides from IE-mac \*/
|
||||||
|
* html .clear {height: 1%;}
|
||||||
|
.clear {display: block;}
|
||||||
|
/* End hide from IE-mac */
|
146
css/Image_iframe.css
Executable file
@ -0,0 +1,146 @@
|
|||||||
|
@import url("typography.css");
|
||||||
|
|
||||||
|
html,body {
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
border-style: none;
|
||||||
|
height: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
form {
|
||||||
|
margin: 0; padding: 0;
|
||||||
|
}
|
||||||
|
form fieldset{
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
border-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 1.4em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Selection Groups
|
||||||
|
*/
|
||||||
|
.SelectionGroup {
|
||||||
|
padding: 0;
|
||||||
|
margin: 10px 0 0 0;
|
||||||
|
}
|
||||||
|
.SelectionGroup li {
|
||||||
|
list-style-type: none;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
.SelectionGroup li input.selector {
|
||||||
|
width: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.SelectionGroup li div.field {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.SelectionGroup li.selected div.field {
|
||||||
|
margin-left: 30px;
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 1em;
|
||||||
|
}
|
||||||
|
.SelectionGroup li.selected label.selector {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TreeDropdownField stying
|
||||||
|
*/
|
||||||
|
div.TreeDropdownField {
|
||||||
|
width: 241px;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
html>body div.TreeDropdownField {
|
||||||
|
position:relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.TreeDropdownField span.items {
|
||||||
|
display: block;
|
||||||
|
height: 100%;
|
||||||
|
border: 1px #7f9db9 solid;
|
||||||
|
cursor: pointer;
|
||||||
|
width: 220px;
|
||||||
|
float: left;
|
||||||
|
padding-top: 2px;
|
||||||
|
padding-bottom: 2px;
|
||||||
|
background-color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.TreeDropdownField div.tree_holder {
|
||||||
|
clear: left;
|
||||||
|
cursor: default;
|
||||||
|
border: 1px black solid;
|
||||||
|
margin: 0;
|
||||||
|
height: 180px;
|
||||||
|
overflow: auto;
|
||||||
|
background-color: white;
|
||||||
|
/**
|
||||||
|
* HACK IE6, see http://www.hedgerwow.com/360/bugs/css-select-free.html
|
||||||
|
*/
|
||||||
|
position:absolute;
|
||||||
|
z-index:10;
|
||||||
|
width:240px;/*must have for any value*/
|
||||||
|
}
|
||||||
|
|
||||||
|
html>body div.TreeDropdownField div.tree_holder {
|
||||||
|
top: 20px;
|
||||||
|
left: 0px;
|
||||||
|
z-index: 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HACK IE6, see http://www.hedgerwow.com/360/bugs/css-select-free.html
|
||||||
|
*/
|
||||||
|
div.TreeDropdownField div.tree_holder iframe {
|
||||||
|
display:none;/* IE5*/
|
||||||
|
display/**/:block;/* IE5*/
|
||||||
|
position:absolute;
|
||||||
|
top:0;
|
||||||
|
left:0;
|
||||||
|
z-index:-1;
|
||||||
|
filter:mask();
|
||||||
|
width:180px; /*must have for any big value*/
|
||||||
|
height:200px/*must have for any big value*/;
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.TreeDropdownField a.editLink {
|
||||||
|
border-width: 1px 1px 1px 0;
|
||||||
|
background: url(../../sapphire/images/TreeDropdownField_button.gif) left top no-repeat;
|
||||||
|
width: 19px;
|
||||||
|
height: 21px;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
float: left;
|
||||||
|
clear: right;
|
||||||
|
z-index: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.Actions {
|
||||||
|
text-align: right;
|
||||||
|
margin: 0;
|
||||||
|
position: absolute;
|
||||||
|
right: 5px;
|
||||||
|
bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mainblock {
|
||||||
|
float: left;
|
||||||
|
border: 1px #CCC solid;
|
||||||
|
padding: 5px;
|
||||||
|
margin-right: 5px;
|
||||||
|
height: 120px;
|
||||||
|
position: relative;
|
||||||
|
}
|
44
css/LeftAndMain_printable.css
Executable file
@ -0,0 +1,44 @@
|
|||||||
|
* {
|
||||||
|
font-family: Helvetica, Verdana, Geneva, Arial, Helvetica, sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
margin-top: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
form .field {
|
||||||
|
margin-bottom: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
form label.left {
|
||||||
|
float: left;
|
||||||
|
width: 15em;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
form div.field {
|
||||||
|
/*margin-left: 10em;*/
|
||||||
|
}
|
||||||
|
|
||||||
|
form .inlineformaction,
|
||||||
|
form .inlineformaction_readonly,
|
||||||
|
form .action,
|
||||||
|
form .action_readonly
|
||||||
|
{
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pageStatusMessage {
|
||||||
|
position: absolute;
|
||||||
|
right: 2em;
|
||||||
|
top: 2em;
|
||||||
|
border: 1px #777 solid;
|
||||||
|
}
|
||||||
|
|
||||||
|
.TableListField table.data th {
|
||||||
|
background: #eee;
|
||||||
|
}
|
||||||
|
|
||||||
|
.TableListField table.data {
|
||||||
|
width: 100%;
|
||||||
|
border: 1px #aaa solid;
|
||||||
|
}
|
404
css/cms_left.css
Normal file
@ -0,0 +1,404 @@
|
|||||||
|
#TreeActions {
|
||||||
|
background-color: #EEE;
|
||||||
|
border-bottom: 1px #CCC solid;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
/* height: 23px; */
|
||||||
|
float: left;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
#TreeActions li.action {
|
||||||
|
float: left;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
padding-right: 7px;
|
||||||
|
list-style-type: none;
|
||||||
|
}
|
||||||
|
#TreeActions li.action a {
|
||||||
|
padding: 0.3em;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
#TreeActions li.action a:hover {
|
||||||
|
background-color: #CCC;
|
||||||
|
}
|
||||||
|
#TreeActions li.selected a {
|
||||||
|
background-color: #CCC;
|
||||||
|
}
|
||||||
|
|
||||||
|
#left form.actionparams {
|
||||||
|
background-color: #CCC;
|
||||||
|
border-bottom: 1px #EEE solid;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#left form.actionparams select {
|
||||||
|
width: 75%;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
#left form.actionparms input.action {
|
||||||
|
width: 5%;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Selection Groups
|
||||||
|
*/
|
||||||
|
#left .SelectionGroup {
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
#left .SelectionGroup li {
|
||||||
|
list-style-type: none;
|
||||||
|
clear: left;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
border: 1px #CCC solid;
|
||||||
|
}
|
||||||
|
#left .SelectionGroup li input {
|
||||||
|
width: 20px;
|
||||||
|
float: left;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
#left .SelectionGroup li div.field {
|
||||||
|
display: none;
|
||||||
|
font-size: 1em;
|
||||||
|
}
|
||||||
|
#left .SelectionGroup li label {
|
||||||
|
margin: 0 0 0 2px;
|
||||||
|
padding: 0;
|
||||||
|
display: block;
|
||||||
|
height: 1.2em;
|
||||||
|
width: auto;
|
||||||
|
float: none;
|
||||||
|
}
|
||||||
|
#left .SelectionGroup li.selected div.field {
|
||||||
|
margin-left: 23px;
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 1em;
|
||||||
|
}
|
||||||
|
#left div.field select {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#left .SelectionGroup li.selected label.selector {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ul.tree span.a {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
ul.tree span.a.over {
|
||||||
|
background-color: #FFFFBB;
|
||||||
|
|
||||||
|
/* these push the highlight out to the left of the window */
|
||||||
|
margin-left: -100px;
|
||||||
|
padding-left: 100px;
|
||||||
|
background-position: 100px 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul.tree span.a.current {
|
||||||
|
font-weight: bold;
|
||||||
|
background-color: #EEEEFF;
|
||||||
|
border-top: 1px #CCCCFF solid;
|
||||||
|
border-bottom: 1px #CCCCFF solid;
|
||||||
|
|
||||||
|
/* these push the highlight out to the left of the window */
|
||||||
|
margin-left: -100px;
|
||||||
|
padding-left: 100px;
|
||||||
|
background-position: 100px 50%;
|
||||||
|
/*position: relative;*/
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
ul.tree span.a.current span#currentNodeActions {
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
top: 0;
|
||||||
|
background-color: white;
|
||||||
|
padding: 0 3px;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
ul.tree span.a.loading span.b span.c a {
|
||||||
|
background-image: url(../images/network-save.gif) !important;
|
||||||
|
margin-left: -3px;
|
||||||
|
padding-left: 21px;
|
||||||
|
background-position : 2px 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul.tree.multiselect span.a span.b a {
|
||||||
|
background-image: url(../images/tickbox-unticked.gif) !important;
|
||||||
|
}
|
||||||
|
ul.tree.multiselect span.a.nodelete span.b a {
|
||||||
|
background-image: url(../images/tickbox-canttick.gif) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ul.tree.multiselect span.a.selected span.b span.c a {
|
||||||
|
background-image: url(../images/tickbox-ticked.gif) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul.tree.multiselect li.selected ul span.a a {
|
||||||
|
background-image: url(../images/tickbox-greyticked.gif) !important;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
ul.tree.multiselect li.selected ul {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Span-B: Plus/Minus icon */
|
||||||
|
ul.tree.multiselect li.selected span.a.children span.b, ul.tree.multiselect li.selected span.a.unexpanded span.b {
|
||||||
|
background-image: none;
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul.tree span.a a.disabled {
|
||||||
|
color: #999;
|
||||||
|
cursor: normal;
|
||||||
|
}
|
||||||
|
ul.tree span.a a.disabled * {
|
||||||
|
padding: 0;
|
||||||
|
background: none;
|
||||||
|
color: #999;
|
||||||
|
cursor: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ul.tree .deleted {
|
||||||
|
color: red;
|
||||||
|
text-decoration: line-through;
|
||||||
|
}
|
||||||
|
/* Created on stage, never published */
|
||||||
|
ul.tree ins,
|
||||||
|
#publication_key ins {
|
||||||
|
color: orange;
|
||||||
|
padding-left : 16px;
|
||||||
|
background-image : url(../../sapphire/images/NewOnStage.png);
|
||||||
|
background-position : 0px 1px;
|
||||||
|
background-repeat : no-repeat;
|
||||||
|
text-decoration : none;
|
||||||
|
}
|
||||||
|
/* Deleated on stage */
|
||||||
|
ul.tree del,
|
||||||
|
#publication_key del {
|
||||||
|
color: red;
|
||||||
|
padding-left : 16px;
|
||||||
|
background-image : url(../../sapphire/images/TrashedOnStage.png);
|
||||||
|
background-repeat : no-repeat;
|
||||||
|
}
|
||||||
|
ul.tree span.modified,
|
||||||
|
#publication_key span.modified {
|
||||||
|
color: green;
|
||||||
|
padding-left : 16px;
|
||||||
|
background-position : 1px 0px;
|
||||||
|
background-image : url(../../sapphire/images/EditedOnStage.png);
|
||||||
|
background-repeat : no-repeat;
|
||||||
|
text-decoration : none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Side tabs
|
||||||
|
*/
|
||||||
|
#SideTabs {
|
||||||
|
float: left;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
width: 18px;
|
||||||
|
height: 80%;
|
||||||
|
}
|
||||||
|
#SideTabs li {
|
||||||
|
clear: left;
|
||||||
|
width: 16px;
|
||||||
|
text-indent: -100em;
|
||||||
|
height: 70px;
|
||||||
|
overflow: hidden;
|
||||||
|
margin: 5px 0 5px 2px;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
#SideTabs li#sidetab_versions { background-image: url(../images/sidetabs/versions.gif); }
|
||||||
|
#SideTabs li#sidetab_sitetree { background-image: url(../images/sidetabs/sitemap.gif); }
|
||||||
|
#SideTabs li#sidetab_tasklist { background-image: url(../images/sidetabs/tasklist.gif); }
|
||||||
|
#SideTabs li#sidetab_waitingon { background-image: url(../images/sidetabs/waitingon.gif); }
|
||||||
|
#SideTabs li#sidetab_comments { background-image: url(../images/sidetabs/comments.gif); }
|
||||||
|
#SideTabs li#sidetab_reports { background-image: url(../images/sidetabs/reports.gif); }
|
||||||
|
#SideTabs li#sidetab_search { background-image: url(../images/sidetabs/search.gif); }
|
||||||
|
#SideTabs li#sidetab_advertisements { background-image: url(../images/sidetabs/advertisements.gif); }
|
||||||
|
|
||||||
|
#SideTabs li#sidetab_versions.selected { background-image: url(../images/sidetabs/versions_over.gif); }
|
||||||
|
#SideTabs li#sidetab_sitetree.selected { background-image: url(../images/sidetabs/sitemap_over.gif); }
|
||||||
|
#SideTabs li#sidetab_tasklist.selected { background-image: url(../images/sidetabs/tasklist_over.gif); }
|
||||||
|
#SideTabs li#sidetab_waitingon.selected { background-image: url(../images/sidetabs/waitingon_over.gif); }
|
||||||
|
#SideTabs li#sidetab_comments.selected { background-image: url(../images/sidetabs/comments_over.gif); }
|
||||||
|
#SideTabs li#sidetab_reports.selected { background-image: url(../images/sidetabs/reports_over.gif); }
|
||||||
|
#SideTabs li#sidetab_search.selected { background-image: url(../images/sidetabs/search_over.gif); }
|
||||||
|
#SideTabs li#sidetab_advertisements.selected { background-image: url(../images/sidetabs/advertisements_over.gif); }
|
||||||
|
|
||||||
|
#left {
|
||||||
|
background-color: #EEE;
|
||||||
|
}
|
||||||
|
|
||||||
|
#treepanes {
|
||||||
|
background-color: #FFF;
|
||||||
|
border-left: 1px #ccc solid;
|
||||||
|
}
|
||||||
|
|
||||||
|
.listpane p {
|
||||||
|
margin: 3px 3px 7px 3px;
|
||||||
|
font-size: 10px;
|
||||||
|
}
|
||||||
|
.listpane ul {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
.listpane ul li {
|
||||||
|
list-style-type: none;
|
||||||
|
padding: 5px 2px 5px 2px;
|
||||||
|
font-size: 10px;
|
||||||
|
border-bottom: 1px #CCC dotted;
|
||||||
|
background-color: #FFF;
|
||||||
|
}
|
||||||
|
.listpane ul li.odd {
|
||||||
|
background-color: #EEE;
|
||||||
|
}
|
||||||
|
|
||||||
|
.listpane ul li .extra {
|
||||||
|
font-style: italic;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
.listpane div.unitBody {
|
||||||
|
overflow: auto;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#CommentList {
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
#CommentList li {
|
||||||
|
list-style-type: none;
|
||||||
|
background-color: #EEE;
|
||||||
|
border: 1px #CCC solid;
|
||||||
|
padding: 3px;
|
||||||
|
margin: 7px;
|
||||||
|
}
|
||||||
|
#CommentList li .extra {
|
||||||
|
margin-top: 3px;
|
||||||
|
font-size: 80%;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ReportSelector_holder {
|
||||||
|
background-color: #EEE;
|
||||||
|
border-bottom: 1px #CCC solid;
|
||||||
|
margin: 0;
|
||||||
|
padding: 3px;
|
||||||
|
}
|
||||||
|
#ReportSelector_holder select {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#treepanes .pane_actions {
|
||||||
|
margin: 7px 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#treepanes .pane_actions a {
|
||||||
|
margin: 4px;
|
||||||
|
padding: 3px;
|
||||||
|
background-color: #EEE;
|
||||||
|
border: 1px #CCC solid;
|
||||||
|
}
|
||||||
|
#treepanes .pane_actions a.current {
|
||||||
|
background-color: #CCC;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#treepanes table {
|
||||||
|
border-collapse: collapse;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#treepanes table thead td {
|
||||||
|
background-color: #777;
|
||||||
|
color: white;
|
||||||
|
border-bottom: 1px #CCC solid;
|
||||||
|
}
|
||||||
|
|
||||||
|
#treepanes table tbody tr {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
#treepanes table tbody tr.odd td {
|
||||||
|
background-color: #EEE;
|
||||||
|
}
|
||||||
|
#treepanes table tbody tr.over td {
|
||||||
|
background-color: #FFFFBB;
|
||||||
|
}
|
||||||
|
|
||||||
|
#treepanes table tbody tr.current td {
|
||||||
|
background-color: #BBBBFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#treepanes #Versions tbody tr.internal {
|
||||||
|
color: #777;
|
||||||
|
}
|
||||||
|
#treepanes #Versions tbody tr.published {
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Change the styling of the root tree node
|
||||||
|
*/
|
||||||
|
ul.tree span.a.Root, ul.tree span.a.last.Root, ul.tree span.a.children.Root {
|
||||||
|
background-image: none;
|
||||||
|
}
|
||||||
|
ul.tree span.a.Root span.c, ul.tree span.a.last.Root span.c, ul.tree span.a.children.Root span.c {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
ul#sitetree.tree ul {
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
ul#sitetree.tree ul ul {
|
||||||
|
margin-left: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
ul.tree span.a.MailType, ul.tree span.a.last.MailType, ul.tree span.a.children.MailType {
|
||||||
|
background-image: none;
|
||||||
|
}
|
||||||
|
ul.tree span.a.MailType span.c, ul.tree span.a.last.MailType span.c, ul.tree span.a.children.MailType span.c {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contextMenu {
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
.contextMenu li {
|
||||||
|
list-style-type: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
.contextMenu li a {
|
||||||
|
display: block;
|
||||||
|
font-family: Tahoma, Verdana, Arial, Helvetica;
|
||||||
|
font-size: 11px;
|
||||||
|
padding: 5px 10px 5px 5px;
|
||||||
|
color: black;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
.contextMenu li a:hover {
|
||||||
|
background-color: #B6BDD2;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
765
css/cms_right.css
Normal file
@ -0,0 +1,765 @@
|
|||||||
|
|
||||||
|
.right form {
|
||||||
|
margin: 1em;
|
||||||
|
position: relative;
|
||||||
|
/*top: 70px;*/
|
||||||
|
}
|
||||||
|
html>body .right from {
|
||||||
|
/*top: 50px;*/
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
iframe {
|
||||||
|
border : none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.right form div.field {
|
||||||
|
clear: both;
|
||||||
|
margin-left: 10em;
|
||||||
|
font-size: 1.2em;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
.right form p.field {
|
||||||
|
clear: both;
|
||||||
|
font-size: 1.2em;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.right form div.field.nolabel {
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.right form label.left {
|
||||||
|
float: left;
|
||||||
|
width: 10em;
|
||||||
|
margin-left: -10em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.right form input.maxlength {
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.right form textarea {
|
||||||
|
width: 98%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.right form textarea.htmleditor {
|
||||||
|
width: 90%;
|
||||||
|
display : none;
|
||||||
|
}
|
||||||
|
html>body .right form textarea.htmleditor {
|
||||||
|
width: 98%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.right form span.readonly {
|
||||||
|
border: 1px #CCC dotted;
|
||||||
|
background-color: #F7F7F7;
|
||||||
|
display: block;
|
||||||
|
width: 98%;
|
||||||
|
padding: 3px;
|
||||||
|
margin:5px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.right form .validation,
|
||||||
|
.right form .error,
|
||||||
|
.right form .required
|
||||||
|
{
|
||||||
|
border: 1px solid #f00;
|
||||||
|
background: #fcc;
|
||||||
|
padding: 0.5em;
|
||||||
|
width: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.right form .TableField .message {
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.right form .creditCardfield input {
|
||||||
|
width: 4.415em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.right form input.checkbox, .right form .optionset input, .right form .htmleditor select, .right form input.action {
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
.right form ul.optionset {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
.right form .optionset li {
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.right form h2 {
|
||||||
|
clear: both;
|
||||||
|
}
|
||||||
|
|
||||||
|
.right form .fieldgroup input, .right form .fieldgroup select {
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TableField (and subclasses)
|
||||||
|
*/
|
||||||
|
table.TableField,
|
||||||
|
table.TableListField,
|
||||||
|
.TableListField table.data,
|
||||||
|
table.CMSList {
|
||||||
|
border-collapse: collapse;
|
||||||
|
border-spacing: 0;
|
||||||
|
border : 1px solid #aaaaaa;
|
||||||
|
width : 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* HACK Preventing IE6 from showing double borders */
|
||||||
|
body>div table.TableField,
|
||||||
|
body>div table.TableListField,
|
||||||
|
body>div .TableListField table.data,
|
||||||
|
body>div table.CMSList {
|
||||||
|
border-collapse: separate;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.TableField td,
|
||||||
|
table.TableListField td,
|
||||||
|
.TableListField table.data td,
|
||||||
|
table.CMSList td {
|
||||||
|
border-style:none;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.TableField th,
|
||||||
|
table.TableListField th,
|
||||||
|
.TableListField table.data th,
|
||||||
|
table.CMSList th {
|
||||||
|
whitespace: no-wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.TableField thead th,
|
||||||
|
.TableListField table.data thead th,
|
||||||
|
table.CMSList thead th {
|
||||||
|
background-image : url(../../cms/images/tables/thead.png);
|
||||||
|
background-repeat : repeat-x;
|
||||||
|
background-position : left bottom;
|
||||||
|
background-color : #ebeadb;
|
||||||
|
height : 24px;
|
||||||
|
border-right : 1px solid #aca899;
|
||||||
|
border-left : 1px solid #ffffff;
|
||||||
|
padding-left : 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.TableField thead th span,
|
||||||
|
.TableListField table.data thead th span {
|
||||||
|
display: block;
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.TableField thead th a,
|
||||||
|
.TableListField table.data thead th a,
|
||||||
|
table.CMSList thead th a {
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.TableField thead th span.sortLink,
|
||||||
|
.TableListField table.data thead th span.sortLink,
|
||||||
|
table.CMSList thead th span.sortLink {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.TableField tfoot tr.summary td,
|
||||||
|
.TableListField table.data tfoot tr.summary td,
|
||||||
|
table.CMSList tfoot tr.summary td {
|
||||||
|
background-color : #ebeadb;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.TableField tbody td,
|
||||||
|
table.TableField tfoot td,
|
||||||
|
.TableListField table.data tbody td,
|
||||||
|
.TableListField table.data tfoot td,
|
||||||
|
table.CMSList tbody td,
|
||||||
|
table.CMSList tfoot td {
|
||||||
|
border : 1px solid #f1efe2;
|
||||||
|
padding-left : 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.TableField td input,
|
||||||
|
.TableListField td input {
|
||||||
|
width: 98%;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.data tbody td input {
|
||||||
|
border:0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.TableField tbody td.checkbox,
|
||||||
|
.TableListField table.data tbody td.checkbox,
|
||||||
|
table.CMSList tbody td.checkbox {
|
||||||
|
border : 1px solid #f1efe2;
|
||||||
|
padding-left : 5px;
|
||||||
|
background-image : url(../../cms/images/tables/checkbox.png);
|
||||||
|
background-repeat : repeat-x;
|
||||||
|
background-position : left bottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.TableField tbody tr.over td,
|
||||||
|
.TableListField table.data tbody tr.over td,
|
||||||
|
table.CMSList tbody td.over td{
|
||||||
|
background-color: #FFC;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.TableField tbody tr.current td,
|
||||||
|
.TableListField table.data tbody tr.current td,
|
||||||
|
table.CMSList tbody td.current td {
|
||||||
|
background-color: #316ac5;
|
||||||
|
color : white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.TableListField table.data tfoot .addlink img {
|
||||||
|
vertical-align: middle;
|
||||||
|
margin: 3px 6px 3px 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.right form .TableField span.readonly {
|
||||||
|
border: 0;
|
||||||
|
background: none;
|
||||||
|
padding: 0;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.TableListField div.utility {
|
||||||
|
margin-top: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.TableListField div.utility a {
|
||||||
|
font-size: 9px;
|
||||||
|
border: 1px solid #aaa;
|
||||||
|
padding: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Selection Groups
|
||||||
|
*/
|
||||||
|
.SelectionGroup {
|
||||||
|
padding: 0px;
|
||||||
|
clear : both;
|
||||||
|
}
|
||||||
|
.SelectionGroup li {
|
||||||
|
list-style-type: none;
|
||||||
|
float : left;
|
||||||
|
clear : both;
|
||||||
|
}
|
||||||
|
.SelectionGroup li input {
|
||||||
|
width: 20px;
|
||||||
|
float : left;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.SelectionGroup li div.field {
|
||||||
|
display: none;
|
||||||
|
font-size: 1em;
|
||||||
|
}
|
||||||
|
.SelectionGroup li input, .right .SelectionGroup li label {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
.SelectionGroup li.selected div.field {
|
||||||
|
margin-left: 30px;
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.SelectionGroup li.selected label.selector {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Composite Fields - raw concatenation of fields for programmatic purposes.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
.right form div.field.CompositeField {
|
||||||
|
margin-left: 7.5em;
|
||||||
|
}
|
||||||
|
.right form div.field.CompositeField div.field {
|
||||||
|
font-size: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.right form div.field.CompositeField {
|
||||||
|
clear: both;
|
||||||
|
}
|
||||||
|
.right form div.field.CompositeField label.left {
|
||||||
|
float: left;
|
||||||
|
width: 10em;
|
||||||
|
margin-left: -10em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.right form div.column2 {
|
||||||
|
float: left;
|
||||||
|
width: 45%;
|
||||||
|
margin-right: 4%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.right form div.multicolumn {
|
||||||
|
width: 100%;
|
||||||
|
float: left;
|
||||||
|
clear: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checkbox set fields
|
||||||
|
*/
|
||||||
|
#right form .CheckboxSetField ul{
|
||||||
|
margin:0;
|
||||||
|
padding:0;
|
||||||
|
}
|
||||||
|
#right form .CheckboxSetField ul li{
|
||||||
|
list-style:none !important;
|
||||||
|
margin:0;
|
||||||
|
padding:0;
|
||||||
|
}
|
||||||
|
#right form .CheckboxSetField input{
|
||||||
|
width:auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.ajaxActions {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
text-align: right;
|
||||||
|
background-image:url(../images/textures/ToolBar.png);
|
||||||
|
height : 21px;
|
||||||
|
border-left : 1px solid #808080;
|
||||||
|
border-bottom : 1px solid #808080;
|
||||||
|
}
|
||||||
|
.ajaxActions input {
|
||||||
|
padding: 0;
|
||||||
|
border-style: none;
|
||||||
|
background-color:transparent;
|
||||||
|
cursor: pointer;
|
||||||
|
float : left;
|
||||||
|
height : 22px;
|
||||||
|
background-image:url(../images/textures/seperator.png);
|
||||||
|
background-repeat:no-repeat;
|
||||||
|
background-position:right;
|
||||||
|
padding-left: 10px;
|
||||||
|
padding-right : 10px;
|
||||||
|
margin : 0px;
|
||||||
|
color : #333333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.right form div.tab {
|
||||||
|
clear: left;
|
||||||
|
overflow: auto;
|
||||||
|
border: 1px #AAA solid;
|
||||||
|
border-top: none;
|
||||||
|
position: relative;
|
||||||
|
top: -3px;
|
||||||
|
margin: 0;
|
||||||
|
padding: 10px;
|
||||||
|
/*width: 98%;*/
|
||||||
|
}
|
||||||
|
.right form div.tabset {
|
||||||
|
border: 1px solid #fff; /* Hack for FF1.5/Win Float-Bug */
|
||||||
|
clear: left;
|
||||||
|
margin: 0;
|
||||||
|
/*width: 100%;*/
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bottom-right
|
||||||
|
*/
|
||||||
|
|
||||||
|
#rightbottom {
|
||||||
|
|
||||||
|
|
||||||
|
border : none;
|
||||||
|
margin-top : 4px;
|
||||||
|
margin-left : 1px;
|
||||||
|
border : 1px solid #808080;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#rightbottom form {
|
||||||
|
margin: 1em;
|
||||||
|
/*position: relative;
|
||||||
|
top: 50px;*/
|
||||||
|
}
|
||||||
|
|
||||||
|
#rightbottom form div {
|
||||||
|
margin-top : 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#rightbottom form div.field {
|
||||||
|
clear: both;
|
||||||
|
margin-left: 10em;
|
||||||
|
font-size: 1.2em;
|
||||||
|
|
||||||
|
border : none;
|
||||||
|
}
|
||||||
|
#rightbottom form label.left {
|
||||||
|
float: left;
|
||||||
|
width: 10em;
|
||||||
|
margin-left: -10em;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#rightbottom form input, #rightbottom form select, #rightbottom form textarea {
|
||||||
|
width: 90%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#rightbottom form input.checkbox, #rightbottom form .optionset input, #rightbottom form .htmleditor select, #rightbottom form input.action {
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
#rightbottom form ul.optionset {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
#rightbottom form .optionset li {
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#rightbottom form h2 {
|
||||||
|
clear: both;
|
||||||
|
}
|
||||||
|
|
||||||
|
#rightbottom form .fieldgroup input, #rightbottom form .fieldgroup select {
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RHS Action Parameters boxes
|
||||||
|
*/
|
||||||
|
#right form.actionparams {
|
||||||
|
border-bottom: 1px #777 solid;
|
||||||
|
border-left: 1px #777 solid;
|
||||||
|
background-color: #CCC;
|
||||||
|
position: absolute;
|
||||||
|
top: 23px;
|
||||||
|
right: 0px;
|
||||||
|
width: 300px;
|
||||||
|
|
||||||
|
z-index: 200;
|
||||||
|
|
||||||
|
margin: 0;
|
||||||
|
padding: 10px;
|
||||||
|
|
||||||
|
}
|
||||||
|
#right form.actionparams div.field {
|
||||||
|
margin-left: 6em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#right form.actionparams label.left {
|
||||||
|
width: 6em;
|
||||||
|
margin-left: -6em;
|
||||||
|
}
|
||||||
|
#right form.actionparams input, #right form.actionparams select, #right form.actionparams textarea {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
#right form.actionparams input.checkbox, #right form.actionparams .optionset input {
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
#right form.actionparams p.label {
|
||||||
|
margin: 10px 0 0 0;
|
||||||
|
}
|
||||||
|
#right form.actionparams p.actions {
|
||||||
|
margin: 10px 0 0 0;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
#right form.actionparams p.actions input {
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
#right form.actionparams .sendingText {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
width: 100%;
|
||||||
|
padding: 10% 0 0 0;
|
||||||
|
height: 60%;
|
||||||
|
background-color: #CCC;
|
||||||
|
font-size: 25px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: white;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#right form.actionparams div.TreeDropdownField {
|
||||||
|
width: 230px;
|
||||||
|
|
||||||
|
}
|
||||||
|
#right form.actionparams div.TreeDropdownField span.items {
|
||||||
|
width: 210px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#right form.actionparams div.TreeDropdownField div.tree_holder {
|
||||||
|
width: 214px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.TreeDropdownField {
|
||||||
|
width: 35em;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
html>body div.TreeDropdownField {
|
||||||
|
position:relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.TreeDropdownField span.items {
|
||||||
|
display: block;
|
||||||
|
height: 100%;
|
||||||
|
border: 1px #7f9db9 solid;
|
||||||
|
cursor: pointer;
|
||||||
|
width: 33em;
|
||||||
|
float: left;
|
||||||
|
padding-top: 2px;
|
||||||
|
padding-bottom: 2px;
|
||||||
|
background-color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.TreeDropdownField div.tree_holder {
|
||||||
|
clear: left;
|
||||||
|
cursor: default;
|
||||||
|
border: 1px black solid;
|
||||||
|
margin: 0;
|
||||||
|
height: 200px;
|
||||||
|
overflow: auto;
|
||||||
|
background-color: white;
|
||||||
|
/**
|
||||||
|
* HACK IE6, see http://www.hedgerwow.com/360/bugs/css-select-free.html
|
||||||
|
*/
|
||||||
|
position:absolute;
|
||||||
|
z-index:10;
|
||||||
|
width:33em;/*must have for any value*/;
|
||||||
|
}
|
||||||
|
|
||||||
|
html>body div.TreeDropdownField div.tree_holder {
|
||||||
|
top: 20px;
|
||||||
|
left: 0px;
|
||||||
|
z-index: 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HACK IE6, see http://www.hedgerwow.com/360/bugs/css-select-free.html
|
||||||
|
*/
|
||||||
|
div.TreeDropdownField div.tree_holder iframe {
|
||||||
|
display:none;/* IE5*/
|
||||||
|
display/**/:block;/* IE5*/
|
||||||
|
position:absolute;
|
||||||
|
top:0;
|
||||||
|
left:0;
|
||||||
|
z-index:-1;
|
||||||
|
filter:mask();
|
||||||
|
width:31em;/*must have for any big value*/
|
||||||
|
height:200px/*must have for any big value*/;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.TreeDropdownField a.editLink {
|
||||||
|
border-width: 1px 1px 1px 0;
|
||||||
|
background: url(../../sapphire/images/TreeDropdownField_button.gif) left top no-repeat;
|
||||||
|
width: 19px;
|
||||||
|
height: 21px;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
float: left;
|
||||||
|
clear: right;
|
||||||
|
z-index: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.autocomplete {
|
||||||
|
background-color: #EEEEEE;
|
||||||
|
margin : 0px;
|
||||||
|
padding : 0px;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
.autocomplete ul {
|
||||||
|
border: 1px #AAA solid;
|
||||||
|
margin : 0px;
|
||||||
|
padding : 0px;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
.autocomplete li {
|
||||||
|
list-style-type: none;
|
||||||
|
cursor: pointer;
|
||||||
|
margin: 0px;
|
||||||
|
font-size : 12px;
|
||||||
|
padding : 3px;
|
||||||
|
white-space : nowrap;
|
||||||
|
}
|
||||||
|
.autocomplete .data {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.autocomplete .informal {
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Status
|
||||||
|
*/
|
||||||
|
|
||||||
|
#statusMessage {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 500;
|
||||||
|
bottom: 15px;
|
||||||
|
right: 30px;
|
||||||
|
text-align: left;
|
||||||
|
|
||||||
|
padding: 15px 15px 15px 40px;
|
||||||
|
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: bold;
|
||||||
|
|
||||||
|
border: 1px solid #cc9;
|
||||||
|
color: #660;
|
||||||
|
background-color: #F9F9E3;
|
||||||
|
}
|
||||||
|
|
||||||
|
#statusMessage.good {
|
||||||
|
border-color: #9c9;
|
||||||
|
color: #060;
|
||||||
|
background: url(../images/alert-good.gif) #E2F9E3 7px no-repeat;
|
||||||
|
}
|
||||||
|
#statusMessage.bad {
|
||||||
|
border-color: #c99;
|
||||||
|
color: #fff;
|
||||||
|
background: url(../images/alert-bad.gif) #c00 7px no-repeat;
|
||||||
|
max-height: 600px;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
.pageStatusMessage {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
background-color: #F9F9E3;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 1.2em;
|
||||||
|
padding: 5px;
|
||||||
|
border: 1px solid #cc9;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#right ins * {
|
||||||
|
background-color: green;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#right del *{
|
||||||
|
background-color: red;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
div#ImportFile iframe {
|
||||||
|
width: 80%;
|
||||||
|
height: auto;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.ReportField {
|
||||||
|
position: relative;
|
||||||
|
border: solid 1px #82C0FF;
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.ReportField thead th {
|
||||||
|
background-color: #82C0FF;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.ReportField td, table.ReportField {
|
||||||
|
border: solid 1px #82C0FF;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.ReportField tr {
|
||||||
|
border-bottom: solid 1px #82C0FF;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.ProgressBar {
|
||||||
|
text-align: center;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.ProgressBar p.text {
|
||||||
|
text-align: center;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.ProgressBar div.BarHolder {
|
||||||
|
width: 100%;
|
||||||
|
border: solid 1px #777;
|
||||||
|
background-color: #FFF;
|
||||||
|
padding: 2px;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.ProgressBar div.BarHolder div.Bar {
|
||||||
|
width: 0%;
|
||||||
|
background-color: #0074C6;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.MemberListFilter,
|
||||||
|
div.MemberFilter {
|
||||||
|
/*float: right;*/
|
||||||
|
width: 40em;
|
||||||
|
margin: 5px 0;
|
||||||
|
padding: 5px;
|
||||||
|
background-color : #ebeadb;
|
||||||
|
border: 1px solid #aca899;
|
||||||
|
}
|
||||||
|
|
||||||
|
.right form div.MemberListFilter label,
|
||||||
|
.right form div.MemberFilter label {
|
||||||
|
display: block;
|
||||||
|
width: 10em;
|
||||||
|
float: left;
|
||||||
|
margin-left: 0em;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.MemberListFilter div.field,
|
||||||
|
div.MemberFilter div.field {
|
||||||
|
margin-left: 0em;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.MemberListFilter input,
|
||||||
|
div.MemberListFilter select,
|
||||||
|
div.MemberFilter input,
|
||||||
|
div.MemberFilter select {
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
#right iframe.AWStatsReport {
|
||||||
|
width: 98%;
|
||||||
|
height: 85%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#sitetree a.contents {
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SecurityAdmin
|
||||||
|
*/
|
||||||
|
#Form_EditForm_Permissions .Arg,
|
||||||
|
#Form_EditForm_Permissions .Enabled {
|
||||||
|
width: 6em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#Form_EditForm_Permissions .Arg input {
|
||||||
|
text-align: right;
|
||||||
|
}
|
18
css/dialog.css
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
p.message {
|
||||||
|
/*background: url(../cms/images/dialogs/alert.gif) repeat top left;*/
|
||||||
|
padding-left: 60px;
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
float: left;
|
||||||
|
margin-left: -60px;
|
||||||
|
}
|
||||||
|
|
||||||
|
span {
|
||||||
|
clear: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: Arial, sans-serif;
|
||||||
|
font-size: 1em;
|
||||||
|
}
|
433
css/layout.css
Normal file
@ -0,0 +1,433 @@
|
|||||||
|
/*
|
||||||
|
Tenative color scheme:
|
||||||
|
#0074C6 #95B2C6 #639DC6 #162F42 #2C5F84
|
||||||
|
4A7695 819BAD
|
||||||
|
*/
|
||||||
|
|
||||||
|
html {
|
||||||
|
margin: 0;
|
||||||
|
height: 100%;
|
||||||
|
overflow:hidden;
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
height: 100%;
|
||||||
|
margin: 0;
|
||||||
|
font-size: 62.5%;
|
||||||
|
background-color: #d4d0c8;
|
||||||
|
}
|
||||||
|
|
||||||
|
#top, #left, #bottom, div.title, div.mceToolbarExternal, ul.tabstrip, div.field label, #separator {
|
||||||
|
|
||||||
|
/*-moz-user-select: none;*/ /*need to explicitly allow input box selection becuase of this */
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
form fieldset {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
border-style: none;
|
||||||
|
}
|
||||||
|
form#Form_EditForm fieldset {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
body.stillLoading select {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/** 3-PANEL LAYOUT **/
|
||||||
|
#top {
|
||||||
|
font-size: 1.4em;
|
||||||
|
height: 51px;
|
||||||
|
padding: 3px;
|
||||||
|
background: url(../images/mainmenu/top-bg.gif) top left repeat-x;
|
||||||
|
color: #FFF;
|
||||||
|
border-bottom: 3px solid #d4d0c8;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
#left {
|
||||||
|
position: absolute;
|
||||||
|
top: 51px;
|
||||||
|
left: 3px;
|
||||||
|
width: 205px;
|
||||||
|
height: 92%;
|
||||||
|
z-index: 50;
|
||||||
|
border : 1px #808080 solid;
|
||||||
|
background-color:#FFFFFF;
|
||||||
|
}
|
||||||
|
#right, #rightbottom {
|
||||||
|
position: absolute;
|
||||||
|
left: 208px;
|
||||||
|
top: 51px;
|
||||||
|
height: 92%;
|
||||||
|
width: 600px;
|
||||||
|
overflow: auto;
|
||||||
|
z-index: 50;
|
||||||
|
border: 1px #808080 solid;
|
||||||
|
background-color: white;
|
||||||
|
margin-right : 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#rightbottom {
|
||||||
|
height: 30%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#separator {
|
||||||
|
position: absolute;
|
||||||
|
top: 51px;
|
||||||
|
left: 205px;
|
||||||
|
height: 92%;
|
||||||
|
width: 3px;
|
||||||
|
cursor: e-resize;
|
||||||
|
border-left : 1px solid #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hidden left-hand panel
|
||||||
|
*/
|
||||||
|
#left.hidden form, #left.hidden .title, #left.hidden #TreeActions {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
#left.hidden #SideTabs {
|
||||||
|
margin-top: 47px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#left.hidden {
|
||||||
|
width: 18px;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
#separator.hidden {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#left div.title, #right div.title, #rightbottom div.title{
|
||||||
|
border-top: 1px #77BBEE solid;
|
||||||
|
background-image:url(../images/textures/obar.gif);
|
||||||
|
height : 22px !important;
|
||||||
|
background-color : #0075C9;
|
||||||
|
background-repeat : repeat-x !important;
|
||||||
|
background-position: 0px 0px;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#rightbottom div.light {
|
||||||
|
|
||||||
|
background-image: url(../images/textures/obar-light.png) !important;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#left div.title div, #right div.title div, #rightbottom div.title div{
|
||||||
|
font-size : 14px;
|
||||||
|
font-weight : bold;
|
||||||
|
color: white;
|
||||||
|
padding: 2px 0 0 4px;
|
||||||
|
background-position: 2px 2px;
|
||||||
|
background-repeat:no-repeat;
|
||||||
|
padding-left : 20px;
|
||||||
|
border-top: 1px #77BBEE solid;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#left h2 {
|
||||||
|
margin: 0;
|
||||||
|
background-image:url(../images/textures/obar-18.gif);
|
||||||
|
height: 18px;
|
||||||
|
color: white;
|
||||||
|
font-size: 12px;
|
||||||
|
padding-left: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/** TOP PANEL **/
|
||||||
|
#top #MainMenu {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
#top #MainMenu li {
|
||||||
|
margin: 8px 8px 0 8px;
|
||||||
|
padding: 6px 8px 0 28px;
|
||||||
|
list-style-type: none;
|
||||||
|
float: left;
|
||||||
|
height: 32px;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
html>body #top #MainMenu li {
|
||||||
|
height: 28px; /* 32 - 6 from the padding */
|
||||||
|
}
|
||||||
|
|
||||||
|
#top #MainMenu a {
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
#top #MainMenu .current {
|
||||||
|
background-color: #d4d0c8;
|
||||||
|
background-position: 5px 5px;
|
||||||
|
position: relative;
|
||||||
|
top : -7px;
|
||||||
|
height : 31px;
|
||||||
|
padding-top : 12px;
|
||||||
|
padding-left : 30px;
|
||||||
|
-moz-border-radius-topright : 8px;
|
||||||
|
-moz-border-radius-topleft : 8px;
|
||||||
|
border : 1px solid #65686e;
|
||||||
|
border-bottom : none;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#top #MainMenu .current A:link,#top #MainMenu .current A:visited {
|
||||||
|
|
||||||
|
color : black;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#top #MainMenu #Menu-content { background-image: url(../images/mainmenu/content.gif); }
|
||||||
|
#top #MainMenu #Menu-security { background-image: url(../images/mainmenu/members.gif); }
|
||||||
|
#top #MainMenu #Menu-report { background-image: url(../images/mainmenu/reports.gif); }
|
||||||
|
#top #MainMenu #Menu-newsletter { background-image: url(../images/mainmenu/emails.gif); }
|
||||||
|
#top #MainMenu #Menu-files { background-image: url(../images/mainmenu/files.gif); }
|
||||||
|
|
||||||
|
#top #MainMenu #Menu-help { background-image: url(../images/mainmenu/help.gif);
|
||||||
|
float: right;
|
||||||
|
/*padding: 6px 28px 0 8px;
|
||||||
|
background-position: 100% 0;*/
|
||||||
|
margin-right: 60px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#top #Logo {
|
||||||
|
float: right;
|
||||||
|
margin: 0;
|
||||||
|
padding: 16px 60px 16px 0;
|
||||||
|
height: 32px;
|
||||||
|
font-size: 16px;
|
||||||
|
background: url(../images/mainmenu/logo.gif) right top no-repeat;
|
||||||
|
}
|
||||||
|
html>body #top #Logo {
|
||||||
|
padding-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#top #Logo a {
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#bottom {
|
||||||
|
width: 100%;
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
border-top: 3px solid #d4d0c8;
|
||||||
|
background-color:#81858d;
|
||||||
|
height: 18px;
|
||||||
|
overflow:hidden;
|
||||||
|
background-image:url(../images/textures/bottom.png);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#bottom .holder {
|
||||||
|
|
||||||
|
text-align: center;
|
||||||
|
padding-top : 3px;
|
||||||
|
padding-left : 3px;
|
||||||
|
padding-right : 6px;
|
||||||
|
font-size: 10px;
|
||||||
|
color: white;
|
||||||
|
border-top: 1px solid #555555;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#bottom #logInStatus {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
#bottom #switchView {
|
||||||
|
float: left;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#bottom .bottomTabs a {
|
||||||
|
display: block;
|
||||||
|
float : left;
|
||||||
|
height : 13px;
|
||||||
|
padding-left : 12px;
|
||||||
|
padding-right : 12px;
|
||||||
|
position:relative;
|
||||||
|
top : -3px;
|
||||||
|
border : 1px solid #65686e;
|
||||||
|
border-top : none;
|
||||||
|
cursor:pointer;
|
||||||
|
background-color: #cdc9c1;
|
||||||
|
color : #333333;
|
||||||
|
-moz-border-radius-bottomright : 4px;
|
||||||
|
-moz-border-radius-bottomleft : 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#bottom .bottomTabs div.blank {
|
||||||
|
display: block;
|
||||||
|
float : left;
|
||||||
|
height : 13px;
|
||||||
|
padding-left : 12px;
|
||||||
|
padding-right : 12px;
|
||||||
|
position:relative;
|
||||||
|
top : -3px;
|
||||||
|
border : 1px solid #65686e;
|
||||||
|
border-top : none;
|
||||||
|
cursor:pointer;
|
||||||
|
background-color: #cdc9c1;
|
||||||
|
color : #333333;
|
||||||
|
|
||||||
|
border : none;
|
||||||
|
background-color: transparent;
|
||||||
|
padding-right: 2px;
|
||||||
|
padding-left: 2px;
|
||||||
|
padding-top : 2px;
|
||||||
|
color:#FFFFFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#bottom .bottomTabs a.current {
|
||||||
|
background-color : #d4d0c8;
|
||||||
|
padding-top : 1px;
|
||||||
|
top : -5px;
|
||||||
|
height : 15px;
|
||||||
|
font-weight:bold;
|
||||||
|
font-size : 11px;
|
||||||
|
border : 1px solid #555555;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** LEFT PANEL **/
|
||||||
|
|
||||||
|
#sitetree_holder {
|
||||||
|
height: 80%;
|
||||||
|
width: 100%;
|
||||||
|
overflow-y: auto;
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
#sitetree {
|
||||||
|
width: 500px; /* IE's chocking right now */
|
||||||
|
}
|
||||||
|
html> #sitetree {
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.spacer, li.spacer {
|
||||||
|
list-style-type: none;
|
||||||
|
float: none;
|
||||||
|
clear: both;
|
||||||
|
background-color: transparent;
|
||||||
|
border-style: none;
|
||||||
|
padding: 0;
|
||||||
|
margin: -1px 0 0 0;
|
||||||
|
height: 1px;
|
||||||
|
font-size: 1px;
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
/** RIGHT PANEL **/
|
||||||
|
|
||||||
|
.mceToolbarExternal {
|
||||||
|
background-color: #EEE;
|
||||||
|
border-bottom: 1px #CCC solid;
|
||||||
|
/*position: absolute;
|
||||||
|
top: 23px;
|
||||||
|
left: 0;*/
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#right form#Form_EditorToolbarLinkForm,
|
||||||
|
#right form#Form_EditorToolbarImageForm,
|
||||||
|
#right form#Form_EditorToolbarFlashForm {
|
||||||
|
background-color: #EEE;
|
||||||
|
border-bottom: 1px #CCC solid;
|
||||||
|
display: none;
|
||||||
|
margin: 1px 0 0 0;
|
||||||
|
padding: 5px;
|
||||||
|
height: 115px;
|
||||||
|
/*
|
||||||
|
* HACK IE (all versions): container needs to be higher stacking order
|
||||||
|
* than any DOM-elemnt under it.
|
||||||
|
* @see http://www.aplus.co.yu/lab/z-pos/
|
||||||
|
*/
|
||||||
|
z-index: 1001;
|
||||||
|
}
|
||||||
|
|
||||||
|
#right form#Form_EditorToolbarImageForm {
|
||||||
|
height: 160px;
|
||||||
|
}
|
||||||
|
#right form#Form_EditorToolbarFlashForm {
|
||||||
|
height: 130px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#right form#Form_EditorToolbarLinkForm ul.optionset {
|
||||||
|
height: 1em;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
#right form#Form_EditorToolbarLinkForm ul.optionset li {
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.thumbnailstrip {
|
||||||
|
height: 58px;
|
||||||
|
overflow: auto;
|
||||||
|
white-space: nowrap;
|
||||||
|
width: 100%;
|
||||||
|
float: left;
|
||||||
|
border : 1px solid #AAAAAA;
|
||||||
|
}
|
||||||
|
|
||||||
|
.thumbnailstrip ul {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.thumbnailstrip li {
|
||||||
|
float: left;
|
||||||
|
margin-right: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.indicator.inline {
|
||||||
|
display: inline;
|
||||||
|
margin-left: 5px;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
p.Actions {
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.indicator.block {
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* CMS specific icons for the tree */
|
||||||
|
|
||||||
|
|
||||||
|
ul.tree li.Root span.Root span.c a {
|
||||||
|
|
||||||
|
background-image : url(../../cms/images/treeicons/root.png) !important;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
49
css/typography.css
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
body {
|
||||||
|
font-size: 62.5%;
|
||||||
|
}
|
||||||
|
* {
|
||||||
|
font-family: Arial;
|
||||||
|
font-size: 1em;
|
||||||
|
|
||||||
|
}
|
||||||
|
p, ul, td, th {
|
||||||
|
font-size: 1.2em;
|
||||||
|
}
|
||||||
|
/* overrides (to avoid larger font-sizing) */
|
||||||
|
ul ul,
|
||||||
|
th p,
|
||||||
|
td p,
|
||||||
|
div ul.optionset {
|
||||||
|
font-size: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul ul {
|
||||||
|
font-size: 1em;
|
||||||
|
}
|
||||||
|
h1 {
|
||||||
|
font-size: 2em;
|
||||||
|
color: #0074C6;
|
||||||
|
}
|
||||||
|
h2 {
|
||||||
|
font-size: 1.8em;
|
||||||
|
color: #0074C6;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: #162F42;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
a:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
a img {
|
||||||
|
border-style: none;
|
||||||
|
}
|
||||||
|
#Header a, #ProductGroups a, #Products a {
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#Header h1 {
|
||||||
|
color: white;
|
||||||
|
}
|
BIN
images/add.gif
Normal file
After Width: | Height: | Size: 1004 B |
BIN
images/alert-bad.gif
Executable file
After Width: | Height: | Size: 1.3 KiB |
BIN
images/alert-good.gif
Executable file
After Width: | Height: | Size: 1.2 KiB |
BIN
images/block.png
Executable file
After Width: | Height: | Size: 3.5 KiB |
BIN
images/bullet_arrow_down.png
Executable file
After Width: | Height: | Size: 201 B |
BIN
images/bullet_arrow_up.png
Executable file
After Width: | Height: | Size: 201 B |
BIN
images/button-bg.gif
Executable file
After Width: | Height: | Size: 176 B |
BIN
images/button-left.gif
Executable file
After Width: | Height: | Size: 334 B |
BIN
images/button-right.gif
Executable file
After Width: | Height: | Size: 555 B |
BIN
images/check.png
Executable file
After Width: | Height: | Size: 3.5 KiB |
BIN
images/delete-small.gif
Executable file
After Width: | Height: | Size: 181 B |
BIN
images/delete.gif
Normal file
After Width: | Height: | Size: 356 B |
BIN
images/dialogs/alert.gif
Normal file
After Width: | Height: | Size: 1.9 KiB |
BIN
images/dialogs/alert.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
images/down.gif
Normal file
After Width: | Height: | Size: 570 B |
BIN
images/edit.gif
Executable file
After Width: | Height: | Size: 1.0 KiB |
BIN
images/loading.gif
Executable file
After Width: | Height: | Size: 6.8 KiB |
BIN
images/locked.gif
Executable file
After Width: | Height: | Size: 973 B |
BIN
images/mainmenu/content.gif
Executable file
After Width: | Height: | Size: 790 B |
BIN
images/mainmenu/content.png
Executable file
After Width: | Height: | Size: 3.6 KiB |
BIN
images/mainmenu/emails.gif
Executable file
After Width: | Height: | Size: 807 B |
BIN
images/mainmenu/files.gif
Executable file
After Width: | Height: | Size: 899 B |
BIN
images/mainmenu/help.gif
Executable file
After Width: | Height: | Size: 809 B |
BIN
images/mainmenu/help.png
Executable file
After Width: | Height: | Size: 3.7 KiB |
BIN
images/mainmenu/logo-smallwhite.png
Executable file
After Width: | Height: | Size: 3.1 KiB |
BIN
images/mainmenu/logo.gif
Executable file
After Width: | Height: | Size: 231 B |
BIN
images/mainmenu/logo.png
Executable file
After Width: | Height: | Size: 6.4 KiB |
BIN
images/mainmenu/members.gif
Executable file
After Width: | Height: | Size: 901 B |
BIN
images/mainmenu/members.png
Executable file
After Width: | Height: | Size: 4.0 KiB |
BIN
images/mainmenu/mgmt.png
Executable file
After Width: | Height: | Size: 3.8 KiB |
BIN
images/mainmenu/reports.gif
Executable file
After Width: | Height: | Size: 873 B |
BIN
images/mainmenu/top-bg.gif
Executable file
After Width: | Height: | Size: 156 B |
BIN
images/network-save-bw.gif
Executable file
After Width: | Height: | Size: 623 B |
BIN
images/network-save.gif
Normal file
After Width: | Height: | Size: 1.8 KiB |
BIN
images/network-saveOLD.gif
Executable file
After Width: | Height: | Size: 913 B |
BIN
images/pagination/record-export.png
Executable file
After Width: | Height: | Size: 817 B |
BIN
images/pagination/record-first-g.png
Executable file
After Width: | Height: | Size: 671 B |
BIN
images/pagination/record-first.png
Executable file
After Width: | Height: | Size: 769 B |
BIN
images/pagination/record-last-g.png
Executable file
After Width: | Height: | Size: 587 B |
BIN
images/pagination/record-last.png
Executable file
After Width: | Height: | Size: 696 B |
BIN
images/pagination/record-next-g.png
Executable file
After Width: | Height: | Size: 521 B |
BIN
images/pagination/record-next.png
Executable file
After Width: | Height: | Size: 606 B |
BIN
images/pagination/record-prev-g.png
Executable file
After Width: | Height: | Size: 528 B |
BIN
images/pagination/record-prev.png
Executable file
After Width: | Height: | Size: 575 B |
BIN
images/pagination/record-print.png
Executable file
After Width: | Height: | Size: 818 B |
BIN
images/panels/EditPage.png
Executable file
After Width: | Height: | Size: 537 B |
BIN
images/panels/MySite.png
Executable file
After Width: | Height: | Size: 1.1 KiB |
BIN
images/right.gif
Normal file
After Width: | Height: | Size: 574 B |
BIN
images/show.png
Executable file
After Width: | Height: | Size: 693 B |
BIN
images/sidetabs/advertisements.gif
Normal file
After Width: | Height: | Size: 685 B |
BIN
images/sidetabs/advertisements_over.gif
Normal file
After Width: | Height: | Size: 924 B |
BIN
images/sidetabs/associations.gif
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
images/sidetabs/associations_over.gif
Normal file
After Width: | Height: | Size: 704 B |
BIN
images/sidetabs/categories.gif
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
images/sidetabs/categories_over.gif
Normal file
After Width: | Height: | Size: 990 B |
BIN
images/sidetabs/comments.gif
Executable file
After Width: | Height: | Size: 712 B |
BIN
images/sidetabs/comments_over.gif
Executable file
After Width: | Height: | Size: 698 B |
BIN
images/sidetabs/reports.gif
Executable file
After Width: | Height: | Size: 693 B |
BIN
images/sidetabs/reports_over.gif
Executable file
After Width: | Height: | Size: 693 B |
BIN
images/sidetabs/search.gif
Executable file
After Width: | Height: | Size: 658 B |
BIN
images/sidetabs/search_over.gif
Executable file
After Width: | Height: | Size: 682 B |
BIN
images/sidetabs/sitemap.gif
Executable file
After Width: | Height: | Size: 696 B |
BIN
images/sidetabs/sitemap_over.gif
Executable file
After Width: | Height: | Size: 714 B |
BIN
images/sidetabs/tasklist.gif
Executable file
After Width: | Height: | Size: 682 B |
BIN
images/sidetabs/tasklist_over.gif
Executable file
After Width: | Height: | Size: 691 B |
BIN
images/sidetabs/template normal.psd
Executable file
BIN
images/sidetabs/template over.psd
Executable file
BIN
images/sidetabs/versions.gif
Executable file
After Width: | Height: | Size: 666 B |
BIN
images/sidetabs/versions_over.gif
Executable file
After Width: | Height: | Size: 684 B |
BIN
images/sidetabs/waitingon.gif
Executable file
After Width: | Height: | Size: 720 B |
BIN
images/sidetabs/waitingon_over.gif
Executable file
After Width: | Height: | Size: 738 B |
BIN
images/tables/checkbox.png
Executable file
After Width: | Height: | Size: 148 B |
BIN
images/tables/thead.png
Executable file
After Width: | Height: | Size: 154 B |
BIN
images/textures/ToolBar.png
Executable file
After Width: | Height: | Size: 176 B |
BIN
images/textures/bottom.png
Executable file
After Width: | Height: | Size: 202 B |
BIN
images/textures/obar-18.gif
Executable file
After Width: | Height: | Size: 88 B |
BIN
images/textures/obar-light.png
Executable file
After Width: | Height: | Size: 214 B |
BIN
images/textures/obar.gif
Executable file
After Width: | Height: | Size: 99 B |
BIN
images/textures/seperator.png
Executable file
After Width: | Height: | Size: 148 B |
BIN
images/tickbox-canttick.gif
Executable file
After Width: | Height: | Size: 86 B |
BIN
images/tickbox-greyticked.gif
Executable file
After Width: | Height: | Size: 76 B |
BIN
images/tickbox-ticked.gif
Normal file
After Width: | Height: | Size: 70 B |
BIN
images/tickbox-unticked.gif
Normal file
After Width: | Height: | Size: 70 B |
BIN
images/treeicons/book-closedfolder.gif
Executable file
After Width: | Height: | Size: 147 B |
BIN
images/treeicons/book-file.gif
Executable file
After Width: | Height: | Size: 147 B |
BIN
images/treeicons/book-openfolder.gif
Executable file
After Width: | Height: | Size: 160 B |
BIN
images/treeicons/brokenlink-closedfolder.gif
Executable file
After Width: | Height: | Size: 266 B |
BIN
images/treeicons/brokenlink-file.gif
Executable file
After Width: | Height: | Size: 272 B |
BIN
images/treeicons/brokenlink-openfolder.gif
Executable file
After Width: | Height: | Size: 262 B |
BIN
images/treeicons/draft-file.png
Executable file
After Width: | Height: | Size: 3.0 KiB |
BIN
images/treeicons/draft-folder.png
Executable file
After Width: | Height: | Size: 563 B |
BIN
images/treeicons/element-closedfolder.gif
Executable file
After Width: | Height: | Size: 461 B |
BIN
images/treeicons/element-file.gif
Executable file
After Width: | Height: | Size: 918 B |
BIN
images/treeicons/element-openfolder.gif
Executable file
After Width: | Height: | Size: 444 B |
BIN
images/treeicons/folder-closedfolder.gif
Executable file
After Width: | Height: | Size: 210 B |
BIN
images/treeicons/folder-file.gif
Executable file
After Width: | Height: | Size: 210 B |
BIN
images/treeicons/folder-openfolder.gif
Executable file
After Width: | Height: | Size: 219 B |
BIN
images/treeicons/home-file (1).png
Executable file
After Width: | Height: | Size: 611 B |
BIN
images/treeicons/home-file.png
Executable file
After Width: | Height: | Size: 791 B |
BIN
images/treeicons/multi-user.gif
Executable file
After Width: | Height: | Size: 554 B |
BIN
images/treeicons/multi-user.png
Executable file
After Width: | Height: | Size: 611 B |
BIN
images/treeicons/page-gold-closedfolder.gif
Executable file
After Width: | Height: | Size: 210 B |
BIN
images/treeicons/page-gold-file.gif
Executable file
After Width: | Height: | Size: 211 B |
BIN
images/treeicons/page-gold-openfolder.gif
Executable file
After Width: | Height: | Size: 219 B |
BIN
images/treeicons/page-shortcut-file.gif
Executable file
After Width: | Height: | Size: 271 B |
BIN
images/treeicons/page-shortcut-gold-file.gif
Executable file
After Width: | Height: | Size: 2.9 KiB |
BIN
images/treeicons/preferences-closedfolder.gif
Executable file
After Width: | Height: | Size: 798 B |
BIN
images/treeicons/preferences-file.gif
Executable file
After Width: | Height: | Size: 798 B |
BIN
images/treeicons/preferences-openfolder.gif
Executable file
After Width: | Height: | Size: 798 B |
BIN
images/treeicons/reports-file.png
Executable file
After Width: | Height: | Size: 475 B |
BIN
images/treeicons/reports-foldericon.png
Executable file
After Width: | Height: | Size: 445 B |
BIN
images/treeicons/reports-openfoldericon.png
Executable file
After Width: | Height: | Size: 461 B |
BIN
images/treeicons/root.png
Executable file
After Width: | Height: | Size: 336 B |
BIN
images/treeicons/sent-file.gif
Executable file
After Width: | Height: | Size: 338 B |
BIN
images/treeicons/sent-folder.png
Executable file
After Width: | Height: | Size: 488 B |
BIN
images/treeicons/task-file.gif
Executable file
After Width: | Height: | Size: 487 B |
BIN
images/treeicons/user-file.gif
Executable file
After Width: | Height: | Size: 774 B |
BIN
images/unlocked.gif
Normal file
After Width: | Height: | Size: 936 B |
BIN
images/unlockedled.gif
Normal file
After Width: | Height: | Size: 936 B |
BIN
images/workflow/note_edit.png
Executable file
After Width: | Height: | Size: 3.5 KiB |
BIN
images/workflow/note_new.png
Executable file
After Width: | Height: | Size: 3.7 KiB |
BIN
images/workflow/note_view.png
Executable file
After Width: | Height: | Size: 3.8 KiB |
BIN
images/workflow/rubberstamp.png
Executable file
After Width: | Height: | Size: 3.1 KiB |
280
javascript/AssetAdmin.js
Executable file
@ -0,0 +1,280 @@
|
|||||||
|
/**
|
||||||
|
* Configuration for the left hand tree
|
||||||
|
*/
|
||||||
|
if(typeof SiteTreeHandlers == 'undefined') SiteTreeHandlers = {};
|
||||||
|
SiteTreeHandlers.parentChanged_url = 'admin/assets/ajaxupdateparent';
|
||||||
|
SiteTreeHandlers.orderChanged_url = 'admin/assets/ajaxupdatesort';
|
||||||
|
SiteTreeHandlers.loadPage_url = 'admin/assets/getitem';
|
||||||
|
SiteTreeHandlers.loadTree_url = 'admin/assets/getsubtree';
|
||||||
|
SiteTreeHandlers.showRecord_url = 'admin/assets/show/';
|
||||||
|
var _HANDLER_FORMS = {
|
||||||
|
addpage : 'addpage_options',
|
||||||
|
deletepage : 'deletepage_options',
|
||||||
|
sortitems : 'sortitems_options'
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Top-right actions
|
||||||
|
*/
|
||||||
|
|
||||||
|
function action_upload_right(e) {
|
||||||
|
if(frames['AssetAdmin_upload'].document && frames['AssetAdmin_upload'].document.getElementById('Form_UploadForm')) {
|
||||||
|
// make sure at least one file is selected for upload
|
||||||
|
var values = "";
|
||||||
|
var inputs = $A(frames['AssetAdmin_upload'].document.getElementsByTagName("input"));
|
||||||
|
inputs.each(function(input) {
|
||||||
|
if(input.type == "file") values += input.value;
|
||||||
|
}.bind(this));
|
||||||
|
|
||||||
|
if(values.length == 0) {
|
||||||
|
alert("Please select at least one file for uploading");
|
||||||
|
openTab("Root_Upload");
|
||||||
|
} else {
|
||||||
|
frames['AssetAdmin_upload'].document.getElementById('Form_UploadForm').submit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Event.stop(e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function action_deletemarked_right() {
|
||||||
|
$('action_deletemarked_options').onComplete = function() {}
|
||||||
|
|
||||||
|
if(confirm("Do you really want to delete the marked files?")) {
|
||||||
|
$('action_deletemarked_options').send();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function action_movemarked_right() {
|
||||||
|
$('action_movemarked_options').toggle();
|
||||||
|
}
|
||||||
|
|
||||||
|
MarkingPropertiesForm = Class.extend('ActionPropertiesForm');
|
||||||
|
MarkingPropertiesForm.applyTo('#action_movemarked_options', "Please select some files to move!");
|
||||||
|
MarkingPropertiesForm.applyTo('#action_deletemarked_options', "Please select some files to delete!");
|
||||||
|
|
||||||
|
MarkingPropertiesForm.prototype = {
|
||||||
|
initialize: function(noneCheckedError) {
|
||||||
|
this.noneCheckedError = noneCheckedError;
|
||||||
|
},
|
||||||
|
|
||||||
|
send: function() {
|
||||||
|
var i, list = "", checkboxes = $('Form_EditForm').elements['Files[]'];
|
||||||
|
if(!checkboxes) checkboxes = [];
|
||||||
|
if(!checkboxes.length) checkboxes = [ checkboxes ];
|
||||||
|
for(i=0;i<checkboxes.length;i++) {
|
||||||
|
if(checkboxes[i].checked) list += (list?',':'') + checkboxes[i].value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(this.elements.DestFolderID && !this.elements.DestFolderID.value) {
|
||||||
|
alert("Please select a folder to move files to!");
|
||||||
|
return false;
|
||||||
|
|
||||||
|
} else if(list == "") {
|
||||||
|
alert(this.noneCheckedError);
|
||||||
|
return false;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
this.elements.FileIDs.value = list;
|
||||||
|
return this.ActionPropertiesForm.send();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add File Action
|
||||||
|
*/
|
||||||
|
addfolder = Class.create();
|
||||||
|
addfolder.applyTo('#addpage');
|
||||||
|
addfolder.prototype = {
|
||||||
|
initialize: function () {
|
||||||
|
Observable.applyTo($(this.id + '_options'));
|
||||||
|
this.getElementsByTagName('a')[0].onclick = returnFalse;
|
||||||
|
$(this.id + '_options').onsubmit = this.form_submit;
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
onclick : function() {
|
||||||
|
statusMessage('Creating new folder...');
|
||||||
|
this.form_submit();
|
||||||
|
/*
|
||||||
|
if(treeactions.toggleSelection(this)) {
|
||||||
|
var selectedNode = $('sitetree').firstSelected();
|
||||||
|
|
||||||
|
if(selectedNode) {
|
||||||
|
while(selectedNode.parentTreeNode && !selectedNode.hints.defaultChild) {
|
||||||
|
$('sitetree').changeCurrentTo(selectedNode.parentTreeNode);
|
||||||
|
selectedNode = selectedNode.parentTreeNode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
form_submit : function() {
|
||||||
|
var st = $('sitetree');
|
||||||
|
|
||||||
|
$('addpage_options').elements.ParentID.value = st.getIdxOf(st.firstSelected());
|
||||||
|
Ajax.SubmitForm('addpage_options', null, {
|
||||||
|
onSuccess : Ajax.Evaluator,
|
||||||
|
onFailure : this.showAddPageError
|
||||||
|
});
|
||||||
|
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
showAddPageError: function(response) {
|
||||||
|
errorMessage('Error adding folder', response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete folder action
|
||||||
|
*/
|
||||||
|
deletefolder = {
|
||||||
|
button_onclick : function() {
|
||||||
|
if(treeactions.toggleSelection(this)) {
|
||||||
|
deletefolder.o1 = $('sitetree').observeMethod('SelectionChanged', deletefolder.treeSelectionChanged);
|
||||||
|
deletefolder.o2 = $('deletepage_options').observeMethod('Close', deletefolder.popupClosed);
|
||||||
|
|
||||||
|
addClass($('sitetree'),'multiselect');
|
||||||
|
|
||||||
|
deletefolder.selectedNodes = { };
|
||||||
|
|
||||||
|
var sel = $('sitetree').firstSelected()
|
||||||
|
if(sel) {
|
||||||
|
var selIdx = $('sitetree').getIdxOf(sel);
|
||||||
|
deletefolder.selectedNodes[selIdx] = true;
|
||||||
|
sel.removeNodeClass('current');
|
||||||
|
sel.addNodeClass('selected');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
treeSelectionChanged : function(selectedNode) {
|
||||||
|
var idx = $('sitetree').getIdxOf(selectedNode);
|
||||||
|
|
||||||
|
if(selectedNode.selected) {
|
||||||
|
selectedNode.removeNodeClass('selected');
|
||||||
|
selectedNode.selected = false;
|
||||||
|
deletefolder.selectedNodes[idx] = false;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
selectedNode.addNodeClass('selected');
|
||||||
|
selectedNode.selected = true;
|
||||||
|
deletefolder.selectedNodes[idx] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
popupClosed : function() {
|
||||||
|
removeClass($('sitetree'),'multiselect');
|
||||||
|
$('sitetree').stopObserving(deletefolder.o1);
|
||||||
|
$('deletepage_options').stopObserving(deletefolder.o2);
|
||||||
|
|
||||||
|
for(var idx in deletefolder.selectedNodes) {
|
||||||
|
if(deletefolder.selectedNodes[idx]) {
|
||||||
|
node = $('sitetree').getTreeNodeByIdx(idx);
|
||||||
|
if(node) {
|
||||||
|
node.removeNodeClass('selected');
|
||||||
|
node.selected = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
form_submit : function() {
|
||||||
|
var csvIDs = "";
|
||||||
|
for(var idx in deletefolder.selectedNodes) {
|
||||||
|
var selectedNode = $('sitetree').getTreeNodeByIdx(idx);
|
||||||
|
var link = selectedNode.getElementsByTagName('a')[0];
|
||||||
|
|
||||||
|
if(deletefolder.selectedNodes[idx] && ( !Element.hasClassName( link, 'contents' ) || confirm( "'" + link.firstChild.nodeValue + "' contains files. Would you like to delete the files and folder?" ) ) )
|
||||||
|
csvIDs += (csvIDs ? "," : "") + idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(csvIDs) {
|
||||||
|
$('deletepage_options').elements.csvIDs.value = csvIDs;
|
||||||
|
|
||||||
|
statusMessage('deleting pages');
|
||||||
|
|
||||||
|
Ajax.SubmitForm('deletepage_options', null, {
|
||||||
|
onSuccess : deletefolder.submit_success,
|
||||||
|
onFailure : function(response) {
|
||||||
|
errorMessage('Error deleting pages', response);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$('deletepage').getElementsByTagName('a')[0].onclick();
|
||||||
|
|
||||||
|
} else {
|
||||||
|
alert("Please select at least 1 page.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
submit_success: function(response) {
|
||||||
|
Ajax.Evaluator(response);
|
||||||
|
treeactions.closeSelection($('deletepage'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Behaviour.register({
|
||||||
|
'#Form_EditForm_Files': {
|
||||||
|
removeFile : function(fileID) {
|
||||||
|
var record;
|
||||||
|
if(record = $('record-' + fileID)) {
|
||||||
|
record.parentNode.removeChild(record);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
'#Form_EditForm_Files a.deletelink' : {
|
||||||
|
onclick : function(event) {
|
||||||
|
ajaxLink(this.href);
|
||||||
|
Event.stop(event);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
'#Form_EditForm' : {
|
||||||
|
changeDetection_fieldsToIgnore : {
|
||||||
|
'Files[]' : true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We don't want hitting the enter key in the name field
|
||||||
|
* to submit the form.
|
||||||
|
*/
|
||||||
|
Behaviour.register({
|
||||||
|
'#Form_EditForm_Name' : {
|
||||||
|
onkeypress : function(event) {
|
||||||
|
event = (event) ? event : window.event;
|
||||||
|
var kc = event.keyCode ? event.keyCode : event.charCode;
|
||||||
|
if(kc == 13) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialisation function to set everything up
|
||||||
|
*/
|
||||||
|
appendLoader(function () {
|
||||||
|
// Set up delete page
|
||||||
|
Observable.applyTo($('deletepage_options'));
|
||||||
|
if($('deletepage')) {
|
||||||
|
$('deletepage').onclick = deletefolder.button_onclick;
|
||||||
|
$('deletepage').getElementsByTagName('a')[0].onclick = function() { return false; };
|
||||||
|
$('deletepage_options').onsubmit = deletefolder.form_submit;
|
||||||
|
}
|
||||||
|
});
|
15
javascript/BulkLoaderAdmin.js
Executable file
@ -0,0 +1,15 @@
|
|||||||
|
/**
|
||||||
|
* Configuration for the left hand tree
|
||||||
|
*/
|
||||||
|
if(typeof SiteTreeHandlers == 'undefined') SiteTreeHandlers = {};
|
||||||
|
SiteTreeHandlers.loadPage_url = 'admin/bulkload/getitem';
|
||||||
|
SiteTreeHandlers.showRecord_url = 'admin/bulkload/show/';;
|
||||||
|
|
||||||
|
Behaviour.register({
|
||||||
|
'#Form_EditForm' : {
|
||||||
|
getPageFromServer: function (className) {
|
||||||
|
$('BulkLoaderIframe').src = 'admin/bulkload/iframe/' + className;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
0
javascript/CMSMain.js
Executable file
270
javascript/CMSMain_left.js
Executable file
@ -0,0 +1,270 @@
|
|||||||
|
if(typeof SiteTreeHandlers == 'undefined') SiteTreeHandlers = {};
|
||||||
|
SiteTreeHandlers.parentChanged_url = 'admin/ajaxupdateparent';
|
||||||
|
SiteTreeHandlers.orderChanged_url = 'admin/ajaxupdatesort';
|
||||||
|
SiteTreeHandlers.loadPage_url = 'admin/getitem';
|
||||||
|
SiteTreeHandlers.loadTree_url = 'admin/getsubtree';
|
||||||
|
|
||||||
|
_NEW_PAGES = new Array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add page action
|
||||||
|
*/
|
||||||
|
addpage = Class.create();
|
||||||
|
addpage.applyTo('#addpage');
|
||||||
|
addpage.prototype = {
|
||||||
|
initialize: function () {
|
||||||
|
Observable.applyTo($(_HANDLER_FORMS[this.id]));
|
||||||
|
this.getElementsByTagName('a')[0].onclick = returnFalse;
|
||||||
|
$(_HANDLER_FORMS[this.id]).onsubmit = this.form_submit;
|
||||||
|
},
|
||||||
|
|
||||||
|
onclick : function() {
|
||||||
|
if(treeactions.toggleSelection(this)) {
|
||||||
|
var selectedNode = $('sitetree').firstSelected();
|
||||||
|
if(selectedNode) {
|
||||||
|
while(selectedNode.parentTreeNode && !selectedNode.hints.defaultChild) {
|
||||||
|
$('sitetree').changeCurrentTo(selectedNode.parentTreeNode);
|
||||||
|
selectedNode = selectedNode.parentTreeNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( selectedNode.hints && selectedNode.hints.defaultChild )
|
||||||
|
$(_HANDLER_FORMS.addpage).elements.PageType.value = selectedNode.hints.defaultChild;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.o1 = $('sitetree').observeMethod('SelectionChanged', this.treeSelectionChanged.bind(this));
|
||||||
|
this.o2 = $(_HANDLER_FORMS[this.id]).observeMethod('Close', this.popupClosed.bind(this));
|
||||||
|
|
||||||
|
$(_HANDLER_FORMS[this.id]).elements.PageType.onchange = this.typeDropdown_change;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
treeSelectionChanged : function(selectedNode) {
|
||||||
|
$(_HANDLER_FORMS.addpage).elements.PageType.value = selectedNode.hints.defaultChild;
|
||||||
|
},
|
||||||
|
|
||||||
|
popupClosed : function() {
|
||||||
|
$('sitetree').stopObserving(this.o1);
|
||||||
|
$(_HANDLER_FORMS.addpage).stopObserving(this.o2);
|
||||||
|
},
|
||||||
|
|
||||||
|
typeDropdown_change : function() {
|
||||||
|
// Don't do anything if we're already on an appropriate node
|
||||||
|
var sel = $('sitetree').firstSelected();
|
||||||
|
if(sel && sel.hints && sel.hints.allowedChildren) {
|
||||||
|
var allowed = sel.hints.allowedChildren;
|
||||||
|
for(i=0;i<allowed.length;i++) if(allowed[i] == this.value) return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( typeof siteTreeHints == 'undefined' )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Otherwise move to the default parent for that.
|
||||||
|
if( siteTreeHints && siteTreeHints[this.value] ) {
|
||||||
|
var newNode = $('sitetree').getTreeNodeByIdx(siteTreeHints[this.value].defaultParent);
|
||||||
|
if(newNode) $('sitetree').changeCurrentTo(newNode);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
form_submit : function() {
|
||||||
|
var st = $('sitetree');
|
||||||
|
var parentID = st.getIdxOf(st.firstSelected());
|
||||||
|
if(parentID && parentID.substr(0,3) == 'new') {
|
||||||
|
alert("You have to save a page before adding children underneath it");
|
||||||
|
|
||||||
|
} else if( Element.hasClassName( st.firstSelected(), "nochildren" ) ) {
|
||||||
|
alert("You can't add children to the selected node" );
|
||||||
|
} else {
|
||||||
|
$(_HANDLER_FORMS.addpage).elements.ParentID.value = parentID ? parentID : 0;
|
||||||
|
|
||||||
|
if( !_NEW_PAGES[parentID] )
|
||||||
|
_NEW_PAGES[parentID] = 1;
|
||||||
|
|
||||||
|
var suffix = _NEW_PAGES[parentID]++;
|
||||||
|
Ajax.SubmitForm(_HANDLER_FORMS.addpage, "action_addpage", {
|
||||||
|
onSuccess : Ajax.Evaluator,
|
||||||
|
onFailure : this.showAddPageError,
|
||||||
|
extraData: '&Suffix=' + suffix
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
showAddPageError: function(response) {
|
||||||
|
errorMessage('Error adding page', response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete page action
|
||||||
|
*/
|
||||||
|
deletepage = {
|
||||||
|
button_onclick : function() {
|
||||||
|
if(treeactions.toggleSelection(this)) {
|
||||||
|
deletepage.o1 = $('sitetree').observeMethod('SelectionChanged', deletepage.treeSelectionChanged);
|
||||||
|
deletepage.o2 = $(_HANDLER_FORMS.deletepage).observeMethod('Close', deletepage.popupClosed);
|
||||||
|
addClass($('sitetree'),'multiselect');
|
||||||
|
|
||||||
|
deletepage.selectedNodes = { };
|
||||||
|
|
||||||
|
var sel = $('sitetree').firstSelected();
|
||||||
|
if(sel && sel.className.indexOf('nodelete') == -1) {
|
||||||
|
var selIdx = $('sitetree').getIdxOf(sel);
|
||||||
|
deletepage.selectedNodes[selIdx] = true;
|
||||||
|
sel.removeNodeClass('current');
|
||||||
|
sel.addNodeClass('selected');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
treeSelectionChanged : function(selectedNode) {
|
||||||
|
var idx = $('sitetree').getIdxOf(selectedNode);
|
||||||
|
|
||||||
|
if(selectedNode.className.indexOf('nodelete') == -1) {
|
||||||
|
if(selectedNode.selected) {
|
||||||
|
selectedNode.removeNodeClass('selected');
|
||||||
|
selectedNode.selected = false;
|
||||||
|
deletepage.selectedNodes[idx] = false;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
selectedNode.addNodeClass('selected');
|
||||||
|
selectedNode.selected = true;
|
||||||
|
deletepage.selectedNodes[idx] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
popupClosed : function() {
|
||||||
|
removeClass($('sitetree'),'multiselect');
|
||||||
|
$('sitetree').stopObserving(deletepage.o1);
|
||||||
|
$(_HANDLER_FORMS.deletepage).stopObserving(deletepage.o2);
|
||||||
|
|
||||||
|
for(var idx in deletepage.selectedNodes) {
|
||||||
|
if(deletepage.selectedNodes[idx]) {
|
||||||
|
node = $('sitetree').getTreeNodeByIdx(idx);
|
||||||
|
if(node) {
|
||||||
|
node.removeNodeClass('selected');
|
||||||
|
node.selected = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
form_submit : function() {
|
||||||
|
var csvIDs = "", count = 0;
|
||||||
|
var st = $('sitetree');
|
||||||
|
var newNodes = new Array();
|
||||||
|
|
||||||
|
for(var idx in deletepage.selectedNodes) {
|
||||||
|
if(deletepage.selectedNodes[idx]) {
|
||||||
|
|
||||||
|
// delete new nodes
|
||||||
|
if( idx.match(/^new-[a-z0-9A-Z\-]+$/) ) {
|
||||||
|
newNodes.push( idx );
|
||||||
|
} else {
|
||||||
|
var i, item, childrenToDelete = st.getTreeNodeByIdx(idx).getElementsByTagName('li');
|
||||||
|
for(i=0;item=childrenToDelete[i];i++) {
|
||||||
|
csvIDs += (csvIDs ? "," : "") + st.getIdxOf(childrenToDelete[i]);
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
csvIDs += (csvIDs ? "," : "") + idx;
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(csvIDs || newNodes.length > 0) {
|
||||||
|
count += newNodes.length;
|
||||||
|
|
||||||
|
if(confirm("Do you really want to delete the " + count + " marked pages?")) {
|
||||||
|
$(_HANDLER_FORMS.deletepage).elements.csvIDs.value = csvIDs;
|
||||||
|
|
||||||
|
statusMessage('deleting pages');
|
||||||
|
|
||||||
|
for( var idx = 0; idx < newNodes.length; idx++ ) {
|
||||||
|
var newNode = $('sitetree').getTreeNodeByIdx( newNodes[idx] );
|
||||||
|
|
||||||
|
if( newNode.parentTreeNode )
|
||||||
|
newNode.parentTreeNode.removeTreeNode( newNode );
|
||||||
|
else
|
||||||
|
alert( newNode.id + ' has no parent node');
|
||||||
|
|
||||||
|
$('Form_EditForm').reloadIfSetTo(idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
newNodes = new Array();
|
||||||
|
|
||||||
|
Ajax.SubmitForm(_HANDLER_FORMS.deletepage, null, {
|
||||||
|
onSuccess : deletepage.submit_success,
|
||||||
|
onFailure : function(response) {
|
||||||
|
errorMessage('Error deleting pages', response);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$('deletepage').getElementsByTagName('a')[0].onclick();
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
alert("Please select at least 1 page.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
submit_success: function(response) {
|
||||||
|
deletepage.selectedNodes = {};
|
||||||
|
|
||||||
|
Ajax.Evaluator(response);
|
||||||
|
treeactions.closeSelection($('deletepage'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialisation function to set everything up
|
||||||
|
*/
|
||||||
|
appendLoader(function () {
|
||||||
|
// Set up deleet page
|
||||||
|
if( !$('deletepage') )
|
||||||
|
return;
|
||||||
|
|
||||||
|
Observable.applyTo($(_HANDLER_FORMS.deletepage));
|
||||||
|
$('deletepage').onclick = deletepage.button_onclick;
|
||||||
|
$('deletepage').getElementsByTagName('a')[0].onclick = function() { return false; };
|
||||||
|
$(_HANDLER_FORMS.deletepage).onsubmit = deletepage.form_submit;
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tree context menu
|
||||||
|
*/
|
||||||
|
TreeContextMenu = {
|
||||||
|
'Edit this page' : function(treeNode) {
|
||||||
|
treeNode.selectTreeNode();
|
||||||
|
},
|
||||||
|
'Duplicate this page' : function(treeNode) {
|
||||||
|
new Ajax.Request(baseHref() + 'admin/duplicate/' + treeNode.getIdx() + '?ajax=1', {
|
||||||
|
method : 'get',
|
||||||
|
onSuccess : Ajax.Evaluator,
|
||||||
|
onFailure : function(response) {
|
||||||
|
errorMessage('Error: ', response);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
'Sort sub-pages' : function(treeNode) {
|
||||||
|
var children = treeNode.treeNodeHolder().childTreeNodes();
|
||||||
|
var sortedChildren = children.sort(function(a, b) {
|
||||||
|
var titleA = a.aTag.innerHTML.replace(/<[^>]*>/g,'');
|
||||||
|
var titleB = b.aTag.innerHTML.replace(/<[^>]*>/g,'');
|
||||||
|
return titleA < titleB ? -1 : (titleA > titleB ? 1 : 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
var i,child;
|
||||||
|
for(i=0;child=sortedChildren[i];i++) {
|
||||||
|
treeNode.appendTreeNode(child);
|
||||||
|
}
|
||||||
|
|
||||||
|
treeNode.onOrderChanged(sortedChildren);
|
||||||
|
}
|
||||||
|
};
|
123
javascript/CMSMain_right.js
Executable file
@ -0,0 +1,123 @@
|
|||||||
|
function action_publish_right() {
|
||||||
|
$('Form_EditForm').notify('BeforeSave');
|
||||||
|
Ajax.SubmitForm('Form_EditForm', 'action_publish', {
|
||||||
|
onSuccess : function(response) {
|
||||||
|
Ajax.Evaluator(response);
|
||||||
|
},
|
||||||
|
onFailure : function(response) {
|
||||||
|
errorMessage('Error publishing content', response);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function action_revert_right() {
|
||||||
|
Ajax.SubmitForm('Form_EditForm', 'action_revert', {
|
||||||
|
onSuccess : Ajax.Evaluator,
|
||||||
|
onFailure : function(response) {
|
||||||
|
errorMessage('Error reverting to live content', response);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
/*function action_submit_right() {
|
||||||
|
$('action_submit_options').configure('CanApprove', 'Awaiting editor approval');
|
||||||
|
$('action_submit_options').toggle();
|
||||||
|
}*/
|
||||||
|
function action_reject_right() {
|
||||||
|
$('action_submit_options').configure('CanReject', 'Content declined');
|
||||||
|
$('action_submit_options').toggle();
|
||||||
|
}
|
||||||
|
function action_submit_right() {
|
||||||
|
$('action_submit_options').configure('CanPublish', 'Awaiting final approval');
|
||||||
|
$('action_submit_options').toggle();
|
||||||
|
}
|
||||||
|
function action_rollback_right() {
|
||||||
|
var options = {
|
||||||
|
OK: function() {
|
||||||
|
var pageID = $('Form_EditForm').elements.ID.value;
|
||||||
|
|
||||||
|
Ajax.SubmitForm('Form_EditForm', 'action_rollback', {
|
||||||
|
onSuccess : function(response) {
|
||||||
|
$('Form_EditForm').getPageFromServer(pageID);
|
||||||
|
statusMessage(response.responseText,'good');
|
||||||
|
},
|
||||||
|
onFailure : function(response) {
|
||||||
|
errorMessage('Error rolling back content', response);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
Cancel:function() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
doYouWantToRollback(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Email containing the link to the archived version of the page
|
||||||
|
*/
|
||||||
|
function action_email_right() {
|
||||||
|
window.open( 'mailto:?subject=' + $('Form_EditForm_ArchiveEmailSubject').value + '&body=' + $('Form_EditForm_ArchiveEmailMessage').value, 'archiveemail' );
|
||||||
|
}
|
||||||
|
|
||||||
|
function action_print_right() {
|
||||||
|
var printURL = $('Form_EditForm').action.replace(/\?.*$/,'') + '/printable/' + $('Form_EditForm').elements.ID.value;
|
||||||
|
if(printURL.substr(0,7) != 'http://') printURL = baseHref() + printURL;
|
||||||
|
|
||||||
|
window.open(printURL, 'printable');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Submit_ActionPropertiesForm = Class.extend('ActionPropertiesForm');
|
||||||
|
Submit_ActionPropertiesForm.applyTo('#action_submit_options');
|
||||||
|
Submit_ActionPropertiesForm.prototype = {
|
||||||
|
/**
|
||||||
|
* Define how this form is to be used
|
||||||
|
*/
|
||||||
|
configure : function(securityLevel, status) {
|
||||||
|
this.securityLevel = securityLevel;
|
||||||
|
this.elements.Status.value = status
|
||||||
|
this.elements.Status.parentNode.getElementsByTagName('span')[0].innerHTML= status;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function suggestStageSiteLink() {
|
||||||
|
var el = $('viewStageSite');
|
||||||
|
el.flasher = setInterval(flashColor.bind(el), 300);
|
||||||
|
setTimeout(stopFlashing.bind(el), 3000);
|
||||||
|
}
|
||||||
|
function flashColor() {
|
||||||
|
if(!this.style.color) this.style.color = '';
|
||||||
|
this.style.color = (this.style.color == '') ? '#00FF00' : '';
|
||||||
|
}
|
||||||
|
function stopFlashing() {
|
||||||
|
clearInterval(this.flasher);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Behaviour.register({
|
||||||
|
'a.cmsEditlink' : {
|
||||||
|
onclick : function() {
|
||||||
|
if(this.href.match(/admin\/show\/([0-9]+)($|#|\?)/)) {
|
||||||
|
$('Form_EditForm').getPageFromServer(RegExp.$1);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Behaviour.register({
|
||||||
|
'select#Form_EditForm_ClassName' : {
|
||||||
|
onchange: function() {
|
||||||
|
alert('The page type will be updated after the page is saved');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Behaviour.register({
|
||||||
|
'#Form_EditForm' : {
|
||||||
|
changeDetection_fieldsToIgnore : {
|
||||||
|
'restricted-chars[Form_EditForm_URLSegment]' : true,
|
||||||
|
'Sort' : true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
13
javascript/CommentList.js
Executable file
@ -0,0 +1,13 @@
|
|||||||
|
CommentList = Class.extend('SidePanel');
|
||||||
|
CommentList.prototype = {
|
||||||
|
destroy: function() {
|
||||||
|
if(this.SidePanel) this.SidePanel.destroy();
|
||||||
|
this.SidePanel = null;
|
||||||
|
},
|
||||||
|
onpagechanged : function() {
|
||||||
|
this.body.innerHTML = '<p>loading...</p>';
|
||||||
|
this.ajaxGetPanel(this.afterPanelLoaded);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CommentList.applyTo('#comments_holder');
|
115
javascript/ForumAdmin.js
Executable file
@ -0,0 +1,115 @@
|
|||||||
|
var _GroupID;
|
||||||
|
Behaviour.register({
|
||||||
|
'#Form_EditForm_Type input':{
|
||||||
|
onclick:function(){
|
||||||
|
var requiredlogin = $('Form_EditForm_RequiredLogin');
|
||||||
|
var usersTab = $('Form_EditForm').getElementsByTagName('ul')[0].getElementsByTagName('li')[2];
|
||||||
|
if(this.value == 'consultation'){
|
||||||
|
if(requiredlogin.checked != 'checked')
|
||||||
|
requiredlogin.checked = 'checked';
|
||||||
|
usersTab.style.display = 'block';
|
||||||
|
if(_GroupID)
|
||||||
|
this.recoverGroupID();
|
||||||
|
Element.disable(requiredlogin);
|
||||||
|
}else{ // this.value == 'open'
|
||||||
|
usersTab.style.display = 'none';
|
||||||
|
this.treatGroupIDAs0();
|
||||||
|
Element.enable(requiredlogin);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
treatGroupIDAs0:function(){
|
||||||
|
var groupIDDiv = $('GroupID');
|
||||||
|
var groupIDs = groupIDDiv.getElementsByTagName('option');
|
||||||
|
for(var i=0; i<groupIDs.length; i++){
|
||||||
|
if(groupIDs[i].selected == true){
|
||||||
|
groupIDs[i].selected = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_GroupID = groupIDs[i].value;
|
||||||
|
groupIDs[0].selected = true;
|
||||||
|
},
|
||||||
|
|
||||||
|
recoverGroupID:function(){
|
||||||
|
var groupIDDiv = $('GroupID');
|
||||||
|
var groupIDs = groupIDDiv.getElementsByTagName('option');
|
||||||
|
groupIDs[0].selected = false;
|
||||||
|
for(var i=0; i<groupIDs.length; i++){
|
||||||
|
if(groupIDs[i].value == _GroupID){
|
||||||
|
groupIDs[i].selected = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_GrouipID = null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
'#Root_Users #GroupID select':{
|
||||||
|
onchange:function() {
|
||||||
|
var source = this.getElementsByTagName('option');
|
||||||
|
|
||||||
|
for(var i=0; i<source.length; i++){
|
||||||
|
if(source[i].selected == true){
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var action=getAjaxURL('getMembersByGroup', source[i].value, 'ajax=1');
|
||||||
|
new Ajax.Updater(
|
||||||
|
{success: 'MemberList'},
|
||||||
|
action,
|
||||||
|
{
|
||||||
|
method: 'get',
|
||||||
|
onFailure: function(response) {errorMessage("Error getting data", response);},
|
||||||
|
onComplete: function() {Behaviour.apply($('MemberList'));}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function getAjaxURL(action, param, getvars) {
|
||||||
|
var base = document.getElementsByTagName('base')[0].href;
|
||||||
|
var url = window.location.href.substr(base.length);
|
||||||
|
if(url.match(/^([^?]+)(\?.*)/)){
|
||||||
|
url=RegExp.$1;
|
||||||
|
}
|
||||||
|
if(!url.match(/^([^\/]+\/)$/)){
|
||||||
|
url = url+"/";
|
||||||
|
}
|
||||||
|
|
||||||
|
if(getvars) getvars = "?" + getvars;
|
||||||
|
else getvars = "";
|
||||||
|
return base + url.replace(/^([^\/]+\/).*/, '$1' + action + '/' + param + getvars);
|
||||||
|
}
|
||||||
|
|
||||||
|
Element.disable = function(el){
|
||||||
|
el.disabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Element.enable = function(el){
|
||||||
|
el.disabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
initialiseCMSIfAForumLoaded = function(){
|
||||||
|
if($('Form_EditForm_Type')) {
|
||||||
|
var types=($('Form_EditForm_Type').getElementsByTagName('input'));
|
||||||
|
for(var i=0; i<types.length; i++){
|
||||||
|
|
||||||
|
if(types[i].checked)
|
||||||
|
{
|
||||||
|
if(types[i].onclick) types[i].onclick();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Behaviour.addLoader(function(){
|
||||||
|
if($('Form_EditForm').observeMethod) {
|
||||||
|
$('Form_EditForm').observeMethod('PageLoaded', initialiseCMSIfAForumLoaded());
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
187
javascript/GenericDataAdmin_left.js
Executable file
@ -0,0 +1,187 @@
|
|||||||
|
/**
|
||||||
|
* Manages searching and displaying of datarecords speccified in GenericDataAdmin.
|
||||||
|
*/
|
||||||
|
LeftPane = Class.create();
|
||||||
|
LeftPane.applyTo('#LeftPane');
|
||||||
|
LeftPane.prototype = {
|
||||||
|
|
||||||
|
effectDuration: .7,
|
||||||
|
|
||||||
|
initialize: function() {
|
||||||
|
Behaviour.register({
|
||||||
|
'#Form_CreationForm_action_createRecord': {
|
||||||
|
onclick: this.addNewRecord.bind(this)
|
||||||
|
},
|
||||||
|
|
||||||
|
'#Form_SearchForm_action_getResults': {
|
||||||
|
onclick: this.filterOnChange.bind(this)
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Small hack to keep forms for search and resultActions separate
|
||||||
|
* in order to avoid DOM-id-duplicates.
|
||||||
|
*/
|
||||||
|
'#Form_ExportForm_action_export': {
|
||||||
|
onclick: function(e) {
|
||||||
|
var el = Event.element(e);
|
||||||
|
var origAction = $('Form_SearchForm').action;
|
||||||
|
$('Form_SearchForm').action = origAction + "&" + el.name + "=1";
|
||||||
|
$('Form_SearchForm').submit();
|
||||||
|
$('Form_SearchForm').action = origAction;
|
||||||
|
Event.stop(e);
|
||||||
|
return false;
|
||||||
|
}.bind(this)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.initAdvancedSearch();
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If a toggle-link is present, provide functionality to show/hide additional search-options.
|
||||||
|
*/
|
||||||
|
initAdvancedSearch: function() {
|
||||||
|
var advancedSearch = $('AdvancedSearchFields');
|
||||||
|
if(!advancedSearch) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
advancedSearch.style.display = "none";
|
||||||
|
var toggleLinks = document.getElementsBySelector('div#LeftPane .ToggleAdvancedSearchFields');
|
||||||
|
if(toggleLinks && toggleLinks[0]) toggleLinks[0].style.display = "block";
|
||||||
|
|
||||||
|
Behaviour.register({
|
||||||
|
"div#LeftPane .ToggleAdvancedSearchFields a":{
|
||||||
|
onclick: function(e){
|
||||||
|
var el = Event.element(e);
|
||||||
|
var advancedSearchDiv = $('AdvancedSearchFields');
|
||||||
|
if(advancedSearchDiv.style.display == 'none') {
|
||||||
|
Effect.SlideDown(advancedSearchDiv,{duration:this.effectDuration});
|
||||||
|
el.firstChild.nodeValue = el.firstChild.nodeValue.replace(/Show/,"Hide");
|
||||||
|
|
||||||
|
} else {
|
||||||
|
Effect.SlideUp(advancedSearchDiv,{duration:this.effectDuration});
|
||||||
|
el.firstChild.nodeValue = el.firstChild.nodeValue.replace(/Hide/,"Show");
|
||||||
|
|
||||||
|
}
|
||||||
|
Event.stop(e);
|
||||||
|
return false;
|
||||||
|
}.bind(this)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param form DOM-Element Needed if you invoke this action externally
|
||||||
|
*/
|
||||||
|
addNewRecord: function(e, form) {
|
||||||
|
var el = Event.element(e);
|
||||||
|
var form = (form) ? form : Event.findElement(e,"form");
|
||||||
|
var link = form.action + "&" + Form.serialize(form);
|
||||||
|
|
||||||
|
var openTab = $('Form_EditForm').getCurrentTab();
|
||||||
|
var callback = function() {
|
||||||
|
statusMessage("Record created","good");
|
||||||
|
}
|
||||||
|
$('Form_EditForm').updateCMSContent(el, openTab, link, callback);
|
||||||
|
|
||||||
|
Event.stop(e);
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
displayNewRecord: function(response) {
|
||||||
|
|
||||||
|
$('Form_EditForm').innerHTML = response.responseText;
|
||||||
|
onload_init_tabstrip();
|
||||||
|
// Makes sure content-behaviour is correctly initialized in the main window.
|
||||||
|
Behaviour.apply( $('Form_EditForm') );
|
||||||
|
},
|
||||||
|
|
||||||
|
filterOnChange: function (e, custom_onComplete) {
|
||||||
|
try {
|
||||||
|
// hide existing results
|
||||||
|
$("ResultTable_holder").innerHTML = "";
|
||||||
|
// show loading indicator
|
||||||
|
showIndicator('SearchLoading', $$('#Form_SearchForm p.Actions')[0]);
|
||||||
|
} catch(e) {}
|
||||||
|
|
||||||
|
|
||||||
|
Ajax.SubmitForm(
|
||||||
|
"Form_SearchForm",
|
||||||
|
"action_getResults",
|
||||||
|
{
|
||||||
|
postBody : 'ajax=1',
|
||||||
|
onSuccess : this.updateResult.bind(this),
|
||||||
|
onFailure : function(response) {
|
||||||
|
errorMessage('Error encountered during search', response);
|
||||||
|
},
|
||||||
|
onComplete : custom_onComplete
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
Event.stop(e);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
updateResult: function(response){
|
||||||
|
// hide loading indicator
|
||||||
|
hideIndicator('SearchLoading');
|
||||||
|
|
||||||
|
var holder;
|
||||||
|
if(holder = $('ResultTable_holder')){
|
||||||
|
holder.innerHTML = response.responseText;
|
||||||
|
Behaviour.apply( $('ResultTable_holder') );
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
clearFilter: function() {
|
||||||
|
var searchForm = $('Form_SearchForm');
|
||||||
|
var fields = searchForm.getElementsByTagName('input');
|
||||||
|
for(var i=0; i<fields.length; i++){
|
||||||
|
fields[0].value = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shows results provided by LeftPane.
|
||||||
|
*/
|
||||||
|
ResultTable = Class.create();
|
||||||
|
ResultTable.applyTo('#ResultTable_holder');
|
||||||
|
ResultTable.prototype = {
|
||||||
|
initialize: function() {
|
||||||
|
Behaviour.register({
|
||||||
|
"#LeftPane a.show":{
|
||||||
|
onclick: function(e){
|
||||||
|
var openTab = $('Form_EditForm').getCurrentTab();
|
||||||
|
$('Form_EditForm').updateCMSContent(this, openTab);
|
||||||
|
Event.stop(e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"#ResultTable_holder a":{
|
||||||
|
onclick: function(e){
|
||||||
|
var el = Event.element(e);
|
||||||
|
var link = (el.nodeName == "A") ? el : document.getElementsBySelector("a.show",el)[0];
|
||||||
|
if(link) {
|
||||||
|
var openTab = $('Form_EditForm').getCurrentTab();
|
||||||
|
$('Form_EditForm').updateCMSContent(link, openTab);
|
||||||
|
}
|
||||||
|
Event.stop(e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function fixHeight_left() {
|
||||||
|
fitToParent('LeftPane');
|
||||||
|
fitToParent('Search_holder',12);
|
||||||
|
if($('SideTabs')) {
|
||||||
|
$('SideTabs').resize();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
116
javascript/GenericDataAdmin_right.js
Executable file
@ -0,0 +1,116 @@
|
|||||||
|
/**
|
||||||
|
* Loads, views and removes records in the main view.
|
||||||
|
*/
|
||||||
|
RightContent = Class.create();
|
||||||
|
RightContent.applyTo('#Form_EditForm');
|
||||||
|
RightContent.prototype = {
|
||||||
|
|
||||||
|
welcomeMessage: "<h1>SilverStripe CMS</h1><p>Welcome to SilverStripe CMS! Please choose click on one of the items on the left pane.</p>",
|
||||||
|
|
||||||
|
initialize : function() {
|
||||||
|
Behaviour.register({
|
||||||
|
'#Form_EditForm_action_delete' : {
|
||||||
|
onclick: this.remove.bind(this)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
updateCMSContent: function(el, currentTab, link, customCallBack) {
|
||||||
|
if(!customCallBack) customCallBack = function(){};
|
||||||
|
|
||||||
|
if(el || link){
|
||||||
|
var reqLink = (el.href) ? el.href : link;
|
||||||
|
|
||||||
|
if(typeof(currentTab) != 'undefined')
|
||||||
|
$('Form_EditForm').openTab = currentTab;
|
||||||
|
|
||||||
|
statusMessage("loading...", null, true);
|
||||||
|
|
||||||
|
new Ajax.Request(reqLink, {
|
||||||
|
asynchronous : true,
|
||||||
|
postBody : 'ajax=1',
|
||||||
|
onSuccess: customCallBack.bind(this),
|
||||||
|
onComplete : this.successfullyReceivedPage.bind(this),
|
||||||
|
onFailure : function(response) {
|
||||||
|
errorMessage('Error loading page',response);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}else{
|
||||||
|
$('Form_EditForm').innerHTML = this.welcomeMessage;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
successfullyReceivedPage : function(response) {
|
||||||
|
$('Form_EditForm').loadNewPage(response.responseText);
|
||||||
|
$('Form_EditForm').initialize();
|
||||||
|
onload_init_tabstrip();
|
||||||
|
// TODO
|
||||||
|
// try to reopen saved tab (before ajax request)
|
||||||
|
if($('Form_EditForm').openTab) {
|
||||||
|
openTab($('Form_EditForm').openTab);
|
||||||
|
} else {
|
||||||
|
$('Form_EditForm').openTab = this.getCurrentTab();
|
||||||
|
openTab($('Form_EditForm').openTab);
|
||||||
|
}
|
||||||
|
|
||||||
|
clearStatusMessage();
|
||||||
|
},
|
||||||
|
|
||||||
|
remove: function(e) {
|
||||||
|
if(window.confirm('Are you sure you want to delete?')){
|
||||||
|
var el = Event.element(e);
|
||||||
|
Ajax.SubmitForm(el.ownerForm, el.name, {
|
||||||
|
postBody : 'ajax=1',
|
||||||
|
onSuccess: Ajax.Evaluator,
|
||||||
|
onFailure: ajaxErrorHandler
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Event.stop(e);
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
deleteEffect: function() {
|
||||||
|
new Effect.Fade(this, {duration:2});
|
||||||
|
window.setTimeout(function() {
|
||||||
|
$('Form_EditForm').updateCMSContent();
|
||||||
|
new Effect.Appear($('Form_EditForm'), {duration:3});
|
||||||
|
}, 3000);
|
||||||
|
},
|
||||||
|
|
||||||
|
tabExists: function(tabName) {
|
||||||
|
if($('Root')){
|
||||||
|
var tags = $('Root').getElementsByTagName('li');
|
||||||
|
for(var i=0; i<tags.length; i++){
|
||||||
|
var current = tags[i].getElementsByTagName('a')[0];
|
||||||
|
var match = current.id.match(/tab-(.+)$/);
|
||||||
|
if(match[0] == tabName) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
getCurrentTab: function() {
|
||||||
|
var current;
|
||||||
|
if($('Root')){
|
||||||
|
var tags = $('Root').getElementsByTagName('li');
|
||||||
|
for(var i=0; i<tags.length; i++){
|
||||||
|
if(Element.hasClassName(tags[0], 'current')){
|
||||||
|
current = tags[i].getElementsByTagName('a')[0];
|
||||||
|
}else{
|
||||||
|
current = tags[0].getElementsByTagName('a')[0]
|
||||||
|
}
|
||||||
|
current.id.match(/tab-(.+)$/);
|
||||||
|
return RegExp.$1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
setTitle: function(title) {
|
||||||
|
var titleHolder = $$('div#right .title div');
|
||||||
|
if(titleHolder[0]) {
|
||||||
|
titleHolder[0].innerHTML = title;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
84
javascript/ImprintStats.js
Executable file
@ -0,0 +1,84 @@
|
|||||||
|
|
||||||
|
/********************************************************
|
||||||
|
* Copyright Nexsys Development Ltd 2002 - 2004 *
|
||||||
|
* Version 1.7 *
|
||||||
|
* http://www.nexsysdev.com *
|
||||||
|
********************************************************/
|
||||||
|
|
||||||
|
var NI_SE = "", NI_SC = "", NI_ST = "", NI_SV = "", NI_IW = 0;
|
||||||
|
|
||||||
|
function ni_TrackHit(server, siteCode, description, section, service, trigger, amount, adCampaign, title, url, layer, basketAdd, basketRemove, parameters)
|
||||||
|
{
|
||||||
|
NI_SE = server; NI_SC = siteCode; NI_ST = section; NI_SV = service;
|
||||||
|
|
||||||
|
function CB()
|
||||||
|
{
|
||||||
|
var cb="", key = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
||||||
|
for (i=0;i<5;i++) cb += key.charAt(Math.floor(Math.random()*52));
|
||||||
|
return cb;
|
||||||
|
}
|
||||||
|
|
||||||
|
function A(B,C)
|
||||||
|
{
|
||||||
|
if (typeof(C) != "undefined" && C != "") return "&"+B+"="+escape(C);
|
||||||
|
else return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof(NI_PAGE) != "undefined")
|
||||||
|
{
|
||||||
|
if (url.indexOf("?") > 0) url += "&ni_page=" + NI_PAGE;
|
||||||
|
else url += "?ni_page=" + NI_PAGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
var p = "http"+(document.URL.indexOf('https:')==0?'s':'');
|
||||||
|
var t = new Date();
|
||||||
|
var u = p+"://"+server+"/Hit.aspx?tv=1&sc="+siteCode;
|
||||||
|
u+=A("lo",description);
|
||||||
|
u+=A("du",url);
|
||||||
|
u+=A("st",section);
|
||||||
|
u+=A("sv",service);
|
||||||
|
u+=A("ac",adCampaign);
|
||||||
|
u+=A("tr",trigger);
|
||||||
|
u+=A("ta",amount);
|
||||||
|
u+=A("ti",title);
|
||||||
|
u+=A("tz",t.getTimezoneOffset());
|
||||||
|
u+=A("ch",t.getHours());
|
||||||
|
u+=A("cb",CB());
|
||||||
|
u+=A("ru",window.document.referrer);
|
||||||
|
u+=A("js","1");
|
||||||
|
u+=A("ul",navigator.appName=="Netscape" ? navigator.language : navigator.userLanguage);
|
||||||
|
u+=A("ba", basketAdd);
|
||||||
|
u+=A("br", basketRemove);
|
||||||
|
u+=A("pm", parameters);
|
||||||
|
|
||||||
|
if (typeof(screen)=="object")
|
||||||
|
{
|
||||||
|
u+=A("sr",screen.width+"x"+screen.height);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (layer == 1)
|
||||||
|
{
|
||||||
|
if (NI_IW == 0) { document.write('<div style="position:absolute;width:1px;height:1px;overflow:hidden"><IMG name="ni_tag" id="ni_tag" border="0" width="1" height="1" src="'+u+'"></div>'); NI_IW = 1; }
|
||||||
|
else { u+=A("ir","1"); document.images.ni_tag.src = u; }
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
document.write('<IMG name="ni_tag" id="ni_tag" border="0" width="1" height="1" src="'+u+'">'); NI_IW = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The following function may be used any number of times in a page to load a file and track a hit
|
||||||
|
* against that file. This is useful when the file being loaded is not html,
|
||||||
|
* or is not under your control, so can't have an imprint tracking code inserted into it.
|
||||||
|
*
|
||||||
|
* E.g. <a href="javascript:ni_LoadUrl('Catalogue.pdf', 'Catalogue')">Download catalogue</a>
|
||||||
|
*
|
||||||
|
* If you consider clicking on the link to be the completion of a transaction, use the 'Sale' trigger
|
||||||
|
* E.g. <a href="javascript:ni_LoadUrl('Catalogue.pdf', 'Catalogue', 'Sale')">Download catalogue</a>
|
||||||
|
*/
|
||||||
|
function ni_LoadUrl(url, title, trigger)
|
||||||
|
{
|
||||||
|
ni_TrackHit(NI_SE, NI_SC, "", NI_ST, NI_SV, trigger, "", "", title, url, NI_IW, "", "", "");
|
||||||
|
document.location.href = url;
|
||||||
|
}
|
||||||
|
|
816
javascript/LeftAndMain.js
Normal file
@ -0,0 +1,816 @@
|
|||||||
|
var _AJAX_LOADING = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Code for the separator bar between the two panes
|
||||||
|
*/
|
||||||
|
function DraggableSeparator() {
|
||||||
|
this.onmousedown = this.onmousedown.bindAsEventListener(this);
|
||||||
|
// this.onselectstart = this.onselectstart.bindAsEventListener(this);
|
||||||
|
}
|
||||||
|
DraggableSeparator.prototype = {
|
||||||
|
onmousedown : function(event) {
|
||||||
|
this.leftBase = $('left').offsetWidth - Event.pointerX(event);
|
||||||
|
this.separatorBase = getDimension($('separator'),'left') - Event.pointerX(event);
|
||||||
|
this.rightBase = getDimension($('right'),'left') - Event.pointerX(event);
|
||||||
|
|
||||||
|
document.onmousemove = this.document_mousemove.bindAsEventListener(this);
|
||||||
|
document.onmouseup = this.document_mouseup.bindAsEventListener(this);
|
||||||
|
|
||||||
|
document.body.onselectstart = this.body_selectstart.bindAsEventListener(this);
|
||||||
|
},
|
||||||
|
document_mousemove : function(event) {
|
||||||
|
$('left').style.width = (this.leftBase + Event.pointerX(event)) + 'px';
|
||||||
|
fixRightWidth();
|
||||||
|
},
|
||||||
|
document_mouseup : function(e) {
|
||||||
|
document.onmousemove = null;
|
||||||
|
},
|
||||||
|
|
||||||
|
body_selectstart : function(event) {
|
||||||
|
Event.stop(event);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function fixRightWidth() {
|
||||||
|
if( !$('right') )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Absolutely position all the elements
|
||||||
|
var sep = getDimension($('left'),'width') + getDimension($('left'),'left');
|
||||||
|
$('separator').style.left = (sep + 2) + 'px';
|
||||||
|
$('right').style.left = (sep + 6) + 'px';
|
||||||
|
|
||||||
|
// Give the remaining space to right
|
||||||
|
var rightWidth = parseInt(document.body.offsetWidth) - parseInt($('left').offsetWidth) - $('separator').offsetWidth - 8;
|
||||||
|
|
||||||
|
if( rightWidth >= 0 )
|
||||||
|
$('right').style.width = rightWidth + 'px';
|
||||||
|
|
||||||
|
var rb;
|
||||||
|
if(rb = $('rightbottom')) {
|
||||||
|
rb.style.left = $('right').style.left;
|
||||||
|
rb.style.width = $('right').style.width;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Behaviour.register({
|
||||||
|
'#separator' : DraggableSeparator,
|
||||||
|
|
||||||
|
'#left' : {
|
||||||
|
hide : function() {
|
||||||
|
if(!this.hidden) {
|
||||||
|
this.hidden = true;
|
||||||
|
this.style.width = null;
|
||||||
|
Element.addClassName(this,'hidden');
|
||||||
|
Element.addClassName('separator','hidden');
|
||||||
|
fixRightWidth();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
show : function() {
|
||||||
|
if(this.hidden) {
|
||||||
|
this.hidden = false;
|
||||||
|
Element.removeClassName(this,'hidden');
|
||||||
|
Element.removeClassName('separator','hidden');
|
||||||
|
fixRightWidth();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
'#MainMenu li' : {
|
||||||
|
onclick : function(event) {
|
||||||
|
window.location.href = this.getElementsByTagName('a')[0].href;
|
||||||
|
Event.stop(event);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
'#Menu-help' : {
|
||||||
|
onclick : function() {
|
||||||
|
var w = window.open(this.getElementsByTagName('a')[0].href, 'help');
|
||||||
|
w.focus();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
window.ontabschanged = function() {
|
||||||
|
var formEl = $('Form_EditForm');
|
||||||
|
|
||||||
|
if( !formEl )
|
||||||
|
return;
|
||||||
|
|
||||||
|
var fs = formEl.getElementsByTagName('fieldset')[0];
|
||||||
|
if(fs) fs.style.height = formEl.style.height;
|
||||||
|
|
||||||
|
// var divs = document.getElementsBySelector('#Form_EditForm div');
|
||||||
|
/*for(i=0;i<divs.length;i++) {
|
||||||
|
if( ( Element.hasClassName(divs[i],'tab') || Element.hasClassName(divs[i],'tabset') ) && isVisible(divs[i]) ) {
|
||||||
|
if(navigator.appName == "Microsoft Internet Explorer")
|
||||||
|
fitToParent(divs[i], i == 0 ? 18 : 0);
|
||||||
|
else
|
||||||
|
fitToParent(divs[i], 3);
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
|
||||||
|
if( _TAB_DIVS_ON_PAGE ) {
|
||||||
|
for(i = 0; i < _TAB_DIVS_ON_PAGE.length; i++ ) {
|
||||||
|
// console.log(_TAB_DIVS_ON_PAGE[i].id);
|
||||||
|
if(navigator.appName == "Microsoft Internet Explorer")
|
||||||
|
fitToParent(_TAB_DIVS_ON_PAGE[i], i == 0 ? 18 : 30);
|
||||||
|
else
|
||||||
|
fitToParent(_TAB_DIVS_ON_PAGE[i], /*3*/ 30);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.onresize = function(init) {
|
||||||
|
var right = $('right');
|
||||||
|
var rightbottom = $('rightbottom');
|
||||||
|
if(rightbottom) rightbottom.style.display = 'none';
|
||||||
|
|
||||||
|
if(typeof fitToParent == 'function') fitToParent('right', 12);
|
||||||
|
|
||||||
|
if( $('left') && $('separator') && right )
|
||||||
|
$('left').style.height = $('separator').style.height = right.style.height;
|
||||||
|
|
||||||
|
if(rightbottom) {
|
||||||
|
var newHeight = (parseInt(right.style.height) / 3);
|
||||||
|
right.style.height = (newHeight*2) + 'px';
|
||||||
|
rightbottom.style.height = newHeight + 'px';
|
||||||
|
rightbottom.style.display = '';
|
||||||
|
rightbottom.style.top = getDimension(right,'top') + (newHeight*2) + 'px';
|
||||||
|
}
|
||||||
|
|
||||||
|
if(typeof fitToParent == 'function') fitToParent('Form_EditForm');
|
||||||
|
if(typeof fixHeight_left == 'function') fixHeight_left();
|
||||||
|
if(typeof fixRightWidth == 'function') fixRightWidth();
|
||||||
|
|
||||||
|
window.ontabschanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
appendLoader(function() {
|
||||||
|
document.body.style.overflow = 'hidden';
|
||||||
|
window.onresize(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
function isVisible(el) {
|
||||||
|
// if(typeof el.isVisible != 'undefined') return el.isVisible;
|
||||||
|
if(el.tagName == "body" || el.tagName == "BODY") return (el.isVisible = true);
|
||||||
|
else if(el.style.display == 'none') return (el.isVisible = false);
|
||||||
|
else return (el.isVisible = isVisible(el.parentNode));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
LeftAndMain_window_unload = function() {
|
||||||
|
window.exiting = true; // this is used by prototype
|
||||||
|
if(typeof autoSave == 'function')
|
||||||
|
autoSave(navigator.appName == "Microsoft Internet Explorer");
|
||||||
|
}
|
||||||
|
|
||||||
|
Event.observe(window, 'beforeunload', LeftAndMain_window_unload);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unlock the locked status message.
|
||||||
|
* Show a queued message, if one exists
|
||||||
|
*/
|
||||||
|
function unlockStatusMessage() {
|
||||||
|
statusMessage.locked = false;
|
||||||
|
if(statusMessage.queued) {
|
||||||
|
statusMessage(
|
||||||
|
statusMessage.queued.msg,
|
||||||
|
statusMessage.queued.type,
|
||||||
|
statusMessage.queued.showNetworkActivity);
|
||||||
|
|
||||||
|
statusMessage.queued = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Move form actions to the top and make them ajax
|
||||||
|
*/
|
||||||
|
function ajaxActionsAtTop(formName, actionHolderName, tabName) {
|
||||||
|
var actions = document.getElementsBySelector('#' + formName + ' .Actions')[0];
|
||||||
|
var holder;
|
||||||
|
|
||||||
|
if(actions) {
|
||||||
|
if((holder = $(actionHolderName)) && holder != actions) {
|
||||||
|
holder.parentNode.removeChild(holder);
|
||||||
|
}
|
||||||
|
|
||||||
|
actions.id = actionHolderName;
|
||||||
|
actions.className = 'ajaxActions';
|
||||||
|
|
||||||
|
$(tabName).appendChild(actions);
|
||||||
|
prepareAjaxActions(actions, formName, tabName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare the ajax actions so that the buttons actually do something
|
||||||
|
*/
|
||||||
|
function prepareAjaxActions(actions, formName, tabName) {
|
||||||
|
var i, button, list = actions.getElementsByTagName('input')
|
||||||
|
for (i=0;button=list[i];i++) {
|
||||||
|
button.ownerForm = $(formName);
|
||||||
|
|
||||||
|
button.onclick = function(e) {
|
||||||
|
// tries to call a custom method of the format "action_<youraction>_right"
|
||||||
|
if(window[this.name + '_' + tabName]) {
|
||||||
|
window[this.name + '_' + tabName](e);
|
||||||
|
} else {
|
||||||
|
statusMessage(ingize(this.value));
|
||||||
|
Ajax.SubmitForm(this.ownerForm, this.name, {
|
||||||
|
onSuccess: Ajax.Evaluator,
|
||||||
|
onFailure: ajaxErrorHandler
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
behaveAs(button, StatusTitle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function ingize(val) {
|
||||||
|
if(!val) val = "process";
|
||||||
|
if(val.substr(val.length-1) == 'e') return val.substr(0, val.length-1) + 'ing...';
|
||||||
|
else return val + 'ing...';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Submit the given form and evaluate the Ajax response.
|
||||||
|
* Needs to be bound to an object with the following parameters to work:
|
||||||
|
* - form
|
||||||
|
* - action
|
||||||
|
* - verb
|
||||||
|
*
|
||||||
|
* The bound function can then be called, with the arguments passed
|
||||||
|
*/
|
||||||
|
|
||||||
|
function ajaxSubmitForm(automated, callAfter, form, action, verb) {
|
||||||
|
// tinyMCE.triggerSave(true);
|
||||||
|
|
||||||
|
var alreadySaved = false;
|
||||||
|
if($(form).elements.length < 2) alreadySaved = true;
|
||||||
|
|
||||||
|
if(alreadySaved) {
|
||||||
|
if(callAfter) callAfter();
|
||||||
|
|
||||||
|
} else {
|
||||||
|
statusMessage(verb + '...', '', true);
|
||||||
|
|
||||||
|
var success = function(response) {
|
||||||
|
Ajax.Evaluator(response);
|
||||||
|
if(callAfter) callAfter();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(callAfter) success = success.bind({callAfter : callAfter});
|
||||||
|
Ajax.SubmitForm(form, action, {
|
||||||
|
onSuccess : success,
|
||||||
|
onFailure : function(response) {
|
||||||
|
errorMessage('Error ' + verb, response);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Post the given fields to the given url
|
||||||
|
*/
|
||||||
|
function ajaxSubmitFieldSet(href, fieldSet, extraData) {
|
||||||
|
// Build data
|
||||||
|
var i,field,data = "ajax=1";
|
||||||
|
for(i=0;field=fieldSet[i];i++) {
|
||||||
|
data += '&' + Form.Element.serialize(field);
|
||||||
|
}
|
||||||
|
if(extraData){
|
||||||
|
data += '&'+extraData;
|
||||||
|
}
|
||||||
|
// Send request
|
||||||
|
new Ajax.Request(href, {
|
||||||
|
method : 'post', postBody : data,
|
||||||
|
onSuccess : function(response) {
|
||||||
|
//alert(response.responseText);
|
||||||
|
Ajax.Evaluator(response);
|
||||||
|
},
|
||||||
|
onFailure : function(response) {
|
||||||
|
alert(response.responseText);
|
||||||
|
//errorMessage('Error: ', response);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Post the given fields to the given url
|
||||||
|
*/
|
||||||
|
function ajaxLink(href) {
|
||||||
|
// Send request
|
||||||
|
new Ajax.Request(href + (href.indexOf("?") == -1 ? "?" : "&") + "ajax=1", {
|
||||||
|
method : 'get',
|
||||||
|
onSuccess : Ajax.Evaluator,
|
||||||
|
onFailure : ajaxErrorHandler
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load a URL into the given form
|
||||||
|
*/
|
||||||
|
function ajaxLoadPage() {
|
||||||
|
statusMessage('loading...', 2, true);
|
||||||
|
new Ajax.Request(this.URL + '&ajax=1', {
|
||||||
|
method : 'get',
|
||||||
|
onSuccess : ajaxLoadPage_success.bind(this)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function ajaxLoadPage_success(response) {
|
||||||
|
statusMessage('loaded');
|
||||||
|
$(this.form).loadNewPage(response.responseText);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Behaviour of the statuts message.
|
||||||
|
*/
|
||||||
|
Behaviour.register({
|
||||||
|
'#statusMessage' : {
|
||||||
|
showMessage : function(message, type, waitTime, clearManually) {
|
||||||
|
if(this.fadeTimer) {
|
||||||
|
clearTimeout(this.fadeTimer);
|
||||||
|
this.fadeTimer = null;
|
||||||
|
}
|
||||||
|
if(this.currentEffect) {
|
||||||
|
this.currentEffect.cancel();
|
||||||
|
this.currentEffect = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.innerHTML = message;
|
||||||
|
this.className = type;
|
||||||
|
Element.setOpacity(this, 1);
|
||||||
|
|
||||||
|
this.style.position = 'absolute';
|
||||||
|
this.style.display = '';
|
||||||
|
this.style.visibility = '';
|
||||||
|
|
||||||
|
if(!clearManually) {
|
||||||
|
this.fade(0.5,waitTime ? waitTime : 5);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
clearMessage : function(waitTime) {
|
||||||
|
this.fade(0.5, waitTime);
|
||||||
|
},
|
||||||
|
fade: function(fadeTime, waitTime) {
|
||||||
|
if(!fadeTime) fadeTime = 0.5;
|
||||||
|
|
||||||
|
// Wait a bit before fading
|
||||||
|
if(waitTime) {
|
||||||
|
this.fadeTimer = setTimeout((function() {
|
||||||
|
this.fade(fadeTime);
|
||||||
|
}).bind(this), waitTime * 1000);
|
||||||
|
|
||||||
|
// Fade straight away
|
||||||
|
} else {
|
||||||
|
this.currentEffect = new Effect.Opacity(this,
|
||||||
|
{ duration: 0.5,
|
||||||
|
transition: Effect.Transitions.linear,
|
||||||
|
from: 1.0, to: 0.0,
|
||||||
|
afterFinish : this.afterFade.bind(this) });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
afterFade : function() {
|
||||||
|
this.style.visibility = 'hidden';
|
||||||
|
this.style.display = 'none';
|
||||||
|
this.innerHTML = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show a status message.
|
||||||
|
*
|
||||||
|
* @param msg String
|
||||||
|
* @param type String (optional) can be 'good' or 'bad'
|
||||||
|
* @param clearManually boolean Don't automatically fade message.
|
||||||
|
*/
|
||||||
|
function statusMessage(msg, type, clearManually) {
|
||||||
|
var statusMessageEl = $('statusMessage');
|
||||||
|
if(statusMessageEl) {
|
||||||
|
if(msg) {
|
||||||
|
statusMessageEl.showMessage(msg, type, msg.length / 20, clearManually);
|
||||||
|
} else {
|
||||||
|
statusMessageEl.clearMessage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearStatusMessage() {
|
||||||
|
$('statusMessage').clearMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when something goes wrong
|
||||||
|
*/
|
||||||
|
function errorMessage(msg, fullMessage) {
|
||||||
|
// More complex error for developers
|
||||||
|
if(fullMessage && window.location.href.indexOf('//dev') != -1) {
|
||||||
|
// Get the message from an Ajax response object
|
||||||
|
try {
|
||||||
|
if(typeof fullMessage == 'object') fullMessage = fullMessage.status + '//' + fullMessage.responseText;
|
||||||
|
} catch(er) {
|
||||||
|
fullMessage = "";
|
||||||
|
}
|
||||||
|
msg = msg + '<br>' + fullMessage.replace(/\n/g,'<br>');
|
||||||
|
}
|
||||||
|
|
||||||
|
$('statusMessage').showMessage(msg,'bad',60);
|
||||||
|
}
|
||||||
|
|
||||||
|
function ajaxErrorHandler(response) {
|
||||||
|
errorMessage('Error talking to server', response);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applying StatusTitle to an element will mean that the title attribute is shown as a statusmessage
|
||||||
|
* upon hover
|
||||||
|
*/
|
||||||
|
StatusTitle = Class.create();
|
||||||
|
StatusTitle.prototype = {
|
||||||
|
onmouseover : function() {
|
||||||
|
if(this.title) {
|
||||||
|
this.message = this.title;
|
||||||
|
this.title = null;
|
||||||
|
}
|
||||||
|
if(this.message) {
|
||||||
|
$('statusMessage').showMessage(this.message);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onmouseout : function() {
|
||||||
|
if(this.message) {
|
||||||
|
$('statusMessage').fade(0.3,1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* BaseForm is the base form class used in the CMS.
|
||||||
|
*/
|
||||||
|
BaseForm = Class.create();
|
||||||
|
BaseForm.prototype = {
|
||||||
|
intitialize: function() {
|
||||||
|
this.visible = this.style.display == 'none' ? false : true;
|
||||||
|
|
||||||
|
// Collect all the buttons and attach handlers
|
||||||
|
this.buttons = [];
|
||||||
|
var i,input,allInputs = this.getElementsByTagName('input');
|
||||||
|
for(i=0;input=allInputs[i];i++) {
|
||||||
|
if(input.type == 'button' || input.type == 'submit') {
|
||||||
|
this.buttons.push(input);
|
||||||
|
input.holder = this;
|
||||||
|
input.onclick = function() { return this.holder.buttonClicked(this); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
show: function() {
|
||||||
|
this.visible = true;
|
||||||
|
Element.hide(show);
|
||||||
|
},
|
||||||
|
hide: function() {
|
||||||
|
this.visible = false;
|
||||||
|
Element.hide(this);
|
||||||
|
},
|
||||||
|
isVisible: function() {
|
||||||
|
return this.visible;
|
||||||
|
},
|
||||||
|
buttonClicked: function(button) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ChangeTracker is a class that can be applied to forms to support change tracking on forms.
|
||||||
|
*/
|
||||||
|
ChangeTracker = Class.create();
|
||||||
|
ChangeTracker.prototype = {
|
||||||
|
initialize: function() {
|
||||||
|
this.resetElements();
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset all the 'changed field' data.
|
||||||
|
*/
|
||||||
|
resetElements: function(debug) {
|
||||||
|
var elements = Form.getElements(this);
|
||||||
|
var i, element;
|
||||||
|
for(i=0;element=elements[i];i++) {
|
||||||
|
// Initialise each element
|
||||||
|
if(element.resetChanged) {
|
||||||
|
element.resetChanged();
|
||||||
|
} else {
|
||||||
|
element.originalSerialized = Form.Element.serialize(element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
field_changed: function() {
|
||||||
|
return this.originalSerialized != Form.Element.serialize(this);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if something in the form has been changed
|
||||||
|
*/
|
||||||
|
isChanged: function() {
|
||||||
|
var elements = Form.getElements(this);
|
||||||
|
var i, element;
|
||||||
|
for(i=0;element=elements[i];i++) {
|
||||||
|
if(!element.isChanged) element.isChanged = this.field_changed;
|
||||||
|
if(!this.changeDetection_fieldsToIgnore[element.name] && element.isChanged()) {
|
||||||
|
|
||||||
|
//if( window.location.href.match( /^https?:\/\/dev/ ) )
|
||||||
|
// Debug.log('Changed:'+ element.id + '(' + this.originalSerialized +')->('+Form.Element.serialize(element)+')' );
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
changeDetection_fieldsToIgnore : {
|
||||||
|
'Sort' : true
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serialize only the fields to change.
|
||||||
|
* You can specify the names of fields that must be included as arguments
|
||||||
|
*/
|
||||||
|
serializeChangedFields: function() {
|
||||||
|
var elements = Form.getElements(this);
|
||||||
|
var queryComponent, queryComponents = new Array();
|
||||||
|
var i, element;
|
||||||
|
|
||||||
|
var forceFields = {};
|
||||||
|
if(arguments) {for(var i=0;i<arguments.length;i++) forceFields[arguments[i]] = true;}
|
||||||
|
|
||||||
|
for(i=0;element=elements[i];i++) {
|
||||||
|
if(!element.name.match(/^action_(.+)$/i)) // For dropdown those 'action_xxx' fields.
|
||||||
|
{ if(!element.isChanged) element.isChanged = this.field_changed;
|
||||||
|
if(forceFields[element.name] || (element.isChanged()) || element.name.match(/\[.*\]/g) ) {
|
||||||
|
queryComponent = Form.Element.serialize(element);
|
||||||
|
if (queryComponent)
|
||||||
|
queryComponents.push(queryComponent);
|
||||||
|
} else {
|
||||||
|
// Used by the Sapphire code to preserve the form field value
|
||||||
|
|
||||||
|
if( element.name.match( '/\]$/' ) )
|
||||||
|
queryComponents.push(element.name.substring( 0, element.name.length - 1 ) + '_unchanged' + ']=1' );
|
||||||
|
else
|
||||||
|
queryComponents.push(element.name + '_unchanged=1');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//alert(queryComponents.join('&'));
|
||||||
|
return queryComponents.join('&');
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serialize all the fields on the page
|
||||||
|
*/
|
||||||
|
serializeAllFields: function() {
|
||||||
|
return Form.serializeWithoutButtons(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ModalForm provides a form with the functionality to prevent certian actions from occurring until
|
||||||
|
* it's been closed.
|
||||||
|
*
|
||||||
|
* To use, You should run the blockEvent method as many times as needed.
|
||||||
|
*/
|
||||||
|
ModalForm = Class.extend('BaseForm');
|
||||||
|
ModalForm.prototype = {
|
||||||
|
/*
|
||||||
|
* Prevent the given event from occurring on the given event while the form is open.
|
||||||
|
* These must be CMS-created events, not built-in javascript events.
|
||||||
|
* For example, form.blockEvent($('sitetree'), 'SelectionChanged')
|
||||||
|
*/
|
||||||
|
blockEvent: function(element, event) {
|
||||||
|
element.observeMethod(event, (function() { return !this.isVisible();}).bind(this) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function doYouWantToRollback(handlers) {
|
||||||
|
var url = document.getElementsByTagName('base')[0].href + 'admin/canceldraftchangesdialog';
|
||||||
|
OpenModalDialog(url, handlers, 'Are you sure?' );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shows a "do you want to save" dialog box.
|
||||||
|
* Returns save / discard / cancel
|
||||||
|
*/
|
||||||
|
|
||||||
|
var _DO_YOU_WANT_TO_SAVE_IS_OPEN = false;
|
||||||
|
function doYouWantToSave(handlers) {
|
||||||
|
// modalDialog('admin/dialog?Message=You+have+changed+this+page.+Do+you+want+to+save+these+changes%3F&Buttons[]=save,Save+changes&Buttons[]=discard,Discard+changes&Buttons[]=cancel,Cancel', handlers);
|
||||||
|
var url = document.getElementsByTagName('base')[0].href + 'admin/savedialog';
|
||||||
|
OpenModalDialog(url, handlers, 'Unsaved Changes' );
|
||||||
|
}
|
||||||
|
|
||||||
|
function modalDialog(url, handlers) {
|
||||||
|
var baseURL = document.getElementsByTagName('base')[0].href;
|
||||||
|
if(window.showModalDialog) {
|
||||||
|
var result = showModalDialog(baseURL + url + '&Modal=1', null, "status:no;dialogWidth:400px;dialogHeight:150px;edge:sunken");
|
||||||
|
if(handlers[result])
|
||||||
|
handlers[result]();
|
||||||
|
|
||||||
|
} else {
|
||||||
|
_DO_YOU_WANT_TO_SAVE_IS_OPEN = true;
|
||||||
|
doYouWantToSave.dialog = new ModalDialog(baseURL + url, handlers);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ModalDialog = Class.create();
|
||||||
|
ModalDialog.prototype = {
|
||||||
|
initialize: function(url, handlers) {
|
||||||
|
this.url = url;
|
||||||
|
this.handlers = handlers;
|
||||||
|
this.timer = setInterval(this.interval.bind(this), 50);
|
||||||
|
this.window = window.open(this.url, 'dialog', "status=no,width=400,height=150,edge=sunken");
|
||||||
|
this.window.dialogObject = this;
|
||||||
|
this.window.linkedObject = this;
|
||||||
|
setTimeout( (function(){this.window.linkedObject = this;}).bind(this), 500);
|
||||||
|
},
|
||||||
|
force: function (val) {
|
||||||
|
this.finished = true;
|
||||||
|
this.clearInterval(this.time);
|
||||||
|
|
||||||
|
if(this.handlers[val]) {
|
||||||
|
_DO_YOU_WANT_TO_SAVE_IS_OPEN = false;
|
||||||
|
(this.handlers[val])();
|
||||||
|
} else {
|
||||||
|
throw("Couldn't find a handler called '" + this.result + "'");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
interval: function() {
|
||||||
|
if(this.finished) {
|
||||||
|
clearInterval(this.timer);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(!this.window || this.window.closed) {
|
||||||
|
clearInterval(this.timer);
|
||||||
|
if(this.handlers) {
|
||||||
|
if(this.handlers[this.result]) {
|
||||||
|
_DO_YOU_WANT_TO_SAVE_IS_OPEN = false;
|
||||||
|
(this.handlers[this.result])();
|
||||||
|
|
||||||
|
} else {
|
||||||
|
throw("Couldn't find a handler called '" + this.result + "'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.window.focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.top._OPEN_DIALOG = null;
|
||||||
|
|
||||||
|
OpenModalDialog = function( url, handlers, message ) {
|
||||||
|
var dialog = new GBModalDialog( url, handlers, message );
|
||||||
|
}
|
||||||
|
|
||||||
|
GBModalDialog = Class.create();
|
||||||
|
GBModalDialog.prototype = {
|
||||||
|
|
||||||
|
initialize: function( url, handlers, message ) {
|
||||||
|
this.url = url;
|
||||||
|
this.handlers = handlers;
|
||||||
|
this.caption = message;
|
||||||
|
|
||||||
|
window.top._OPEN_DIALOG = this;
|
||||||
|
|
||||||
|
GB_show( this.caption, this.url, 110, 450 );
|
||||||
|
},
|
||||||
|
|
||||||
|
execHandler: function( handler ) {
|
||||||
|
GB_hide();
|
||||||
|
|
||||||
|
if( this.handlers[handler] )
|
||||||
|
this.handlers[handler]();
|
||||||
|
else
|
||||||
|
throw( "Unknown handler '" + handler + "'" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function hideLoading() {
|
||||||
|
$('Loading').style.display = 'none';
|
||||||
|
document.body.className = '';
|
||||||
|
}
|
||||||
|
function baseHref() {
|
||||||
|
var baseTags = document.getElementsByTagName('base');
|
||||||
|
if(baseTags) return baseTags[0].href;
|
||||||
|
else return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
returnFalse = function() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
showResponseAsSuccessMessage = function(response) {
|
||||||
|
statusMessage(response.responseText, 'good');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function is called by prototype when it receives notification that the user was logged out.
|
||||||
|
* It redirects back to the login form.
|
||||||
|
*/
|
||||||
|
function onSessionLost() {
|
||||||
|
alert("You've been logged out of the server, so we're going to send you back to the log-in screen.");
|
||||||
|
window.location.href = baseHref() + 'Security/login?BackURL=' + encodeURIComponent(window.location.href);
|
||||||
|
}
|
||||||
|
|
||||||
|
var _CURRENT_CONTEXT_MENU = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new context menu
|
||||||
|
* @param event The event object
|
||||||
|
* @param owner The DOM element that this context-menu was requested from
|
||||||
|
* @param menuItems A map of title -> method; context-menu operations to get called
|
||||||
|
*/
|
||||||
|
function createContextMenu(event, owner, menuItems) {
|
||||||
|
if(_CURRENT_CONTEXT_MENU) {
|
||||||
|
document.body.removeChild(_CURRENT_CONTEXT_MENU);
|
||||||
|
_CURRENT_CONTEXT_MENU = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var menu = document.createElement("ul");
|
||||||
|
menu.className = 'contextMenu';
|
||||||
|
menu.style.position = 'absolute';
|
||||||
|
menu.style.left = event.clientX + 'px';
|
||||||
|
menu.style.top = event.clientY + 'px';
|
||||||
|
|
||||||
|
var menuItemName, menuItemTag, menuATag;
|
||||||
|
for(menuItemName in menuItems) {
|
||||||
|
menuItemTag = document.createElement("li");
|
||||||
|
|
||||||
|
menuATag = document.createElement("a");
|
||||||
|
menuATag.href = "#";
|
||||||
|
menuATag.onclick = menuATag.oncontextmenu = contextmenu_onclick;
|
||||||
|
menuATag.innerHTML = menuItemName;
|
||||||
|
menuATag.handler = menuItems[menuItemName];
|
||||||
|
menuATag.owner = owner;
|
||||||
|
|
||||||
|
menuItemTag.appendChild(menuATag);
|
||||||
|
menu.appendChild(menuItemTag);
|
||||||
|
}
|
||||||
|
|
||||||
|
document.body.appendChild(menu);
|
||||||
|
|
||||||
|
document.body.onclick = contextmenu_close;
|
||||||
|
|
||||||
|
_CURRENT_CONTEXT_MENU = menu;
|
||||||
|
|
||||||
|
return menu;
|
||||||
|
}
|
||||||
|
|
||||||
|
function contextmenu_close() {
|
||||||
|
if(_CURRENT_CONTEXT_MENU) {
|
||||||
|
document.body.removeChild(_CURRENT_CONTEXT_MENU);
|
||||||
|
_CURRENT_CONTEXT_MENU = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function contextmenu_onclick() {
|
||||||
|
this.handler(this.owner);
|
||||||
|
contextmenu_close();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shows an ajax loading indicator.
|
||||||
|
*
|
||||||
|
* @param id String Identifier for the newly created image
|
||||||
|
* @param container ID/DOM Element
|
||||||
|
* @param imgSrc String (optional)
|
||||||
|
* @param insertionType Object (optional) Prototype-style insertion-classes, defaults to Insertion.Bottom
|
||||||
|
* @param displayType String (optional) "inline" or "block"
|
||||||
|
*/
|
||||||
|
function showIndicator(id, container, imgSrc, insertionType, displayType) {
|
||||||
|
if(!id || !$(container)) return false;
|
||||||
|
if(!imgSrc) imgSrc = "cms/images/network-save.gif";
|
||||||
|
if(!displayType) displayType = "inline";
|
||||||
|
if(!insertionType) insertionType = Insertion.Bottom;
|
||||||
|
|
||||||
|
if(!$(id)) {
|
||||||
|
var html = '<img src="' + imgSrc + '" class="indicator ' + displayType + '" id="' + id + '" style="display: none" />';
|
||||||
|
new insertionType(container, html);
|
||||||
|
}
|
||||||
|
|
||||||
|
Effect.Appear(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
function hideIndicator(id) {
|
||||||
|
Effect.Fade(id, {duration: 0.3});
|
||||||
|
}
|
475
javascript/LeftAndMain_left.js
Executable file
@ -0,0 +1,475 @@
|
|||||||
|
/**
|
||||||
|
* LeftAndMain_left.js
|
||||||
|
* Code for supporting the left-hand panel of all the 2-pane admin windows
|
||||||
|
* This includes code for the action panel at the top, and the draggable, ajax-linked tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if(typeof SiteTreeHandlers == 'undefined') SiteTreeHandlers = {};
|
||||||
|
SiteTreeHandlers.parentChanged_url = 'admin/ajaxupdateparent';
|
||||||
|
SiteTreeHandlers.orderChanged_url = 'admin/ajaxupdatesort';
|
||||||
|
SiteTreeHandlers.showRecord_url = 'admin/show/';
|
||||||
|
|
||||||
|
var _HANDLER_FORMS = {
|
||||||
|
addpage : 'Form_AddPageOptionsForm',
|
||||||
|
deletepage : 'deletepage_options',
|
||||||
|
sortitems : 'sortitems_options'
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overload this with a real context menu if necessary
|
||||||
|
*/
|
||||||
|
var TreeContextMenu = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extra methods for the tree when used in the LHS of the CMS
|
||||||
|
*/
|
||||||
|
TreeAPI = Class.create();
|
||||||
|
TreeAPI.prototype = {
|
||||||
|
/**
|
||||||
|
* Perform the given code on the each tree node with the given index.
|
||||||
|
* There could be more than one :-)
|
||||||
|
* @param idx The index of the tree node
|
||||||
|
* @param code A method to be executed, that will be passed the tree node
|
||||||
|
*/
|
||||||
|
performOnTreeNode: function(idx, code) {
|
||||||
|
var treeNode = this.getTreeNodeByIdx(idx);
|
||||||
|
if(!treeNode) return;
|
||||||
|
|
||||||
|
if(treeNode.className.indexOf('manyparents') == -1) {
|
||||||
|
code(treeNode);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
var i,item,allNodes = this.getElementsByTagName('li');
|
||||||
|
for(i=0;item=allNodes[i];i++) {
|
||||||
|
if(treeNode.id == item.id) code(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
getTreeNodeByIdx : function(idx) {
|
||||||
|
if(!idx) idx = "0";
|
||||||
|
var node = document.getElementById('record-' + idx);
|
||||||
|
if(idx == "0" && !node) node = document.getElementById('record-root');
|
||||||
|
return node;
|
||||||
|
},
|
||||||
|
getIdxOf : function(treeNode) {
|
||||||
|
if(treeNode && treeNode.id && treeNode.id.match(/record-([0-9a-zA-Z\-]+)$/))
|
||||||
|
return RegExp.$1;
|
||||||
|
},
|
||||||
|
createTreeNode : function(idx, title, pageType) {
|
||||||
|
var i;
|
||||||
|
var node = document.createElement('li');
|
||||||
|
node.id = 'record-' + idx;
|
||||||
|
node.className = pageType;
|
||||||
|
|
||||||
|
var aTag = document.createElement('a');
|
||||||
|
aTag.href = SiteTreeHandlers.showRecord_url + idx;
|
||||||
|
aTag.innerHTML = title;
|
||||||
|
node.appendChild(aTag);
|
||||||
|
|
||||||
|
SiteTreeNode.create(node, this.options);
|
||||||
|
|
||||||
|
return node;
|
||||||
|
},
|
||||||
|
|
||||||
|
setNodeIdx : function(idx, newIdx) {
|
||||||
|
this.performOnTreeNode(idx, function(treeNode) {
|
||||||
|
treeNode.id = 'record-' + newIdx;
|
||||||
|
var aTag = treeNode.getElementsByTagName('a')[0];
|
||||||
|
aTag.href = aTag.href.replace(idx, newIdx);
|
||||||
|
});
|
||||||
|
|
||||||
|
var treeNode = this.getTreeNodeByIdx(idx);
|
||||||
|
},
|
||||||
|
|
||||||
|
setNodeTitle : function(idx, title) {
|
||||||
|
this.performOnTreeNode(idx, function(treeNode) {
|
||||||
|
var aTag = treeNode.getElementsByTagName('a')[0];
|
||||||
|
aTag.innerHTML = title;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
setNodeIcon: function(idx, oldClassName, newClassName) {
|
||||||
|
this.performOnTreeNode(idx, function(treeNode) {
|
||||||
|
treeNode.className = treeNode.className.replace(oldClassName,newClassName);
|
||||||
|
treeNode.aSpan.className = 'a ' + treeNode.className.replace('closed','spanClosed');
|
||||||
|
treeNode.setIconByClass();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
setCurrentByIdx : function(idx) {
|
||||||
|
if(this.selected) {
|
||||||
|
var i,item;
|
||||||
|
for(i=0;item=this.selected[i];i++) {
|
||||||
|
item.removeNodeClass('current');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
__tree = this;
|
||||||
|
__tree.selected = [];
|
||||||
|
|
||||||
|
this.performOnTreeNode(idx, function(treeNode) {
|
||||||
|
__tree.selected.push(treeNode);
|
||||||
|
treeNode.expose();
|
||||||
|
treeNode.addNodeClass('current');
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
changeCurrentTo : function(newNode) {
|
||||||
|
if(this.selected) {
|
||||||
|
var i,item;
|
||||||
|
for(i=0;item=this.selected[i];i++) {
|
||||||
|
item.removeNodeClass('current');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
newNode.addNodeClass('current');
|
||||||
|
|
||||||
|
this.selected = [newNode];
|
||||||
|
newNode.expose();
|
||||||
|
},
|
||||||
|
|
||||||
|
firstSelected : function() {
|
||||||
|
if(this.selected) return this.selected[0];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extra methods for the tree node when used in the LHS of the CMS
|
||||||
|
*/
|
||||||
|
TreeNodeAPI = Class.create();
|
||||||
|
TreeNodeAPI.prototype = {
|
||||||
|
selectTreeNode : function() {
|
||||||
|
if(this.getElementsByTagName('a')[0].href) {
|
||||||
|
_AJAX_LOADING = true;
|
||||||
|
if($('sitetree').notify('SelectionChanged', this)) {
|
||||||
|
autoSave(true, this.getPageFromServer.bind(this));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
getPageFromServer : function() {
|
||||||
|
$('Form_EditForm').getPageFromServer(this.id.replace('record-',''), this);
|
||||||
|
_AJAX_LOADING = false;
|
||||||
|
},
|
||||||
|
ajaxExpansion : function() {
|
||||||
|
this.addNodeClass('loading');
|
||||||
|
var ul = this.treeNodeHolder(false);
|
||||||
|
ul.innerHTML = 'loading...';
|
||||||
|
|
||||||
|
// Any attempts to add children to this page should, in fact, cue them up for insertion later
|
||||||
|
ul.cuedNewNodes = [];
|
||||||
|
ul.appendTreeNode = function(node) {
|
||||||
|
this.cuedNewNodes[this.cuedNewNodes.length] = node;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
new Ajax.Request(SiteTreeHandlers.loadTree_url + '&ajax=1&ID=' + this.getIdx(), {
|
||||||
|
onSuccess : this.installSubtree.bind(this),
|
||||||
|
onFailure : this.showSubtreeLoadingError
|
||||||
|
});
|
||||||
|
},
|
||||||
|
showSubtreeLoadingError: function(response) {
|
||||||
|
errorMessage('error loading subtree', response);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Context menu
|
||||||
|
*/
|
||||||
|
oncontextmenu: function(event) {
|
||||||
|
if(TreeContextMenu) {
|
||||||
|
if(!event) event = window.event;
|
||||||
|
createContextMenu(event, this, TreeContextMenu);
|
||||||
|
Event.stop(event);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* In the case of Tree & DraggableTree, the root tree and the sub-trees all use the same class.
|
||||||
|
* In this case, however, SiteTree has a much bigger API and so SiteSubTree is smaller.
|
||||||
|
*/
|
||||||
|
SiteSubTree = Class.extend('Tree').extend('TreeAPI');
|
||||||
|
SiteSubTree.prototype = {
|
||||||
|
castAsTreeNode: function(li) {
|
||||||
|
behaveAs(li, SiteTreeNode, this.options);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Our SiteTree class extends the tree object with a richer manipulation API.
|
||||||
|
* The server will send a piece javascript that uses these functions. In this way, the server
|
||||||
|
* has flexibility over its operation, but the Script->HTML interface is kept client-side.
|
||||||
|
*/
|
||||||
|
SiteTree = Class.extend('SiteSubTree');
|
||||||
|
SiteTree.prototype = {
|
||||||
|
initialize : function() {
|
||||||
|
this.Tree.initialize();
|
||||||
|
|
||||||
|
/*
|
||||||
|
if(!this.tree.selected) this.tree.selected = [];
|
||||||
|
var els = this.getElementsByTagName('li');
|
||||||
|
for(var i=0;i<els.length;i++) if(els[i].className.indexOf('current') > -1) {
|
||||||
|
this.tree.selected.push(els[i]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
this.observeMethod('SelectionChanged', this.interruptLoading.bind(this) );
|
||||||
|
},
|
||||||
|
destroy: function () {
|
||||||
|
if(this.Tree) this.Tree.destroy();
|
||||||
|
this.Tree = null;
|
||||||
|
this.SiteSubTree = null;
|
||||||
|
this.TreeAPI = null;
|
||||||
|
this.selected = null;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop the currently loading node from loading.
|
||||||
|
*/
|
||||||
|
interruptLoading: function( newLoadingNode ) {
|
||||||
|
if( this.loadingNode ) this.loadingNode.removeNodeClass('loading');
|
||||||
|
this.loadingNode = newLoadingNode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SiteTreeNode = Class.extend('TreeNode').extend('TreeNodeAPI');
|
||||||
|
SiteTreeNode.prototype = {
|
||||||
|
initialize: function(options) {
|
||||||
|
this.TreeNode.initialize(options);
|
||||||
|
|
||||||
|
if(this.className && this.className.match(/ *([^ ]+)( +|$)/)) {
|
||||||
|
if((typeof siteTreeHints != 'undefined') && siteTreeHints[ RegExp.$1 ]) {
|
||||||
|
this.hints = siteTreeHints[ RegExp.$1 ];
|
||||||
|
this.dropperOptions = {
|
||||||
|
accept : (this.hints.allowedChildren && (this.className.indexOf('nochildren') == -1))
|
||||||
|
? this.hints.allowedChildren : 'none'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(this.className.indexOf('current') > -1) {
|
||||||
|
if(!this.tree.selected) this.tree.selected = [];
|
||||||
|
this.tree.selected.push(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!this.hints) this.hints = {}
|
||||||
|
},
|
||||||
|
|
||||||
|
destroy: function () {
|
||||||
|
if(this.TreeNode) this.TreeNode.destroy();
|
||||||
|
this.TreeNode = null;
|
||||||
|
this.TreeNodeAPI = null;
|
||||||
|
},
|
||||||
|
|
||||||
|
castAsTree: function(childUL) {
|
||||||
|
behaveAs(childUL, SiteSubTree, this.options);
|
||||||
|
if(this.draggableObj) childUL.makeDraggable();
|
||||||
|
},
|
||||||
|
|
||||||
|
onselect: function() {
|
||||||
|
this.selectTreeNode();
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Drag'n'drop handlers - Ajax saving
|
||||||
|
*/
|
||||||
|
onParentChanged : function(node, oldParent, newParent) {
|
||||||
|
if(newParent.id.match(/^record-new/)) {
|
||||||
|
alert("You must save the page before dragging children into it");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( node == newParent || node.getIdx() == newParent.getIdx() ) {
|
||||||
|
alert("You cannot add a page to itself");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(node.innerHTML.toLowerCase().indexOf('<del') > -1) {
|
||||||
|
alert("You can't moved deleted pages");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( Element.hasClassName( newParent, 'nochildren' ) ) {
|
||||||
|
alert("You can't add children to that node");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var currentlyOpenPageID = 0;
|
||||||
|
if($('Form_EditForm').elements.ID) currentlyOpenPageID = $('Form_EditForm').elements.ID.value;
|
||||||
|
|
||||||
|
statusMessage('saving...', '', true);
|
||||||
|
new Ajax.Request(SiteTreeHandlers.parentChanged_url, {
|
||||||
|
method : 'post',
|
||||||
|
postBody : 'ID=' + node.getIdx() + '&ParentID=' + newParent.getIdx() + '&CurrentlyOpenPageID=' + currentlyOpenPageID,
|
||||||
|
onSuccess : Ajax.Evaluator,
|
||||||
|
onFailure : function(response) {
|
||||||
|
errorMessage('error saving parent', response);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the tree has been resorted
|
||||||
|
* nodeList is a list of all the nodes in the correct rder
|
||||||
|
* movedNode is the node that actually got moved to trigger this resorting
|
||||||
|
*/
|
||||||
|
onOrderChanged : function(nodeList, movedNode) {
|
||||||
|
statusMessage('saving...', '', true);
|
||||||
|
|
||||||
|
var i, parts = Array();
|
||||||
|
sort = 0;
|
||||||
|
|
||||||
|
for(i=0;i<nodeList.length;i++) {
|
||||||
|
if(nodeList[i].getIdx && nodeList[i].getIdx()) {
|
||||||
|
parts[parts.length] = 'ID[]=' + nodeList[i].getIdx();
|
||||||
|
|
||||||
|
// Ensure that the order of new records is preserved when they are moved THEN saved
|
||||||
|
if( nodeList[i].id.indexOf("record-new") == 0 )
|
||||||
|
if( $('Form_EditForm_ID') && ( 'record-' + $('Form_EditForm_ID').value == nodeList[i].id ) )
|
||||||
|
if( $('Form_EditForm_Sort') )
|
||||||
|
$('Form_EditForm_Sort').value = ++sort;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(movedNode.getIdx && movedNode.getIdx()) {
|
||||||
|
parts[parts.length] = 'MovedNodeID=' + movedNode.getIdx();
|
||||||
|
}
|
||||||
|
|
||||||
|
var currentlyOpenPageID = 0;
|
||||||
|
if($('Form_EditForm').elements.ID) currentlyOpenPageID = $('Form_EditForm').elements.ID.value;
|
||||||
|
|
||||||
|
if(parts) {
|
||||||
|
new Ajax.Request(SiteTreeHandlers.orderChanged_url, {
|
||||||
|
method : 'post',
|
||||||
|
postBody : parts.join('&') + '&CurrentlyOpenPageID=' + currentlyOpenPageID,
|
||||||
|
/*onSuccess : function(response) {
|
||||||
|
// statusMessage(response.responseText, 'good');
|
||||||
|
},*/
|
||||||
|
onSuccess: Ajax.Evaluator,
|
||||||
|
onFailure : function(response) {
|
||||||
|
errorMessage('error saving order', response);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Action panel at the top of the left hand tree
|
||||||
|
*/
|
||||||
|
treeactions = {
|
||||||
|
toggleSelection : function(li) {
|
||||||
|
li = (li.tagName.toLowerCase() == 'a') ? li.parentNode : li;
|
||||||
|
|
||||||
|
// Deselect
|
||||||
|
if(li.selected) this.closeSelection(li, true);
|
||||||
|
else this.openSelection(li, true);
|
||||||
|
|
||||||
|
fixHeight_left();
|
||||||
|
return li.selected;
|
||||||
|
},
|
||||||
|
closeSelection : function(li, nested) {
|
||||||
|
li = (li.tagName.toLowerCase() == 'a') ? li.parentNode : li;
|
||||||
|
|
||||||
|
Element.hide(_HANDLER_FORMS[li.id]);
|
||||||
|
removeClass(li, 'selected');
|
||||||
|
li.selected = false;
|
||||||
|
$(_HANDLER_FORMS[li.id]).notify('Close');
|
||||||
|
if(li.onclose) li.onclose();
|
||||||
|
|
||||||
|
if(typeof nested == 'undefined') fixHeight_left();
|
||||||
|
},
|
||||||
|
openSelection: function(li, nested) {
|
||||||
|
li = (li.tagName.toLowerCase() == 'a') ? li.parentNode : li;
|
||||||
|
|
||||||
|
addClass(li, 'selected');
|
||||||
|
Element.show(_HANDLER_FORMS[li.id]);
|
||||||
|
li.selected = true;
|
||||||
|
|
||||||
|
// Deselect siblings
|
||||||
|
var i, set = li.parentNode.getElementsByTagName('li');
|
||||||
|
for(i=0;i<set.length;i++) if(li != set[i] && set[i].selected) {
|
||||||
|
this.closeSelection(set[i], true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(typeof nested == 'undefined') fixHeight_left();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function fixHeight_left() {
|
||||||
|
if($('treepanes')) fitToParent('treepanes');
|
||||||
|
var st = $('SideTabs');
|
||||||
|
if(st) st.resize();
|
||||||
|
else fitToParent('sitetree_holder');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build the site tree
|
||||||
|
SiteTree.applyTo('#sitetree');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reorganise action
|
||||||
|
*/
|
||||||
|
ReorganiseAction = Class.create();
|
||||||
|
ReorganiseAction.applyTo('#sortitems');
|
||||||
|
ReorganiseAction.prototype = {
|
||||||
|
initialize: function () {
|
||||||
|
this.getElementsByTagName('a')[0].onclick = returnFalse;
|
||||||
|
Observable.applyTo($(_HANDLER_FORMS[this.id]));
|
||||||
|
},
|
||||||
|
|
||||||
|
onclick : function() {
|
||||||
|
if(treeactions.toggleSelection(this)) {
|
||||||
|
$('sitetree').makeDraggable();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
onclose : function() {
|
||||||
|
$('sitetree').stopBeingDraggable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Control the site tree filter
|
||||||
|
*/
|
||||||
|
SiteTreeFilterForm = Class.create();
|
||||||
|
SiteTreeFilterForm.applyTo('form#SiteTreeFilter');
|
||||||
|
SiteTreeFilterForm.prototype = {
|
||||||
|
|
||||||
|
initialize: function() {
|
||||||
|
this.valueField = $('SiteTreeFilterValue');
|
||||||
|
this.submitButton = $('SiteTreeFilterButton');
|
||||||
|
this.submitButton.onclick = this.submitclick.bind(this);
|
||||||
|
// this.submitButton.onclick = function() { alert('Click'); return false; }
|
||||||
|
},
|
||||||
|
|
||||||
|
submitclick: function() {
|
||||||
|
new Ajax.Request( this.action + '?SiteTreeFilterValue=' + encodeURIComponent( this.valueField.value ) + '&ajax=1', {
|
||||||
|
onSuccess: function( response ) {
|
||||||
|
alert(response.responseText);
|
||||||
|
$('sitetree').innerHTML = response.responseText;
|
||||||
|
Behaviour.apply( $('sitetree') );
|
||||||
|
statusMessage('Filtered tree','good');
|
||||||
|
},
|
||||||
|
onFailure: function( response ) {
|
||||||
|
errorMessage('Could not filter site tree<br />' + response.responseText);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
463
javascript/LeftAndMain_right.js
Executable file
@ -0,0 +1,463 @@
|
|||||||
|
|
||||||
|
CMSForm = Class.extend('ChangeTracker').extend('Observable');
|
||||||
|
CMSForm.prototype = {
|
||||||
|
initialize : function(fn) {
|
||||||
|
this.ChangeTracker.initialize();
|
||||||
|
this.formName = fn;
|
||||||
|
this.prepareForm();
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Processing called whenever a page is loaded in the right - including the initial one
|
||||||
|
*/
|
||||||
|
prepareForm : function() {
|
||||||
|
ajaxActionsAtTop(this.id, 'form_actions_' + this.formName, this.formName);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load actions from a string containing the HTML content
|
||||||
|
*/
|
||||||
|
loadActionsFromString : function(actionHTML) {
|
||||||
|
var actionHolder = $('form_actions_' + this.formName);
|
||||||
|
actionHolder.innerHTML = actionHTML;
|
||||||
|
prepareAjaxActions(actionHolder, this.id, this.formName);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close the form down without any corrective action, after its been deleted.
|
||||||
|
*/
|
||||||
|
closeIfSetTo: function(id) {
|
||||||
|
if(this.elements.ID && this.elements.ID.value == id) {
|
||||||
|
this.innerHTML = "<p>This page was deleted. To edit a page, select it from the left.</p>";
|
||||||
|
if(typeof tinyMCE != 'undefined') tinyMCE.instances = [];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reload lose the form if the current page is open.
|
||||||
|
*/
|
||||||
|
reloadIfSetTo: function(id) {
|
||||||
|
if(this.elements.ID && this.elements.ID.value == id) {
|
||||||
|
this.getPageFromServer(id);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
close: function() {
|
||||||
|
this.innerHTML = "<p> </p>";
|
||||||
|
var actions;
|
||||||
|
if(actions = $('form_actions_' + this.formName)) {
|
||||||
|
actions.parentNode.removeChild(actions);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
updateStatus: function( newStatus ) {
|
||||||
|
|
||||||
|
if( $('Form_EditForm_Status') )
|
||||||
|
$('Form_EditForm_Status').innerHTML = "STATUS: " + newStatus;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load a new page into the right-hand form.
|
||||||
|
*
|
||||||
|
* @param formContent string
|
||||||
|
* @param response object (optional)
|
||||||
|
* @param evalResponse boolean (optional)
|
||||||
|
*/
|
||||||
|
loadNewPage : function(formContent, response, evalResponse) {
|
||||||
|
//alert('here: ' + formContent);
|
||||||
|
var rightHTML = formContent;
|
||||||
|
|
||||||
|
// Rewrite # links
|
||||||
|
rightHTML = rightHTML.replace(/href *= *"#/g, 'href="' + window.location.href.replace(/#.*$/,'') + '#');
|
||||||
|
|
||||||
|
// Rewrite iframe links (for IE)
|
||||||
|
rightHTML = rightHTML.replace(/(<iframe[^>]*src=")([^"]+)("[^>]*>)/g, '$1' + baseHref() + '$2$3');
|
||||||
|
|
||||||
|
// Prepare iframes for removal, otherwise we get loading bugs
|
||||||
|
var i, allIframes = this.getElementsByTagName('iframe');
|
||||||
|
if(allIframes) for(i=0;i<allIframes.length;i++) {
|
||||||
|
allIframes[i].contentWindow.location.href = 'about:blank';
|
||||||
|
allIframes[i].parentNode.removeChild(allIframes[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(response && evalResponse) {
|
||||||
|
Ajax.Evaluator(response);
|
||||||
|
} else {
|
||||||
|
this.innerHTML = rightHTML;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the form attributes from embedded fields
|
||||||
|
var attr;
|
||||||
|
|
||||||
|
for(attr in {'action':true ,'method':true,'enctype':true,'name':true}) {
|
||||||
|
if(this.elements['_form_' + attr]) {
|
||||||
|
this[attr] = this.elements['_form_' + attr].value;
|
||||||
|
this.elements['_form_' + attr].parentNode.removeChild(this.elements['_form_' + attr]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
allIframes = this.getElementsByTagName('iframe');
|
||||||
|
if(allIframes) for(i=0;i<allIframes.length;i++) {
|
||||||
|
try {
|
||||||
|
allIframes[i].contentWindow.location.href = allIframes[i].src;
|
||||||
|
} catch(er) {alert(er.message);}
|
||||||
|
}
|
||||||
|
|
||||||
|
_TAB_DIVS_ON_PAGE = [];
|
||||||
|
|
||||||
|
try {
|
||||||
|
var tabs = document.getElementsBySelector('#' + this.id + ' ul.tabstrip');
|
||||||
|
} catch(er) { /*alert('a: '+ er.message + '\n' + er.line);*/ }
|
||||||
|
try {
|
||||||
|
for(var i=0;i<tabs.length;i++) if(tabs[i].tagName) initTabstrip(tabs[i]);
|
||||||
|
} catch(er) { /*alert('b: '+ er.message + '\n' + er.line);*/ }
|
||||||
|
|
||||||
|
if((typeof tinyMCE != 'undefined') && tinyMCE.instances) {
|
||||||
|
tinyMCE.instances = [];
|
||||||
|
tinyMCE.isLoaded = false;
|
||||||
|
tinyMCE.onLoad();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(this.prepareForm) this.prepareForm();
|
||||||
|
|
||||||
|
Behaviour.apply(this);
|
||||||
|
|
||||||
|
if(this.resetElements) this.resetElements();
|
||||||
|
|
||||||
|
window.ontabschanged();
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Save the contens of the form, by submitting it and resetting is changed checker
|
||||||
|
* on success.
|
||||||
|
*/
|
||||||
|
save: function(ifChanged, callAfter, action) {
|
||||||
|
_AJAX_LOADING = true;
|
||||||
|
if(typeof tinyMCE != 'undefined') tinyMCE.triggerSave();
|
||||||
|
if(!action) action = "save";
|
||||||
|
|
||||||
|
var __callAfter = callAfter;
|
||||||
|
var __form = this;
|
||||||
|
|
||||||
|
if(__form.notify) __form.notify('BeforeSave', __form.elements.ID.value);
|
||||||
|
|
||||||
|
// validate if required
|
||||||
|
if(this.validate && !this.validate()) {
|
||||||
|
// TODO Automatically switch to the tab/position of the first error
|
||||||
|
statusMessage("Validation failed.", "bad");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var success = function(response) {
|
||||||
|
Ajax.Evaluator(response);
|
||||||
|
__form.resetElements();
|
||||||
|
if(__callAfter) __callAfter();
|
||||||
|
if(__form.notify) __form.notify('PageSaved', __form.elements.ID.value);
|
||||||
|
_AJAX_LOADING = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(ifChanged) {
|
||||||
|
var data = this.serializeChangedFields('ID') + '&ajax=1&action_' + action + '=1';
|
||||||
|
} else {
|
||||||
|
var data = this.serializeAllFields() + '&ajax=1&action_' + action + '=1';
|
||||||
|
}
|
||||||
|
|
||||||
|
statusMessage("Saving...", null, true);
|
||||||
|
new Ajax.Request(this.action, {
|
||||||
|
method : this.method,
|
||||||
|
postBody: data,
|
||||||
|
onSuccess : success,
|
||||||
|
onFailure : function(response) {
|
||||||
|
errorMessage('Error saving content', response);
|
||||||
|
_AJAX_LOADING = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
loadPage_url : 'admin/getpage'
|
||||||
|
}
|
||||||
|
|
||||||
|
CMSRightForm = Class.extend('CMSForm');
|
||||||
|
CMSRightForm.prototype = {
|
||||||
|
intialize: function() {
|
||||||
|
this.CMSForm.initialize('right');
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load the given URL (with &ajax=1) into this form
|
||||||
|
*/
|
||||||
|
loadURLFromServer : function(url) {
|
||||||
|
var urlParts = url.match( /ID=(\d+)/ );
|
||||||
|
var id = urlParts ? urlParts[1] : null;
|
||||||
|
|
||||||
|
if( !url.match( /^https?:\/\/.*/ ) )
|
||||||
|
url = document.getElementsByTagName('base')[0].href + url;
|
||||||
|
|
||||||
|
new Ajax.Request( url + '&ajax=1', {
|
||||||
|
asynchronous : true,
|
||||||
|
onSuccess : function( response ) {
|
||||||
|
$('Form_EditForm').successfullyReceivedPage(response,id);
|
||||||
|
},
|
||||||
|
onFailure : function(response) {
|
||||||
|
alert(response.responseText);
|
||||||
|
errorMessage('error loading page',response);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
successfullyReceivedPage : function(response,pageID) {
|
||||||
|
var loadingNode = $('sitetree').loadingNode;
|
||||||
|
|
||||||
|
if( loadingNode && pageID && parseInt( $('sitetree').getIdxOf( loadingNode ) ) != pageID ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// must wait until the javascript has finished
|
||||||
|
document.body.style.cursor = 'wait';
|
||||||
|
|
||||||
|
this.loadNewPage(response.responseText);
|
||||||
|
|
||||||
|
var subform;
|
||||||
|
if(subform = $('Form_MemberForm')) subform.close();
|
||||||
|
if(subform = $('Form_SubForm')) subform.close();
|
||||||
|
|
||||||
|
if(this.elements.ID) {
|
||||||
|
this.notify('PageLoaded', this.elements.ID.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(this.receivingID) {
|
||||||
|
// Treenode might not exist if that part of the tree is closed
|
||||||
|
var treeNode = loadingNode ? loadingNode : $('sitetree').getTreeNodeByIdx(this.receivingID);
|
||||||
|
if(treeNode) {
|
||||||
|
$('sitetree').setCurrentByIdx(treeNode.getIdx());
|
||||||
|
treeNode.removeNodeClass('loading');
|
||||||
|
}
|
||||||
|
statusMessage('');
|
||||||
|
}
|
||||||
|
|
||||||
|
// must wait until the javascript has finished
|
||||||
|
document.body.style.cursor = 'default';
|
||||||
|
|
||||||
|
},
|
||||||
|
didntReceivePage : function(response) {
|
||||||
|
errorMessage('error loading page', response);
|
||||||
|
$('sitetree').getTreeNodeByIdx(this.elements.ID.value).removeNodeClass('loading');
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request a page from the server via Ajax
|
||||||
|
*/
|
||||||
|
getPageFromServer : function(id, treeNode) {
|
||||||
|
// if(id && id.match(/^[A-Za-z0-9_]+$/)) {
|
||||||
|
if(id && (id == 'root' || parseInt(id) == id || (id.substr && id.substr(0,3) == 'new') )) {
|
||||||
|
this.receivingID = id;
|
||||||
|
|
||||||
|
// Treenode might not exist if that part of the tree is closed
|
||||||
|
if(!treeNode) treeNode = $('sitetree').getTreeNodeByIdx(id);
|
||||||
|
|
||||||
|
if(treeNode) {
|
||||||
|
$('sitetree').loadingNode = treeNode;
|
||||||
|
treeNode.addNodeClass('loading');
|
||||||
|
url = treeNode.aTag.href + (treeNode.aTag.href.indexOf('?')==-1?'?':'&') + 'ajax=1';
|
||||||
|
}
|
||||||
|
if(SiteTreeHandlers.loadPage_url) {
|
||||||
|
var sep = (SiteTreeHandlers.loadPage_url.indexOf('?') == -1) ? '?' : '&';
|
||||||
|
url = SiteTreeHandlers.loadPage_url + sep + 'ID=' + id;
|
||||||
|
}
|
||||||
|
|
||||||
|
statusMessage("loading...");
|
||||||
|
this.loadURLFromServer(url);
|
||||||
|
} else {
|
||||||
|
throw("getPageFromServer: Bad page ID: " + id);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the status field
|
||||||
|
*/
|
||||||
|
setStatus: function(newStatus) {
|
||||||
|
var statusLabel = document.getElementsBySelector('label.pageStatusMessage')[0];
|
||||||
|
if(statusLabel) statusLabel.innerHTML = "STATUS: " + newStatus;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CMSForm.applyTo('#Form_SubForm', 'rightbottom');
|
||||||
|
CMSRightForm.applyTo('#Form_EditForm', 'right');
|
||||||
|
|
||||||
|
|
||||||
|
function action_save_right() {
|
||||||
|
_AJAX_LOADING = true;
|
||||||
|
$('Form_EditForm').save(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ActionPropertiesForm = Class.create();
|
||||||
|
ActionPropertiesForm.prototype = {
|
||||||
|
/**
|
||||||
|
* Open the form
|
||||||
|
*/
|
||||||
|
|
||||||
|
open : function() {
|
||||||
|
var allInputs = this.getElementsByTagName('input');
|
||||||
|
this.submitButton = allInputs[allInputs.length-1];
|
||||||
|
this.submitButton.onclick = this.send.bind(this);
|
||||||
|
this.style.display = '';
|
||||||
|
|
||||||
|
// This detects whether we've opened a new page
|
||||||
|
if($('Form_EditForm').elements.ID.value != this.elements.ID.value) {
|
||||||
|
this.elements.ID.value = $('Form_EditForm').elements.ID.value;
|
||||||
|
|
||||||
|
new Ajax.Updater(
|
||||||
|
'action_submit_options_recipient',
|
||||||
|
'admin/getpagemembers/' + this.elements.ID.value + '?SecurityLevel=' + this.securityLevel
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
close : function() {
|
||||||
|
this.style.display = 'none';
|
||||||
|
},
|
||||||
|
toggle : function() {
|
||||||
|
if(this.style.display == '') this.close();
|
||||||
|
else this.open();
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Submit the option form and carry out the action
|
||||||
|
*/
|
||||||
|
send : function() {
|
||||||
|
// Show a "submitting..." box
|
||||||
|
if(!this.sendingText) {
|
||||||
|
this.sendingText = document.createElement('div');
|
||||||
|
this.sendingText.innerHTML = 'Submitting...';
|
||||||
|
this.sendingText.className = 'sendingText';
|
||||||
|
Element.setOpacity(this.sendingText, 0.9);
|
||||||
|
this.appendChild(this.sendingText);
|
||||||
|
}
|
||||||
|
this.sendingText.style.display = '';
|
||||||
|
|
||||||
|
// Send the request
|
||||||
|
var buttons = document.getElementsBySelector('#' + this.id + ' p.Actions input');
|
||||||
|
var actionButton = null;
|
||||||
|
|
||||||
|
if( buttons )
|
||||||
|
actionButton = buttons[0];
|
||||||
|
ajaxSubmitForm(false, this.onComplete.bind(this), this, actionButton ? actionButton.name : '', 'submit');
|
||||||
|
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process the action's Ajax response
|
||||||
|
*/
|
||||||
|
onComplete: function() {
|
||||||
|
this.sendingText.style.display = 'none';
|
||||||
|
if(this.elements.Message) this.elements.Message.value = '';
|
||||||
|
this.close();
|
||||||
|
$('Form_EditForm').getPageFromServer($('Form_EditForm_ID').value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle auto-saving. Detects if changes have been made, and if so save everything on the page.
|
||||||
|
* If confirmation is true it will ask for confirmation.
|
||||||
|
*/
|
||||||
|
function autoSave(confirmation, callAfter) {
|
||||||
|
if(typeof tinyMCE != 'undefined') tinyMCE.triggerSave();
|
||||||
|
|
||||||
|
var __forms = []
|
||||||
|
if($('Form_EditForm')) __forms.push($('Form_EditForm'));
|
||||||
|
if($('Form_SubForm')) __forms.push($('Form_SubForm'));
|
||||||
|
if($('Form_MemberForm')) __forms.push($('Form_MemberForm'));
|
||||||
|
|
||||||
|
var __somethingHasChanged = false;
|
||||||
|
var __callAfter = callAfter;
|
||||||
|
|
||||||
|
__forms.each(function(form) {
|
||||||
|
if(form.isChanged && form.isChanged()) {
|
||||||
|
__somethingHasChanged = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if(__somethingHasChanged) {
|
||||||
|
var options = {
|
||||||
|
save: function() {
|
||||||
|
statusMessage('saving...', '', true);
|
||||||
|
var i;
|
||||||
|
for(i=0;i<__forms.length;i++) {
|
||||||
|
if(__forms[i].isChanged && __forms[i].isChanged()) {
|
||||||
|
if(i == 0) __forms[i].save(true, __callAfter);
|
||||||
|
else __forms[i].save(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
discard: function() {
|
||||||
|
__forms.each(function(form) { form.resetElements(false); });
|
||||||
|
if(__callAfter) __callAfter();
|
||||||
|
},
|
||||||
|
cancel: function() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fix the modal dialog!
|
||||||
|
*/
|
||||||
|
if(confirmation ) doYouWantToSave(options);
|
||||||
|
else options.save();
|
||||||
|
|
||||||
|
} else {
|
||||||
|
if(__callAfter) __callAfter();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
StageLink = Class.create();
|
||||||
|
StageLink.prototype = {
|
||||||
|
initialize: function(getVars, urlField) {
|
||||||
|
this.getVars = getVars;
|
||||||
|
this.urlField = urlField;
|
||||||
|
|
||||||
|
var boundNewPage = this.newPage.bind(this);
|
||||||
|
|
||||||
|
$('Form_EditForm').observeMethod('PageLoaded', boundNewPage);
|
||||||
|
$('Form_EditForm').observeMethod('PageSaved', boundNewPage);
|
||||||
|
$('Form_EditForm').observeMethod('PagePublished', boundNewPage);
|
||||||
|
$('Form_EditForm').observeMethod('PageUnpublished', boundNewPage);
|
||||||
|
|
||||||
|
this.newPage();
|
||||||
|
},
|
||||||
|
newPage : function() {
|
||||||
|
var linkField = $('Form_EditForm').elements[this.urlField];
|
||||||
|
var linkVal = linkField ? linkField.value : null;
|
||||||
|
if(linkVal) {
|
||||||
|
if(this.id != 'viewArchivedSite') this.style.display = '';
|
||||||
|
this.href = this.baseURL() + linkVal + '/' + this.getVars;
|
||||||
|
} else {
|
||||||
|
if(this.id != 'viewArchivedSite') this.style.display = 'none';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onclick : function() {
|
||||||
|
var w = window.open(this.href, windowName('site'));
|
||||||
|
w.focus();
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
baseURL : function() {
|
||||||
|
return document.getElementsByTagName('base')[0].href;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StageLink.applyTo('#viewStageSite', '?stage=Stage', 'StageURLSegment');
|
||||||
|
StageLink.applyTo('#viewLiveSite', '?stage=Live', 'LiveURLSegment');
|
||||||
|
StageLink.applyTo('#viewArchivedSite', '', 'URLSegment');
|
||||||
|
|
||||||
|
window.name = windowName('cms');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a unique window name that contains the URL
|
||||||
|
*/
|
||||||
|
function windowName(suffix) {
|
||||||
|
var base = document.getElementsByTagName('base')[0].href.replace('http://','').replace(/\//g,'_').replace(/\./g,'_');
|
||||||
|
return base + suffix;
|
||||||
|
}
|
322
javascript/MemberList.js
Executable file
@ -0,0 +1,322 @@
|
|||||||
|
/**
|
||||||
|
* Auto-lookup on ajax fields
|
||||||
|
*/
|
||||||
|
AjaxMemberLookup = {
|
||||||
|
initialise : function() {
|
||||||
|
var div = document.createElement('div');
|
||||||
|
div.id = this.id + '_ac';
|
||||||
|
div.className = 'autocomplete';
|
||||||
|
this.parentNode.appendChild(div);
|
||||||
|
new Ajax.Autocompleter(this.id, div.id, 'admin/security/autocomplete/' + this.name, {
|
||||||
|
afterUpdateElement : this.afterAutocomplete.bind(this)
|
||||||
|
});
|
||||||
|
},
|
||||||
|
afterAutocomplete : function(field, selectedItem) {
|
||||||
|
var data = selectedItem.getElementsByTagName('span')[1].innerHTML;
|
||||||
|
var items = data.split(",");
|
||||||
|
form = Element.ancestorOfType(field, 'form');
|
||||||
|
|
||||||
|
form.elements.FirstName.value = items[0];
|
||||||
|
form.elements.Surname.value = items[1];
|
||||||
|
form.elements.Email.value = items[2];
|
||||||
|
if(items[3])
|
||||||
|
form.elements.Password.value = items[3];
|
||||||
|
|
||||||
|
var fieldSet = field.parentNode.parentNode.getElementsByTagName('input');
|
||||||
|
|
||||||
|
ajaxSubmitFieldSet('admin/security/savemember?MemberListBaseGroupID='.$('MemberListBaseGroupID'), fieldSet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Member list behaviour
|
||||||
|
*/
|
||||||
|
|
||||||
|
Behaviour.register({
|
||||||
|
'#MemberList tr' : {
|
||||||
|
onmouseover : hover_over,
|
||||||
|
onmouseout : hover_out,
|
||||||
|
|
||||||
|
onclick : function() {
|
||||||
|
if(this.className.indexOf('addrow') == -1) {
|
||||||
|
Element.addClassName(this, 'loading');
|
||||||
|
new Ajax.Request('admin/security/getmember', {
|
||||||
|
method : 'post',
|
||||||
|
postBody : 'ID=' + this.id.replace('member-','') + '&ajax=1',
|
||||||
|
onSuccess : this.select_success.bind(this)
|
||||||
|
});
|
||||||
|
|
||||||
|
} else {
|
||||||
|
if($('Form_MemberForm'))
|
||||||
|
$('Form_MemberForm').innerHTML = "<p>Choose a member from above.</p>";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
select_success : function(response) {
|
||||||
|
Element.removeClassName(this, 'loading');
|
||||||
|
$('Form_MemberForm').loadNewPage(response.responseText);
|
||||||
|
|
||||||
|
statusMessage('loaded','good');
|
||||||
|
// for (var n in tinyMCE.instances) tinyMCE.removeMCEControl(n);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
'#MemberList thead tr' : {
|
||||||
|
onmouseover : null,
|
||||||
|
onmouseout : null,
|
||||||
|
onclick : null
|
||||||
|
},
|
||||||
|
|
||||||
|
'#MemberList' : {
|
||||||
|
initialise : function() {
|
||||||
|
this.headerMap = [];
|
||||||
|
|
||||||
|
var i, item, headers = this.getElementsByTagName('thead')[0].getElementsByTagName('tr')[0].getElementsByTagName('td');
|
||||||
|
for(i=0;item=headers[i];i++) {
|
||||||
|
this.headerMap[i] = item.className;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
setRecordDetails : function(id, details, groupID) {
|
||||||
|
var row = document.getElementById('member-' + id);
|
||||||
|
if(row) {
|
||||||
|
var i, item, cells = row.getElementsByTagName('td');
|
||||||
|
for(i=0;item=cells[i];i++) {
|
||||||
|
if(details[this.headerMap[i]]) {
|
||||||
|
item.innerHTML = details[this.headerMap[i]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.createRecord(id, details, groupID);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
createRecord : function (id, details, groupId) {
|
||||||
|
var row = document.createElement('tr');
|
||||||
|
row.id = 'member-' + id;
|
||||||
|
var i, cell, cellField;
|
||||||
|
for(i=0;cellField=this.headerMap[i];i++) {
|
||||||
|
cell = document.createElement('td')
|
||||||
|
if(details[cellField]) {
|
||||||
|
cell.innerHTML = details[cellField];
|
||||||
|
}
|
||||||
|
row.appendChild(cell);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the delete icon
|
||||||
|
if(typeof groupId == 'undefined')
|
||||||
|
var groupId = $('Form_EditForm').elements.ID.value;
|
||||||
|
cell = document.createElement('td')
|
||||||
|
cell.innerHTML = '<a class="deletelink" href="admin/security/removememberfromgroup/' + groupId + '/' + id + '"><img src="cms/images/delete.gif" alt="delete" /></a>';
|
||||||
|
cell.getElementsByTagName('0');
|
||||||
|
row.appendChild(cell);
|
||||||
|
|
||||||
|
var tbody = this.getElementsByTagName('tbody')[0];
|
||||||
|
var addRow = document.getElementsByClassName('addrow',tbody)[0];
|
||||||
|
if(addRow) tbody.insertBefore(row, addRow);
|
||||||
|
else tbody.appendChild(row);
|
||||||
|
Behaviour.apply(row, true);
|
||||||
|
},
|
||||||
|
|
||||||
|
clearAddForm : function() {
|
||||||
|
var tbody = this.getElementsByTagName('tbody')[0];
|
||||||
|
var addRow = document.getElementsByClassName('addrow',tbody)[0];
|
||||||
|
if(addRow) {
|
||||||
|
var i,field,fields = addRow.getElementsByTagName('input');
|
||||||
|
for(i=0;field=fields[i];i++) {
|
||||||
|
if(field.type != 'hidden' && field.type != 'submit') field.value = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
removeMember : function(memberID) {
|
||||||
|
var record;
|
||||||
|
if(record = $('member-' + memberID)) {
|
||||||
|
record.parentNode.removeChild(record);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
'#MemberList input' : AjaxMemberLookup,
|
||||||
|
|
||||||
|
'#MemberList a.deletelink' : {
|
||||||
|
onclick : function(event) {
|
||||||
|
if(confirm("Do you want to remove this member from the group?")) {
|
||||||
|
this.getElementsByTagName('img')[0].src = 'cms/images/network-save.gif';
|
||||||
|
ajaxLink(this.href);
|
||||||
|
}
|
||||||
|
Event.stop(event);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
'#MemberList tr.addrow' : {
|
||||||
|
onmouseover : null,
|
||||||
|
onmouseout : null,
|
||||||
|
onclick : null
|
||||||
|
},
|
||||||
|
|
||||||
|
'#MemberList tr.addrow td.actions input' : {
|
||||||
|
initialise: function() {
|
||||||
|
data = this.parentNode.parentNode.getElementsByTagName('input');
|
||||||
|
var i,item,error = [];
|
||||||
|
for(i=0;item=data[i];i++) {
|
||||||
|
item.originalSerialized = Form.Element.serialize(item);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
onclick : function(event) {
|
||||||
|
data = this.parentNode.parentNode.getElementsByTagName('input');
|
||||||
|
var i,item,error = [];
|
||||||
|
for(i=0;item=data[i];i++) {
|
||||||
|
if(item.name == 'Email' && !item.value) error[error.length] = "Email";
|
||||||
|
if(item.name == 'Password' && !item.value) error[error.length] = "Password";
|
||||||
|
}
|
||||||
|
if(error.length > 0) {
|
||||||
|
alert('Please enter a ' + error.join(' and a ') + ' to add a member.');
|
||||||
|
|
||||||
|
} else {
|
||||||
|
ajaxSubmitFieldSet('admin/security/addmember?MemberListBaseGroupID='.$('MemberListBaseGroupID') , data);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
'#Form_EditForm' : {
|
||||||
|
changeDetection_fieldsToIgnore : {
|
||||||
|
'MemberListBaseGroup' : true,
|
||||||
|
'MemberListOrderByField' : true,
|
||||||
|
'MemberListOrderByOrder' : true,
|
||||||
|
'MemberListGroup' : true,
|
||||||
|
'MemberListSearch' : true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
MemberListFilterButton = Class.create();
|
||||||
|
MemberListFilterButton.applyTo('div.MemberListField #MemberListFilterButton');
|
||||||
|
MemberListFilterButton.prototype = {
|
||||||
|
initialize: function() {
|
||||||
|
this.inputFields = new Array();
|
||||||
|
|
||||||
|
var childNodes = this.parentNode.getElementsByTagName('input');
|
||||||
|
|
||||||
|
for( var index = 0; index < childNodes.length; index++ ) {
|
||||||
|
if( childNodes[index].tagName ) {
|
||||||
|
childNodes[index].resetChanged = function() { return false; }
|
||||||
|
childNodes[index].isChanged = function() { return false; }
|
||||||
|
this.inputFields.push( childNodes[index] );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
childNodes = this.parentNode.getElementsByTagName('select');
|
||||||
|
|
||||||
|
for( var index = 0; index < childNodes.length; index++ ) {
|
||||||
|
if( childNodes[index].tagName ) {
|
||||||
|
childNodes[index].resetChanged = function() { return false; }
|
||||||
|
childNodes[index].field_changed = function() { return false; }
|
||||||
|
this.inputFields.push( childNodes[index] );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
isChanged: function() {
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
onclick: function(event) {
|
||||||
|
|
||||||
|
var baseGroup = $('MemberListBaseGroupID').value;
|
||||||
|
|
||||||
|
var updateURL = 'admin/security/listmembers/' + baseGroup + '?';
|
||||||
|
|
||||||
|
for( var index = 0; index < this.inputFields.length; index++ ) {
|
||||||
|
if( this.inputFields[index].tagName ) {
|
||||||
|
updateURL += this.inputFields[index].name + '=' + encodeURIComponent( this.inputFields[index].value ) + '&';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateURL += 'ajax=1';
|
||||||
|
|
||||||
|
new Ajax.Request( updateURL, {
|
||||||
|
onSuccess: function( response ) {
|
||||||
|
$('MemberList').innerHTML = response.responseText;
|
||||||
|
// Behaviour.debug();
|
||||||
|
Behaviour.apply( $('MemberList') );
|
||||||
|
},
|
||||||
|
onFailure: function( response ) {
|
||||||
|
errorMessage('Could not filter results: ' + response.responseText );
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Behaviour.register({
|
||||||
|
'div#MemberList div.PageControls a' : {
|
||||||
|
onclick: function() {
|
||||||
|
new Ajax.Request( this.href + '&ajax=1', {
|
||||||
|
onSuccess: function( response ) {
|
||||||
|
$('MemberList').innerHTML = response.responseText;
|
||||||
|
// Behaviour.debug();
|
||||||
|
Behaviour.apply( $('MemberList') );
|
||||||
|
},
|
||||||
|
onFailure: function( response ) {
|
||||||
|
errorMessage('Could not filter results: ' + response.responseText );
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function reloadMemberList( groupID ) {
|
||||||
|
|
||||||
|
if( !groupID )
|
||||||
|
groupID = $('MemberListBaseGroupID').value;
|
||||||
|
|
||||||
|
if($('MemberListStart')) var listStart = $('MemberListStart').value;
|
||||||
|
else var listStart = 0;
|
||||||
|
|
||||||
|
new Ajax.Request( 'admin/security/listmembers?&ajax=1&MemberListBaseGroup=' + groupID + '&MemberListStart=' + listStart, {
|
||||||
|
onSuccess: function( response ) {
|
||||||
|
$('MemberList').innerHTML = response.responseText;
|
||||||
|
// Behaviour.debug();
|
||||||
|
Behaviour.apply( $('MemberList') );
|
||||||
|
},
|
||||||
|
onFailure: function( response ) {
|
||||||
|
errorMessage('Could not filter results: ' + response.responseText );
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Behaviour.register({
|
||||||
|
'.MemberListFilter a.showhide' : {
|
||||||
|
initialise: function() {
|
||||||
|
this.open = !Element.hasClassName( this, 'closed' );
|
||||||
|
},
|
||||||
|
|
||||||
|
onclick: function() {
|
||||||
|
if( this.open )
|
||||||
|
this.openControls();
|
||||||
|
else
|
||||||
|
this.closeControls();
|
||||||
|
|
||||||
|
this.open = !this.open;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
openControls: function() {
|
||||||
|
Element.removeClassName( this, 'closed' );
|
||||||
|
$('MemberListFilterControls').style.display = 'block';
|
||||||
|
},
|
||||||
|
|
||||||
|
closeControls: function() {
|
||||||
|
Element.removeClassName( this, 'closed' );
|
||||||
|
$('MemberListFilterControls').style.display = 'none';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
298
javascript/MemberTableField.js
Executable file
@ -0,0 +1,298 @@
|
|||||||
|
/**
|
||||||
|
* Modified 2006-10-05, Ingo Schommer
|
||||||
|
* This is more or less a copy of Member.js, with additions and changes
|
||||||
|
* to match the switch from Member.php to MemberTableField.php all over the UI.
|
||||||
|
* Eventually it will replace Member.js (please remove this message then).
|
||||||
|
*/
|
||||||
|
|
||||||
|
// no confirm message for removal from a group
|
||||||
|
ComplexTableField.prototype.deleteConfirmMessage = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Auto-lookup on ajax fields
|
||||||
|
*/
|
||||||
|
AjaxMemberLookup = {
|
||||||
|
initialise : function() {
|
||||||
|
var div = document.createElement('div');
|
||||||
|
div.id = this.id + '_ac';
|
||||||
|
div.className = 'autocomplete';
|
||||||
|
this.parentNode.appendChild(div);
|
||||||
|
if(this.id) {
|
||||||
|
new Ajax.Autocompleter(this.id, div.id, 'admin/security/autocomplete/' + this.name, {
|
||||||
|
afterUpdateElement : this.afterAutocomplete.bind(this)
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
},
|
||||||
|
afterAutocomplete : function(field, selectedItem) {
|
||||||
|
var data = selectedItem.getElementsByTagName('span')[1].innerHTML;
|
||||||
|
var items = data.split(",");
|
||||||
|
form = Element.ancestorOfType(field, 'form');
|
||||||
|
// TODO more flexible column-detection
|
||||||
|
form.elements.FirstName.value = items[0];
|
||||||
|
form.elements.Surname.value = items[1];
|
||||||
|
form.elements.Email.value = items[2];
|
||||||
|
if(items[3] && form.elements.Password)
|
||||||
|
form.elements.Password.value = items[3];
|
||||||
|
|
||||||
|
//var fieldSet = field.parentNode.parentNode.getElementsByTagName('input');
|
||||||
|
//ajaxSubmitFieldSet('admin/security/savemember?MemberBaseGroupID='.$('MemberBaseGroupID'), fieldSet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MemberTableField = Class.create();
|
||||||
|
MemberTableField.applyTo('#Form_EditForm div.MemberTableField');
|
||||||
|
MemberTableField.prototype = {
|
||||||
|
|
||||||
|
initialize: function() {
|
||||||
|
Behaviour.register({
|
||||||
|
'#Form_EditForm div.MemberFilter input' : {
|
||||||
|
onkeypress : this.prepareSearch.bind(this)
|
||||||
|
},
|
||||||
|
|
||||||
|
'#Form_EditForm div.MemberTableField table.data tr.addtogrouprow input' : {
|
||||||
|
onkeypress : this.prepareAddToGroup.bind(this)
|
||||||
|
},
|
||||||
|
|
||||||
|
'#Form_EditForm div.MemberTableField table.data tr.addtogrouprow #Form_AddRecordForm_action_addtogroup' : {
|
||||||
|
onclick : this.prepareAddToGroup.bind(this)
|
||||||
|
},
|
||||||
|
|
||||||
|
'#Form_EditForm div.MemberTableField table.data tr.addtogrouprow td.actions input' : {
|
||||||
|
initialise: function() {
|
||||||
|
data = this.parentNode.parentNode.getElementsByTagName('input');
|
||||||
|
var i,item,error = [];
|
||||||
|
for(i=0;item=data[i];i++) {
|
||||||
|
item.originalSerialized = Form.Element.serialize(item);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onclick : this.addToGroup.bind(this)
|
||||||
|
},
|
||||||
|
|
||||||
|
//'#Form_EditForm div.MemberTableField input' : AjaxMemberLookup,
|
||||||
|
|
||||||
|
'#Form_EditForm' : {
|
||||||
|
changeDetection_fieldsToIgnore : {
|
||||||
|
'ctf[start]' : true,
|
||||||
|
'ctf[ID]' : true,
|
||||||
|
'MemberOrderByField' : true,
|
||||||
|
'MemberOrderByOrder' : true,
|
||||||
|
'MemberGroup' : true,
|
||||||
|
'MemberFilterButton' : true,
|
||||||
|
'MemberFieldName' : true,
|
||||||
|
'MemberDontShowPassword' : true,
|
||||||
|
'Surname' : true,
|
||||||
|
'FirstName' : true,
|
||||||
|
'Email' : true,
|
||||||
|
'MemberSearch' : true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
// prevent submission of wrong form-button (MemberFilterButton)
|
||||||
|
prepareAddToGroup: function(e) {
|
||||||
|
// IE6 doesnt send an event-object with onkeypress
|
||||||
|
var event = (e) ? e : window.event;
|
||||||
|
var keyCode = (event.keyCode) ? event.keyCode : event.which;
|
||||||
|
if(keyCode == Event.KEY_RETURN) {
|
||||||
|
var el = Event.element(event);
|
||||||
|
this.addToGroup(event);
|
||||||
|
Event.stop(event);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// prevent submission of wrong form-button (MemberFilterButton)
|
||||||
|
prepareSearch: function(e) {
|
||||||
|
// IE6 doesnt send an event-object with onkeypress
|
||||||
|
var event = (e) ? e : window.event;
|
||||||
|
var keyCode = (event.keyCode) ? event.keyCode : event.which;
|
||||||
|
|
||||||
|
if(keyCode == Event.KEY_RETURN) {
|
||||||
|
var el = Event.element(event);
|
||||||
|
$('MemberFilterButton').onclick(event);
|
||||||
|
Event.stop(event);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
addToGroup: function(e) {
|
||||||
|
// only submit parts of the form
|
||||||
|
var data = this.parentNode.parentNode.getElementsByTagName('input');
|
||||||
|
var i,item,error = [];
|
||||||
|
var form = Event.findElement(e,"form");
|
||||||
|
|
||||||
|
for(i=0;item=data[i];i++) {
|
||||||
|
if(item.name == 'Email' && !item.value) error[error.length] = "Email";
|
||||||
|
if(item.name == 'Password' && !item.value) error[error.length] = "Password";
|
||||||
|
}
|
||||||
|
|
||||||
|
if(error.length > 0) {
|
||||||
|
alert('Please enter a ' + error.join(' and a ') + ' to add a member.');
|
||||||
|
} else {
|
||||||
|
updateURL = "";
|
||||||
|
updateURL += Event.findElement(e,"form").action;
|
||||||
|
// we can't set "fieldName" as a HiddenField because there might be multiple ComplexTableFields in a single EditForm-container
|
||||||
|
updateURL += "&fieldName="+$('MemberFieldName').value;
|
||||||
|
updateURL += "&action_callfieldmethod&&methodName=addtogroup&";
|
||||||
|
|
||||||
|
ajaxSubmitFieldSet(updateURL, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
initialise : function() {
|
||||||
|
this.headerMap = [];
|
||||||
|
|
||||||
|
var i, item, headers = this.getElementsByTagName('thead')[0].getElementsByTagName('tr')[0].getElementsByTagName('td');
|
||||||
|
for(i=0;item=headers[i];i++) {
|
||||||
|
this.headerMap[i] = item.className;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
setRecordDetails : function(id, details, groupID) {
|
||||||
|
var row = document.getElementById('member-' + id);
|
||||||
|
if(row) {
|
||||||
|
var i, item, cells = row.getElementsByTagName('td');
|
||||||
|
for(i=0;item=cells[i];i++) {
|
||||||
|
if(details[this.headerMap[i]]) {
|
||||||
|
item.innerHTML = details[this.headerMap[i]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.createRecord(id, details, groupID);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
createRecord : function (id, details, groupId) {
|
||||||
|
var row = document.createElement('tr');
|
||||||
|
row.id = 'member-' + id;
|
||||||
|
var i, cell, cellField;
|
||||||
|
for(i=0;cellField=this.headerMap[i];i++) {
|
||||||
|
cell = document.createElement('td')
|
||||||
|
if(details[cellField]) {
|
||||||
|
cell.innerHTML = details[cellField];
|
||||||
|
}
|
||||||
|
row.appendChild(cell);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the delete icon
|
||||||
|
if(typeof groupId == 'undefined')
|
||||||
|
var groupId = $('Form_EditForm').elements.ID.value;
|
||||||
|
cell = document.createElement('td')
|
||||||
|
cell.innerHTML = '<a class="deletelink" href="admin/security/removememberfromgroup/' + groupId + '/' + id + '"><img src="cms/images/delete.gif" alt="delete" /></a>';
|
||||||
|
cell.getElementsByTagName('0');
|
||||||
|
row.appendChild(cell);
|
||||||
|
|
||||||
|
var tbody = this.getElementsByTagName('tbody')[0];
|
||||||
|
var addRow = document.getElementsByClassName('addrow',tbody)[0];
|
||||||
|
if(addRow) tbody.insertBefore(row, addRow);
|
||||||
|
else tbody.appendChild(row);
|
||||||
|
Behaviour.apply(row, true);
|
||||||
|
},
|
||||||
|
clearAddForm : function() {
|
||||||
|
var tbody = this.getElementsByTagName('tbody')[0];
|
||||||
|
var addRow = document.getElementsByClassName('addrow',tbody)[0];
|
||||||
|
if(addRow) {
|
||||||
|
var i,field,fields = addRow.getElementsByTagName('input');
|
||||||
|
for(i=0;field=fields[i];i++) {
|
||||||
|
if(field.type != 'hidden' && field.type != 'submit') field.value = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
removeMember : function(memberID) {
|
||||||
|
var record;
|
||||||
|
if(record = $('member-' + memberID)) {
|
||||||
|
record.parentNode.removeChild(record);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
MemberFilterButton = Class.create();
|
||||||
|
MemberFilterButton.applyTo('#Form_EditForm #MemberFilterButton');
|
||||||
|
MemberFilterButton.prototype = {
|
||||||
|
initialize: function() {
|
||||||
|
this.inputFields = new Array();
|
||||||
|
|
||||||
|
var childNodes = this.parentNode.getElementsByTagName('input');
|
||||||
|
|
||||||
|
for( var index = 0; index < childNodes.length; index++ ) {
|
||||||
|
if( childNodes[index].tagName ) {
|
||||||
|
childNodes[index].resetChanged = function() { return false; }
|
||||||
|
childNodes[index].isChanged = function() { return false; }
|
||||||
|
this.inputFields.push( childNodes[index] );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
childNodes = this.parentNode.getElementsByTagName('select');
|
||||||
|
|
||||||
|
for( var index = 0; index < childNodes.length; index++ ) {
|
||||||
|
if( childNodes[index].tagName ) {
|
||||||
|
childNodes[index].resetChanged = function() { return false; }
|
||||||
|
childNodes[index].field_changed = function() { return false; }
|
||||||
|
this.inputFields.push( childNodes[index] );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
isChanged: function() {
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
onclick: function(e) {
|
||||||
|
if(!$('ctf-ID') || !$('MemberFieldName')) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var updateURL = "";
|
||||||
|
updateURL += Event.findElement(e,"form").action;
|
||||||
|
// we can't set "fieldName" as a HiddenField because there might be multiple ComplexTableFields in a single EditForm-container
|
||||||
|
updateURL += "&fieldName="+$('MemberFieldName').value;
|
||||||
|
updateURL += "&action_callfieldmethod&&methodName=ajax_refresh&";
|
||||||
|
for( var index = 0; index < this.inputFields.length; index++ ) {
|
||||||
|
if( this.inputFields[index].tagName ) {
|
||||||
|
updateURL += this.inputFields[index].name + '=' + encodeURIComponent( this.inputFields[index].value ) + '&';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
updateURL += 'ajax=1';
|
||||||
|
|
||||||
|
new Ajax.Request( updateURL, {
|
||||||
|
onSuccess: Ajax.Evaluator,
|
||||||
|
onFailure: function( response ) {
|
||||||
|
errorMessage('Could not filter results: ' + response.responseText );
|
||||||
|
}.bind(this)
|
||||||
|
});
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// has to be external from initialize() because otherwise request will double on each reload - WTF
|
||||||
|
Behaviour.register({
|
||||||
|
'#Form_EditForm div.MemberTableField table.data input' : AjaxMemberLookup
|
||||||
|
});
|
||||||
|
|
||||||
|
/*
|
||||||
|
function reloadMemberTableField( groupID ) {
|
||||||
|
|
||||||
|
if( !groupID )
|
||||||
|
groupID = $('MemberBaseGroupID').value;
|
||||||
|
|
||||||
|
if($('MemberStart')) var listStart = $('MemberStart').value;
|
||||||
|
else var listStart = 0;
|
||||||
|
|
||||||
|
new Ajax.Request( 'admin/security/listmembers?&ajax=1&MemberBaseGroup=' + groupID + '&MemberStart=' + listStart, {
|
||||||
|
onSuccess: function( response ) {
|
||||||
|
$('MemberList').innerHTML = response.responseText;
|
||||||
|
// Behaviour.debug();
|
||||||
|
Behaviour.apply( $('MemberList') );
|
||||||
|
},
|
||||||
|
onFailure: function( response ) {
|
||||||
|
errorMessage('Could not filter results: ' + response.responseText );
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
*/
|
42
javascript/MemberTableField_popup.js
Executable file
@ -0,0 +1,42 @@
|
|||||||
|
MemberTableFieldPopupForm = Class.extend("ComplexTableFieldPopupForm");
|
||||||
|
MemberTableFieldPopupForm.prototype = {
|
||||||
|
initialize: function() {
|
||||||
|
this.ComplexTableFieldPopupForm.initialize();
|
||||||
|
|
||||||
|
Behaviour.register({
|
||||||
|
"form#MemberTableField_Popup_DetailForm input.action": {
|
||||||
|
onclick: this.submitForm.bind(this)
|
||||||
|
},
|
||||||
|
|
||||||
|
'form#MemberTableField_Popup_DetailForm input' : {
|
||||||
|
initialise : function() {
|
||||||
|
if(this.name == 'FirstName' || this.name == 'Surname' || this.name == 'Email') {
|
||||||
|
var div = document.createElement('div');
|
||||||
|
div.id = this.id + '_ac';
|
||||||
|
div.className = 'autocomplete';
|
||||||
|
this.parentNode.appendChild(div);
|
||||||
|
/*
|
||||||
|
new Ajax.Autocompleter(this.id, div.id, 'admin/security/autocomplete/' + this.name, {
|
||||||
|
afterUpdateElement : this.afterAutocomplete.bind(this)
|
||||||
|
});
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
},
|
||||||
|
afterAutocomplete : function(field, selectedItem) {
|
||||||
|
var data = selectedItem.getElementsByTagName('span')[1].innerHTML;
|
||||||
|
var items = data.split(",");
|
||||||
|
|
||||||
|
this.elements.FirstName.value = items[0];
|
||||||
|
this.elements.Surname.value = items[1];
|
||||||
|
this.elements.Email.value = items[2];
|
||||||
|
this.elements.Password.value = items[3];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//'form#MemberTableField_Popup_DetailForm input' : AjaxMemberLookup
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
MemberTableFieldPopupForm.applyTo('form#MemberTableField_Popup_DetailForm');
|
404
javascript/NewsletterAdmin_left.js
Executable file
@ -0,0 +1,404 @@
|
|||||||
|
if(typeof SiteTreeHandlers == 'undefined') SiteTreeHandlers = {};
|
||||||
|
SiteTreeHandlers.showNLDraft_url = 'admin/newsletter/shownewsletter/';
|
||||||
|
SiteTreeHandlers.showNLType_url = 'admin/newsletter/showtype/';
|
||||||
|
|
||||||
|
SiteTree.prototype = {
|
||||||
|
castAsTreeNode: function(li) {
|
||||||
|
behaveAs(li, SiteTreeNode, this.options);
|
||||||
|
},
|
||||||
|
|
||||||
|
getIdxOf : function(treeNode) {
|
||||||
|
if(treeNode && treeNode.id)
|
||||||
|
return treeNode.id;
|
||||||
|
},
|
||||||
|
|
||||||
|
getTreeNodeByIdx : function(idx) {
|
||||||
|
if(!idx) idx = "0";
|
||||||
|
return document.getElementById(idx);
|
||||||
|
},
|
||||||
|
|
||||||
|
initialise: function() {
|
||||||
|
this.observeMethod('SelectionChanged', this.changeCurrentTo);
|
||||||
|
},
|
||||||
|
|
||||||
|
createTreeNode : function(idx, title, pageType, secondIdx) {
|
||||||
|
var i;
|
||||||
|
var node = document.createElement('li');
|
||||||
|
node.className = pageType;
|
||||||
|
|
||||||
|
var aTag = document.createElement('a');
|
||||||
|
|
||||||
|
node.id = idx;
|
||||||
|
|
||||||
|
if( pageType == 'Draft' ) {
|
||||||
|
aTag.href = SiteTreeHandlers.showNLDraft_url + idx;
|
||||||
|
} else {
|
||||||
|
aTag.href = SiteTreeHandlers.showNLType_url + idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
aTag.innerHTML = title;
|
||||||
|
node.appendChild(aTag);
|
||||||
|
|
||||||
|
SiteTreeNode.create(node, this.options);
|
||||||
|
|
||||||
|
return node;
|
||||||
|
},
|
||||||
|
|
||||||
|
addDraftNode: function( title, parentID, draftID ) {
|
||||||
|
var draftNode = this.createTreeNode( 'draft_' + parentID + '_' + draftID, title, 'Draft', parentID );
|
||||||
|
this.getTreeNodeByIdx('drafts_' + parentID).appendTreeNode( draftNode );
|
||||||
|
this.changeCurrentTo( draftNode );
|
||||||
|
},
|
||||||
|
|
||||||
|
addTypeNode: function( title, parentID ) {
|
||||||
|
var typeNode = this.createTreeNode( 'mailtype_' + parentID, title, 'MailType' );
|
||||||
|
var draftsNode = this.createTreeNode( 'drafts_' + parentID, 'Drafts', 'DraftFolder' );
|
||||||
|
var sentItemsNode = this.createTreeNode( 'sent_' + parentID, 'Sent Items', 'SentFolder' );
|
||||||
|
var mailingListNode = this.createTreeNode( 'recipients_' + parentID, 'Mailing List', 'Recipients' );
|
||||||
|
typeNode.appendTreeNode( draftsNode );
|
||||||
|
Element.addClassName( draftsNode, 'nodelete');
|
||||||
|
typeNode.appendTreeNode( sentItemsNode );
|
||||||
|
Element.addClassName(sentItemsNode,'nodelete');
|
||||||
|
typeNode.appendTreeNode( mailingListNode );
|
||||||
|
Element.addClassName(mailingListNode,'nodelete');
|
||||||
|
this.appendTreeNode( typeNode );
|
||||||
|
this.changeCurrentTo( typeNode );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete pages action
|
||||||
|
*/
|
||||||
|
deletedraft = {
|
||||||
|
button_onclick : function() {
|
||||||
|
if(treeactions.toggleSelection(this)) {
|
||||||
|
deletedraft.o1 = $('sitetree').observeMethod('SelectionChanged', deletedraft.treeSelectionChanged);
|
||||||
|
deletedraft.o2 = $('deletedrafts_options').observeMethod('Close', deletedraft.popupClosed);
|
||||||
|
addClass($('sitetree'),'multiselect');
|
||||||
|
|
||||||
|
var sel = $('sitetree').firstSelected();
|
||||||
|
|
||||||
|
if(sel) {
|
||||||
|
var selIdx = $('sitetree').getIdxOf(sel);
|
||||||
|
deletedraft.selectedNodes = { };
|
||||||
|
deletedraft.selectedNodes[selIdx] = true;
|
||||||
|
sel.removeNodeClass('current');
|
||||||
|
sel.addNodeClass('selected');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
treeSelectionChanged : function(selectedNode) {
|
||||||
|
var idx = $('sitetree').getIdxOf(selectedNode);
|
||||||
|
|
||||||
|
if(selectedNode.selected) {
|
||||||
|
selectedNode.removeNodeClass('selected');
|
||||||
|
selectedNode.selected = false;
|
||||||
|
deletedraft.selectedNodes[idx] = false;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
selectedNode.addNodeClass('selected');
|
||||||
|
selectedNode.selected = true;
|
||||||
|
deletedraft.selectedNodes[idx] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
popupClosed : function() {
|
||||||
|
removeClass($('sitetree'),'multiselect');
|
||||||
|
$('sitetree').stopObserving(deletedraft.o1);
|
||||||
|
$('deletedrafts_options').stopObserving(deletedraft.o2);
|
||||||
|
|
||||||
|
for(var idx in deletedraft.selectedNodes) {
|
||||||
|
if(deletedraft.selectedNodes[idx]) {
|
||||||
|
node = $('sitetree').getTreeNodeByIdx(idx);
|
||||||
|
if(node) {
|
||||||
|
node.removeNodeClass('selected');
|
||||||
|
node.selected = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
form_submit : function() {
|
||||||
|
var csvIDs = "";
|
||||||
|
for(var idx in deletedraft.selectedNodes) {
|
||||||
|
if(deletedraft.selectedNodes[idx]) csvIDs += (csvIDs ? "," : "") + idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(csvIDs) {
|
||||||
|
$('deletedrafts_options').elements.csvIDs.value = csvIDs;
|
||||||
|
|
||||||
|
Ajax.SubmitForm('deletedrafts_options', null, {
|
||||||
|
onSuccess : function(response) {
|
||||||
|
Ajax.Evaluator(response);
|
||||||
|
|
||||||
|
var sel;
|
||||||
|
if((sel = $('sitetree').selected) && sel.parentNode) sel.addNodeClass('current');
|
||||||
|
// else $('Form_EditForm').innerHTML = "";
|
||||||
|
|
||||||
|
treeactions.closeSelection($('deletedrafts'));
|
||||||
|
},
|
||||||
|
onFailure : function(response) {
|
||||||
|
errorMessage('Error deleting drafts', response);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$('deletedrafts').getElementsByTagName('a')[0].onclick();
|
||||||
|
|
||||||
|
} else {
|
||||||
|
alert("Please select at least 1 page.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
treeSelectionChanged : function(selectedNode) {
|
||||||
|
var idx = $('sitetree').getIdxOf(selectedNode);
|
||||||
|
|
||||||
|
if( !deletedraft.selectedNodes )
|
||||||
|
deletedraft.selectedNodes = {};
|
||||||
|
|
||||||
|
if(selectedNode.className.indexOf('nodelete') == -1) {
|
||||||
|
if(selectedNode.selected) {
|
||||||
|
selectedNode.removeNodeClass('selected');
|
||||||
|
selectedNode.selected = false;
|
||||||
|
deletedraft.selectedNodes[idx] = false;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
selectedNode.addNodeClass('selected');
|
||||||
|
selectedNode.selected = true;
|
||||||
|
deletedraft.selectedNodes[idx] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SiteTreeNode.prototype.onselect = function() {
|
||||||
|
$('sitetree').changeCurrentTo(this);
|
||||||
|
if($('sitetree').notify('SelectionChanged', this)) {
|
||||||
|
if(typeof save != 'undefined'){
|
||||||
|
autoSave(true, this.getPageFromServer.bind(this));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.getPageFromServer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
SiteTreeNode.prototype.getPageFromServer = function() {
|
||||||
|
|
||||||
|
var match = this.id.match(/(mailtype|drafts|draft|sent|recipients)_([\d]+)$/);
|
||||||
|
var openTabName = null;
|
||||||
|
var currentPageID = null;
|
||||||
|
|
||||||
|
if( $('Form_EditForm_ID') )
|
||||||
|
currentPageID = $('Form_EditForm_ID').value;
|
||||||
|
|
||||||
|
var newPageID = null;
|
||||||
|
var otherID = null;
|
||||||
|
var type = null;
|
||||||
|
|
||||||
|
var forceReload = $('Form_EditForm_Type') && $('Form_EditForm_Type').value == 'Newsletter';
|
||||||
|
|
||||||
|
if( match ) {
|
||||||
|
|
||||||
|
// open the appropriate tab
|
||||||
|
switch( match[1] ) {
|
||||||
|
case 'recipients':
|
||||||
|
openTabName = 'Root_Recipients_set_Recipients';
|
||||||
|
break;
|
||||||
|
case 'drafts':
|
||||||
|
openTabName = 'Root_Drafts';
|
||||||
|
break;
|
||||||
|
case 'sent':
|
||||||
|
openTabName = 'Root_Sent_set_Sent';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
newPageID = match[2];
|
||||||
|
type = match[1];
|
||||||
|
} else if(this.id.match(/(mailtype|drafts|draft|sent|recipients)_([\d]+)_([\d]+)$/)) {
|
||||||
|
newPageID = RegExp.$2;
|
||||||
|
type = RegExp.$1;
|
||||||
|
otherID = RegExp.$3;
|
||||||
|
forceReload = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( forceReload || currentPageID != newPageID )
|
||||||
|
$('Form_EditForm').getPageFromServer(newPageID, type, otherID, openTabName);
|
||||||
|
else
|
||||||
|
openTab( openTabName );
|
||||||
|
};
|
||||||
|
|
||||||
|
function draft_sent_ok( newsletterID, draftID ) {
|
||||||
|
var draftsListNode = $('drafts_' + newsletterID);
|
||||||
|
var sentListNode = $('sent_' + newsletterID);
|
||||||
|
var draftNode = $('draft_' + newsletterID + '_' + draftID );
|
||||||
|
|
||||||
|
draftsListNode.removeTreeNode( draftNode );
|
||||||
|
draftNode.id = 'sent_' + newsletterID + '_' + draftID;
|
||||||
|
sentListNode.appendTreeNode( draftNode, null );
|
||||||
|
statusMessage('Sent email to mailing list', 'good');
|
||||||
|
}
|
||||||
|
|
||||||
|
function resent_ok( newsletterID, sentID ) {
|
||||||
|
statusMessage('Resent email to mailing list', 'good');
|
||||||
|
}
|
||||||
|
|
||||||
|
function reloadSiteTree() {
|
||||||
|
|
||||||
|
new Ajax.Request( 'admin/newsletter/getsitetree', {
|
||||||
|
method: get,
|
||||||
|
onSuccess: function( response ) {
|
||||||
|
$('sitetree_holder').innerHTML = response.responseText;
|
||||||
|
},
|
||||||
|
onFailure: function( response ) {
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
_HANDLER_FORMS['addtype'] = 'addtype_options';
|
||||||
|
_HANDLER_FORMS['deletedrafts'] = 'deletedrafts_options';
|
||||||
|
|
||||||
|
AddForm = Class.extend('addpage');
|
||||||
|
AddForm.applyTo('#addtype');
|
||||||
|
AddForm.prototype = {
|
||||||
|
initialize: function () {
|
||||||
|
Observable.applyTo($(_HANDLER_FORMS[this.id]));
|
||||||
|
this.getElementsByTagName('a')[0].onclick = returnFalse;
|
||||||
|
$(_HANDLER_FORMS[this.id]).onsubmit = this.form_submit;
|
||||||
|
},
|
||||||
|
|
||||||
|
form_submit : function() {
|
||||||
|
var st = $('sitetree');
|
||||||
|
|
||||||
|
if( st.selected.length )
|
||||||
|
selectedNode = st.selected[0];
|
||||||
|
else
|
||||||
|
selectedNode = st.selected;
|
||||||
|
|
||||||
|
var parentID = null;
|
||||||
|
|
||||||
|
while( selectedNode && !parentID ) {
|
||||||
|
if( selectedNode && selectedNode.id && selectedNode.id.match(/mailtype_([0-9a-z\-]+)$/) )
|
||||||
|
parentID = RegExp.$1;
|
||||||
|
else
|
||||||
|
selectedNode = selectedNode.parentNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(parentID && parentID.substr(0,3) == 'new') {
|
||||||
|
alert("You have to save a page before adding children underneath it");
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
this.elements.ParentID.value = parentID;
|
||||||
|
|
||||||
|
var type = 'draft';
|
||||||
|
var selectIDPrefix = 'draft_' + parentID + '_';
|
||||||
|
|
||||||
|
if( $('add_type').value == 'type' ) {
|
||||||
|
type = 'type';
|
||||||
|
selectIDPrefix = 'mailtype_';
|
||||||
|
}
|
||||||
|
|
||||||
|
var request = new Ajax.Request( this.action + '?ajax=1&PageType=' + type + '&ParentID=' + parentID, {
|
||||||
|
method: 'get',
|
||||||
|
asynchronous: true,
|
||||||
|
onSuccess : function( response ) {
|
||||||
|
|
||||||
|
$('Form_EditForm').loadNewPage(response.responseText);
|
||||||
|
|
||||||
|
// create a new node and add it to the site tree
|
||||||
|
if( type == 'draft' ) {
|
||||||
|
$('sitetree').addDraftNode('New draft newsletter', parentID, $('Form_EditForm_ID').value );
|
||||||
|
} else {
|
||||||
|
$('sitetree').addTypeNode('New newsletter type', $('Form_EditForm_ID').value );
|
||||||
|
}
|
||||||
|
|
||||||
|
statusMessage('Added new ' + type);
|
||||||
|
},
|
||||||
|
|
||||||
|
onFailure : function(response) {
|
||||||
|
alert(response.responseText);
|
||||||
|
statusMessage('Could not add new ' + type );
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
reloadSiteTree: function( response ) {
|
||||||
|
statusMessage('Added new newsletter type', 'good' );
|
||||||
|
$('sitetree_holder').innerHTML = response.responseText;
|
||||||
|
Behaviour.apply( $('sitetree_holder') );
|
||||||
|
},
|
||||||
|
|
||||||
|
button_onclick : function() {
|
||||||
|
if(treeactions.toggleSelection(this)) {
|
||||||
|
var selectedNode = $('sitetree').firstSelected();
|
||||||
|
if(selectedNode) {
|
||||||
|
while(selectedNode.parentTreeNode && !selectedNode.hints.defaultChild) {
|
||||||
|
$('sitetree').changeCurrentTo(selectedNode.parentTreeNode);
|
||||||
|
selectedNode = selectedNode.parentTreeNode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.o1 = $('sitetree').observeMethod('SelectionChanged', this.treeSelectionChanged.bind(this));
|
||||||
|
this.o2 = $(_HANDLER_FORMS[this.id]).observeMethod('Close', this.popupClosed.bind(this));
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
treeSelectionChanged: function( treeNode ) {
|
||||||
|
this.selected = treeNode;
|
||||||
|
},
|
||||||
|
|
||||||
|
popupClosed: function() {
|
||||||
|
$('sitetree').stopObserving(this.o1);
|
||||||
|
$(_HANDLER_FORMS.addtype).stopObserving(this.o2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeTreeNodeByIdx( tree, nodeID ) {
|
||||||
|
var node = tree.getTreeNodeByIdx( nodeID );
|
||||||
|
if( node )
|
||||||
|
if( node.parentTreeNode )
|
||||||
|
node.parentTreeNode.removeTreeNode( node );
|
||||||
|
else
|
||||||
|
$('sitetree').removeTreeNode( node );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialisation function to set everything up
|
||||||
|
*/
|
||||||
|
Behaviour.addLoader(function () {
|
||||||
|
// Set up add draft
|
||||||
|
Observable.applyTo($('addtype_options'));
|
||||||
|
|
||||||
|
if( $('addtype') ) {
|
||||||
|
if( AddForm.button_click )
|
||||||
|
$('addtype').getElementsByTagName('a')[0].onclick = function() {return false;};
|
||||||
|
if( AddForm.button_click )
|
||||||
|
$('addtype_options').onsubmit = AddForm.form_submit;
|
||||||
|
}
|
||||||
|
// Set up delete drafts
|
||||||
|
Observable.applyTo($('deletedrafts_options'));
|
||||||
|
|
||||||
|
var deleteDrafts = $('deletedrafts');
|
||||||
|
|
||||||
|
if( deleteDrafts ) {
|
||||||
|
deleteDrafts.onclick = deletedraft.button_onclick;
|
||||||
|
deleteDrafts.getElementsByTagName('a')[0].onclick = function() {return false;};
|
||||||
|
$('deletedrafts_options').onsubmit = deletedraft.form_submit;
|
||||||
|
}
|
||||||
|
});
|
536
javascript/NewsletterAdmin_right.js
Executable file
@ -0,0 +1,536 @@
|
|||||||
|
function action_send_right() {
|
||||||
|
$('action_send_options').toggle();
|
||||||
|
}
|
||||||
|
|
||||||
|
CMSForm.applyTo('#Form_MemberForm','rightbottom');
|
||||||
|
|
||||||
|
Behaviour.register({
|
||||||
|
'#Form_EditForm' : {
|
||||||
|
initialise : function() {
|
||||||
|
this.openTab = null;
|
||||||
|
this.prepareForm();
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Processing called whenever a page is loaded in the right - including the initial one
|
||||||
|
*/
|
||||||
|
prepareForm : function() {
|
||||||
|
ajaxActionsAtTop('Form_EditForm', 'form_actions', 'right');
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request a page from the server via Ajax
|
||||||
|
*/
|
||||||
|
getPageFromServer : function(id, type, otherid,openTabName) {
|
||||||
|
|
||||||
|
this.openTab = openTabName;
|
||||||
|
|
||||||
|
if(id && parseInt(id) == id) {
|
||||||
|
this.receivingID = id;
|
||||||
|
|
||||||
|
// Treenode might not exist if that part of the tree is closed
|
||||||
|
var treeNode = $('sitetree').getTreeNodeByIdx(id);
|
||||||
|
if(treeNode) treeNode.addNodeClass('loading');
|
||||||
|
statusMessage("loading...");
|
||||||
|
|
||||||
|
var requestURL = 'admin/newsletter/showtype/' + id;
|
||||||
|
|
||||||
|
if( otherid )
|
||||||
|
requestURL = 'admin/newsletter/shownewsletter/' + otherid;
|
||||||
|
|
||||||
|
new Ajax.Request(requestURL, {
|
||||||
|
asynchronous : true,
|
||||||
|
method : 'post',
|
||||||
|
postBody : /*'ID=' + id + 'type=' + type + */'ajax=1'+ (otherid?('&otherid='+otherid):''),
|
||||||
|
onSuccess : this.successfullyReceivedPage.bind(this),
|
||||||
|
onFailure : function(response) {
|
||||||
|
errorMessage('error loading page',response);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
throw("getPageFromServer: Bad page ID: " + id);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
successfullyReceivedPage : function(response) {
|
||||||
|
this.loadNewPage(response.responseText);
|
||||||
|
|
||||||
|
// Treenode might not exist if that part of the tree is closed
|
||||||
|
var treeNode = $('sitetree').getTreeNodeByIdx(this.receivingID);
|
||||||
|
if(treeNode) {
|
||||||
|
$('sitetree').changeCurrentTo(treeNode);
|
||||||
|
treeNode.removeNodeClass('loading');
|
||||||
|
}
|
||||||
|
statusMessage('');
|
||||||
|
|
||||||
|
onload_init_tabstrip();
|
||||||
|
|
||||||
|
if( this.openTab ) {
|
||||||
|
openTab( this.openTab );
|
||||||
|
this.openTab = null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
didntReceivePage : function(response) {
|
||||||
|
errorMessage('error loading page', response);
|
||||||
|
$('sitetree').getTreeNodeByIdx(this.elements.ID.value).removeNodeClass('loading');
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load a new page into the right-hand form
|
||||||
|
*/
|
||||||
|
loadNewPage : function(formContent) {
|
||||||
|
rightHTML = formContent;
|
||||||
|
rightHTML = rightHTML.replace(/href *= *"#/g, 'href="' + window.location.href.replace(/#.*$/,'') + '#');
|
||||||
|
|
||||||
|
// Prepare iframes for removal, otherwise we get loading bugs
|
||||||
|
var i, allIframes = this.getElementsByTagName('iframe');
|
||||||
|
if(allIframes) for(i=0;i<allIframes.length;i++) {
|
||||||
|
allIframes[i].contentWindow.location.href = 'about:blank';
|
||||||
|
allIframes[i].parentNode.removeChild(allIframes[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.innerHTML = rightHTML;
|
||||||
|
|
||||||
|
allIframes = this.getElementsByTagName('iframe');
|
||||||
|
if(allIframes) for(i=0;i<allIframes.length;i++) {
|
||||||
|
try {
|
||||||
|
allIframes[i].contentWindow.location.href = allIframes[i].src;
|
||||||
|
} catch(er) {alert(er.message);}
|
||||||
|
}
|
||||||
|
|
||||||
|
_TAB_DIVS_ON_PAGE = [];
|
||||||
|
|
||||||
|
try {
|
||||||
|
var tabs = document.getElementsBySelector('#Form_EditForm ul.tabstrip');
|
||||||
|
} catch(er) {/* alert('a: '+ er.message + '\n' + er.line);*/ }
|
||||||
|
try {
|
||||||
|
for(var i=0;i<tabs.length;i++) if(tabs[i].tagName) initTabstrip(tabs[i]);
|
||||||
|
} catch(er) { /*alert('b: '+ er.message + '\n' + er.line); */}
|
||||||
|
|
||||||
|
if((typeof tinyMCE != 'undefined') && tinyMCE.instances) {
|
||||||
|
tinyMCE.instances = [];
|
||||||
|
tinyMCE.isLoaded = false;
|
||||||
|
tinyMCE.onLoad();
|
||||||
|
}
|
||||||
|
|
||||||
|
onload_init_tabstrip();
|
||||||
|
|
||||||
|
// if(this.prepareForm) this.prepareForm();
|
||||||
|
Behaviour.apply($('Form_EditForm'));
|
||||||
|
if(this.prepareForm)
|
||||||
|
this.prepareForm();
|
||||||
|
|
||||||
|
this.resetElements();
|
||||||
|
|
||||||
|
window.ontabschanged();
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save the contens of the form, by submitting it and resetting is changed checker
|
||||||
|
* on success.
|
||||||
|
*/
|
||||||
|
save: function(ifChanged, callAfter) {
|
||||||
|
|
||||||
|
_AJAX_LOADING = true;
|
||||||
|
if(typeof tinyMCE != 'undefined') tinyMCE.triggerSave();
|
||||||
|
|
||||||
|
var __callAfter = callAfter;
|
||||||
|
var __form = this;
|
||||||
|
|
||||||
|
if(__form.notify) __form.notify('BeforeSave', __form.elements.ID.value);
|
||||||
|
|
||||||
|
var success = function(response) {
|
||||||
|
|
||||||
|
Ajax.Evaluator(response);
|
||||||
|
__form.resetElements();
|
||||||
|
|
||||||
|
if(__callAfter) __callAfter();
|
||||||
|
if(__form.notify) __form.notify('PageSaved', __form.elements.ID.value);
|
||||||
|
_AJAX_LOADING = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var action = 'action_save';
|
||||||
|
|
||||||
|
if( $('Form_NewsletterEditForm_Type') && $('Form_NewsletterEditForm_Type').value == 'Newsletter' )
|
||||||
|
action += 'newsletter';
|
||||||
|
|
||||||
|
if(ifChanged) {
|
||||||
|
var data = this.serializeChangedFields('ID') + '&ajax=1&' + action + '=1';
|
||||||
|
} else {
|
||||||
|
var data = this.serializeAllFields() + '&ajax=1&' + action + '=1';
|
||||||
|
}
|
||||||
|
|
||||||
|
// alert(this.action + '\n\n' + data);
|
||||||
|
|
||||||
|
new Ajax.Request(this.action, {
|
||||||
|
method : this.method,
|
||||||
|
postBody: data,
|
||||||
|
onSuccess : success,
|
||||||
|
onFailure : function(response) {
|
||||||
|
alert(response.responseText);
|
||||||
|
errorMessage('Error saving content', response);
|
||||||
|
_AJAX_LOADING = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Behaviour.register({
|
||||||
|
'#action_send_options' : {
|
||||||
|
/**
|
||||||
|
* Open the form
|
||||||
|
*/
|
||||||
|
open : function() {
|
||||||
|
this.elements.NewsletterID.value = $('Form_EditForm').elements.ID.value;
|
||||||
|
|
||||||
|
var allInputs = this.getElementsByTagName('input');
|
||||||
|
this.submitButton = allInputs[allInputs.length-1];
|
||||||
|
this.submitButton.onclick = this.send.bind(this);
|
||||||
|
this.style.display = '';
|
||||||
|
},
|
||||||
|
close : function() {
|
||||||
|
this.style.display = 'none';
|
||||||
|
},
|
||||||
|
toggle : function() {
|
||||||
|
if(this.style.display == '') this.close();
|
||||||
|
else this.open();
|
||||||
|
},
|
||||||
|
|
||||||
|
send_test: function() {
|
||||||
|
// Show a "submitting..." box
|
||||||
|
if(!this.sendingText) {
|
||||||
|
this.sendingText = document.createElement('div');
|
||||||
|
this.sendingText.innerHTML = 'Sending newsletter...';
|
||||||
|
this.sendingText.className = 'sendingText';
|
||||||
|
Element.setOpacity(this.sendingText, 0.9);
|
||||||
|
this.appendChild(this.sendingText);
|
||||||
|
}
|
||||||
|
this.sendingText.style.display = '';
|
||||||
|
|
||||||
|
var form = $('Form_EditForm');
|
||||||
|
|
||||||
|
if( form.isChanged() )
|
||||||
|
form.save();
|
||||||
|
|
||||||
|
// Send the request
|
||||||
|
ajaxSubmitForm(false, this.onCompleteTest.bind(this), this, '', 'sendnewsletter')
|
||||||
|
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
send: function() {
|
||||||
|
if( $('SendTypeList').checked )
|
||||||
|
return this.send_to_list();
|
||||||
|
else
|
||||||
|
return this.send_test();
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Submit the option form and carry out the action
|
||||||
|
*/
|
||||||
|
send_to_list : function() {
|
||||||
|
// Show a "submitting..." box
|
||||||
|
/*if(!this.sendingText) {
|
||||||
|
this.sendingText = document.createElement('div');
|
||||||
|
this.sendingText.innerHTML = 'Sending newsletter...';
|
||||||
|
this.sendingText.className = 'sendingText';
|
||||||
|
Element.setOpacity(this.sendingText, 0.9);
|
||||||
|
this.appendChild(this.sendingText);
|
||||||
|
}
|
||||||
|
this.sendingText.style.display = '';*/
|
||||||
|
|
||||||
|
var form = $('Form_EditForm');
|
||||||
|
|
||||||
|
if( form.isChanged() )
|
||||||
|
form.save();
|
||||||
|
|
||||||
|
|
||||||
|
if( $('SendProgressBar') )
|
||||||
|
$('SendProgressBar').start();
|
||||||
|
|
||||||
|
// Send the request
|
||||||
|
Ajax.SubmitForm( $('Form_EditForm'), 'action_sendnewsletter', {
|
||||||
|
extraData: '&SendType=List',
|
||||||
|
onSuccess: this.incrementProcess.bind(this),
|
||||||
|
onFailure: function(response) {
|
||||||
|
statusMessage(response.responseText);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
/*var form = $('Form_EditForm');
|
||||||
|
var data = form.serializeChangedFields('ID','type') + '&ajax=1&action_savenewsletter=1&SendType=List';
|
||||||
|
|
||||||
|
new Ajax.Request(form.action, {
|
||||||
|
method : form.method,
|
||||||
|
postBody: data,
|
||||||
|
onSuccess : this.incrementProcess.bind(this),
|
||||||
|
onFailure : function(response) {
|
||||||
|
errorMessage('Error sending to mailing list', response);
|
||||||
|
}
|
||||||
|
});*/
|
||||||
|
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
incrementProcess: function( response ) {
|
||||||
|
var processParts = response.responseText.match( /(\d+):(\d+)\/(\d+)/ );
|
||||||
|
|
||||||
|
if( !processParts || parseInt( processParts[2] ) >= parseInt( processParts[3] ) ) {
|
||||||
|
this.onComplete( response );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// update the progress bar
|
||||||
|
$('SendProgressBar').setProgress( ( parseInt( processParts[2] ) / parseInt( processParts[3] ) ) * 100 );
|
||||||
|
var estimate = $('SendProgressBar').estimateTime();
|
||||||
|
$('SendProgressBar').setText( 'Sent ' + processParts[2] + ' of ' + processParts[3] + '. ' + estimate + ' remaining...' );
|
||||||
|
|
||||||
|
// set the action to the batch process controller
|
||||||
|
var updateRequest = baseHref() + 'processes/next/' + processParts[1] + '/10?ajax=1';
|
||||||
|
|
||||||
|
var request = new Ajax.Request( updateRequest, {
|
||||||
|
onSuccess: this.incrementProcess.bind(this),
|
||||||
|
onFailure: function( response ) {
|
||||||
|
errorMessage( response.responseText );
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process the action's Ajax response
|
||||||
|
*/
|
||||||
|
onComplete: function( response ) {
|
||||||
|
// $('SendProgressBar').setProgress( 100 );
|
||||||
|
// $('SendProgressBar').setText( 'Done!' );
|
||||||
|
$('SendProgressBar').reset();
|
||||||
|
// this.elements.Message.value = '';
|
||||||
|
this.close();
|
||||||
|
if( response )
|
||||||
|
Ajax.Evaluator(response);
|
||||||
|
},
|
||||||
|
|
||||||
|
onCompleteTest: function( response ) {
|
||||||
|
// this.sendingText.innerHTML = '';
|
||||||
|
if( this.sendingText.parentNode == this )
|
||||||
|
this.removeChild( this.sendingText );
|
||||||
|
this.close();
|
||||||
|
if( response )
|
||||||
|
Ajax.Evaluator(response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
function save(ifChanged, callAfter, confirmation) {
|
||||||
|
tinyMCE.triggerSave(true);
|
||||||
|
|
||||||
|
var form = $('Form_EditForm');
|
||||||
|
|
||||||
|
var alreadySaved = false;
|
||||||
|
|
||||||
|
if(ifChanged) {
|
||||||
|
if(form.elements.length < 2) alreadySaved = true;
|
||||||
|
else alreadySaved = !form.isChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(alreadySaved && ifChanged) { // pressing save always saves
|
||||||
|
if(!ifChanged && !confirmation) {
|
||||||
|
statusMessage("There's nothing to save");
|
||||||
|
}
|
||||||
|
|
||||||
|
if(callAfter) callAfter();
|
||||||
|
|
||||||
|
} else {
|
||||||
|
var options = {
|
||||||
|
save: (function() {
|
||||||
|
statusMessage('saving...', '', true);
|
||||||
|
|
||||||
|
var success = function(response) {
|
||||||
|
Ajax.Evaluator(response);
|
||||||
|
if(this.callAfter) this.callAfter();
|
||||||
|
}
|
||||||
|
if(callAfter) success = success.bind(this);
|
||||||
|
|
||||||
|
if( $('Form_EditForm_Type').value == 'Newsletter' )
|
||||||
|
var data = form.serializeChangedFields('ID','type') + '&ajax=1&action_savenewsletter=1';
|
||||||
|
else
|
||||||
|
var data = form.serializeChangedFields('ID','type') + '&ajax=1&action_save=1';
|
||||||
|
|
||||||
|
new Ajax.Request(form.action, {
|
||||||
|
method : form.method,
|
||||||
|
postBody: data,
|
||||||
|
onSuccess : success,
|
||||||
|
onFailure : function(response) {
|
||||||
|
errorMessage('Error saving content', response);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}).bind({callAfter : callAfter}),
|
||||||
|
|
||||||
|
discard: callAfter,
|
||||||
|
cancel: function() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(confirmation) doYouWantToSave(options);
|
||||||
|
else options.save();
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Behaviour.register({
|
||||||
|
/**
|
||||||
|
* When the iframe has loaded, apply the listeners
|
||||||
|
*/
|
||||||
|
'div#ImportFile' : {
|
||||||
|
frameLoaded: function( doc ) {
|
||||||
|
this.showTable = true;
|
||||||
|
var fileField = doc.getElementsByTagName('input')[0];
|
||||||
|
var idField = doc.getElementsByTagName('input')[1];
|
||||||
|
|
||||||
|
idField.value = $('Form_EditForm_ID').value;
|
||||||
|
|
||||||
|
fileField.onchange = this.selectedFile.bind(this);
|
||||||
|
},
|
||||||
|
loadTable: function( doc ) {
|
||||||
|
this.innerHTML = doc.getElementsByTagName('body')[0].innerHTML;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
NewsletterList = Class.create();
|
||||||
|
NewsletterList.applyTo('table.NewsletterList');
|
||||||
|
NewsletterList.prototype = {
|
||||||
|
initialize: function() {
|
||||||
|
this.tableBody = this.getElementsByTagName('tbody')[0];
|
||||||
|
this.deleteLinks = this.getElementsByTagName('a');
|
||||||
|
|
||||||
|
for( var i = 0; i < this.deleteLinks.length; i++ ) {
|
||||||
|
this.deleteLinks[i].onclick = this.deleteNewsletter.bindAsEventListener(this);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
deleteNewsletter: function( event ) {
|
||||||
|
var link = event.target;
|
||||||
|
|
||||||
|
if( event.srcElement )
|
||||||
|
link = event.srcElement;
|
||||||
|
|
||||||
|
var rowNode = link.parentNode.parentNode;
|
||||||
|
|
||||||
|
new Ajax.Request( link.href, {
|
||||||
|
onSuccess: this.removeRow,
|
||||||
|
onFailure: function( response ) {
|
||||||
|
alert('The newsletter could not be deleted');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
removeRow: function( response ) {
|
||||||
|
this.tableBody.removeChild( $(response.responseText) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add page action
|
||||||
|
* @todo Remove duplication between this and the CMSMain Add page action
|
||||||
|
*/
|
||||||
|
Behaviour.register( {
|
||||||
|
'#Form_EditForm_action_save': {
|
||||||
|
onclick : function() {
|
||||||
|
$('Form_EditForm').save();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle auto-saving. Detects if changes have been made, and if so save everything on the page.
|
||||||
|
* If confirmation is true it will ask for confirmation.
|
||||||
|
*/
|
||||||
|
function autoSave(confirmation, callAfter) {
|
||||||
|
if(typeof tinyMCE != 'undefined') tinyMCE.triggerSave();
|
||||||
|
|
||||||
|
var __forms = []
|
||||||
|
if($('Form_EditForm')) __forms.push($('Form_EditForm'));
|
||||||
|
if($('Form_SubForm')) __forms.push($('Form_SubForm'));
|
||||||
|
if($('Form_MemberForm')) __forms.push($('Form_MemberForm'));
|
||||||
|
|
||||||
|
var __somethingHasChanged = false;
|
||||||
|
var __callAfter = callAfter;
|
||||||
|
|
||||||
|
__forms.each(function(form) {
|
||||||
|
if(form.isChanged && form.isChanged()) {
|
||||||
|
__somethingHasChanged = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if(__somethingHasChanged) {
|
||||||
|
var options = {
|
||||||
|
save: function() {
|
||||||
|
statusMessage('saving...', '', true);
|
||||||
|
var i;
|
||||||
|
for(i=0;i<__forms.length;i++) {
|
||||||
|
if(__forms[i].isChanged && __forms[i].isChanged()) {
|
||||||
|
if(i == 0) __forms[i].save(true, __callAfter);
|
||||||
|
else __forms[i].save(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
discard: function() {
|
||||||
|
__forms.each(function(form) { form.resetElements(false); });
|
||||||
|
if(__callAfter) __callAfter();
|
||||||
|
},
|
||||||
|
cancel: function() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(confirmation) doYouWantToSave(options);
|
||||||
|
else options.save();
|
||||||
|
|
||||||
|
} else {
|
||||||
|
if(__callAfter) __callAfter();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function reloadRecipientsList() {
|
||||||
|
|
||||||
|
var id = $('Form_EditForm_ID').value;
|
||||||
|
|
||||||
|
var request = new Ajax.Request( 'admin/newsletter/getrecipientslist/' + id + '?ajax=1', {
|
||||||
|
onSuccess: function( response ) {
|
||||||
|
$('MemberList').outerHTML = response.responseText;
|
||||||
|
Behaviour.apply( $('MemberList') );
|
||||||
|
},
|
||||||
|
onFailure: function(response) {
|
||||||
|
statusMessage('Could not automatically refresh recipients list', 'bad');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/*RecipientImportField = Class.create();
|
||||||
|
RecipientImportField.applyTo('iframe.RecipientImportField');
|
||||||
|
RecipientImportField.prototype = {
|
||||||
|
initialize: function() {
|
||||||
|
this.src = document.getElementsByTagName('base')[0].href + this.src;
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We don't want hitting the enter key in the subject field
|
||||||
|
* to submit the form.
|
||||||
|
*/
|
||||||
|
Behaviour.register({
|
||||||
|
'#Form_EditForm_Subject' : {
|
||||||
|
onkeypress : function(event) {
|
||||||
|
event = (event) ? event : window.event;
|
||||||
|
var kc = event.keyCode ? event.keyCode : event.charCode;
|
||||||
|
if(kc == 13) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
172
javascript/NewsletterMemberList.js
Executable file
@ -0,0 +1,172 @@
|
|||||||
|
/**
|
||||||
|
* Auto-lookup on ajax fields
|
||||||
|
*/
|
||||||
|
AjaxMemberLookup = {
|
||||||
|
initialise : function() {
|
||||||
|
var div = document.createElement('div');
|
||||||
|
div.id = this.id + '_ac';
|
||||||
|
div.className = 'autocomplete';
|
||||||
|
this.parentNode.appendChild(div);
|
||||||
|
new Ajax.Autocompleter(this.id, div.id, 'admin/security/autocomplete/' + this.name, {
|
||||||
|
afterUpdateElement : this.afterAutocomplete.bind(this)
|
||||||
|
});
|
||||||
|
},
|
||||||
|
afterAutocomplete : function(field, selectedItem) {
|
||||||
|
var data = selectedItem.getElementsByTagName('span')[1].innerHTML;
|
||||||
|
var items = data.split(",");
|
||||||
|
form = Element.ancestorOfType(field, 'form');
|
||||||
|
|
||||||
|
form.elements.FirstName.value = items[0];
|
||||||
|
form.elements.Surname.value = items[1];
|
||||||
|
form.elements.Email.value = items[2];
|
||||||
|
form.elements.Password.value = items[3];
|
||||||
|
var fieldSet = field.parentNode.parentNode.getElementsByTagName('input');
|
||||||
|
ajaxSubmitFieldSet('admin/newsletter/savemember', fieldSet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Member list behaviour
|
||||||
|
*/
|
||||||
|
|
||||||
|
Behaviour.register({
|
||||||
|
'#MemberList tr' : {
|
||||||
|
onmouseover : hover_over,
|
||||||
|
onmouseout : hover_out,
|
||||||
|
|
||||||
|
onclick : function() {
|
||||||
|
if(this.className.indexOf('addrow') == -1) {
|
||||||
|
Element.addClassName(this, 'loading');
|
||||||
|
new Ajax.Request('admin/newsletter/getmember', {
|
||||||
|
method : 'post',
|
||||||
|
postBody : 'ID=' + this.id.replace('member-','') + '&ajax=1',
|
||||||
|
onSuccess : this.select_success.bind(this)
|
||||||
|
});
|
||||||
|
|
||||||
|
} else {
|
||||||
|
$('Form_MemberForm').innerHTML = "<p>Choose a member from above.</p>";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
select_success : function(response) {
|
||||||
|
Element.removeClassName(this, 'loading');
|
||||||
|
$('Form_MemberForm').loadNewPage(response.responseText);
|
||||||
|
|
||||||
|
statusMessage('loaded','good');
|
||||||
|
// for (var n in tinyMCE.instances) tinyMCE.removeMCEControl(n);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
'#MemberList thead tr' : {
|
||||||
|
onmouseover : null,
|
||||||
|
onmouseout : null,
|
||||||
|
onclick : null
|
||||||
|
},
|
||||||
|
|
||||||
|
'#MemberList' : {
|
||||||
|
initialise : function() {
|
||||||
|
this.headerMap = [];
|
||||||
|
|
||||||
|
var i, item, headers = this.getElementsByTagName('thead')[0].getElementsByTagName('tr')[0].getElementsByTagName('td');
|
||||||
|
for(i=0;item=headers[i];i++) {
|
||||||
|
this.headerMap[i] = item.className;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
setRecordDetails : function(id, details, groupID) {
|
||||||
|
var row = document.getElementById('member-' + id);
|
||||||
|
if(row) {
|
||||||
|
var i, item, cells = row.getElementsByTagName('td');
|
||||||
|
for(i=0;item=cells[i];i++) {
|
||||||
|
if(details[this.headerMap[i]]) {
|
||||||
|
item.innerHTML = details[this.headerMap[i]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.createRecord(id, details, groupID);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
createRecord : function (id, details, groupId) {
|
||||||
|
var row = document.createElement('tr');
|
||||||
|
row.id = 'member-' + id;
|
||||||
|
var i, cell, cellField;
|
||||||
|
for(i=0;cellField=this.headerMap[i];i++) {
|
||||||
|
cell = document.createElement('td')
|
||||||
|
if(details[cellField]) {
|
||||||
|
cell.innerHTML = details[cellField];
|
||||||
|
}
|
||||||
|
row.appendChild(cell);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the delete icon
|
||||||
|
if(typeof groupId == 'undefined')
|
||||||
|
var groupId = $('Form_EditForm').elements.ID.value;
|
||||||
|
cell = document.createElement('td')
|
||||||
|
cell.innerHTML = '<a class="deletelink" href="admin/security/removememberfromgroup/' + groupId + '/' + id + '"><img src="cms/images/delete.gif" alt="delete" /></a>';
|
||||||
|
cell.getElementsByTagName('0');
|
||||||
|
row.appendChild(cell);
|
||||||
|
|
||||||
|
var tbody = this.getElementsByTagName('tbody')[0];
|
||||||
|
var addRow = document.getElementsByClassName('addrow',tbody)[0];
|
||||||
|
if(addRow) tbody.insertBefore(row, addRow);
|
||||||
|
else tbody.appendChild(row);
|
||||||
|
Behaviour.apply(row, true);
|
||||||
|
},
|
||||||
|
|
||||||
|
clearAddForm : function() {
|
||||||
|
var tbody = this.getElementsByTagName('tbody')[0];
|
||||||
|
var addRow = document.getElementsByClassName('addrow',tbody)[0];
|
||||||
|
if(addRow) {
|
||||||
|
var i,field,fields = addRow.getElementsByTagName('input');
|
||||||
|
for(i=0;field=fields[i];i++) {
|
||||||
|
if(field.type != 'hidden' && field.type != 'submit') field.value = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
removeMember : function(memberID) {
|
||||||
|
var record;
|
||||||
|
if(record = $('member-' + memberID)) {
|
||||||
|
record.parentNode.removeChild(record);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
'#MemberList input' : AjaxMemberLookup,
|
||||||
|
|
||||||
|
'#MemberList a.deletelink' : {
|
||||||
|
onclick : function(event) {
|
||||||
|
if(confirm("Do you want to remove this member from the group?")) {
|
||||||
|
this.getElementsByTagName('img')[0].src = 'cms/images/network-save.gif';
|
||||||
|
ajaxLink(this.href);
|
||||||
|
}
|
||||||
|
Event.stop(event);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
'#MemberList tr.addrow' : {
|
||||||
|
onmouseover : null,
|
||||||
|
onmouseout : null,
|
||||||
|
onclick : null
|
||||||
|
},
|
||||||
|
|
||||||
|
'#MemberList tr.addrow td.actions input' : {
|
||||||
|
onclick : function(event) {
|
||||||
|
data = this.parentNode.parentNode.getElementsByTagName('input');
|
||||||
|
var i,item,error = [];
|
||||||
|
for(i=0;item=data[i];i++) {
|
||||||
|
if(item.name == 'Email' && !item.value) error[error.length] = "Email";
|
||||||
|
if(item.name == 'Password' && !item.value) error[error.length] = "Password";
|
||||||
|
}
|
||||||
|
if(error.length > 0) {
|
||||||
|
alert('Please enter a ' + error.join(' and a ') + ' to add a member.');
|
||||||
|
|
||||||
|
} else {
|
||||||
|
ajaxSubmitFieldSet('admin/newsletter/addmember', data);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
13
javascript/Newsletter_UploadForm.js
Executable file
@ -0,0 +1,13 @@
|
|||||||
|
Behaviour.register({
|
||||||
|
'body' : {
|
||||||
|
onload: function() {
|
||||||
|
top.getElementById('ImportFile').frameLoaded( document );
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
'form input[type=file]' : {
|
||||||
|
onchange: function() {
|
||||||
|
this.form.submit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
186
javascript/PageCommentInterface.js
Executable file
@ -0,0 +1,186 @@
|
|||||||
|
/**
|
||||||
|
* Ajax to support the comment posting system
|
||||||
|
*/
|
||||||
|
|
||||||
|
PageCommentInterface = Class.create();
|
||||||
|
|
||||||
|
PageCommentInterface.prototype = {
|
||||||
|
initialize: function() {
|
||||||
|
Behaviour.register({
|
||||||
|
'#PageCommentInterface_Form_PageCommentsPostCommentForm_action_postcomment' : {
|
||||||
|
onclick : this.postComment
|
||||||
|
},
|
||||||
|
|
||||||
|
'#PageComments a.deletelink' : {
|
||||||
|
onclick : this.deleteComment
|
||||||
|
},
|
||||||
|
'#PageComments a.spamlink' : {
|
||||||
|
onclick : this.reportSpam
|
||||||
|
},
|
||||||
|
'#PageComments a.hamlink' : {
|
||||||
|
onclick : this.reportHam
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
postComment: function() {
|
||||||
|
var form = $("PageCommentInterface_Form_PageCommentsPostCommentForm");
|
||||||
|
var message = $("PageCommentInterface_Form_PageCommentsPostCommentForm_error");
|
||||||
|
|
||||||
|
if(form.elements.Name.value && form.elements.Comment.value) {
|
||||||
|
if(noComments = $('NoComments')) {
|
||||||
|
Element.remove(noComments);
|
||||||
|
var pageComments = document.createElement('ul');
|
||||||
|
pageComments.id = 'PageComments';
|
||||||
|
$('CommentHolder').appendChild(pageComments);
|
||||||
|
}
|
||||||
|
|
||||||
|
message.style.display = 'none';
|
||||||
|
|
||||||
|
// Create a new <li> for the post
|
||||||
|
var pageComments = $('PageComments').getElementsByTagName('li');
|
||||||
|
var __newComment = document.createElement('li');
|
||||||
|
|
||||||
|
// Add it to the list with a 'loading' message
|
||||||
|
$('PageComments').insertBefore(__newComment, pageComments[0]);
|
||||||
|
__newComment.innerHTML = '<p><img src="cms/images/network-save.gif" /> Loading...</p>';
|
||||||
|
|
||||||
|
// Submit the form via ajax
|
||||||
|
Ajax.SubmitForm(form, "action_postcomment", {
|
||||||
|
onSuccess : function(response) {
|
||||||
|
// Load the response into the new <li>
|
||||||
|
__newComment.innerHTML = response.responseText;
|
||||||
|
Behaviour.apply(__newComment);
|
||||||
|
|
||||||
|
// Flash it using Scriptaculous
|
||||||
|
new Effect.Highlight(__newComment, { endcolor: '#e9e9e9' } );
|
||||||
|
if(response.responseText.match('<b>Spam detected!!</b>')) {
|
||||||
|
__newComment.className = 'spam';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onFailure : function(response) {
|
||||||
|
alert(response.responseText);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Clear the fields
|
||||||
|
|
||||||
|
// form.elements.Name.value = "";
|
||||||
|
form.elements.Comment.value = "";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
} else {
|
||||||
|
message.style.display = '';
|
||||||
|
message.innerHTML = "Please enter your name and a comment to be posted to the site.";
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ajax handler of moderation removal
|
||||||
|
*/
|
||||||
|
deleteComment: function() {
|
||||||
|
var __comment = this.parentNode.parentNode.parentNode;
|
||||||
|
|
||||||
|
__comment.getElementsByTagName('span')[0].innerHTML = "Removing...";
|
||||||
|
|
||||||
|
new Ajax.Request(this.href + '?ajax=1', {
|
||||||
|
onSuccess : function(response) {
|
||||||
|
// Clear our wee status message
|
||||||
|
__comment.getElementsByTagName('span')[0].innerHTML = "Removing...";
|
||||||
|
|
||||||
|
// Remove it using Scriptaculous
|
||||||
|
new Effect.Highlight(__comment, {
|
||||||
|
startcolor: '#cc9999' , endcolor: '#e9e9e9', duration: 0.5,
|
||||||
|
afterFinish : function () {
|
||||||
|
var commentList = __comment.parentNode;
|
||||||
|
commentList.removeChild(__comment);
|
||||||
|
if(!commentList.firstChild) {
|
||||||
|
$('CommentHolder').innerHTML = "<p id=\"NoComments\">No one has commented on this page yet.</p>";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
},
|
||||||
|
|
||||||
|
onFailure : function(response) {
|
||||||
|
alert(response.responseText);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ajax handler of spam reporting
|
||||||
|
*/
|
||||||
|
reportSpam: function() {
|
||||||
|
var __comment = this.parentNode.parentNode.parentNode;
|
||||||
|
|
||||||
|
__comment.getElementsByTagName('span')[0].innerHTML = "Reporting spam...";
|
||||||
|
|
||||||
|
|
||||||
|
new Ajax.Request(this.href + '?ajax=1', {
|
||||||
|
onSuccess : function(response) {
|
||||||
|
if(response.responseText != '') {
|
||||||
|
// Load the response into the <li>
|
||||||
|
__comment.innerHTML = response.responseText;
|
||||||
|
Behaviour.apply(__comment);
|
||||||
|
|
||||||
|
// Flash it using Scriptaculous
|
||||||
|
new Effect.Highlight(__comment, { endcolor: '#cc9999' } );
|
||||||
|
|
||||||
|
__comment.className = 'spam';
|
||||||
|
} else {
|
||||||
|
new Effect.Highlight(__comment, {
|
||||||
|
startcolor: '#cc9999' , endcolor: '#e9e9e9', duration: 0.5,
|
||||||
|
afterFinish : function() {
|
||||||
|
var commentList = __comment.parentNode;
|
||||||
|
commentList.removeChild(__comment);
|
||||||
|
if(!commentList.firstChild) {
|
||||||
|
$('CommentHolder').innerHTML = "<p id=\"NoComments\">No one has commented on this page yet.</p>";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
onFailure : function(response) {
|
||||||
|
alert(response.responseText);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ajax handler of ham reporting
|
||||||
|
*/
|
||||||
|
reportHam: function() {
|
||||||
|
var __comment = this.parentNode.parentNode.parentNode;
|
||||||
|
|
||||||
|
__comment.getElementsByTagName('span')[0].innerHTML = "Reporting as not spam...";
|
||||||
|
|
||||||
|
new Ajax.Request(this.href + '?ajax=1', {
|
||||||
|
onSuccess : function(response) {
|
||||||
|
// Load the response into the <li>
|
||||||
|
__comment.innerHTML = response.responseText;
|
||||||
|
Behaviour.apply(__comment);
|
||||||
|
|
||||||
|
// Flash it using Scriptaculous
|
||||||
|
new Effect.Highlight(__comment, { endcolor: '#e9e9e9' } );
|
||||||
|
__comment.className = 'notspam';
|
||||||
|
},
|
||||||
|
|
||||||
|
onFailure : function(response) {
|
||||||
|
alert(response.responseText);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PageCommentInterface.applyTo("#PageComments_holder");
|
54
javascript/ReportAdmin_left.js
Executable file
@ -0,0 +1,54 @@
|
|||||||
|
if(typeof SiteTreeHandlers == 'undefined') SiteTreeHandlers = {};
|
||||||
|
|
||||||
|
SiteTree.prototype = {
|
||||||
|
castAsTreeNode: function(li) {
|
||||||
|
behaveAs(li, SiteTreeNode, this.options);
|
||||||
|
},
|
||||||
|
|
||||||
|
getIdxOf : function(treeNode) {
|
||||||
|
if(treeNode && treeNode.id)
|
||||||
|
return treeNode.id;
|
||||||
|
},
|
||||||
|
|
||||||
|
getTreeNodeByIdx : function(idx) {
|
||||||
|
if(!idx) idx = "0";
|
||||||
|
return document.getElementById(idx);
|
||||||
|
},
|
||||||
|
|
||||||
|
initialise: function() {
|
||||||
|
this.observeMethod('SelectionChanged', this.changeCurrentTo);
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
SiteTreeNode.prototype.onselect = function() {
|
||||||
|
$('sitetree').changeCurrentTo(this);
|
||||||
|
if($('sitetree').notify('SelectionChanged', this)) {
|
||||||
|
this.getPageFromServer();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
SiteTreeNode.prototype.getPageFromServer = function() {
|
||||||
|
if(this.id)
|
||||||
|
$('Form_EditForm').getPageFromServer(this.id);
|
||||||
|
};
|
||||||
|
|
||||||
|
function reloadSiteTree() {
|
||||||
|
|
||||||
|
new Ajax.Request( 'admin/report/getsitetree', {
|
||||||
|
method: get,
|
||||||
|
onSuccess: function( response ) {
|
||||||
|
$('sitetree_holder').innerHTML = response.responseText;
|
||||||
|
},
|
||||||
|
onFailure: function( response ) {
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
appendLoader(function () {
|
||||||
|
if(typeof($('sitetree').selected) == 'undefined');
|
||||||
|
var selectedNode = $('sitetree').getElementsByTagName('li')[1];
|
||||||
|
selectedNode.onselect();
|
||||||
|
});
|
118
javascript/ReportAdmin_right.js
Executable file
@ -0,0 +1,118 @@
|
|||||||
|
|
||||||
|
Behaviour.register({
|
||||||
|
'#Form_EditForm' : {
|
||||||
|
initialise : function() {
|
||||||
|
this.openTab = null;
|
||||||
|
this.prepareForm();
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Processing called whenever a page is loaded in the right - including the initial one
|
||||||
|
*/
|
||||||
|
prepareForm : function() {
|
||||||
|
ajaxActionsAtTop('Form_EditForm', 'form_actions', 'right');
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request a page from the server via Ajax
|
||||||
|
*/
|
||||||
|
getPageFromServer : function(id) {
|
||||||
|
if(id) {
|
||||||
|
this.receivingID = id;
|
||||||
|
|
||||||
|
// Treenode might not exist if that part of the tree is closed
|
||||||
|
var treeNode = $('sitetree').getTreeNodeByIdx(id);
|
||||||
|
|
||||||
|
if(treeNode) treeNode.addNodeClass('loading');
|
||||||
|
|
||||||
|
statusMessage("loading...");
|
||||||
|
|
||||||
|
var requestURL = 'admin/reports/showreport/' + id;
|
||||||
|
new Ajax.Request(requestURL, {
|
||||||
|
asynchronous : true,
|
||||||
|
method : 'post',
|
||||||
|
postBody : 'ajax=1',
|
||||||
|
onSuccess : this.successfullyReceivedPage.bind(this),
|
||||||
|
onFailure : function(response) {
|
||||||
|
errorMessage('error loading page',response);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
throw("getPageFromServer: Bad page ID: " + id);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
successfullyReceivedPage : function(response) {
|
||||||
|
this.loadNewPage(response.responseText);
|
||||||
|
|
||||||
|
// Treenode might not exist if that part of the tree is closed
|
||||||
|
var treeNode = $('sitetree').getTreeNodeByIdx(this.receivingID);
|
||||||
|
if(treeNode) {
|
||||||
|
$('sitetree').changeCurrentTo(treeNode);
|
||||||
|
treeNode.removeNodeClass('loading');
|
||||||
|
}
|
||||||
|
statusMessage('');
|
||||||
|
|
||||||
|
onload_init_tabstrip();
|
||||||
|
|
||||||
|
if( this.openTab ) {
|
||||||
|
openTab( this.openTab );
|
||||||
|
this.openTab = null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
didntReceivePage : function(response) {
|
||||||
|
errorMessage('error loading page', response);
|
||||||
|
$('sitetree').getTreeNodeByIdx(this.elements.ID.value).removeNodeClass('loading');
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load a new page into the right-hand form
|
||||||
|
*/
|
||||||
|
loadNewPage : function(formContent) {
|
||||||
|
rightHTML = formContent;
|
||||||
|
rightHTML = rightHTML.replace(/href *= *"#/g, 'href="' + window.location.href.replace(/#.*$/,'') + '#');
|
||||||
|
|
||||||
|
// Prepare iframes for removal, otherwise we get loading bugs
|
||||||
|
var i, allIframes = this.getElementsByTagName('iframe');
|
||||||
|
if(allIframes) for(i=0;i<allIframes.length;i++) {
|
||||||
|
allIframes[i].contentWindow.location.href = 'about:blank';
|
||||||
|
allIframes[i].parentNode.removeChild(allIframes[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.innerHTML = rightHTML;
|
||||||
|
|
||||||
|
allIframes = this.getElementsByTagName('iframe');
|
||||||
|
if(allIframes) for(i=0;i<allIframes.length;i++) {
|
||||||
|
try {
|
||||||
|
allIframes[i].contentWindow.location.href = allIframes[i].src;
|
||||||
|
} catch(er) {alert(er.message);}
|
||||||
|
}
|
||||||
|
|
||||||
|
_TAB_DIVS_ON_PAGE = [];
|
||||||
|
|
||||||
|
try {
|
||||||
|
var tabs = document.getElementsBySelector('#Form_EditForm ul.tabstrip');
|
||||||
|
} catch(er) {/* alert('a: '+ er.message + '\n' + er.line);*/ }
|
||||||
|
try {
|
||||||
|
for(var i=0;i<tabs.length;i++) if(tabs[i].tagName) initTabstrip(tabs[i]);
|
||||||
|
} catch(er) { /*alert('b: '+ er.message + '\n' + er.line); */}
|
||||||
|
|
||||||
|
if((typeof tinyMCE != 'undefined') && tinyMCE.instances) {
|
||||||
|
tinyMCE.instances = [];
|
||||||
|
tinyMCE.isLoaded = false;
|
||||||
|
tinyMCE.onLoad();
|
||||||
|
}
|
||||||
|
|
||||||
|
// if(this.prepareForm) this.prepareForm();
|
||||||
|
Behaviour.apply($('Form_EditForm'));
|
||||||
|
if(this.prepareForm)
|
||||||
|
this.prepareForm();
|
||||||
|
|
||||||
|
this.resetElements();
|
||||||
|
|
||||||
|
window.ontabschanged();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
147
javascript/SecurityAdmin_left.js
Executable file
@ -0,0 +1,147 @@
|
|||||||
|
SiteTreeHandlers.parentChanged_url = 'admin/security/ajaxupdateparent';
|
||||||
|
SiteTreeHandlers.orderChanged_url = 'admin/security/ajaxupdatesort';
|
||||||
|
SiteTreeHandlers.loadPage_url = 'admin/security/getitem';
|
||||||
|
|
||||||
|
_HANDLER_FORMS['deletegroup'] = 'deletegroup_options';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add page action
|
||||||
|
* @todo Remove duplication between this and the CMSMain Add page action
|
||||||
|
*/
|
||||||
|
addgroup = {
|
||||||
|
button_onclick : function() {
|
||||||
|
addgroup.form_submit();
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
form_submit : function() {
|
||||||
|
var st = $('sitetree');
|
||||||
|
$('addgroup_options').elements.ParentID.value = st.firstSelected() ? st.getIdxOf(st.firstSelected()) : 0;
|
||||||
|
Ajax.SubmitForm('addgroup_options', null, {
|
||||||
|
onSuccess : Ajax.Evaluator,
|
||||||
|
onFailure : function(response) {
|
||||||
|
errorMessage('Error adding page', response);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete page action
|
||||||
|
*/
|
||||||
|
deletegroup = {
|
||||||
|
button_onclick : function() {
|
||||||
|
/*if( $('deletegroup_options').style.display == 'none' )
|
||||||
|
$('deletegroup_options').style.display = 'block';
|
||||||
|
else
|
||||||
|
$('deletegroup_options').style.display = 'none';*/
|
||||||
|
|
||||||
|
if(treeactions.toggleSelection(this)) {
|
||||||
|
$('deletegroup_options').style.display = 'block';
|
||||||
|
|
||||||
|
deletegroup.o1 = $('sitetree').observeMethod('SelectionChanged', deletegroup.treeSelectionChanged);
|
||||||
|
deletegroup.o2 = $('deletegroup_options').observeMethod('Close', deletegroup.popupClosed);
|
||||||
|
addClass($('sitetree'),'multiselect');
|
||||||
|
|
||||||
|
deletegroup.selectedNodes = { };
|
||||||
|
|
||||||
|
var sel = $('sitetree').firstSelected();
|
||||||
|
if(sel && sel.className.indexOf('nodelete') == -1) {
|
||||||
|
var selIdx = $('sitetree').getIdxOf(sel);
|
||||||
|
deletegroup.selectedNodes[selIdx] = true;
|
||||||
|
sel.removeNodeClass('current');
|
||||||
|
sel.addNodeClass('selected');
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
$('deletegroup_options').style.display = 'none';
|
||||||
|
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
treeSelectionChanged : function(selectedNode) {
|
||||||
|
var idx = $('sitetree').getIdxOf(selectedNode);
|
||||||
|
|
||||||
|
if(selectedNode.className.indexOf('nodelete') == -1) {
|
||||||
|
if(selectedNode.selected) {
|
||||||
|
selectedNode.removeNodeClass('selected');
|
||||||
|
selectedNode.selected = false;
|
||||||
|
deletegroup.selectedNodes[idx] = false;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
selectedNode.addNodeClass('selected');
|
||||||
|
selectedNode.selected = true;
|
||||||
|
deletegroup.selectedNodes[idx] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
popupClosed : function() {
|
||||||
|
removeClass($('sitetree'),'multiselect');
|
||||||
|
$('sitetree').stopObserving(deletegroup.o1);
|
||||||
|
$('deletegroup_options').stopObserving(deletegroup.o2);
|
||||||
|
|
||||||
|
for(var idx in deletegroup.selectedNodes) {
|
||||||
|
if(deletegroup.selectedNodes[idx]) {
|
||||||
|
node = $('sitetree').getTreeNodeByIdx(idx);
|
||||||
|
if(node) {
|
||||||
|
node.removeNodeClass('selected');
|
||||||
|
node.selected = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
form_submit : function() {
|
||||||
|
var csvIDs = "";
|
||||||
|
for(var idx in deletegroup.selectedNodes) {
|
||||||
|
if(deletegroup.selectedNodes[idx]) csvIDs += (csvIDs ? "," : "") + idx;
|
||||||
|
}
|
||||||
|
if(csvIDs) {
|
||||||
|
$('deletegroup_options').elements.csvIDs.value = csvIDs;
|
||||||
|
|
||||||
|
Ajax.SubmitForm('deletegroup_options', null, {
|
||||||
|
onSuccess : function(response) {
|
||||||
|
Ajax.Evaluator(response);
|
||||||
|
|
||||||
|
var sel;
|
||||||
|
if((sel = $('sitetree').firstSelected()) && sel.parentNode) sel.addNodeClass('current');
|
||||||
|
else $('Form_EditForm').innerHTML = "";
|
||||||
|
|
||||||
|
treeactions.closeSelection($('deletegroup'));
|
||||||
|
},
|
||||||
|
onFailure : function(response) {
|
||||||
|
errorMessage('Error deleting pages', response);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$('deletegroup').getElementsByTagName('a')[0].onclick();
|
||||||
|
|
||||||
|
} else {
|
||||||
|
alert("Please select at least 1 page.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialisation function to set everything up
|
||||||
|
*/
|
||||||
|
Behaviour.addLoader(function () {
|
||||||
|
// Set up add page
|
||||||
|
Observable.applyTo($('addgroup_options'));
|
||||||
|
$('addgroup').onclick = addgroup.button_onclick;
|
||||||
|
$('addgroup').getElementsByTagName('a')[0].onclick = function() {return false;};
|
||||||
|
$('addgroup_options').onsubmit = addgroup.form_submit;
|
||||||
|
|
||||||
|
// Set up delete page
|
||||||
|
Observable.applyTo($('deletegroup_options'));
|
||||||
|
$('deletegroup').onclick = deletegroup.button_onclick;
|
||||||
|
$('deletegroup').getElementsByTagName('a')[0].onclick = function() {return false;};
|
||||||
|
$('deletegroup_options').onsubmit = deletegroup.form_submit;
|
||||||
|
});
|
11
javascript/SecurityAdmin_right.js
Executable file
@ -0,0 +1,11 @@
|
|||||||
|
/**
|
||||||
|
* CAUTION: Assumes that a MemberTableField-instance is present as an editing form
|
||||||
|
*/
|
||||||
|
function action_addmember_right() {
|
||||||
|
var memberTableFields = document.getElementsBySelector('#Form_EditForm div.MemberTableField');
|
||||||
|
var tables = document.getElementsBySelector('#Form_EditForm div.MemberTableField table');
|
||||||
|
var addLinks = document.getElementsBySelector('#Form_EditForm div.MemberTableField a.addlink');
|
||||||
|
try {
|
||||||
|
memberTableFields[0].openPopup(null,addLinks[0].href,tables[0]);
|
||||||
|
} catch(err) {}
|
||||||
|
}
|
70
javascript/SideReports.js
Executable file
@ -0,0 +1,70 @@
|
|||||||
|
SideReports = Class.extend('SidePanel');
|
||||||
|
SideReports.prototype = {
|
||||||
|
initialize: function() {
|
||||||
|
this.selector = $('ReportSelector');
|
||||||
|
if(this.selector) this.selector.holder = this;
|
||||||
|
this.SidePanel.initialize();
|
||||||
|
},
|
||||||
|
|
||||||
|
destroy: function() {
|
||||||
|
if(this.SidePanel) this.SidePanel.destroy();
|
||||||
|
this.SidePanel = null;
|
||||||
|
if(this.selector) this.selector.holder = null;
|
||||||
|
this.selector = null;
|
||||||
|
},
|
||||||
|
|
||||||
|
onshow: function() {
|
||||||
|
if(this.selector.value) this.showreport();
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve a report via ajax
|
||||||
|
*/
|
||||||
|
showreport: function() {
|
||||||
|
if(this.selector.value) {
|
||||||
|
this.body.innerHTML = '<p>loading...</p>';
|
||||||
|
this.ajaxGetPanel(this.afterPanelLoaded);
|
||||||
|
} else {
|
||||||
|
this.body.innerHTML = "<p>choose a report in the dropdown.</p>";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
afterPanelLoaded : function() {
|
||||||
|
SideReportRecord.applyTo('#' + this.id + ' a');
|
||||||
|
},
|
||||||
|
ajaxURL: function() {
|
||||||
|
return 'admin/sidereport/' + this.selector.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
SideReportDropdown = Class.create();
|
||||||
|
SideReportDropdown.prototype = {
|
||||||
|
destroy: function() {
|
||||||
|
this.onchange = null;
|
||||||
|
this.holder = null;
|
||||||
|
},
|
||||||
|
onchange: function() {
|
||||||
|
this.holder.showreport();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
SideReportRecord = Class.create();
|
||||||
|
SideReportRecord.prototype = {
|
||||||
|
destroy: function() {
|
||||||
|
this.onclick = null;
|
||||||
|
},
|
||||||
|
|
||||||
|
onclick : function(event) {
|
||||||
|
Event.stop(event);
|
||||||
|
$('sitetree').loadingNode = $('sitetree').getTreeNodeByIdx( this.getID() );
|
||||||
|
$('Form_EditForm').getPageFromServer(this.getID());
|
||||||
|
},
|
||||||
|
getID : function() {
|
||||||
|
if(this.href.match(/\/([^\/]+)$/)) return parseInt(RegExp.$1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SideReportDropdown.applyTo('#ReportSelector');
|
||||||
|
SideReportRecord.applyTo('#reports_holder a');
|
||||||
|
SideReports.applyTo('#reports_holder');
|
358
javascript/SideTabs.js
Executable file
@ -0,0 +1,358 @@
|
|||||||
|
SideTabs = Class.create();
|
||||||
|
SideTabs.prototype = {
|
||||||
|
/**
|
||||||
|
* Set up the SideTab set. Note that tabs themselves are automatically constructed
|
||||||
|
* from the HTML.
|
||||||
|
*/
|
||||||
|
initialize: function() {
|
||||||
|
this.tabs = this.getElementsByTagName('li');
|
||||||
|
this.resize();
|
||||||
|
$('Form_EditForm').observeMethod('PageLoaded',this.onpagechanged.bind(this));
|
||||||
|
},
|
||||||
|
|
||||||
|
destroy: function() {
|
||||||
|
this.tabs = null;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fix all the panes' sizes in reponse to one being opened or closed
|
||||||
|
*/
|
||||||
|
resize: function() {
|
||||||
|
var i,numOpenPanes=0,otherPanes = [];
|
||||||
|
for(i=0;i<this.tabs.length;i++) {
|
||||||
|
if(this.tabs[i].selected) {
|
||||||
|
numOpenPanes++;
|
||||||
|
otherPanes[otherPanes.length] = this.tabs[i].linkedPane;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(numOpenPanes > 0) {
|
||||||
|
$('left').show();
|
||||||
|
var totalHeight = getFittingHeight(this.tabs[0].linkedPane, 0, otherPanes);
|
||||||
|
var eachHeight = totalHeight / numOpenPanes;
|
||||||
|
for(i=0;i<this.tabs.length;i++) {
|
||||||
|
if(this.tabs[i].selected) {
|
||||||
|
this.tabs[i].linkedPane.style.height = eachHeight + 'px';
|
||||||
|
if(this.tabs[i].linkedPane.onresize) this.tabs[i].linkedPane.onresize();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// All panes closed, hide the whole left-hand panel
|
||||||
|
} else {
|
||||||
|
$('left').hide();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Refresh all the panes after we change to a new page
|
||||||
|
*/
|
||||||
|
onpagechanged: function() {
|
||||||
|
var item,i;
|
||||||
|
for(i=0;item=this.tabs[i];i++) {
|
||||||
|
if(this.tabs[i].selected && this.tabs[i].linkedPane.onpagechanged) this.tabs[i].linkedPane.onpagechanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SideTabItem = Class.create();
|
||||||
|
|
||||||
|
SideTabItem.prototype = {
|
||||||
|
/**
|
||||||
|
* Set up one of the side tabs
|
||||||
|
*/
|
||||||
|
initialize: function() {
|
||||||
|
var holderID = this.id.replace('sidetab_','') + '_holder';
|
||||||
|
this.linkedPane = $(holderID);
|
||||||
|
if(!this.linkedPane) throw("Can't find item: " + holderID);
|
||||||
|
this.selected = (this.className && this.className.indexOf('selected') > -10);
|
||||||
|
this.holder = this.parentNode;
|
||||||
|
this.linkedPane.style.display = this.selected ? '' : 'none';
|
||||||
|
},
|
||||||
|
|
||||||
|
destroy: function() {
|
||||||
|
this.holder = null;
|
||||||
|
this.linkedPane = null;
|
||||||
|
this.onclick = null;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler for <li> click
|
||||||
|
*/
|
||||||
|
onclick: function(event) {
|
||||||
|
Event.stop(event);
|
||||||
|
this.toggle();
|
||||||
|
},
|
||||||
|
toggle: function() {
|
||||||
|
if(this.selected) this.close();
|
||||||
|
else this.open();
|
||||||
|
},
|
||||||
|
open: function() {
|
||||||
|
if(!this.selected) {
|
||||||
|
this.selected = true;
|
||||||
|
Element.addClassName(this,'selected');
|
||||||
|
this.linkedPane.style.display = '';
|
||||||
|
this.holder.resize();
|
||||||
|
if(this.linkedPane.onshow) {
|
||||||
|
this.linkedPane.onshow();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
close: function() {
|
||||||
|
if(this.selected) {
|
||||||
|
this.selected = false;
|
||||||
|
Element.removeClassName(this,'selected');
|
||||||
|
this.linkedPane.style.display = 'none';
|
||||||
|
this.holder.resize();
|
||||||
|
if(this.holder.onhide) this.holder.onhide();
|
||||||
|
if(this.linkedPane.onclose) this.linkedPane.onclose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Application order is important - the Items must be created before the SideTabs object.
|
||||||
|
SideTabItem.applyTo('#SideTabs li');
|
||||||
|
SideTabs.applyTo('#SideTabs');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generic base class for all side panels
|
||||||
|
*/
|
||||||
|
SidePanel = Class.create();
|
||||||
|
SidePanel.prototype = {
|
||||||
|
initialize : function() {
|
||||||
|
this.body = this.getElementsByTagName('div')[0];
|
||||||
|
},
|
||||||
|
destroy: function() {
|
||||||
|
this.body = null;
|
||||||
|
},
|
||||||
|
onshow : function() {
|
||||||
|
this.onresize();
|
||||||
|
this.body.innerHTML = '<p>loading...</p>';
|
||||||
|
this.ajaxGetPanel(this.afterPanelLoaded);
|
||||||
|
},
|
||||||
|
onresize : function() {
|
||||||
|
fitToParent(this.body);
|
||||||
|
},
|
||||||
|
ajaxGetPanel : function(onComplete) {
|
||||||
|
fitToParent(this.body);
|
||||||
|
new Ajax.Updater(this.body, this.ajaxURL(), {
|
||||||
|
onComplete : onComplete ? onComplete.bind(this) : null,
|
||||||
|
onFailure : this.ajaxPanelError
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
ajaxPanelError : function (response) {
|
||||||
|
errorMessage("error getting side panel", response);
|
||||||
|
},
|
||||||
|
|
||||||
|
ajaxURL : function() {
|
||||||
|
var srcName = this.id.replace('_holder','');
|
||||||
|
var id = $('Form_EditForm').elements.ID;
|
||||||
|
if(id) id = id.value; else id = "";
|
||||||
|
return 'admin/' + srcName + '/' + id + '?ajax=1';
|
||||||
|
},
|
||||||
|
|
||||||
|
afterPanelLoaded : function() {
|
||||||
|
},
|
||||||
|
onpagechanged : function() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SidePanelRecord = Class.create();
|
||||||
|
SidePanelRecord.prototype = {
|
||||||
|
onclick : function(event) {
|
||||||
|
Event.stop(event);
|
||||||
|
$('Form_EditForm').getPageFromServer(this.getID());
|
||||||
|
},
|
||||||
|
destroy: function() {
|
||||||
|
this.onclick = null;
|
||||||
|
},
|
||||||
|
getID : function() {
|
||||||
|
if(this.href.match(/\/([^\/]+)$/)) return parseInt(RegExp.$1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class that will add an 'over' class when the mouse is over the object
|
||||||
|
*/
|
||||||
|
Highlighter = Class.create();
|
||||||
|
Highlighter.prototype = {
|
||||||
|
onmouseover: function() {
|
||||||
|
Element.addClassName(this,'over');
|
||||||
|
},
|
||||||
|
onmouseout: function() {
|
||||||
|
Element.removeClassName(this,'over');
|
||||||
|
},
|
||||||
|
destroy: function() {
|
||||||
|
this.onmouseover = null;
|
||||||
|
this.onmouseout = null;
|
||||||
|
},
|
||||||
|
select: function(dontHide) {
|
||||||
|
if(dontHide) {
|
||||||
|
Element.addClassName(this,'current');
|
||||||
|
this.parentNode.lastSelected = null;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
if(this.parentNode.lastSelected) {
|
||||||
|
Element.removeClassName(this.parentNode.lastSelected,'current');
|
||||||
|
Element.addClassName(this,'current');
|
||||||
|
|
||||||
|
} else {
|
||||||
|
var i,item;
|
||||||
|
for(i=0;item=this.parentNode.childNodes[i];i++) {
|
||||||
|
if(item.tagName) {
|
||||||
|
if(item == this) Element.addClassName(item,'current');
|
||||||
|
else Element.removeClassName(item,'current');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.parentNode.lastSelected = this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Version list
|
||||||
|
*/
|
||||||
|
VersionList = Class.extend('SidePanel');
|
||||||
|
VersionList.prototype = {
|
||||||
|
initialize : function() {
|
||||||
|
this.mode = 'view';
|
||||||
|
this.SidePanel.initialize();
|
||||||
|
},
|
||||||
|
destroy: function() {
|
||||||
|
this.SidePanel = null;
|
||||||
|
this.onclose = null;
|
||||||
|
},
|
||||||
|
|
||||||
|
ajaxURL : function() {
|
||||||
|
return this.SidePanel.ajaxURL() + '&unpublished=' + ($('versions_showall').checked ? 1 : 0);
|
||||||
|
},
|
||||||
|
afterPanelLoaded : function() {
|
||||||
|
this.idLoaded = $('Form_EditForm').elements.ID.value;
|
||||||
|
VersionItem.applyTo('#' + this.id + ' tbody tr');
|
||||||
|
},
|
||||||
|
|
||||||
|
select : function(pageID, versionID, sourceEl) {
|
||||||
|
if(this.mode == 'view') {
|
||||||
|
sourceEl.select();
|
||||||
|
$('Form_EditForm').loadURLFromServer('admin/getversion/' + pageID + '/' + versionID);
|
||||||
|
$('viewArchivedSite').style.display = '';
|
||||||
|
$('viewArchivedSite').getVars = '?archiveDate=' + sourceEl.getElementsByTagName('td')[1].className;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
$('viewArchivedSite').style.display = 'none';
|
||||||
|
|
||||||
|
if(this.otherVersionID) {
|
||||||
|
sourceEl.select();
|
||||||
|
this.otherSourceEl.select(true);
|
||||||
|
statusMessage('Loading comparison...');
|
||||||
|
$('Form_EditForm').loadURLFromServer('admin/compareversions/' + pageID + '/?From=' + this.otherVersionID + '&To=' + versionID);
|
||||||
|
} else {
|
||||||
|
sourceEl.select();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.otherVersionID = versionID;
|
||||||
|
this.otherSourceEl = sourceEl;
|
||||||
|
},
|
||||||
|
onpagechanged : function() {
|
||||||
|
if(this.idLoaded != $('Form_EditForm').elements.ID.value) {
|
||||||
|
this.body.innerHTML = '<p>loading...</p>';
|
||||||
|
this.ajaxGetPanel(this.afterPanelLoaded);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
refresh : function() {
|
||||||
|
this.ajaxGetPanel(this.afterPanelLoaded);
|
||||||
|
},
|
||||||
|
onclose : function() {
|
||||||
|
$('Form_EditForm').getPageFromServer(this.idLoaded);
|
||||||
|
$('viewArchivedSite').style.display = 'none';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VersionItem = Class.extend('Highlighter');
|
||||||
|
VersionItem.prototype = {
|
||||||
|
initialize : function() {
|
||||||
|
this.holder = $('versions_holder');
|
||||||
|
},
|
||||||
|
|
||||||
|
destroy: function() {
|
||||||
|
this.holder = null;
|
||||||
|
this.onclick = null;
|
||||||
|
},
|
||||||
|
|
||||||
|
onclick : function() {
|
||||||
|
this.holder.select(this.pageID(), this.versionID(), this);
|
||||||
|
},
|
||||||
|
idPair : function() {
|
||||||
|
if(this.id.match(/page-([^-]+)-version-([^-]+)$/)) {
|
||||||
|
return RegExp.$1 + '/' + RegExp.$2;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
pageID : function() {
|
||||||
|
if(this.id.match(/page-([^-]+)-version-([^-]+)$/)) {
|
||||||
|
return RegExp.$1;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
versionID : function() {
|
||||||
|
if(this.id.match(/page-([^-]+)-version-([^-]+)$/)) {
|
||||||
|
return RegExp.$2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VersionAction = Class.create();
|
||||||
|
VersionAction.prototype = {
|
||||||
|
initialize: function() {
|
||||||
|
this.holder = $('versions_holder');
|
||||||
|
this.selector = this.getElementsByTagName('select')[0];
|
||||||
|
this.selector.holder = this;
|
||||||
|
this.selector.onchange = function() { this.holder.changed(this.value,this); return false; }
|
||||||
|
|
||||||
|
this.showallCheckbox = $('versions_showall');
|
||||||
|
this.showallCheckbox.holder = this;
|
||||||
|
this.showallCheckbox.onclick = this.showall_change;
|
||||||
|
},
|
||||||
|
|
||||||
|
showall_change: function() {
|
||||||
|
this.holder.holder.refresh();
|
||||||
|
},
|
||||||
|
|
||||||
|
destroy: function() {
|
||||||
|
if(this.selector) {
|
||||||
|
this.selector.holder = null;
|
||||||
|
this.selector.onchange = null;
|
||||||
|
}
|
||||||
|
if(this.showallCheckbox) {
|
||||||
|
this.showallCheckbox.holder = null;
|
||||||
|
this.showallCheckbox.onchange = null;
|
||||||
|
}
|
||||||
|
this.holder = null;
|
||||||
|
this.selector = null;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler function whenever an action is clicked
|
||||||
|
*/
|
||||||
|
changed: function(url, selector) {
|
||||||
|
switch(url) {
|
||||||
|
case 'view': return this.view();
|
||||||
|
case 'compare': return this.compare();
|
||||||
|
default: errorMessage("VersionAction.changed: I don't know about action '" + url + "'");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
view: function() {
|
||||||
|
this.holder.mode = 'view';
|
||||||
|
},
|
||||||
|
|
||||||
|
compare: function() {
|
||||||
|
this.holder.mode = 'compare';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
VersionItem.applyTo('#versions_holder tbody tr');
|
||||||
|
VersionAction.applyTo('#versions_holder p.pane_actions');
|
||||||
|
VersionList.applyTo('#versions_holder');
|
||||||
|
|
21
javascript/TaskList.js
Executable file
@ -0,0 +1,21 @@
|
|||||||
|
TaskList = Class.extend('SidePanel');
|
||||||
|
TaskList.prototype = {
|
||||||
|
destroy: function() {
|
||||||
|
if(this.SidePanel) this.SidePanel.destroy();
|
||||||
|
this.SidePanel = null;
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Called after the panel has been ajax-loaded in
|
||||||
|
*/
|
||||||
|
afterPanelLoaded : function() {
|
||||||
|
TaskListRecord.applyTo('#' + this.id + ' a');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TaskListRecord = SidePanelRecord;
|
||||||
|
|
||||||
|
TaskListRecord.applyTo('#tasklist_holder a');
|
||||||
|
TaskList.applyTo('#tasklist_holder');
|
||||||
|
|
||||||
|
TaskListRecord.applyTo('#waiting_holder a');
|
||||||
|
TaskList.applyTo('#waitingon_holder');
|
56
javascript/ThumbnailStripField.js
Executable file
@ -0,0 +1,56 @@
|
|||||||
|
ThumbnailStripField = Class.create();
|
||||||
|
// We do this instead of div.thumbnailstrip for efficiency. It means that ThumbnailStripField can only be used in the
|
||||||
|
// CMS toolbar
|
||||||
|
ThumbnailStripField.applyTo('#Image');
|
||||||
|
ThumbnailStripField.applyTo('#Flash');
|
||||||
|
ThumbnailStripField.prototype = {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var updateMethod string Specifies the Ajax-call for getting files
|
||||||
|
* (currently either "getimages" or "getflash"). This can be specified
|
||||||
|
* in the PHP-constructor of ThumbnailStripField and is passed to the client
|
||||||
|
* as a fake css-class.
|
||||||
|
*/
|
||||||
|
updateMethod: "getimages",
|
||||||
|
|
||||||
|
initialize: function() {
|
||||||
|
try {
|
||||||
|
this.updateMethod = this.className.match(/updatemethod=([^ ]+)/)[1];
|
||||||
|
} catch(err) {}
|
||||||
|
|
||||||
|
if(this.className.match(/parent=([^ ]+)/)) {
|
||||||
|
// HACK: This is hard-coded to only work with TreeDropdownFields
|
||||||
|
var parentField = $(RegExp.$1).parentNode;
|
||||||
|
if(parentField) {
|
||||||
|
parentField.observeMethod('Change', this.ajaxGetFiles.bind(this));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
ajaxGetFiles: function(folderID) {
|
||||||
|
this.innerHTML = '<span style="float: left">Loading...</span>'
|
||||||
|
var ajaxURL = this.helperURLBase() + '&methodName='+this.updateMethod+'&folderID=' + folderID;
|
||||||
|
new Ajax.Updater(this, ajaxURL, {
|
||||||
|
method : 'get',
|
||||||
|
onComplete : this.reapplyBehaviour.bind(this),
|
||||||
|
onFailure : function(response) { errorMessage("Error getting files", response); }
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
reapplyBehaviour: function() {
|
||||||
|
Behaviour.apply(this);
|
||||||
|
},
|
||||||
|
|
||||||
|
helperURLBase: function() {
|
||||||
|
var fieldName = this.id; //this.id.replace(this.ownerForm().name + '_','');
|
||||||
|
|
||||||
|
return this.ownerForm().action + '&action_callfieldmethod=1&fieldName=' + fieldName + '&ajax=1'
|
||||||
|
},
|
||||||
|
|
||||||
|
ownerForm: function() {
|
||||||
|
var f =this.parentNode;
|
||||||
|
while(f && f.tagName.toLowerCase() != 'form') f = f.parentNode;
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
10
javascript/dialog.js
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
Behaviour.register({
|
||||||
|
'.buttons button' : {
|
||||||
|
onclick: function() {
|
||||||
|
window.top._OPEN_DIALOG.execHandler( this.name );
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
42
javascript/tinymce.template.js
Executable file
@ -0,0 +1,42 @@
|
|||||||
|
function nullConverter(url) {
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TinyMCE initialisation template.
|
||||||
|
* $ variables are replaced by string search & replace. It's pretty crude.
|
||||||
|
*/
|
||||||
|
tinyMCE.init({
|
||||||
|
mode : "specific_textareas",
|
||||||
|
textarea_trigger : "tinymce",
|
||||||
|
width: -1,
|
||||||
|
height: -1,
|
||||||
|
auto_resize : true,
|
||||||
|
theme : "advanced",
|
||||||
|
content_css : "$ContentCSS",
|
||||||
|
document_base_url: "$BaseURL",
|
||||||
|
urlconverter_callback : "nullConverter",
|
||||||
|
|
||||||
|
setupcontent_callback : "sapphiremce_setupcontent",
|
||||||
|
cleanup_callback : "sapphiremce_cleanup",
|
||||||
|
|
||||||
|
Theme_Advanced_Layout_manager : "SimpleLayout",
|
||||||
|
theme_advanced_toolbar_location : "manually_placed",
|
||||||
|
theme_advanced_toolbar_align : "left",
|
||||||
|
theme_advanced_toolbar_parent : "right",
|
||||||
|
plugins : "contextmenu,table,emotions,flash",
|
||||||
|
table_inline_editing : true,
|
||||||
|
theme_advanced_buttons1 : "italic,underline,strikethrough,separator,justifyleft,justifycenter,justifyright,justifyfull,styleselect,formatselect,separator,bullist,numlist,outdent,indent,hr,charmap",
|
||||||
|
theme_advanced_buttons2 : "undo,redo,separator,cut,copy,paste,separator,search,replace,separator,flash",
|
||||||
|
theme_advanced_buttons3 : "",
|
||||||
|
theme_advanced_buttons3_add : "emotions",
|
||||||
|
/*
|
||||||
|
theme_advanced_buttons1 : "bold,italic,underline,strikethrough,separator,justifyleft,justifycenter,justifyright,justifyfull,styleselect,formatselect,separator,bullist,numlist,outdent,indent,hr,charmap",
|
||||||
|
theme_advanced_buttons2 : "undo,redo,separator,cut,copy,paste,separator,search,replace,separator,link,unlink,anchor,image,separator,cleanup,removeformat,visualaid,code,separator,tablecontrols",
|
||||||
|
*/
|
||||||
|
safari_warning : false,
|
||||||
|
relative_urls : true,
|
||||||
|
verify_html : true,
|
||||||
|
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],-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]"
|
||||||
|
});
|
1
silverstripe_version
Normal file
@ -0,0 +1 @@
|
|||||||
|
$URL$
|
24
templates/AssetAdmin_uploadiframe.ss
Executable file
@ -0,0 +1,24 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
background-color: #fff !important;
|
||||||
|
}
|
||||||
|
fieldset {
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
border-style: none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<% base_tag %>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<% if CanUpload %>
|
||||||
|
$UploadForm
|
||||||
|
<% else %>
|
||||||
|
You do not have permission to upload files into this folder.
|
||||||
|
<% end_if %>
|
||||||
|
</body>
|
1
templates/BulkLoaderAdmin_iframe.ss
Executable file
@ -0,0 +1 @@
|
|||||||
|
$EditForm
|
9
templates/BulkLoaderAdmin_preview.ss
Executable file
@ -0,0 +1,9 @@
|
|||||||
|
<p>$Message
|
||||||
|
|
||||||
|
Results
|
||||||
|
|
||||||
|
<% control Results %>
|
||||||
|
list changes here
|
||||||
|
<% end_control %>
|
||||||
|
|
||||||
|
$ConfirmForm
|
36
templates/CMSMain_dialog.ss
Executable file
@ -0,0 +1,36 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<script>
|
||||||
|
function buttonClick(el) {
|
||||||
|
<% if Modal %>
|
||||||
|
window.returnValue = el.name;
|
||||||
|
window.close();
|
||||||
|
<% else %>
|
||||||
|
if(window.linkedObject) {
|
||||||
|
if(el.name) {
|
||||||
|
window.linkedObject.result = el.name;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
throw("Couldn't find the button name");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw("Can't find window.linkedObject to send the button click back to the main window");
|
||||||
|
}
|
||||||
|
window.close();
|
||||||
|
<% end_if %>
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<p>$Message</p>
|
||||||
|
|
||||||
|
<p style="align: center">
|
||||||
|
<% control Buttons %>
|
||||||
|
<input type="button" value="$Title" name="$Name" onclick="buttonClick(this);" />
|
||||||
|
<% end_control %>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
30
templates/CMSMain_versions.ss
Executable file
@ -0,0 +1,30 @@
|
|||||||
|
<table id="Versions">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<td>#</td>
|
||||||
|
<td>When</td>
|
||||||
|
<td>Author</td>
|
||||||
|
<td>Publisher</td>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<% control Versions %>
|
||||||
|
<tr id="page-$RecordID-version-$Version" class="$EvenOdd $PublishedClass">
|
||||||
|
<td>$Version</td>
|
||||||
|
<td class="$LastEdited" title="$LastEdited.Ago">$LastEdited.Nice</td>
|
||||||
|
<td>$Author.FirstName $Author.Surname.Initial</td>
|
||||||
|
<td>
|
||||||
|
<% if Published %>
|
||||||
|
<% if Publisher %>
|
||||||
|
$Publisher.FirstName $Publisher.Surname.Initial
|
||||||
|
<% else %>
|
||||||
|
Unknown
|
||||||
|
<% end_if %>
|
||||||
|
<% else %>
|
||||||
|
Not published
|
||||||
|
<% end_if %>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<% end_control %>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
14
templates/CommentList.ss
Executable file
@ -0,0 +1,14 @@
|
|||||||
|
<% if Comments %>
|
||||||
|
<ul id="CommentList">
|
||||||
|
<% control Comments %>
|
||||||
|
<li class="$EvenOdd">
|
||||||
|
$Comment
|
||||||
|
<div class="extra">$Action $Created.Ago - $Author.FirstName $Author.Surname.Initial</div>
|
||||||
|
</li>
|
||||||
|
<% end_control %>
|
||||||
|
</ul>
|
||||||
|
<% else %>
|
||||||
|
<p>There are no comments on this page.</p>
|
||||||
|
<p>Comments are created whenever one of the 'workflow actions'
|
||||||
|
are undertaken - Publish, Reject, Submit.</p>
|
||||||
|
<% end_if %>
|
14
templates/Dialog.ss
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
|
||||||
|
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" >
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
|
||||||
|
<% base_tag %>
|
||||||
|
<title>$Title</title>
|
||||||
|
</head>
|
||||||
|
<body class="$DialogType">
|
||||||
|
<p class="message"><img src="cms/images/dialogs/{$DialogType}.gif" /><span>$Message</span></p>
|
||||||
|
<div class="buttons">
|
||||||
|
$Buttons
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
52
templates/Includes/AssetAdmin_left.ss
Executable file
@ -0,0 +1,52 @@
|
|||||||
|
<script>
|
||||||
|
_TREE_ICONS = {};
|
||||||
|
_TREE_ICONS['Folder'] = {
|
||||||
|
fileIcon: 'jsparty/tree/images/page-closedfolder.gif',
|
||||||
|
openFolderIcon: 'jsparty/tree/images/page-openfolder.gif',
|
||||||
|
closedFolderIcon: 'jsparty/tree/images/page-closedfolder.gif'
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="title"><div style="background-image : url(cms/images/panels/MySite.png)">Files & Images</div></div>
|
||||||
|
<div id="treepanes">
|
||||||
|
<div id="sitetree_holder">
|
||||||
|
<ul id="TreeActions">
|
||||||
|
<li class="action" id="addpage"><a href="admin/addpage">Create</a></li>
|
||||||
|
<li class="action" id="deletepage"><a href="admin/deletepage">Delete</a></li>
|
||||||
|
<li class="action" id="sortitems"><a href="#">Reorganise</a></li>
|
||||||
|
</ul>
|
||||||
|
<div style="clear:both;"></div>
|
||||||
|
<form class="actionparams" id="addpage_options" style="display: none" action="admin/assets/addfolder">
|
||||||
|
<div>
|
||||||
|
<input type="hidden" name="ParentID" />
|
||||||
|
<input class="action" type="submit" value="Go" />
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<form class="actionparams" id="deletepage_options" style="display: none" action="admin/assets/deletefolder">
|
||||||
|
<p>Select the folders that you want to delete and then click the button below</p>
|
||||||
|
<div>
|
||||||
|
<input type="hidden" name="csvIDs" />
|
||||||
|
<input type="submit" value="Delete the selected folders" />
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<form class="actionparams" id="sortitems_options" style="display: none">
|
||||||
|
<p id="sortitems_message" style="margin: 0">To reorganise your folders, drag them around as desired.</p>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
|
||||||
|
<!--
|
||||||
|
<ul style="float: left" id="SideTabs">
|
||||||
|
<li id="sidetab_sitetree" class="selected">Site Map</li>
|
||||||
|
<li id="sidetab_tasklist">Task List</li>
|
||||||
|
<li id="sidetab_waitingon">Waiting on</li>
|
||||||
|
<li id="sidetab_versions">Versions</li>
|
||||||
|
<li id="sidetab_comments">Comments</li>
|
||||||
|
<li id="sidetab_reports">Reports</li>
|
||||||
|
</ul>
|
||||||
|
-->
|
||||||
|
|
||||||
|
$SiteTreeAsUL
|
||||||
|
</div>
|
||||||
|
</div>
|
34
templates/Includes/AssetAdmin_right.ss
Executable file
@ -0,0 +1,34 @@
|
|||||||
|
<div class="title"><div >Folder contents</div></div>
|
||||||
|
|
||||||
|
<div id="form_actions_right" class="ajaxActions">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
$MoveMarkedOptionsForm
|
||||||
|
$DeleteMarkedOptionsForm
|
||||||
|
<!--
|
||||||
|
<form class="actionparams" id="action_movemarked_options" style="display:none" action="admin/assets/movemarked">
|
||||||
|
<fieldset>
|
||||||
|
<input type="hidden" name="ID" />
|
||||||
|
<div class="field">
|
||||||
|
<label class="left">Move files to</label>
|
||||||
|
$ChooseFolderField
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
<p class="actions">
|
||||||
|
<input type="submit" value="Move marked files" />
|
||||||
|
</p>
|
||||||
|
</form>
|
||||||
|
-->
|
||||||
|
|
||||||
|
<% if EditForm %>
|
||||||
|
$EditForm
|
||||||
|
<% else %>
|
||||||
|
<form id="Form_EditForm" action="admin/assets/?executeForm=EditForm" method="post" enctype="multipart/form-data">
|
||||||
|
<h1>$ApplicationName</h1>
|
||||||
|
|
||||||
|
<p>Welcome to $ApplicationName! Please choose a page from the left.</p>
|
||||||
|
</form>
|
||||||
|
<% end_if %>
|
||||||
|
|
||||||
|
<p id="statusMessage" style="visibility:hidden"></p>
|
27
templates/Includes/AssetTableField.ss
Executable file
@ -0,0 +1,27 @@
|
|||||||
|
<div id="$id" class="$Classes">
|
||||||
|
<% include TableListField_PageControls %>
|
||||||
|
<table class="data">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<% if Markable %><th width="18"> </th><% end_if %>
|
||||||
|
<% control Headings %>
|
||||||
|
<th class="$Name">$Title</th>
|
||||||
|
<% end_control %>
|
||||||
|
<th width="18"> </th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<% control Items %>
|
||||||
|
<tr id="record-$Parent.Name-$ID">
|
||||||
|
<% if Markable %><td width="18" class="markingcheckbox">$MarkingCheckbox</td><% end_if %>
|
||||||
|
<% control Fields %>
|
||||||
|
<td>$Value</td>
|
||||||
|
<% end_control %>
|
||||||
|
<td width="18">
|
||||||
|
<a class="popuplink editlink" href="$EditLink" target="_blank"><img src="cms/images/edit.gif" alt="edit" /></a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<% end_control %>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
20
templates/Includes/BulkLoaderAdmin_left.ss
Executable file
@ -0,0 +1,20 @@
|
|||||||
|
<div class="title"><div>Functions</div></div>
|
||||||
|
|
||||||
|
<div id="treepanes">
|
||||||
|
<div id="sitetree_holder" style="overflow:auto">
|
||||||
|
<% if BulkLoaders %>
|
||||||
|
<ul id="sitetree" class="tree unformatted">
|
||||||
|
<li id="$ID" class="root Root"><a>Batch entry functions</a>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<% control BulkLoaders %>
|
||||||
|
<li id="record-$class">
|
||||||
|
<a href="admin/bulkload/show/$class">$Title</a>
|
||||||
|
</li>
|
||||||
|
<% end_control %>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<% end_if %>
|
||||||
|
</div>
|
||||||
|
</div>
|
7
templates/Includes/BulkLoaderAdmin_right.ss
Executable file
@ -0,0 +1,7 @@
|
|||||||
|
<div class="title"><div>Bulk loader</div></div>
|
||||||
|
|
||||||
|
<form id="Form_EditForm" action="admin/bulkload?executeForm=EditForm" method="post" enctype="multipart/form-data">
|
||||||
|
<iframe style="width: 100%; height: 100%" src="admin/bulkload/iframe/$Class"></iframe>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<p id="statusMessage" style="visibility:hidden"></p>
|
28
templates/Includes/CMSLeft.ss
Executable file
@ -0,0 +1,28 @@
|
|||||||
|
<h1>Site Content</h1>
|
||||||
|
|
||||||
|
<ul id="TreeActions">
|
||||||
|
<li class="action" id="addpage"><a href="admin/addpage">New Page...</a></li>
|
||||||
|
<li class="action" id="deletepage"><a href="admin/deletepage">Delete Pages...</a></li>
|
||||||
|
<li class="spacer"></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<form class="actionparams" id="addpage_options" style="display: none" action="admin/addpage">
|
||||||
|
<select name="Type">
|
||||||
|
<% control PageTypes %>
|
||||||
|
<option value="$ClassName">$AddAction</option>
|
||||||
|
<% end_control %>
|
||||||
|
</select>
|
||||||
|
<input type="hidden" name="ParentID" />
|
||||||
|
<input class="action" type="submit" value="Go" />
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<form class="actionparams" id="deletepage_options" style="display: none" action="admin/deletepages">
|
||||||
|
<p>Select the pages that you want to delete and then click the button below</p>
|
||||||
|
|
||||||
|
<input type="hidden" name="csvIDs" />
|
||||||
|
<input type="submit" value="Delete the selected pages" />
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<div id="sitetree_holder" style="overflow:auto">
|
||||||
|
$SiteTreeAsUL
|
||||||
|
</div>
|
130
templates/Includes/CMSMain_left.ss
Executable file
@ -0,0 +1,130 @@
|
|||||||
|
<div class="title"><div style="background-image : url(cms/images/panels/MySite.png)">My Site</div></div>
|
||||||
|
|
||||||
|
<ul style="float: left; clear: left;" id="SideTabs">
|
||||||
|
<li id="sidetab_sitetree" class="selected">Site Map</li>
|
||||||
|
<!--<li id="sidetab_search">Search</li>-->
|
||||||
|
<% if EnterpriseCMS %>
|
||||||
|
<li id="sidetab_tasklist">Task List</li>
|
||||||
|
<li id="sidetab_waitingon">Waiting on</li>
|
||||||
|
<% end_if %>
|
||||||
|
<li id="sidetab_versions">Versions</li>
|
||||||
|
<% if EnterpriseCMS %>
|
||||||
|
<li id="sidetab_comments">Comments</li>
|
||||||
|
<% end_if %>
|
||||||
|
<li id="sidetab_reports">Reports</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<div id="treepanes">
|
||||||
|
|
||||||
|
<div id="sitetree_holder">
|
||||||
|
|
||||||
|
<ul id="TreeActions">
|
||||||
|
<li class="action" id="addpage"><a href="admin/addpage">Create</a></li>
|
||||||
|
<li class="action" id="deletepage"><a href="admin/deletepage">Delete</a></li>
|
||||||
|
<li class="action" id="sortitems"><a href="#">Reorganise</a></li>
|
||||||
|
<!-- <li class="action" id="duplicate"><a href="#">Duplicate</a></li>
|
||||||
|
Sam: this should be put into the Create area, I think, so we don't stuff up the layout -->
|
||||||
|
</ul>
|
||||||
|
<div style="clear:both;"></div>
|
||||||
|
<% control AddPageOptionsForm %>
|
||||||
|
<form class="actionparams" id="$FormName" style="display: none" action="$FormAction">
|
||||||
|
<% control Fields %>
|
||||||
|
$FieldHolder
|
||||||
|
<% end_control %>
|
||||||
|
<!--
|
||||||
|
<div>
|
||||||
|
<select name="Type">
|
||||||
|
<% control PageTypes %>
|
||||||
|
<option value="$ClassName">$AddAction</option>
|
||||||
|
<% end_control %>
|
||||||
|
</select>
|
||||||
|
<input type="hidden" name="ParentID" />
|
||||||
|
</div>
|
||||||
|
-->
|
||||||
|
<div>
|
||||||
|
<input class="action" type="submit" value="Go" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</form>
|
||||||
|
<% end_control %>
|
||||||
|
|
||||||
|
<form class="actionparams" id="deletepage_options" style="display: none" action="admin/deleteitems">
|
||||||
|
<p>Select the pages that you want to delete and then click the button below</p>
|
||||||
|
<div>
|
||||||
|
<input type="hidden" name="csvIDs" />
|
||||||
|
<input type="submit" value="Delete the selected pages" />
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<form class="actionparams" id="sortitems_options" style="display: none">
|
||||||
|
<p id="sortitems_message" style="margin: 0">To reorganise your site, drag the pages around as desired.</p>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<% control DuplicatePagesOptionsForm %>
|
||||||
|
<form class="actionparams" id="duplicate_options" style="display: none" action="admin/duplicateSiteTree">
|
||||||
|
<p>Select the pages that you want to duplicate, whether it's children should be included, and where you want the duplicates placed</p>
|
||||||
|
<div>
|
||||||
|
<input type="hidden" name="csvIDs" />
|
||||||
|
<input type="submit" value="Duplicate" />
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<% end_control %>
|
||||||
|
|
||||||
|
<div id="publication_key" style="border-bottom: 1px #CCC solid; background-color: #EEE; padding: 3px;">
|
||||||
|
Key:
|
||||||
|
<ins style="cursor: help" title="Added to the stage site and not published yet">new</ins>
|
||||||
|
<del style="cursor: help" title="Deleted from the stage site but still on the live site">deleted</del>
|
||||||
|
<span style="cursor: help" title="Edited on the stage site and not published yet" class="modified">changed</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
$SiteTreeAsUL
|
||||||
|
</div>
|
||||||
|
<!--<div id="search_holder" style="display:none">
|
||||||
|
<h2>Search</h2>
|
||||||
|
<div class="unitBody">
|
||||||
|
</div>
|
||||||
|
</div>-->
|
||||||
|
|
||||||
|
<% if EnterpriseCMS %>
|
||||||
|
<div class="listpane" id="tasklist_holder" style="display:none">
|
||||||
|
<h2>Tasklist</h2>
|
||||||
|
<div class="unitBody">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="listpane" id="waitingon_holder" style="display:none">
|
||||||
|
<h2>Waiting on</h2>
|
||||||
|
<div class="unitBody">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<% end_if %>
|
||||||
|
<div class="listpane" id="versions_holder" style="display:none">
|
||||||
|
<h2>History</h2>
|
||||||
|
|
||||||
|
<p class="pane_actions" id="versions_actions">
|
||||||
|
<select>
|
||||||
|
<option value="view" selected="selected">View (click to see)</option>
|
||||||
|
<option value="compare" >Compare (click 2 to see)</option>
|
||||||
|
</select>
|
||||||
|
<br />
|
||||||
|
|
||||||
|
<input type="checkbox" id="versions_showall" /> Show unpublished versions
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="unitBody">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<% if EnterpriseCMS %>
|
||||||
|
<div class="listpane" id="comments_holder" style="display:none">
|
||||||
|
<h2>Comments</h2>
|
||||||
|
<div class="unitBody">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<% end_if %>
|
||||||
|
<div class="listpane" id="reports_holder" style="display:none">
|
||||||
|
<h2>Reports</h2>
|
||||||
|
<p id="ReportSelector_holder">$ReportSelector</p>
|
||||||
|
<div class="unitBody">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
38
templates/Includes/CMSMain_right.ss
Executable file
@ -0,0 +1,38 @@
|
|||||||
|
<div class="title"><div style="background-image : url(cms/images/panels/EditPage.png)">Edit Page</div></div>
|
||||||
|
<% include Editor_toolbar %>
|
||||||
|
|
||||||
|
<div id="form_actions_right" class="ajaxActions">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form class="actionparams" id="action_submit_options" style="display:none" action="admin/submit">
|
||||||
|
<fieldset>
|
||||||
|
<input type="hidden" name="ID" />
|
||||||
|
<div class="field" id="action_submit_options_recipient">
|
||||||
|
<label class="left">Send to</label>
|
||||||
|
<span>loading...</span>
|
||||||
|
</div>
|
||||||
|
<div class="field" id="action_submit_options_status">
|
||||||
|
<label class="left">Status</label>
|
||||||
|
<input type="hidden" name="Status" />
|
||||||
|
<span></span>
|
||||||
|
</div>
|
||||||
|
<p class="label">Do you have any message for your editor?</p>
|
||||||
|
<textarea name="Message" rows="4" cols="20"></textarea>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
<p class="actions">
|
||||||
|
<input type="submit" value="Submit for approval" />
|
||||||
|
</p>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<% if EditForm %>
|
||||||
|
$EditForm
|
||||||
|
<% else %>
|
||||||
|
<form id="Form_EditForm" action="admin?executeForm=EditForm" method="post" enctype="multipart/form-data">
|
||||||
|
<h1>$ApplicationName</h1>
|
||||||
|
|
||||||
|
<p>Welcome to $ApplicationName! Please choose a page from the left.</p>
|
||||||
|
</form>
|
||||||
|
<% end_if %>
|
||||||
|
|
||||||
|
<p id="statusMessage" style="visibility:hidden"></p>
|
0
templates/Includes/CMSMain_rightbottom.ss
Normal file
21
templates/Includes/CMSRight.ss
Executable file
@ -0,0 +1,21 @@
|
|||||||
|
<h1>Edit Content</h1>
|
||||||
|
|
||||||
|
<div class="mceToolbar_background"> </div>
|
||||||
|
|
||||||
|
<p id="statusMessage"></p>
|
||||||
|
|
||||||
|
<div id="form_actions">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<% if EditForm %>
|
||||||
|
$EditForm
|
||||||
|
<% else %>
|
||||||
|
<h1>$ApplicationName</h1>
|
||||||
|
<div class="mceToolbar_background"> </div>
|
||||||
|
<p id="statusMessage" style="display:none"></p>
|
||||||
|
|
||||||
|
<form id="Form_EditForm" action="admin?executeForm=EditForm" method="post" enctype="multipart/form-data">
|
||||||
|
<p>Welcome to $ApplicationName! Please choose a page from the left.</p>
|
||||||
|
</form>
|
||||||
|
<% end_if %>
|
||||||
|
</body>
|
41
templates/Includes/Editor_toolbar.ss
Executable file
@ -0,0 +1,41 @@
|
|||||||
|
<% control EditorToolbar %>
|
||||||
|
<div class="mceToolbarExternal" id="mce_editor_toolbar">
|
||||||
|
<table width="100%" border="0">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<!--
|
||||||
|
<a title="Jump to tool buttons - Alt+Q, Jump to editor - Alt-Z, Jump to element path - Alt-X" accesskey="q" href="#">
|
||||||
|
</a>
|
||||||
|
-->
|
||||||
|
|
||||||
|
<% control Buttons %>
|
||||||
|
<% if Type = button %>
|
||||||
|
<a href="#$Command">
|
||||||
|
<img width="20" height="20" class="mceButtonNormal" title="$Title" alt="$Title" src="$Icon" id="mce_editor_$IDSegment" />
|
||||||
|
</a>
|
||||||
|
<% else_if Type = dropdown %>
|
||||||
|
<select name="$Command" class="mceSelectList" id="mce_editor_$IDSegment">$Options</select>
|
||||||
|
<% else_if Type = separator %>
|
||||||
|
<img width="1" height="15" class="mceSeparatorLine" src="{$MceRoot}themes/advanced/images/spacer.gif" alt="|" />
|
||||||
|
<% else_if Type = break %>
|
||||||
|
<br />
|
||||||
|
<% end_if %>
|
||||||
|
<% end_control %>
|
||||||
|
|
||||||
|
<br />
|
||||||
|
<!--<a onfocus="tinyMCE.getInstanceById('mce_editor_0').getWin().focus();" accesskey="z" href="#">
|
||||||
|
</a>-->
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
$LinkForm
|
||||||
|
|
||||||
|
$ImageForm
|
||||||
|
|
||||||
|
$FlashForm
|
||||||
|
|
||||||
|
<% end_control %>
|
28
templates/Includes/GenericDataAdmin_left.ss
Executable file
@ -0,0 +1,28 @@
|
|||||||
|
<style>
|
||||||
|
#left_separator {
|
||||||
|
border-bottom: 3px solid #d4d0c8;
|
||||||
|
}
|
||||||
|
|
||||||
|
#SearchFrom_holder{
|
||||||
|
border-bottom: 1px solid #808080;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<div id="LeftPane">
|
||||||
|
<h2>Add Listing</h2>
|
||||||
|
<div id="SearchForm_holder" class="lefttop" style="overflow:auto">
|
||||||
|
$AddForm
|
||||||
|
</div>
|
||||||
|
<h2>Search Listings</h2>
|
||||||
|
<div id="SearchForm_holder" class="leftbottom" style="overflow:auto">
|
||||||
|
$SearchForm
|
||||||
|
</div>
|
||||||
|
<div id="left_separator">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<h2>Search Results</h2>
|
||||||
|
<div id="ResultTable_holder" class="leftbottom" style="overflow:auto">
|
||||||
|
$Results
|
||||||
|
</div>
|
||||||
|
</div>
|
16
templates/Includes/GenericDataAdmin_right.ss
Executable file
@ -0,0 +1,16 @@
|
|||||||
|
<div class="title"><div style="background-image : url(cms/images/panels/EditPage.png)">Edit Page</div></div>
|
||||||
|
<% include Editor_toolbar %>
|
||||||
|
|
||||||
|
<% if EditForm %>
|
||||||
|
$EditForm
|
||||||
|
<% else %>
|
||||||
|
<form id="Form_EditForm" action="admin?executeForm=EditForm" method="post" enctype="multipart/form-data">
|
||||||
|
<h1>$ApplicationName</h1>
|
||||||
|
|
||||||
|
<p>Welcome to $ApplicationName! Please choose click on one of the entries on the left pane.</p>
|
||||||
|
|
||||||
|
</form>
|
||||||
|
<% end_if %>
|
||||||
|
|
||||||
|
|
||||||
|
<p id="statusMessage" style="visibility:hidden"></p>
|
0
templates/Includes/LeftAndMain_rightbottom.ss
Normal file
15
templates/Includes/MemberList_PageControls.ss
Executable file
@ -0,0 +1,15 @@
|
|||||||
|
<div class="PageControls">
|
||||||
|
<input name="MemberListStart" id="MemberListStart" type="hidden" value="$MemberListStart" />
|
||||||
|
<% if LastLink %><a class="Last" href="$LastLink" title="View last $PageSize members"><img src="cms/images/pagination/record-last.png" alt="View last $PageSize members" /></a>
|
||||||
|
<% else %><span class="Last"><img src="cms/images/pagination/record-last-g.png" alt="View last $PageSize members" /></span><% end_if %>
|
||||||
|
<% if FirstLink %><a class="First" href="$FirstLink" title="View first $PageSize members"><img src="cms/images/pagination/record-first.png" alt="View first $PageSize members" /></a>
|
||||||
|
<% else %><span class="First"><img src="cms/images/pagination/record-first-g.png" alt="View first $PageSize members" /></span><% end_if %>
|
||||||
|
<% if PrevLink %><a class="Prev" href="$PrevLink" title="View previous $PageSize members"><img src="cms/images/pagination/record-prev.png" alt="View previous $PageSize members" /></a>
|
||||||
|
<% else %><img class="Prev" src="cms/images/pagination/record-prev-g.png" alt="View previous $PageSize members" /><% end_if %>
|
||||||
|
<span class="Count">
|
||||||
|
Displaying $FirstMember to $LastMember of $TotalMembers
|
||||||
|
</span>
|
||||||
|
<% if NextLink %><a class="Next" href="$NextLink" title="View next $PageSize members"><img src="cms/images/pagination/record-next.png" alt="View next $PageSize members" /></a>
|
||||||
|
<% else %><img class="Next" src="cms/images/pagination/record-next-g.png" alt="View next $PageSize members" /><% end_if %>
|
||||||
|
|
||||||
|
</div>
|
42
templates/Includes/MemberList_Table.ss
Executable file
@ -0,0 +1,42 @@
|
|||||||
|
<% include MemberList_PageControls %>
|
||||||
|
<table class="CMSList MemberList">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<td class="FirstName">First Name</td>
|
||||||
|
<td class="Surname">Surname</td>
|
||||||
|
<td class="Email">Email Address</td>
|
||||||
|
<% if DontShowPassword %>
|
||||||
|
<% else %>
|
||||||
|
<td class="Password">Password</td>
|
||||||
|
<% end_if %>
|
||||||
|
<td> </td>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
|
||||||
|
<tbody>
|
||||||
|
|
||||||
|
<% if DontShowPassword %>
|
||||||
|
<% control Members %>
|
||||||
|
<tr id="member-$ID">
|
||||||
|
<td>$FirstName</td>
|
||||||
|
<td>$Surname</td>
|
||||||
|
<td>$Email</td>
|
||||||
|
<td><a class="deletelink" href="admin/security/removememberfromgroup/$GroupID/$ID"><img src="cms/images/delete.gif" alt="delete" /></a></td>
|
||||||
|
</tr>
|
||||||
|
<% end_control %>
|
||||||
|
<% else %>
|
||||||
|
<% control Members %>
|
||||||
|
<tr id="member-$ID">
|
||||||
|
<td>$FirstName</td>
|
||||||
|
<td>$Surname</td>
|
||||||
|
<td>$Email</td>
|
||||||
|
<td>$Password</td>
|
||||||
|
<td><a class="deletelink" href="admin/security/removememberfromgroup/$GroupID/$ID"><img src="cms/images/delete.gif" alt="delete" /></a></td>
|
||||||
|
</tr>
|
||||||
|
<% end_control %>
|
||||||
|
<% end_if %>
|
||||||
|
|
||||||
|
|
||||||
|
$AddRecordForm.AsTableRow
|
||||||
|
</tbody>
|
||||||
|
</table>
|
77
templates/Includes/MemberTableField.ss
Executable file
@ -0,0 +1,77 @@
|
|||||||
|
<div class="$Classes" id="$id">
|
||||||
|
<div class="MemberFilter">
|
||||||
|
$SearchForm
|
||||||
|
</div>
|
||||||
|
<div id="MemberList">
|
||||||
|
<% include TableListField_PageControls %>
|
||||||
|
<table class="data">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<% if Markable %><th width="18"> </th><% end_if %>
|
||||||
|
<% control Headings %>
|
||||||
|
<th class="$Name">$Title</th>
|
||||||
|
<% end_control %>
|
||||||
|
<% if Can(show) %><th width="18"> </th><% end_if %>
|
||||||
|
<% if Can(edit) %><th width="18"> </th><% end_if %>
|
||||||
|
<% if Can(delete) %><th width="18"> </th><% end_if %>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tfoot>
|
||||||
|
<tr class="addtogrouprow">
|
||||||
|
<% if Markable %><td width="18"> </dh><% end_if %>
|
||||||
|
$AddRecordForm.CellFields
|
||||||
|
<td class="actions" colspan="2">$AddRecordForm.CellActions</td>
|
||||||
|
</tr>
|
||||||
|
<tr style="display: none;">
|
||||||
|
<% if Markable %><td width="18"> </td><% end_if %>
|
||||||
|
<td colspan="$ItemCount">
|
||||||
|
<a class="popuplink addlink" href="$AddLink" alt="add"><img src="cms/images/add.gif" alt="add" /></a><a class="popuplink addlink" href="$AddLink" alt="add">Add new $Title</a>
|
||||||
|
</td>
|
||||||
|
<% if Can(show) %><td width="18"> </td><% end_if %>
|
||||||
|
<% if Can(edit) %><td width="18"> </td><% end_if %>
|
||||||
|
<% if Can(delete) %><td width="18"> </td><% end_if %>
|
||||||
|
</tr>
|
||||||
|
</tfoot>
|
||||||
|
<tbody>
|
||||||
|
<% if Items %>
|
||||||
|
<% control Items %>
|
||||||
|
<tr id="record-$Parent.Name-$ID">
|
||||||
|
<% if Markable %><td width="18" class="markingcheckbox">$MarkingCheckbox</td><% end_if %>
|
||||||
|
<% control Fields %>
|
||||||
|
<td>$Value</td>
|
||||||
|
<% end_control %>
|
||||||
|
<% if Can(show) %>
|
||||||
|
<td width="18">
|
||||||
|
<a class="popuplink showlink" href="$ShowLink" title="Show this member" target="_blank"><img src="cms/images/show.png" alt="show" /></a>
|
||||||
|
</td>
|
||||||
|
<% end_if %>
|
||||||
|
<% if Can(edit) %>
|
||||||
|
<td width="18">
|
||||||
|
<a class="popuplink editlink" href="$EditLink" title="Edit this member" target="_blank"><img src="cms/images/edit.gif" alt="edit" /></a>
|
||||||
|
</td>
|
||||||
|
<% end_if %>
|
||||||
|
<% if Can(delete) %>
|
||||||
|
<td width="18">
|
||||||
|
<a class="deletelink" href="$DeleteLink" title="Delete this member"><img src="cms/images/delete.gif" alt="delete" /></a>
|
||||||
|
</td>
|
||||||
|
<% end_if %>
|
||||||
|
</tr>
|
||||||
|
<% end_control %>
|
||||||
|
<% else %>
|
||||||
|
<tr class="notfound">
|
||||||
|
<% if Markable %><th width="18"> </th><% end_if %>
|
||||||
|
<td colspan="$Headings.Count"><i>No $NamePlural found</i></td>
|
||||||
|
<% if Can(show) %><td width="18"> </td><% end_if %>
|
||||||
|
<% if Can(edit) %><td width="18"> </td><% end_if %>
|
||||||
|
<% if Can(delete) %><td width="18"> </td><% end_if %>
|
||||||
|
</tr>
|
||||||
|
<% end_if %>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<div class="utility">
|
||||||
|
<% if Can(export) %>
|
||||||
|
$ExportButton
|
||||||
|
<% end_if %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
24
templates/Includes/NewsletterAdmin_BouncedList.ss
Executable file
@ -0,0 +1,24 @@
|
|||||||
|
<% if Entries %>
|
||||||
|
<table class="CMSList BouncedList" summary="Emails that have bounced">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>User name</th>
|
||||||
|
<th>Email address</th>
|
||||||
|
<th>Reason:</th>
|
||||||
|
<th>Last bounce at</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<% control Entries %>
|
||||||
|
<tr>
|
||||||
|
<td>$Member.FirstName $Member.Surname</td>
|
||||||
|
<td>$Member.Email</td>
|
||||||
|
<td>$Record.BounceMessage</td>
|
||||||
|
<td>$Record.Created.Long</td>
|
||||||
|
</tr>
|
||||||
|
<% end_control %>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<% else %>
|
||||||
|
<p>No emails sent have bounced.</p>
|
||||||
|
<% end_if %>
|
30
templates/Includes/NewsletterAdmin_SiteTree.ss
Executable file
@ -0,0 +1,30 @@
|
|||||||
|
<% if NewsletterTypes %>
|
||||||
|
<ul id="sitetree" class="tree unformatted">
|
||||||
|
<% control NewsletterTypes %>
|
||||||
|
<li id="mailtype_$ID" class="MailType">
|
||||||
|
<a href="#">$Title</a>
|
||||||
|
<ul>
|
||||||
|
<li id="drafts_$ID" class="DraftFolder nodelete"><a href="$baseURL/admin/showtype/$ID">Drafts</a>
|
||||||
|
<% if DraftNewsletters %>
|
||||||
|
<ul>
|
||||||
|
<% control DraftNewsletters %>
|
||||||
|
<li class="Draft" id="draft_{$ParentID}_{$ID}"><a href="$baseURL/admin/newsletter/shownewsletter/$ID">$Title</a></li>
|
||||||
|
<% end_control %>
|
||||||
|
</ul>
|
||||||
|
<% end_if %>
|
||||||
|
</li>
|
||||||
|
<li id="sent_$ID" class="SentFolder nodelete"><a href="$baseURL/admin/showtype/$ID">Sent Items</a>
|
||||||
|
<% if SentNewsletters %>
|
||||||
|
<ul>
|
||||||
|
<% control SentNewsletters %>
|
||||||
|
<li class="Sent" id="sent_{$ParentID}_{$ID}"><a href="$baseURL/admin/newsletter/shownewsletter/$ID">$Title</a></li>
|
||||||
|
<% end_control %>
|
||||||
|
</ul>
|
||||||
|
<% end_if %>
|
||||||
|
</li>
|
||||||
|
<li id="recipients_$ID" class="Recipients nodelete"><a href="$baseURL/admin/newsletter/showtype/$ID">Mailing List</a></li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<% end_control %>
|
||||||
|
</ul>
|
||||||
|
<% end_if %>
|
20
templates/Includes/NewsletterAdmin_UnsubscribedList.ss
Executable file
@ -0,0 +1,20 @@
|
|||||||
|
<% if Entries %>
|
||||||
|
<table class="CMSList UnsubscribedList" summary="Unsubscribed users">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>User name</td>
|
||||||
|
<td>Unsubscribed on</td>
|
||||||
|
</tr>
|
||||||
|
<% control Entries %>
|
||||||
|
<tr>
|
||||||
|
<td>$Member.FirstName $Member.Surname</td>
|
||||||
|
<td>$Record.Created.Long</td>
|
||||||
|
</tr>
|
||||||
|
<% end_control %>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<% else %>
|
||||||
|
<p>
|
||||||
|
No users have unsubscribed from this newsletter.
|
||||||
|
</p>
|
||||||
|
<% end_if %>
|
50
templates/Includes/NewsletterAdmin_left.ss
Executable file
@ -0,0 +1,50 @@
|
|||||||
|
<style>
|
||||||
|
ul.tree li.MailType span.MailType a {
|
||||||
|
background-image: url(cms/images/treeicons/sent-folder.png);
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
ul.tree li.MailType span.Recipients a {
|
||||||
|
background-image: url(cms/images/treeicons/multi-user.png);
|
||||||
|
}
|
||||||
|
ul.tree li.MailType span.Recipients a {
|
||||||
|
background-image: url(cms/images/treeicons/multi-user.png);
|
||||||
|
}
|
||||||
|
ul.tree li.MailType span.SentFolder a, ul.tree span.SentFolder.children a {
|
||||||
|
background-image: url(cms/images/treeicons/sent-folder.png);
|
||||||
|
}
|
||||||
|
ul.tree li.MailType span.DraftFolder a, ul.tree span.DraftFolder.children a {
|
||||||
|
background-image: url(cms/images/treeicons/draft-folder.png);
|
||||||
|
}
|
||||||
|
ul.tree li.MailType span.Draft a {
|
||||||
|
background-image: url(cms/images/treeicons/draft-file.png);
|
||||||
|
}
|
||||||
|
ul.tree li.MailType span.Sent a {
|
||||||
|
background-image: url(cms/images/treeicons/sent-file.gif);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<div class="title"><div>Newsletters</div></div>
|
||||||
|
<div id="treepanes">
|
||||||
|
<div id="sitetree_holder">
|
||||||
|
<ul id="TreeActions">
|
||||||
|
<li class="action" id="addtype"><a href="admin/newsletter/addtype">Create</a></li>
|
||||||
|
<li class="action" id="deletedrafts"><a href="admin/newsletter/deletedrafts">Delete</a></li>
|
||||||
|
</ul>
|
||||||
|
<div style="clear:both;"></div>
|
||||||
|
<form class="actionparams" id="addtype_options" style="display: none" action="admin/newsletter/addtype">
|
||||||
|
<input type="hidden" name="ParentID" value="" />
|
||||||
|
<select name="PageType" id="add_type">
|
||||||
|
<option value="type">Add new type</option>
|
||||||
|
<option value="draft">Add new draft</option>
|
||||||
|
</select>
|
||||||
|
<input class="action" type="submit" value="Go" />
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<form class="actionparams" id="deletedrafts_options" style="display: none" action="admin/newsletter/remove">
|
||||||
|
<p>Select the drafts that you want to delete and then click the button below</p>
|
||||||
|
<input type="hidden" name="csvIDs" />
|
||||||
|
<input type="submit" value="Delete the selected drafts" />
|
||||||
|
</form>
|
||||||
|
<% include NewsletterAdmin_SiteTree %>
|
||||||
|
</div>
|
||||||
|
</div>
|
32
templates/Includes/NewsletterAdmin_right.ss
Executable file
@ -0,0 +1,32 @@
|
|||||||
|
<div class="title"><div>Edit Content</div></div>
|
||||||
|
|
||||||
|
<% include Editor_toolbar %>
|
||||||
|
|
||||||
|
<div id="form_actions_right" class="ajaxActions">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<form class="actionparams" id="action_send_options" style="display:none" action="admin/newsletter/sendnewsletter">
|
||||||
|
<fieldset>
|
||||||
|
<input type="hidden" name="NewsletterID" />
|
||||||
|
<ul class="optionset">
|
||||||
|
<li><input name="SendType" id="SendTypeTest" value="Test" checked="checked" type="radio" /> Send test to <input name="TestEmail" type="text" /></li>
|
||||||
|
<li><input name="SendType" id="SendTypeList" value="List" type="radio" /> Send to the mailing list</li>
|
||||||
|
</ul>
|
||||||
|
$SendProgressBar
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
<p class="actions">
|
||||||
|
<input type="submit" value="Send newsletter" />
|
||||||
|
</p>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<% if EditForm %>
|
||||||
|
$EditForm
|
||||||
|
<% else %>
|
||||||
|
<form id="Form_EditForm" action="admin/newsletter?executeForm=EditForm" method="post" enctype="multipart/form-data">
|
||||||
|
<p>Welcome to the $ApplicationName newsletter admininistration section. Please choose a folder from the left.</p>
|
||||||
|
</form>
|
||||||
|
<% end_if %>
|
||||||
|
|
||||||
|
<p id="statusMessage" style="visibility:hidden"></p>
|
14
templates/Includes/ReportAdmin_SiteTree.ss
Executable file
@ -0,0 +1,14 @@
|
|||||||
|
<% if Reports %>
|
||||||
|
|
||||||
|
<ul id="sitetree" class="tree unformatted">
|
||||||
|
<li id="$ID" class="root"><a>Reports</a>
|
||||||
|
<ul>
|
||||||
|
<% control Reports %>
|
||||||
|
<li id="$ID">
|
||||||
|
<a href="$baseURL/admin/showreport/$ID" title="$TreeDescription">$TreeTitle</a>
|
||||||
|
</li>
|
||||||
|
<% end_control %>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<% end_if %>
|
17
templates/Includes/ReportAdmin_left.ss
Executable file
@ -0,0 +1,17 @@
|
|||||||
|
<style>
|
||||||
|
|
||||||
|
|
||||||
|
ul.tree a{
|
||||||
|
background-image: url(cms/images/treeicons/reports-file.png);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<div class="title"><div>Reports</div></div>
|
||||||
|
|
||||||
|
<div id="treepanes">
|
||||||
|
<div id="sitetree_holder">
|
||||||
|
<% include ReportAdmin_SiteTree %>
|
||||||
|
</div>
|
||||||
|
</div>
|
11
templates/Includes/ReportAdmin_right.ss
Executable file
@ -0,0 +1,11 @@
|
|||||||
|
<div class="title"><div>Report Details</div></div>
|
||||||
|
|
||||||
|
<% if EditForm %>
|
||||||
|
$EditForm
|
||||||
|
<% else %>
|
||||||
|
<form id="Form_EditForm" action="admin/reports?executeForm=EditForm" method="post" enctype="multipart/form-data">
|
||||||
|
<p>Welcome to the $ApplicationName reporting section. Please choose a specific report from the left.</p>
|
||||||
|
</form>
|
||||||
|
<% end_if %>
|
||||||
|
|
||||||
|
<p id="statusMessage" style="visibility:hidden"></p>
|
28
templates/Includes/SecurityAdmin_left.ss
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
<div class="title"><div>Security Groups</div></div>
|
||||||
|
<div id="treepanes">
|
||||||
|
<div id="sitetree_holder">
|
||||||
|
<ul id="TreeActions">
|
||||||
|
<li class="action" id="addgroup"><a href="admin/security/addgroup">Create</a></li>
|
||||||
|
<li class="action" id="deletegroup"><a href="admin/security/deletegroup">Delete</a></li>
|
||||||
|
<li class="action" id="sortitems"><a href="#">Reorganise</a></li>
|
||||||
|
</ul>
|
||||||
|
<div style="clear:both;"></div>
|
||||||
|
<form class="actionparams" id="addgroup_options" style="display: none" action="admin/security/addgroup">
|
||||||
|
<input type="hidden" name="ParentID" />
|
||||||
|
<input class="action" type="submit" value="Go" />
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<form class="actionparams" id="deletegroup_options" style="display: none" action="admin/security/deleteitems">
|
||||||
|
<p>Select the pages that you want to delete and then click the button below</p>
|
||||||
|
|
||||||
|
<input type="hidden" name="csvIDs" />
|
||||||
|
<input type="submit" value="Delete the selected groups" />
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<form class="actionparams" id="sortitems_options" style="display: none" action="">
|
||||||
|
<p id="sortitems_message" style="margin: 0">To reorganise your site, drag the pages around as desired.</p>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
$SiteTreeAsUL
|
||||||
|
</div>
|
||||||
|
</div>
|
14
templates/Includes/SecurityAdmin_right.ss
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<div class="title"><div>Users in this Group</div></div>
|
||||||
|
|
||||||
|
<div id="form_actions_right" class="ajaxActions">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<% if EditForm %>
|
||||||
|
$EditForm
|
||||||
|
<% else %>
|
||||||
|
<form id="Form_EditForm" action="admin/security?executeForm=EditForm" method="post" enctype="multipart/form-data">
|
||||||
|
<p>Welcome to the $ApplicationName security admininistration section. Please choose a group from the left.</p>
|
||||||
|
</form>
|
||||||
|
<% end_if %>
|
||||||
|
|
||||||
|
<p id="statusMessage" style="visibility:hidden"></p>
|
72
templates/LeftAndMain.ss
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
|
||||||
|
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" >
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
|
||||||
|
<% base_tag %>
|
||||||
|
<title>$ApplicationName</title>
|
||||||
|
|
||||||
|
<link rel="stylesheet" type="text/css" href="cms/css/typography.css" />
|
||||||
|
<link rel="stylesheet" type="text/css" href="cms/css/layout.css" />
|
||||||
|
<link rel="stylesheet" type="text/css" href="cms/css/cms_left.css" />
|
||||||
|
<link rel="stylesheet" type="text/css" href="cms/css/cms_right.css" />
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body class="stillLoading">
|
||||||
|
<div id="Loading" style="background: #FFF url(cms/images/loading.gif) 50% 50% no-repeat; position: absolute;z-index: 100000;height: 100%;width: 100%;margin: 0;padding: 0;z-index: 100000;position: absolute;">Loading...</div>
|
||||||
|
|
||||||
|
<div id="top">
|
||||||
|
<div id="Logo" style="$LogoStyle">
|
||||||
|
<% if ApplicationLogoText %>
|
||||||
|
<a href="http://www.silverstripe.com/">$ApplicationLogoText</a><br />
|
||||||
|
<% end_if %>
|
||||||
|
</div>
|
||||||
|
<ul id="MainMenu">
|
||||||
|
<% control MainMenu %>
|
||||||
|
<li class="$LinkingMode" id="Menu-$Code"><a href="$Link">$Title</a></li>
|
||||||
|
<% end_control %>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
</div><div id="left" style="float:left">
|
||||||
|
$Left
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div id="separator" style="float:left">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="right" id="right">
|
||||||
|
$Right
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<% if RightBottom %>
|
||||||
|
<div class="right" id="rightbottom">
|
||||||
|
$RightBottom
|
||||||
|
</div>
|
||||||
|
<% end_if %>
|
||||||
|
|
||||||
|
<div id="bottom">
|
||||||
|
<div class="holder">
|
||||||
|
<div id="logInStatus">
|
||||||
|
<a href="http://www.silverstripe.com" title="Silverstripe Website">Silverstripe CMS</a> -
|
||||||
|
<abbr style="border-style: none" title="This is the $ApplicationName version that you are currently running, technically it's the CVS branch">$CMSVersion</abbr>
|
||||||
|
<% control CurrentMember %>
|
||||||
|
Logged in as $FirstName $Surname - <a href="Security/logout">log out</a>
|
||||||
|
<% end_control %>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="switchView" class="bottomTabs">
|
||||||
|
<% if class = CMSMain %>
|
||||||
|
<div class="blank"> View page in: </div>
|
||||||
|
<% else %>
|
||||||
|
<div class="blank"> Switch to: </div>
|
||||||
|
<% end_if %>
|
||||||
|
<a class="current">CMS</a>
|
||||||
|
<a id="viewStageSite" href="home/?stage=Stage" style="left : -1px;">Draft Site</a>
|
||||||
|
<a id="viewLiveSite" href="home/?stage=Live" style="left : -3px;">Published Site</a>
|
||||||
|
<a style="display: none;left : -5px;" id="viewArchivedSite" href="home/">Archived Site</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script type="text/javascript">Behaviour.addLoader(hideLoading)</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
12
templates/LeftAndMain_printable.ss
Executable file
@ -0,0 +1,12 @@
|
|||||||
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
|
||||||
|
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" >
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
|
||||||
|
<% base_tag %>
|
||||||
|
<title>SilverStripe CMS - $Title</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body onblur="window.close()">
|
||||||
|
$PrintForm
|
||||||
|
</body>
|
||||||
|
</html>
|
17
templates/MemberList.ss
Executable file
@ -0,0 +1,17 @@
|
|||||||
|
<div id="MemberListField">
|
||||||
|
<div class="MemberListFilter">
|
||||||
|
<a class="showhide closed" href="#">Filter</a>
|
||||||
|
<div id="MemberListFilterControls">
|
||||||
|
<input id="MemberListBaseGroupID" type="hidden" name="MemberListBaseGroup" value="$GroupID" />
|
||||||
|
<input id="MemberListDontShowPassword" type="hidden" name="MemberListDontShowPassword" value="$DontShowPassword" />
|
||||||
|
$SearchField
|
||||||
|
$OrderByField
|
||||||
|
$GroupFilter
|
||||||
|
<input id="MemberListFilterButton" type="submit" value="Filter" name="filter" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="MemberList">
|
||||||
|
<% include MemberList_Table %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
19
templates/NewsletterList.ss
Executable file
@ -0,0 +1,19 @@
|
|||||||
|
<table id="{$Status}List" class="CMSList NewsletterList">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<td class="Email">Subject</td>
|
||||||
|
<td class="Surname">Content</td>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
|
||||||
|
<tbody>
|
||||||
|
<% control Newsletters %>
|
||||||
|
<tr id="letter-$ID">
|
||||||
|
<td>$Subject</td>
|
||||||
|
<td>$Content</td>
|
||||||
|
</tr>
|
||||||
|
<% end_control %>
|
||||||
|
|
||||||
|
<!-- $AddRecordForm.AsTableRow -->
|
||||||
|
</tbody>
|
||||||
|
</table>
|
48
templates/Newsletter_RecipientImportField.ss
Executable file
@ -0,0 +1,48 @@
|
|||||||
|
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<% base_tag %>
|
||||||
|
$MetaTags
|
||||||
|
<style type="text/css" media="screen">
|
||||||
|
@import "mot/css/css.css";
|
||||||
|
@import "mot/css/typography.css";
|
||||||
|
</style>
|
||||||
|
<style type="text/css" media="print">
|
||||||
|
@import "mot/css/print.css";
|
||||||
|
</style>
|
||||||
|
<style type="text/css">
|
||||||
|
body {
|
||||||
|
padding-top: 1em;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<script src="jsparty/prototype.js" type="text/javascript"></script>
|
||||||
|
<script src="jsparty/behaviour.js" type="text/javascript"></script>
|
||||||
|
<script src="cms/javascript/Newsletter_UploadForm.js" type="text/javascript"></script>
|
||||||
|
<% if ImportMessage %>
|
||||||
|
<script type="text/javascript">
|
||||||
|
top.statusMessage('Imported new members','good');
|
||||||
|
top.reloadRecipientsList();
|
||||||
|
</script>
|
||||||
|
<% end_if %>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<% if ImportMessage %>
|
||||||
|
<p>
|
||||||
|
$ImportMessage
|
||||||
|
<ul>
|
||||||
|
<li><label>New members imported:</label>$NewMembers</li>
|
||||||
|
<li><label>Members updated:</label>$ChangedMembers</li>
|
||||||
|
<li><label>Number of details changed:</label>$ChangedFields</li>
|
||||||
|
<li><label>Records skipped:</label>$SkippedRecords</li>
|
||||||
|
<li><label>Time taken: </label>$Time seconds</li>
|
||||||
|
</ul>
|
||||||
|
</p>
|
||||||
|
<% end_if %>
|
||||||
|
<% if ErrorMessage %>
|
||||||
|
<p class="message bad">
|
||||||
|
$ErrorMessage
|
||||||
|
</p>
|
||||||
|
<% end_if %>
|
||||||
|
$UploadForm
|
||||||
|
</body>
|
||||||
|
</html>
|
46
templates/Newsletter_RecipientImportField_Table.ss
Executable file
@ -0,0 +1,46 @@
|
|||||||
|
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<% base_tag %>
|
||||||
|
$MetaTags
|
||||||
|
<style type="text/css" media="screen">
|
||||||
|
@import "mot/css/css.css";
|
||||||
|
@import "mot/css/typography.css";
|
||||||
|
</style>
|
||||||
|
<style type="text/css" media="print">
|
||||||
|
@import "mot/css/print.css";
|
||||||
|
</style>
|
||||||
|
<script src="jsparty/prototype.js" type="text/javascript"></script>
|
||||||
|
<script src="jsparty/behaviour.js" type="text/javascript"></script>
|
||||||
|
<script src="cms/javascript/Newsletter_UploadForm.js" type="text/javascript"></script>
|
||||||
|
</head>
|
||||||
|
<body onload="">
|
||||||
|
<h1>Contents of $FileName</h1>
|
||||||
|
<form method="post" action="admin/newsletter/?executeForm=UploadForm" name="UploadForm">
|
||||||
|
<% control CustomSetFields %>
|
||||||
|
$FieldHolder
|
||||||
|
<% end_control %>
|
||||||
|
<input type="submit" name="action_confirm" value="Confirm" />
|
||||||
|
<input type="submit" name="action_cancel" value="Cancel" />
|
||||||
|
<input type="hidden" name="ID" value="$TypeID" />
|
||||||
|
<table summary="Recipients imported from $FileName">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<% control ColumnHeaders %>
|
||||||
|
<th>
|
||||||
|
$Field
|
||||||
|
</th>
|
||||||
|
<% end_control %>
|
||||||
|
</tr>
|
||||||
|
<% control Rows %>
|
||||||
|
<tr>
|
||||||
|
<% control Cells %>
|
||||||
|
<td>$Value</td>
|
||||||
|
<% end_control %>
|
||||||
|
</tr>
|
||||||
|
<% end_control %>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</form>
|
||||||
|
</body>
|
||||||
|
</html>
|
45
templates/PageCommentInterface.ss
Executable file
@ -0,0 +1,45 @@
|
|||||||
|
<div id="PageComments_holder" class="typography">
|
||||||
|
<h4>Post your comment</h4>
|
||||||
|
|
||||||
|
$PostCommentForm
|
||||||
|
|
||||||
|
<h4>Comments</h4>
|
||||||
|
|
||||||
|
<div id="CommentHolder">
|
||||||
|
<% if Comments %>
|
||||||
|
<ul id="PageComments">
|
||||||
|
<% control Comments %>
|
||||||
|
<li class="$EvenOdd<% if FirstLast %> $FirstLast <% end_if %> $SpamClass">
|
||||||
|
<% include PageCommentInterface_singlecomment %>
|
||||||
|
</li>
|
||||||
|
<% end_control %>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<% if Comments.MoreThanOnePage %>
|
||||||
|
<div id="PageCommentsPagination">
|
||||||
|
<% if Comments.PrevLink %>
|
||||||
|
<a href="$Comments.PrevLink">« previous</a>
|
||||||
|
<% end_if %>
|
||||||
|
|
||||||
|
<% if Comments.Pages %>
|
||||||
|
<% control Comments.Pages %>
|
||||||
|
<% if CurrentBool %>
|
||||||
|
<strong>$PageNum</strong>
|
||||||
|
<% else %>
|
||||||
|
<a href="$Link">$PageNum</a>
|
||||||
|
<% end_if %>
|
||||||
|
<% end_control %>
|
||||||
|
<% end_if %>
|
||||||
|
|
||||||
|
<% if Comments.NextLink %>
|
||||||
|
<a href="$Comments.NextLink">next »</a>
|
||||||
|
<% end_if %>
|
||||||
|
</div>
|
||||||
|
<% end_if %>
|
||||||
|
<% else %>
|
||||||
|
<p id="NoComments">No one has commented on this page yet.</p>
|
||||||
|
<% end_if %>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
16
templates/PageCommentInterface_singlecomment.ss
Executable file
@ -0,0 +1,16 @@
|
|||||||
|
<p class="comment">$Comment.XML</p>
|
||||||
|
<p class="info">
|
||||||
|
<span>Posted by $Name, $Created.Nice ($Created.Ago)</span>
|
||||||
|
<br />
|
||||||
|
<span>
|
||||||
|
<% if DeleteLink %>
|
||||||
|
<a href="$DeleteLink" class="deletelink">remove this comment</a>
|
||||||
|
<% end_if %>
|
||||||
|
<% if SpamLink %>
|
||||||
|
<a href="$SpamLink" class="spamlink">this comment is spam</a>
|
||||||
|
<% end_if %>
|
||||||
|
<% if HamLink %>
|
||||||
|
<a href="$HamLink" class="hamlink">this comment is not spam</a>
|
||||||
|
<% end_if %>
|
||||||
|
</span>
|
||||||
|
</p>
|
1
templates/ReceivedFormSubmission.ss
Executable file
@ -0,0 +1 @@
|
|||||||
|
$OnCompleteMessage
|
10
templates/TaskList.ss
Executable file
@ -0,0 +1,10 @@
|
|||||||
|
<p>$Message</p>
|
||||||
|
|
||||||
|
<ul id="TaskList">
|
||||||
|
<% control Tasks %>
|
||||||
|
<li class="$EvenOdd">
|
||||||
|
<a href="admin/show/$ID">$Title</a>
|
||||||
|
<div class="extra">by $RequestedBy.FirstName $RequestedBy.Surname.Initial, $Created.Ago</div>
|
||||||
|
</li>
|
||||||
|
<% end_control %>
|
||||||
|
</ul>
|
3
templates/ThumbnailStripField.ss
Executable file
@ -0,0 +1,3 @@
|
|||||||
|
<div id="$Name" class="thumbnailstrip parent=$ParentField updatemethod=$updateMethod">
|
||||||
|
<span style="float: left">(Choose a folder above)</span>
|
||||||
|
</div>
|
10
templates/WaitingOn.ss
Executable file
@ -0,0 +1,10 @@
|
|||||||
|
<p>$Message</p>
|
||||||
|
|
||||||
|
<ul id="TaskList">
|
||||||
|
<% control Tasks %>
|
||||||
|
<li class="$EvenOdd">
|
||||||
|
<a href="admin/show/$ID">$Title</a>
|
||||||
|
<div class="extra">assigned to $AssignedTo.FirstName $AssignedTo.Surname.Initial $LastEdited.Ago</div>
|
||||||
|
</li>
|
||||||
|
<% end_control %>
|
||||||
|
</ul>
|
20
templates/email/SubmittedFormEmail.ss
Executable file
@ -0,0 +1,20 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>$Subject</h1>
|
||||||
|
<p>
|
||||||
|
The following data was submitted to your website:
|
||||||
|
</p>
|
||||||
|
$Body
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<% control Fields %>
|
||||||
|
<tr>
|
||||||
|
<td><b>$Title</b></td>
|
||||||
|
<td>$Value</td>
|
||||||
|
</tr>
|
||||||
|
<% end_control %>
|
||||||
|
</table>
|
||||||
|
</body>
|
||||||
|
</html>
|
3
templates/email/ViewArchivedEmail.ss
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
You have asked to view the content of our site on $ArchiveDate.Date
|
||||||
|
|
||||||
|
You can access the archived site at this link: $ArchiveURL
|