Commit 148866ef authored by Simonas's avatar Simonas

previous code added

parent e3bd3220

Too many changes to show.

To preserve performance only 1000 of 1000+ files are displayed.

naujas puslapis
\ No newline at end of file
.DS_Store
.svn
node_modules
silverstripe-cache
nbproject
/assets/Uploads/*
/assets/news-articles/thumbnails/*
/assets/image-gallery/*
*.orig
!.gitignore
php_value memory_limit 512M
php_value max_execution_time 850
### SILVERSTRIPE START ###
<Files *.ss>
Order deny,allow
Deny from all
Allow from 127.0.0.1
</Files>
<Files web.config>
Order deny,allow
Deny from all
</Files>
ErrorDocument 404 /assets/error-404.html
ErrorDocument 500 /assets/error-500.html
<ifModule mod_headers.c>
# WEEK
<FilesMatch "\.(ico|gif|jpg|jpeg|png|flv|pdf)$">
Header set Cache-Control "max-age=604800"
</FilesMatch>
<FilesMatch "\.(js|css|swf)$">
Header set Cache-Control "max-age=604800"
</FilesMatch>
# 45 MIN
<FilesMatch "\.(html|htm|txt)$">
Header set Cache-Control "max-age=2700"
</FilesMatch>
</ifModule>
<IfModule mod_alias.c>
RedirectMatch 403 /silverstripe-cache(/|$)
</IfModule>
<ifModule mod_gzip.c>
mod_gzip_on Yes
mod_gzip_dechunk Yes
mod_gzip_item_include file .(html?|txt|css|js|php|pl)$
mod_gzip_item_include handler ^cgi-script$
mod_gzip_item_include mime ^text/.*
mod_gzip_item_include mime ^application/x-javascript.*
mod_gzip_item_exclude mime ^image/.*
mod_gzip_item_exclude rspheader ^Content-Encoding:.*gzip.*
</ifModule>
<IfModule mod_rewrite.c>
SetEnv HTTP_MOD_REWRITE On
RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_URI} ^(.*)$
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule .* sapphire/main.php?url=%1&%{QUERY_STRING} [L]
</IfModule>
#php_flag safe_mode O
#php_value upload_max_filesize 10M
#php_value post_max_size 10M
### SILVERSTRIPE END ###
#php_flag display_startup_errors on
#php_flag display_errors on
#php_flag html_errors on
# enable PHP error logging
php_flag log_errors on
php_value error_log /home/biuro/domains/biuro.lt/public_html/errors.log
# Data source for the "arb" svn merge tool (see http://github.com/sminnee/arborist).
# Keeps track of all opensource branches of submodules
# that could be merged back to the current submodule paths.
trunk:
.: /open/phpinstaller/trunk
sapphire: /open/modules/sapphire/trunk
cms: /open/modules/cms/trunk
jsparty: /open/modules/jsparty/trunk
\ No newline at end of file
#
# This makefile is a secondary way of installing SilverStripe.
# It is used for things like continuous integration
#
# Most users should simply visit the site root in your web browser.
#
test:
$(MAKE) QUERYSTRING="$(QUERYSTRING)" -C sapphire test
getallmodules:
if [ -d auth_openid ]; then svn update auth_openid; else svn co http://svn.silverstripe.com/open/modules/auth_openid/trunk auth_openid; fi
if [ -d blog ]; then svn update blog; else svn co http://svn.silverstripe.com/open/modules/blog/trunk blog; fi
if [ -d cmsworkflow ]; then svn update cmsworkflow; else svn co http://svn.silverstripe.com/open/modules/cmsworkflow/trunk cmsworkflow; fi
if [ -d forum ]; then svn update forum; else svn co http://svn.silverstripe.com/open/modules/forum/trunk forum; fi
if [ -d genericdataadmin ]; then svn update genericdataadmin; else svn co http://svn.silverstripe.com/open/modules/genericdataadmin/trunk genericdataadmin; fi
if [ -d multiform ]; then svn update multiform; else svn co http://svn.silverstripe.com/open/modules/multiform/trunk multiform; fi
if [ -d subsites ]; then svn update subsites; else svn co http://svn.silverstripe.com/open/modules/subsites/trunk subsites; fi
if [ -d userforms ]; then svn update userforms; else svn co http://svn.silverstripe.com/open/modules/userforms/trunk userforms; fi
\ No newline at end of file
This diff is collapsed.
<?php
// skirta pasitikrinti ar export.biuro.lt pasiekiamas iš www.biuro.lt webserverio.
print "!!!!!<pre>";
file_get_contents("http://export.biuro.lt/www.php");
var_dump($http_response_header);
if (!is_array($http_response_header)) print "http://export.biuro.lt nepasiekiamas";
<component lightWeight="true">
<attach event="onpropertychange" onevent="checkPropertyChange()" />
<attach event="ondetach" onevent="restore()" />
<script>
//<![CDATA[
var doc = element.document;
function init() {
updateBorderBoxWidth();
updateBorderBoxHeight();
}
function restore() {
element.runtimeStyle.width = "";
element.runtimeStyle.height = "";
}
/* border width getters */
function getBorderWidth(sSide) {
if (element.currentStyle["border" + sSide + "Style"] == "none")
return 0;
var n = parseInt(element.currentStyle["border" + sSide + "Width"]);
return n || 0;
}
function getBorderLeftWidth() { return getBorderWidth("Left"); }
function getBorderRightWidth() { return getBorderWidth("Right"); }
function getBorderTopWidth() { return getBorderWidth("Top"); }
function getBorderBottomWidth() { return getBorderWidth("Bottom"); }
/* end border width getters */
/* padding getters */
function getPadding(sSide) {
var n = parseInt(element.currentStyle["padding" + sSide]);
return n || 0;
}
function getPaddingLeft() { return getPadding("Left"); }
function getPaddingRight() { return getPadding("Right"); }
function getPaddingTop() { return getPadding("Top"); }
function getPaddingBottom() { return getPadding("Bottom"); }
/* end padding getters */
function getBoxSizing() {
var s = element.style;
var cs = element.currentStyle
if (typeof s.boxSizing != "undefined" && s.boxSizing != "")
return s.boxSizing;
if (typeof s["box-sizing"] != "undefined" && s["box-sizing"] != "")
return s["box-sizing"];
if (typeof cs.boxSizing != "undefined" && cs.boxSizing != "")
return cs.boxSizing;
if (typeof cs["box-sizing"] != "undefined" && cs["box-sizing"] != "")
return cs["box-sizing"];
return getDocumentBoxSizing();
}
function getDocumentBoxSizing() {
if (doc.compatMode == null || doc.compatMode == "BackCompat")
return "border-box";
return "content-box"
}
/* width and height setters */
function setBorderBoxWidth(n) {
element.runtimeStyle.width = Math.max(0, n - getBorderLeftWidth() -
getPaddingLeft() - getPaddingRight() - getBorderRightWidth()) + "px";
}
function setBorderBoxHeight(n) {
element.runtimeStyle.height = Math.max(0, n - getBorderTopWidth() -
getPaddingTop() - getPaddingBottom() - getBorderBottomWidth()) + "px";
}
function setContentBoxWidth(n) {
element.runtimeStyle.width = Math.max(0, n + getBorderLeftWidth() +
getPaddingLeft() + getPaddingRight() + getBorderRightWidth()) + "px";
}
function setContentBoxHeight(n) {
element.runtimeStyle.height = Math.max(0, n + getBorderTopWidth() +
getPaddingTop() + getPaddingBottom() + getBorderBottomWidth()) + "px";
}
/* end width and height setters */
function updateBorderBoxWidth() {
element.runtimeStyle.width = "";
if (getDocumentBoxSizing() == getBoxSizing())
return;
var csw = element.currentStyle.width;
if (csw != "auto" && csw.indexOf("px") != -1) {
if (getBoxSizing() == "border-box")
setBorderBoxWidth(parseInt(csw));
else
setContentBoxWidth(parseInt(csw));
}
}
function updateBorderBoxHeight() {
element.runtimeStyle.height = "";
if (getDocumentBoxSizing() == getBoxSizing())
return;
var csh = element.currentStyle.height;
if (csh != "auto" && csh.indexOf("px") != -1) {
if (getBoxSizing() == "border-box")
setBorderBoxHeight(parseInt(csh));
else
setContentBoxHeight(parseInt(csh));
}
}
function checkPropertyChange() {
var pn = event.propertyName;
var undef;
if (pn == "style.boxSizing" && element.style.boxSizing == "") {
element.style.removeAttribute("boxSizing");
element.runtimeStyle.boxSizing = undef;
}
switch (pn) {
case "style.width":
case "style.borderLeftWidth":
case "style.borderLeftStyle":
case "style.borderRightWidth":
case "style.borderRightStyle":
case "style.paddingLeft":
case "style.paddingRight":
updateBorderBoxWidth();
break;
case "style.height":
case "style.borderTopWidth":
case "style.borderTopStyle":
case "style.borderBottomWidth":
case "style.borderBottomStyle":
case "style.paddingTop":
case "style.paddingBottom":
updateBorderBoxHeight();
break;
case "className":
case "style.boxSizing":
updateBorderBoxWidth();
updateBorderBoxHeight();
break;
}
}
init();
//]]>
</script>
</component>
\ No newline at end of file
<?php
/**
* Simple captcha
* Author: Donatas Navidonskis
*
*/
session_start();
// generate 6 digits number
$numbers = rand(10000, 99999);
// set number to session
$_SESSION["verifycode"] = $numbers;
// set height of image
$Height = 25;
// set width of image
$Width = 65;
// Creating Background
$Background = imagecreate($Width, $Height);
$Foreground = imagecolorallocate($Background, 255, 255, 255); // white foreground
$TextColor = imagecolorallocate($Background, 0, 0, 0); // black text color
$FontSize = 14;
// set header to show image
header("Cache-Control: no-cache, must-revalidate");
header("Content-type: image/jpeg");
// Creating image
imagestring($Background, $FontSize, 5, 5, $numbers, $TextColor);
imagejpeg($Background);
?>
\ No newline at end of file
<FilesMatch "\.(php|php3|php4|php5|phtml|inc)$">
Deny from all
</FilesMatch>
<FilesMatch "silverstripe_version$">
Deny from all
</FilesMatch>
\ No newline at end of file
<?php
/**
* Extended URL rules for the CMS module
*
* @package cms
*/
Director::addRules(50, array(
'processes//$Action/$ID/$Batch' => 'BatchProcess_Controller',
'admin/help//$Action/$ID' => 'CMSHelp',
'admin/bulkload//$Action/$ID/$OtherID' => 'BulkLoaderAdmin',
'admin/cms//$Action/$ID/$OtherID' => 'CMSMain',
'PageComment//$Action/$ID' => 'PageComment_Controller',
'dev/buildcache/$Action' => 'RebuildStaticCacheTask',
));
CMSMenu::add_director_rules();
// Default CMS HTMLEditorConfig
HtmlEditorConfig::get('cms')->setOptions(array(
'friendly_name' => 'Default CMS',
'priority' => '50',
'mode' => 'none',
'body_class' => 'typography',
'document_base_url' => Director::absoluteBaseURL(),
'urlconverter_callback' => "nullConverter",
'setupcontent_callback' => "sapphiremce_setupcontent",
'cleanup_callback' => "sapphiremce_cleanup",
'use_native_selects' => true, // fancy selects are bug as of SS 2.3.0
'valid_elements' => "@[id|class|style|title],#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|style],-ol[class],-ul[class],-li[class],br,img[id|dir|longdesc|usemap|class|src|border|alt=|title|width|height|align],-sub[class],-sup[class],-blockquote[dir|class],-table[border=0|cellspacing|cellpadding|width|height|class|align|summary|dir|id|style],-tr[id|dir|class|rowspan|width|height|align|valign|bgcolor|background|bordercolor|style],tbody[id|class|style],thead[id|class|style],tfoot[id|class|style],#td[id|dir|class|colspan|rowspan|width|height|align|valign|scope|style],-th[id|dir|class|colspan|rowspan|width|height|align|valign|scope|style],caption[id|dir|class],-div[id|dir|class|align|style],-span[class|align|style],-pre[class|align],address[class|align],-h1[id|dir|class|align|style],-h2[id|dir|class|align|style],-h3[id|dir|class|align|style],-h4[id|dir|class|align|style],-h5[id|dir|class|align|style],-h6[id|dir|class|align|style],hr[class],dd[id|class|title|dir],dl[id|class|title|dir],dt[id|class|title|dir],@[id,style,class],picture[class],source[media|type|srcset]",
'extended_valid_elements' => "img[class|src|srcset|alt|title|hspace|vspace|width|height|align|onmouseover|onmouseout|name|usemap],iframe[src|name|width|height|align|frameborder|marginwidth|marginheight|scrolling],object[width|height|data|type],param[name|value],map[class|name|id],area[shape|coords|href|target|alt]"
));
HtmlEditorConfig::get('cms')->enablePlugins('media', 'fullscreen');
HtmlEditorConfig::get('cms')->enablePlugins(array('ssbuttons' => '../../../cms/javascript/tinymce_ssbuttons/editor_plugin_src.js'));
HtmlEditorConfig::get('cms')->insertButtonsBefore('formatselect', 'styleselect');
HtmlEditorConfig::get('cms')->insertButtonsBefore('advcode', 'ssimage', 'ssflash', 'sslink', 'unlink', 'anchor', 'separator' );
HtmlEditorConfig::get('cms')->insertButtonsAfter ('advcode', 'fullscreen', 'separator');
HtmlEditorConfig::get('cms')->removeButtons('tablecontrols');
HtmlEditorConfig::get('cms')->addButtonsToLine(3, 'tablecontrols');
// Register default side reports
SS_Report::register("SideReport", "SideReport_EmptyPages");
SS_Report::register("SideReport", "SideReport_RecentlyEdited");
SS_Report::register("SideReport", "SideReport_ToDo");
if (class_exists('SubsiteReportWrapper')) SS_Report::register('ReportAdmin', 'SubsiteReportWrapper("BrokenLinksReport")',-20);
else SS_Report::register('ReportAdmin', 'BrokenLinksReport',-20);
This diff is collapsed.
<?php
/**
* A special kind of complex table field for manipulating assets.
*
* @package cms
* @subpackage assets
*/
class AssetTableField extends ComplexTableField {
protected $folder;
protected $template = "AssetTableField";
protected $permissions = array(
"edit",
"delete",
//"export",
);
/**
* Indicates whether a search is being executed on this object
*/
protected $searchingFor = null;
function __construct($controller, $name, $sourceClass, $fieldList, $detailFormFields, $sourceFilter = "", $sourceSort = "", $sourceJoin = "") {
parent::__construct($controller, $name, $sourceClass, $fieldList, $detailFormFields, $sourceFilter, $sourceSort, $sourceJoin);
$SNG_file = singleton('File');
// If search was request, filter the results here
$SQL_search = (!empty($_REQUEST['FileSearch'])) ? Convert::raw2sql($_REQUEST['FileSearch']) : null;
if($SQL_search) {
$searchFilters = array();
foreach($SNG_file->searchableFields() as $fieldName => $fieldSpec) {
if(strpos($fieldName, '.') === false) $searchFilters[] = "\"$fieldName\" LIKE '%{$SQL_search}%'";
}
$this->sourceFilter = '(' . implode(' OR ', $searchFilters) . ')';
$this->searchingFor = $_REQUEST['FileSearch'];
// @todo Integrate search form more closely and don't rely on deprecated
// $extraLinkParams.
$this->extraLinkParams = array(
'FileSearch' => $SQL_search
);
}
$this->sourceSort = 'Title';
$this->Markable = true;
}
/**
* Creates the link to this form, including the search pattern
*
* @return string
*/
function CurrentLink() {
$link = parent::CurrentLink();
if(isset($_REQUEST['FileSearch']) ) {
if ( strpos($link, '?')!==false )
$link .= "&";
else
$link .= "/?";
$link .= "FileSearch=".urlencode($_REQUEST['FileSearch']);
}
return $link;
}
function FieldHolder() {
$ret = parent::FieldHolder();
Requirements::javascript(CMS_DIR . '/javascript/AssetTableField.js');
return $ret;
}
function FirstLink() {
$link = parent::FirstLink();
if($link && isset($_REQUEST['FileSearch'])) {
return $link . '&FileSearch=' . $_REQUEST['FileSearch'];
}
return $link;
}
function PrevLink() {
$link = parent::PrevLink();
if($link && isset($_REQUEST['FileSearch'])) {
return $link . '&FileSearch=' . $_REQUEST['FileSearch'];
}
return $link;
}
function NextLink() {
$link = parent::NextLink();
if($link && isset($_REQUEST['FileSearch'])) {
return $link . '&FileSearch=' . $_REQUEST['FileSearch'];
}
return $link;
}
function LastLink() {
$link = parent::LastLink();
if($link && isset($_REQUEST['FileSearch'])) {
return $link . '&FileSearch=' . $_REQUEST['FileSearch'];
}
return $link;
}
function setFolder($folder) {
$this->folder = $folder;
$this->sourceFilter .= ($this->sourceFilter) ? " AND " : "";
// If you are searching for files then show all those from subfolders
if($this->searchingFor) {
$folderIDs = $nextIDSet = array($folder->ID);
$folderClasses = "'" . implode("','", ClassInfo::subclassesFor("Folder")) . "'";
while($nextIDSet) {
// TO DO: In 2.4 this should be refactored to use the new data mapper.
$nextIDSet = DB::query("SELECT \"ID\" FROM \"File\" WHERE \"ParentID\" IN ("
. implode(", " , $nextIDSet) . ") AND \"ClassName\" IN ($folderClasses)")->column();
if($nextIDSet) $folderIDs = array_merge($folderIDs, $nextIDSet);
}
$this->sourceFilter .= " \"ParentID\" IN (" . implode(", ", $folderIDs) . ") AND \"ClassName\" <> 'Folder'";
// Otherwise just show the direct contents
} else {
$this->sourceFilter .= " \"ParentID\" = '" . $folder->ID . "' AND \"ClassName\" <> 'Folder'";
}
}
function Folder() {
return $this->folder;
}
function sourceID() {
if($this->folder) return $this->folder->ID;
}
/**
* Get the pop-up fields for the given record.
*/
function getCustomFieldsFor($childData) {
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'>"._t('AssetTableField.URL','URL')."</label>";
$urlLink .= "<span class='readonly'><a href='{$childData->Link()}'>{$childData->RelativeLink()}</a></span>";
$urlLink .= "</div>";
$detailFormFields = new FieldSet(
new TabSet("BottomRoot",
$mainTab = new Tab('Main',
new TextField("Title", _t('AssetTableField.TITLE','Title')),
new TextField("Name", _t('AssetTableField.FILENAME','Filename')),
new LiteralField("AbsoluteURL", $urlLink),
new ReadonlyField("FileType", _t('AssetTableField.TYPE','Type')),
new ReadonlyField("Size", _t('AssetTableField.SIZE','Size'), $childData->getSize()),
new DropdownField("OwnerID", _t('AssetTableField.OWNER','Owner'), Member::mapInCMSGroups()),
new DateField_Disabled("Created", _t('AssetTableField.CREATED','First uploaded')),
new DateField_Disabled("LastEdited", _t('AssetTableField.LASTEDIT','Last changed'))
)
)
);
$mainTab->setTitle(_t('AssetTableField.MAIN', 'Main'));
if(is_a($childData, 'Image')) {
$tab = $detailFormFields->findOrMakeTab("BottomRoot.Image", _t('AssetTableField.IMAGE', 'Image'));
$big = $childData->URL;
$formattedImage = $childData->getFormattedImage('AssetLibraryPreview');
$thumbnail = $formattedImage ? $formattedImage->URL : '';
// Hmm this required the translated string to be appended to BottomRoot to add this to the Main tab
$detailFormFields->addFieldToTab('BottomRoot.Main',
new ReadonlyField("Dimensions", _t('AssetTableField.DIM','Dimensions'))
);
$tab->push(
new LiteralField("ImageFull",
"<img id='thumbnailImage' src='{$thumbnail}?r=" . rand(1,100000) . "' alt='{$childData->Name}' />"
)
);
}
if(!($childData instanceof Folder)) {
$mainTab->push(
new CheckboxField("ShowInSearch", $childData->fieldLabel('ShowInSearch'))
);
}
if($childData && $childData->hasMethod('BackLinkTracking')) {
if(class_exists('Subsite')) Subsite::disable_subsite_filter(true);
$links = $childData->BackLinkTracking();
if(class_exists('Subsite')) Subsite::disable_subsite_filter(false);
if($links && $links->exists()) {
$backlinks = array();
foreach($links as $link) {
$backlinks[] = "<li><a href=\"admin/show/$link->ID\">" . $link->Breadcrumbs(null,true). "</a></li>";
}
$backlinks = "<div style=\"clear:left\">". _t('AssetTableField.PAGESLINKING','The following pages link to this file:') ."<ul>" . implode("",$backlinks) . "</ul></div>";
}
if(!isset($backlinks)) $backlinks = "<p>". _t('AssetTableField.NOLINKS',"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');
if($childData) $childData->extend('updateCMSFields', $detailFormFields);
return $detailFormFields;
}
/**
* Provide some HTML for a search form, to be
* added above the AssetTable field, allowing
* a user to filter the current table's files
* by their filename.
*
* @return string HTML for search form
*/
function SearchForm() {
$searchFields = new FieldGroup(
new TextField('FileSearch', _t('MemberTableField.SEARCH', 'Search'), $this->searchingFor),
new HiddenField("ctf[ID]", '', $this->ID),
new HiddenField('FileFieldName', '', $this->name)
);
$actionFields = new LiteralField(
'FileFilterButton',
'<input type="submit" class="action" name="FileFilterButton" value="' . _t('MemberTableField.FILTER', 'Filter') . '" id="FileFilterButton"/>'
);
$fieldContainer = new FieldGroup(
$searchFields,
$actionFields
);
return $fieldContainer->FieldHolder();
}
}
?>
<?php
/**
* A special kind of form used to make the action dialogs that appear just underneath the top-right
* buttons in the CMS
*
* @package cms
* @subpackage core
*/
class CMSActionOptionsForm extends Form {
function FormAttributes() {
return "class=\"actionparams\" style=\"display:none\" " . parent::FormAttributes();
}
function FormName() {
$action = $this->actions->First()->Name();
return "{$action}_options";
}
}
?>
\ No newline at end of file
<?php
/**
* A class representing back actions
*
* <code>
* CMSMain::register_batch_action('publishitems', new CMSBatchAction('doPublish',
* _t('CMSBatchActions.PUBLISHED_PAGES', 'published %d pages')));
* </code>
*
* @package cms
* @subpackage batchaction
*/
abstract class CMSBatchAction extends Object {
/**
* The the text to show in the dropdown for this action
*/
abstract function getActionTitle();
/**
* Get text to be shown while the action is being processed, of the form
* "publishing pages".
*/
abstract function getDoingText();
/**
* Run this action for the given set of pages.
* Return a set of status-updated JavaScript to return to the CMS.
*/
abstract function run(DataObjectSet $pages);
/**
* Helper method for processing batch actions.
* Returns a set of status-updating JavaScript to return to the CMS.
*
* @param $pages The DataObjectSet of SiteTree objects to perform this batch action
* on.
* @param $helperMethod The method to call on each of those objects.
*/
public function batchaction(DataObjectSet $pages, $helperMethod, $successMessage, $arguments = array()) {
$failures = 0;
foreach($pages as $page) {
// Perform the action
if (call_user_func_array(array($page, $helperMethod), $arguments) === false) {
$failures++;
FormResponse::add("\$('sitetree').addNodeClassByIdx('$page->ID', 'failed');");
}
// Now make sure the tree title is appropriately updated
$publishedRecord = DataObject::get_by_id('SiteTree', $page->ID);
if ($publishedRecord) {
$JS_title = Convert::raw2js($publishedRecord->TreeTitle());
FormResponse::add("\$('sitetree').setNodeTitle($page->ID, '$JS_title');");
}
$page->destroy();
unset($page);
}
$message = sprintf($successMessage, $pages->Count()-$failures, $failures);
FormResponse::add('statusMessage("'.$message.'","good");');
return FormResponse::respond();
}
/**
* Helper method for applicablePages() methods. Acts as a skeleton implementation.
*
* @param $ids The IDs passed to applicablePages
* @param $methodName The canXXX() method to call on each page to check if the action is applicable
* @param $checkStagePages Set to true if you want to check stage pages
* @param $checkLivePages Set to true if you want to check live pages (e.g, for deleted-from-draft)
*/
function applicablePagesHelper($ids, $methodName, $checkStagePages = true, $checkLivePages = true) {
if(!is_array($ids)) user_error("Bad \$ids passed to applicablePagesHelper()", E_USER_WARNING);
if(!is_string($methodName)) user_error("Bad \$methodName passed to applicablePagesHelper()", E_USER_WARNING);
$applicableIDs = array();
$SQL_ids = implode(', ', array_filter($ids, 'is_numeric'));
$draftPages = DataObject::get("SiteTree", "\"SiteTree\".\"ID\" IN ($SQL_ids)");
$onlyOnLive = array_fill_keys($ids, true);
if($checkStagePages) {
foreach($draftPages as $page) {
unset($onlyOnLive[$page->ID]);
if($page->$methodName()) $applicableIDs[] = $page->ID;
}
}
// Get the pages that only exist on live (deleted from stage)
if($checkLivePages && $onlyOnLive) {
$SQL_ids = implode(', ', array_keys($onlyOnLive));
$livePages = Versioned::get_by_stage("SiteTree", "Live", "\"SiteTree\".\"ID\" IN ($SQL_ids)");
if($livePages) foreach($livePages as $page) {
if($page->$methodName()) $applicableIDs[] = $page->ID;
}
}
return $applicableIDs;
}
// if your batchaction has parameters, return a fieldset here
function getParameterFields() {
return false;
}
/**
* If you wish to restrict the batch action to some users, overload this function.
*/
function canView() {
return true;
}
}
/**
* Publish items batch action.
*
* @package cms
* @subpackage batchaction
*/
class CMSBatchAction_Publish extends CMSBatchAction {
function getActionTitle() {
return _t('CMSBatchActions.PUBLISH_PAGES', 'Publish');
}
function getDoingText() {
return _t('CMSBatchActions.PUBLISHING_PAGES', 'Publishing pages');
}
function run(DataObjectSet $pages) {
return $this->batchaction($pages, 'doPublish',
_t('CMSBatchActions.PUBLISHED_PAGES', 'Published %d pages, %d failures')
);
}
function applicablePages($ids) {
return $this->applicablePagesHelper($ids, 'canPublish', true, false);
}
}
/**
* Delete items batch action.
*
* @package cms
* @subpackage batchaction
*/
class CMSBatchAction_Delete extends CMSBatchAction {
function getActionTitle() {
return _t('CMSBatchActions.DELETE_DRAFT_PAGES', 'Delete from draft site');
}
function getDoingText() {
return _t('CMSBatchActions.DELETING_DRAFT_PAGES', 'Deleting selected pages from the draft site');
}
function run(DataObjectSet $pages) {
$failures = 0;
foreach($pages as $page) {
$id = $page->ID;
// Perform the action
if($page->canDelete()) $page->delete();
else $failures++;
// check to see if the record exists on the live site, if it doesn't remove the tree node
$liveRecord = Versioned::get_one_by_stage( 'SiteTree', 'Live', "\"SiteTree\".\"ID\"=$id");
if($liveRecord) {
$liveRecord->IsDeletedFromStage = true;
$title = Convert::raw2js($liveRecord->TreeTitle());
FormResponse::add("$('sitetree').setNodeTitle($id, '$title');");
FormResponse::add("$('Form_EditForm').reloadIfSetTo($id);");
} else {
FormResponse::add("var node = $('sitetree').getTreeNodeByIdx('$id');");
FormResponse::add("if(node && node.parentTreeNode) node.parentTreeNode.removeTreeNode(node);");
FormResponse::add("$('Form_EditForm').reloadIfSetTo($id);");
}
$page->destroy();
unset($page);
}
$message = sprintf(_t('CMSBatchActions.DELETED_DRAFT_PAGES', 'Deleted %d pages from the draft site, %d failures'), $pages->Count()-$failures, $failures);
FormResponse::add('statusMessage("'.$message.'","good");');
return FormResponse::respond();
}
function applicablePages($ids) {
return $this->applicablePagesHelper($ids, 'canDelete', true, false);
}
}
/**
* Unpublish (delete from live site) items batch action.
*
* @package cms
* @subpackage batchaction
*/
class CMSBatchAction_DeleteFromLive extends CMSBatchAction {
function getActionTitle() {
return _t('CMSBatchActions.DELETE_PAGES', 'Delete from published site');
}
function getDoingText() {
return _t('CMSBatchActions.DELETING_PAGES', 'Deleting selected pages from the published site');
}
function run(DataObjectSet $pages) {
$ids = $pages->column('ID');
$this->batchaction($pages, 'doUnpublish',
_t('CMSBatchActions.DELETED_PAGES', 'Deleted %d pages from the published site, %d failures')
);
foreach($ids as $pageID) {
$id = $pageID;
// check to see if the record exists on the stage site, if it doesn't remove the tree node
$stageRecord = Versioned::get_one_by_stage( 'SiteTree', 'Stage', "\"SiteTree\".\"ID\"=$id");
if($stageRecord) {
$stageRecord->IsAddedToStage = true;
$title = Convert::raw2js($stageRecord->TreeTitle());
FormResponse::add("$('sitetree').setNodeTitle($id, '$title');");
FormResponse::add("$('Form_EditForm').reloadIfSetTo($id);");
} else {
FormResponse::add("var node = $('sitetree').getTreeNodeByIdx('$id');");
FormResponse::add("if(node && node.parentTreeNode) node.parentTreeNode.removeTreeNode(node);");
FormResponse::add("$('Form_EditForm').reloadIfSetTo($id);");
}
}
return FormResponse::respond();
}
function applicablePages($ids) {
return $this->applicablePagesHelper($ids, 'canDelete', false, true);
}
}
<?php
/**
* Special request handler for admin/batchaction
*
* @package cms
* @subpackage batchaction
*/
class CMSBatchActionHandler extends RequestHandler {
static $batch_actions = array(
'publish' => 'CMSBatchAction_Publish',
'delete' => 'CMSBatchAction_Delete',
'deletefromlive' => 'CMSBatchAction_DeleteFromLive',
);
static $url_handlers = array(
'$BatchAction/applicablepages' => 'handleApplicablePages',
'$BatchAction/confirmation' => 'handleConfirmation',
'$BatchAction' => 'handleAction',
);
protected $parentController;
protected $urlSegment;
/**
* Register a new batch action. Each batch action needs to be represented by a subclass
* of
*
* @param $urlSegment The URL Segment of the batch action - the URL used to process this
* action will be admin/batchactions/(urlSegment)
* @param $batchActionClass The name of the CMSBatchAction subclass to register
*/
static function register($urlSegment, $batchActionClass) {
if(is_subclass_of($batchActionClass, 'CMSBatchAction')) {
self::$batch_actions[$urlSegment] = $batchActionClass;
} else {
user_error("CMSBatchActionHandler::register() - Bad class '$batchActionClass'", E_USER_ERROR);
}
}
function __construct($parentController, $urlSegment) {
$this->parentController = $parentController;
$this->urlSegment = $urlSegment;
parent::__construct();
}
function Link() {
return Controller::join_links($this->parentController->Link(), $this->urlSegment);
}
function handleAction($request) {
// This method can't be called without ajax.
if(!Director::is_ajax()) {
Director::redirectBack();
return;
}
// Protect against CSRF on destructive action
if(!SecurityToken::inst()->checkRequest($request)) return $this->httpError(400);
$actions = Object::get_static($this->class, 'batch_actions');
$actionClass = $actions[$request->param('BatchAction')];
$actionHandler = new $actionClass();
// Sanitise ID list and query the database for apges
$ids = split(' *, *', trim($request->requestVar('csvIDs')));
foreach($ids as $k => $v) if(!is_numeric($v)) unset($ids[$k]);
if($ids) {
$pages = DataObject::get('SiteTree', "\"SiteTree\".\"ID\" IN (" . implode(", ", $ids) . ")");
// If we didn't query all the pages, then find the rest on the live site
if(!$pages || $pages->Count() < sizeof($ids)) {
foreach($ids as $id) $idsFromLive[$id] = true;
if($pages) foreach($pages as $page) unset($idsFromLive[$page->ID]);
$idsFromLive = array_keys($idsFromLive);
// Debug::message("\"SiteTree\".\"ID\" IN (" . implode(", ", $idsFromLive) . ")");
$livePages = Versioned::get_by_stage('SiteTree', 'Live', "\"SiteTree\".\"ID\" IN (" . implode(", ", $idsFromLive) . ")");
if($pages) $pages->merge($livePages);
else $pages = $livePages;
}
} else {
$pages = new DataObjectSet();
}
return $actionHandler->run($pages);
}
function handleApplicablePages($request) {
// Find the action handler
$actions = Object::get_static($this->class, 'batch_actions');
$actionClass = $actions[$request->param('BatchAction')];
$actionHandler = new $actionClass();
// Sanitise ID list and query the database for apges
$ids = split(' *, *', trim($request->requestVar('csvIDs')));
foreach($ids as $k => $id) $ids[$k] = (int)$id;
$ids = array_filter($ids);
if($actionHandler->hasMethod('applicablePages')) {
$applicableIDs = $actionHandler->applicablePages($ids);
} else {
$applicableIDs = $ids;
}
$response = new SS_HTTPResponse(json_encode($applicableIDs));
$response->addHeader("Content-type", "application/json");
return $response;
}
function handleConfirmation($request) {
// Find the action handler
$actions = Object::get_static($this->class, 'batch_actions');
$actionClass = $actions[$request->param('BatchAction')];
$actionHandler = new $actionClass();
// Sanitise ID list and query the database for apges
$ids = split(' *, *', trim($request->requestVar('csvIDs')));
foreach($ids as $k => $id) $ids[$k] = (int)$id;
$ids = array_filter($ids);
if($actionHandler->hasMethod('confirmationDialog')) {
$response = new SS_HTTPResponse(json_encode($actionHandler->confirmationDialog($ids)));
} else {
$response = new SS_HTTPResponse(json_encode(array('alert' => false)));
}
$response->addHeader("Content-type", "application/json");
return $response;
}
/**
* Return a DataObjectSet of ArrayData objects containing the following pieces of info
* about each batch action:
* - Link
* - Title
* - DoingText
*/
function batchActionList() {
$actions = Object::get_static($this->class, 'batch_actions');
$actionList = new DataObjectSet();
foreach($actions as $urlSegment => $actionClass) {
$actionObj = new $actionClass();
if($actionObj->canView()) {
$actionDef = new ArrayData(array(
"Link" => Controller::join_links($this->Link(), $urlSegment),
"Title" => $actionObj->getActionTitle(),
"DoingText" => $actionObj->getDoingText(),
));
$actionList->push($actionDef);
}
}
return $actionList;
}
}
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
<?php
/**
* A simple CMS menu item
*
* @package cms
* @subpackage content
*/
class CMSMenuItem extends Object
{
/**
* The (translated) menu title
* @var string $title
*/
public $title;
/**
* Relative URL
* @var string $url
*/
public $url;
/**
* Parent controller class name
* @var string $controller
*/
public $controller;
/**
* Menu priority (sort order)
* @var integer $priority
*/
public $priority;
/**
* Create a new CMS Menu Item
* @param string $title
* @param string $url
* @param string $controller Controller class name
* @param integer $priority The sort priority of the item
*/
public function __construct($title, $url, $controller = null, $priority = -1) {
$this->title = $title;
$this->url = $url;
$this->controller = $controller;
$this->priority = $priority;
parent::__construct();
}
}
?>
\ No newline at end of file
<?php
/**
* Base class for filtering the subtree for certain node statuses.
*
* The simplest way of building a CMSSiteTreeFilter is to create a pagesToBeShown() method that
* returns an Iterator of maps, each entry containing the 'ID' and 'ParentID' of the pages to be
* included in the tree. The reuslt of a DB::query() can be returned directly.
*
* If you wish to make a more complex tree, you can overload includeInTree($page) to return true/
* false depending on whether the given page should be included. Note that you will need to include
* parent helper pages yourself.
*
* @package cms
* @subpackage content
*/
abstract class CMSSiteTreeFilter extends Object {
protected $ids = null;
protected $expanded = array();
static function showInList() {
return true;
}
function getTree() {
if(method_exists($this, 'pagesIncluded')) {
$this->populateIDs();
}
$leftAndMain = new CMSMain();
$tree = $leftAndMain->getSiteTreeFor('SiteTree', isset($_REQUEST['ID']) ? $_REQUEST['ID'] : 0, null, null, array($this, 'includeInTree'), count($this->ids));
// Trim off the outer tag
$tree = ereg_replace('^[ \t\r\n]*<ul[^>]*>','', $tree);
$tree = ereg_replace('</ul[^>]*>[ \t\r\n]*$','', $tree);
return $tree;
}
/**
* Populate $this->ids with the IDs of the pages returned by pagesIncluded(), also including
* the necessary parent helper pages.
*/
protected function populateIDs() {
if($res = $this->pagesIncluded()) {
/* And keep a record of parents we don't need to get parents of themselves, as well as IDs to mark */
foreach($res as $row) {
if ($row['ParentID']) $parents[$row['ParentID']] = true;
$this->ids[$row['ID']] = true;
}
while (!empty($parents)) {
$res = DB::query('SELECT "ParentID", "ID" FROM "SiteTree" WHERE "ID" in ('.implode(',',array_keys($parents)).')');
$parents = array();
foreach($res as $row) {
if ($row['ParentID']) $parents[$row['ParentID']] = true;
$this->ids[$row['ID']] = true;
$this->expanded[$row['ID']] = true;
}
}
}
}
/**
* Returns true if the given page should be included in the tree.
*/
public function includeInTree($page) {
return isset($this->ids[$page->ID]) && $this->ids[$page->ID] ? true : false;
}
}
/**
* @package cms
* @subpackage content
*/
class CMSSiteTreeFilter_DeletedPages extends CMSSiteTreeFilter {
static function title() {
return _t('CMSSiteTreeFilter.DELETEDPAGES', "All pages, including deleted");
}
function getTree() {
$leftAndMain = new CMSMain();
$tree = $leftAndMain->getSiteTreeFor('SiteTree', isset($_REQUEST['ID']) ? $_REQUEST['ID'] : 0, "AllHistoricalChildren", "numHistoricalChildren");
// Trim off the outer tag
$tree = ereg_replace('^[ \t\r\n]*<ul[^>]*>','', $tree);
$tree = ereg_replace('</ul[^>]*>[ \t\r\n]*$','', $tree);
return $tree;
}
}
/**
* @package cms
* @subpackage content
*/
class CMSSiteTreeFilter_ChangedPages extends CMSSiteTreeFilter {
static function title() {
return _t('CMSSiteTreeFilter.CHANGEDPAGES', "Changed pages");
}
function pagesIncluded() {
return DB::query('SELECT "ParentID", "ID" FROM "SiteTree" WHERE "Status" LIKE \'Saved%\'');
}
}
/**
* @package cms
* @subpackage content
*/
class CMSSiteTreeFilter_Search extends CMSSiteTreeFilter {
public $data;
function __construct() {
$this->data = $_REQUEST;
}
static function showInList() { return false; }
static function title() {
return _t('CMSSiteTreeFilter.SEARCH', 'Search');
}
/**
* Retun an array of maps containing the keys, 'ID' and 'ParentID' for each page to be displayed
* in the search.
*/
function pagesIncluded() {
$data = $this->data;
$this->ids = array();
$this->expanded = array();
$where = array();
// Match against URLSegment, Title, MenuTitle & Content
if (isset($data['SiteTreeSearchTerm'])) {
$term = Convert::raw2sql($data['SiteTreeSearchTerm']);
$where[] = "\"URLSegment\" LIKE '%$term%' OR \"Title\" LIKE '%$term%' OR \"MenuTitle\" LIKE '%$term%' OR \"Content\" LIKE '%$term%'";
}
// Match against date
if (isset($data['SiteTreeFilterDate'])) {
$date = $data['SiteTreeFilterDate'];
$date = ((int)substr($date,6,4)) . '-' . ((int)substr($date,3,2)) . '-' . ((int)substr($date,0,2));
$where[] = "\"LastEdited\" > '$date'";
}
// Match against exact ClassName
if (isset($data['ClassName']) && $data['ClassName'] != 'All') {
$klass = Convert::raw2sql($data['ClassName']);
$where[] = "\"ClassName\" = '$klass'";
}
// Partial string match against a variety of fields
foreach (CMSMain::T_SiteTreeFilterOptions() as $key => $value) {
if (!empty($data[$key])) {
$match = Convert::raw2sql($data[$key]);
$where[] = "\"$key\" LIKE '%$match%'";
}
}
$where = empty($where) ? '' : 'WHERE (' . implode(') AND (',$where) . ')';
$parents = array();
/* Do the actual search */
$res = DB::query('SELECT "ParentID", "ID" FROM "SiteTree" '.$where);
return $res;
}
}
<?php
/**
* Comment administration system within the CMS
* @package cms
* @subpackage comments
*/
class CommentAdmin extends LeftAndMain {
static $url_segment = 'comments';
static $url_rule = '/$Action';
static $menu_title = 'Comments';
static $allowed_actions = array(
'approvedmarked',
'deleteall',
'deletemarked',
'hammarked',
'showtable',
'spammarked',
'EditForm',
'unmoderated'
);
/**
* @var int The number of comments per page for the {@link CommentTable} in this admin.
*/
static $comments_per_page = '20';
public function init() {
parent::init();
Requirements::javascript(CMS_DIR . '/javascript/CommentAdmin_right.js');
Requirements::css(CMS_DIR . '/css/CommentAdmin.css');
}
public function showtable($params) {
return $this->getLastFormIn($this->renderWith('CommentAdmin_right'));
}
public function Section() {
$url = rtrim($_SERVER['REQUEST_URI'], '/');
if(strrpos($url, '&')) {
$url = substr($url, 0, strrpos($url, '&'));
}
$section = substr($url, strrpos($url, '/') + 1);
if($section != 'approved' && $section != 'unmoderated' && $section != 'spam') {
$section = Session::get('CommentsSection');
}
if($section != 'approved' && $section != 'unmoderated' && $section != 'spam') {
$section = 'approved';
}
return $section;
}
public function EditForm() {
$section = $this->Section();
if($section == 'approved') {
$filter = "\"IsSpam\" = 0 AND \"NeedsModeration\" = 0";
$title = "<h2>". _t('CommentAdmin.APPROVEDCOMMENTS', 'Approved Comments')."</h2>";
} else if($section == 'unmoderated') {
$filter = '"NeedsModeration" = 1';
$title = "<h2>"._t('CommentAdmin.COMMENTSAWAITINGMODERATION', 'Comments Awaiting Moderation')."</h2>";
} else {
$filter = '"IsSpam" = 1';
$title = "<h2>"._t('CommentAdmin.SPAM', 'Spam')."</h2>";
}
$filter .= ' AND "ParentID">0';
$tableFields = array(
"Name" => _t('CommentAdmin.AUTHOR', 'Author'),
"Comment" => _t('CommentAdmin.COMMENT', 'Comment'),
"Parent.Title" => _t('CommentAdmin.PAGE', 'Page'),
"CommenterURL" => _t('CommentAdmin.COMMENTERURL', 'URL'),
"Created" => _t('CommentAdmin.DATEPOSTED', 'Date Posted')
);
$popupFields = new FieldSet(
new TextField('Name', _t('CommentAdmin.NAME', 'Name')),
new TextField('CommenterURL', _t('CommentAdmin.COMMENTERURL', 'URL')),
new TextareaField('Comment', _t('CommentAdmin.COMMENT', 'Comment'))
);
$idField = new HiddenField('ID', '', $section);
$table = new CommentTableField($this, "Comments", "PageComment", $section, $tableFields, $popupFields, array($filter), 'Created DESC');
$table->setParentClass(false);
$table->setFieldCasting(array(
'Created' => 'SS_Datetime->Full',
'Comment' => array('HTMLText->LimitCharacters', 150)
));
$table->setPageSize(self::get_comments_per_page());
$table->addSelectOptions(array('all'=>'All', 'none'=>'None'));
$table->Markable = true;
$fields = new FieldSet(
new LiteralField("Title", $title),
$idField,
$table
);
$actions = new FieldSet();
if($section == 'unmoderated') {
$actions->push(new FormAction('acceptmarked', _t('CommentAdmin.ACCEPT', 'Accept')));
}
if($section == 'approved' || $section == 'unmoderated') {
$actions->push(new FormAction('spammarked', _t('CommentAdmin.SPAMMARKED', 'Mark as spam')));
}
if($section == 'spam') {
$actions->push(new FormAction('hammarked', _t('CommentAdmin.MARKASNOTSPAM', 'Mark as not spam')));
}
$actions->push(new FormAction('deletemarked', _t('CommentAdmin.DELETE', 'Delete')));
if($section == 'spam') {
$actions->push(new FormAction('deleteall', _t('CommentAdmin.DELETEALL', 'Delete All')));
}
$form = new Form($this, "EditForm", $fields, $actions);
return $form;
}
function deletemarked() {
$numComments = 0;
$folderID = 0;
$deleteList = '';
if($_REQUEST['Comments']) {
foreach($_REQUEST['Comments'] as $commentid) {
$comment = DataObject::get_by_id('PageComment', $commentid);
if($comment) {
$comment->delete();
$numComments++;
}
}
} else {
user_error("No comments in $commentList could be found!", E_USER_ERROR);
}
echo <<<JS
$deleteList
$('Form_EditForm').getPageFromServer($('Form_EditForm_ID').value);
statusMessage("Deleted $numComments comments.");
JS;
}
function deleteall() {
$numComments = 0;
$spam = DataObject::get('PageComment', '"PageComment"."IsSpam" = 1');
if($spam) {
$numComments = $spam->Count();
foreach($spam as $comment) {
$comment->delete();
}
}
$msg = sprintf(_t('CommentAdmin.DELETED', 'Deleted %s comments.'), $numComments);
echo <<<JS
$('Form_EditForm').getPageFromServer($('Form_EditForm_ID').value);
statusMessage("$msg");
JS;
}
function spammarked() {
$numComments = 0;
$folderID = 0;
$deleteList = '';
if($_REQUEST['Comments']) {
foreach($_REQUEST['Comments'] as $commentid) {
$comment = DataObject::get_by_id('PageComment', $commentid);
if($comment) {
$comment->IsSpam = true;
$comment->NeedsModeration = false;
$comment->write();
if(SSAkismet::isEnabled()) {
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.
}
}
$numComments++;
}
}
} else {
user_error("No comments in $commentList could be found!", E_USER_ERROR);
}
$msg = sprintf(_t('CommentAdmin.MARKEDSPAM', 'Marked %s comments as spam.'), $numComments);
echo <<<JS
$deleteList
$('Form_EditForm').getPageFromServer($('Form_EditForm_ID').value);
statusMessage("$msg");
JS;
}
function hammarked() {
$numComments = 0;
$folderID = 0;
$deleteList = '';
if($_REQUEST['Comments']) {
foreach($_REQUEST['Comments'] as $commentid) {
$comment = DataObject::get_by_id('PageComment', $commentid);
if($comment) {
$comment->IsSpam = false;
$comment->NeedsModeration = false;
$comment->write();
if(SSAkismet::isEnabled()) {
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.
}
}
$numComments++;
}
}
} else {
user_error("No comments in $commentList could be found!", E_USER_ERROR);
}
$msg = sprintf(_t('CommentAdmin.MARKEDNOTSPAM', 'Marked %s comments as not spam.'), $numComments);
echo <<<JS
$deleteList
$('Form_EditForm').getPageFromServer($('Form_EditForm_ID').value);
statusMessage("$msg");
JS;
}
function acceptmarked() {
$numComments = 0;
$folderID = 0;
$deleteList = '';
if($_REQUEST['Comments']) {
foreach($_REQUEST['Comments'] as $commentid) {
$comment = DataObject::get_by_id('PageComment', $commentid);
if($comment) {
$comment->IsSpam = false;
$comment->NeedsModeration = false;
$comment->write();
$numComments++;
}
}
} else {
user_error("No comments in $commentList could be found!", E_USER_ERROR);
}
$msg = sprintf(_t('CommentAdmin.APPROVED', 'Accepted %s comments.'), $numComments);
echo <<<JS
$deleteList
$('Form_EditForm').getPageFromServer($('Form_EditForm_ID').value);
statusMessage("Accepted $numComments comments.");
JS;
}
/**
* Return the number of moderated comments
*/
function NumModerated() {
return DB::query("SELECT COUNT(*) FROM \"PageComment\" WHERE \"IsSpam\"=0 AND \"NeedsModeration\"=0")->value();
}
/**
* Return the number of unmoderated comments
*/
function NumUnmoderated() {
return DB::query("SELECT COUNT(*) FROM \"PageComment\" WHERE \"IsSpam\"=0 AND \"NeedsModeration\"=1")->value();
}
/**
* Return the number of comments marked as spam
*/
function NumSpam() {
return DB::query("SELECT COUNT(*) FROM \"PageComment\" WHERE \"IsSpam\"=1")->value();
}
/**
* @param $num int
*/
function set_comments_per_page($num){
self::$comments_per_page = $num;
}
/**
* @return int
*/
function get_comments_per_page(){
return self::$comments_per_page;
}
}
?>
<?php
/**
* Special kind of ComplexTableField for managing comments.
* @package cms
* @subpackage comments
*/
class CommentTableField extends ComplexTableField {
protected $template = "CommentTableField";
protected $mode;
function __construct($controller, $name, $sourceClass, $mode, $fieldList, $detailFormFields = null, $sourceFilter = "", $sourceSort = "Created", $sourceJoin = "") {
$this->mode = $mode;
Session::set('CommentsSection', $mode);
parent::__construct($controller, $name, $sourceClass, $fieldList, $detailFormFields, $sourceFilter, $sourceSort, $sourceJoin);
$this->Markable = true;
// Note: These keys have special behaviour associated through TableListField.js
$this->selectOptions = array(
'all' => _t('CommentTableField.SELECTALL', 'All'),
'none' => _t('CommentTableField.SELECTNONE', 'None')
);
// search
$search = isset($_REQUEST['CommentSearch']) ? Convert::raw2sql($_REQUEST['CommentSearch']) : null;
if(!empty($_REQUEST['CommentSearch'])) {
$this->sourceFilter[] = "( \"Name\" LIKE '%$search%' OR \"Comment\" LIKE '%$search%')";
}
}
function FieldHolder() {
$ret = parent::FieldHolder();
Requirements::javascript(CMS_DIR . '/javascript/CommentTableField.js');
return $ret;
}
function Items() {
$this->sourceItems = $this->sourceItems();
if(!$this->sourceItems) {
return null;
}
$pageStart = (isset($_REQUEST['ctf'][$this->Name()]['start']) && is_numeric($_REQUEST['ctf'][$this->Name()]['start'])) ? $_REQUEST['ctf'][$this->Name()]['start'] : 0;
$this->sourceItems->setPageLimits($pageStart, $this->pageSize, $this->totalCount);
$output = new DataObjectSet();
foreach($this->sourceItems as $pageIndex=>$item) {
$output->push(Object::create('CommentTableField_Item',$item, $this, $pageStart+$pageIndex));
}
return $output;
}
function HasSpamButton() {
return $this->mode == 'approved' || $this->mode == 'unmoderated';
}
function HasApproveButton() {
return $this->mode == 'unmoderated';
}
function HasHamButton() {
return $this->mode == 'spam';
}
function SearchForm() {
$query = isset($_GET['CommentSearch']) ? $_GET['CommentSearch'] : null;
$searchFields = new FieldGroup(
new TextField('CommentSearch', _t('CommentTableField.SEARCH', 'Search'), $query),
new HiddenField("ctf[ID]",'',$this->mode),
new HiddenField('CommentFieldName','',$this->name)
);
$actionFields = new LiteralField('CommentFilterButton','<input type="submit" name="CommentFilterButton" value="'. _t('CommentTableField.FILTER', 'Filter') .'" id="CommentFilterButton"/>');
$fieldContainer = new FieldGroup(
$searchFields,
$actionFields
);
return $fieldContainer->FieldHolder();
}
function handleItem($request) {
return new CommentTableField_ItemRequest($this, $request->param('ID'));
}
}
/**
* Single row of a {@link CommentTableField}
* @package cms
* @subpackage comments
*/
class CommentTableField_Item extends ComplexTableField_Item {
function HasSpamButton() {
return $this->parent()->HasSpamButton();
}
function HasApproveButton() {
return $this->parent()->HasApproveButton();
}
function HasHamButton() {
return $this->parent()->HasHamButton();
}
/**
* @return String
*/
function SpamLink() {
return Controller::join_links($this->Link(), "pagecommentaction", 'reportspam', $this->ID);
}
/**
* @return String
*/
function HamLink() {
return Controller::join_links($this->Link(), "pagecommentaction", 'reportham', $this->ID);
}
/**
* @return String
*/
function ApproveLink() {
return Controller::join_links($this->Link(), "pagecommentaction", 'approve', $this->ID);
}
}
/**
* @package cms
* @subpackage comments
*/
class CommentTableField_ItemRequest extends ComplexTableField_ItemRequest {
static $url_handlers = array(
'pagecommentaction/$Action/$ID' => 'handlePageCommentAction',
);
/**
* @param SS_HTTPRequest $request
* @return SS_HTTPResponse
*/
function handlePageCommentAction($request) {
$action = $request->param('Action');
$whitelist = array('approve', 'reportspam', 'reportham');
if(!in_array($action, $whitelist)) $this->httpError(403);
$c = new PageComment_Controller($request);
$c->init();
return $c->$action($request);
}
}
?>
\ No newline at end of file
This diff is collapsed.
<?php
/**
* A FormField showing a list of files
* @package cms
* @subpackage assets
*/
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"');
}
}
?>
\ No newline at end of file
<?php
/**
* Imports {@link Group} records by CSV upload, as defined in
* {@link GroupCsvBulkLoader}.
*
* @package cms
* @subpackage batchactions
*/
class GroupImportForm extends Form {
/**
* @var Group Optional group relation
*/
protected $group;
function __construct($controller, $name, $fields = null, $actions = null, $validator = null) {
if(!$fields) {
$helpHtml = _t(
'GroupImportForm.Help1',
'<p>Import one or more groups in <em>CSV</em> format (comma-separated values). <small><a href="#" class="toggle-advanced">Show advanced usage</a></small></p>'
);
$helpHtml .= _t(
'GroupImportForm.Help2',
'<div class="advanced">
<h4>Advanced usage</h4>
<ul>
<li>Allowed columns: <em>%s</em></li>
<li>Existing groups are matched by their unique <em>Code</em> value, and updated with any new values from the imported file</li>
<li>Group hierarchies can be created by using a <em>ParentCode</em> column.</li>
<li>Permission codes can be assigned by the <em>PermissionCode</em> column. Existing permission codes are not cleared.</li>
</ul>
</div>');
$importer = new GroupCsvBulkLoader();
$importSpec = $importer->getImportSpec();
$helpHtml = sprintf($helpHtml, implode(', ', array_keys($importSpec['fields'])));
$fields = new FieldSet(
new LiteralField('Help', $helpHtml),
$fileField = new FileField(
'CsvFile',
_t(
'SecurityAdmin_MemberImportForm.FileFieldLabel',
'CSV File <small>(Allowed extensions: *.csv)</small>'
)
)
);
$fileField->getValidator()->setAllowedExtensions(array('csv'));
}
if(!$actions) $actions = new FieldSet(
new FormAction('doImport', _t('SecurityAdmin_MemberImportForm.BtnImport', 'Import'))
);
if(!$validator) $validator = new RequiredFields('CsvFile');
parent::__construct($controller, $name, $fields, $actions, $validator);
$this->addExtraClass('import-form');
}
function doImport($data, $form) {
$loader = new GroupCsvBulkLoader();
// load file
$result = $loader->load($data['CsvFile']['tmp_name']);
// result message
$msgArr = array();
if($result->CreatedCount()) $msgArr[] = sprintf(
_t('GroupImportForm.ResultCreated', 'Created %d groups'),
$result->CreatedCount()
);
if($result->UpdatedCount()) $msgArr[] = sprintf(
_t('GroupImportForm.ResultUpdated', 'Updated %d groups'),
$result->UpdatedCount()
);
if($result->DeletedCount()) $msgArr[] = sprintf(
_t('GroupImportForm.ResultDeleted', 'Deleted %d groups'),
$result->DeletedCount()
);
$msg = ($msgArr) ? implode(',', $msgArr) : _t('MemberImportForm.ResultNone', 'No changes');
$this->sessionMessage($msg, 'good');
Director::redirectBack();
}
}
?>
\ No newline at end of file
<?php
/**
* Base class for HTML cleaning classes.
*
* @package sapphire
* @subpackage misc
*/
abstract class HTMLCleaner extends Object {
/**
* Passed $content, return HTML that has been tidied.
* @return string $content HTML, tidied
*/
public abstract function cleanHTML($content);
}
?>
This diff is collapsed.
<?php
/**
* Plug-ins for additional functionality in your LeftAndMain classes.
*
* @package cms
* @subpackage core
*/
abstract class LeftAndMainDecorator extends Extension {
function init() {
}
function accessedCMS() {
}
function augmentNewSiteTreeItem(&$item) {
}
}
?>
\ No newline at end of file
<?php
/**
* Imports {@link Member} records by CSV upload, as defined in
* {@link MemberCsvBulkLoader}.
*
* @package cms
* @subpackage batchactions
*/
class MemberImportForm extends Form {
/**
* @var Group Optional group relation
*/
protected $group;
function __construct($controller, $name, $fields = null, $actions = null, $validator = null) {
if(!$fields) {
$helpHtml = _t(
'MemberImportForm.Help1',
'<p>Import members in <em>CSV format</em> (comma-separated values). <small><a href="#" class="toggle-advanced">Show advanced usage</a></small></p>'
);
$helpHtml .= _t(
'MemberImportForm.Help2',
'<div class="advanced">
<h4>Advanced usage</h4>
<ul>
<li>Allowed columns: <em>%s</em></li>
<li>Existing members are matched by their unique <em>Code</em> property, and updated with any new values from the imported file.</li>
<li>Groups can be assigned by the <em>Groups</em> column. Groups are identified by their <em>Code</em> property, multiple groups can be separated by comma. Existing group memberships are not cleared.</li>
</ul>
</div>');
$importer = new MemberCsvBulkLoader();
$importSpec = $importer->getImportSpec();
$helpHtml = sprintf($helpHtml, implode(', ', array_keys($importSpec['fields'])));
$fields = new FieldSet(
new LiteralField('Help', $helpHtml),
$fileField = new FileField(
'CsvFile',
_t(
'SecurityAdmin_MemberImportForm.FileFieldLabel',
'CSV File <small>(Allowed extensions: *.csv)</small>'
)
)
);
$fileField->getValidator()->setAllowedExtensions(array('csv'));
}
if(!$actions) $actions = new FieldSet(
new FormAction('doImport', _t('SecurityAdmin_MemberImportForm.BtnImport', 'Import'))
);
if(!$validator) $validator = new RequiredFields('CsvFile');
parent::__construct($controller, $name, $fields, $actions, $validator);
Requirements::javascript(CMS_DIR . '/javascript/MemberImportForm.js');
$this->addExtraClass('import-form');
}
function doImport($data, $form) {
$loader = new MemberCsvBulkLoader();
// optionally set group relation
if($this->group) $loader->setGroups(array($this->group));
// load file
$result = $loader->load($data['CsvFile']['tmp_name']);
// result message
$msgArr = array();
if($result->CreatedCount()) $msgArr[] = sprintf(
_t('MemberImportForm.ResultCreated', 'Created %d members'),
$result->CreatedCount()
);
if($result->UpdatedCount()) $msgArr[] = sprintf(
_t('MemberImportForm.ResultUpdated', 'Updated %d members'),
$result->UpdatedCount()
);
if($result->DeletedCount()) $msgArr[] = sprintf(
_t('MemberImportForm.ResultDeleted', 'Deleted %d members'),
$result->DeletedCount()
);
$msg = ($msgArr) ? implode(',', $msgArr) : _t('MemberImportForm.ResultNone', 'No changes');
$this->sessionMessage($msg, 'good');
Director::redirectBack();
}
/**
* @param $group Group
*/
function setGroup($group) {
$this->group = $group;
}
/**
* @return Group
*/
function getGroup($group) {
return $this->group;
}
}
?>
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
<?php
/**
* Reports section of the CMS.
*
* All reports that should show in the ReportAdmin section
* of the CMS need to subclass {@link SS_Report}, and implement
* the appropriate methods and variables that are required.
*
* @see SS_Report
*
* @package cms
* @subpackage reports
*/
class ReportAdmin extends LeftAndMain {
static $url_segment = 'reports';
static $url_rule = '/$Action/$ID';
static $menu_title = 'Reports';
static $template_path = null; // defaults to (project)/templates/email
public function init() {
parent::init();
Requirements::javascript(CMS_DIR . '/javascript/ReportAdmin_left.js');
Requirements::javascript(CMS_DIR . '/javascript/ReportAdmin_right.js');
Requirements::css(CMS_DIR . '/css/ReportAdmin.css');
// Set custom options for TinyMCE specific to ReportAdmin
HtmlEditorConfig::get('cms')->setOption('ContentCSS', project() . '/css/editor.css');
HtmlEditorConfig::get('cms')->setOption('Lang', i18n::get_tinymce_lang());
// Always block the HtmlEditorField.js otherwise it will be sent with an ajax request
Requirements::block(SAPPHIRE_DIR . '/javascript/HtmlEditorField.js');
}
/**
* Does the parent permission checks, but also
* makes sure that instantiatable subclasses of
* {@link Report} exist. By default, the CMS doesn't
* include any Reports, so there's no point in showing
*
* @param Member $member
* @return boolean
*/
function canView($member = null) {
if(!$member && $member !== FALSE) $member = Member::currentUser();
if(!parent::canView($member)) return false;
$hasViewableSubclasses = false;
foreach($this->Reports() as $report) {
if($report->canView($member)) return true;
}
return false;
}
/**
* Return a DataObjectSet of SS_Report subclasses
* that are available for use.
*
* @return DataObjectSet
*/
public function Reports() {
$output = new DataObjectSet();
foreach(SS_Report::get_reports('ReportAdmin') as $report) {
if($report->canView()) $output->push($report);
}
return $output;
}
/**
* Show a report based on the URL query string.
*
* @param SS_HTTPRequest $request The HTTP request object
*/
public function show($request) {
$params = $request->allParams();
return $this->showWithEditForm($params, $this->reportEditFormFor($params['ID']));
}
/**
* @TODO What does this do?
*
* @param unknown_type $params
* @param unknown_type $editForm
* @return unknown
*/
protected function showWithEditForm($params, $editForm) {
if(isset($params['ID'])) Session::set('currentReport', $params['ID']);
if(isset($params['OtherID'])) Session::set('currentOtherID', $params['OtherID']);
if(Director::is_ajax()) {
SSViewer::setOption('rewriteHashlinks', false);
$result = $this->customise(array(
'EditForm' => $editForm
))->renderWith($this->getTemplatesWithSuffix('_right'));
return $this->getLastFormIn($result);
}
return array();
}
/**
* For the current report that the user is viewing,
* return a Form instance with the fields for that
* report.
*
* @return Form
*/
public function EditForm() {
// Return the report if the ID is sent by request, or we're specifically asking for the edit form
$id = isset($_REQUEST['ID']) ? $_REQUEST['ID'] : ($this->getRequest()->latestParam('Action') == 'EditForm') ? Session::get('currentReport') : null;
if($id) {
foreach($this->Reports() as $report) {
if($id == $report->ID()) return $this->reportEditFormFor($id);
}
}
return false;
}
/**
* Get the current report
*
* @return SS_Report
*/
public function CurrentReport() {
$id = isset($_REQUEST['ID']) ? $_REQUEST['ID'] : ($this->getRequest()->latestParam('Action') == 'EditForm') ? Session::get('currentReport') : null;
if($id) {
foreach($this->Reports() as $report) {
if($id == $report->ID()) return $report;
}
}
return false;
}
/**
* Return a Form instance with fields for the
* particular report currently viewed.
*
* @TODO Dealing with multiple data types for the
* $id parameter is confusing. Ideally, it should
* deal with only one.
*
* @param id|string $id The ID of the report, or class name
* @return Form
*/
public function reportEditFormFor($id) {
$page = false;
$fields = new FieldSet();
$actions = new FieldSet();
$reports = SS_Report::get_reports('ReportAdmin');
$obj = $reports[$id];
if($obj) $fields = $obj->getCMSFields();
if($obj) $actions = $obj->getCMSActions();
$idField = new HiddenField('ID');
$idField->setValue($id);
$fields->push($idField);
$form = new Form($this, 'EditForm', $fields, $actions);
$form->loadDataFrom($_REQUEST);
// Include search criteria in the form action so that pagination works
$filteredCriteria = array_merge($_GET, $_POST);
foreach(array('ID','url','ajax','ctf','update','action_updatereport','SecurityID') as $notAParam) {
unset($filteredCriteria[$notAParam]);
}
$formLink = $this->Link() . '/EditForm';
if($filteredCriteria) $formLink .= '?' . http_build_query($filteredCriteria);
$form->setFormAction($formLink);
$form->setTemplate('ReportAdminForm');
return $form;
}
/**
* Determine if we have reports and need
* to display the "Reports" main menu item
* in the CMS.
*
* The test for an existance of a report
* is done by checking for a subclass of
* "SS_Report" that exists.
*
* @return boolean
*/
public static function has_reports() {
return sizeof(SS_Report::get_reports('ReportAdmin')) > 0;
}
public function updatereport() {
FormResponse::load_form($this->EditForm()->forTemplate());
return FormResponse::respond();
}
}
?>
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
#ReportSelector_holder {
background-color: #EEE;
border-bottom: 1px #CCC solid;
margin: 0;
padding: 3px;
}
#right iframe.AWStatsReport {
width: 98%;
height: 85%;
}
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment