diff --git a/core/Core.php b/core/Core.php index 31bbca9ec..a1f71f04a 100755 --- a/core/Core.php +++ b/core/Core.php @@ -125,26 +125,8 @@ define('PR_LOW',10); /** * Ensure we have enough memory */ -$memString = ini_get("memory_limit"); -switch(strtolower(substr($memString, -1))) { -case "k": - $memory = round(substr($memString, 0, -1)*1024); - break; -case "m": - $memory = round(substr($memString, 0, -1)*1024*1024); - break; -case "g": - $memory = round(substr($memString, 0, -1)*1024*1024*1024); - break; -default: - $memory = round($memString); -} -// Check we have at least 64M -if ($memory < (64 * 1024 * 1024)) { - // Increase memory limit - ini_set('memory_limit', '64M'); -} +increase_memory_limit_to('64M'); /////////////////////////////////////////////////////////////////////////////// // INCLUDES @@ -305,4 +287,28 @@ function _t($entity, $string = "", $priority = 40, $context = "") { return i18n::_t($entity, $string, $priority, $context); } +/** + * Increase the memory limit to the given level if it's currently too low. + * @param A memory limit string, such as "64M" + */ +function increase_memory_limit_to($memoryLimit) { + // Increase the memory limit if it's too low + if(translate_memstring($memoryLimit) > translate_memstring(ini_get('memory_limit'))) { + ini_set('memory_limit', $memoryLimit); + } +} + +/** + * Turn a memory string, such as 512M into an actual number of bytes. + * @param A memory limit string, such as "64M" + */ +function translate_memstring($memString) { + switch(strtolower(substr($memString, -1))) { + case "k": return round(substr($memString, 0, -1)*1024); + case "m": return round(substr($memString, 0, -1)*1024*1024); + case "g": return round(substr($memString, 0, -1)*1024*1024*1024); + default: return round($memString); + } +} + ?> diff --git a/core/model/SiteTree.php b/core/model/SiteTree.php index 8e51f80c4..03c8c10ed 100644 --- a/core/model/SiteTree.php +++ b/core/model/SiteTree.php @@ -1298,13 +1298,6 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid //new NamedLabelField("Status", $message, "pageStatusMessage", true) ); - if(!Permission::check('SITETREE_GRANT_ACCESS')) { - $fields->makeFieldReadonly($viewersOptionsField); - $fields->makeFieldReadonly($viewerGroupsField); - $fields->makeFieldReadonly($editorsOptionsField); - $fields->makeFieldReadonly($editorGroupsField); - } - $viewersOptionsSource = array(); if($this->Parent()->ID || $this->CanViewType == 'Inherit') $viewersOptionsSource["Inherit"] = _t('SiteTree.INHERIT', "Inherit from parent page"); $viewersOptionsSource["Anyone"] = _t('SiteTree.ACCESSANYONE', "Anyone"); @@ -1317,6 +1310,13 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid $editorsOptionsSource["LoggedInUsers"] = _t('SiteTree.EDITANYONE', "Anyone who can log-in to the CMS"); $editorsOptionsSource["OnlyTheseUsers"] = _t('SiteTree.EDITONLYTHESE', "Only these people (choose from list)"); $editorsOptionsField->setSource($editorsOptionsSource); + + if(!Permission::check('SITETREE_GRANT_ACCESS')) { + $fields->makeFieldReadonly($viewersOptionsField); + $fields->makeFieldReadonly($viewerGroupsField); + $fields->makeFieldReadonly($editorsOptionsField); + $fields->makeFieldReadonly($editorGroupsField); + } $tabContent->setTitle(_t('SiteTree.TABCONTENT', "Content")); $tabMain->setTitle(_t('SiteTree.TABMAIN', "Main")); diff --git a/core/model/fieldtypes/HTMLText.php b/core/model/fieldtypes/HTMLText.php index 16b7402b3..eb074d28c 100755 --- a/core/model/fieldtypes/HTMLText.php +++ b/core/model/fieldtypes/HTMLText.php @@ -23,62 +23,90 @@ class HTMLText extends Text { } /** - * Create a summary of the content. This will either be the first paragraph, or the first $maxWords - * words, whichever is shorter + * Create a summary of the content. This will be some section of the first paragraph, limited by + * $maxWords. All internal tags are stripped out - the return value is a string + * + * This is sort of the HTML aware equivilent to Text#Summary, although the logic for summarising is not exactly the same + * + * @param int $maxWords Maximum number of words to return - may return less, but never more. Pass -1 for no limit + * @param int $flex Number of words to search through when looking for a nice cut point + * @param string $add What to add to the end of the summary if we cut at a less-than-ideal cut point + * @return string A nice(ish) summary with no html tags (but possibly still some html entities) + * + * @see sapphire/core/model/fieldtypes/Text#Summary($maxWords) */ - public function Summary( $maxWords = 50 ) { - // split the string into tags and words - $parts = Convert::xml2array( $this->value ); - - // store any unmatched tags - $tagStack = array(); - - $pIndex = 0; - - // find the first paragraph tag - for( $i = 0; $i < count( $parts ); $i++ ) - if( strpos( $parts[$i], '
' ) { - $summary .= $parts[$pIndex]; - break; - } - elseif( preg_match( '/<\/(\w+)>/', $parts[$pIndex], $endTag ) && $endTag[1] == substr( $tagStack[count($tagStack) - 1], 1, strlen( $endTag[1] ) ) ) { - array_pop( $tagStack ); - $words++; - $summary .= $parts[$pIndex++]; - } elseif( preg_match( '/^<\w+/', $parts[$pIndex] ) ) { - array_push( $tagStack, $parts[$pIndex] ); - $words++; - $summary .= $parts[$pIndex++]; - } else - $summary .= $parts[$pIndex++] . ' '; - } - - // Tags that shouldn't be closed - $noClose = array("br", "img"); - - // make sure that the summary is well formed XHTML by closing tags - while( $openTag = array_pop( $tagStack ) ) { - preg_match( '/^<(\w+)\s+/', $openTag, $tagName ); - if(sizeof($tagName) > 0) { - if(!in_array($tagName[1], $noClose)) { - $summary .= "{$tagName[1]}>"; - } + public function Summary($maxWords = 50, $flex = 15, $add = '...') { + $str = false; + + /* First we need the text of the first paragraph, without tags. Try using SimpleXML first */ + if (class_exists('SimpleXMLElement')) { + $doc = new DOMDocument(); + + /* 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); } + catch (Exception $e) { $res = false; } + restore_error_handler(); + + if ($res) { + $xml = simplexml_import_dom($doc); + $res = $xml->xpath('//p'); + if (!empty($res)) $str = strip_tags($res[0]->asXML()); } } - return $summary; + /* If that failed, most likely the passed HTML is broken. use a simple regex + a custom more brutal strip_tags. We don't use strip_tags because + * that does very badly on broken HTML*/ + if (!$str) { + /* See if we can pull a paragraph out*/ + if (preg_match('{
]*)?>(.*[A-Za-z]+.*)
}', $this->value, $matches)) $str = $matches[2]; + /* If _that_ failed, just use the whole text */ + else $str = $this->value; + + /* Now pull out all the html-alike stuff */ + $str = preg_replace('{?[a-zA-Z]+[^<>]*>}', '', $str); /* Take out anything that is obviously a tag */ + $str = preg_replace('{|<|>}', '', $str); /* Strip out any left over looking bits. Textual < or > should already be encoded to < or > */ + } + + /* Now split into words. If we are under the maxWords limit, just return the whole string (re-implode for whitespace normalization) */ + $words = preg_split('/\s+/', $str); + if ($maxWords == -1 || count($words) <= $maxWords) return implode(' ', $words); + + /* Otherwise work backwards for a looking for a sentence ending (we try to avoid abbreviations, but aren't very good at it) */ + for ($i = $maxWords; $i >= $maxWords - $flex && $i >= 0; $i--) { + if (preg_match('/\.$/', $words[$i]) && !preg_match('/(Dr|Mr|Mrs|Ms|Miss|Sr|Jr|No)\.$/i', $words[$i])) { + return implode(' ', array_slice($words, 0, $i+1)); + } + } + + /* If we didn't find a sentence ending quickly enough, just cut at the maxWords point and add '...' to the end */ + return implode(' ', array_slice($words, 0, $maxWords)) . $add; } + /** + * Returns the first sentence from the first paragraph. If it can't figure out what the first paragraph is (or there isn't one) + * it returns the same as Summary() + * + * This is the HTML aware equivilent to Text#FirstSentence + * + * @see sapphire/core/model/fieldtypes/Text#FirstSentence() + */ + function FirstSentence() { + /* Use summary's html processing logic to get the first paragraph */ + $paragraph = $this->Summary(-1); + + /* Then look for the first sentence ending. We could probably use a nice regex, but for now this will do */ + $words = preg_split('/\s+/', $paragraph); + foreach ($words as $i => $word) { + if (preg_match('/\.$/', $word) && !preg_match('/(Dr|Mr|Mrs|Ms|Miss|Sr|Jr|No)\.$/i', $word)) { + return implode(' ', array_slice($words, 0, $i+1)); + } + } + + /* If we didn't find a sentence ending, use the summary. We re-call rather than using paragraph so that Summary will limit the result this time */ + return $this->Summary(); + } + public function scaffoldFormField($title = null, $params = null) { return new HtmlEditorField($this->name, $title); } diff --git a/dev/BulkLoader.php b/dev/BulkLoader.php index 77613497d..6b581ac23 100644 --- a/dev/BulkLoader.php +++ b/dev/BulkLoader.php @@ -130,7 +130,9 @@ abstract class BulkLoader extends ViewableData { */ public function load($filepath, $memory_limit='512M') { ini_set('max_execution_time', 3600); - ini_set('memory_limit', $memory_limit); + + increase_memory_limit_to($memory_limit); + return $this->processAll($filepath); } diff --git a/dev/Debug.php b/dev/Debug.php index e0b40d2b4..d065076d3 100644 --- a/dev/Debug.php +++ b/dev/Debug.php @@ -646,14 +646,14 @@ function errorHandler($errno, $errstr, $errfile, $errline) { case E_ERROR: case E_CORE_ERROR: case E_USER_ERROR: - Debug::fatalHandler($errno, $errstr, $errfile, $errline, $bt); + Debug::fatalHandler($errno, $errstr, $errfile, $errline, null); break; case E_NOTICE: case E_WARNING: case E_CORE_WARNING: case E_USER_WARNING: - Debug::warningHandler($errno, $errstr, $errfile, $errline, $bt); + Debug::warningHandler($errno, $errstr, $errfile, $errline, null); break; } diff --git a/dev/DevelopmentAdmin.php b/dev/DevelopmentAdmin.php index a103b18bc..506902b71 100644 --- a/dev/DevelopmentAdmin.php +++ b/dev/DevelopmentAdmin.php @@ -63,6 +63,7 @@ class DevelopmentAdmin extends Controller { function index() { $actions = array( "build" => "Build/rebuild this environment (formerly db/build). Call this whenever you have updated your project sources", + "reset" => "Reset this environment - truncate the database and rebuild. This is useful after testing to start with a fresh working copy", "tests" => "See a list of unit tests to run", "tests/all" => "Run all tests", "jstests" => "See a list of JavaScript tests to run", @@ -79,15 +80,7 @@ class DevelopmentAdmin extends Controller { $renderer = new DebugView(); $renderer->writeHeader(); - - // Todo: the database info could be encapsulated better - global $databaseConfig; - $summaryInfo = "Base URL: " . Director::absoluteBaseURL() . "\n" - . "Document root: " . Director::baseFolder() . "\n" - . "Database: " . DB::getConn()->class . ", database " . DB::getConn()->currentDatabase() - . " on " . $databaseConfig['server']; - - $renderer->writeInfo("Sapphire Development Tools", $summaryInfo); + $renderer->writeInfo("Sapphire Development Tools", Director::absoluteBaseURL()); $base = Director::baseURL(); echo '