diff --git a/core/Convert.php b/core/Convert.php
index ae9be210e..c9ede1f7b 100755
--- a/core/Convert.php
+++ b/core/Convert.php
@@ -33,7 +33,7 @@ class Convert extends Object {
foreach($val as $k => $v) $val[$k] = self::raw2att($v);
return $val;
} else {
- return str_replace(array('&','"',"'",'<','>'), array('&','"',''','<','>'), $val);
+ return str_replace(array('&','"',"'",'<','>'), array('&','"',''','<','>'), $val);
}
}
@@ -71,7 +71,7 @@ class Convert extends Object {
foreach($val as $k => $v) $val[$k] = self::raw2xml($v);
return $val;
} else {
- return str_replace(array('&','<','>',"\n",'"',"'"), array('&','<','>','
','"','''), $val);
+ return str_replace(array('&','<','>',"\n",'"',"'"), array('&','<','>','
','"','''), $val);
}
}
@@ -133,7 +133,7 @@ class Convert extends Object {
// More complex text needs to use html2raw instead
if(strpos($val,'<') !== false) return self::html2raw($val);
- $converted = str_replace(array('&','<','>','"','''), array('&','<','>','"',"'"), $val);
+ $converted = str_replace(array('&','<','>','"',''', '''), array('&','<','>','"',"'", "'"), $val);
$converted = ereg_replace('[0-9]+;', '', $converted);
return $converted;
}
@@ -335,4 +335,4 @@ class Convert extends Object {
}
-?>
\ No newline at end of file
+?>
diff --git a/core/Session.php b/core/Session.php
index f52975197..d4049eda3 100644
--- a/core/Session.php
+++ b/core/Session.php
@@ -194,7 +194,9 @@ class Session {
if(!session_id() && !headers_sent()) {
session_set_cookie_params(self::$timeout, Director::baseURL());
- session_start();
+ // @ is to supress win32 warnings/notices when session wasn't cleaned up properly
+ // There's nothing we can do about this, because it's an operating system function!
+ @session_start();
}
}
diff --git a/core/model/DataObject.php b/core/model/DataObject.php
index c89aa5cba..910867a72 100644
--- a/core/model/DataObject.php
+++ b/core/model/DataObject.php
@@ -262,8 +262,14 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
$this->class = get_class($this);
foreach($record as $k => $v) {
if($v) {
+ if($k == 'Created' || $k == 'LastEdited') {
+ $fieldtype = 'SSDatetime';
+ } else {
+ $fieldtype = $this->db($k);
+ }
+
// MSSQLDatabase::date() uses datetime for the data type for "Date" and "SSDatetime"
- switch($this->db($k)) {
+ switch($fieldtype) {
case "Date":
$v = preg_replace('/:[0-9][0-9][0-9]([ap]m)$/i', ' \\1', $v);
$record[$k] = date('Y-m-d', strtotime($v));
diff --git a/core/model/Hierarchy.php b/core/model/Hierarchy.php
index ac79b91b3..20ff69f21 100644
--- a/core/model/Hierarchy.php
+++ b/core/model/Hierarchy.php
@@ -429,66 +429,18 @@ class Hierarchy extends DataObjectDecorator {
$stageChildren = $this->owner->stageChildren(true);
$this->_cache_allChildrenIncludingDeleted = $stageChildren;
- $this->owner->extend("augmentAllChildrenIncludingDeleted", $stageChildren, $context);
-
- // Add live site content, if required.
+ // Add live site content that doesn't exist on the stage site, if required.
if($this->owner->hasExtension('Versioned')) {
- // Get all the requisite data, and index it
- $liveChildren = $this->owner->liveChildren(true);
-
- if(isset($stageChildren)) {
- foreach($stageChildren as $child) {
- $idxStageChildren[$child->ID] = $child;
- }
- }
-
- if(isset($liveChildren)) {
+ // Next, go through the live children. Only some of these will be listed
+ $liveChildren = $this->owner->liveChildren(true, true);
+ if($liveChildren) {
foreach($liveChildren as $child) {
- $idxLiveChildren[$child->ID] = $child;
- }
- }
-
- DataObject::disable_subclass_access();
- if($idxStageChildren) {
- $foundInLive = Versioned::get_by_stage( $baseClass, 'Live', "\"{$baseClass}\".\"ID\" IN (" . implode(",", array_keys($idxStageChildren)) . ")", "" );
- }
-
- if($idxLiveChildren) {
- $foundInStage = Versioned::get_by_stage( $baseClass, 'Stage', "\"{$baseClass}\".\"ID\" IN (" . implode(",", array_keys($idxLiveChildren)) . ")", "" );
- }
- DataObject::enable_subclass_access();
-
- if(isset($foundInLive)) {
- foreach($foundInLive as $child) {
- $idxFoundInLive[$child->ID] = $child;
- }
- }
-
- if(isset($foundInStage)) {
- foreach($foundInStage as $child) {
- $idxFoundInStage[$child->ID] = $child;
- }
- }
-
- $this->_cache_allChildrenIncludingDeleted = new DataObjectSet();
-
- // First, go through the stage children. They will all be listed but may be different colours
- if($stageChildren) {
- foreach($stageChildren as $child) {
$this->_cache_allChildrenIncludingDeleted->push($child);
}
}
-
- // Next, go through the live children. Only some of these will be listed
- if($liveChildren) {
- foreach($liveChildren as $child) {
- // Not found on stage = deleted page. Anything else is ignored
- if(!isset($idxFoundInStage[$child->ID])) {
- $this->_cache_allChildrenIncludingDeleted->push($child);
- }
- }
- }
}
+
+ $this->owner->extend("augmentAllChildrenIncludingDeleted", $stageChildren, $context);
} else {
user_error("Hierarchy::AllChildren() Couldn't determine base class for '{$this->owner->class}'", E_USER_ERROR);
@@ -529,7 +481,10 @@ class Hierarchy extends DataObjectDecorator {
$extraFilter = $showAll ? '' : " AND \"ShowInMenus\"=1";
$baseClass = ClassInfo::baseDataClass($this->owner->class);
- $staged = DataObject::get($baseClass, "\"{$baseClass}\".\"ParentID\" = " . (int)$this->owner->ID . " AND \"{$baseClass}\".\"ID\" != " . (int)$this->owner->ID . $extraFilter, "");
+ $staged = DataObject::get($baseClass, "\"{$baseClass}\".\"ParentID\" = "
+ . (int)$this->owner->ID . " AND \"{$baseClass}\".\"ID\" != " . (int)$this->owner->ID
+ . $extraFilter, "");
+
if(!$staged) $staged = new DataObjectSet();
$this->owner->extend("augmentStageChildren", $staged, $showAll);
return $staged;
@@ -538,12 +493,28 @@ class Hierarchy extends DataObjectDecorator {
/**
* Return children from the live site, if it exists.
* @param boolean $showAll Include all of the elements, even those not shown in the menus.
+ * @param boolean $onlyDeletedFromStage Only return items that have been deleted from stage
* @return DataObjectSet
*/
- public function liveChildren($showAll = false) {
+ public function liveChildren($showAll = false, $onlyDeletedFromStage = false) {
$extraFilter = $showAll ? '' : " AND \"ShowInMenus\"=1";
+ $join = "";
+
$baseClass = ClassInfo::baseDataClass($this->owner->class);
- return Versioned::get_by_stage($baseClass, "Live", "\"{$baseClass}\".\"ParentID\" = " . (int)$this->owner->ID . " AND \"{$baseClass}\".\"ID\" != " . (int)$this->owner->ID. $extraFilter, "");
+
+ if($onlyDeletedFromStage) {
+ // Note that the lack of double-quotes around $baseClass are the only thing preventing
+ // it from being rewritten to {$baseClass}_Live. This is brittle, won't work in
+ // postgres, and will need to be fixed *somehow*. Also, this code should probably be
+ // refactored to be pushed into Versioned somehow; perhaps a "doesn't exist on stage X"
+ // option for get_by_stage.
+ $join = "LEFT JOIN {$baseClass} ON {$baseClass}.\"ID\" = \"{$baseClass}\".\"ID\"";
+ $extraFilter .= " AND {$baseClass}.\"ID\" IS NULL";
+ }
+
+ return Versioned::get_by_stage($baseClass, "Live", "\"{$baseClass}\".\"ParentID\" = "
+ . (int)$this->owner->ID . " AND \"{$baseClass}\".\"ID\" != " . (int)$this->owner->ID
+ . $extraFilter, null, $join);
}
/**
diff --git a/core/model/Versioned.php b/core/model/Versioned.php
index 4105dfb09..bdad8d863 100755
--- a/core/model/Versioned.php
+++ b/core/model/Versioned.php
@@ -442,7 +442,12 @@ class Versioned extends DataObjectDecorator {
$oldStage = Versioned::$reading_stage;
Versioned::$reading_stage = $toStage;
+
+ $conn = DB::getConn();
+ if($conn->hasMethod('allowPrimaryKeyEditing')) $conn->allowPrimaryKeyEditing($baseClass, true);
$from->write();
+ if($conn->hasMethod('allowPrimaryKeyEditing')) $conn->allowPrimaryKeyEditing($baseClass, false);
+
$from->destroy();
Versioned::$reading_stage = $oldStage;
diff --git a/core/model/fieldtypes/Date.php b/core/model/fieldtypes/Date.php
index 9ccc5d2b1..4a0672d04 100644
--- a/core/model/fieldtypes/Date.php
+++ b/core/model/fieldtypes/Date.php
@@ -123,7 +123,7 @@ class Date extends DBField {
*/
function Ago() {
if($this->value) {
- if(time() > strtotime($this->value)) {
+ if(strtotime($this->value) == time() || time() > strtotime($this->value)) {
return sprintf(
_t(
'Date.TIMEDIFFAGO',
diff --git a/core/model/fieldtypes/HTMLText.php b/core/model/fieldtypes/HTMLText.php
index eb074d28c..5ba70dd6f 100755
--- a/core/model/fieldtypes/HTMLText.php
+++ b/core/model/fieldtypes/HTMLText.php
@@ -44,7 +44,9 @@ class HTMLText extends Text {
/* Catch warnings thrown by loadHTML and turn them into a failure boolean rather than a SilverStripe error */
set_error_handler(create_function('$no, $str', 'throw new Exception("HTML Parse Error: ".$str);'), E_ALL);
- try { $res = $doc->loadHTML('' . $this->value); }
+ // Nonbreaking spaces get converted into weird characters, so strip them
+ $value = str_replace(' ', ' ', $this->value);
+ try { $res = $doc->loadHTML('' . $value); }
catch (Exception $e) { $res = false; }
restore_error_handler();
diff --git a/dev/SapphireREPL.php b/dev/SapphireREPL.php
index e51103a40..6bc3c67da 100644
--- a/dev/SapphireREPL.php
+++ b/dev/SapphireREPL.php
@@ -24,7 +24,7 @@ define('30719',E_ALL);
*/
class SapphireREPL extends Controller {
- private function error_handler( $errno, $errstr, $errfile, $errline, $errctx ) {
+ public function error_handler( $errno, $errstr, $errfile, $errline, $errctx ) {
// Ignore unless important error
if ( ($errno & ~( 2048 | 8192 | 16384 )) == 0 ) return ;
// Otherwise throw exception to handle in REPL loop
diff --git a/filesystem/Folder.php b/filesystem/Folder.php
index 71f62351b..f00a941cb 100755
--- a/filesystem/Folder.php
+++ b/filesystem/Folder.php
@@ -191,11 +191,15 @@ class Folder extends File {
if($oldFile == $file && $i > 2) user_error("Couldn't fix $file with $i", E_USER_ERROR);
}
- if(file_exists($tmpFile['tmp_name']) && copy($tmpFile['tmp_name'], "$base/$file")) {
+ //$fullFilename = "$base/$file";
+ $fullFilename = $base . DIRECTORY_SEPARATOR . str_replace(array("\\","/") , DIRECTORY_SEPARATOR, $file );
+
+ if(file_exists($tmpFile['tmp_name']) && copy($tmpFile['tmp_name'], $fullFilename)) {
// Update with the new image
return $this->constructChild(basename($file));
} else {
- user_error("Folder::addUploadToFolder: Couldn't copy '$tmpFile[tmp_name]' to '$file'", E_USER_ERROR);
+ if(!file_exists($tmpFile['tmp_name'])) user_error("Folder::addUploadToFolder: '$tmpFile[tmp_name]' doesn't exist", E_USER_ERROR);
+ else user_error("Folder::addUploadToFolder: Couldn't copy '$tmpFile[tmp_name]' to '$fullFilename'", E_USER_ERROR);
return false;
}
}
@@ -260,7 +264,18 @@ class Folder extends File {
* Returns true if this folder has children
*/
public function hasChildren() {
- return $this->ID && $this->myChildren() && $this->myChildren()->Count() > 0;
+ return (bool)DB::query("SELECT COUNT(*) FROM \"File\" WHERE ParentID = "
+ . (int)$this->ID)->value();
+ }
+
+ /**
+ * Returns true if this folder has children
+ */
+ public function hasChildFolders() {
+ $SQL_folderClasses = Convert::raw2sql(ClassInfo::subclassesFor('Folder'));
+
+ return (bool)DB::query("SELECT COUNT(*) FROM \"File\" WHERE ParentID = " . (int)$this->ID
+ . " AND \"ClassName\" IN ('" . implode("','", $SQL_folderClasses) . "')")->value();
}
/**
@@ -311,7 +326,6 @@ class Folder extends File {
*/
function getCMSFields() {
$nameField = ($this->ID > 0) ? new TextField("Name") : new HiddenField("Name");
-
$fileList = new AssetTableField(
$this,
"Files",
@@ -350,10 +364,10 @@ class Folder extends File {
new LiteralField("UploadIframe",
$this->getUploadIframe()
)
- ),
+ )/*,
new Tab("UnusedFiles", _t('Folder.UNUSEDFILESTAB', "Unused files"),
new Folder_UnusedAssetsField($this)
- )
+ )*/
),
new HiddenField("ID")
);
@@ -422,6 +436,12 @@ class Folder extends File {
HTML;
}
+ /**
+ * Get the children of this folder that are also folders.
+ */
+ function ChildFolders() {
+ return DataObject::get("Folder", "ParentID = " . (int)$this->ID);
+ }
}
class Folder_UnusedAssetsField extends CompositeField {
diff --git a/forms/FormAction.php b/forms/FormAction.php
index d649c31db..2c36efb1c 100755
--- a/forms/FormAction.php
+++ b/forms/FormAction.php
@@ -19,6 +19,16 @@ class FormAction extends FormField {
*/
public $useButtonTag = false;
+ private $buttonContent = null;
+
+ /**
+ * Add content inside a button field.
+ */
+ function setButtonContent($content) {
+ $this->buttonContent = (string) $content;
+ }
+
+
/**
* Create a new action button.
* @param action The method to call when the button is clicked
@@ -74,7 +84,7 @@ class FormAction extends FormField {
$attributes['class'] = $attributes['class'] . ' disabled';
}
- return $this->createTag('button', $attributes, $this->attrTitle());
+ return $this->createTag('button', $attributes, $this->buttonContent ? $this->buttonContent : $this->attrTitle());
} else {
$attributes = array(
'class' => 'action' . ($this->extraClass() ? $this->extraClass() : ''),
@@ -117,4 +127,4 @@ class FormAction_WithoutLabel extends FormAction {
return null;
}
}
-?>
\ No newline at end of file
+?>