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
|