FIX Remove instances of lines longer than 120c

The entire framework repo (with the exception of system-generated files) has been amended to respect the 120c line-length limit.  This is in preparation for the enforcement of this rule with PHP_CodeSniffer.
This commit is contained in:
Sam Minnee 2012-09-27 09:34:00 +12:00
parent 28bd939580
commit 1f7fc1f76a
282 changed files with 4780 additions and 2640 deletions

View File

@ -18,7 +18,9 @@ DatabaseAdapterRegistry::register(
'title' => 'SQL Server 2008',
'helperPath' => 'mssql/code/MSSQLDatabaseConfigurationHelper.php',
'supported' => (function_exists('mssql_connect') || function_exists('sqlsrv_connect')),
'missingExtensionText' => 'Neither the <a href="http://php.net/mssql">mssql</a> or <a href="http://www.microsoft.com/sqlserver/2005/en/us/PHP-Driver.aspx">sqlsrv</a> PHP extensions are available. Please install or enable one of them and refresh this page.'
'missingExtensionText' => 'Neither the <a href="http://php.net/mssql">mssql</a> or'
. ' <a href="http://www.microsoft.com/sqlserver/2005/en/us/PHP-Driver.aspx">sqlsrv</a> PHP extensions are'
. ' available. Please install or enable one of them and refresh this page.'
)
);
@ -28,7 +30,8 @@ DatabaseAdapterRegistry::register(
'title' => 'PostgreSQL 8.3+',
'helperPath' => 'postgresql/code/PostgreSQLDatabaseConfigurationHelper.php',
'supported' => function_exists('pg_query'),
'missingExtensionText' => 'The <a href="http://php.net/pgsql">pgsql</a> PHP extension is not available. Please install or enable it and refresh this page.'
'missingExtensionText' => 'The <a href="http://php.net/pgsql">pgsql</a> PHP extension is not available. Please'
. ' install or enable it and refresh this page.'
)
);
@ -38,11 +41,15 @@ DatabaseAdapterRegistry::register(
'title' => 'SQLite 3.3+',
'helperPath' => 'sqlite3/code/SQLiteDatabaseConfigurationHelper.php',
'supported' => (class_exists('SQLite3') || class_exists('PDO')),
'missingExtensionText' => 'The <a href="http://php.net/manual/en/book.sqlite3.php">SQLite3</a> and <a href="http://php.net/manual/en/book.pdo.php">PDO</a> classes are not available. Please install or enable one of them and refresh this page.',
'missingExtensionText' => 'The <a href="http://php.net/manual/en/book.sqlite3.php">SQLite3</a> and'
. ' <a href="http://php.net/manual/en/book.pdo.php">PDO</a> classes are not available. Please install or'
. ' enable one of them and refresh this page.',
'fields' => array(
'path' => array(
'title' => 'Database path<br /><small>Absolute path, writeable by the webserver user.<br />Recommended to be outside of your webroot</small>',
'default' => realpath(dirname($_SERVER['SCRIPT_FILENAME'])) . DIRECTORY_SEPARATOR . 'assets' . DIRECTORY_SEPARATOR . '.db'
'title' => 'Database path<br /><small>Absolute path, writeable by the webserver user.<br />'
. 'Recommended to be outside of your webroot</small>',
'default' => realpath(dirname($_SERVER['SCRIPT_FILENAME'])) . DIRECTORY_SEPARATOR . 'assets'
. DIRECTORY_SEPARATOR . '.db'
),
'database' => array(
'title' => 'Database name',

View File

@ -11,16 +11,33 @@ HtmlEditorConfig::get('cms')->setOptions(array(
'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]",
'extended_valid_elements' => "img[class|src|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]",
'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]",
'extended_valid_elements' => "img[class|src|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]",
'spellchecker_rpc_url' => THIRDPARTY_DIR . '/tinymce-spellchecker/rpc.php'
));
HtmlEditorConfig::get('cms')->enablePlugins('media', 'fullscreen', 'inlinepopups');
HtmlEditorConfig::get('cms')->enablePlugins(array('ssbuttons' => sprintf('../../../%s/tinymce_ssbuttons/editor_plugin_src.js', THIRDPARTY_DIR)));
HtmlEditorConfig::get('cms')->enablePlugins(array(
'ssbuttons' => sprintf('../../../%s/tinymce_ssbuttons/editor_plugin_src.js', THIRDPARTY_DIR)
));
HtmlEditorConfig::get('cms')->insertButtonsBefore('formatselect', 'styleselect');
HtmlEditorConfig::get('cms')->addButtonsToLine(2, 'ssmedia', 'ssflash', 'sslink', 'unlink', 'anchor', 'separator','code', 'fullscreen', 'separator');
HtmlEditorConfig::get('cms')->addButtonsToLine(2,
'ssmedia', 'ssflash', 'sslink', 'unlink', 'anchor', 'separator','code', 'fullscreen', 'separator');
HtmlEditorConfig::get('cms')->removeButtons('tablecontrols');
HtmlEditorConfig::get('cms')->addButtonsToLine(3, 'tablecontrols');

View File

@ -13,7 +13,8 @@ class AdminRootController extends Controller {
/**
* @var string
* @config
* The LeftAndMain child that will be used as the initial panel to display if none is selected (i.e. if you visit /admin)
* The LeftAndMain child that will be used as the initial panel to display if none is selected (i.e. if you
* visit /admin)
*/
static $default_panel = 'SecurityAdmin';

View File

@ -85,7 +85,9 @@ class CMSBatchActionHandler extends RequestHandler {
foreach($ids as $k => $v) if(!is_numeric($v)) unset($ids[$k]);
if($ids) {
if(class_exists('Translatable') && Object::has_extension('SiteTree','Translatable')) Translatable::disable_locale_filter();
if(class_exists('Translatable') && Object::has_extension('SiteTree','Translatable')) {
Translatable::disable_locale_filter();
}
$pages = DataObject::get(
$this->recordClass,
@ -96,7 +98,9 @@ class CMSBatchActionHandler extends RequestHandler {
)
);
if(class_exists('Translatable') && Object::has_extension('SiteTree','Translatable')) Translatable::enable_locale_filter();
if(class_exists('Translatable') && Object::has_extension('SiteTree','Translatable')) {
Translatable::enable_locale_filter();
}
if(Object::has_extension($this->recordClass, 'Versioned')) {
// If we didn't query all the pages, then find the rest on the live site

View File

@ -77,7 +77,8 @@ class CMSMenu extends Object implements IteratorAggregate, i18nEntityProvider
* @param string $code A unique identifier (used to create a CSS ID and its key in {@link $menu_items})
* @param string $menuTitle The link's title in the CMS menu
* @param string $url The url of the link
* @param integer $priority The menu priority (sorting order) of the menu item. Higher priorities will be further left.
* @param integer $priority The menu priority (sorting order) of the menu item. Higher priorities will be further
* left.
* @return boolean The result of the operation.
*/
public static function add_link($code, $menuTitle, $url, $priority = -1) {

View File

@ -17,7 +17,8 @@ class GroupImportForm extends Form {
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>'
'<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',
@ -25,9 +26,11 @@ class GroupImportForm extends Form {
<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>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>
<li>Permission codes can be assigned by the <em>PermissionCode</em> column. Existing permission codes are not
cleared.</li>
</ul>
</div>');
@ -48,11 +51,11 @@ class GroupImportForm extends Form {
$fileField->getValidator()->setAllowedExtensions(array('csv'));
}
if(!$actions) $actions = new FieldList(
$importAction = new FormAction('doImport', _t('SecurityAdmin_MemberImportForm.BtnImport', 'Import from CSV'))
);
$importAction->addExtraClass('ss-ui-button');
if(!$actions) {
$action = new FormAction('doImport', _t('SecurityAdmin_MemberImportForm.BtnImport', 'Import from CSV'));
$action->addExtraClass('ss-ui-button');
$actions = new FieldList($action);
}
if(!$validator) $validator = new RequiredFields('CsvFile');

View File

@ -200,9 +200,14 @@ class LeftAndMain extends Controller implements PermissionProvider {
// if no alternate menu items have matched, return a permission error
$messageSet = array(
'default' => _t('LeftAndMain.PERMDEFAULT',"Please choose an authentication method and enter your credentials to access the CMS."),
'alreadyLoggedIn' => _t('LeftAndMain.PERMALREADY',"I'm sorry, but you can't access that part of the CMS. If you want to log in as someone else, do so below"),
'logInAgain' => _t('LeftAndMain.PERMAGAIN',"You have been logged out of the CMS. If you would like to log in again, enter a username and password below."),
'default' => _t('LeftAndMain.PERMDEFAULT',
"Please choose an authentication method and enter your credentials to access the CMS."),
'alreadyLoggedIn' => _t('LeftAndMain.PERMALREADY',
"I'm sorry, but you can't access that part of the CMS. If you want to log in as someone else, do"
. " so below"),
'logInAgain' => _t('LeftAndMain.PERMAGAIN',
"You have been logged out of the CMS. If you would like to log in again, enter a username and"
. " password below."),
);
return Security::permissionFailure($this, $messageSet);
@ -683,7 +688,9 @@ class LeftAndMain extends Controller implements PermissionProvider {
* Children, AllChildrenIncludingDeleted, or AllHistoricalChildren
* @return String Nested unordered list with links to each page
*/
public function getSiteTreeFor($className, $rootID = null, $childrenMethod = null, $numChildrenMethod = null, $filterFunction = null, $minNodeCount = 30) {
public function getSiteTreeFor($className, $rootID = null, $childrenMethod = null, $numChildrenMethod = null,
$filterFunction = null, $minNodeCount = 30) {
// Filter criteria
$params = $this->request->getVar('q');
if(isset($params['FilterClass']) && $filterClass = $params['FilterClass']){
@ -696,7 +703,10 @@ class LeftAndMain extends Controller implements PermissionProvider {
}
// Default childrenMethod and numChildrenMethod
if(!$childrenMethod) $childrenMethod = ($filter && $filter->getChildrenMethod()) ? $filter->getChildrenMethod() : 'AllChildrenIncludingDeleted';
if(!$childrenMethod) $childrenMethod = ($filter && $filter->getChildrenMethod())
? $filter->getChildrenMethod()
: 'AllChildrenIncludingDeleted';
if(!$numChildrenMethod) $numChildrenMethod = 'numChildren';
if(!$filterFunction) $filterFunction = ($filter) ? array($filter, 'isPageIncluded') : null;
@ -714,7 +724,8 @@ class LeftAndMain extends Controller implements PermissionProvider {
// NOTE: SiteTree/CMSMain coupling :-(
if(class_exists('SiteTree')) {
SiteTree::prepopulate_permission_cache('CanEditType', $obj->markedNodeIDs(), 'SiteTree::can_edit_multiple');
SiteTree::prepopulate_permission_cache('CanEditType', $obj->markedNodeIDs(),
'SiteTree::can_edit_multiple');
}
// getChildrenAsUL is a flexible and complex way of traversing the tree
@ -789,20 +800,31 @@ class LeftAndMain extends Controller implements PermissionProvider {
$ids = explode(',', $request->getVar('ids'));
foreach($ids as $id) {
$record = $this->getRecord($id);
$recordController = ($this->stat('tree_class') == 'SiteTree') ? singleton('CMSPageEditController') : $this;
$recordController = ($this->stat('tree_class') == 'SiteTree')
? singleton('CMSPageEditController')
: $this;
// Find the next & previous nodes, for proper positioning (Sort isn't good enough - it's not a raw offset)
// TODO: These methods should really be in hierarchy - for a start it assumes Sort exists
$next = $prev = null;
$className = $this->stat('tree_class');
$next = DataObject::get($className)->filter('ParentID', $record->ParentID)->filter('Sort:GreaterThan', $record->Sort)->first();
$next = DataObject::get($className)
->filter('ParentID', $record->ParentID)
->filter('Sort:GreaterThan', $record->Sort)
->first();
if (!$next) {
$prev = DataObject::get($className)->filter('ParentID', $record->ParentID)->filter('Sort:LessThan', $record->Sort)->reverse()->first();
$prev = DataObject::get($className)
->filter('ParentID', $record->ParentID)
->filter('Sort:LessThan', $record->Sort)
->reverse()
->first();
}
$link = Controller::join_links($recordController->Link("show"), $record->ID);
$html = LeftAndMain_TreeNode::create($record, $link, $this->isCurrentPage($record))->forTemplate() . '</li>';
$html = LeftAndMain_TreeNode::create($record, $link, $this->isCurrentPage($record))
->forTemplate() . '</li>';
$data[$id] = array(
'html' => $html,
@ -874,7 +896,8 @@ class LeftAndMain extends Controller implements PermissionProvider {
if (!Permission::check('SITETREE_REORGANISE') && !Permission::check('ADMIN')) {
$this->response->setStatusCode(
403,
_t('LeftAndMain.CANT_REORGANISE',"You do not have permission to rearange the site tree. Your change was not saved.")
_t('LeftAndMain.CANT_REORGANISE',
"You do not have permission to rearange the site tree. Your change was not saved.")
);
return;
}
@ -889,7 +912,8 @@ class LeftAndMain extends Controller implements PermissionProvider {
if(($parentID == '0' || $root == 'root') && !SiteConfig::current_site_config()->canCreateTopLevel()){
$this->response->setStatusCode(
403,
_t('LeftAndMain.CANT_REORGANISE',"You do not have permission to alter Top level pages. Your change was not saved.")
_t('LeftAndMain.CANT_REORGANISE',
"You do not have permission to alter Top level pages. Your change was not saved.")
);
return;
}
@ -905,8 +929,7 @@ class LeftAndMain extends Controller implements PermissionProvider {
if(!$node) {
$this->response->setStatusCode(
500,
_t(
'LeftAndMain.PLEASESAVE',
_t('LeftAndMain.PLEASESAVE',
"Please Save Page: This page could not be upated because it hasn't been saved yet."
)
);
@ -933,7 +956,8 @@ class LeftAndMain extends Controller implements PermissionProvider {
}
}
$this->response->addHeader('X-Status', rawurlencode(_t('LeftAndMain.REORGANISATIONSUCCESSFUL', 'Reorganised the site tree successfully.')));
$this->response->addHeader('X-Status',
rawurlencode(_t('LeftAndMain.REORGANISATIONSUCCESSFUL', 'Reorganised the site tree successfully.')));
}
// Update sorting
@ -950,11 +974,13 @@ class LeftAndMain extends Controller implements PermissionProvider {
// Nodes that weren't "actually moved" shouldn't be registered as
// having been edited; do a direct SQL update instead
++$counter;
DB::query(sprintf("UPDATE \"%s\" SET \"Sort\" = %d WHERE \"ID\" = '%d'", $className, $counter, $id));
DB::query(sprintf("UPDATE \"%s\" SET \"Sort\" = %d WHERE \"ID\" = '%d'",
$className, $counter, $id));
}
}
$this->response->addHeader('X-Status', rawurlencode(_t('LeftAndMain.REORGANISATIONSUCCESSFUL', 'Reorganised the site tree successfully.')));
$this->response->addHeader('X-Status',
rawurlencode(_t('LeftAndMain.REORGANISATIONSUCCESSFUL', 'Reorganised the site tree successfully.')));
}
return Convert::raw2json($statusUpdates);
@ -1527,7 +1553,8 @@ class LeftAndMainMarkingFilter {
// We need to recurse up the tree,
// finding ParentIDs for each ID until we run out of parents
while (!empty($parents)) {
$res = DB::query('SELECT "ParentID", "ID" FROM "SiteTree" WHERE "ID" in ('.implode(',',array_keys($parents)).')');
$res = DB::query('SELECT "ParentID", "ID" FROM "SiteTree"'
. ' WHERE "ID" in ('.implode(',',array_keys($parents)).')');
$parents = array();
foreach($res as $row) {
@ -1623,12 +1650,11 @@ class LeftAndMain_TreeNode extends ViewableData {
*/
public function forTemplate() {
$obj = $this->obj;
return "<li id=\"record-$obj->ID\" data-id=\"$obj->ID\" data-pagetype=\"$obj->ClassName\" class=\"" . $this->getClasses() . "\">" .
"<ins class=\"jstree-icon\">&nbsp;</ins>" .
"<a href=\"" . $this->getLink() . "\" title=\"" .
_t('LeftAndMain.PAGETYPE','Page type: ') .
"$obj->class\" ><ins class=\"jstree-icon\">&nbsp;</ins><span class=\"text\">" . ($obj->TreeTitle).
"</span></a>";
return "<li id=\"record-$obj->ID\" data-id=\"$obj->ID\" data-pagetype=\"$obj->ClassName\" class=\""
. $this->getClasses() . "\">" . "<ins class=\"jstree-icon\">&nbsp;</ins>"
. "<a href=\"" . $this->getLink() . "\" title=\"" . _t('LeftAndMain.PAGETYPE','Page type: ')
. "$obj->class\" ><ins class=\"jstree-icon\">&nbsp;</ins><span class=\"text\">" . ($obj->TreeTitle)
. "</span></a>";
}
public function getClasses() {

View File

@ -17,7 +17,8 @@ class MemberImportForm extends Form {
if(!$fields) {
$helpHtml = _t(
'MemberImportForm.Help1',
'<p>Import users in <em>CSV format</em> (comma-separated values). <small><a href="#" class="toggle-advanced">Show advanced usage</a></small></p>'
'<p>Import users in <em>CSV format</em> (comma-separated values).'
. ' <small><a href="#" class="toggle-advanced">Show advanced usage</a></small></p>'
);
$helpHtml .= _t(
'MemberImportForm.Help2',
@ -25,8 +26,10 @@ class MemberImportForm extends Form {
<h4>Advanced usage</h4>
<ul>
<li>Allowed columns: <em>%s</em></li>
<li>Existing users 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>
<li>Existing users 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>');
@ -47,11 +50,11 @@ class MemberImportForm extends Form {
$fileField->getValidator()->setAllowedExtensions(array('csv'));
}
if(!$actions) $actions = new FieldList(
$importAction = new FormAction('doImport', _t('SecurityAdmin_MemberImportForm.BtnImport', 'Import from CSV'))
);
$importAction->addExtraClass('ss-ui-button');
if(!$actions) {
$action = new FormAction('doImport', _t('SecurityAdmin_MemberImportForm.BtnImport', 'Import from CSV'));
$action->addExtraClass('ss-ui-button');
$actions = new FieldList($action);
}
if(!$validator) $validator = new RequiredFields('CsvFile');

View File

@ -17,7 +17,8 @@
* @todo ajax result display
* @todo relation formfield scaffolding (one tab per relation) - relations don't have DBField sublclasses, we do
* we define the scaffold defaults. can be ComplexTableField instances for a start.
* @todo has_many/many_many relation autocomplete field (HasManyComplexTableField doesn't work well with larger datasets)
* @todo has_many/many_many relation autocomplete field (HasManyComplexTableField doesn't work well with larger
* datasets)
*
* Long term TODOs:
* @todo Hook into RESTful interface on DataObjects (yet to be developed)
@ -150,7 +151,8 @@ abstract class ModelAdmin extends LeftAndMain {
);
$form->addExtraClass('cms-edit-form cms-panel-padded center');
$form->setTemplate($this->getTemplatesWithSuffix('_EditForm'));
$form->setFormAction(Controller::join_links($this->Link($this->sanitiseClassName($this->modelClass)), 'EditForm'));
$editFormAction = Controller::join_links($this->Link($this->sanitiseClassName($this->modelClass)), 'EditForm');
$form->setFormAction($editFormAction);
$form->setAttribute('data-pjax-fragment', 'CurrentForm');
$this->extend('updateEditForm', $form);
@ -354,7 +356,10 @@ abstract class ModelAdmin extends LeftAndMain {
))->renderWith('ModelAdmin_ImportSpec');
$fields->push(new LiteralField("SpecFor{$modelName}", $specHTML));
$fields->push(new CheckboxField('EmptyBeforeImport', _t('ModelAdmin.EMPTYBEFOREIMPORT', 'Clear Database before import'), false));
$fields->push(
new CheckboxField('EmptyBeforeImport', _t('ModelAdmin.EMPTYBEFOREIMPORT', 'Clear Database before import'),
false)
);
$actions = new FieldList(
new FormAction('import', _t('ModelAdmin.IMPORT', 'Import from CSV'))
@ -366,7 +371,9 @@ abstract class ModelAdmin extends LeftAndMain {
$fields,
$actions
);
$form->setFormAction(Controller::join_links($this->Link($this->sanitiseClassName($this->modelClass)), 'ImportForm'));
$form->setFormAction(
Controller::join_links($this->Link($this->sanitiseClassName($this->modelClass)), 'ImportForm')
);
$this->extend('updateImportForm', $form);
@ -385,7 +392,9 @@ abstract class ModelAdmin extends LeftAndMain {
* @param SS_HTTPRequest $request
*/
public function import($data, $form, $request) {
if(!$this->showImportForm || (is_array($this->showImportForm) && !in_array($this->modelClass,$this->showImportForm))) {
if(!$this->showImportForm || (is_array($this->showImportForm)
&& !in_array($this->modelClass,$this->showImportForm))) {
return false;
}
@ -420,7 +429,9 @@ abstract class ModelAdmin extends LeftAndMain {
'ModelAdmin.DELETEDRECORDS', "Deleted {count} records.",
array('count' => $results->DeletedCount())
);
if(!$results->CreatedCount() && !$results->UpdatedCount()) $message .= _t('ModelAdmin.NOIMPORT', "Nothing to import");
if(!$results->CreatedCount() && !$results->UpdatedCount()) {
$message .= _t('ModelAdmin.NOIMPORT', "Nothing to import");
}
$form->sessionMessage($message, 'good');
$this->redirectBack();

View File

@ -96,7 +96,8 @@ class SecurityAdmin extends LeftAndMain implements PermissionProvider {
sprintf('<p class="caution-remove"><strong>%s</strong></p>',
_t(
'SecurityAdmin.MemberListCaution',
'Caution: Removing members from this list will remove them from all groups and the database'
'Caution: Removing members from this list will remove them from all groups and the'
. ' database'
)
)
),
@ -104,7 +105,8 @@ class SecurityAdmin extends LeftAndMain implements PermissionProvider {
new LiteralField(
'MemberImportFormIframe',
sprintf(
'<iframe src="%s" id="MemberImportFormIframe" width="100%%" height="250px" border="0"></iframe>',
'<iframe src="%s" id="MemberImportFormIframe" width="100%%" height="250px" border="0">'
. '</iframe>',
$this->Link('memberimport')
)
)
@ -115,7 +117,8 @@ class SecurityAdmin extends LeftAndMain implements PermissionProvider {
new LiteralField(
'GroupImportFormIframe',
sprintf(
'<iframe src="%s" id="GroupImportFormIframe" width="100%%" height="250px" border="0"></iframe>',
'<iframe src="%s" id="GroupImportFormIframe" width="100%%" height="250px" border="0">'
. '</iframe>',
$this->Link('groupimport')
)
)
@ -280,13 +283,16 @@ class SecurityAdmin extends LeftAndMain implements PermissionProvider {
'EDIT_PERMISSIONS' => array(
'name' => _t('SecurityAdmin.EDITPERMISSIONS', 'Manage permissions for groups'),
'category' => _t('Permissions.PERMISSIONS_CATEGORY', 'Roles and access permissions'),
'help' => _t('SecurityAdmin.EDITPERMISSIONS_HELP', 'Ability to edit Permissions and IP Addresses for a group. Requires the "Access to \'Security\' section" permission.'),
'help' => _t('SecurityAdmin.EDITPERMISSIONS_HELP',
'Ability to edit Permissions and IP Addresses for a group.'
. ' Requires the "Access to \'Security\' section" permission.'),
'sort' => 0
),
'APPLY_ROLES' => array(
'name' => _t('SecurityAdmin.APPLY_ROLES', 'Apply roles to groups'),
'category' => _t('Permissions.PERMISSIONS_CATEGORY', 'Roles and access permissions'),
'help' => _t('SecurityAdmin.APPLY_ROLES_HELP', 'Ability to edit the roles assigned to a group. Requires the "Access to \'Users\' section" permission.'),
'help' => _t('SecurityAdmin.APPLY_ROLES_HELP', 'Ability to edit the roles assigned to a group.'
. ' Requires the "Access to \'Users\' section" permission.'),
'sort' => 0
)
);

View File

@ -17,9 +17,12 @@ class CMSMenuTest extends SapphireTest implements TestOnly {
$menuItems = CMSMenu::get_menu_items();
$menuItem = $menuItems['CMSMenuTest_LeftAndMainController'];
$this->assertInstanceOf('CMSMenuItem', $menuItem, 'Controller menu item is of class CMSMenuItem');
$this->assertEquals($menuItem->url, singleton('CMSMenuTest_LeftAndMainController')->Link(), 'Controller menu item has the correct link');
$this->assertEquals($menuItem->controller, 'CMSMenuTest_LeftAndMainController', 'Controller menu item has the correct controller class');
$this->assertEquals($menuItem->priority, singleton('CMSMenuTest_LeftAndMainController')->stat('menu_priority'), 'Controller menu item has the correct priority');
$this->assertEquals($menuItem->url, singleton('CMSMenuTest_LeftAndMainController')->Link(),
'Controller menu item has the correct link');
$this->assertEquals($menuItem->controller, 'CMSMenuTest_LeftAndMainController',
'Controller menu item has the correct controller class');
$this->assertEquals($menuItem->priority, singleton('CMSMenuTest_LeftAndMainController')->stat('menu_priority'),
'Controller menu item has the correct priority');
CMSMenu::clear_menu();
// Add a link to the menu

View File

@ -24,7 +24,8 @@ class LeftAndMainTest extends FunctionalTest {
public function testSaveTreeNodeSorting() {
$this->loginWithPermission('ADMIN');
$rootPages = DataObject::get('LeftAndMainTest_Object', '"ParentID" = 0', '"ID"'); // forcing sorting for non-MySQL
// forcing sorting for non-MySQL
$rootPages = DataObject::get('LeftAndMainTest_Object', '"ParentID" = 0', '"ID"');
$siblingIDs = $rootPages->column('ID');
$page1 = $rootPages->offsetGet(0);
$page2 = $rootPages->offsetGet(1);

View File

@ -1,7 +1,7 @@
<?php
/**
* A DataFormatter object handles transformation of data from SilverStripe model objects to a particular output format, and vice versa.
* This is most commonly used in developing RESTful APIs.
* A DataFormatter object handles transformation of data from SilverStripe model objects to a particular output
* format, and vice versa. This is most commonly used in developing RESTful APIs.
*
* @package framework
* @subpackage formatters
@ -247,7 +247,9 @@ abstract class DataFormatter extends Object {
if(is_array($this->customFields)) {
foreach($this->customFields as $fieldName) {
// @todo Possible security risk by making methods accessible - implement field-level security
if($obj->hasField($fieldName) || $obj->hasMethod("get{$fieldName}")) $dbFields[$fieldName] = $fieldName;
if($obj->hasField($fieldName) || $obj->hasMethod("get{$fieldName}")) {
$dbFields[$fieldName] = $fieldName;
}
}
} else {
// by default, all database fields are selected
@ -257,7 +259,9 @@ abstract class DataFormatter extends Object {
if(is_array($this->customAddFields)) {
foreach($this->customAddFields as $fieldName) {
// @todo Possible security risk by making methods accessible - implement field-level security
if($obj->hasField($fieldName) || $obj->hasMethod("get{$fieldName}")) $dbFields[$fieldName] = $fieldName;
if($obj->hasField($fieldName) || $obj->hasMethod("get{$fieldName}")) {
$dbFields[$fieldName] = $fieldName;
}
}
}

View File

@ -74,7 +74,11 @@ class JSONDataFormatter extends DataFormatter {
} else {
$href = Director::absoluteURL(self::$api_base . "$className/$id/$relName");
}
$serobj->$relName = ArrayData::array_to_object(array("className" => $relClass, "href" => "$href.json", "id" => $obj->$fieldName));
$serobj->$relName = ArrayData::array_to_object(array(
"className" => $relClass,
"href" => "$href.json",
"id" => $obj->$fieldName
));
}
foreach($obj->has_many() as $relName => $relClass) {
@ -89,7 +93,11 @@ class JSONDataFormatter extends DataFormatter {
foreach($items as $item) {
//$href = Director::absoluteURL(self::$api_base . "$className/$id/$relName/$item->ID");
$href = Director::absoluteURL(self::$api_base . "$relClass/$item->ID");
$innerParts[] = ArrayData::array_to_object(array("className" => $relClass, "href" => "$href.json", "id" => $obj->$fieldName));
$innerParts[] = ArrayData::array_to_object(array(
"className" => $relClass,
"href" => "$href.json",
"id" => $obj->$fieldName
));
}
$serobj->$relName = $innerParts;
}
@ -106,7 +114,11 @@ class JSONDataFormatter extends DataFormatter {
foreach($items as $item) {
//$href = Director::absoluteURL(self::$api_base . "$className/$id/$relName/$item->ID");
$href = Director::absoluteURL(self::$api_base . "$relClass/$item->ID");
$innerParts[] = ArrayData::array_to_object(array("className" => $relClass, "href" => "$href.json", "id" => $obj->$fieldName));
$innerParts[] = ArrayData::array_to_object(array(
"className" => $relClass,
"href" => "$href.json",
"id" => $obj->$fieldName
));
}
$serobj->$relName = $innerParts;
}

View File

@ -147,7 +147,8 @@ class RSSFeed extends ViewableData {
if(isset($this->entries)) {
foreach($this->entries as $entry) {
$output->push(new RSSFeed_Entry($entry, $this->titleField, $this->descriptionField, $this->authorField));
$output->push(
new RSSFeed_Entry($entry, $this->titleField, $this->descriptionField, $this->authorField));
}
}
return $output;
@ -329,8 +330,13 @@ class RSSFeed_Entry extends ViewableData {
* @return string Returns the URL of this entry
*/
public function AbsoluteLink() {
if($this->failover->hasMethod('AbsoluteLink')) return $this->failover->AbsoluteLink();
else if($this->failover->hasMethod('Link')) return Director::absoluteURL($this->failover->Link());
else user_error($this->failover->class . " object has neither an AbsoluteLink nor a Link method. Can't put a link in the RSS feed", E_USER_WARNING);
if($this->failover->hasMethod('AbsoluteLink')) {
return $this->failover->AbsoluteLink();
} else if($this->failover->hasMethod('Link')) {
return Director::absoluteURL($this->failover->Link());
} else {
user_error($this->failover->class . " object has neither an AbsoluteLink nor a Link method."
. " Can't put a link in the RSS feed", E_USER_WARNING);
}
}
}

View File

@ -94,7 +94,9 @@ class RestfulService extends ViewableData {
}
/**
* Makes a request to the RESTful server, and return a {@link RestfulService_Response} object for parsing of the result.
* Makes a request to the RESTful server, and return a {@link RestfulService_Response} object for parsing of the
* result.
*
* @todo Better POST, PUT, DELETE, and HEAD support
* @todo Caching of requests - probably only GET and HEAD requestst
* @todo JSON support in RestfulService_Response
@ -102,7 +104,8 @@ class RestfulService extends ViewableData {
*
* This is a replacement of {@link connect()}.
*
* @return RestfulService_Response - If curl request produces error, the returned response's status code will be 500
* @return RestfulService_Response - If curl request produces error, the returned response's status code will
* be 500
*/
public function request($subURL = '', $method = "GET", $data = null, $headers = null, $curlOptions = array()) {
@ -116,7 +119,9 @@ class RestfulService extends ViewableData {
$cache_path = $cachedir."/xmlresponse_$cache_file";
// Check for unexpired cached feed (unless flush is set)
if(!isset($_GET['flush']) && @file_exists($cache_path) && @filemtime($cache_path) + $this->cache_expire > time()) {
if(!isset($_GET['flush']) && @file_exists($cache_path)
&& @filemtime($cache_path) + $this->cache_expire > time()) {
$store = file_get_contents($cache_path);
$response = unserialize($store);

View File

@ -79,7 +79,8 @@ class XMLDataFormatter extends DataFormatter {
} else {
$href = Director::absoluteURL(self::$api_base . "$className/$id/$relName");
}
$xml .= "<$relName linktype=\"has_one\" href=\"$href.xml\" id=\"" . $obj->$fieldName . "\"></$relName>\n";
$xml .= "<$relName linktype=\"has_one\" href=\"$href.xml\" id=\"" . $obj->$fieldName
. "\"></$relName>\n";
}
foreach($obj->has_many() as $relName => $relClass) {

34
cache/Cache.php vendored
View File

@ -15,7 +15,8 @@
*
* USING A CACHE
*
* $cache = SS_Cache::factory('foo') ; // foo is any name (try to be specific), and is used to get configuration & storage info
* $cache = SS_Cache::factory('foo') ; // foo is any name (try to be specific), and is used to get configuration &
* storage info
*
* if (!($result = $cache->load($cachekey))) {
* $result = caluate some how;
@ -24,8 +25,9 @@
*
* return $result;
*
* Normally there's no need to remove things from the cache - the cache backends clear out entries based on age & maximum
* allocated storage. If you include the version of the object in the cache key, even object changes don't need any invalidation
* Normally there's no need to remove things from the cache - the cache backends clear out entries based on age &
* maximum allocated storage. If you include the version of the object in the cache key, even object changes don't
* need any invalidation
*
* DISABLING CACHING IN DEV MOVE
*
@ -38,11 +40,13 @@
* (in _config.php)
*
* SS_Cache::add_backend('primary_memcached', 'Memcached',
* array('host' => 'localhost', 'port' => 11211, 'persistent' => true, 'weight' => 1, 'timeout' => 5, 'retry_interval' => 15, 'status' => true, 'failure_callback' => '' )
* array('host' => 'localhost', 'port' => 11211, 'persistent' => true, 'weight' => 1, 'timeout' => 5,
* 'retry_interval' => 15, 'status' => true, 'failure_callback' => '' )
* );
*
* SS_Cache::pick_backend('primary_memcached', 'any', 10);
* SS_Cache::pick_backend('default', 'aggregate', 20); // Aggregate needs a backend with tag support, which memcached doesn't provide
* // Aggregate needs a backend with tag support, which memcached doesn't provide
* SS_Cache::pick_backend('default', 'aggregate', 20);
* </code>
*
* USING APC AND FILE AS TWO LEVEL STORE
@ -55,7 +59,8 @@
* 'slow_backend_options' => array('cache_dir' => TEMP_FOLDER . DIRECTORY_SEPARATOR . 'cache')
* ));
*
* SS_Cache::pick_backend('two-level', 'any', 10); // No need for special backend for aggregate - TwoLevels with a File slow backend supports tags
* // No need for special backend for aggregate - TwoLevels with a File slow backend supports tags
* SS_Cache::pick_backend('two-level', 'any', 10);
*
* @author hfried
* @package framework
@ -75,7 +80,10 @@ class SS_Cache {
if (!isset(self::$backends['default'])) {
$cachedir = TEMP_FOLDER . DIRECTORY_SEPARATOR . 'cache';
if (!is_dir($cachedir)) mkdir($cachedir);
self::$backends['default'] = array('File', array('cache_dir' => TEMP_FOLDER . DIRECTORY_SEPARATOR . 'cache'));
self::$backends['default'] = array(
'File',
array('cache_dir' => TEMP_FOLDER . DIRECTORY_SEPARATOR . 'cache')
);
self::$cache_lifetime['default'] = array('lifetime' => 600, 'priority' => 1);
}
}
@ -98,8 +106,9 @@ class SS_Cache {
*
* @param string $name The name of the backend, as passed as the first argument to add_backend
* @param string $for The name of the cache to pick this backend for (or 'any' for any backend)
* @param integer $priority The priority of this pick - the call with the highest number will be the actual backend picked.
* A backend picked for a specific cache name will always be used instead of 'any' if it exists, no matter the priority.
* @param integer $priority The priority of this pick - the call with the highest number will be the actual
* backend picked. A backend picked for a specific cache name will always be used instead
* of 'any' if it exists, no matter the priority.
* @return none
*/
public static function pick_backend($name, $for, $priority=1) {
@ -124,7 +133,8 @@ class SS_Cache {
*
* @param string $for The name of the cache to set this lifetime for (or 'any' for all backends)
* @param integer $lifetime The lifetime of an item of the cache, in seconds, or -1 to disable caching
* @param integer $priority The priority. The highest priority setting is used. Unlike backends, 'any' is not special in terms of priority.
* @param integer $priority The priority. The highest priority setting is used. Unlike backends, 'any' is not
* special in terms of priority.
*/
public static function set_cache_lifetime($for, $lifetime=600, $priority=1) {
self::init();
@ -132,7 +142,9 @@ class SS_Cache {
$current = -1;
if (isset(self::$cache_lifetime[$for])) $current = self::$cache_lifetime[$for]['priority'];
if ($priority >= $current) self::$cache_lifetime[$for] = array('lifetime' => $lifetime, 'priority' => $priority);
if ($priority >= $current) {
self::$cache_lifetime[$for] = array('lifetime' => $lifetime, 'priority' => $priority);
}
}
/**

View File

@ -3,7 +3,8 @@
/**
* File similar to main.php designed for command-line scripts
*
* This file lets you execute SilverStripe requests from the command-line. The URL is passed as the first argument to the scripts.
* This file lets you execute SilverStripe requests from the command-line. The URL is passed as the first argument to
* the scripts.
*
* @package framework
* @subpackage core
@ -74,7 +75,8 @@ DB::connect($databaseConfig);
// Get the request URL from the querystring arguments
$url = isset($_SERVER['argv'][1]) ? $_SERVER['argv'][1] : null;
if(!$url) {
echo 'Please specify an argument to cli-script.php/sake. For more information, visit http://doc.silverstripe.org/doku.php?id=sake';
echo 'Please specify an argument to cli-script.php/sake. For more information, visit'
. ' http://doc.silverstripe.org/framework/en/topics/commandline';
die();
}

View File

@ -18,20 +18,24 @@
* - SS_DATABASE_TIMEZONE: Set the database timezone to something other than the system timezone.
*
* There is one more setting that is intended to be used by people who work on SilverStripe.
* - SS_DATABASE_CHOOSE_NAME: Boolean/Int. If set, then the system will choose a default database name for you if one isn't give
* in the $database variable. The database name will be "SS_" followed by the name of the folder into which you have installed
* SilverStripe. If this is enabled, it means that the phpinstaller will work out of the box without the installer needing to
* alter any files. This helps prevent accidental changes to the environment.
* - SS_DATABASE_CHOOSE_NAME: Boolean/Int. If set, then the system will choose a default database name for you if
* one isn't give in the $database variable. The database name will be "SS_" followed by the name of the folder
* into which you have installed SilverStripe. If this is enabled, it means that the phpinstaller will work out of
* the box without the installer needing to alter any files. This helps prevent accidental changes to the
* environment.
*
* If SS_DATABASE_CHOOSE_NAME is an integer greater than one, then an ancestor folder will be used for the database name. This
* is handy for a site that's hosted from /sites/examplesite/www or /buildbot/allmodules-2.3/build. If it's 2, the parent folder
* will be chosen; if it's 3 the grandparent, and so on.
* If SS_DATABASE_CHOOSE_NAME is an integer greater than one, then an ancestor folder will be used for the database
* name. This is handy for a site that's hosted from /sites/examplesite/www or /buildbot/allmodules-2.3/build. If
* it's 2, the parent folder will be chosen; if it's 3 the grandparent, and so on.
*
* You can configure the environment with this define:
*
* - SS_ENVIRONMENT_TYPE: The environment type: dev, test or live.
*
* You can configure the default admin with these defines
* - SS_DEFAULT_ADMIN_USERNAME: The username of the default admin - this is a non-database user with administrative privileges.
* You can configure the default admin with these defines:
*
* - SS_DEFAULT_ADMIN_USERNAME: The username of the default admin - this is a non-database user with administrative
* privileges.
* - SS_DEFAULT_ADMIN_PASSWORD: The password of the default admin.
* - SS_USE_BASIC_AUTH: Protect the site with basic auth (good for test sites)
*
@ -46,12 +50,17 @@
* _ss_environment.php handler
*/
if(defined('SS_ENVIRONMENT_FILE')) {
// Only perform valdiation if SS_ENVIRONMENT_FILE is actually set, which is to say, there is an _ss_environment.php file
// Only perform valdiation if SS_ENVIRONMENT_FILE is actually set, which is to say, there is an
// _ss_environment.php file
foreach(array(
'SS_DATABASE_PASSWORD',
'SS_DATABASE_USERNAME',
'SS_ENVIRONMENT_TYPE',) as $reqDefine) {
if(!defined($reqDefine)) user_error("$reqDefine must be defined in your _ss_environment.php. See http://doc.silverstripe.org/doku.php?id=environment-management for more infomration", E_USER_ERROR);
if(!defined($reqDefine)) {
user_error("$reqDefine must be defined in your _ss_environment.php."
. "See http://doc.silverstripe.org/framework/en/topics/environment-management for more infomration",
E_USER_ERROR);
}
}
}
@ -100,7 +109,12 @@ if(defined('SS_SEND_ALL_EMAILS_TO')) {
}
if(defined('SS_DEFAULT_ADMIN_USERNAME')) {
if(!defined('SS_DEFAULT_ADMIN_PASSWORD')) user_error("SS_DEFAULT_ADMIN_PASSWORD must be defined in your _ss_environment.php, if SS_DEFAULT_ADMIN_USERNAME is defined. See http://doc.silverstripe.org/doku.php?id=environment-management for more infomration", E_USER_ERROR);
if(!defined('SS_DEFAULT_ADMIN_PASSWORD')) {
user_error("SS_DEFAULT_ADMIN_PASSWORD must be defined in your _ss_environment.php,"
. "if SS_DEFAULT_ADMIN_USERNAME is defined. See "
. "http://doc.silverstripe.org/framework/en/topics/environment-management for more infomration",
E_USER_ERROR);
}
Security::setDefaultAdmin(SS_DEFAULT_ADMIN_USERNAME, SS_DEFAULT_ADMIN_PASSWORD);
}
if(defined('SS_USE_BASIC_AUTH') && SS_USE_BASIC_AUTH) {

View File

@ -33,16 +33,16 @@ class ContentNegotiator {
protected static $enabled = false;
/**
* Set the character set encoding for this page. By default it's utf-8, but you could change it to, say, windows-1252, to
* improve interoperability with extended characters being imported from windows excel.
* Set the character set encoding for this page. By default it's utf-8, but you could change it to, say,
* windows-1252, to improve interoperability with extended characters being imported from windows excel.
*/
public static function set_encoding($encoding) {
self::$encoding = $encoding;
}
/**
* Return the character encoding set bhy ContentNegotiator::set_encoding(). It's recommended that all classes that need to
* specify the character set make use of this function.
* Return the character encoding set bhy ContentNegotiator::set_encoding(). It's recommended that all classes
* that need to specify the character set make use of this function.
*/
public static function get_encoding() {
return self::$encoding;
@ -70,7 +70,10 @@ class ContentNegotiator {
$contentType = $response->getHeader("Content-Type");
// Disable content negotation for other content types
if($contentType && substr($contentType, 0,9) != 'text/html' && substr($contentType, 0,21) != 'application/xhtml+xml') return false;
if($contentType && substr($contentType, 0,9) != 'text/html'
&& substr($contentType, 0,21) != 'application/xhtml+xml') {
return false;
}
if(self::$enabled) return true;
else return (substr($response->getBody(),0,5) == '<' . '?xml');
@ -91,8 +94,8 @@ class ContentNegotiator {
$chosenFormat = $_GET['forceFormat'];
} else {
// The W3C validator doesn't send an HTTP_ACCEPT header, but it can support xhtml. We put this special case in here so that
// designers don't get worried that their templates are HTML4.
// The W3C validator doesn't send an HTTP_ACCEPT header, but it can support xhtml. We put this special
// case in here so that designers don't get worried that their templates are HTML4.
if(isset($_SERVER['HTTP_USER_AGENT']) && substr($_SERVER['HTTP_USER_AGENT'], 0, 14) == 'W3C_Validator/') {
$chosenFormat = "xhtml";
@ -175,7 +178,9 @@ class ContentNegotiator {
// Only replace the doctype in templates with the xml header
if($hasXMLHeader) {
$content = preg_replace('/<!DOCTYPE[^>]+>/', '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">', $content);
$content = preg_replace('/<!DOCTYPE[^>]+>/',
'<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">',
$content);
}
$content = preg_replace('/<html xmlns="[^"]+"/','<html ', $content);

View File

@ -76,7 +76,8 @@ class Controller extends RequestHandler implements TemplateGlobalProvider {
$member = Member::currentUser();
if($member) {
if(!headers_sent()) Cookie::set("PastMember", true, 90, null, null, false, true);
DB::query("UPDATE \"Member\" SET \"LastVisited\" = " . DB::getConn()->now() . " WHERE \"ID\" = $member->ID", null);
DB::query("UPDATE \"Member\" SET \"LastVisited\" = " . DB::getConn()->now()
. " WHERE \"ID\" = $member->ID", null);
}
}
@ -136,7 +137,10 @@ class Controller extends RequestHandler implements TemplateGlobalProvider {
// Init
$this->baseInitCalled = false;
$this->init();
if(!$this->baseInitCalled) user_error("init() method on class '$this->class' doesn't call Controller::init(). Make sure that you have parent::init() included.", E_USER_WARNING);
if(!$this->baseInitCalled) {
user_error("init() method on class '$this->class' doesn't call Controller::init()."
. "Make sure that you have parent::init() included.", E_USER_WARNING);
}
$this->extend('onAfterInit');
@ -148,12 +152,18 @@ class Controller extends RequestHandler implements TemplateGlobalProvider {
$body = parent::handleRequest($request, $model);
if($body instanceof SS_HTTPResponse) {
if(isset($_REQUEST['debug_request'])) Debug::message("Request handler returned SS_HTTPResponse object to $this->class controller; returning it without modification.");
if(isset($_REQUEST['debug_request'])) {
Debug::message("Request handler returned SS_HTTPResponse object to $this->class controller;"
. "returning it without modification.");
}
$this->response = $body;
} else {
if(is_object($body)) {
if(isset($_REQUEST['debug_request'])) Debug::message("Request handler $body->class object to $this->class controller;, rendering with template returned by $body->class::getViewer()");
if(isset($_REQUEST['debug_request'])) {
Debug::message("Request handler $body->class object to $this->class controller;"
. "rendering with template returned by $body->class::getViewer()");
}
$body = $body->getViewer($request->latestParam('Action'))->process($body);
}
@ -407,12 +417,12 @@ class Controller extends RequestHandler implements TemplateGlobalProvider {
}
}
//-----------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------
/**
* Pushes this controller onto the stack of current controllers.
* This means that any redirection, session setting, or other things that rely on Controller::curr() will now write to this
* controller object.
* This means that any redirection, session setting, or other things that rely on Controller::curr() will now
* write to this controller object.
*/
public function pushCurrent() {
array_unshift(self::$controller_stack, $this);
@ -433,7 +443,8 @@ class Controller extends RequestHandler implements TemplateGlobalProvider {
if($this === self::$controller_stack[0]) {
array_shift(self::$controller_stack);
} else {
user_error("popCurrent called on $this->class controller, but it wasn't at the top of the stack", E_USER_WARNING);
user_error("popCurrent called on $this->class controller, but it wasn't at the top of the stack",
E_USER_WARNING);
}
}
@ -444,7 +455,8 @@ class Controller extends RequestHandler implements TemplateGlobalProvider {
if(!$this->response) $this->response = new SS_HTTPResponse();
if($this->response->getHeader('Location')) {
user_error("Already directed to " . $this->response->getHeader('Location') . "; now trying to direct to $url", E_USER_WARNING);
user_error("Already directed to " . $this->response->getHeader('Location')
. "; now trying to direct to $url", E_USER_WARNING);
return;
}
@ -491,7 +503,8 @@ class Controller extends RequestHandler implements TemplateGlobalProvider {
/**
* Tests whether a redirection has been requested.
* @return string If redirect() has been called, it will return the URL redirected to. Otherwise, it will return null;
* @return string If redirect() has been called, it will return the URL redirected to. Otherwise, it will
* return null;
*/
public function redirectedTo() {
return $this->response && $this->response->getHeader('Location');

View File

@ -23,14 +23,16 @@ class Cookie {
* @param boolean $secure See http://php.net/set_session
* @param boolean $httpOnly See http://php.net/set_session
*/
public static function set($name, $value, $expiry = 90, $path = null, $domain = null, $secure = false, $httpOnly = false) {
public static function set($name, $value, $expiry = 90, $path = null, $domain = null, $secure = false,
$httpOnly = false) {
if(!headers_sent($file, $line)) {
$expiry = $expiry > 0 ? time()+(86400*$expiry) : $expiry;
$path = ($path) ? $path : Director::baseURL();
setcookie($name, $value, $expiry, $path, $domain, $secure, $httpOnly);
} else {
if(self::$report_errors) {
user_error("Cookie '$name' can't be set. The site started outputting was content at line $line in $file", E_USER_WARNING);
user_error("Cookie '$name' can't be set. The site started outputting content at line $line in $file",
E_USER_WARNING);
}
}
}

View File

@ -2,10 +2,11 @@
/**
* Director is responsible for processing URLs, and providing environment information.
*
* The most important part of director is {@link Director::direct()}, which is passed a URL and will execute the appropriate
* controller.
* The most important part of director is {@link Director::direct()}, which is passed a URL and will execute the
* appropriate controller.
*
* Director also has a number of static methods that provide information about the environment, such as {@link Director::set_environment_type()}.
* Director also has a number of static methods that provide information about the environment, such as
* {@link Director::set_environment_type()}.
*
* @package framework
* @subpackage control
@ -37,12 +38,14 @@ class Director implements TemplateGlobalProvider {
*
* The director is responsible for turning URLs into Controller objects.
*
* @param $priority The priority of the rules; higher values will get your rule checked first.
* We recommend priority 100 for your site's rules. The built-in rules are priority 10, standard modules are priority 50.
* @param $priority The priority of the rules; higher values will get your rule checked first. We recommend
* priority 100 for your site's rules. The built-in rules are priority 10, standard modules are
* priority 50.
*/
public static function addRules($priority, $rules) {
if ($priority != 100) {
Deprecation::notice('3.0', 'Priority argument is now ignored - use the default of 100. You should really be setting routes via _config yaml fragments though.', Deprecation::SCOPE_GLOBAL);
Deprecation::notice('3.0', 'Priority argument is now ignored - use the default of 100. You should really'
. ' be setting routes via _config yaml fragments though.', Deprecation::SCOPE_GLOBAL);
}
Config::inst()->update('Director', 'rules', $rules);
@ -53,15 +56,15 @@ class Director implements TemplateGlobalProvider {
*
* Request processing is handled as follows:
* - Director::direct() creates a new SS_HTTPResponse object and passes this to Director::handleRequest().
* - Director::handleRequest($request) checks each of the Director rules and identifies a controller to handle this
* request.
* - Controller::handleRequest($request) is then called. This will find a rule to handle the URL, and call the rule
* handling method.
* - Director::handleRequest($request) checks each of the Director rules and identifies a controller to handle
* this request.
* - Controller::handleRequest($request) is then called. This will find a rule to handle the URL, and call the
* rule handling method.
* - RequestHandler::handleRequest($request) is recursively called whenever a rule handling method returns a
* RequestHandler object.
*
* In addition to request processing, Director will manage the session, and perform the output of the actual response
* to the browser.
* In addition to request processing, Director will manage the session, and perform the output of the actual
* response to the browser.
*
* @param $url String, the URL the user is visiting, without the querystring.
* @uses handleRequest() rule-lookup logic is handled by this.
@ -85,7 +88,9 @@ class Director implements TemplateGlobalProvider {
}
$req = new SS_HTTPRequest(
(isset($_SERVER['X-HTTP-Method-Override'])) ? $_SERVER['X-HTTP-Method-Override'] : $_SERVER['REQUEST_METHOD'],
(isset($_SERVER['X-HTTP-Method-Override']))
? $_SERVER['X-HTTP-Method-Override']
: $_SERVER['REQUEST_METHOD'],
$url,
$_GET,
ArrayLib::array_merge_recursive((array)$_POST, (array)$_FILES),
@ -98,7 +103,9 @@ class Director implements TemplateGlobalProvider {
}
// Only resume a session if its not started already, and a session identifier exists
if(!isset($_SESSION) && (isset($_COOKIE[session_name()]) || isset($_REQUEST[session_name()]))) Session::start();
if(!isset($_SESSION) && (isset($_COOKIE[session_name()]) || isset($_REQUEST[session_name()]))) {
Session::start();
}
// Initiate an empty session - doesn't initialize an actual PHP session until saved (see belwo)
$session = new Session(isset($_SESSION) ? $_SESSION : null);
@ -167,14 +174,15 @@ class Director implements TemplateGlobalProvider {
/**
* Test a URL request, returning a response object.
*
* This method is the counterpart of Director::direct() that is used in functional testing. It will execute the URL given,
* This method is the counterpart of Director::direct() that is used in functional testing. It will execute the
* URL given, and return the result as an SS_HTTPResponse object.
*
* @param string $url The URL to visit
* @param array $postVars The $_POST & $_FILES variables
* @param Session $session The {@link Session} object representing the current session. By passing the same object to multiple
* calls of Director::test(), you can simulate a persisted session.
* @param string $httpMethod The HTTP method, such as GET or POST. It will default to POST if postVars is set, GET otherwise.
* Overwritten by $postVars['_method'] if present.
* @param Session $session The {@link Session} object representing the current session. By passing the same
* object to multiple calls of Director::test(), you can simulate a persisted session.
* @param string $httpMethod The HTTP method, such as GET or POST. It will default to POST if postVars is set,
* GET otherwise. Overwritten by $postVars['_method'] if present.
* @param string $body The HTTP body
* @param array $headers HTTP headers with key-value pairs
* @param array $cookies to populate $_COOKIE
@ -184,7 +192,9 @@ class Director implements TemplateGlobalProvider {
* @uses getControllerForURL() The rule-lookup logic is handled by this.
* @uses Controller::run() Controller::run() handles the page logic for a Director::direct() call.
*/
public static function test($url, $postVars = null, $session = null, $httpMethod = null, $body = null, $headers = null, $cookies = null, &$request = null) {
public static function test($url, $postVars = null, $session = null, $httpMethod = null, $body = null,
$headers = null, $cookies = null, &$request = null) {
// These are needed so that calling Director::test() doesnt muck with whoever is calling it.
// Really, it's some inappropriate coupling and should be resolved by making less use of statics
$oldStage = Versioned::current_stage();
@ -264,8 +274,11 @@ class Director implements TemplateGlobalProvider {
foreach($rules as $pattern => $controllerOptions) {
if(is_string($controllerOptions)) {
if(substr($controllerOptions,0,2) == '->') $controllerOptions = array('Redirect' => substr($controllerOptions,2));
else $controllerOptions = array('Controller' => $controllerOptions);
if(substr($controllerOptions,0,2) == '->') {
$controllerOptions = array('Redirect' => substr($controllerOptions,2));
} else {
$controllerOptions = array('Controller' => $controllerOptions);
}
}
if(($arguments = $request->match($pattern, true)) !== false) {
@ -359,7 +372,9 @@ class Director implements TemplateGlobalProvider {
public static function absoluteURL($url, $relativeToSiteBase = false) {
if(!isset($_SERVER['REQUEST_URI'])) return false;
if(strpos($url,'/') === false && !$relativeToSiteBase) $url = dirname($_SERVER['REQUEST_URI'] . 'x') . '/' . $url;
if(strpos($url,'/') === false && !$relativeToSiteBase) {
$url = dirname($_SERVER['REQUEST_URI'] . 'x') . '/' . $url;
}
if(substr($url,0,4) != "http") {
if($url[0] != "/") $url = Director::baseURL() . $url;
@ -373,7 +388,8 @@ class Director implements TemplateGlobalProvider {
/**
* Returns the part of the URL, 'http://www.mysite.com'.
*
* @return boolean|string The domain from the PHP environment. Returns FALSE is this environment variable isn't set.
* @return boolean|string The domain from the PHP environment. Returns FALSE is this environment variable isn't
* set.
*/
public static function protocolAndHost() {
if(self::$alternateBaseURL) {
@ -392,7 +408,8 @@ class Director implements TemplateGlobalProvider {
'your _ss_environment.php as instructed on the "sake" page of the doc.silverstripe.com wiki';
else $errorSuggestion = "";
user_error("Director::protocolAndHost() lacks sufficient information - HTTP_HOST not set.$errorSuggestion", E_USER_WARNING);
user_error("Director::protocolAndHost() lacks sufficient information - HTTP_HOST not set."
. $errorSuggestion, E_USER_WARNING);
return false;
}
@ -404,8 +421,11 @@ class Director implements TemplateGlobalProvider {
* @return String
*/
public static function protocol() {
if(isset($_SERVER['HTTP_X_FORWARDED_PROTOCOL']) && strtolower($_SERVER['HTTP_X_FORWARDED_PROTOCOL']) == 'https') return "https://";
return (isset($_SERVER['SSL']) || (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off')) ? 'https://' : 'http://';
if(isset($_SERVER['HTTP_X_FORWARDED_PROTOCOL'])&&strtolower($_SERVER['HTTP_X_FORWARDED_PROTOCOL'])=='https') {
return "https://";
}
return (isset($_SERVER['SSL']) || (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off'))
? 'https://' : 'http://';
}
/**
@ -423,7 +443,8 @@ class Director implements TemplateGlobalProvider {
/**
* Tests whether a redirection has been requested.
* @deprecated 2.5 Use Controller->redirectedTo() instead
* @return string If redirect() has been called, it will return the URL redirected to. Otherwise, it will return null;
* @return string If redirect() has been called, it will return the URL redirected to. Otherwise, it will
* return null;
*/
public static function redirected_to() {
Deprecation::notice('2.5', 'Use Controller->redirectedTo() instead.');
@ -717,7 +738,10 @@ class Director implements TemplateGlobalProvider {
$matched = true;
}
if($matched && (!isset($_SERVER['HTTPS']) || $_SERVER['HTTPS'] == 'off') && !(isset($_SERVER['HTTP_X_FORWARDED_PROTOCOL']) && strtolower($_SERVER['HTTP_X_FORWARDED_PROTOCOL']) == 'https')) {
if($matched && (!isset($_SERVER['HTTPS']) || $_SERVER['HTTPS'] == 'off')
&& !(isset($_SERVER['HTTP_X_FORWARDED_PROTOCOL'])
&& strtolower($_SERVER['HTTP_X_FORWARDED_PROTOCOL']) == 'https')) {
$destURL = str_replace('http:', 'https:', Director::absoluteURL($_SERVER['REQUEST_URI']));
// This coupling to SapphireTest is necessary to test the destination URL and to not interfere with tests
@ -725,7 +749,8 @@ class Director implements TemplateGlobalProvider {
return $destURL;
} else {
if(!headers_sent()) header("Location: $destURL");
die("<h1>Your browser is not accepting header redirects</h1><p>Please <a href=\"$destURL\">click here</a>");
die("<h1>Your browser is not accepting header redirects</h1>"
. "<p>Please <a href=\"$destURL\">click here</a>");
}
} else {
return false;
@ -737,10 +762,12 @@ class Director implements TemplateGlobalProvider {
*/
public static function forceWWW() {
if(!Director::isDev() && !Director::isTest() && strpos($_SERVER['HTTP_HOST'], 'www') !== 0) {
$destURL = str_replace(Director::protocol(), Director::protocol() . 'www.', Director::absoluteURL($_SERVER['REQUEST_URI']));
$destURL = str_replace(Director::protocol(), Director::protocol() . 'www.',
Director::absoluteURL($_SERVER['REQUEST_URI']));
header("Location: $destURL", true, 301);
die("<h1>Your browser is not accepting header redirects</h1><p>Please <a href=\"$destURL\">click here</a>");
die("<h1>Your browser is not accepting header redirects</h1>"
. "<p>Please <a href=\"$destURL\">click here</a>");
}
}
@ -783,31 +810,34 @@ class Director implements TemplateGlobalProvider {
* - test sites, such as the one you show the client before going live.
* - the live site itself.
*
* The behaviour of these environments often varies slightly. For example, development sites may have errors dumped to the screen,
* and order confirmation emails might be sent to the developer instead of the client.
* The behaviour of these environments often varies slightly. For example, development sites may have errors
* dumped to the screen, and order confirmation emails might be sent to the developer instead of the client.
*
* To help with this, SilverStripe supports the notion of an environment type. The environment type can be dev, test, or live.
* To help with this, SilverStripe supports the notion of an environment type. The environment type can be dev,
* test, or live.
*
* You can set it explicitly with Director::set_environment_tpye(). Or you can use {@link Director::set_dev_servers()} and {@link Director::set_test_servers()}
* to set it implicitly, based on the value of $_SERVER['HTTP_HOST']. If the HTTP_HOST value is one of the servers listed, then
* the environment type will be test or dev. Otherwise, the environment type will be live.
* You can set it explicitly with Director::set_environment_tpye(). Or you can use
* {@link Director::set_dev_servers()} and {@link Director::set_test_servers()} to set it implicitly, based on the
* value of $_SERVER['HTTP_HOST']. If the HTTP_HOST value is one of the servers listed, then the environment type
* will be test or dev. Otherwise, the environment type will be live.
*
* Dev mode can also be forced by putting ?isDev=1 in your URL, which will ask you to log in and then push the site into dev
* mode for the remainder of the session. Putting ?isDev=0 onto the URL can turn it back.
* Dev mode can also be forced by putting ?isDev=1 in your URL, which will ask you to log in and then push the
* site into dev mode for the remainder of the session. Putting ?isDev=0 onto the URL can turn it back.
*
* Test mode can also be forced by putting ?isTest=1 in your URL, which will ask you to log in and then push the site into test
* mode for the remainder of the session. Putting ?isTest=0 onto the URL can turn it back.
* Test mode can also be forced by putting ?isTest=1 in your URL, which will ask you to log in and then push the
* site into test mode for the remainder of the session. Putting ?isTest=0 onto the URL can turn it back.
*
* Generally speaking, these methods will be called from your _config.php file.
*
* Once the environment type is set, it can be checked with {@link Director::isDev()}, {@link Director::isTest()}, and
* {@link Director::isLive()}.
* Once the environment type is set, it can be checked with {@link Director::isDev()}, {@link Director::isTest()},
* and {@link Director::isLive()}.
*
* @param $et string The environment type: dev, test, or live.
*/
public static function set_environment_type($et) {
if($et != 'dev' && $et != 'test' && $et != 'live') {
user_error("Director::set_environment_type passed '$et'. It should be passed dev, test, or live", E_USER_WARNING);
user_error("Director::set_environment_type passed '$et'. It should be passed dev, test, or live",
E_USER_WARNING);
} else {
self::$environment_type = $et;
}

View File

@ -41,7 +41,8 @@ class HTTP {
*/
public static function absoluteURLs($html) {
$html = str_replace('$CurrentPageURL', $_SERVER['REQUEST_URI'], $html);
return HTTP::urlRewriter($html, '(substr($URL,0,1) == "/") ? ( Director::protocolAndHost() . $URL ) : ( (preg_match("/^[A-Za-z]+:/", $URL)) ? $URL : Director::absoluteBaseURL() . $URL )' );
return HTTP::urlRewriter($html, '(substr($URL,0,1) == "/") ? ( Director::protocolAndHost() . $URL ) :'
. ' ( (preg_match("/^[A-Za-z]+:/", $URL)) ? $URL : Director::absoluteBaseURL() . $URL )' );
}
/*
@ -252,8 +253,8 @@ class HTTP {
/**
* Add the appropriate caching headers to the response, including If-Modified-Since / 304 handling.
*
* @param SS_HTTPResponse The SS_HTTPResponse object to augment. Omitted the argument or passing a string is deprecated; in these
* cases, the headers are output directly.
* @param SS_HTTPResponse The SS_HTTPResponse object to augment. Omitted the argument or passing a string is
* deprecated; in these cases, the headers are output directly.
*/
public static function add_cache_headers($body = null) {
// Validate argument
@ -266,16 +267,21 @@ class HTTP {
// below.
if(Director::isDev()) return;
// The headers have been sent and we don't have an SS_HTTPResponse object to attach things to; no point in us trying.
// The headers have been sent and we don't have an SS_HTTPResponse object to attach things to; no point in
// us trying.
if(headers_sent() && !$body) return;
// Popuplate $responseHeaders with all the headers that we want to build
$responseHeaders = array();
if(function_exists('apache_request_headers')) {
$requestHeaders = apache_request_headers();
if(isset($requestHeaders['X-Requested-With']) && $requestHeaders['X-Requested-With'] == 'XMLHttpRequest') self::$cache_age = 0;
if(isset($requestHeaders['X-Requested-With']) && $requestHeaders['X-Requested-With']=='XMLHttpRequest') {
self::$cache_age = 0;
}
// bdc: now we must check for DUMB IE6:
if(isset($requestHeaders['x-requested-with']) && $requestHeaders['x-requested-with'] == 'XMLHttpRequest') self::$cache_age = 0;
if(isset($requestHeaders['x-requested-with']) && $requestHeaders['x-requested-with']=='XMLHttpRequest') {
self::$cache_age = 0;
}
}
if(self::$cache_age > 0) {

View File

@ -1,15 +1,15 @@
<?php
/**
* Represents a HTTP-request, including a URL that is tokenised for parsing, and a request method (GET/POST/PUT/DELETE).
* This is used by {@link RequestHandler} objects to decide what to do.
* Represents a HTTP-request, including a URL that is tokenised for parsing, and a request method
* (GET/POST/PUT/DELETE). This is used by {@link RequestHandler} objects to decide what to do.
*
* The intention is that a single SS_HTTPRequest object can be passed from one object to another, each object calling
* match() to get the information that they need out of the URL. This is generally handled by
* {@link RequestHandler::handleRequest()}.
*
* @todo Accept X_HTTP_METHOD_OVERRIDE http header and $_REQUEST['_method'] to override request types (useful for webclients
* not supporting PUT and DELETE)
* @todo Accept X_HTTP_METHOD_OVERRIDE http header and $_REQUEST['_method'] to override request types (useful for
* webclients not supporting PUT and DELETE)
*
* @package framework
* @subpackage control
@ -398,7 +398,9 @@ class SS_HTTPRequest implements ArrayAccess {
if($varRequired && !isset($this->dirParts[$i])) return false;
$arguments[$varName] = isset($this->dirParts[$i]) ? $this->dirParts[$i] : null;
if($part == '$Controller' && (!ClassInfo::exists($arguments['Controller']) || !is_subclass_of($arguments['Controller'], 'Controller'))) {
if($part == '$Controller' && (!ClassInfo::exists($arguments['Controller'])
|| !is_subclass_of($arguments['Controller'], 'Controller'))) {
return false;
}
@ -570,7 +572,8 @@ class SS_HTTPRequest implements ArrayAccess {
* Returns all mimetypes from the HTTP "Accept" header
* as an array.
*
* @param boolean $includeQuality Don't strip away optional "quality indicators", e.g. "application/xml;q=0.9" (Default: false)
* @param boolean $includeQuality Don't strip away optional "quality indicators", e.g. "application/xml;q=0.9"
* (Default: false)
* @return array
*/
public function getAcceptMimetypes($includeQuality = false) {

View File

@ -215,7 +215,8 @@ class SS_HTTPResponse {
if(in_array($this->statusCode, self::$redirect_codes) && headers_sent($file, $line)) {
$url = $this->headers['Location'];
echo
"<p>Redirecting to <a href=\"$url\" title=\"Please click this link if your browser does not redirect you\">$url... (output started on $file, line $line)</a></p>
"<p>Redirecting to <a href=\"$url\" title=\"Click this link if your browser does not redirect you\">"
. "$url... (output started on $file, line $line)</a></p>
<meta http-equiv=\"refresh\" content=\"1; url=$url\" />
<script type=\"text/javascript\">setTimeout('window.location.href = \"$url\"', 50);</script>";
} else {

View File

@ -69,7 +69,9 @@ class PjaxResponseNegotiator {
} elseif ($fragmentStr = $request->getHeader('X-Pjax')) {
$fragments = explode(',', $fragmentStr);
} else {
if($request->isAjax()) throw new SS_HTTPResponse_Exception("Ajax requests to this URL require an X-Pjax header.", 400);
if($request->isAjax()) {
throw new SS_HTTPResponse_Exception("Ajax requests to this URL require an X-Pjax header.", 400);
}
$response->setBody(call_user_func($callbacks['default']));
return $response;
}

View File

@ -5,22 +5,27 @@
*
* Any RequestHandler object can be made responsible for handling its own segment of the URL namespace.
* The {@link Director} begins the URL parsing process; it will parse the beginning of the URL to identify which
* controller is being used. It will then call {@link handleRequest()} on that Controller, passing it the parameters that it
* parsed from the URL, and the {@link SS_HTTPRequest} that contains the remainder of the URL to be parsed.
* controller is being used. It will then call {@link handleRequest()} on that Controller, passing it the parameters
* that it parsed from the URL, and the {@link SS_HTTPRequest} that contains the remainder of the URL to be parsed.
*
* You can use ?debug_request=1 to view information about the different components and rule matches for a specific URL.
*
* In SilverStripe, URL parsing is distributed throughout the object graph. For example, suppose that we have a search form
* that contains a {@link TreeMultiSelectField} named "Groups". We want to use ajax to load segments of this tree as they are needed
* rather than downloading the tree right at the beginning. We could use this URL to get the tree segment that appears underneath
* In SilverStripe, URL parsing is distributed throughout the object graph. For example, suppose that we have a
* search form that contains a {@link TreeMultiSelectField} named "Groups". We want to use ajax to load segments of
* this tree as they are needed rather than downloading the tree right at the beginning. We could use this URL to get
* the tree segment that appears underneath
*
* Group #36: "admin/crm/SearchForm/field/Groups/treesegment/36"
* - Director will determine that admin/crm is controlled by a new ModelAdmin object, and pass control to that.
* Matching Director Rule: "admin/crm" => "ModelAdmin" (defined in mysite/_config.php)
* - ModelAdmin will determine that SearchForm is controlled by a Form object returned by $this->SearchForm(), and pass control to that.
* - ModelAdmin will determine that SearchForm is controlled by a Form object returned by $this->SearchForm(), and
* pass control to that.
* Matching $url_handlers: "$Action" => "$Action" (defined in RequestHandler class)
* - Form will determine that field/Groups is controlled by the Groups field, a TreeMultiselectField, and pass control to that.
* - Form will determine that field/Groups is controlled by the Groups field, a TreeMultiselectField, and pass
* control to that.
* Matching $url_handlers: 'field/$FieldName!' => 'handleField' (defined in Form class)
* - TreeMultiselectField will determine that treesegment/36 is handled by its treesegment() method. This method will return an HTML fragment that is output to the screen.
* - TreeMultiselectField will determine that treesegment/36 is handled by its treesegment() method. This method
* will return an HTML fragment that is output to the screen.
* Matching $url_handlers: "$Action/$ID" => "handleItem" (defined in TreeMultiSelectField class)
*
* {@link RequestHandler::handleRequest()} is where this behaviour is implemented.
@ -54,10 +59,11 @@ class RequestHandler extends ViewableData {
* The default URL handling rules. This specifies that the next component of the URL corresponds to a method to
* be called on this RequestHandlingData object.
*
* The keys of this array are parse rules. See {@link SS_HTTPRequest::match()} for a description of the rules available.
* The keys of this array are parse rules. See {@link SS_HTTPRequest::match()} for a description of the rules
* available.
*
* The values of the array are the method to be called if the rule matches. If this value starts with a '$', then the
* named parameter of the parsed URL wil be used to determine the method name.
* The values of the array are the method to be called if the rule matches. If this value starts with a '$', then
* the named parameter of the parsed URL wil be used to determine the method name.
*/
static $url_handlers = array(
'$Action' => '$Action',
@ -70,10 +76,14 @@ class RequestHandler extends ViewableData {
*
* <code>
* array(
* 'someaction', // someaction can be accessed by anyone, any time
* 'otheraction' => true, // So can otheraction
* 'restrictedaction' => 'ADMIN', // restrictedaction can only be people with ADMIN privilege
* 'complexaction' '->canComplexAction' // complexaction can only be accessed if $this->canComplexAction() returns true
* // someaction can be accessed by anyone, any time
* 'someaction',
* // So can otheraction
* 'otheraction' => true,
* // restrictedaction can only be people with ADMIN privilege
* 'restrictedaction' => 'ADMIN',
* // complexaction can only be accessed if $this->canComplexAction() returns true
* 'complexaction' '->canComplexAction'
* );
* </code>
*
@ -141,13 +151,16 @@ class RequestHandler extends ViewableData {
$urlHandlers = Config::inst()->get($handlerClass, 'url_handlers', Config::FIRST_SET);
if($urlHandlers) foreach($urlHandlers as $rule => $action) {
if(isset($_REQUEST['debug_request'])) Debug::message("Testing '$rule' with '" . $request->remaining() . "' on $this->class");
if(isset($_REQUEST['debug_request'])) {
Debug::message("Testing '$rule' with '" . $request->remaining() . "' on $this->class");
}
if($params = $request->match($rule, true)) {
// Backwards compatible setting of url parameters, please use SS_HTTPRequest->latestParam() instead
//Director::setUrlParams($request->latestParams());
if(isset($_REQUEST['debug_request'])) {
Debug::message("Rule '$rule' matched to action '$action' on $this->class. Latest request params: " . var_export($request->latestParams(), true));
Debug::message("Rule '$rule' matched to action '$action' on $this->class."
. " Latest request params: " . var_export($request->latestParams(), true));
}
// Actions can reference URL parameters, eg, '$Action/$ID/$OtherID' => '$Action',
@ -155,7 +168,9 @@ class RequestHandler extends ViewableData {
if($this->checkAccessAction($action)) {
if(!$action) {
if(isset($_REQUEST['debug_request'])) Debug::message("Action not set; using default action method name 'index'");
if(isset($_REQUEST['debug_request'])) {
Debug::message("Action not set; using default action method name 'index'");
}
$action = "index";
} else if(!is_string($action)) {
user_error("Non-string method name: " . var_export($action, true), E_USER_ERROR);
@ -163,14 +178,15 @@ class RequestHandler extends ViewableData {
try {
if(!$this->hasMethod($action)) {
return $this->httpError(404, "Action '$action' isn't available on class " . get_class($this) . ".");
return $this->httpError(404, "Action '$action' isn't available on class "
. get_class($this) . ".");
}
$result = $this->$action($request);
} catch(SS_HTTPResponse_Exception $responseException) {
$result = $responseException->getResponse();
}
} else {
return $this->httpError(403, "Action '$action' isn't allowed on class " . get_class($this) . ".");
return $this->httpError(403, "Action '$action' isn't allowed on class " . get_class($this));
}
if($result instanceof SS_HTTPResponse && $result->isError()) {
@ -178,11 +194,13 @@ class RequestHandler extends ViewableData {
return $result;
}
// If we return a RequestHandler, call handleRequest() on that, even if there is no more URL to parse.
// It might have its own handler. However, we only do this if we haven't just parsed an empty rule ourselves,
// to prevent infinite loops. Also prevent further handling of controller actions which return themselves
// to avoid infinite loops.
if($this !== $result && !$request->isEmptyPattern($rule) && is_object($result) && $result instanceof RequestHandler) {
// If we return a RequestHandler, call handleRequest() on that, even if there is no more URL to
// parse. It might have its own handler. However, we only do this if we haven't just parsed an
// empty rule ourselves, to prevent infinite loops. Also prevent further handling of controller
// actions which return themselves to avoid infinite loops.
if($this !== $result && !$request->isEmptyPattern($rule) && is_object($result)
&& $result instanceof RequestHandler) {
$returnValue = $result->handleRequest($request, $model);
// Array results can be used to handle
@ -256,7 +274,9 @@ class RequestHandler extends ViewableData {
if($isKey || $isValue || $isWildcard) return true;
}
if(!is_array($actions) || !$this->config()->get('allowed_actions', Config::UNINHERITED | Config::EXCLUDE_EXTRA_SOURCES)) {
if(!is_array($actions) || !$this->config()->get('allowed_actions',
Config::UNINHERITED | Config::EXCLUDE_EXTRA_SOURCES)) {
if($action != 'init' && $action != 'run' && method_exists($this, $action)) return true;
}
@ -300,9 +320,11 @@ class RequestHandler extends ViewableData {
// it should be allowed.
if($action == 'index' || empty($action)) return true;
if($allowedActions === null || !$this->config()->get('allowed_actions', Config::UNINHERITED | Config::EXCLUDE_EXTRA_SOURCES)) {
// If no allowed_actions are provided, then we should only let through actions that aren't handled by magic methods
// we test this by calling the unmagic method_exists.
if($allowedActions === null || !$this->config()->get('allowed_actions',
Config::UNINHERITED | Config::EXCLUDE_EXTRA_SOURCES)) {
// If no allowed_actions are provided, then we should only let through actions that aren't handled by
// magic methods we test this by calling the unmagic method_exists.
if(method_exists($this, $action)) {
// Disallow any methods which aren't defined on RequestHandler or subclasses
// (e.g. ViewableData->getSecurityID())

View File

@ -5,21 +5,24 @@
* The static methods are used to manipulate the currently active controller's session.
* The instance methods are used to manipulate a particular session. There can be more than one of these created.
*
* In order to support things like testing, the session is associated with a particular Controller. In normal usage, this is loaded from
* and saved to the regular PHP session, but for things like static-page-generation and unit-testing, you can create multiple Controllers,
* each with their own session.
* In order to support things like testing, the session is associated with a particular Controller. In normal usage,
* this is loaded from and saved to the regular PHP session, but for things like static-page-generation and
* unit-testing, you can create multiple Controllers, each with their own session.
*
* The instance object is basically just a way of manipulating a set of nested maps, and isn't specific to session data.
* The instance object is basically just a way of manipulating a set of nested maps, and isn't specific to session
* data.
*
* <b>Saving Data</b>
*
* You can write a value to a users session from your PHP code using the static function {@link Session::set()}. You can add this line in any function or file you wish to save the value.
* You can write a value to a users session from your PHP code using the static function {@link Session::set()}. You
* can add this line in any function or file you wish to save the value.
*
* <code>
* Session::set('MyValue', 6);
* </code>
*
* Saves the value of "6" to the MyValue session data. You can also save arrays or serialized objects in session (but note there may be size restrictions as to how much you can save)
* Saves the value of "6" to the MyValue session data. You can also save arrays or serialized objects in session (but
* note there may be size restrictions as to how much you can save)
*
* <code>
* // save a variable
@ -59,13 +62,15 @@
*
* <b>Clearing Data</b>
*
* Once you have accessed a value from the Session it doesn't automatically wipe the value from the Session, you have to specifically remove it. To clear a value you can either delete 1 session value by the name that you saved it
* Once you have accessed a value from the Session it doesn't automatically wipe the value from the Session, you have
* to specifically remove it. To clear a value you can either delete 1 session value by the name that you saved it
*
* <code>
* Session::clear('MyValue'); // myvalue is no longer 6.
* </code>
*
* Or you can clear every single value in the session at once. Note SilverStripe stores some of its own session data including form and page comment information. None of this is vital but clear_all will clear everything.
* Or you can clear every single value in the session at once. Note SilverStripe stores some of its own session data
* including form and page comment information. None of this is vital but clear_all will clear everything.
*
* <code>
* Session::clear_all();

View File

@ -62,7 +62,8 @@
* // but has a lower priority, it is NOT overridden
* 'class' => 'ClassName', // the name of the PHP class
* 'src' => '/path/to/file' // the location of the class
* 'type' => 'singleton|prototype' // if you want prototype object generation, set it as the type
* 'type' => 'singleton|prototype' // if you want prototype object generation, set it as the
* // type
* // By default, singleton is assumed
*
* 'construct' => array( // properties to set at construction

View File

@ -13,55 +13,64 @@
* If the value is an array, each value in the array may also be one of those three types
*
* A property can have a value specified in multiple locations, each of which have a hardcoded or explicit priority.
* We combine all these values together into a "composite" value using rules that depend on the priority order of the locations
* to give the final value, using these rules:
* We combine all these values together into a "composite" value using rules that depend on the priority order of the
* locations to give the final value, using these rules:
*
* - If the value is an array, each array is added to the _beginning_ of the composite array in ascending priority order.
* If a higher priority item has a non-integer key which is the same as a lower priority item, the value of those items
* is merged using these same rules, and the result of the merge is located in the same location the higher priority item
* would be if there was no key clash. Other than in this key-clash situation, within the particular array, order is preserved.
* - If the value is an array, each array is added to the _beginning_ of the composite array in ascending priority
* order. If a higher priority item has a non-integer key which is the same as a lower priority item, the value of
* those items is merged using these same rules, and the result of the merge is located in the same location the
* higher priority item would be if there was no key clash. Other than in this key-clash situation, within the
* particular array, order is preserved.
*
* - If the value is not an array, the highest priority value is used without any attempt to merge
*
* It is an error to have mixed types of the same named property in different locations (but an error will not necessarily
* be raised due to optimisations in the lookup code)
* It is an error to have mixed types of the same named property in different locations (but an error will not
* necessarily be raised due to optimisations in the lookup code)
*
* The exception to this is "false-ish" values - empty arrays, empty strings, etc. When merging a non-false-ish value with a
* false-ish value, the result will be the non-false-ish value regardless of priority. When merging two false-sh values
* the result will be the higher priority false-ish value.
* The exception to this is "false-ish" values - empty arrays, empty strings, etc. When merging a non-false-ish value
* with a false-ish value, the result will be the non-false-ish value regardless of priority. When merging two
* false-ish values the result will be the higher priority false-ish value.
*
* The locations that configuration values are taken from in highest -> lowest priority order
*
* - Any values set via a call to Config#update
*
* - The configuration values taken from the YAML files in _config directories (internally sorted in before / after order, where
* the item that is latest is highest priority)
* - The configuration values taken from the YAML files in _config directories (internally sorted in before / after
* order, where the item that is latest is highest priority)
*
* - Any static set on an "additional static source" class (such as an extension) named the same as the name of the property
* - Any static set on an "additional static source" class (such as an extension) named the same as the name of the
* property
*
* - Any static set on the class named the same as the name of the property
*
* - The composite configuration value of the parent class of this class
*
* At some of these levels you can also set masks. These remove values from the composite value at their priority point rather than add.
* They are much simpler. They consist of a list of key / value pairs. When applied against the current composite value
* At some of these levels you can also set masks. These remove values from the composite value at their priority
* point rather than add. They are much simpler. They consist of a list of key / value pairs. When applied against the
* current composite value:
*
* - If the composite value is a sequential array, any member of that array that matches any value in the mask is removed
* - If the composite value is a sequential array, any member of that array that matches any value in the mask is
* removed
*
* - If the composite value is an associative array, any member of that array that matches both the key and value of any pair in the mask is removed
* - If the composite value is an associative array, any member of that array that matches both the key and value of
* any pair in the mask is removed
*
* - If the composite value is not an array, if that value matches any value in the mask it is removed
*
*
*/
class Config {
/** @var Object - A marker instance for the "anything" singleton value. Don't access directly, even in-class, always use self::anything() */
/**
* [A marker instance for the "anything" singleton value. Don't access directly, even in-class, always use
* self::anything()
* @var Object
*/
static private $_anything = null;
/**
* Get a marker class instance that is used to do a "remove anything with this key" by adding $key => Config::anything() to the suppress array
* todo: Does this follow the SS coding conventions? Config::get_anything_marker_instance() is a lot less elegant.
* Get a marker class instance that is used to do a "remove anything with this key" by adding
* $key => Config::anything() to the suppress array
*
* @todo Does this follow the SS coding conventions? Config::get_anything_marker_instance() is a lot less elegant.
* @return Object
*/
static public function anything() {
@ -71,25 +80,41 @@ class Config {
// -- Source options bitmask --
/** @const source options bitmask value - merge all parent configuration in as lowest priority */
/**
* source options bitmask value - merge all parent configuration in as lowest priority
* @const
*/
const INHERITED = 0;
/** @const source options bitmask value - only get configuration set for this specific class, not any of it's parents */
/**
* source options bitmask value - only get configuration set for this specific class, not any of it's parents
* @const
*/
const UNINHERITED = 1;
/** @const source options bitmask value - inherit, but stop on the first class that actually provides a value (event an empty value) */
/**
* source options bitmask value - inherit, but stop on the first class that actually provides a value (event an
* empty value)
* @const
*/
const FIRST_SET = 2;
/** @const source options bitmask value - do not use additional statics sources (such as extension) */
const EXCLUDE_EXTRA_SOURCES = 4;
// -- get_value_type response enum --
/** @const Return flag for get_value_type indicating value is a scalar (or really just not-an-array, at least ATM)*/
/**
* Return flag for get_value_type indicating value is a scalar (or really just not-an-array, at least ATM)
* @const
*/
const ISNT_ARRAY = 1;
/** @const Return flag for get_value_type indicating value is an array */
/**
* Return flag for get_value_type indicating value is an array
* @const
*/
const IS_ARRAY = 2;
/**
* Get whether the value is an array or not. Used to be more complicated, but still nice sugar to have an enum to compare
* and not just a true/false value
* Get whether the value is an array or not. Used to be more complicated, but still nice sugar to have an enum
* to compare and not just a true/false value
* @param $val any - The value
* @return int - One of ISNT_ARRAY or IS_ARRAY
*/
@ -103,7 +128,8 @@ class Config {
* @throws UnexpectedValueException
*/
static protected function type_mismatch() {
throw new UnexpectedValueException('Type mismatch in configuration. All values for a particular property must contain the same type (or no value at all).');
throw new UnexpectedValueException('Type mismatch in configuration. All values for a particular property must'
. ' contain the same type (or no value at all).');
}
/* @todo If we can, replace next static & static methods with DI once that's in */
@ -320,11 +346,14 @@ class Config {
* Get the config value associated for a given class and property
*
* This merges all current sources and overrides together to give final value
* todo: Currently this is done every time. This function is an inner loop function, so we really need to be caching heavily here.
* todo: Currently this is done every time. This function is an inner loop function, so we really need to be
* caching heavily here.
*
* @param $class string - The name of the class to get the value for
* @param $name string - The property to get the value for
* @param int $sourceOptions - Bitmask which can be set to some combintain of Config::UNINHERITED, Config::FIRST_SET, and Config::EXCLUDE_EXTENSIONS.
* @param int $sourceOptions Bitmask which can be set to some combintain of Config::UNINHERITED,
* Config::FIRST_SET, and Config::EXCLUDE_EXTENSIONS.
*
* Config::UNINHERITED does not include parent classes when merging configuration fragments
* Config::FIRST_SET stops inheriting once the first class that sets a value (even an empty value) is encoutered
* Config::EXCLUDE_EXTRA_SOURCES does not include any additional static sources (such as extensions)
@ -333,9 +362,12 @@ class Config {
* Setting both Config::UNINHERITED and Config::FIRST_SET behaves the same as just Config::UNINHERITED
*
* should the parent classes value be merged in as the lowest priority source?
* @param null $result array|scalar - Reference to a variable to put the result in. Also returned, so this can be left as null safely. If you do pass a value, it will be treated as the highest priority value in the result chain
* @param null $suppress array - Internal use when called by child classes. Array of mask pairs to filter value by
* @return array|scalar - The value of the config item, or null if no value set. Could be an associative array, sequential array or scalar depending on value (see class docblock)
* @param $result array|scalar Reference to a variable to put the result in. Also returned, so this can be left
* as null safely. If you do pass a value, it will be treated as the highest priority
* value in the result chain
* @param $suppress array Internal use when called by child classes. Array of mask pairs to filter value by
* @return array|scalar The value of the config item, or null if no value set. Could be an associative array,
* sequential array or scalar depending on value (see class docblock)
*/
public function get($class, $name, $sourceOptions = 0, &$result = null, $suppress = null) {
// If result is already not something to merge into, just return it
@ -351,7 +383,9 @@ class Config {
}
if (isset($this->suppresses[$k][$class][$name])) {
$suppress = $suppress ? array_merge($suppress, $this->suppresses[$k][$class][$name]) : $this->suppresses[$k][$class][$name];
$suppress = $suppress
? array_merge($suppress, $this->suppresses[$k][$class][$name])
: $this->suppresses[$k][$class][$name];
}
}
@ -384,7 +418,8 @@ class Config {
}
// Finally, merge in the values from the parent class
if (($sourceOptions & self::UNINHERITED) != self::UNINHERITED && (($sourceOptions & self::FIRST_SET) != self::FIRST_SET || $result === null)) {
if (($sourceOptions & self::UNINHERITED) != self::UNINHERITED
&& (($sourceOptions & self::FIRST_SET) != self::FIRST_SET || $result === null)) {
$parent = get_parent_class($class);
if ($parent) $this->get($parent, $name, $sourceOptions, $result, $suppress);
}
@ -399,8 +434,8 @@ class Config {
/**
* Update a configuration value
*
* Configuration is modify only. The value passed is merged into the existing configuration. If you want to replace the
* current value, you'll need to call remove first.
* Configuration is modify only. The value passed is merged into the existing configuration. If you want to
* replace the current value, you'll need to call remove first.
*
* @param $class string - The class to update a configuration value for
* @param $name string - The configuration property name to update
@ -429,12 +464,13 @@ class Config {
*
* Works like this:
* - Check the current override array, and remove any values that match the arguments provided
* - Keeps track of the arguments passed to this method, and in get filters everything _except_ the current override array to
* exclude any match
* - Keeps track of the arguments passed to this method, and in get filters everything _except_ the current
* override array to exclude any match
*
* This way we can re-set anything removed by a call to this function by calling set. Because the current override
* array is only filtered immediately on calling this remove method, that value will then be exposed. However, every
* other source is filtered on request, so no amount of changes to parent's configuration etc can override a remove call.
* array is only filtered immediately on calling this remove method, that value will then be exposed. However,
* every other source is filtered on request, so no amount of changes to parent's configuration etc can override a
* remove call.
*
* @param $class string - The class to remove a configuration value from
* @param $name string - The configuration name
@ -462,7 +498,9 @@ class Config {
$this->overrides[0][$class][$name] = self::filter_array_by_suppress_array($value, array($suppress));
}
else {
if (self::check_value_contained_in_suppress_array($value, array($suppress))) unset($this->overrides[0][$class][$name]);
if (self::check_value_contained_in_suppress_array($value, array($suppress))) {
unset($this->overrides[0][$class][$name]);
}
}
}

View File

@ -244,7 +244,8 @@ class Convert {
// Expand hyperlinks
if(!$preserveLinks && !$config['PreserveLinks']) {
$data = preg_replace('/<a[^>]*href\s*=\s*"([^"]*)">(.*?)<\/a>/ie', "Convert::html2raw('\\2').'[\\1]'", $data);
$data = preg_replace('/<a[^>]*href\s*=\s*"([^"]*)">(.*?)<\/a>/ie', "Convert::html2raw('\\2').'[\\1]'",
$data);
$data = preg_replace('/<a[^>]*href\s*=\s*([^ ]*)>(.*?)<\/a>/ie', "Convert::html2raw('\\2').'[\\1]'", $data);
}

View File

@ -29,7 +29,8 @@
*
* @todo This file currently contains a lot of bits and pieces, and its various responsibilities should probably be
* moved into different subsystems.
* @todo A lot of this stuff is very order-independent; for example, the require_once calls have to happen after the defines.'
* @todo A lot of this stuff is very order-independent; for example, the require_once calls have to happen after the
* defines.'
* This could be decoupled.
* @package framework
* @subpackage core
@ -45,7 +46,12 @@ error_reporting(E_ALL | E_STRICT);
/**
* Include _ss_environment.php files
*/
$envFiles = array('_ss_environment.php', '../_ss_environment.php', '../../_ss_environment.php', '../../../_ss_environment.php');
$envFiles = array(
'_ss_environment.php',
'../_ss_environment.php',
'../../_ss_environment.php',
'../../../_ss_environment.php');
foreach($envFiles as $envFile) {
if(@file_exists($envFile)) {
define('SS_ENVIRONMENT_FILE', $envFile);
@ -324,7 +330,8 @@ function getClassFile($className) {
function singleton($className) {
if($className == "Config") user_error("Don't pass Config to singleton()", E_USER_ERROR);
if(!isset($className)) user_error("singleton() Called without a class", E_USER_ERROR);
if(!is_string($className)) user_error("singleton() passed bad class_name: " . var_export($className,true), E_USER_ERROR);
if(!is_string($className)) user_error("singleton() passed bad class_name: " . var_export($className,true),
E_USER_ERROR);
return Injector::inst()->get($className);
}

View File

@ -5,10 +5,16 @@
* in config yaml fragments
*/
class SS_DAG implements IteratorAggregate {
/** @var array|null - The nodes/vertices in the graph. Should be a numeric sequence of items (no string keys, no gaps). */
/**
* The nodes/vertices in the graph. Should be a numeric sequence of items (no string keys, no gaps).
* @var array|null
*/
protected $data;
/** @var array - The edges in the graph, in $to_idx => [$from_idx1, $from_idx2, ...] format */
/**
* The edges in the graph, in $to_idx => [$from_idx1, $from_idx2, ...] format
* @var array
*/
protected $dag;
public function __construct($data = null) {
@ -28,11 +34,13 @@ class SS_DAG implements IteratorAggregate {
}
/**
* Add an edge from one vertex to another
* @param $from integer|any - The index in $data of the node/vertex, or the node/vertex itself, that the edge goes from
* @param $to integer|any - The index in $data of the node/vertex, or the node/vertex itself, that the edge goes to
* Add an edge from one vertex to another.
*
* When passing actual nodes (as opposed to indexes), uses array_search with strict = true to find
*
* @param $from integer|any The index in $data of the node/vertex, or the node/vertex itself, that the edge
* goes from
* @param $to integer|any - The index in $data of the node/vertex, or the node/vertex itself, that the edge goes to
*/
public function addedge($from, $to) {
$i = is_numeric($from) ? $from : array_search($from, $this->data, true);

View File

@ -749,7 +749,8 @@ class Diff
if($tagStack[$listName]) $rechunked[$listName][sizeof($rechunked[$listName])-1] .= ' ' . $item;
else $rechunked[$listName][] = $item;
if($lookForTag && !$tagStack[$listName] && isset($item[0]) && $item[0] == "<" && substr($item,0,2) != "</") {
if($lookForTag && !$tagStack[$listName] && isset($item[0]) && $item[0] == "<"
&& substr($item,0,2) != "</") {
$tagStack[$listName] = 1;
} else if($tagStack[$listName]) {
if(substr($item,0,2) == "</") $tagStack[$listName]--;

View File

@ -176,9 +176,14 @@ abstract class Object {
case T_CONSTANT_ENCAPSED_STRING:
$argString = $token[1];
switch($argString[0]) {
case '"': $argString = stripcslashes(substr($argString,1,-1)); break;
case "'": $argString = str_replace(array("\\\\", "\\'"),array("\\", "'"), substr($argString,1,-1)); break;
default: throw new Exception("Bad T_CONSTANT_ENCAPSED_STRING arg $argString");
case '"':
$argString = stripcslashes(substr($argString,1,-1));
break;
case "'":
$argString = str_replace(array("\\\\", "\\'"),array("\\", "'"), substr($argString,1,-1));
break;
default:
throw new Exception("Bad T_CONSTANT_ENCAPSED_STRING arg $argString");
}
$bucket[] = $argString;
break;
@ -271,14 +276,15 @@ abstract class Object {
}
/**
* Get the value of a static property of a class, even in that property is declared protected (but not private), without any inheritance,
* merging or parent lookup if it doesn't exist on the given class.
* Get the value of a static property of a class, even in that property is declared protected (but not private),
* without any inheritance, merging or parent lookup if it doesn't exist on the given class.
*
* @static
* @param $class - The class to get the static from
* @param $name - The property to get from the class
* @param null $default - The value to return if property doesn't exist on class
* @return any - The value of the static property $name on class $class, or $default if that property is not defined
* @return any - The value of the static property $name on class $class, or $default if that property is not
* defined
*/
public static function static_lookup($class, $name, $default = null) {
if (is_subclass_of($class, 'Object')) {
@ -288,7 +294,8 @@ abstract class Object {
}
return $default;
} else {
// TODO: This gets set once, then not updated, so any changes to statics after this is called the first time for any class won't be exposed
// TODO: This gets set once, then not updated, so any changes to statics after this is called the first
// time for any class won't be exposed
static $static_properties = array();
if (!isset($static_properties[$class])) {
@ -307,7 +314,9 @@ abstract class Object {
$static_properties[$parent] = $reflection->getStaticProperties();
}
if (!isset($static_properties[$parent][$name]) || $static_properties[$parent][$name] !== $value) return $value;
if (!isset($static_properties[$parent][$name]) || $static_properties[$parent][$name] !== $value) {
return $value;
}
}
}
@ -321,8 +330,8 @@ abstract class Object {
* up the extra static var chain until it reaches the top, or it reaches a replacement static.
*
* If any extra values are discovered, they are then merged with the default PHP static values, or in some cases
* completely replace the default PHP static when you set $replace = true, and do not define extra data on any child
* classes
* completely replace the default PHP static when you set $replace = true, and do not define extra data on any
* child classes
*
* @param string $class
* @param string $name the property name
@ -447,11 +456,13 @@ abstract class Object {
}
$extensionClass = $matches[1];
if(!class_exists($extensionClass)) {
user_error(sprintf('Object::add_extension() - Can\'t find extension class for "%s"', $extensionClass), E_USER_ERROR);
user_error(sprintf('Object::add_extension() - Can\'t find extension class for "%s"', $extensionClass),
E_USER_ERROR);
}
if(!is_subclass_of($extensionClass, 'Extension')) {
user_error(sprintf('Object::add_extension() - Extension "%s" is not a subclass of Extension', $extensionClass), E_USER_ERROR);
user_error(sprintf('Object::add_extension() - Extension "%s" is not a subclass of Extension',
$extensionClass), E_USER_ERROR);
}
// unset some caches
@ -529,7 +540,7 @@ abstract class Object {
}
}
// -----------------------------------------------------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------
private static $extension_sources = array();
@ -560,7 +571,8 @@ abstract class Object {
$sources[] = $extensionClass;
if(!ClassInfo::has_method_from($extensionClass, 'add_to_class', 'Extension')) {
Deprecation::notice('3.1.0', "add_to_class deprecated on $extensionClass. Use get_extra_config instead");
Deprecation::notice('3.1.0',
"add_to_class deprecated on $extensionClass. Use get_extra_config instead");
}
call_user_func(array($extensionClass, 'add_to_class'), $class, $extensionClass, $extensionArgs);
@ -582,7 +594,8 @@ abstract class Object {
foreach(ClassInfo::ancestry(get_called_class()) as $class) {
if(in_array($class, self::$unextendable_classes)) continue;
$extensions = Config::inst()->get($class, 'extensions', Config::UNINHERITED | Config::EXCLUDE_EXTRA_SOURCES);
$extensions = Config::inst()->get($class, 'extensions',
Config::UNINHERITED | Config::EXCLUDE_EXTRA_SOURCES);
if($extensions) foreach($extensions as $extension) {
$instance = self::create_from_string($extension);
@ -638,8 +651,8 @@ abstract class Object {
);
} else {
throw new Exception (
"Object->__call(): $this->class cannot pass control to $config[property]($config[index])." .
' Perhaps this object was mistakenly destroyed?'
"Object->__call(): $this->class cannot pass control to $config[property]($config[index])."
. ' Perhaps this object was mistakenly destroyed?'
);
}
@ -652,7 +665,8 @@ abstract class Object {
default :
throw new Exception (
"Object->__call(): extra method $method is invalid on $this->class:" . var_export($config, true)
"Object->__call(): extra method $method is invalid on $this->class:"
. var_export($config, true)
);
}
} else {
@ -662,7 +676,7 @@ abstract class Object {
}
}
// -----------------------------------------------------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------
/**
* Return TRUE if a method exists on this object
@ -780,7 +794,8 @@ abstract class Object {
*
* @param string $method the method name
* @param string $code the PHP code - arguments will be in an array called $args, while you can access this object
* by using $obj. Note that you cannot call protected methods, as the method is actually an external function
* by using $obj. Note that you cannot call protected methods, as the method is actually an external
* function
*/
protected function createMethod($method, $code) {
self::$extra_methods[$this->class][strtolower($method)] = array (
@ -788,7 +803,7 @@ abstract class Object {
);
}
// -----------------------------------------------------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------
/**
* @see Object::get_static()
@ -818,7 +833,7 @@ abstract class Object {
Deprecation::notice('2.4', 'Use a custom static on your object instead.');
}
// -----------------------------------------------------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------
/**
* Return true if this object "exists" i.e. has a sensible value
@ -856,7 +871,7 @@ abstract class Object {
return $this->class;
}
// -----------------------------------------------------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------
/**
* Calls a method if available on both this object and all applied {@link Extensions}, and then attempts to merge
@ -945,7 +960,7 @@ abstract class Object {
return $this->extension_instances;
}
// -----------------------------------------------------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------
/**
* Cache the results of an instance method in this object to a file, or if it is already cache return the cached

View File

@ -9,20 +9,36 @@
*/
class SS_ConfigManifest {
/** @var array All the values needed to be collected to determine the correct combination of fragements for the current environment. */
/**
* All the values needed to be collected to determine the correct combination of fragements for
* the current environment.
* @var array
*/
protected $variantKeySpec = array();
/** @var array All the _config.php files. Need to be included every request & can't be cached. Not variant specific. */
/**
* All the _config.php files. Need to be included every request & can't be cached. Not variant specific.
* @var array
*/
protected $phpConfigSources = array();
/** @var array All the _config/*.yml fragments pre-parsed and sorted in ascending include order. Not variant specific. */
/**
* All the _config/*.yml fragments pre-parsed and sorted in ascending include order. Not variant specific.
* @var array
*/
protected $yamlConfigFragments = array();
/** @var array The calculated config from _config/*.yml, sorted, filtered and merged. Variant specific. */
/**
* The calculated config from _config/*.yml, sorted, filtered and merged. Variant specific.
* @var array
*/
public $yamlConfig = array();
/** @var array A side-effect of collecting the _config fragments is the calculation of all module directories, since the definition
* of a module is "a directory that contains either a _config.php file or a _config directory */
/**
* A side-effect of collecting the _config fragments is the calculation of all module directories, since
* the definition of a module is "a directory that contains either a _config.php file or a _config directory
* @var array
*/
public $modules = array();
/** Adds a path as a module */
@ -63,7 +79,8 @@ class SS_ConfigManifest {
$this->variantKeySpec = $this->cache->load('variant_key_spec');
// Try getting the pre-filtered & merged config for this variant
if (!($this->yamlConfig = $this->cache->load('yaml_config_'.$this->variantKey()))) {
// Otherwise, if we do have the yaml config fragments (and we should since we have a variant key spec) work out the config for this variant
// Otherwise, if we do have the yaml config fragments (and we should since we have a variant key spec)
// work out the config for this variant
if ($this->yamlConfigFragments = $this->cache->load('yaml_config_fragments')) {
$this->buildYamlConfigVariant();
}
@ -88,8 +105,9 @@ class SS_ConfigManifest {
}
/**
* Returns the string that uniquely identifies this variant. The variant is the combination of classes, modules, environment,
* environment variables and constants that selects which yaml fragments actually make it into the configuration because of "only"
* Returns the string that uniquely identifies this variant. The variant is the combination of classes, modules,
* environment, environment variables and constants that selects which yaml fragments actually make it into the
* configuration because of "only"
* and "except" rules.
*
* @return string
@ -97,7 +115,9 @@ class SS_ConfigManifest {
public function variantKey() {
$key = $this->variantKeySpec; // Copy to fill in actual values
if (isset($key['environment'])) $key['environment'] = Director::isDev() ? 'dev' : (Director::isTest() ? 'test' : 'live');
if (isset($key['environment'])) {
$key['environment'] = Director::isDev() ? 'dev' : (Director::isTest() ? 'test' : 'live');
}
if (isset($key['envvars'])) foreach ($key['envvars'] as $variable => $foo) {
$key['envvars'][$variable] = isset($_ENV[$variable]) ? $_ENV[$variable] : null;
@ -111,9 +131,9 @@ class SS_ConfigManifest {
}
/**
* Completely regenerates the manifest file. Scans through finding all php _config.php and yaml _config/*.ya?ml files,
* parses the yaml files into fragments, sorts them and figures out what values need to be checked to pick the
* correct variant.
* Completely regenerates the manifest file. Scans through finding all php _config.php and yaml _config/*.ya?ml
* files,parses the yaml files into fragments, sorts them and figures out what values need to be checked to pick
* the correct variant.
*
* Does _not_ build the actual variant
*
@ -177,7 +197,8 @@ class SS_ConfigManifest {
$this->addModule(dirname(dirname($pathname)));
// Use the Zend copy of this script to prevent class conflicts when RailsYaml is included
require_once 'thirdparty/zend_translate_railsyaml/library/Translate/Adapter/thirdparty/sfYaml/lib/sfYamlParser.php';
require_once 'thirdparty/zend_translate_railsyaml/library/Translate/Adapter/thirdparty/sfYaml/lib/'
. 'sfYamlParser.php';
$parser = new sfYamlParser();
// The base header
@ -203,7 +224,9 @@ class SS_ConfigManifest {
// Otherwise it's a set of header/document pairs
else {
// If we got an odd number of parts the config file doesn't have a header for every document
if (count($parts) % 2 != 0) user_error("Configuration file $basename does not have an equal number of headers and config blocks");
if (count($parts) % 2 != 0) {
user_error("Configuration file $basename does not have an equal number of headers and config blocks");
}
// Step through each pair
for ($i = 0; $i < count($parts); $i+=2) {
@ -223,7 +246,8 @@ class SS_ConfigManifest {
// For each, parse out into module/file#name, and set any missing to "*"
$header[$order] = array();
foreach($orderparts as $part) {
preg_match('! (?P<module>\*|\w+)? (\/ (?P<file>\*|\w+))? (\# (?P<fragment>\*|\w+))? !x', $part, $match);
preg_match('! (?P<module>\*|\w+)? (\/ (?P<file>\*|\w+))? (\# (?P<fragment>\*|\w+))? !x',
$part, $match);
$header[$order][] = array(
'module' => isset($match['module']) && $match['module'] ? $match['module'] : '*',
@ -279,7 +303,8 @@ class SS_ConfigManifest {
$res .= '<dl>';
foreach ($e->dag as $node) {
$res .= "<dt>{$node['from']['module']}/{$node['from']['file']}#{$node['from']['name']} marked to come after</dt><dd><ul>";
$res .= "<dt>{$node['from']['module']}/{$node['from']['file']}#{$node['from']['name']}"
. " marked to come after</dt><dd><ul>";
foreach ($node['to'] as $to) {
$res .= "<li>{$to['module']}/{$to['file']}#{$to['name']}</li>";
}
@ -290,14 +315,15 @@ class SS_ConfigManifest {
echo $res;
}
user_error('Based on their before & after rules two fragments both need to be before/after each other', E_USER_ERROR);
user_error('Based on their before & after rules two fragments both need to be before/after each other',
E_USER_ERROR);
}
}
/**
* Return a string "after", "before" or "undefined" depending on whether the YAML fragment array element passed as $a should
* be positioned after, before, or either compared to the YAML fragment array element passed as $b
* Return a string "after", "before" or "undefined" depending on whether the YAML fragment array element passed
* as $a should be positioned after, before, or either compared to the YAML fragment array element passed as $b
*
* @param $a Array - a YAML config fragment as loaded by addYAMLConfigFile
* @param $b Array - a YAML config fragment as loaded by addYAMLConfigFile
@ -340,7 +366,9 @@ class SS_ConfigManifest {
if ($partmatches === true) $level += 1;
}
if ($matchlevel[$rulename] === false || $level > $matchlevel[$rulename]) $matchlevel[$rulename] = $level;
if ($matchlevel[$rulename] === false || $level > $matchlevel[$rulename]) {
$matchlevel[$rulename] = $level;
}
}
}
@ -356,8 +384,8 @@ class SS_ConfigManifest {
}
/**
* This function filters the loaded yaml fragments, removing any that can't ever have their "only" and "except" rules
* match
* This function filters the loaded yaml fragments, removing any that can't ever have their "only" and "except"
* rules match.
*
* Some tests in "only" and "except" rules need to be checked per request, but some are manifest based -
* these are invariant over requests and only need checking on manifest rebuild. So we can prefilter these before
@ -380,7 +408,8 @@ class SS_ConfigManifest {
* Returns false if the prefilterable parts of the rule aren't met, and true if they are
*
* @param $rules array - A hash of rules as allowed in the only or except portion of a config fragment header
* @return bool - True if the rules are met, false if not. (Note that depending on whether we were passed an only or an except rule,
* @return bool - True if the rules are met, false if not. (Note that depending on whether we were passed an
* only or an except rule,
* which values means accept or reject a fragment
*/
public function matchesPrefilterVariantRules($rules) {
@ -403,7 +432,8 @@ class SS_ConfigManifest {
}
/**
* Builds the variant key spec - the list of values that need to be build to give a key that uniquely identifies this variant.
* Builds the variant key spec - the list of values that need to be build to give a key that uniquely identifies
* this variant.
*/
public function buildVariantKeySpec() {
$this->variantKeySpec = array();
@ -422,8 +452,8 @@ class SS_ConfigManifest {
switch (strtolower($k)) {
case 'classexists':
case 'moduleexists':
// Classes and modules are a special case - we can pre-filter on config regenerate because we already know
// if the class or module exists
// Classes and modules are a special case - we can pre-filter on config regenerate because we
// already know if the class or module exists
break;
case 'environment':

View File

@ -1,8 +1,9 @@
<?php
/**
* A tokenised regular expression is a parser, similar to a regular expression, that acts on tokens rather than characters.
* This is a crucial component of the ManifestBuilder.
* A tokenised regular expression is a parser, similar to a regular expression, that acts on tokens rather than
* characters. This is a crucial component of the ManifestBuilder.
*
* @package framework
* @subpackage core
*/
@ -28,7 +29,8 @@ class TokenisedRegularExpression {
}
}
$startKeys = array_keys($tokenTypes, is_array($this->expression[0]) ? $this->expression[0][0] : $this->expression[0]);
$startKeys = array_keys($tokenTypes, is_array($this->expression[0])
? $this->expression[0][0] : $this->expression[0]);
$allMatches = array();
foreach($startKeys as $startKey) {
@ -48,7 +50,9 @@ class TokenisedRegularExpression {
if($expectation == $tokens[$tokenPos][0]) {
if(isset($expressionRule['save_to'])) {
// Append to an array
if(substr($expressionRule['save_to'],-2) == '[]') $matches[substr($expressionRule['save_to'],0,-2)][] = $tokens[$tokenPos][1];
if(substr($expressionRule['save_to'],-2) == '[]') {
$matches[substr($expressionRule['save_to'],0,-2)][] = $tokens[$tokenPos][1];
}
// Regular variable setting
else $matches[$expressionRule['save_to']] = $tokens[$tokenPos][1];
}
@ -62,14 +66,16 @@ class TokenisedRegularExpression {
return true;
// This step is optional
} else if(isset($expressionRule['optional']) && $this->matchFrom($tokenPos, $expressionPos+1, $tokens, $matches)) {
} else if(isset($expressionRule['optional'])
&& $this->matchFrom($tokenPos, $expressionPos+1, $tokens, $matches)) {
return true;
// Process jumps
} else if(isset($expressionRule['can_jump_to'])) {
if(is_array($expressionRule['can_jump_to'])) foreach($expressionRule['can_jump_to'] as $canJumpTo) {
// can_jump_to & optional both set
if(isset($expressionRule['optional']) && $this->matchFrom($tokenPos, $canJumpTo, $tokens, $matches)) {
if(isset($expressionRule['optional'])
&& $this->matchFrom($tokenPos, $canJumpTo, $tokens, $matches)) {
return true;
}
// can_jump_to set (optional may or may not be set)
@ -79,7 +85,8 @@ class TokenisedRegularExpression {
} else {
// can_jump_to & optional both set
if(isset($expressionRule['optional']) && $this->matchFrom($tokenPos, $expressionRule['can_jump_to'], $tokens, $matches)) {
if(isset($expressionRule['optional'])
&& $this->matchFrom($tokenPos, $expressionRule['can_jump_to'], $tokens, $matches)) {
return true;
}
// can_jump_to set (optional may or may not be set)
@ -90,7 +97,9 @@ class TokenisedRegularExpression {
}
} else if(isset($expressionRule['optional'])) {
if(isset($this->expression[$expressionPos+1])) return $this->matchFrom($tokenPos, $expressionPos+1, $tokens, $matches);
if(isset($this->expression[$expressionPos+1])) {
return $this->matchFrom($tokenPos, $expressionPos+1, $tokens, $matches);
}
else return true;
}

View File

@ -90,7 +90,9 @@ class SS_Backtrace {
$match = false;
if(@$bt[$i]['class']) {
foreach(self::$ignore_function_args as $fnSpec) {
if(is_array($fnSpec) && $bt[$i]['class'] == $fnSpec[0] && $bt[$i]['function'] == $fnSpec[1]) $match = true;
if(is_array($fnSpec) && $bt[$i]['class'] == $fnSpec[0] && $bt[$i]['function'] == $fnSpec[1]) {
$match = true;
}
}
} else {
if(in_array($bt[$i]['function'], self::$ignore_function_args)) $match = true;
@ -121,7 +123,8 @@ class SS_Backtrace {
}
/**
* Return the full function name. If showArgs is set to true, a string representation of the arguments will be shown
* Return the full function name. If showArgs is set to true, a string representation of the arguments will be
* shown
*
* @param Object $item
* @param boolean $showArg

View File

@ -51,7 +51,8 @@ class CSSContentParser extends Object {
$this->simpleXML = @simplexml_load_string($tidy, 'SimpleXMLElement', LIBXML_NOWARNING);
if(!$this->simpleXML) {
throw new Exception('CSSContentParser::__construct(): Could not parse content. Please check the PHP extension tidy is installed.');
throw new Exception('CSSContentParser::__construct(): Could not parse content.'
. ' Please check the PHP extension tidy is installed.');
}
parent::__construct();

View File

@ -20,8 +20,10 @@ class SS_Cli extends Object {
/**
* Return text encoded for CLI output, optionally coloured
* @param string $fgColour The foreground colour - black, red, green, yellow, blue, magenta, cyan, white. Null is default.
* @param string $bgColour The foreground colour - black, red, green, yellow, blue, magenta, cyan, white. Null is default.
* @param string $fgColour The foreground colour - black, red, green, yellow, blue, magenta, cyan, white.
* Null is default.
* @param string $bgColour The foreground colour - black, red, green, yellow, blue, magenta, cyan, white.
* Null is default.
* @param string $bold A boolean variable - bold or not.
*/
public static function text($text, $fgColour = null, $bgColour = null, $bold = false) {
@ -41,8 +43,10 @@ class SS_Cli extends Object {
/**
* Send control codes for changing text to the given colour
* @param string $fgColour The foreground colour - black, red, green, yellow, blue, magenta, cyan, white. Null is default.
* @param string $bgColour The foreground colour - black, red, green, yellow, blue, magenta, cyan, white. Null is default.
* @param string $fgColour The foreground colour - black, red, green, yellow, blue, magenta, cyan, white.
* Null is default.
* @param string $bgColour The foreground colour - black, red, green, yellow, blue, magenta, cyan, white.
* Null is default.
* @param string $bold A boolean variable - bold or not.
*/
public static function start_colour($fgColour = null, $bgColour = null, $bold = false) {

View File

@ -39,12 +39,15 @@ class CliTestReporter extends SapphireTestReporter {
echo SS_Cli::text(" AT LEAST ONE FAILURE ", "black", "red");
}
echo sprintf("\n\n%d tests run: %s, %s, and %s\n", $testCount, SS_Cli::text("$passCount passes"), SS_Cli::text("$failCount failures"), SS_Cli::text("$incompleteCount incomplete"));
echo sprintf("\n\n%d tests run: %s, %s, and %s\n", $testCount, SS_Cli::text("$passCount passes"),
SS_Cli::text("$failCount failures"), SS_Cli::text("$incompleteCount incomplete"));
echo "Maximum memory usage: " . number_format(memory_get_peak_usage()/(1024*1024), 1) . "M\n\n";
// Use sake dev/tests/all --showslow to show slow tests
if((isset($_GET['args']) && is_array($_GET['args']) && in_array('--showslow', $_GET['args'])) || isset($_GET['showslow'])) {
if((isset($_GET['args']) && is_array($_GET['args']) && in_array('--showslow', $_GET['args']))
|| isset($_GET['showslow'])) {
$avgSpeed = round(array_sum($this->testSpeeds) / count($this->testSpeeds), 3);
echo "Slow tests (more than the average $avgSpeed seconds):\n";
@ -83,8 +86,7 @@ class CliTestReporter extends SapphireTestReporter {
$filteredTrace = array();
$ignoredClasses = array('TestRunner');
foreach($test['trace'] as $item) {
if(
isset($item['file'])
if(isset($item['file'])
&& strpos($item['file'], 'PHPUnit/Framework') === false
&& (!isset($item['class']) || !in_array($item['class'], $ignoredClasses))) {
@ -92,7 +94,9 @@ class CliTestReporter extends SapphireTestReporter {
}
if(isset($item['class']) && isset($item['function']) && $item['class'] == 'PHPUnit_Framework_TestSuite'
&& $item['function'] == 'run') break;
&& $item['function'] == 'run') {
break;
}
}

View File

@ -90,7 +90,8 @@ class CsvBulkLoader extends BulkLoader {
} elseif(strpos($fieldName, '.') !== false) {
// we have a relation column with dot notation
list($relationName,$columnName) = explode('.', $fieldName);
$relationObj = $obj->getComponent($relationName); // always gives us an component (either empty or existing)
// always gives us an component (either empty or existing)
$relationObj = $obj->getComponent($relationName);
$relationObj->write();
$obj->{"{$relationName}ID"} = $relationObj->ID;
$obj->write();
@ -152,7 +153,8 @@ class CsvBulkLoader extends BulkLoader {
$SQL_fieldName = Convert::raw2sql($duplicateCheck);
if(!isset($record[$fieldName])) {
return false;
//user_error("CsvBulkLoader:processRecord: Couldn't find duplicate identifier '{$fieldName}' in columns", E_USER_ERROR);
//user_error("CsvBulkLoader:processRecord: Couldn't find duplicate identifier '{$fieldName}'
//in columns", E_USER_ERROR);
}
$SQL_fieldValue = $record[$fieldName];
$existingRecord = DataObject::get_one($this->objectClass, "\"$SQL_fieldName\" = '{$SQL_fieldValue}'");
@ -163,7 +165,8 @@ class CsvBulkLoader extends BulkLoader {
} elseif($SNG_objectClass->hasMethod($duplicateCheck['callback'])) {
$existingRecord = $SNG_objectClass->{$duplicateCheck['callback']}($record[$fieldName], $record);
} else {
user_error("CsvBulkLoader::processRecord(): {$duplicateCheck['callback']} not found on importer or object class.", E_USER_ERROR);
user_error("CsvBulkLoader::processRecord():"
. " {$duplicateCheck['callback']} not found on importer or object class.", E_USER_ERROR);
}
if($existingRecord) return $existingRecord;

View File

@ -67,9 +67,12 @@ class Debug {
if($showHeader) {
$caller = Debug::caller();
if(Director::is_ajax() || Director::is_cli())
echo "Debug ($caller[class]$caller[type]$caller[function]() in " . basename($caller['file']) . ":$caller[line])\n";
echo "Debug ($caller[class]$caller[type]$caller[function]() in " . basename($caller['file'])
. ":$caller[line])\n";
else
echo "<div style=\"background-color: white; text-align: left;\">\n<hr>\n<h3>Debug <span style=\"font-size: 65%\">($caller[class]$caller[type]$caller[function]() \nin " . basename($caller['file']) . ":$caller[line])</span>\n</h3>\n";
echo "<div style=\"background-color: white; text-align: left;\">\n<hr>\n"
. "<h3>Debug <span style=\"font-size: 65%\">($caller[class]$caller[type]$caller[function]()"
. " \nin " . basename($caller['file']) . ":$caller[line])</span>\n</h3>\n";
}
echo Debug::text($val);
@ -88,7 +91,8 @@ class Debug {
public static function endshow($val) {
if(!Director::isLive()) {
$caller = Debug::caller();
echo "<hr>\n<h3>Debug \n<span style=\"font-size: 65%\">($caller[class]$caller[type]$caller[function]() \nin " . basename($caller['file']) . ":$caller[line])</span>\n</h3>\n";
echo "<hr>\n<h3>Debug \n<span style=\"font-size: 65%\">($caller[class]$caller[type]$caller[function]()"
. " \nin " . basename($caller['file']) . ":$caller[line])</span>\n</h3>\n";
echo Debug::text($val);
die();
}
@ -141,7 +145,8 @@ class Debug {
$val = '(bool) ' . $val;
} else {
if(!Director::is_cli() && !Director::is_ajax()) {
$val = "<pre style=\"font-family: Courier new\">" . htmlentities($val, ENT_COMPAT, 'UTF-8') . "</pre>\n";
$val = "<pre style=\"font-family: Courier new\">" . htmlentities($val, ENT_COMPAT, 'UTF-8')
. "</pre>\n";
}
}
@ -310,8 +315,8 @@ class Debug {
* @uses ErrorPage
*
* @param int $statusCode HTTP Status Code (Default: 500)
* @param string $friendlyErrorMessage User-focused error message. Should not contain code pointers or "tech-speak".
* Used in the HTTP Header and ajax responses.
* @param string $friendlyErrorMessage User-focused error message. Should not contain code pointers
* or "tech-speak". Used in the HTTP Header and ajax responses.
* @param string $friendlyErrorDetail Detailed user-focused message. Is just used if no {@link ErrorPage} is found
* for this specific status code.
* @return string HTML error message for non-ajax requests, plaintext for ajax-request.
@ -461,8 +466,11 @@ class Debug {
* @param string $errorType "warning" or "error"
* @return boolean
*/
public static function emailError($emailAddress, $errno, $errstr, $errfile, $errline, $errcontext, $errorType = "Error") {
Deprecation::notice('2.5', 'Use SS_Log instead. See the class documentation in SS_Log.php for more information.');
public static function emailError($emailAddress, $errno, $errstr, $errfile, $errline, $errcontext,
$errorType = "Error") {
Deprecation::notice('2.5',
'Use SS_Log instead. See the class documentation in SS_Log.php for more information.');
$priority = ($errorType == 'Error') ? SS_Log::ERR : SS_Log::WARN;
$writer = new SS_LogEmailWriter($emailAddress);
SS_Log::add_writer($writer, $priority);
@ -490,7 +498,8 @@ class Debug {
* @deprecated 2.5 See SS_Log on setting up error file logging
*/
protected static function log_error_if_necessary($errno, $errstr, $errfile, $errline, $errcontext, $errtype) {
Deprecation::notice('2.5', 'Use SS_Log instead. See the class documentation in SS_Log.php for more information.');
Deprecation::notice('2.5',
'Use SS_Log instead. See the class documentation in SS_Log.php for more information.');
$priority = ($errtype == 'Error') ? SS_Log::ERR : SS_Log::WARN;
$writer = new SS_LogFileWriter('../' . self::$log_errors_to);
SS_Log::add_writer($writer, $priority);
@ -636,9 +645,10 @@ class Debug {
}
// This basically does the same as
// Security::permissionFailure(null, "You need to login with developer access to make use of debugging tools.");
// Security::permissionFailure(null, "You need to login with developer access to make use of debugging tools.")
// We have to do this because of how early this method is called in execution.
$_SESSION['Security']['Message']['message'] = "You need to login with developer access to make use of debugging tools.";
$_SESSION['Security']['Message']['message']
= "You need to login with developer access to make use of debugging tools.";
$_SESSION['Security']['Message']['type'] = 'warning';
$_SESSION['BackURL'] = $_SERVER['REQUEST_URI'];
header($_SERVER['SERVER_PROTOCOL'] . " 302 Found");

View File

@ -67,7 +67,8 @@ class DebugView extends Object {
*/
public function Breadcrumbs() {
$basePath = str_replace(Director::protocolAndHost(), '', Director::absoluteBaseURL());
$relPath = parse_url(substr($_SERVER['REQUEST_URI'], strlen($basePath), strlen($_SERVER['REQUEST_URI'])), PHP_URL_PATH);
$relPath = parse_url(substr($_SERVER['REQUEST_URI'], strlen($basePath), strlen($_SERVER['REQUEST_URI'])),
PHP_URL_PATH);
$parts = explode('/', $relPath);
$base = Director::absoluteBaseURL();
$pathPart = "";

View File

@ -76,7 +76,8 @@ class Deprecation {
}
/**
* Given a backtrace, get the module name from the caller two removed (the caller of the method that called #notice)
* Given a backtrace, get the module name from the caller two removed (the caller of the method that called
* #notice)
*
* @static
* @param $backtrace array - a backtrace as returned from debug_backtrace
@ -139,7 +140,9 @@ class Deprecation {
if(self::$module_version_overrides) {
$module = self::get_calling_module_from_trace($backtrace = debug_backtrace(0));
if(isset(self::$module_version_overrides[$module])) $checkVersion = self::$module_version_overrides[$module];
if(isset(self::$module_version_overrides[$module])) {
$checkVersion = self::$module_version_overrides[$module];
}
}
// Check the version against the notice version

View File

@ -79,7 +79,8 @@ class DevelopmentAdmin extends Controller {
"buildcache" => "Rebuild the static cache, if you're using StaticPublisher",
"tests" => "See a list of unit tests to run",
"tests/all" => "Run all tests",
"tests/startsession" => "Start a test session in your browser (gives you a temporary database with default content)",
"tests/startsession" => "Start a test session in your browser"
. " (gives you a temporary database with default content)",
"tests/endsession" => "Ends a test session",
"jstests" => "See a list of JavaScript tests to run",
"jstests/all" => "Run all JavaScript tests",
@ -99,7 +100,8 @@ class DevelopmentAdmin extends Controller {
echo '<div class="options"><ul>';
$evenOdd = "odd";
foreach($actions as $action => $description) {
echo "<li class=\"$evenOdd\"><a href=\"{$base}dev/$action\"><b>/dev/$action:</b> $description</a></li>\n";
echo "<li class=\"$evenOdd\"><a href=\"{$base}dev/$action\"><b>/dev/$action:</b>"
. " $description</a></li>\n";
$evenOdd = ($evenOdd == "odd") ? "even" : "odd";
}

View File

@ -44,8 +44,8 @@ class FunctionalTest extends SapphireTest {
/**
* If this is true, then 30x Location headers will be automatically followed.
* If not, then you will have to manaully call $this->mainSession->followRedirection() to follow them. However, this will let you inspect
* the intermediary headers
* If not, then you will have to manaully call $this->mainSession->followRedirection() to follow them.
* However, this will let you inspect the intermediary headers
*/
protected $autoFollowRedirection = true;
@ -71,7 +71,8 @@ class FunctionalTest extends SapphireTest {
$this->useDraftSite();
}
// Unprotect the site, tests are running with the assumption it's off. They will enable it on a case-by-case basis.
// Unprotect the site, tests are running with the assumption it's off. They will enable it on a case-by-case
// basis.
BasicAuth::protect_entire_site(false);
SecurityToken::disable();
@ -91,7 +92,9 @@ class FunctionalTest extends SapphireTest {
public function get($url, $session = null, $headers = null, $cookies = null) {
$this->cssParser = null;
$response = $this->mainSession->get($url, $session, $headers, $cookies);
if($this->autoFollowRedirection && is_object($response) && $response->getHeader('Location')) $response = $this->mainSession->followRedirection();
if($this->autoFollowRedirection && is_object($response) && $response->getHeader('Location')) {
$response = $this->mainSession->followRedirection();
}
return $response;
}
@ -102,7 +105,9 @@ class FunctionalTest extends SapphireTest {
public function post($url, $data, $headers = null, $session = null, $body = null, $cookies = null) {
$this->cssParser = null;
$response = $this->mainSession->post($url, $data, $headers, $session, $body, $cookies);
if($this->autoFollowRedirection && is_object($response) && $response->getHeader('Location')) $response = $this->mainSession->followRedirection();
if($this->autoFollowRedirection && is_object($response) && $response->getHeader('Location')) {
$response = $this->mainSession->followRedirection();
}
return $response;
}
@ -128,7 +133,9 @@ class FunctionalTest extends SapphireTest {
public function submitForm($formID, $button = null, $data = array()) {
$this->cssParser = null;
$response = $this->mainSession->submitForm($formID, $button, $data);
if($this->autoFollowRedirection && is_object($response) && $response->getHeader('Location')) $response = $this->mainSession->followRedirection();
if($this->autoFollowRedirection && is_object($response) && $response->getHeader('Location')) {
$response = $this->mainSession->followRedirection();
}
return $response;
}
@ -186,7 +193,8 @@ class FunctionalTest extends SapphireTest {
foreach($expectedMatches as $match) {
if(!isset($actuals[$match])) {
throw new PHPUnit_Framework_AssertionFailedError(
"Failed asserting the CSS selector '$selector' has a partial match to the expected elements:\n'" . implode("'\n'", $expectedMatches) . "'\n\n"
"Failed asserting the CSS selector '$selector' has a partial match to the expected elements:\n'"
. implode("'\n'", $expectedMatches) . "'\n\n"
. "Instead the following elements were found:\n'" . implode("'\n'", array_keys($actuals)) . "'"
);
return false;
@ -218,7 +226,8 @@ class FunctionalTest extends SapphireTest {
if($expectedMatches != $actuals) {
throw new PHPUnit_Framework_AssertionFailedError(
"Failed asserting the CSS selector '$selector' has an exact match to the expected elements:\n'" . implode("'\n'", $expectedMatches) . "'\n\n"
"Failed asserting the CSS selector '$selector' has an exact match to the expected elements:\n'"
. implode("'\n'", $expectedMatches) . "'\n\n"
. "Instead the following elements were found:\n'" . implode("'\n'", $actuals) . "'"
);
return false;
@ -250,7 +259,8 @@ class FunctionalTest extends SapphireTest {
foreach($expectedMatches as $match) {
if(!isset($actuals[$match])) {
throw new PHPUnit_Framework_AssertionFailedError(
"Failed asserting the CSS selector '$selector' has a partial match to the expected elements:\n'" . implode("'\n'", $expectedMatches) . "'\n\n"
"Failed asserting the CSS selector '$selector' has a partial match to the expected elements:\n'"
. implode("'\n'", $expectedMatches) . "'\n\n"
. "Instead the following elements were found:\n'" . implode("'\n'", array_keys($actuals)) . "'"
);
return false;
@ -280,7 +290,8 @@ class FunctionalTest extends SapphireTest {
if($expectedMatches != $actuals) {
throw new PHPUnit_Framework_AssertionFailedError(
"Failed asserting the CSS selector '$selector' has an exact match to the expected elements:\n'" . implode("'\n'", $expectedMatches) . "'\n\n"
"Failed asserting the CSS selector '$selector' has an exact match to the expected elements:\n'"
. implode("'\n'", $expectedMatches) . "'\n\n"
. "Instead the following elements were found:\n'" . implode("'\n'", $actuals) . "'"
);
}
@ -300,7 +311,8 @@ class FunctionalTest extends SapphireTest {
/**
* Use the draft (stage) site for testing.
* This is helpful if you're not testing publication functionality and don't want "stage management" cluttering your test.
* This is helpful if you're not testing publication functionality and don't want "stage management" cluttering
* your test.
*
* @param bool toggle the use of the draft site
*/

View File

@ -111,7 +111,8 @@ class JSTestRunner extends Controller {
} else {
$allTests = $this->getAllTestFiles();
if(!array_key_exists($test, $allTests)) {
user_error("TestRunner::only(): Invalid TestCase '$className', cannot find matching class", E_USER_ERROR);
user_error("TestRunner::only(): Invalid TestCase '$className', cannot find matching class",
E_USER_ERROR);
}
$this->runTests(array($test));

View File

@ -38,7 +38,8 @@ class SS_LogErrorEmailFormatter implements Zend_Log_Formatter_Interface {
$data = '';
$data .= '<style type="text/css">html, body, table {font-family: sans-serif; font-size: 12px;}</style>';
$data .= "<div style=\"border: 5px $colour solid;\">\n";
$data .= "<p style=\"color: white; background-color: $colour; margin: 0\">[$errorType] $errstr<br />$errfile:$errline\n<br />\n<br />\n</p>\n";
$data .= "<p style=\"color: white; background-color: $colour; margin: 0\">"
. "[$errorType] $errstr<br />$errfile:$errline\n<br />\n<br />\n</p>\n";
// Get a backtrace, filtering out debug method calls
$data .= SS_Backtrace::backtrace(true, false, array(

View File

@ -4,7 +4,8 @@
*
* <b>Creating Migration Tasks</b>
*
* To create your own migration task all you need to do is define your own subclass of MigrationTask and define the following functions
* To create your own migration task all you need to do is define your own subclass of MigrationTask and define the
* following functions
*
* <i>mysite/code/MyMigrationTask.php</i>
*

View File

@ -71,8 +71,10 @@ class Profiler {
public static function show($showTrace = false) {
if(!self::$inst) self::$inst = new Profiler(true,true);
echo "<div style=\"position: absolute; z-index: 100000; top: 20px; left: 20px; background-color: white; padding: 20px; border: 1px #AAA solid; height: 80%; overflow: auto;\">";
echo "<p><a href=\"#\" onclick=\"this.parentNode.parentNode.style.display = 'none'; return false;\">(Click to close)</a></p>";
echo "<div style=\"position: absolute; z-index: 100000; top: 20px; left: 20px; background-color: white;"
. " padding: 20px; border: 1px #AAA solid; height: 80%; overflow: auto;\">";
echo "<p><a href=\"#\" onclick=\"this.parentNode.parentNode.style.display = 'none'; return false;\">"
. "(Click to close)</a></p>";
self::$inst->printTimers();
if($showTrace) self::$inst->printTrace();
echo "</div>";

View File

@ -36,7 +36,10 @@ class SapphireREPL extends Controller {
}
public function index() {
if(!Director::is_cli()) return "The SilverStripe Interactive Command-line doesn't work in a web browser. Use 'sake interactive' from the command-line to run.";
if(!Director::is_cli()) {
return "The SilverStripe Interactive Command-line doesn't work in a web browser."
. " Use 'sake interactive' from the command-line to run.";
}
/* Try using PHP_Shell if it exists */
@ -56,7 +59,8 @@ class SapphireREPL extends Controller {
if ( $command == 'help' || $command == '?' ) {
print "help or ? to exit\n" ;
print "quit or \q to exit\n" ;
print "install PHP_Shell for a more advanced interface with auto-completion and readline support\n\n" ;
print "install PHP_Shell for a more advanced interface with"
. " auto-completion and readline support\n\n" ;
continue ;
}
@ -76,7 +80,9 @@ class SapphireREPL extends Controller {
}
catch( Exception $__repl_exception ) {
echo SS_Cli::start_colour("red");
printf( '%s (code: %d) got thrown'.PHP_EOL, get_class($__repl_exception), $__repl_exception->getCode() );
printf( '%s (code: %d) got thrown'.PHP_EOL,
get_class($__repl_exception),
$__repl_exception->getCode() );
print $__repl_exception;
echo "\n";
}

View File

@ -3,7 +3,8 @@ require_once 'TestRunner.php';
/**
* Test case class for the Sapphire framework.
* Sapphire unit testing is based on PHPUnit, but provides a number of hooks into our data model that make it easier to work with.
* Sapphire unit testing is based on PHPUnit, but provides a number of hooks into our data model that make it easier
* to work with.
*
* @package framework
* @subpackage testing
@ -199,7 +200,9 @@ class SapphireTest extends PHPUnit_Framework_TestCase {
// Set up fixture
if($fixtureFile || $this->usesDatabase || !self::using_temp_db()) {
if(substr(DB::getConn()->currentDatabase(), 0, strlen($prefix) + 5) != strtolower(sprintf('%stmpdb', $prefix))) {
if(substr(DB::getConn()->currentDatabase(), 0, strlen($prefix) + 5)
!= strtolower(sprintf('%stmpdb', $prefix))) {
//echo "Re-creating temp database... ";
self::create_temp_db();
//echo "done.\n";
@ -223,7 +226,9 @@ class SapphireTest extends PHPUnit_Framework_TestCase {
foreach($fixtureFiles as $fixtureFilePath) {
// Support fixture paths relative to the test class, rather than relative to webroot
// String checking is faster than file_exists() calls.
$isRelativeToFile = (strpos('/', $fixtureFilePath) === false || preg_match('/^\.\./', $fixtureFilePath));
$isRelativeToFile = (strpos('/', $fixtureFilePath) === false
|| preg_match('/^\.\./', $fixtureFilePath));
if($isRelativeToFile) {
$resolvedPath = realpath($pathForClass . '/' . $fixtureFilePath);
if($resolvedPath) $fixtureFilePath = $resolvedPath;
@ -345,7 +350,8 @@ class SapphireTest extends PHPUnit_Framework_TestCase {
*/
protected function idFromFixture($className, $identifier) {
if(!$this->fixtures) {
user_error("You've called idFromFixture() but you haven't specified static \$fixture_file.\n", E_USER_WARNING);
user_error("You've called idFromFixture() but you haven't specified static \$fixture_file.\n",
E_USER_WARNING);
return;
}
@ -374,7 +380,8 @@ class SapphireTest extends PHPUnit_Framework_TestCase {
*/
protected function allFixtureIDs($className) {
if(!$this->fixtures) {
user_error("You've called allFixtureIDs() but you haven't specified static \$fixture_file.\n", E_USER_WARNING);
user_error("You've called allFixtureIDs() but you haven't specified static \$fixture_file.\n",
E_USER_WARNING);
return;
}
@ -393,7 +400,8 @@ class SapphireTest extends PHPUnit_Framework_TestCase {
*/
protected function objFromFixture($className, $identifier) {
if(!$this->fixtures) {
user_error("You've called objFromFixture() but you haven't specified static \$fixture_file.\n", E_USER_WARNING);
user_error("You've called objFromFixture() but you haven't specified static \$fixture_file.\n",
E_USER_WARNING);
return;
}
@ -514,7 +522,8 @@ class SapphireTest extends PHPUnit_Framework_TestCase {
* @param $from
* @param $subject
* @param $content
* @return An array containing the keys: 'type','to','from','subject','content', 'plainContent','attachedFiles','customHeaders','htmlContent',inlineImages'
* @return array Contains keys: 'type', 'to', 'from', 'subject','content', 'plainContent', 'attachedFiles',
* 'customHeaders', 'htmlContent', 'inlineImages'
*/
public function findEmail($to, $from = null, $subject = null, $content = null) {
return $this->mailer->findEmail($to, $from, $subject, $content);
@ -527,7 +536,8 @@ class SapphireTest extends PHPUnit_Framework_TestCase {
* @param $from
* @param $subject
* @param $content
* @return An array containing the keys: 'type','to','from','subject','content', 'plainContent','attachedFiles','customHeaders','htmlContent',inlineImages'
* @return array Contains the keys: 'type', 'to', 'from', 'subject', 'content', 'plainContent', 'attachedFiles',
* 'customHeaders', 'htmlContent', inlineImages'
*/
public function assertEmailSent($to, $from = null, $subject = null, $content = null) {
// To do - this needs to be turned into a "real" PHPUnit ass
@ -700,7 +710,8 @@ class SapphireTest extends PHPUnit_Framework_TestCase {
public static function using_temp_db() {
$dbConn = DB::getConn();
$prefix = defined('SS_DATABASE_PREFIX') ? SS_DATABASE_PREFIX : 'ss_';
return $dbConn && (substr($dbConn->currentDatabase(), 0, strlen($prefix) + 5) == strtolower(sprintf('%stmpdb', $prefix)));
return $dbConn && (substr($dbConn->currentDatabase(), 0, strlen($prefix) + 5)
== strtolower(sprintf('%stmpdb', $prefix)));
}
public static function kill_temp_db() {
@ -732,7 +743,8 @@ class SapphireTest extends PHPUnit_Framework_TestCase {
// Some DataExtensions keep a static cache of information that needs to
// be reset whenever the database is cleaned out
foreach(array_merge(ClassInfo::subclassesFor('DataExtension'), ClassInfo::subclassesFor('DataObject')) as $class) {
$classes = array_merge(ClassInfo::subclassesFor('DataExtension'), ClassInfo::subclassesFor('DataObject'));
foreach($classes as $class) {
$toCall = array($class, 'on_db_reset');
if(is_callable($toCall)) call_user_func($toCall);
}

View File

@ -141,13 +141,20 @@ class SapphireTestReporter implements PHPUnit_Framework_TestListener {
if($test instanceof PHPUnit_Framework_TestCase) {
$this->endCurrentTest();
$this->currentTest = array(
'name' => preg_replace('(\(.*\))', '', $test->toString()), // the name of the test (without the suite name)
'timeElapsed' => 0, // execution time of the test
'status' => TEST_SUCCESS, // status of the test execution
'message' => '', // user message of test result
'exception' => NULL, // original caught exception thrown by test upon failure/error
'trace' => NULL, // Stacktrace used for exception handling
'uid' => md5(microtime()) // a unique ID for this test (used for identification purposes in results)
// the name of the test (without the suite name)
'name' => preg_replace('(\(.*\))', '', $test->toString()),
// execution time of the test
'timeElapsed' => 0,
// status of the test execution
'status' => TEST_SUCCESS,
// user message of test result
'message' => '',
// original caught exception thrown by test upon failure/error
'exception' => NULL,
// Stacktrace used for exception handling
'trace' => NULL,
// a unique ID for this test (used for identification purposes in results)
'uid' => md5(microtime())
);
if($this->hasTimer) $this->timer->start();
}
@ -381,7 +388,8 @@ class SapphireTestReporter implements PHPUnit_Framework_TestListener {
}
$result = ($failCount || $errorCount) ? 'fail' : 'pass';
echo "<div class=\"status $result\">";
echo "<h2><span>$testCount</span> tests run: <span>$passCount</span> passes, <span>$failCount</span> failures, and <span>$incompleteCount</span> incomplete with <span>$errorCount</span> errors</h2>";
echo "<h2><span>$testCount</span> tests run: <span>$passCount</span> passes, <span>$failCount</span> failures,"
. " and <span>$incompleteCount</span> incomplete with <span>$errorCount</span> errors</h2>";
echo "</div>";
}

View File

@ -88,7 +88,10 @@ class TaskRunner extends Controller {
if($taskClasses) foreach($taskClasses as $class) {
if(!singleton($class)->isEnabled()) continue;
$desc = (Director::is_cli()) ? Convert::html2raw(singleton($class)->getDescription()) : singleton($class)->getDescription();
$desc = (Director::is_cli())
? Convert::html2raw(singleton($class)->getDescription())
: singleton($class)->getDescription();
$availableTasks[] = array(
'class' => $class,
'title' => singleton($class)->getTitle(),

View File

@ -37,14 +37,16 @@ class TeamCityListener implements PHPUnit_Framework_TestListener {
$class = get_class($test);
$message = $this->escape("Exception: {$e->getMessage()}");
$trace = $this->escape($e->getTraceAsString());
echo "##teamcity[testFailed type='exception' name='{$class}.{$test->getName()}' message='$message' details='$trace']\n";
echo "##teamcity[testFailed type='exception' name='{$class}.{$test->getName()}' message='$message'"
. " details='$trace']\n";
}
public function addFailure(PHPUnit_Framework_Test $test, PHPUnit_Framework_AssertionFailedError $e, $time) {
$class = get_class($test);
$message = $this->escape($e->getMessage());
$trace = $this->escape($e->getTraceAsString());
echo "##teamcity[testFailed type='failure' name='{$class}.{$test->getName()}' message='$message' details='$trace']\n";
echo "##teamcity[testFailed type='failure' name='{$class}.{$test->getName()}' message='$message'"
. " details='$trace']\n";
}
public function addIncompleteTest(PHPUnit_Framework_Test $test, Exception $e, $time) {

View File

@ -31,7 +31,9 @@ class TestMailer extends Mailer {
* Send a multi-part HTML email
* TestMailer will merely record that the email was asked to be sent, without sending anything.
*/
public function sendHTML($to, $from, $subject, $htmlContent, $attachedFiles = false, $customHeaders = false, $plainContent = false, $inlineImages = false) {
public function sendHTML($to, $from, $subject, $htmlContent, $attachedFiles = false, $customHeaders = false,
$plainContent = false, $inlineImages = false) {
$this->emailsSent[] = array(
'type' => 'html',
'to' => $to,
@ -64,7 +66,8 @@ class TestMailer extends Mailer {
* @param $from
* @param $subject
* @param $content
* @return An array containing the keys: 'type','to','from','subject','content', 'plainContent','attachedFiles','customHeaders','htmlContent',inlineImages'
* @return array Contains the keys: 'type', 'to', 'from', 'subject', 'content', 'plainContent', 'attachedFiles',
* 'customHeaders', 'htmlContent', 'inlineImages'
*/
public function findEmail($to, $from = null, $subject = null, $content = null) {
foreach($this->emailsSent as $email) {

View File

@ -62,7 +62,8 @@ class TestRunner extends Controller {
* @var Array Blacklist certain directories for the coverage report.
* Filepaths are relative to the webroot, without leading slash.
*
* @see http://www.phpunit.de/manual/current/en/appendixes.configuration.html#appendixes.configuration.blacklist-whitelist
* @see http://www.phpunit.de/manual/current/en/appendixes.configuration.html
* #appendixes.configuration.blacklist-whitelist
*/
static $coverage_filter_dirs = array(
'*/thirdparty',
@ -229,7 +230,8 @@ class TestRunner extends Controller {
$classNames = explode(',', $request->param('TestCase'));
foreach($classNames as $className) {
if(!class_exists($className) || !is_subclass_of($className, 'SapphireTest')) {
user_error("TestRunner::only(): Invalid TestCase '$className', cannot find matching class", E_USER_ERROR);
user_error("TestRunner::only(): Invalid TestCase '$className', cannot find matching class",
E_USER_ERROR);
}
}
@ -341,8 +343,8 @@ class TestRunner extends Controller {
/**
* Start a test session.
* Usage: visit dev/tests/startsession?fixture=(fixturefile). A test database will be constructed, and your browser session will be amended
* to use this database. This can only be run on dev and test sites.
* Usage: visit dev/tests/startsession?fixture=(fixturefile). A test database will be constructed, and your
* browser session will be amended to use this database. This can only be run on dev and test sites.
*
* See {@link setdb()} for an alternative approach which just sets a database
* name, and is used for more advanced use cases like interacting with test databases
@ -352,13 +354,15 @@ class TestRunner extends Controller {
if(!Director::isLive()) {
if(SapphireTest::using_temp_db()) {
$endLink = Director::baseURL() . "dev/tests/endsession";
return "<p><a id=\"end-session\" href=\"$endLink\">You're in the middle of a test session; click here to end it.</a></p>";
return "<p><a id=\"end-session\" href=\"$endLink\">You're in the middle of a test session;"
. " click here to end it.</a></p>";
} else if(!isset($_GET['fixture'])) {
$me = Director::baseURL() . "dev/tests/startsession";
return <<<HTML
<form action="$me">
<p>Enter a fixture file name to start a new test session. Don't forget to visit dev/tests/endsession when you're done!</p>
<p>Enter a fixture file name to start a new test session. Don't forget to visit dev/tests/endsession when
you're done!</p>
<p>Fixture file (leave blank to start with default set-up): <input id="fixture-file" name="fixture" /></p>
<input type="hidden" name="flush" value="1">
<p><input id="start-session" value="Start test session" type="submit" /></p>
@ -398,12 +402,15 @@ HTML;
foreach($dataClasses as $dataClass) singleton($dataClass)->requireDefaultRecords();
}
return "<p>Started testing session with fixture '$fixtureFile'. Time to start testing; where would you like to start?</p>
return "<p>Started testing session with fixture '$fixtureFile'.
Time to start testing; where would you like to start?</p>
<ul>
<li><a id=\"home-link\" href=\"" .Director::baseURL() . "\">Homepage - published site</a></li>
<li><a id=\"draft-link\" href=\"" .Director::baseURL() . "?stage=Stage\">Homepage - draft site</a></li>
<li><a id=\"draft-link\" href=\"" .Director::baseURL() . "?stage=Stage\">Homepage - draft site
</a></li>
<li><a id=\"admin-link\" href=\"" .Director::baseURL() . "admin/\">CMS Admin</a></li>
<li><a id=\"endsession-link\" href=\"" .Director::baseURL() . "dev/tests/endsession\">End your test session</a></li>
<li><a id=\"endsession-link\" href=\"" .Director::baseURL() . "dev/tests/endsession\">
End your test session</a></li>
</ul>";
}
@ -449,7 +456,8 @@ HTML;
<li><a id=\"home-link\" href=\"" .Director::baseURL() . "\">Homepage - published site</a></li>
<li><a id=\"draft-link\" href=\"" .Director::baseURL() . "?stage=Stage\">Homepage - draft site</a></li>
<li><a id=\"admin-link\" href=\"" .Director::baseURL() . "admin/\">CMS Admin</a></li>
<li><a id=\"endsession-link\" href=\"" .Director::baseURL() . "dev/tests/endsession\">End your test session</a></li>
<li><a id=\"endsession-link\" href=\"" .Director::baseURL() . "dev/tests/endsession\">
End your test session</a></li>
</ul>";
}
@ -460,14 +468,16 @@ HTML;
if(isset($_GET['fixture']) && ($fixtureFile = $_GET['fixture'])) {
$fixture = new YamlFixture($fixtureFile);
$fixture->saveIntoDatabase();
return "<p>Re-test the test database with fixture '$fixtureFile'. Time to start testing; where would you like to start?</p>";
return "<p>Re-test the test database with fixture '$fixtureFile'. Time to start testing; where would"
. " you like to start?</p>";
} else {
return "<p>Re-test the test database. Time to start testing; where would you like to start?</p>";
}
} else {
return "<p>dev/tests/emptydb can only be used with a temporary database. Perhaps you should use dev/tests/startsession first?</p>";
return "<p>dev/tests/emptydb can only be used with a temporary database. Perhaps you should use"
. " dev/tests/startsession first?</p>";
}
}
@ -478,7 +488,8 @@ HTML;
return "<p>Test session ended.</p>
<ul>
<li><a id=\"home-link\" href=\"" .Director::baseURL() . "\">Return to your site</a></li>
<li><a id=\"startsession-link\" href=\"" .Director::baseURL() . "dev/tests/startsession\">Start a new test session</a></li>
<li><a id=\"startsession-link\" href=\"" .Director::baseURL() . "dev/tests/startsession\">
Start a new test session</a></li>
</ul>";
}

View File

@ -44,7 +44,8 @@ class TestSession {
public function get($url, $session = null, $headers = null, $cookies = null) {
$headers = (array) $headers;
if($this->lastUrl && !isset($headers['Referer'])) $headers['Referer'] = $this->lastUrl;
$this->lastResponse = Director::test($url, null, $session ? $session : $this->session, null, null, $headers, $cookies);
$this->lastResponse
= Director::test($url, null, $session ? $session : $this->session, null, null, $headers, $cookies);
$this->lastUrl = $url;
if(!$this->lastResponse) user_error("Director::test($url) returned null", E_USER_WARNING);
return $this->lastResponse;
@ -57,7 +58,8 @@ class TestSession {
public function post($url, $data, $headers = null, $session = null, $body = null, $cookies = null) {
$headers = (array) $headers;
if($this->lastUrl && !isset($headers['Referer'])) $headers['Referer'] = $this->lastUrl;
$this->lastResponse = Director::test($url, $data, $session ? $session : $this->session, null, $body, $headers, $cookies);
$this->lastResponse
= Director::test($url, $data, $session ? $session : $this->session, null, $body, $headers, $cookies);
$this->lastUrl = $url;
if(!$this->lastResponse) user_error("Director::test($url) returned null", E_USER_WARNING);
return $this->lastResponse;
@ -104,7 +106,8 @@ class TestSession {
return $this->post($url, $postVars);
} else {
user_error("TestSession::submitForm called when there is no form loaded. Visit the page with the form first", E_USER_WARNING);
user_error("TestSession::submitForm called when there is no form loaded."
. " Visit the page with the form first", E_USER_WARNING);
}
}

View File

@ -20,8 +20,8 @@ require_once 'thirdparty/spyc/spyc.php';
* <code>
* Parent: =>Page.about
* </code>
* This will tell the system to set the ParentID database field to the ID of the Page object with the identifier 'about'.
* This can be used on any has-one or many-many relationship.
* This will tell the system to set the ParentID database field to the ID of the Page object with the identifier
* 'about'. This can be used on any has-one or many-many relationship.
* Note that we use the name of the relationship (Parent), and not the name of the database field (ParentID)
*
* On many-many relationships, you should specify a comma separated list of values.
@ -102,7 +102,8 @@ class YamlFixture extends Object {
if(!Director::is_absolute($fixture)) $fixture = Director::baseFolder().'/'. $fixture;
if(!file_exists($fixture)) {
throw new InvalidArgumentException('YamlFixture::__construct(): Fixture path "' . $fixture . '" not found');
throw new InvalidArgumentException('YamlFixture::__construct(): Fixture path "' . $fixture
. '" not found');
}
$this->fixtureFile = $fixture;
@ -216,9 +217,13 @@ class YamlFixture extends Object {
// The database needs to allow inserting values into the foreign key column (ID in our case)
$conn = DB::getConn();
if(method_exists($conn, 'allowPrimaryKeyEditing')) $conn->allowPrimaryKeyEditing(ClassInfo::baseDataClass($dataClass), true);
if(method_exists($conn, 'allowPrimaryKeyEditing')) {
$conn->allowPrimaryKeyEditing(ClassInfo::baseDataClass($dataClass), true);
}
$obj->write(false, true);
if(method_exists($conn, 'allowPrimaryKeyEditing')) $conn->allowPrimaryKeyEditing(ClassInfo::baseDataClass($dataClass), false);
if(method_exists($conn, 'allowPrimaryKeyEditing')) {
$conn->allowPrimaryKeyEditing(ClassInfo::baseDataClass($dataClass), false);
}
}
// Populate the dictionary with the ID

View File

@ -53,7 +53,8 @@ class DatabaseAdapterRegistry {
$moduleName = array_shift($path);
$missingModuleText = isset($config['missingModuleText'])
? $config['missingModuleText']
: 'The SilverStripe module, '.$moduleName.', is missing or incomplete. Please <a href="http://silverstripe.org/modules">download it</a>.';
: 'The SilverStripe module, '.$moduleName.', is missing or incomplete.'
. ' Please <a href="http://silverstripe.org/modules">download it</a>.';
$config['missingModuleText'] = $missingModuleText;
$config['missingExtensionText'] = $missingExtensionText;
@ -70,7 +71,9 @@ class DatabaseAdapterRegistry {
public static function autodiscover() {
foreach(glob(dirname(__FILE__) . '/../../../*', GLOB_ONLYDIR) as $directory) {
if(file_exists($directory . '/_register_database.php')) include_once($directory . '/_register_database.php');
if(file_exists($directory . '/_register_database.php')) {
include_once($directory . '/_register_database.php');
}
}
}

View File

@ -16,7 +16,10 @@ define('FRAMEWORK_NAME', 'framework');
if (version_compare(phpversion(), '5.3.2', '<')) {
header("HTTP/1.1 500 Server Error");
echo str_replace(array('$PHPVersion', 'sapphire'), array(phpversion(), FRAMEWORK_NAME), file_get_contents(FRAMEWORK_NAME . "/dev/install/php5-required.html"));
echo str_replace(
array('$PHPVersion', 'sapphire'),
array(phpversion(), FRAMEWORK_NAME),
file_get_contents(FRAMEWORK_NAME . "/dev/install/php5-required.html"));
die();
}

View File

@ -59,8 +59,10 @@ class PhpUnitWrapper_3_4 extends PhpUnitWrapper {
$ret = PHPUnit_Util_Report::render($this->getFrameworkTestResults(), ASSETS_PATH . '/coverage-report/');
$coverageApp = ASSETS_PATH . '/coverage-report/' . preg_replace('/[^A-Za-z0-9]/','_',preg_replace('/(\/$)|(^\/)/','',Director::baseFolder())) . '.html';
$coverageTemplates = ASSETS_PATH . '/coverage-report/' . preg_replace('/[^A-Za-z0-9]/','_',preg_replace('/(\/$)|(^\/)/','',realpath(TEMP_FOLDER))) . '.html';
$coverageApp = ASSETS_PATH . '/coverage-report/'
. preg_replace('/[^A-Za-z0-9]/','_',preg_replace('/(\/$)|(^\/)/','',Director::baseFolder())) . '.html';
$coverageTemplates = ASSETS_PATH . '/coverage-report/'
. preg_replace('/[^A-Za-z0-9]/','_',preg_replace('/(\/$)|(^\/)/','',realpath(TEMP_FOLDER))) . '.html';
echo "<p>Coverage reports available here:<ul>
<li><a href=\"$coverageApp\">Coverage report of the application</a></li>

View File

@ -16,7 +16,8 @@ if(isset($_SERVER['SERVER_NAME'])) {
*/
define('X_MAILER', 'SilverStripe Mailer - version 2006.06.21');
}
// Note: The constant 'BOUNCE_EMAIL' should be defined as a valid email address for where bounces should be returned to.
// Note: The constant 'BOUNCE_EMAIL' should be defined as a valid email address for where bounces should be returned
// to.
/**
* Class to support sending emails.
@ -141,7 +142,9 @@ class Email extends ViewableData {
/**
* Create a new email.
*/
public function __construct($from = null, $to = null, $subject = null, $body = null, $bounceHandlerURL = null, $cc = null, $bcc = null) {
public function __construct($from = null, $to = null, $subject = null, $body = null, $bounceHandlerURL = null,
$cc = null, $bcc = null) {
if($from != null) $this->from = $from;
if($to != null) $this->to = $to;
if($subject != null) $this->subject = $subject;
@ -363,7 +366,8 @@ class Email extends ViewableData {
if (function_exists('filter_var')) {
return filter_var($address, FILTER_VALIDATE_EMAIL);
} else {
return preg_match('#^([a-zA-Z0-9_+\.\-]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$#', $address);
return preg_match('#^([a-zA-Z0-9_+\.\-]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)'
. '|(([a-zA-Z0-9\-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$#', $address);
}
}
@ -488,7 +492,8 @@ class Email extends ViewableData {
Requirements::restore();
return self::mailer()->sendHTML($to, $this->from, $subject, $this->body, $this->attachments, $headers, $this->plaintext_body);
return self::mailer()->sendHTML($to, $this->from, $subject, $this->body, $this->attachments, $headers,
$this->plaintext_body);
}
/**
@ -525,10 +530,11 @@ class Email extends ViewableData {
/**
* CC every email generated by the Email class to the given address.
* It won't affect the original delivery in the same way that send_all_emails_to does. It just adds a CC header
* with the given email address. Note that you can only call this once - subsequent calls will overwrite the configuration
* variable.
* with the given email address. Note that you can only call this once - subsequent calls will overwrite the
* configuration variable.
*
* This can be used when you have a system that relies heavily on email and you want someone to be checking all correspondence.
* This can be used when you have a system that relies heavily on email and you want someone to be checking all
* correspondence.
*
* if(Director::isLive()) Email::cc_all_emails_to("supportperson@example.com")
*/
@ -539,10 +545,11 @@ class Email extends ViewableData {
/**
* BCC every email generated by the Email class to the given address.
* It won't affect the original delivery in the same way that send_all_emails_to does. It just adds a BCC header
* with the given email address. Note that you can only call this once - subsequent calls will overwrite the configuration
* variable.
* with the given email address. Note that you can only call this once - subsequent calls will overwrite the
* configuration variable.
*
* This can be used when you have a system that relies heavily on email and you want someone to be checking all correspondence.
* This can be used when you have a system that relies heavily on email and you want someone to be checking all
* correspondence.
*
* if(Director::isLive()) Email::cc_all_emails_to("supportperson@example.com")
*/
@ -667,7 +674,8 @@ class Email_BounceHandler extends Controller {
$SQL_email = Convert::raw2sql($email);
$SQL_bounceTime = Convert::raw2sql("$date $time");
$duplicateBounce = DataObject::get_one("Email_BounceRecord", "\"BounceEmail\" = '$SQL_email' AND (\"BounceTime\"+INTERVAL 1 MINUTE) > '$SQL_bounceTime'");
$duplicateBounce = DataObject::get_one("Email_BounceRecord",
"\"BounceEmail\" = '$SQL_email' AND (\"BounceTime\"+INTERVAL 1 MINUTE) > '$SQL_bounceTime'");
if(!$duplicateBounce) {
$record = new Email_BounceRecord();
@ -677,8 +685,9 @@ class Email_BounceHandler extends Controller {
if( $member ) {
$record->MemberID = $member->ID;
// If the SilverStripeMessageID (taken from the X-SilverStripeMessageID header embedded in the email) is sent,
// then log this bounce in a Newsletter_SentRecipient record so it will show up on the 'Sent Status Report' tab of the Newsletter
// If the SilverStripeMessageID (taken from the X-SilverStripeMessageID header embedded in the email)
// is sent, then log this bounce in a Newsletter_SentRecipient record so it will show up on the 'Sent
// Status Report' tab of the Newsletter
if( isset($_REQUEST['SilverStripeMessageID'])) {
// Note: was sent out with: $project . '.' . $messageID;
$message_id_parts = explode('.', $_REQUEST['SilverStripeMessageID']);
@ -690,7 +699,9 @@ class Email_BounceHandler extends Controller {
$SQL_newsletterID = Convert::raw2sql($newsletter_id_date_parts[0]);
// Log the bounce
$oldNewsletterSentRecipient = DataObject::get_one("Newsletter_SentRecipient", "\"MemberID\" = '$SQL_memberID' AND \"ParentID\" = '$SQL_newsletterID' AND \"Email\" = '$SQL_email'");
$oldNewsletterSentRecipient = DataObject::get_one("Newsletter_SentRecipient",
"\"MemberID\" = '$SQL_memberID' AND \"ParentID\" = '$SQL_newsletterID'"
. " AND \"Email\" = '$SQL_email'");
// Update the Newsletter_SentRecipient record if it exists
if($oldNewsletterSentRecipient) {
@ -707,9 +718,11 @@ class Email_BounceHandler extends Controller {
}
// Now we are going to Blacklist this member so that email will not be sent to them in the future.
// Note: Sending can be re-enabled by going to 'Mailing List' 'Bounced' tab and unchecking the box under 'Blacklisted'
// Note: Sending can be re-enabled by going to 'Mailing List' 'Bounced' tab and unchecking the box
// under 'Blacklisted'
$member->setBlacklistedEmail(TRUE);
echo '<p><b>Member: '.$member->FirstName.' '.$member->Surname.' <'.$member->Email.'> was added to the Email Blacklist!</b></p>';
echo '<p><b>Member: '.$member->FirstName.' '.$member->Surname
.' <'.$member->Email.'> was added to the Email Blacklist!</b></p>';
}
}

View File

@ -27,8 +27,11 @@ class Mailer extends Object {
*
* @return bool
*/
public function sendHTML($to, $from, $subject, $htmlContent, $attachedFiles = false, $customheaders = false, $plainContent = false, $inlineImages = false) {
return htmlEmail($to, $from, $subject, $htmlContent, $attachedFiles, $customheaders, $plainContent, $inlineImages);
public function sendHTML($to, $from, $subject, $htmlContent, $attachedFiles = false, $customheaders = false,
$plainContent = false, $inlineImages = false) {
return htmlEmail($to, $from, $subject, $htmlContent, $attachedFiles, $customheaders,
$plainContent, $inlineImages);
}
}
@ -43,7 +46,9 @@ class Mailer extends Object {
*
* @return bool
*/
function htmlEmail($to, $from, $subject, $htmlContent, $attachedFiles = false, $customheaders = false, $plainContent = false, $inlineImages = false) {
function htmlEmail($to, $from, $subject, $htmlContent, $attachedFiles = false, $customheaders = false,
$plainContent = false, $inlineImages = false) {
if ($customheaders && is_array($customheaders) == false) {
echo "htmlEmail($to, $from, $subject, ...) could not send mail: improper \$customheaders passed:<BR>";
dieprintr($customheaders);
@ -69,7 +74,9 @@ function htmlEmail($to, $from, $subject, $htmlContent, $attachedFiles = false, $
$headers["Content-Type"] = "text/plain; charset=utf-8";
$headers["Content-Transfer-Encoding"] = $plainEncoding ? $plainEncoding : "quoted-printable";
$plainPart = processHeaders($headers, ($plainEncoding == "base64") ? chunk_split(base64_encode($plainContent),60) : wordwrap(QuotedPrintable_encode($plainContent),75));
$plainPart = processHeaders($headers, ($plainEncoding == "base64")
? chunk_split(base64_encode($plainContent),60)
: wordwrap(QuotedPrintable_encode($plainContent),75));
// Make the HTML part
$headers["Content-Type"] = "text/html; charset=utf-8";
@ -167,7 +174,8 @@ function htmlEmail($to, $from, $subject, $htmlContent, $attachedFiles = false, $
* Send a plain text e-mail
*/
function plaintextEmail($to, $from, $subject, $plainContent, $attachedFiles, $customheaders = false) {
$plainEncoding = false; // Not ensurely where this is supposed to be set, but defined it false for now to remove php notices
// Not ensurely where this is supposed to be set, but defined it false for now to remove php notices
$plainEncoding = false;
if ($customheaders && is_array($customheaders) == false) {
echo "htmlEmail($to, $from, $subject, ...) could not send mail: improper \$customheaders passed:<BR>";
@ -182,7 +190,9 @@ function plaintextEmail($to, $from, $subject, $plainContent, $attachedFiles, $cu
$headers["Content-Type"] = "text/plain; charset=utf-8";
$headers["Content-Transfer-Encoding"] = $plainEncoding ? $plainEncoding : "quoted-printable";
$plainContent = ($plainEncoding == "base64") ? chunk_split(base64_encode($plainContent),60) : QuotedPrintable_encode($plainContent);
$plainContent = ($plainEncoding == "base64")
? chunk_split(base64_encode($plainContent),60)
: QuotedPrintable_encode($plainContent);
// Messages with attachments are handled differently
if($attachedFiles) {
@ -256,10 +266,12 @@ function encodeMultipart($parts, $contentType, $headers = false) {
$headers["Content-Transfer-Encoding"] = "7bit";
if($contentType == "multipart/alternative") {
//$baseMessage = "This is an encoded HTML message. There are two parts: a plain text and an HTML message, open whatever suits you better.";
// $baseMessage = "This is an encoded HTML message. There are two parts: a plain text and an HTML message,
// open whatever suits you better.";
$baseMessage = "\nThis is a multi-part message in MIME format.";
} else {
//$baseMessage = "This is a message containing attachments. The e-mail body is contained in the first attachment";
// $baseMessage = "This is a message containing attachments. The e-mail body is contained in the first
// attachment";
$baseMessage = "\nThis is a multi-part message in MIME format.";
}
@ -334,18 +346,20 @@ function processHeaders($headers, $body = false) {
*
* h5. contentLocation
*
* Content Location is one of the two methods allowed for embedding images into an html email. It's also the simplest, and best supported
* Content Location is one of the two methods allowed for embedding images into an html email. It's also the simplest,
* and best supported.
*
* Assume we have an email with this in the body:
*
* <img src="http://example.com/image.gif" />
*
* To display the image, an email viewer would have to download the image from the web every time it is displayed. Due to privacy issues, most
* viewers will not display any images unless the user clicks 'Show images in this email'. Not optimal.
* To display the image, an email viewer would have to download the image from the web every time it is displayed. Due
* to privacy issues, most viewers will not display any images unless the user clicks 'Show images in this email'. Not
* optimal.
*
* However, we can also include a copy of this image as an attached file in the email. By giving it a contentLocation of "http://example.com/image.gif"
* most email viewers will use this attached copy instead of downloading it. Better, most viewers will show it without a 'Show images in this email'
* conformation.
* However, we can also include a copy of this image as an attached file in the email. By giving it a contentLocation
* of "http://example.com/image.gif" most email viewers will use this attached copy instead of downloading it. Better,
* most viewers will show it without a 'Show images in this email' conformation.
*
* Here is an example of passing this information through Email.php:
*

View File

@ -124,8 +124,8 @@ class File extends DataObject {
*/
static $app_categories = array(
'audio' => array(
"aif" ,"au" ,"mid" ,"midi" ,"mp3" ,"ra" ,"ram" ,"rm","mp3" ,"wav" ,"m4a" ,"snd" ,"aifc" ,"aiff" ,"wma" ,"apl",
"avr" ,"cda" ,"mp4" ,"ogg"
"aif" ,"au" ,"mid" ,"midi" ,"mp3" ,"ra" ,"ram" ,"rm","mp3" ,"wav" ,"m4a" ,"snd" ,"aifc" ,"aiff" ,"wma",
"apl", "avr" ,"cda" ,"mp4" ,"ogg"
),
'mov' => array(
"mpeg" ,"mpg" ,"m1v" ,"mp2" ,"mpa" ,"mpe" ,"ifo" ,"vob","avi" ,"wmv" ,"asf" ,"m2v" ,"qt"
@ -333,7 +333,8 @@ class File extends DataObject {
$formattedImage = $this->getFormattedImage('SetWidth', Image::$asset_preview_width);
$thumbnail = $formattedImage ? $formattedImage->URL : '';
$previewField = new LiteralField("ImageFull",
"<img id='thumbnailImage' class='thumbnail-preview' src='{$thumbnail}?r=" . rand(1,100000) . "' alt='{$this->Name}' />\n"
"<img id='thumbnailImage' class='thumbnail-preview' src='{$thumbnail}?r="
. rand(1,100000) . "' alt='{$this->Name}' />\n"
);
} else {
$previewField = new LiteralField("ImageFull", $this->CMSThumbnail());
@ -526,11 +527,16 @@ class File extends DataObject {
if(!file_exists($pathAfterAbs)) {
if(!is_a($this, 'Folder')) {
// Only throw a fatal error if *both* before and after paths don't exist.
if(!file_exists($pathBeforeAbs)) throw new Exception("Cannot move $pathBefore to $pathAfter - $pathBefore doesn't exist");
if(!file_exists($pathBeforeAbs)) {
throw new Exception("Cannot move $pathBefore to $pathAfter - $pathBefore doesn't exist");
}
// Check that target directory (not the file itself) exists.
// Only check if we're dealing with a file, otherwise the folder will need to be created
if(!file_exists(dirname($pathAfterAbs))) throw new Exception("Cannot move $pathBefore to $pathAfter - Directory " . dirname($pathAfter) . " doesn't exist");
if(!file_exists(dirname($pathAfterAbs))) {
throw new Exception("Cannot move $pathBefore to $pathAfter - Directory " . dirname($pathAfter)
. " doesn't exist");
}
}
// Rename file or folder
@ -589,14 +595,18 @@ class File extends DataObject {
$base = pathinfo($name, PATHINFO_BASENAME);
$ext = self::get_file_extension($name);
$suffix = 1;
while(DataObject::get_one("File", "\"Name\" = '" . Convert::raw2sql($name) . "' AND \"ParentID\" = " . (int)$this->ParentID)) {
while(DataObject::get_one("File", "\"Name\" = '" . Convert::raw2sql($name)
. "' AND \"ParentID\" = " . (int)$this->ParentID)) {
$suffix++;
$name = "$base-$suffix$ext";
}
}
// Update title
if(!$this->getField('Title')) $this->__set('Title', str_replace(array('-','_'),' ', preg_replace('/\.[^.]+$/', '', $name)));
if(!$this->getField('Title')) {
$this->__set('Title', str_replace(array('-','_'),' ', preg_replace('/\.[^.]+$/', '', $name)));
}
// Update actual field value
$this->setField('Name', $name);
@ -676,7 +686,8 @@ class File extends DataObject {
*/
public function getRelativePath() {
if($this->ParentID) {
$p = DataObject::get_by_id('Folder', $this->ParentID, false); // Don't use the cache, the parent has just been changed
// Don't use the cache, the parent has just been changed
$p = DataObject::get_by_id('Folder', $this->ParentID, false);
if($p && $p->exists()) return $p->getRelativePath() . $this->getField("Name");
else return ASSETS_DIR . "/" . $this->getField("Name");
} else if($this->getField("Name")) {

View File

@ -30,7 +30,8 @@ class Filesystem extends Object {
* Remove a directory and all subdirectories and files.
*
* @param String $folder Absolute folder path
* @param Boolean $contentsOnly If this is true then the contents of the folder will be removed but not the folder itself
* @param Boolean $contentsOnly If this is true then the contents of the folder will be removed but not the
* folder itself
*/
public static function removeFolder($folder, $contentsOnly = false) {

View File

@ -99,11 +99,14 @@ class Folder extends File {
$deleted = 0;
// First, merge any children that are duplicates
$duplicateChildrenNames = DB::query("SELECT \"Name\" FROM \"File\" WHERE \"ParentID\" = $parentID GROUP BY \"Name\" HAVING count(*) > 1")->column();
$duplicateChildrenNames = DB::query("SELECT \"Name\" FROM \"File\""
. " WHERE \"ParentID\" = $parentID GROUP BY \"Name\" HAVING count(*) > 1")->column();
if($duplicateChildrenNames) foreach($duplicateChildrenNames as $childName) {
$childName = Convert::raw2sql($childName);
// Note, we do this in the database rather than object-model; otherwise we get all sorts of problems about deleting files
$children = DB::query("SELECT \"ID\" FROM \"File\" WHERE \"Name\" = '$childName' AND \"ParentID\" = $parentID")->column();
// Note, we do this in the database rather than object-model; otherwise we get all sorts of problems
// about deleting files
$children = DB::query("SELECT \"ID\" FROM \"File\""
. " WHERE \"Name\" = '$childName' AND \"ParentID\" = $parentID")->column();
if($children) {
$keptChild = array_shift($children);
foreach($children as $removedChild) {
@ -111,7 +114,8 @@ class Folder extends File {
DB::query("DELETE FROM \"File\" WHERE \"ID\" = $removedChild");
}
} else {
user_error("Inconsistent database issue: SELECT ID FROM \"File\" WHERE Name = '$childName' AND ParentID = $parentID should have returned data", E_USER_WARNING);
user_error("Inconsistent database issue: SELECT ID FROM \"File\" WHERE Name = '$childName'"
. " AND ParentID = $parentID should have returned data", E_USER_WARNING);
}
}
@ -143,7 +147,9 @@ class Folder extends File {
if(file_exists($baseDir)) {
$actualChildren = scandir($baseDir);
foreach($actualChildren as $actualChild) {
if($actualChild[0] == '.' || $actualChild[0] == '_' || substr($actualChild,0,6) == 'Thumbs' || $actualChild == 'web.config') {
if($actualChild[0] == '.' || $actualChild[0] == '_' || substr($actualChild,0,6) == 'Thumbs'
|| $actualChild == 'web.config') {
continue;
}
@ -214,7 +220,8 @@ class Folder extends File {
DB::query("INSERT INTO \"File\"
(\"ClassName\", \"ParentID\", \"OwnerID\", \"Name\", \"Filename\", \"Created\", \"LastEdited\", \"Title\")
VALUES ('$className', $this->ID, $ownerID, '$name', '$filename', " . DB::getConn()->now() . ',' . DB::getConn()->now() . ", '$name')");
VALUES ('$className', $this->ID, $ownerID, '$name', '$filename', "
. DB::getConn()->now() . ',' . DB::getConn()->now() . ", '$name')");
return DB::getGeneratedID("File");
}
@ -226,7 +233,8 @@ class Folder extends File {
*/
public function addUploadToFolder($tmpFile) {
if(!is_array($tmpFile)) {
user_error("Folder::addUploadToFolder() Not passed an array. Most likely, the form hasn't got the right enctype", E_USER_ERROR);
user_error("Folder::addUploadToFolder() Not passed an array."
. " Most likely, the form hasn't got the right enctype", E_USER_ERROR);
}
if(!isset($tmpFile['size'])) {
return;
@ -279,8 +287,12 @@ class Folder extends File {
// Update with the new image
return $this->constructChild(basename($file . $ext));
} else {
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 '$base/$file$ext'", 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 '$base/$file$ext'",
E_USER_ERROR);
}
return false;
}
}

View File

@ -277,8 +277,8 @@ class GD extends Object {
}
/**
* Resize the image by preserving aspect ratio. By default, it will keep the image inside the maxWidth and maxHeight
* Passing useAsMinimum will make the smaller dimension equal to the maximum corresponding dimension
* Resize the image by preserving aspect ratio. By default, it will keep the image inside the maxWidth
* and maxHeight. Passing useAsMinimum will make the smaller dimension equal to the maximum corresponding dimension
*/
public function resizeRatio( $maxWidth, $maxHeight, $useAsMinimum = false ) {
@ -351,7 +351,9 @@ class GD extends Object {
$destY = round( ($height - $destHeight) / 2 );
}
imagecopyresampled($newGD, $this->gd, $destX, $destY, 0, 0, $destWidth, $destHeight, $this->width, $this->height);
imagecopyresampled($newGD, $this->gd,
$destX, $destY, 0, 0,
$destWidth, $destHeight, $this->width, $this->height);
}
$output = clone $this;
$output->setGD($newGD);
@ -363,7 +365,8 @@ class GD extends Object {
* $rv = red value, defaults to 38
* $gv = green value, defaults to 36
* $bv = blue value, defaults to 26
* Based (more or less entirely, with changes for readability) on code from http://www.teckis.com/scriptix/thumbnails/teck.html
* Based (more or less entirely, with changes for readability) on code from
* http://www.teckis.com/scriptix/thumbnails/teck.html
*/
public function greyscale($rv=38, $gv=36, $bv=26) {
$width = $this->width;

View File

@ -106,7 +106,8 @@ class Upload extends Controller {
}
if(!is_array($tmpFile)) {
user_error("Upload::load() Not passed an array. Most likely, the form hasn't got the right enctype", E_USER_ERROR);
user_error("Upload::load() Not passed an array. Most likely, the form hasn't got the right enctype",
E_USER_ERROR);
}
if(!$tmpFile['size']) {
@ -152,7 +153,9 @@ class Upload extends Controller {
} else {
$relativeFilePath .= '_'.$i;
}
if($oldFilePath == $relativeFilePath && $i > 2) user_error("Couldn't fix $relativeFilePath with $i tries", E_USER_ERROR);
if($oldFilePath == $relativeFilePath && $i > 2) {
user_error("Couldn't fix $relativeFilePath with $i tries", E_USER_ERROR);
}
}
if(file_exists($tmpFile['tmp_name']) && copy($tmpFile['tmp_name'], "$base/$relativeFilePath")) {
@ -470,7 +473,8 @@ class Upload_Validator {
if(!isset($pathInfo['extension'])) {
return in_array('', $this->allowedExtensions, true);
} else {
return (!count($this->allowedExtensions) || in_array(strtolower($pathInfo['extension']), $this->allowedExtensions));
return (!count($this->allowedExtensions)
|| in_array(strtolower($pathInfo['extension']), $this->allowedExtensions));
}
}

View File

@ -14,7 +14,9 @@ class AjaxUniqueTextField extends TextField {
protected $restrictedRegex;
public function __construct($name, $title, $restrictedField, $restrictedTable, $value = "", $maxLength = null, $validationURL = null, $restrictedRegex = null ){
public function __construct($name, $title, $restrictedField, $restrictedTable, $value = "", $maxLength = null,
$validationURL = null, $restrictedRegex = null ){
$this->maxLength = $maxLength;
$this->restrictedField = $restrictedField;
@ -37,7 +39,8 @@ class AjaxUniqueTextField extends TextField {
$url = Convert::raw2att( $this->validateURL );
if($this->restrictedRegex)
$restrict = "<input type=\"hidden\" class=\"hidden\" name=\"{$this->name}Restricted\" id=\"" . $this->id() . "RestrictedRegex\" value=\"{$this->restrictedRegex}\" />";
$restrict = "<input type=\"hidden\" class=\"hidden\" name=\"{$this->name}Restricted\" id=\"" . $this->id()
. "RestrictedRegex\" value=\"{$this->restrictedRegex}\" />";
$attributes = array(
'type' => 'text',
@ -61,7 +64,8 @@ class AjaxUniqueTextField extends TextField {
))->value();
if( $result && ( $result > 0 ) ) {
$validator->validationError( $this->name, _t('Form.VALIDATIONNOTUNIQUE', "The value entered is not unique") );
$validator->validationError($this->name,
_t('Form.VALIDATIONNOTUNIQUE', "The value entered is not unique"));
return false;
}

View File

@ -2,7 +2,8 @@
/**
* Displays a set of checkboxes as a logical group.
*
* ASSUMPTION -> IF you pass your source as an array, you pass values as an array too. Likewise objects are handled the same.
* ASSUMPTION -> IF you pass your source as an array, you pass values as an array too. Likewise objects are handled
* the same.
*
* Example:
* <code php>
@ -21,8 +22,11 @@
*
* <b>Saving</b>
* The checkbox set field will save its data in one of ways:
* * If the field name matches a many-many join on the object being edited, that many-many join will be updated to link to the objects selected on the checkboxes. In this case, the keys of your value map should be the IDs of the database records.
* * If the field name matches a database field, a comma-separated list of values will be saved to that field. The keys can be text or numbers.
* - If the field name matches a many-many join on the object being edited, that many-many join will be updated to
* link to the objects selected on the checkboxes. In this case, the keys of your value map should be the IDs of
* the database records.
* - If the field name matches a database field, a comma-separated list of values will be saved to that field. The
* keys can be text or numbers.
*
* @todo Document the different source data that can be used
* with this form field - e.g ComponentSet, ArrayList,
@ -154,7 +158,8 @@ class CheckboxSetField extends OptionsetField {
* Load a value into this CheckboxSetField
*/
public function setValue($value, $obj = null) {
// If we're not passed a value directly, we can look for it in a relation method on the object passed as a second arg
// If we're not passed a value directly, we can look for it in a relation method on the object passed as a
// second arg
if(!$value && $obj && $obj instanceof DataObject && $obj->hasMethod($this->name)) {
$funcName = $this->name;
$value = $obj->$funcName()->getIDList();

View File

@ -14,10 +14,12 @@
* Example-URL for a "DetailForm"-call explained:
* "/admin/family/?executeForm=EditForm&action_callfieldmethod&fieldName=Individual&childID=7&methodName=edit"
* - executeForm Name of the form on the main rendering page (e.g. "FamilyAdmin")
* - action_callfieldmethod Trigger to call a method of a single field in "EditForm" instead of rendering the whole thing
* - action_callfieldmethod Trigger to call a method of a single field in "EditForm" instead of rendering the
* whole thing
* - fieldName Name of the targeted formField
* - methodName Method on the formfield (e.g. "ComplexTableField")
* - childID Identifier of the database-record (the targeted table is determined by the $sourceClass parameter)
* - childID Identifier of the database-record (the targeted table is determined by the $sourceClass
* parameter)
*
* @deprecated 3.0 Use GridField with GridFieldConfig_RecordEditor
*
@ -191,7 +193,9 @@ class ComplexTableField extends TableListField {
* @param string $sourceSort
* @param string $sourceJoin
*/
public function __construct($controller, $name, $sourceClass, $fieldList = null, $detailFormFields = null, $sourceFilter = "", $sourceSort = "", $sourceJoin = "") {
public function __construct($controller, $name, $sourceClass, $fieldList = null, $detailFormFields = null,
$sourceFilter = "", $sourceSort = "", $sourceJoin = "") {
$this->detailFormFields = $detailFormFields;
$this->controller = $controller;
$this->pageSize = 10;
@ -251,7 +255,13 @@ JS;
return null;
}
$pageStart = (isset($_REQUEST['ctf'][$this->getName()]['start']) && is_numeric($_REQUEST['ctf'][$this->getName()]['start'])) ? $_REQUEST['ctf'][$this->getName()]['start'] : 0;
if(isset($_REQUEST['ctf'][$this->getName()]['start'])) {
$pageStart = $_REQUEST['ctf'][$this->getName()]['start'];
if(!is_numeric($pageStart)) $pageStart = 0;
} else {
$pageStart = 0;
}
$output = new ArrayList();
foreach($sourceItems as $pageIndex=>$item) {
@ -582,16 +592,17 @@ class ComplexTableField_ItemRequest extends TableListField_ItemRequest {
// used to discover fields if requested and for population of field
if(is_numeric($this->itemID)) {
// we have to use the basedataclass, otherwise we might exclude other subclasses
return DataObject::get_by_id(ClassInfo::baseDataClass(Object::getCustomClass($this->ctf->sourceClass())), $this->itemID);
return DataObject::get_by_id(
ClassInfo::baseDataClass(Object::getCustomClass($this->ctf->sourceClass())), $this->itemID);
}
}
/**
* Renders view, edit and add, depending on the given information.
* The form needs several parameters to function independently of its "parent-form", some derived from the context into a hidden-field,
* some derived from the parent context (which is not accessible here) and delivered by GET:
* ID, Identifier of the currently edited record (only if record is loaded).
* The form needs several parameters to function independently of its "parent-form", some derived from the context
* into a hidden-field, some derived from the parent context (which is not accessible here) and delivered by
* GET:ID, Identifier of the currently edited record (only if record is loaded).
* <parentIDName>, Link back to the correct parent record (e.g. "parentID").
* parentClass, Link back to correct container-class (the parent-record might have many 'has-one'-relationships)
* CAUTION: "ID" in the DetailForm would be the "childID" in the overview table.
@ -672,7 +683,8 @@ class ComplexTableField_ItemRequest extends TableListField_ItemRequest {
public function PopupFirstLink() {
$this->ctf->LinkToItem();
if(!isset($_REQUEST['ctf']['start']) || !is_numeric($_REQUEST['ctf']['start']) || $_REQUEST['ctf']['start'] == 0) {
if(!isset($_REQUEST['ctf']['start']) || !is_numeric($_REQUEST['ctf']['start'])
|| $_REQUEST['ctf']['start'] == 0) {
return null;
}
@ -681,7 +693,8 @@ class ComplexTableField_ItemRequest extends TableListField_ItemRequest {
}
public function PopupLastLink() {
if(!isset($_REQUEST['ctf']['start']) || !is_numeric($_REQUEST['ctf']['start']) || $_REQUEST['ctf']['start'] == $this->TotalCount()-1) {
if(!isset($_REQUEST['ctf']['start']) || !is_numeric($_REQUEST['ctf']['start'])
|| $_REQUEST['ctf']['start'] == $this->TotalCount()-1) {
return null;
}
@ -690,7 +703,8 @@ class ComplexTableField_ItemRequest extends TableListField_ItemRequest {
}
public function PopupNextLink() {
if(!isset($_REQUEST['ctf']['start']) || !is_numeric($_REQUEST['ctf']['start']) || $_REQUEST['ctf']['start'] == $this->TotalCount()-1) {
if(!isset($_REQUEST['ctf']['start']) || !is_numeric($_REQUEST['ctf']['start'])
|| $_REQUEST['ctf']['start'] == $this->TotalCount()-1) {
return null;
}
@ -699,7 +713,8 @@ class ComplexTableField_ItemRequest extends TableListField_ItemRequest {
}
public function PopupPrevLink() {
if(!isset($_REQUEST['ctf']['start']) || !is_numeric($_REQUEST['ctf']['start']) || $_REQUEST['ctf']['start'] == 0) {
if(!isset($_REQUEST['ctf']['start']) || !is_numeric($_REQUEST['ctf']['start'])
|| $_REQUEST['ctf']['start'] == 0) {
return null;
}

View File

@ -169,7 +169,11 @@ class CompositeField extends FormField {
$name = $field->getName();
if($name) {
$formName = (isset($this->form)) ? $this->form->FormName() : '(unknown form)';
if(isset($list[$name])) user_error("collateDataFields() I noticed that a field called '$name' appears twice in your form: '{$formName}'. One is a '{$field->class}' and the other is a '{$list[$name]->class}'", E_USER_ERROR);
if(isset($list[$name])) {
user_error("collateDataFields() I noticed that a field called '$name' appears twice in"
. " your form: '{$formName}'. One is a '{$field->class}' and the other is a"
. " '{$list[$name]->class}'", E_USER_ERROR);
}
$list[$name] = $field;
}
}
@ -302,7 +306,8 @@ class CompositeField extends FormField {
* the children collection. Doesn't work recursively.
*
* @param string|FormField
* @return Position in children collection (first position starts with 0). Returns FALSE if the field can't be found.
* @return int Position in children collection (first position starts with 0). Returns FALSE if the field can't
* be found.
*/
public function fieldPosition($field) {
if(is_string($field)) $field = $this->fieldByName($field);

View File

@ -67,7 +67,9 @@ class ConfirmedPasswordField extends FormField {
* @param boolean $showOnClick
* @param string $titleConfirmField Alternate title (not localizeable)
*/
public function __construct($name, $title = null, $value = "", $form = null, $showOnClick = false, $titleConfirmField = null) {
public function __construct($name, $title = null, $value = "", $form = null, $showOnClick = false,
$titleConfirmField = null) {
// naming with underscores to prevent values from actually being saved somewhere
$this->children = new FieldList(
new PasswordField(
@ -191,7 +193,8 @@ class ConfirmedPasswordField extends FormField {
$this->value = $value['_Password'];
}
if($this->showOnClick && isset($value['_PasswordFieldVisible'])){
$this->children->fieldByName($this->getName() . '[_PasswordFieldVisible]')->setValue($value['_PasswordFieldVisible']);
$this->children->fieldByName($this->getName() . '[_PasswordFieldVisible]')
->setValue($value['_PasswordFieldVisible']);
}
} else {
if($value || (!$value && $this->canBeEmpty)) {
@ -231,14 +234,16 @@ class ConfirmedPasswordField extends FormField {
// both password-fields should be the same
if($value != $passwordConfirmField->Value()) {
$validator->validationError($name, _t('Form.VALIDATIONPASSWORDSDONTMATCH',"Passwords don't match"), "validation", false);
$validator->validationError($name, _t('Form.VALIDATIONPASSWORDSDONTMATCH',"Passwords don't match"),
"validation", false);
return false;
}
if(!$this->canBeEmpty) {
// both password-fields shouldn't be empty
if(!$value || !$passwordConfirmField->Value()) {
$validator->validationError($name, _t('Form.VALIDATIONPASSWORDSNOTEMPTY', "Passwords can't be empty"), "validation", false);
$validator->validationError($name, _t('Form.VALIDATIONPASSWORDSNOTEMPTY', "Passwords can't be empty"),
"validation", false);
return false;
}
}
@ -280,7 +285,8 @@ class ConfirmedPasswordField extends FormField {
if(!preg_match('/^(([a-zA-Z]+\d+)|(\d+[a-zA-Z]+))[a-zA-Z0-9]*$/',$value)) {
$validator->validationError(
'Password',
_t('Form.VALIDATIONSTRONGPASSWORD', "Passwords must have at least one digit and one alphanumeric character"),
_t('Form.VALIDATIONSTRONGPASSWORD',
"Passwords must have at least one digit and one alphanumeric character"),
"validation",
false
);

View File

@ -10,12 +10,14 @@
class CountryDropdownField extends DropdownField {
/**
* @var bool - Should we default the dropdown to the region determined from the user's locale?
* Should we default the dropdown to the region determined from the user's locale?
* @var bool
*/
static $default_to_locale = true;
/**
* @var string - The region code to default to if default_to_locale is set to false, or we can't determine a region from a locale
* The region code to default to if default_to_locale is set to false, or we can't determine a region from a locale
* @var string
*/
static $default_country = 'NZ';

View File

@ -16,11 +16,15 @@ class CreditCardField extends TextField {
$parts = array_pad($parts, 4, "");
// TODO Mark as disabled/readonly
$field = "<span id=\"{$this->name}_Holder\" class=\"creditCardField\">" .
"<input autocomplete=\"off\" name=\"{$this->name}[0]\" value=\"$parts[0]\" maxlength=\"4\"" . $this->getTabIndexHTML(0) . " /> - " .
"<input autocomplete=\"off\" name=\"{$this->name}[1]\" value=\"$parts[1]\" maxlength=\"4\"" . $this->getTabIndexHTML(1) . " /> - " .
"<input autocomplete=\"off\" name=\"{$this->name}[2]\" value=\"$parts[2]\" maxlength=\"4\"" . $this->getTabIndexHTML(2) . " /> - " .
"<input autocomplete=\"off\" name=\"{$this->name}[3]\" value=\"$parts[3]\" maxlength=\"4\"" . $this->getTabIndexHTML(3) . " /></span>";
$field = "<span id=\"{$this->name}_Holder\" class=\"creditCardField\">"
. "<input autocomplete=\"off\" name=\"{$this->name}[0]\" value=\"$parts[0]\" maxlength=\"4\""
. $this->getTabIndexHTML(0) . " /> - "
. "<input autocomplete=\"off\" name=\"{$this->name}[1]\" value=\"$parts[1]\" maxlength=\"4\""
. $this->getTabIndexHTML(1) . " /> - "
. "<input autocomplete=\"off\" name=\"{$this->name}[2]\" value=\"$parts[2]\" maxlength=\"4\""
. $this->getTabIndexHTML(2) . " /> - "
. "<input autocomplete=\"off\" name=\"{$this->name}[3]\" value=\"$parts[3]\" maxlength=\"4\""
. $this->getTabIndexHTML(3) . " /></span>";
return $field;
}

View File

@ -46,8 +46,11 @@ class CurrencyField extends TextField {
}
public function validate($validator) {
if(!empty ($this->value) && !preg_match('/^\s*(\-?\$?|\$\-?)?(\d{1,3}(\,\d{3})*|(\d+))(\.\d{2})?\s*$/', $this->value)) {
$validator->validationError($this->name, _t('Form.VALIDCURRENCY', "Please enter a valid currency"), "validation", false);
if(!empty ($this->value)
&& !preg_match('/^\s*(\-?\$?|\$\-?)?(\d{1,3}(\,\d{3})*|(\d+))(\.\d{2})?\s*$/', $this->value)) {
$validator->validationError($this->name, _t('Form.VALIDCURRENCY', "Please enter a valid currency"),
"validation", false);
return false;
}
return true;
@ -72,7 +75,8 @@ class CurrencyField_Readonly extends ReadonlyField{
$val = '<i>'._t('CurrencyField.CURRENCYSYMBOL', '$').'0.00</i>';
}
$valforInput = $this->value ? Convert::raw2att($val) : "";
return "<span class=\"readonly ".$this->extraClass()."\" id=\"" . $this->id() . "\">$val</span><input type=\"hidden\" name=\"".$this->name."\" value=\"".$valforInput."\" />";
return "<span class=\"readonly ".$this->extraClass()."\" id=\"" . $this->id() . "\">$val</span>"
. "<input type=\"hidden\" name=\"".$this->name."\" value=\"".$valforInput."\" />";
}
/**
@ -104,7 +108,8 @@ class CurrencyField_Disabled extends CurrencyField{
$val = '<i>'._t('CurrencyField.CURRENCYSYMBOL', '$').'0.00</i>';
}
$valforInput = $this->value ? Convert::raw2att($val) : "";
return "<input class=\"text\" type=\"text\" disabled=\"disabled\" name=\"".$this->name."\" value=\"".$valforInput."\" />";
return "<input class=\"text\" type=\"text\" disabled=\"disabled\""
. " name=\"".$this->name."\" value=\"".$valforInput."\" />";
}
}

View File

@ -342,7 +342,8 @@ class DateField extends TextField {
$validator->validationError(
$this->name,
_t(
'DateField.VALIDDATEMINDATE', "Your date has to be newer or matching the minimum allowed date ({date})",
'DateField.VALIDDATEMINDATE',
"Your date has to be newer or matching the minimum allowed date ({date})",
array('date' => $minDate->toString($this->getConfig('dateformat')))
),
"validation",
@ -361,8 +362,8 @@ class DateField extends TextField {
if(!$this->valueObj->isEarlier($maxDate) && !$this->valueObj->equals($maxDate)) {
$validator->validationError(
$this->name,
_t(
'DateField.VALIDDATEMAXDATE', "Your date has to be older or matching the maximum allowed date ({date})",
_t('DateField.VALIDDATEMAXDATE',
"Your date has to be older or matching the maximum allowed date ({date})",
array('date' => $maxDate->toString($this->getConfig('dateformat')))
),
"validation",
@ -401,13 +402,17 @@ class DateField extends TextField {
case 'min':
$format = $this->getConfig('datavalueformat');
if($val && !Zend_Date::isDate($val, $format) && !strtotime($val)) {
throw new InvalidArgumentException('Date "%s" is not a valid minimum date format (%s) or strtotime() argument', $val, $format);
throw new InvalidArgumentException(
sprintf('Date "%s" is not a valid minimum date format (%s) or strtotime() argument',
$val, $format));
}
break;
case 'max':
$format = $this->getConfig('datavalueformat');
if($val && !Zend_Date::isDate($val, $format) && !strtotime($val)) {
throw new InvalidArgumentException('Date "%s" is not a valid maximum date format (%s) or strtotime() argument', $val, $format);
throw new InvalidArgumentException(
sprintf('Date "%s" is not a valid maximum date format (%s) or strtotime() argument',
$val, $format));
}
break;
}
@ -438,11 +443,13 @@ class DateField_Disabled extends DateField {
public function Field($properties = array()) {
if($this->valueObj) {
if($this->valueObj->isToday()) {
$val = Convert::raw2xml($this->valueObj->toString($this->getConfig('dateformat')) . ' ('._t('DateField.TODAY','today').')');
$val = Convert::raw2xml($this->valueObj->toString($this->getConfig('dateformat'))
. ' ('._t('DateField.TODAY','today').')');
} else {
$df = new Date($this->name);
$df->setValue($this->dataValue());
$val = Convert::raw2xml($this->valueObj->toString($this->getConfig('dateformat')) . ', ' . $df->Ago());
$val = Convert::raw2xml($this->valueObj->toString($this->getConfig('dateformat'))
. ', ' . $df->Ago());
}
} else {
$val = '<i>('._t('DateField.NOTSET', 'not set').')</i>';

View File

@ -9,7 +9,8 @@
* # Configuration
*
* The {@link setConfig()} method is only used to configure common properties of this field.
* To configure the {@link DateField} and {@link TimeField} instances contained within, use their own {@link setConfig()} methods.
* To configure the {@link DateField} and {@link TimeField} instances contained within, use their own
* {@link setConfig()} methods.
*
* Example:
* <code>
@ -141,8 +142,10 @@ class DatetimeField extends FormField {
$this->timeField->setValue($val['time']);
if($this->dateField->dataValue() && $this->timeField->dataValue()) {
$userValueObj = new Zend_Date(null, null, $this->locale);
$userValueObj->setDate($this->dateField->dataValue(), $this->dateField->getConfig('datavalueformat'));
$userValueObj->setTime($this->timeField->dataValue(), $this->timeField->getConfig('datavalueformat'));
$userValueObj->setDate($this->dateField->dataValue(),
$this->dateField->getConfig('datavalueformat'));
$userValueObj->setTime($this->timeField->dataValue(),
$this->timeField->getConfig('datavalueformat'));
if($userTz) $userValueObj->setTimezone($dataTz);
$this->value = $userValueObj->get($this->getConfig('datavalueformat'), $this->locale);
unset($userValueObj);
@ -169,7 +172,8 @@ class DatetimeField extends FormField {
if($this->dateField->getConfig('dmyfields')) {
$this->dateField->setValue($valueObj->toArray());
} else {
$this->dateField->setValue($valueObj->get($this->dateField->getConfig('dateformat'), $this->locale));
$this->dateField->setValue(
$valueObj->get($this->dateField->getConfig('dateformat'), $this->locale));
}
$this->timeField->setValue($valueObj->get($this->timeField->getConfig('timeformat'), $this->locale));
}

View File

@ -110,18 +110,21 @@ class DropdownField extends FormField {
* @param $source An map of the dropdown items
* @param $value The current value
* @param $form The parent form
* @param $emptyString mixed Add an empty selection on to of the {@link $source}-Array
* (can also be boolean, which results in an empty string)
* Argument is deprecated in 3.1, please use {@link setEmptyString()} and/or {@link setHasEmptyDefault(true)} instead.
* @param $emptyString mixed Add an empty selection on to of the {@link $source}-Array (can also be boolean, which
* results in an empty string). Argument is deprecated in 3.1, please use
* {@link setEmptyString()} and/or {@link setHasEmptyDefault(true)} instead.
*/
public function __construct($name, $title=null, $source=array(), $value='', $form=null, $emptyString=null) {
$this->setSource($source);
if($emptyString === true) {
Deprecation::notice('3.1', 'Please use setHasEmptyDefault(true) instead of passing a boolean true $emptyString argument', Deprecation::SCOPE_GLOBAL);
Deprecation::notice('3.1',
'Please use setHasEmptyDefault(true) instead of passing a boolean true $emptyString argument',
Deprecation::SCOPE_GLOBAL);
}
if(is_string($emptyString)) {
Deprecation::notice('3.1', 'Please use setEmptyString() instead of passing a string $emptyString argument.', Deprecation::SCOPE_GLOBAL);
Deprecation::notice('3.1', 'Please use setEmptyString() instead of passing a string emptyString argument.',
Deprecation::SCOPE_GLOBAL);
}
if($emptyString) $this->setHasEmptyDefault(true);

View File

@ -33,7 +33,8 @@ class EmailField extends TextField {
public function validate($validator) {
$this->value = trim($this->value);
$pcrePattern = '^[a-z0-9!#$%&\'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&\'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$';
$pcrePattern = '^[a-z0-9!#$%&\'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&\'*+/=?^_`{|}~-]+)*'
. '@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$';
// PHP uses forward slash (/) to delimit start/end of pattern, so it must be escaped
$pregSafePattern = str_replace('/', '\\/', $pcrePattern);

View File

@ -76,9 +76,13 @@ class FieldList extends ArrayList {
$name = $field->getName();
if(isset($list[$name])) {
$errSuffix = "";
if($this->form) $errSuffix = " in your '{$this->form->class}' form called '" . $this->form->Name() . "'";
else $errSuffix = '';
user_error("collateDataFields() I noticed that a field called '$name' appears twice$errSuffix.", E_USER_ERROR);
if($this->form) {
$errSuffix = " in your '{$this->form->class}' form called '" . $this->form->Name() . "'";
} else {
$errSuffix = '';
}
user_error("collateDataFields() I noticed that a field called '$name' appears twice$errSuffix.",
E_USER_ERROR);
}
$list[$name] = $field;
}
@ -89,8 +93,8 @@ class FieldList extends ArrayList {
* Add an extra field to a tab within this FieldList.
* This is most commonly used when overloading getCMSFields()
*
* @param string $tabName The name of the tab or tabset. Subtabs can be referred to as TabSet.Tab or TabSet.Tab.Subtab.
* This function will create any missing tabs.
* @param string $tabName The name of the tab or tabset. Subtabs can be referred to as TabSet.Tab
* or TabSet.Tab.Subtab. This function will create any missing tabs.
* @param FormField $field The {@link FormField} object to add to the end of that tab.
* @param string $insertBefore The name of the field to insert before. Optional.
*/
@ -110,7 +114,8 @@ class FieldList extends ArrayList {
* Add a number of extra fields to a tab within this FieldList.
* This is most commonly used when overloading getCMSFields()
*
* @param string $tabName The name of the tab or tabset. Subtabs can be referred to as TabSet.Tab or TabSet.Tab.Subtab.
* @param string $tabName The name of the tab or tabset. Subtabs can be referred to as TabSet.Tab
* or TabSet.Tab.Subtab.
* This function will create any missing tabs.
* @param array $fields An array of {@link FormField} objects.
*/
@ -266,7 +271,8 @@ class FieldList extends ArrayList {
$parts = explode('.',$tabName);
$last_idx = count($parts) - 1;
// We could have made this recursive, but I've chosen to keep all the logic code within FieldList rather than add it to TabSet and Tab too.
// We could have made this recursive, but I've chosen to keep all the logic code within FieldList rather than
// add it to TabSet and Tab too.
$currentPointer = $this;
foreach($parts as $k => $part) {
$parentPointer = $currentPointer;
@ -285,7 +291,8 @@ class FieldList extends ArrayList {
}
else {
$withName = ($parentPointer->hasMethod('Name')) ? " named '{$parentPointer->getName()}'" : null;
user_error("FieldList::addFieldToTab() Tried to add a tab to object '{$parentPointer->class}'{$withName} - '$part' didn't exist.", E_USER_ERROR);
user_error("FieldList::addFieldToTab() Tried to add a tab to object"
. " '{$parentPointer->class}'{$withName} - '$part' didn't exist.", E_USER_ERROR);
}
}
}
@ -310,7 +317,8 @@ class FieldList extends ArrayList {
if($child->isComposite()) {
return $child->fieldByName($remainder);
} else {
user_error("Trying to get field '$remainder' from non-composite field $child->class.$name", E_USER_WARNING);
user_error("Trying to get field '$remainder' from non-composite field $child->class.$name",
E_USER_WARNING);
return null;
}
} else {
@ -551,7 +559,8 @@ class FieldList extends ArrayList {
* the children collection. Doesn't work recursively.
*
* @param string|FormField
* @return Position in children collection (first position starts with 0). Returns FALSE if the field can't be found.
* @return int Position in children collection (first position starts with 0). Returns FALSE if the field can't
* be found.
*/
public function fieldPosition($field) {
if(is_object($field)) $field = $field->getName();

View File

@ -189,7 +189,8 @@ class FileIFrameField extends FileField {
try {
$this->upload->loadIntoFile($_FILES['Upload'], $fileObject, $this->folderName);
} catch (Exception $e){
$form->sessionMessage(_t('FileIFrameField.DISALLOWEDFILETYPE', 'This filetype is not allowed to be uploaded'), 'bad');
$form->sessionMessage(_t('FileIFrameField.DISALLOWEDFILETYPE',
'This filetype is not allowed to be uploaded'), 'bad');
$controller->redirectBack();
return;
}

View File

@ -20,7 +20,8 @@
*
* <h2>Validation</h2>
* Each form needs some form of {@link Validator} to trigger the {@link FormField->validate()} methods for each field.
* You can't disable validator for security reasons, because crucial behaviour like extension checks for file uploads depend on it.
* You can't disable validator for security reasons, because crucial behaviour like extension checks for file uploads
* depend on it.
* The default validator is an instance of {@link RequiredFields}.
* If you want to enforce serverside-validation to be ignored for a specific {@link FormField},
* you need to subclass it.
@ -149,15 +150,22 @@ class Form extends RequestHandler {
* @param Controller $controller The parent controller, necessary to create the appropriate form action tag.
* @param String $name The method on the controller that will return this form object.
* @param FieldList $fields All of the fields in the form - a {@link FieldList} of {@link FormField} objects.
* @param FieldList $actions All of the action buttons in the form - a {@link FieldLis} of {@link FormAction} objects
* @param FieldList $actions All of the action buttons in the form - a {@link FieldLis} of
* {@link FormAction} objects
* @param Validator $validator Override the default validator instance (Default: {@link RequiredFields})
*/
public function __construct($controller, $name, FieldList $fields, FieldList $actions, $validator = null) {
parent::__construct();
if(!$fields instanceof FieldList) throw new InvalidArgumentException('$fields must be a valid FieldList instance');
if(!$actions instanceof FieldList) throw new InvalidArgumentException('$fields must be a valid FieldList instance');
if($validator && !$validator instanceof Validator) throw new InvalidArgumentException('$validator must be a Valdidator instance');
if(!$fields instanceof FieldList) {
throw new InvalidArgumentException('$fields must be a valid FieldList instance');
}
if(!$actions instanceof FieldList) {
throw new InvalidArgumentException('$fields must be a valid FieldList instance');
}
if($validator && !$validator instanceof Validator) {
throw new InvalidArgumentException('$validator must be a Valdidator instance');
}
$fields->setForm($this);
$actions->setForm($this);
@ -178,7 +186,9 @@ class Form extends RequestHandler {
// Check if CSRF protection is enabled, either on the parent controller or from the default setting. Note that
// method_exists() is used as some controllers (e.g. GroupTest) do not always extend from Object.
if(method_exists($controller, 'securityTokenEnabled') || (method_exists($controller, 'hasMethod') && $controller->hasMethod('securityTokenEnabled'))) {
if(method_exists($controller, 'securityTokenEnabled') || (method_exists($controller, 'hasMethod')
&& $controller->hasMethod('securityTokenEnabled'))) {
$securityEnabled = $controller->securityTokenEnabled();
} else {
$securityEnabled = SecurityToken::is_enabled();
@ -294,7 +304,8 @@ class Form extends RequestHandler {
sprintf('Action "%s" not allowed on form (Name: "%s")', $funcName, $this->name)
);
}
// TODO : Once we switch to a stricter policy regarding allowed_actions (meaning actions must be set explicitly in allowed_actions in order to run)
// TODO : Once we switch to a stricter policy regarding allowed_actions (meaning actions must be set
// explicitly in allowed_actions in order to run)
// Uncomment the following for checking security against running actions on form fields
/* else {
// Try to find a field that has the action, and allows it
@ -315,7 +326,8 @@ class Form extends RequestHandler {
// Validate the form
if(!$this->validate()) {
if(Director::is_ajax()) {
// Special case for legacy Validator.js implementation (assumes eval'ed javascript collected through FormResponse)
// Special case for legacy Validator.js implementation (assumes eval'ed javascript collected through
// FormResponse)
$acceptType = $request->getHeader('Accept');
if(strpos($acceptType, 'application/json') !== FALSE) {
// Send validation errors back as JSON with a flag at the start
@ -720,7 +732,8 @@ class Form extends RequestHandler {
}
/**
* Set the target of this form to any value - useful for opening the form contents in a new window or refreshing another frame
* Set the target of this form to any value - useful for opening the form contents in a new window or refreshing
* another frame
*
* @param target The value of the target
*/
@ -863,8 +876,8 @@ class Form extends RequestHandler {
/**
* Set the form action attribute to a custom URL.
*
* Note: For "normal" forms, you shouldn't need to use this method. It is recommended only for situations where you have
* two relatively distinct parts of the system trying to communicate via a form post.
* Note: For "normal" forms, you shouldn't need to use this method. It is recommended only for situations where
* you have two relatively distinct parts of the system trying to communicate via a form post.
*/
public function setFormAction($path) {
$this->formActionPath = $path;
@ -1124,9 +1137,9 @@ class Form extends RequestHandler {
)
) {
// We don't actually call the method because it might be slow.
// In a later release, relation methods will just return references to the query that should be executed,
// and so we will be able to safely pass the return value of the
// relation method to the first argument of setValue
// In a later release, relation methods will just return references to the query that should be
// executed, and so we will be able to safely pass the return value of the relation method to the
// first argument of setValue
$val = $data->__get($name);
$hasObjectValue = true;
} else if(strpos($name,'[') && is_array($data) && !isset($data[$name])) {
@ -1289,7 +1302,8 @@ class Form extends RequestHandler {
$content = $this->forTemplate();
$this->IncludeFormTag = true;
$content .= "<input type=\"hidden\" name=\"_form_action\" id=\"" . $this->FormName . "_form_action\" value=\"" . $this->FormAction() . "\" />\n";
$content .= "<input type=\"hidden\" name=\"_form_action\" id=\"" . $this->FormName . "_form_action\""
. " value=\"" . $this->FormAction() . "\" />\n";
$content .= "<input type=\"hidden\" name=\"_form_name\" value=\"" . $this->FormName() . "\" />\n";
$content .= "<input type=\"hidden\" name=\"_form_method\" value=\"" . $this->FormMethod() . "\" />\n";
$content .= "<input type=\"hidden\" name=\"_form_enctype\" value=\"" . $this->FormEncType() . "\" />\n";
@ -1336,8 +1350,8 @@ class Form extends RequestHandler {
/**
* Disable the default button.
* Ordinarily, when a form is processed and no action_XXX button is available, then the first button in the actions list
* will be pressed. However, if this is "delete", for example, this isn't such a good idea.
* Ordinarily, when a form is processed and no action_XXX button is available, then the first button in the
* actions list will be pressed. However, if this is "delete", for example, this isn't such a good idea.
*/
public function disableDefaultAction() {
$this->hasDefaultAction = false;
@ -1485,13 +1499,14 @@ class Form extends RequestHandler {
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// TESTING HELPERS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Test a submission of this form.
* @return SS_HTTPResponse the response object that the handling controller produces. You can interrogate this in your unit test.
* @return SS_HTTPResponse the response object that the handling controller produces. You can interrogate this in
* your unit test.
*/
public function testSubmission($action, $data) {
$data['action_' . $action] = true;
@ -1504,7 +1519,8 @@ class Form extends RequestHandler {
/**
* Test an ajax submission of this form.
* @return SS_HTTPResponse the response object that the handling controller produces. You can interrogate this in your unit test.
* @return SS_HTTPResponse the response object that the handling controller produces. You can interrogate this in
* your unit test.
*/
public function testAjaxSubmission($action, $data) {
$data['ajax'] = 1;

View File

@ -321,7 +321,9 @@ class FormField extends RequestHandler {
* @param $class String
*/
public function removeExtraClass($class) {
if(isset($this->extraClasses) && array_key_exists($class, $this->extraClasses)) unset($this->extraClasses[$class]);
if(isset($this->extraClasses) && array_key_exists($class, $this->extraClasses)) {
unset($this->extraClasses[$class]);
}
return $this;
}
@ -335,9 +337,9 @@ class FormField extends RequestHandler {
* - 'name': {@link setName}
*
* CAUTION Doesn't work on most fields which are composed of more than one HTML form field:
* AjaxUniqueTextField, CheckboxSetField, ComplexTableField, CompositeField, ConfirmedPasswordField, CountryDropdownField,
* CreditCardField, CurrencyField, DateField, DatetimeField, FieldGroup, GridField, HtmlEditorField,
* ImageField, ImageFormAction, InlineFormAction, ListBoxField, etc.
* AjaxUniqueTextField, CheckboxSetField, ComplexTableField, CompositeField, ConfirmedPasswordField,
* CountryDropdownField, CreditCardField, CurrencyField, DateField, DatetimeField, FieldGroup, GridField,
* HtmlEditorField, ImageField, ImageFormAction, InlineFormAction, ListBoxField, etc.
*
* @param string
* @param string
@ -780,8 +782,11 @@ class FormField extends RequestHandler {
public function createTag($tag, $attributes, $content = null) {
$preparedAttributes = '';
foreach($attributes as $k => $v) {
// Note: as indicated by the $k == value item here; the decisions over what to include in the attributes can sometimes get finicky
if(!empty($v) || $v === '0' || $k == 'value') $preparedAttributes .= " $k=\"" . Convert::raw2att($v) . "\"";
// Note: as indicated by the $k == value item here; the decisions over what to include in the attributes
// can sometimes get finicky
if(!empty($v) || $v === '0' || $k == 'value') {
$preparedAttributes .= " $k=\"" . Convert::raw2att($v) . "\"";
}
}
if($content || $tag != 'input') return "<$tag$preparedAttributes>$content</$tag>";
@ -828,7 +833,8 @@ class FormField extends RequestHandler {
}
public function debug() {
return "$this->class ($this->name: $this->title : <font style='color:red;'>$this->message</font>) = $this->value";
return "$this->class ($this->name: $this->title : <font style='color:red;'>$this->message</font>)"
. " = $this->value";
}
/**

View File

@ -115,7 +115,9 @@ class FormScaffolder extends Object {
// only add relational fields if an ID is present
if($this->obj->ID) {
// add has_many relation fields
if($this->obj->has_many() && ($this->includeRelations === true || isset($this->includeRelations['has_many']))) {
if($this->obj->has_many()
&& ($this->includeRelations === true || isset($this->includeRelations['has_many']))) {
foreach($this->obj->has_many() as $relationship => $component) {
if($this->tabbed) {
$relationTab = $fields->findOrMakeTab(
@ -123,7 +125,9 @@ class FormScaffolder extends Object {
$this->obj->fieldLabel($relationship)
);
}
$fieldClass = (isset($this->fieldClasses[$relationship])) ? $this->fieldClasses[$relationship] : 'GridField';
$fieldClass = (isset($this->fieldClasses[$relationship]))
? $this->fieldClasses[$relationship]
: 'GridField';
$grid = Object::create($fieldClass,
$relationship,
$this->obj->fieldLabel($relationship),
@ -138,7 +142,9 @@ class FormScaffolder extends Object {
}
}
if($this->obj->many_many() && ($this->includeRelations === true || isset($this->includeRelations['many_many']))) {
if($this->obj->many_many()
&& ($this->includeRelations === true || isset($this->includeRelations['many_many']))) {
foreach($this->obj->many_many() as $relationship => $component) {
if($this->tabbed) {
$relationTab = $fields->findOrMakeTab(
@ -147,7 +153,10 @@ class FormScaffolder extends Object {
);
}
$fieldClass = (isset($this->fieldClasses[$relationship])) ? $this->fieldClasses[$relationship] : 'GridField';
$fieldClass = (isset($this->fieldClasses[$relationship]))
? $this->fieldClasses[$relationship]
: 'GridField';
$grid = Object::create($fieldClass,
$relationship,
$this->obj->fieldLabel($relationship),

View File

@ -2,9 +2,12 @@
/**
* ComplexTableField designed to edit a has_many join.
*
* This field allows you to show a 1-to-many relation with a group of DataObjects as a (readonly) tabular list. Its most useful when you want to manage the relationship itself thanks the **check boxes** present on each line of the table.
* This field allows you to show a 1-to-many relation with a group of DataObjects as a (readonly) tabular list. Its
* most useful when you want to manage the relationship itself thanks the **check boxes** present on each line of the
* table.
*
* Moreover, you can not do any mistake anymore in the relation by checking a DataObject already linked with another of the parent class.
* Moreover, you can not do any mistake anymore in the relation by checking a DataObject already linked with another
* of the parent class.
*
* See {@link ComplexTableField} for more documentation on the base-class.
*
@ -23,7 +26,8 @@
* );
* </code>
*
* Notice: You still have different ways to customize the popup window as in the parent-class {@link ComplexTableField}.
* Notice: You still have different ways to customize the popup window as in the parent-class
* {@link ComplexTableField}.
*
* @see http://doc.silverstripe.org/tutorial/5-dataobject-relationship-management
*
@ -38,9 +42,11 @@ class HasManyComplexTableField extends ComplexTableField {
protected $addTitle;
protected $htmlListEndName = 'CheckedList'; // If you change the value, do not forget to change it also in the JS file
// If you change the value, do not forget to change it also in the JS file
protected $htmlListEndName = 'CheckedList';
protected $htmlListField = 'selected'; // If you change the value, do not forget to change it also in the JS file
// If you change the value, do not forget to change it also in the JS file
protected $htmlListField = 'selected';
public $template = 'RelationComplexTableField';
@ -48,8 +54,11 @@ class HasManyComplexTableField extends ComplexTableField {
protected $relationAutoSetting = false;
public function __construct($controller, $name, $sourceClass, $fieldList = null, $detailFormFields = null, $sourceFilter = "", $sourceSort = "", $sourceJoin = "") {
parent::__construct($controller, $name, $sourceClass, $fieldList, $detailFormFields, $sourceFilter, $sourceSort, $sourceJoin);
public function __construct($controller, $name, $sourceClass, $fieldList = null, $detailFormFields = null,
$sourceFilter = "", $sourceSort = "", $sourceJoin = "") {
parent::__construct($controller, $name, $sourceClass, $fieldList, $detailFormFields,
$sourceFilter, $sourceSort, $sourceJoin);
Deprecation::notice('3.0', 'Use GridField with GridFieldConfig_RelationEditor', Deprecation::SCOPE_CLASS);
@ -57,7 +66,10 @@ class HasManyComplexTableField extends ComplexTableField {
if($controllerClass = $this->controllerClass()) {
$this->joinField = $this->getParentIdName($controllerClass, $this->sourceClass);
if(!$this->joinField) user_error("Can't find a has_one relationship from '$this->sourceClass' to '$controllerClass'", E_USER_WARNING);
if(!$this->joinField) {
user_error("Can't find a has_one relationship from '$this->sourceClass' to '$controllerClass'",
E_USER_WARNING);
}
} else {
user_error("Can't figure out the data class of $controller", E_USER_WARNING);
}
@ -91,8 +103,10 @@ class HasManyComplexTableField extends ComplexTableField {
$fieldName = $this->name;
$saveDest = $record->$fieldName();
if(! $saveDest)
user_error("HasManyComplexTableField::saveInto() Field '$fieldName' not found on $record->class.$record->ID", E_USER_ERROR);
if(!$saveDest) {
user_error("HasManyComplexTableField::saveInto() Field $fieldName not found on $record->class.$record->ID"
, E_USER_ERROR);
}
$items = array();
@ -154,9 +168,11 @@ class HasManyComplexTableField_Item extends ComplexTableField_Item {
$parentID = $this->parent->getControllerID();
if($this->parent->IsReadOnly || ($joinVal > 0 && $joinVal != $parentID))
return "<input class=\"checkbox\" type=\"checkbox\" name=\"$name\" value=\"{$this->item->ID}\" disabled=\"disabled\"/>";
return "<input class=\"checkbox\" type=\"checkbox\" name=\"$name\" value=\"{$this->item->ID}\"
disabled=\"disabled\"/>";
else if($joinVal == $parentID)
return "<input class=\"checkbox\" type=\"checkbox\" name=\"$name\" value=\"{$this->item->ID}\" checked=\"checked\"/>";
return "<input class=\"checkbox\" type=\"checkbox\" name=\"$name\" value=\"{$this->item->ID}\"
checked=\"checked\"/>";
else
return "<input class=\"checkbox\" type=\"checkbox\" name=\"$name\" value=\"{$this->item->ID}\"/>";
}

View File

@ -2,7 +2,9 @@
/**
* ComplexTableField with a radio button column, designed to edit a has_one join.
*
* This [RelationTable](RelationTable) allows you to show a **1-to-1** or **1-to-many** relation with a group of DataObjects as a (readonly) tabular list (similiar to [ComplexTableField](ComplexTableField)). Its most useful when you want to manage the relationship itself thanks the **radio buttons** present on each line of the table.
* This [RelationTable](RelationTable) allows you to show a **1-to-1** or **1-to-many** relation with a group of
* DataObjects as a (readonly) tabular list (similiar to [ComplexTableField](ComplexTableField)). Its most useful when
* you want to manage the relationship itself thanks the **radio buttons** present on each line of the table.
*
* Moreover, you have the possibility to uncheck a radio button in order to make the relation as null.
*
@ -21,10 +23,13 @@
* );
* </code>
*
* **Notice** : You still have different ways to customize the popup window as in the parent-class [ComplexTableField](ComplexTableField).
* **Notice** : You still have different ways to customize the popup window as in the parent-class
* [ComplexTableField](ComplexTableField).
*
* This field is made to manage a **has_one** relation. In the SilverStripe relation between DataObjects, you can use this relation for **1-to-1** and **1-to-many** relations.
* By default, a HasOneComplexTableField manages a **1-to-many** relation. If you want to specify that the relation that you manage is a **1-to-1** relation, add this code :
* This field is made to manage a **has_one** relation. In the SilverStripe relation between DataObjects, you can use
* this relation for **1-to-1** and **1-to-many** relations.
* By default, a HasOneComplexTableField manages a **1-to-many** relation. If you want to specify that the relation
* that you manage is a **1-to-1** relation, add this code :
*
* <code>
* $tablefield->setOneToOne();
@ -92,9 +97,11 @@ class HasOneComplexTableField_Item extends ComplexTableField_Item {
$childID = $this->item->ID;
if($this->parent->IsReadOnly || ($isOneToOne && $joinVal != $childID && $this->parent->isChildSet($childID)))
return "<input class=\"radio\" type=\"radio\" name=\"$name\" value=\"{$this->item->ID}\" disabled=\"disabled\"/>";
return "<input class=\"radio\" type=\"radio\" name=\"$name\" value=\"{$this->item->ID}\"
disabled=\"disabled\"/>";
else if($joinVal == $childID)
return "<input class=\"radio\" type=\"radio\" name=\"$name\" value=\"{$this->item->ID}\" checked=\"checked\"/>";
return "<input class=\"radio\" type=\"radio\" name=\"$name\" value=\"{$this->item->ID}\"
checked=\"checked\"/>";
else
return "<input class=\"radio\" type=\"radio\" name=\"$name\" value=\"{$this->item->ID}\"/>";
}

View File

@ -20,9 +20,10 @@ class HeaderField extends DatalessField {
$args = func_get_args();
if(!isset($args[1]) || is_numeric($args[1])) {
$title = (isset($args[0])) ? $args[0] : null;
// Use "HeaderField(title)" as the default field name for a HeaderField; if it's just set to title then we risk
// causing accidental duplicate-field creation.
$name = 'HeaderField' . $title; // this means i18nized fields won't be easily accessible through fieldByName()
// Use "HeaderField(title)" as the default field name for a HeaderField; if it's just set to title then we
// risk causing accidental duplicate-field creation.
// this means i18nized fields won't be easily accessible through fieldByName()
$name = 'HeaderField' . $title;
$headingLevel = (isset($args[1])) ? $args[1] : null;
$form = (isset($args[3])) ? $args[3] : null;
}

View File

@ -3,8 +3,9 @@
/**
* A PHP version of TinyMCE's configuration, to allow various parameters to be configured on a site or section basis
*
* There can be multiple HtmlEditorConfig's, which should always be created / accessed using HtmlEditorConfig::get. You can then set
* the currently active config using set_active. Whichever config is active when HtmlEditorField#Field is called wins.
* There can be multiple HtmlEditorConfig's, which should always be created / accessed using HtmlEditorConfig::get.
* You can then set the currently active config using set_active. Whichever config is active when
* HtmlEditorField#Field is called wins.
*
* @author "Hamish Friedlander" <hamish@silverstripe.com>
* @package forms
@ -16,9 +17,12 @@ class HtmlEditorConfig {
static $current = null;
/**
* Get the HtmlEditorConfig object for the given identifier. This is a correct way to get an HtmlEditorConfig instance - do not call 'new'
* Get the HtmlEditorConfig object for the given identifier. This is a correct way to get an HtmlEditorConfig
* instance - do not call 'new'
*
* @param $identifier string - the identifier for the config set
* @return HtmlEditorConfig - the configuration object. This will be created if it does not yet exist for that identifier
* @return HtmlEditorConfig - the configuration object. This will be created if it does not yet exist for that
* identifier
*/
public static function get($identifier = 'default') {
if (!array_key_exists($identifier, self::$configs)) self::$configs[$identifier] = new HtmlEditorConfig();
@ -98,8 +102,11 @@ class HtmlEditorConfig {
* Holder list of buttons, organised by line
*/
protected $buttons = array(
1 => array('bold','italic','underline','strikethrough','separator','justifyleft','justifycenter','justifyright','justifyfull','formatselect','separator','bullist','numlist','outdent','indent','blockquote','hr','charmap'),
2 => array('undo','redo','separator','cut','copy','paste','pastetext','pasteword','spellchecker','separator','advcode','search','replace','selectall','visualaid','separator','tablecontrols'),
1 => array('bold','italic','underline','strikethrough','separator',
'justifyleft','justifycenter','justifyright','justifyfull','formatselect','separator',
'bullist','numlist','outdent','indent','blockquote','hr','charmap'),
2 => array('undo','redo','separator','cut','copy','paste','pastetext','pasteword','spellchecker','separator',
'advcode','search','replace','selectall','visualaid','separator','tablecontrols'),
3 => array()
);
@ -187,7 +194,8 @@ class HtmlEditorConfig {
* Totally re-set the buttons on a given line
*
* @param integer from 1..3 - The line number to redefine
* @param string a string or several strings, or a single array of strings - The button names to make this line contain
* @param string a string or several strings, or a single array of strings - The button names to make this line
* contain
* @return null
*/
public function setButtonsForLine() {
@ -205,7 +213,8 @@ class HtmlEditorConfig {
/**
* Add buttons to the end of a line
* @param integer from 1..3
* @param string a string, or several strings, or a single array of strings - The button names to add to the end of this line
* @param string a string, or several strings, or a single array of strings - The button names to add to the end
* of this line
* @return null
*/
public function addButtonsToLine() {
@ -222,9 +231,11 @@ class HtmlEditorConfig {
/**
* Internal function for adding and removing buttons related to another button
* @param $name string - the name of the button to modify
* @param $offset integer - the offset relative to that button to perform an array_splice at - 0 for before $name, 1 for after
* @param $offset integer - the offset relative to that button to perform an array_splice at - 0 for before $name,
* 1 for after
* @param $del integer - the number of buttons to remove at the position given by index(string) + offset
* @param $add mixed - an array or single item to insert at the position given by index(string) + offset, or null for no insertion
* @param $add mixed - an array or single item to insert at the position given by index(string) + offset,
* or null for no insertion
* @return boolean - true if $name matched a button, false otherwise
*/
protected function modifyButtons($name, $offset, $del=0, $add=null) {
@ -241,7 +252,8 @@ class HtmlEditorConfig {
/**
* Insert buttons before the first occurance of another button
* @param string - the name of the button to insert other buttons before
* @param string a string, or several strings, or a single array of strings - the button names to insert before that button
* @param string a string, or several strings, or a single array of strings - the button names to insert before
* that button
* @return boolean - true if insertion occured, false if it did not (because the given button name was not found)
*/
public function insertButtonsBefore() {
@ -253,7 +265,8 @@ class HtmlEditorConfig {
/**
* Insert buttons after the first occurance of another button
* @param string - the name of the button to insert other buttons after
* @param string a string, or several strings, or a single array of strings - the button names to insert after that button
* @param string a string, or several strings, or a single array of strings - the button names to insert after
* that button
* @return boolean - true if insertion occured, false if it did not (because the given button name was not found)
*/
public function insertButtonsAfter() {

View File

@ -46,7 +46,10 @@ class HtmlEditorField extends TextareaField {
* @see TextareaField::__construct()
*/
public function __construct($name, $title = null, $value = '') {
if(count(func_get_args()) > 3) Deprecation::notice('3.0', 'Use setRows() and setColumns() instead of constructor arguments', Deprecation::SCOPE_GLOBAL);
if(count(func_get_args()) > 3) {
Deprecation::notice('3.0', 'Use setRows() and setColumns() instead of constructor arguments',
Deprecation::SCOPE_GLOBAL);
}
parent::__construct($name, $title, $value);
@ -119,7 +122,8 @@ class HtmlEditorField extends TextareaField {
// clear out any broken link classes
if($class = $link->getAttribute('class')) {
$link->setAttribute('class', preg_replace('/(^ss-broken|ss-broken$| ss-broken )/', null, $class));
$link->setAttribute('class',
preg_replace('/(^ss-broken|ss-broken$| ss-broken )/', null, $class));
}
$linkedPages[] = $ID;
@ -177,7 +181,8 @@ class HtmlEditorField extends TextareaField {
// Save file & link tracking data.
if(class_exists('SiteTree')) {
if($record->ID && $record->many_many('LinkTracking') && $tracker = $record->LinkTracking()) {
$tracker->removeByFilter(sprintf('"FieldName" = \'%s\' AND "SiteTreeID" = %d', $this->name, $record->ID));
$tracker->removeByFilter(sprintf('"FieldName" = \'%s\' AND "SiteTreeID" = %d',
$this->name, $record->ID));
if($linkedPages) foreach($linkedPages as $item) {
$SQL_fieldName = Convert::raw2sql($this->name);
@ -187,7 +192,9 @@ class HtmlEditorField extends TextareaField {
}
if($record->ID && $record->many_many('ImageTracking') && $tracker = $record->ImageTracking()) {
$tracker->where(sprintf('"FieldName" = \'%s\' AND "SiteTreeID" = %d', $this->name, $record->ID))->removeAll();
$tracker->where(
sprintf('"FieldName" = \'%s\' AND "SiteTreeID" = %d', $this->name, $record->ID)
)->removeAll();
$fieldName = $this->name;
if($linkedFiles) foreach($linkedFiles as $item) {
@ -222,7 +229,9 @@ class HtmlEditorField extends TextareaField {
class HtmlEditorField_Readonly extends ReadonlyField {
public function Field($properties = array()) {
$valforInput = $this->value ? Convert::raw2att($this->value) : "";
return "<span class=\"readonly typography\" id=\"" . $this->id() . "\">" . ( $this->value && $this->value != '<p></p>' ? $this->value : '<i>(not set)</i>' ) . "</span><input type=\"hidden\" name=\"".$this->name."\" value=\"".$valforInput."\" />";
return "<span class=\"readonly typography\" id=\"" . $this->id() . "\">"
. ( $this->value && $this->value != '<p></p>' ? $this->value : '<i>(not set)</i>' )
. "</span><input type=\"hidden\" name=\"".$this->name."\" value=\"".$valforInput."\" />";
}
public function Type() {
return 'htmleditorfield readonly';
@ -290,11 +299,13 @@ class HtmlEditorField_Toolbar extends RequestHandler {
* @return Form
*/
public function LinkForm() {
$siteTree = new TreeDropdownField('internal', _t('HtmlEditorField.PAGE', "Page"), 'SiteTree', 'ID', 'MenuTitle', true);
$siteTree = new TreeDropdownField('internal', _t('HtmlEditorField.PAGE', "Page"),
'SiteTree', 'ID', 'MenuTitle', true);
// mimic the SiteTree::getMenuTitle(), which is bypassed when the search is performed
$siteTree->setSearchFunction(array($this, 'siteTreeSearchCallback'));
$numericLabelTmpl = '<span class="step-label"><span class="flyout">%d</span><span class="arrow"></span><strong class="title">%s</strong></span>';
$numericLabelTmpl = '<span class="step-label"><span class="flyout">%d</span><span class="arrow"></span>'
. '<strong class="title">%s</strong></span>';
$form = new Form(
$this->controller,
"{$this->name}/LinkForm",
@ -302,7 +313,8 @@ class HtmlEditorField_Toolbar extends RequestHandler {
$headerWrap = new CompositeField(
new LiteralField(
'Heading',
sprintf('<h3 class="htmleditorfield-mediaform-heading insert">%s</h3>', _t('HtmlEditorField.LINK', 'Insert Link'))
sprintf('<h3 class="htmleditorfield-mediaform-heading insert">%s</h3>',
_t('HtmlEditorField.LINK', 'Insert Link'))
)
),
$contentComposite = new CompositeField(
@ -319,7 +331,8 @@ class HtmlEditorField_Toolbar extends RequestHandler {
'internal'
),
new LiteralField('Step2',
'<div class="step2">' . sprintf($numericLabelTmpl, '2', _t('HtmlEditorField.DETAILS', 'Details')) . '</div>'
'<div class="step2">'
. sprintf($numericLabelTmpl, '2', _t('HtmlEditorField.DETAILS', 'Details')) . '</div>'
),
$siteTree,
new TextField('external', _t('HtmlEditorField.URL', 'URL'), 'http://'),
@ -327,7 +340,8 @@ class HtmlEditorField_Toolbar extends RequestHandler {
new TreeDropdownField('file', _t('HtmlEditorField.FILE', 'File'), 'File', 'ID', 'Title', true),
new TextField('Anchor', _t('HtmlEditorField.ANCHORVALUE', 'Anchor')),
new TextField('Description', _t('HtmlEditorField.LINKDESCR', 'Link description')),
new CheckboxField('TargetBlank', _t('HtmlEditorField.LINKOPENNEWWIN', 'Open link in a new window?')),
new CheckboxField('TargetBlank',
_t('HtmlEditorField.LINKOPENNEWWIN', 'Open link in a new window?')),
new HiddenField('Locale', null, $this->controller->Locale)
)
),
@ -362,7 +376,8 @@ class HtmlEditorField_Toolbar extends RequestHandler {
* @return Form
*/
public function MediaForm() {
// TODO Handle through GridState within field - currently this state set too late to be useful here (during request handling)
// TODO Handle through GridState within field - currently this state set too late to be useful here (during
// request handling)
$parentID = $this->controller->getRequest()->requestVar('ParentID');
$fileFieldConfig = GridFieldConfig::create()->addComponents(
@ -383,10 +398,12 @@ class HtmlEditorField_Toolbar extends RequestHandler {
'Name' => _t('File.Name'),
));
$numericLabelTmpl = '<span class="step-label"><span class="flyout">%d</span><span class="arrow"></span><strong class="title">%s</strong></span>';
$numericLabelTmpl = '<span class="step-label"><span class="flyout">%d</span><span class="arrow"></span>'
. '<strong class="title">%s</strong></span>';
$fromCMS = new CompositeField(
new LiteralField('headerSelect', '<h4>' . sprintf($numericLabelTmpl, '1', _t('HtmlEditorField.FindInFolder', 'Find in Folder')) . '</h4>'),
new LiteralField('headerSelect',
'<h4>'.sprintf($numericLabelTmpl, '1', _t('HtmlEditorField.FindInFolder', 'Find in Folder')).'</h4>'),
$select = new TreeDropdownField('ParentID', "", 'Folder'),
$fileField
);
@ -396,9 +413,11 @@ class HtmlEditorField_Toolbar extends RequestHandler {
$fromWeb = new CompositeField(
new LiteralField('headerURL', '<h4>' . sprintf($numericLabelTmpl, '1', _t('HtmlEditorField.ADDURL', 'Add URL')) . '</h4>'),
new LiteralField('headerURL',
'<h4>' . sprintf($numericLabelTmpl, '1', _t('HtmlEditorField.ADDURL', 'Add URL')) . '</h4>'),
$remoteURL = new TextField('RemoteURL', 'http://'),
new LiteralField('addURLImage', '<button class="action ui-action-constructive ui-button field add-url" data-icon="addMedia"></button>')
new LiteralField('addURLImage',
'<button class="action ui-action-constructive ui-button field add-url" data-icon="addMedia"></button>')
);
$remoteURL->addExtraClass('remoteurl');
@ -431,7 +450,8 @@ class HtmlEditorField_Toolbar extends RequestHandler {
$allFields = new CompositeField(
$tabSet,
new LiteralField('headerEdit', '<h4 class="field header-edit">' . sprintf($numericLabelTmpl, '2', _t('HtmlEditorField.ADJUSTDETAILSDIMENSIONS', 'Details &amp; dimensions')) . '</h4>'),
new LiteralField('headerEdit', '<h4 class="field header-edit">' . sprintf($numericLabelTmpl, '2',
_t('HtmlEditorField.ADJUSTDETAILSDIMENSIONS', 'Details &amp; dimensions')) . '</h4>'),
$editComposite = new CompositeField(
new LiteralField('contentEdit', '<div class="content-edit ss-uploadfield-files files"></div>')
)
@ -442,8 +462,10 @@ class HtmlEditorField_Toolbar extends RequestHandler {
$headings = new CompositeField(
new LiteralField(
'Heading',
sprintf('<h3 class="htmleditorfield-mediaform-heading insert">%s</h3>', _t('HtmlEditorField.INSERTMEDIA', 'Insert Media')).
sprintf('<h3 class="htmleditorfield-mediaform-heading update">%s</h3>', _t('HtmlEditorField.UpdateMEDIA', 'Update Media'))
sprintf('<h3 class="htmleditorfield-mediaform-heading insert">%s</h3>',
_t('HtmlEditorField.INSERTMEDIA', 'Insert Media')).
sprintf('<h3 class="htmleditorfield-mediaform-heading update">%s</h3>',
_t('HtmlEditorField.UpdateMEDIA', 'Update Media'))
)
);
@ -518,7 +540,8 @@ class HtmlEditorField_Toolbar extends RequestHandler {
}
// Instanciate file wrapper and get fields based on its type
// Check if appCategory is an image and exists on the local system, otherwise use oEmbed to refference a remote image
// Check if appCategory is an image and exists on the local system, otherwise use oEmbed to refference a
// remote image
if($file && $file->appCategory() == 'image' && Director::is_site_url($url)) {
$fileWrapper = new HtmlEditorField_Image($url, $file);
} elseif(!Director::is_site_url($url)) {
@ -574,7 +597,8 @@ class HtmlEditorField_Toolbar extends RequestHandler {
}
$previewField = new LiteralField("ImageFull",
"<img id='thumbnailImage' class='thumbnail-preview' src='{$thumbnailURL}?r=" . rand(1,100000) . "' alt='{$file->Name}' />\n"
"<img id='thumbnailImage' class='thumbnail-preview' src='{$thumbnailURL}?r=" . rand(1,100000)
. "' alt='{$file->Name}' />\n"
);
if($file->Width != null){
@ -648,7 +672,8 @@ class HtmlEditorField_Toolbar extends RequestHandler {
$fields = new FieldList(
$dimensionsField = new FieldGroup(_t('HtmlEditorField.IMAGEDIMENSIONS', 'Dimensions'),
$widthField = new TextField('Width', _t('HtmlEditorField.IMAGEWIDTHPX', 'Width'), $file->Width),
$heightField = new TextField('Height', " x " . _t('HtmlEditorField.IMAGEHEIGHTPX', 'Height'), $file->Height)
$heightField = new TextField('Height', " x " . _t('HtmlEditorField.IMAGEHEIGHTPX', 'Height'),
$file->Height)
)
);
$dimensionsField->addExtraClass('dimensions');
@ -672,13 +697,15 @@ class HtmlEditorField_Toolbar extends RequestHandler {
}
$previewField = new LiteralField("ImageFull",
"<img id='thumbnailImage' class='thumbnail-preview' src='{$thumbnailURL}?r=" . rand(1,100000) . "' alt='{$file->Name}' />\n"
"<img id='thumbnailImage' class='thumbnail-preview' src='{$thumbnailURL}?r=" . rand(1,100000)
. "' alt='{$file->Name}' />\n"
);
if($file->Width != null){
$dimensionsField = new FieldGroup(_t('HtmlEditorField.IMAGEDIMENSIONS', 'Dimensions'),
$widthField = new TextField('Width', _t('HtmlEditorField.IMAGEWIDTHPX', 'Width'), $file->Width),
$heightField = new TextField('Height', " x " . _t('HtmlEditorField.IMAGEHEIGHTPX', 'Height'), $file->Height)
$heightField = new TextField('Height', " x " . _t('HtmlEditorField.IMAGEHEIGHTPX', 'Height'),
$file->Height)
);
}
@ -692,23 +719,31 @@ class HtmlEditorField_Toolbar extends RequestHandler {
new ReadonlyField("FileType", _t('AssetTableField.TYPE','File type') . ':', $file->FileType),
new ReadonlyField("Size", _t('AssetTableField.SIZE','File size') . ':', $file->getSize()),
$urlField = new ReadonlyField('ClickableURL', _t('AssetTableField.URL','URL'),
sprintf('<a href="%s" target="_blank" class="file-url">%s</a>', $file->Link(), $file->RelativeLink())
sprintf('<a href="%s" target="_blank" class="file-url">%s</a>',
$file->Link(), $file->RelativeLink())
),
new DateField_Disabled("Created", _t('AssetTableField.CREATED','First uploaded') . ':', $file->Created),
new DateField_Disabled("LastEdited", _t('AssetTableField.LASTEDIT','Last changed') . ':', $file->LastEdited)
new DateField_Disabled("Created", _t('AssetTableField.CREATED','First uploaded') . ':',
$file->Created),
new DateField_Disabled("LastEdited", _t('AssetTableField.LASTEDIT','Last changed') . ':',
$file->LastEdited)
)
)->setName("FilePreviewData")->addExtraClass('cms-file-info-data')
)->setName("FilePreview")->addExtraClass('cms-file-info'),
TextField::create(
'AltText',
_t('HtmlEditorField.IMAGEALT', 'Alternative text (alt)'),
$file->Title,
80
)->setDescription(_t('HtmlEditorField.IMAGEALTTEXTDESC', 'Shown to screen readers or if image can not be displayed')),
)->setDescription(
_t('HtmlEditorField.IMAGEALTTEXTDESC', 'Shown to screen readers or if image can not be displayed')),
TextField::create(
'Title',
_t('HtmlEditorField.IMAGETITLETEXT', 'Title text (tooltip)')
)->setDescription(_t('HtmlEditorField.IMAGETITLETEXTDESC', 'For additional information about the image')),
)->setDescription(
_t('HtmlEditorField.IMAGETITLETEXTDESC', 'For additional information about the image')),
new TextField('CaptionText', _t('HtmlEditorField.CAPTIONTEXT', 'Caption text')),
$alignment = new DropdownField(
'CSSClass',
@ -854,7 +889,7 @@ class HtmlEditorField_Embed extends HtmlEditorField_File {
rawurlencode(_t(
'HtmlEditorField.URLNOTANOEMBEDRESOURCE',
"The URL '{url}' could not be turned into a media resource.",
"The URL that has been passed is not a valid Oembed resource, and the embed element could not be created.",
"The given URL is not a valid Oembed resource; the embed element couldn't be created.",
array('url' => $url)
)));
$controller->response->setStatusCode(404);

View File

@ -5,13 +5,15 @@
*
* <b>Usage</b>
*
* If you want to upload all assets from this field to a given folder you can define the folder in 2 ways. Either in the constructor or as a method on the field
* If you want to upload all assets from this field to a given folder you can define the folder in 2 ways. Either in
* the constructor or as a method on the field
*
* <code>
* $myField = new ImageField("myName", "Upload image below", null, null, null, "myFolder");
* </code>
*
* Will upload images into the assets/myFolder folder. If that folder does not exist it will create it for you. You can also define it as a method
* Will upload images into the assets/myFolder folder. If that folder does not exist it will create it for you. You can
* also define it as a method
*
* <code>
* $myField = new ImageField("myName");
@ -47,7 +49,8 @@ class ImageField extends FileIFrameField {
public function EditFileForm() {
Deprecation::notice('3.0', 'Use UploadField', Deprecation::SCOPE_CLASS);
$filter = create_function('$item', 'return (in_array("Folder", ClassInfo::ancestry($item->ClassName)) || in_array("Image", ClassInfo::ancestry($item->ClassName)));');
$filter = create_function('$item', 'return (in_array("Folder", ClassInfo::ancestry($item->ClassName)) ||'
. ' in_array("Image", ClassInfo::ancestry($item->ClassName)));');
$form = parent::EditFileForm();
$form->Fields()->dataFieldByName('ExistingFile')->setFilterFunction($filter);

View File

@ -17,8 +17,12 @@ class ImageFormAction extends FormAction {
* @param hoverImage The image to display on hover
* @param form The parent form, auto-set when the field is placed inside a form
*/
public function __construct($action, $title = "", $image = "", $hoverImage = null, $className = null, $form = null) {
Deprecation::notice('3.0', "Use FormAction with setAttribute('src', 'myimage.png') and custom JavaScript to achieve hover effect", Deprecation::SCOPE_CLASS);
public function __construct($action, $title = "", $image = "", $hoverImage = null, $className = null,
$form = null) {
Deprecation::notice('3.0',
"Use FormAction with setAttribute('src', 'myimage.png') and custom JavaScript to achieve hover effect",
Deprecation::SCOPE_CLASS);
$this->image = $image;
$this->hoverImage = $hoverImage;
@ -33,6 +37,7 @@ class ImageFormAction extends FormAction {
$classClause = '';
if($this->className) $classClause = $this->className . ' ';
if($this->hoverImage) $classClause .= 'rollover ';
return "<input class=\"{$classClause}action\" id=\"" . $this->id() . "\" type=\"image\" name=\"{$this->name}\" src=\"{$this->image}\" title=\"{$this->title}\" alt=\"{$this->title}\" />";
return "<input class=\"{$classClause}action\" id=\"" . $this->id() . "\" type=\"image\" name=\"{$this->name}\""
. " src=\"{$this->image}\" title=\"{$this->title}\" alt=\"{$this->title}\" />";
}
}

View File

@ -29,10 +29,12 @@ class InlineFormAction extends FormField {
public function Field($properties = array()) {
if($this->includeDefaultJS) {
Requirements::javascriptTemplate(FRAMEWORK_DIR . '/javascript/InlineFormAction.js',array('ID'=>$this->id()));
Requirements::javascriptTemplate(FRAMEWORK_DIR . '/javascript/InlineFormAction.js',
array('ID'=>$this->id()));
}
return "<input type=\"submit\" name=\"action_{$this->name}\" value=\"{$this->title}\" id=\"{$this->id()}\" class=\"action{$this->extraClass}\" />";
return "<input type=\"submit\" name=\"action_{$this->name}\" value=\"{$this->title}\" id=\"{$this->id()}\""
. " class=\"action{$this->extraClass}\" />";
}
public function Title() {
@ -60,7 +62,8 @@ class InlineFormAction_ReadOnly extends FormField {
protected $readonly = true;
public function Field($properties = array()) {
return "<input type=\"submit\" name=\"action_{$this->name}\" value=\"{$this->title}\" id=\"{$this->id()}\" disabled=\"disabled\" class=\"action disabled$this->extraClass\" />";
return "<input type=\"submit\" name=\"action_{$this->name}\" value=\"{$this->title}\" id=\"{$this->id()}\""
. " disabled=\"disabled\" class=\"action disabled$this->extraClass\" />";
}
public function Title() {

View File

@ -135,7 +135,8 @@ class ListboxField extends DropdownField {
public function setSource($source) {
if($source) {
$hasCommas = array_filter(array_keys($source), create_function('$key', 'return strpos($key, ",") !== FALSE;'));
$hasCommas = array_filter(array_keys($source),
create_function('$key', 'return strpos($key, ",") !== FALSE;'));
if($hasCommas) {
throw new InvalidArgumentException('No commas allowed in $source keys');
}
@ -182,7 +183,9 @@ class ListboxField extends DropdownField {
$idList = (is_array($this->value)) ? array_values($this->value) : array();
if(!$record->ID) {
$record->write(); // record needs to have an ID in order to set relationships
$relation = ($fieldname && $record && $record->hasMethod($fieldname)) ? $record->$fieldname() : null;
$relation = ($fieldname && $record && $record->hasMethod($fieldname))
? $record->$fieldname()
: null;
}
$relation->setByIDList($idList);
} elseif($fieldname && $record) {

View File

@ -41,11 +41,13 @@ class ManyManyComplexTableField extends HasManyComplexTableField {
public $itemClass = 'ManyManyComplexTableField_Item';
public function __construct($controller, $name, $sourceClass, $fieldList = null, $detailFormFields = null, $sourceFilter = "", $sourceSort = "", $sourceJoin = "") {
public function __construct($controller, $name, $sourceClass, $fieldList = null, $detailFormFields = null,
$sourceFilter = "", $sourceSort = "", $sourceJoin = "") {
Deprecation::notice('3.0', 'Use GridField with GridFieldConfig_RelationEditor', Deprecation::SCOPE_CLASS);
parent::__construct($controller, $name, $sourceClass, $fieldList, $detailFormFields, $sourceFilter, $sourceSort, $sourceJoin);
parent::__construct($controller, $name, $sourceClass, $fieldList, $detailFormFields,
$sourceFilter, $sourceSort, $sourceJoin);
$classes = array_reverse(ClassInfo::ancestry($this->controllerClass()));
foreach($classes as $class) {
@ -70,7 +72,9 @@ class ManyManyComplexTableField extends HasManyComplexTableField {
$sourceField = 'Child';
$parentID = $this->controller->ID;
$this->sourceJoin .= " LEFT JOIN \"$manyManyTable\" ON (\"$source\".\"ID\" = \"$manyManyTable\".\"{$sourceField}ID\" AND \"{$this->manyManyParentClass}ID\" = '$parentID')";
$this->sourceJoin .= " LEFT JOIN \"$manyManyTable\"
ON (\"$source\".\"ID\" = \"$manyManyTable\".\"{$sourceField}ID\"
AND \"{$this->manyManyParentClass}ID\" = '$parentID')";
$this->joinField = 'Checked';
}
@ -99,9 +103,11 @@ class ManyManyComplexTableField_Item extends ComplexTableField_Item {
$name = $this->parent->getName() . '[]';
if($this->parent->IsReadOnly)
return "<input class=\"checkbox\" type=\"checkbox\" name=\"$name\" value=\"{$this->item->ID}\" disabled=\"disabled\"/>";
return "<input class=\"checkbox\" type=\"checkbox\" name=\"$name\" value=\"{$this->item->ID}\"
disabled=\"disabled\"/>";
else if($this->item->{$this->parent->joinField})
return "<input class=\"checkbox\" type=\"checkbox\" name=\"$name\" value=\"{$this->item->ID}\" checked=\"checked\"/>";
return "<input class=\"checkbox\" type=\"checkbox\" name=\"$name\" value=\"{$this->item->ID}\"
checked=\"checked\"/>";
else
return "<input class=\"checkbox\" type=\"checkbox\" name=\"$name\" value=\"{$this->item->ID}\"/>";
}

View File

@ -26,17 +26,22 @@ class MemberDatetimeOptionsetField extends OptionsetField {
$disabled = ($this->disabled || in_array($key, $this->disabledItems)) ? "disabled=\"disabled\"" : "";
$ATT_key = Convert::raw2att($key);
$options .= "<li class=\"".$extraClass."\"><input id=\"$itemID\" name=\"$this->name\" type=\"radio\" value=\"$key\"$checked $disabled class=\"radio\" /> <label title=\"$ATT_key\" for=\"$itemID\">$value</label></li>\n";
$options .= "<li class=\"".$extraClass."\">"
. "<input id=\"$itemID\" name=\"$this->name\" type=\"radio\" value=\"$key\"$checked $disabled"
. " class=\"radio\" /> <label title=\"$ATT_key\" for=\"$itemID\">$value</label></li>\n";
}
// Add "custom" input field
$value = ($this->value && !array_key_exists($this->value, $this->source)) ? $this->value : null;
$checked = ($value) ? " checked=\"checked\"" : '';
$options .= "<li class=\"valCustom\">"
. sprintf("<input id=\"%s_custom\" name=\"%s\" type=\"radio\" value=\"__custom__\" class=\"radio\" %s />", $itemID, $this->name, $checked)
. sprintf('<label for="%s_custom">%s:</label>', $itemID, _t('MemberDatetimeOptionsetField.Custom', 'Custom'))
. sprintf("<input id=\"%s_custom\" name=\"%s\" type=\"radio\" value=\"__custom__\" class=\"radio\" %s />",
$itemID, $this->name, $checked)
. sprintf('<label for="%s_custom">%s:</label>',
$itemID, _t('MemberDatetimeOptionsetField.Custom', 'Custom'))
. sprintf("<input class=\"customFormat\" name=\"%s_custom\" value=\"%s\" />\n", $this->name, $value)
. sprintf("<input type=\"hidden\" class=\"formatValidationURL\" value=\"%s\" />", $this->Link() . '/validate');
. sprintf("<input type=\"hidden\" class=\"formatValidationURL\" value=\"%s\" />",
$this->Link() . '/validate');
$options .= ($value) ? sprintf(
'<span class="preview">(%s: "%s")</span>',
_t('MemberDatetimeOptionsetField.Preview', 'Preview'),
@ -61,21 +66,39 @@ class MemberDatetimeOptionsetField extends OptionsetField {
*/
public function getFormattingHelpText() {
$output = '<ul>';
$output .= '<li>YYYY = ' . _t('MemberDatetimeOptionsetField.FOURDIGITYEAR', 'Four-digit year', 40, 'Help text describing what "YYYY" means in ISO date formatting') . '</li>';
$output .= '<li>YY = ' . _t('MemberDatetimeOptionsetField.TWODIGITYEAR', 'Two-digit year', 40, 'Help text describing what "YY" means in ISO date formatting') . '</li>';
$output .= '<li>MMMM = ' . _t('MemberDatetimeOptionsetField.FULLNAMEMONTH', 'Full name of month (e.g. June)', 40, 'Help text describing what "MMMM" means in ISO date formatting') . '</li>';
$output .= '<li>MMM = ' . _t('MemberDatetimeOptionsetField.SHORTMONTH', 'Short name of month (e.g. Jun)', 40, 'Help text letting describing what "MMM" means in ISO date formatting') . '</li>';
$output .= '<li>MM = ' . _t('MemberDatetimeOptionsetField.TWODIGITMONTH', 'Two-digit month (01=January, etc.)', 40, 'Help text describing what "MM" means in ISO date formatting') . '</li>';
$output .= '<li>M = ' . _t('MemberDatetimeOptionsetField.MONTHNOLEADING', 'Month digit without leading zero', 40, 'Help text describing what "M" means in ISO date formatting') . '</li>';
$output .= '<li>dd = ' . _t('MemberDatetimeOptionsetField.TWODIGITDAY', 'Two-digit day of month', 40, 'Help text describing what "dd" means in ISO date formatting') . '</li>';
$output .= '<li>d = ' . _t('MemberDatetimeOptionsetField.DAYNOLEADING', 'Day of month without leading zero', 40, 'Help text describing what "d" means in ISO date formatting') . '</li>';
$output .= '<li>hh = ' . _t('MemberDatetimeOptionsetField.TWODIGITHOUR', 'Two digits of hour (00 through 23)', 40, 'Help text describing what "hh" means in ISO date formatting') . '</li>';
$output .= '<li>h = ' . _t('MemberDatetimeOptionsetField.HOURNOLEADING', 'Hour without leading zero', 40, 'Help text describing what "h" means in ISO date formatting') . '</li>';
$output .= '<li>mm = ' . _t('MemberDatetimeOptionsetField.TWODIGITMINUTE', 'Two digits of minute (00 through 59)', 40, 'Help text describing what "mm" means in ISO date formatting') . '</li>';
$output .= '<li>m = ' . _t('MemberDatetimeOptionsetField.MINUTENOLEADING', 'Minute without leading zero', 40, 'Help text describing what "m" means in ISO date formatting') . '</li>';
$output .= '<li>ss = ' . _t('MemberDatetimeOptionsetField.TWODIGITSECOND', 'Two digits of second (00 through 59)', 40, 'Help text describing what "ss" means in ISO date formatting') . '</li>';
$output .= '<li>s = ' . _t('MemberDatetimeOptionsetField.DIGITSDECFRACTIONSECOND', 'One or more digits representing a decimal fraction of a second', 40, 'Help text describing what "s" means in ISO date formatting') . '</li>';
$output .= '<li>a = ' . _t('MemberDatetimeOptionsetField.AMORPM', 'AM (Ante meridiem) or PM (Post meridiem)', 40, 'Help text describing what "a" means in ISO date formatting') . '</li>';
$output .= '<li>YYYY = ' . _t('MemberDatetimeOptionsetField.FOURDIGITYEAR', 'Four-digit year',
40, 'Help text describing what "YYYY" means in ISO date formatting') . '</li>';
$output .= '<li>YY = ' . _t('MemberDatetimeOptionsetField.TWODIGITYEAR', 'Two-digit year',
40, 'Help text describing what "YY" means in ISO date formatting') . '</li>';
$output .= '<li>MMMM = ' . _t('MemberDatetimeOptionsetField.FULLNAMEMONTH', 'Full name of month (e.g. June)',
40, 'Help text describing what "MMMM" means in ISO date formatting') . '</li>';
$output .= '<li>MMM = ' . _t('MemberDatetimeOptionsetField.SHORTMONTH', 'Short name of month (e.g. Jun)',
40, 'Help text letting describing what "MMM" means in ISO date formatting') . '</li>';
$output .= '<li>MM = ' . _t('MemberDatetimeOptionsetField.TWODIGITMONTH', 'Two-digit month (01=January, etc.)',
40, 'Help text describing what "MM" means in ISO date formatting') . '</li>';
$output .= '<li>M = ' . _t('MemberDatetimeOptionsetField.MONTHNOLEADING', 'Month digit without leading zero',
40, 'Help text describing what "M" means in ISO date formatting') . '</li>';
$output .= '<li>dd = ' . _t('MemberDatetimeOptionsetField.TWODIGITDAY', 'Two-digit day of month',
40, 'Help text describing what "dd" means in ISO date formatting') . '</li>';
$output .= '<li>d = ' . _t('MemberDatetimeOptionsetField.DAYNOLEADING', 'Day of month without leading zero',
40, 'Help text describing what "d" means in ISO date formatting') . '</li>';
$output .= '<li>hh = ' . _t('MemberDatetimeOptionsetField.TWODIGITHOUR', 'Two digits of hour (00 through 23)',
40, 'Help text describing what "hh" means in ISO date formatting') . '</li>';
$output .= '<li>h = ' . _t('MemberDatetimeOptionsetField.HOURNOLEADING', 'Hour without leading zero',
40, 'Help text describing what "h" means in ISO date formatting') . '</li>';
$output .= '<li>mm = ' . _t('MemberDatetimeOptionsetField.TWODIGITMINUTE',
'Two digits of minute (00 through 59)',
40, 'Help text describing what "mm" means in ISO date formatting') . '</li>';
$output .= '<li>m = ' . _t('MemberDatetimeOptionsetField.MINUTENOLEADING', 'Minute without leading zero',
40, 'Help text describing what "m" means in ISO date formatting') . '</li>';
$output .= '<li>ss = ' . _t('MemberDatetimeOptionsetField.TWODIGITSECOND',
'Two digits of second (00 through 59)',
40, 'Help text describing what "ss" means in ISO date formatting') . '</li>';
$output .= '<li>s = ' . _t('MemberDatetimeOptionsetField.DIGITSDECFRACTIONSECOND',
'One or more digits representing a decimal fraction of a second',
40, 'Help text describing what "s" means in ISO date formatting') . '</li>';
$output .= '<li>a = ' . _t('MemberDatetimeOptionsetField.AMORPM', 'AM (Ante meridiem) or PM (Post meridiem)',
40, 'Help text describing what "a" means in ISO date formatting') . '</li>';
$output .= '</ul>';
return $output;
}
@ -101,7 +124,8 @@ class MemberDatetimeOptionsetField extends OptionsetField {
return true;
} else {
if($validator) {
$validator->validationError($this->name, _t('MemberDatetimeOptionsetField.DATEFORMATBAD',"Date format is invalid"), "validation", false);
$validator->validationError($this->name,
_t('MemberDatetimeOptionsetField.DATEFORMATBAD',"Date format is invalid"), "validation", false);
}
return false;
}

View File

@ -60,7 +60,9 @@ class MoneyField extends FormField {
$field = new DropdownField(
"{$name}[Currency]",
_t('MoneyField.FIELDLABELCURRENCY', 'Currency'),
ArrayLib::is_associative($allowedCurrencies) ? $allowedCurrencies : array_combine($allowedCurrencies,$allowedCurrencies)
ArrayLib::is_associative($allowedCurrencies)
? $allowedCurrencies
: array_combine($allowedCurrencies,$allowedCurrencies)
);
} else {
$field = new TextField(

View File

@ -1,6 +1,7 @@
<?php
/**
* NullableField is a field that wraps other fields when you want to allow the user to specify whether the value of the field is null or not.
* NullableField is a field that wraps other fields when you want to allow the user to specify whether the value of
* the field is null or not.
*
* The classic case is to wrap a TextField so that the user can distinguish between an empty string and a null string.
* $a = new NullableField(new TextField("Field1", "Field 1", "abc"));
@ -11,7 +12,9 @@
* You can retrieve the value of the wrapped field from the NullableField as follows:
* $field->Value() or $field->dataValue()
*
* You can specify the label to use for the "is null" checkbox. If you want to use I8N for this label then specify it like this:
* You can specify the label to use for the "is null" checkbox. If you want to use I8N for this label then specify it
* like this:
*
* $field->setIsNullLabel(_T(SOME_MODULE_ISNULL_LABEL, "Is Null");
*
* @author Pete Bacon Darwin
@ -44,7 +47,8 @@ class NullableField extends FormField {
// Set a default label if one is not provided.
$this->isNullLabel = _t('NullableField.IsNullLabel', 'Is Null');
}
parent::__construct($valueField->getName(), $valueField->Title(), $valueField->Value(), $valueField->getForm(), $valueField->RightTitle());
parent::__construct($valueField->getName(), $valueField->Title(), $valueField->Value(),
$valueField->getForm(), $valueField->RightTitle());
$this->readonly = $valueField->isReadonly();
}
@ -84,7 +88,8 @@ class NullableField extends FormField {
}
$nullableCheckbox->setValue(is_null($this->dataValue()));
return $this->valueField->Field() . ' ' . $nullableCheckbox->Field() . '&nbsp;<span>' . $this->getIsNullLabel().'</span>';
return $this->valueField->Field() . ' ' . $nullableCheckbox->Field()
. '&nbsp;<span>' . $this->getIsNullLabel().'</span>';
}
/**

View File

@ -11,7 +11,10 @@ class PasswordField extends TextField {
* maxlength
*/
public function __construct($name, $title = null, $value = "") {
if(count(func_get_args()) > 3) Deprecation::notice('3.0', 'Use setMaxLength() instead of constructor arguments', Deprecation::SCOPE_GLOBAL);
if(count(func_get_args()) > 3) {
Deprecation::notice('3.0', 'Use setMaxLength() instead of constructor arguments',
Deprecation::SCOPE_GLOBAL);
}
parent::__construct($name, $title, $value);
}

View File

@ -17,7 +17,8 @@ class PhoneNumberField extends FormField {
protected $countryCode;
protected $ext;
public function __construct( $name, $title = null, $value = '', $extension = null, $areaCode = null, $countryCode = null) {
public function __construct($name, $title = null, $value = '', $extension = null, $areaCode = null,
$countryCode = null) {
$this->areaCode = $areaCode;
$this->ext = $extension;

View File

@ -85,7 +85,9 @@ class RequiredFields extends Validator {
}
if($formField && $error) {
$errorMessage = sprintf(_t('Form.FIELDISREQUIRED', '%s is required'), strip_tags('"' . ($formField->Title() ? $formField->Title() : $fieldName) . '"'));
$errorMessage = sprintf(_t('Form.FIELDISREQUIRED', '%s is required'),
strip_tags('"' . ($formField->Title() ? $formField->Title() : $fieldName) . '"'));
if($msg = $formField->getCustomValidationMessage()) {
$errorMessage = $msg;
}

View File

@ -52,7 +52,8 @@ class SelectionGroup extends CompositeField {
$itemID = $this->ID() . '_' . (++$count);
$extra = array(
"RadioButton" => "<input class=\"selector\" type=\"radio\" id=\"$itemID\" name=\"$this->name\" value=\"$key\"$checked />",
"RadioButton" => "<input class=\"selector\" type=\"radio\" id=\"$itemID\" name=\"$this->name\""
. " value=\"$key\"$checked />",
"RadioLabel" => "<label for=\"$itemID\">$title</label>",
"Selected" => $firstSelected,
);

View File

@ -69,9 +69,15 @@
class SimpleImageField extends FileField {
public function __construct($name, $title = null, $value = null) {
Deprecation::notice('3.0', "SimpleImageField is deprecated. Use UploadField with \$myField->allowedExtensions = array('jpg', 'gif', 'png')", Deprecation::SCOPE_CLASS);
Deprecation::notice('3.0',
"SimpleImageField is deprecated. Use UploadField with "
. "\$myField->allowedExtensions = array('jpg', 'gif', 'png')",
Deprecation::SCOPE_CLASS);
if(count(func_get_args()) > 3) Deprecation::notice('3.0', 'Use setRightTitle() and setFolderName() instead of constructor arguments', Deprecation::SCOPE_GLOBAL);
if(count(func_get_args()) > 3) {
Deprecation::notice('3.0', 'Use setRightTitle() and setFolderName() instead of constructor arguments',
Deprecation::SCOPE_GLOBAL);
}
parent::__construct($name, $title, $value);

View File

@ -126,7 +126,8 @@ class TabSet extends CompositeField {
if($child->isComposite()) {
return $child->fieldByName($remainder);
} else {
user_error("Trying to get field '$remainder' from non-composite field $child->class.$name", E_USER_WARNING);
user_error("Trying to get field '$remainder' from non-composite field $child->class.$name",
E_USER_WARNING);
return null;
}
} else {

View File

@ -22,14 +22,14 @@ class TableField extends TableListField {
protected $fieldList;
/**
* A "Field = Value" filter can be specified by setting $this->filterField and $this->filterValue. This has the advantage of auto-populating
* new records
* A "Field = Value" filter can be specified by setting $this->filterField and $this->filterValue. This has the
* advantage of auto-populating new records
*/
protected $filterField = null;
/**
* A "Field = Value" filter can be specified by setting $this->filterField and $this->filterValue. This has the advantage of auto-populating
* new records
* A "Field = Value" filter can be specified by setting $this->filterField and $this->filterValue. This has the
* advantage of auto-populating new records
*/
protected $filterValue = null;
@ -86,9 +86,12 @@ class TableField extends TableListField {
* @param $name string The fieldname
* @param $sourceClass string The source class of this field
* @param $fieldList array An array of field headings of Fieldname => Heading Text (eg. heading1)
* @param $fieldTypes array An array of field types of fieldname => fieldType (eg. formfield). Do not use for extra data/hiddenfields.
* @param $filterField string The field to filter by. Give the filter value in $sourceFilter. The value will automatically be set on new records.
* @param $sourceFilter string If $filterField has a value, then this is the value to filter by. Otherwise, it is a SQL filter expression.
* @param $fieldTypes array An array of field types of fieldname => fieldType (eg. formfield). Do not use for
* extra data/hiddenfields.
* @param $filterField string The field to filter by. Give the filter value in $sourceFilter. The value will
* automatically be set on new records.
* @param $sourceFilter string If $filterField has a value, then this is the value to filter by. Otherwise, it is
* a SQL filter expression.
* @param $editExisting boolean (Note: Has to stay on this position for legacy reasons)
* @param $sourceSort string
* @param $sourceJoin string
@ -634,7 +637,8 @@ class TableField_Item extends TableListField_Item {
// transformation
if(isset($this->parent->transformationConditions[$origFieldName])) {
$transformation = $this->parent->transformationConditions[$origFieldName]['transformation'];
$rule = str_replace("\$","\$this->item->", $this->parent->transformationConditions[$origFieldName]['rule']);
$rule = str_replace("\$","\$this->item->",
$this->parent->transformationConditions[$origFieldName]['rule']);
$ruleApplies = null;
eval('$ruleApplies = ('.$rule.');');
if($ruleApplies) {

View File

@ -173,7 +173,8 @@ class TableListField extends FormField {
/**
* @var array Definitions for highlighting table-rows with a specific class. You can use all column-names
* in the result of a query. Use in combination with {@setCustomQuery} to select custom properties and joined objects.
* in the result of a query. Use in combination with {@setCustomQuery} to select custom properties and joined
* objects.
*
* Example:
* array(
@ -310,7 +311,9 @@ JS
public function Headings() {
$headings = array();
foreach($this->fieldList as $fieldName => $fieldTitle) {
$isSorted = (isset($_REQUEST['ctf'][$this->getName()]['sort']) && $fieldName == $_REQUEST['ctf'][$this->getName()]['sort']);
$isSorted = (isset($_REQUEST['ctf'][$this->getName()]['sort'])
&& $fieldName == $_REQUEST['ctf'][$this->getName()]['sort']);
// we can't allow sorting with partial summaries (groupByField)
$isSortable = ($this->form && $this->isFieldSortable($fieldName) && !$this->groupByField);
@ -320,17 +323,28 @@ JS
$sortLink = HTTP::setGetVar("ctf[{$this->getName()}][sort]", $fieldName, $sortLink,'&');
// Apply sort direction to the current sort field
if(!empty($_REQUEST['ctf'][$this->getName()]['sort']) && ($_REQUEST['ctf'][$this->getName()]['sort'] == $fieldName)) {
$dir = isset($_REQUEST['ctf'][$this->getName()]['dir']) ? $_REQUEST['ctf'][$this->getName()]['dir'] : null;
$dir = trim(strtolower($dir));
$newDir = ($dir == 'desc') ? null : 'desc';
$sortLink = HTTP::setGetVar("ctf[{$this->getName()}][dir]", Convert::raw2xml($newDir), $sortLink,'&');
if(!empty($_REQUEST['ctf'][$this->getName()]['sort'])
&& ($_REQUEST['ctf'][$this->getName()]['sort'] == $fieldName)) {
if(isset($_REQUEST['ctf'][$this->getName()]['dir'])) {
$dir = $_REQUEST['ctf'][$this->getName()]['dir'];
} else {
$dir = null;
}
if(isset($_REQUEST['ctf'][$this->getName()]['search']) && is_array($_REQUEST['ctf'][$this->getName()]['search'])) {
$dir = trim(strtolower($dir));
$newDir = ($dir == 'desc') ? null : 'desc';
$sortLink = HTTP::setGetVar("ctf[{$this->getName()}][dir]", Convert::raw2xml($newDir),
$sortLink,'&');
}
if(isset($_REQUEST['ctf'][$this->getName()]['search'])
&& is_array($_REQUEST['ctf'][$this->getName()]['search'])) {
foreach($_REQUEST['ctf'][$this->getName()]['search'] as $parameter => $value) {
$XML_search = Convert::raw2xml($value);
$sortLink = HTTP::setGetVar("ctf[{$this->getName()}][search][$parameter]", $XML_search, $sortLink,'&');
$sortLink = HTTP::setGetVar("ctf[{$this->getName()}][search][$parameter]", $XML_search,
$sortLink,'&');
}
}
} else {
@ -339,11 +353,15 @@ JS
$headings[] = new ArrayData(array(
"Name" => $fieldName,
"Title" => ($this->sourceClass()) ? singleton($this->sourceClass())->fieldLabel($fieldTitle) : $fieldTitle,
"Title" => ($this->sourceClass())
? singleton($this->sourceClass())->fieldLabel($fieldTitle)
: $fieldTitle,
"IsSortable" => $isSortable,
"SortLink" => $sortLink,
"SortBy" => $isSorted,
"SortDirection" => (isset($_REQUEST['ctf'][$this->getName()]['dir'])) ? $_REQUEST['ctf'][$this->getName()]['dir'] : null
"SortDirection" => (isset($_REQUEST['ctf'][$this->getName()]['dir']))
? $_REQUEST['ctf'][$this->getName()]['dir']
: null
));
}
return new ArrayList($headings);
@ -403,7 +421,8 @@ JS
}
public function setCustomSourceItems(SS_List $items) {
user_error('TableList::setCustomSourceItems() deprecated, just pass the items into the constructor', E_USER_WARNING);
user_error('TableList::setCustomSourceItems() deprecated, just pass the items into the constructor',
E_USER_WARNING);
// The type-hinting above doesn't seem to work consistently
if($items instanceof SS_List) {
@ -433,8 +452,14 @@ JS
// To disable pagination, set $this->showPagination to false.
if($this->showPagination && $this->pageSize) {
$SQL_limit = (int)$this->pageSize;
if(isset($_REQUEST['ctf'][$this->getName()]['start']) && is_numeric($_REQUEST['ctf'][$this->getName()]['start'])) {
$SQL_start = (isset($_REQUEST['ctf'][$this->getName()]['start'])) ? intval($_REQUEST['ctf'][$this->getName()]['start']) : "0";
if(isset($_REQUEST['ctf'][$this->getName()]['start'])
&& is_numeric($_REQUEST['ctf'][$this->getName()]['start'])) {
if(isset($_REQUEST['ctf'][$this->getName()]['start'])) {
$SQL_start = intval($_REQUEST['ctf'][$this->getName()]['start']);
} else {
$SQL_start = "0";
}
} else {
$SQL_start = 0;
}
@ -519,7 +544,8 @@ JS
* Configure this table to open a popup window
*/
public function setClick_PopupLoad($urlBase) {
$this->clickAction = "var w = window.open(baseHref() + '$urlBase' + this.id.replace(/.*-(\d*)$/,'$1'), 'popup'); w.focus();";
$this->clickAction = "var w = window.open(baseHref()+'$urlBase'+this.id.replace(/.*-(\d*)$/,'$1'), 'popup');"
. " w.focus();";
return $this;
}
@ -779,7 +805,10 @@ JS
public function FirstLink() {
$start = 0;
if(!isset($_REQUEST['ctf'][$this->getName()]['start']) || !is_numeric($_REQUEST['ctf'][$this->getName()]['start']) || $_REQUEST['ctf'][$this->getName()]['start'] == 0) {
if(!isset($_REQUEST['ctf'][$this->getName()]['start'])
|| !is_numeric($_REQUEST['ctf'][$this->getName()]['start'])
|| $_REQUEST['ctf'][$this->getName()]['start'] == 0) {
return null;
}
$baseLink = ($this->paginationBaseLink) ? $this->paginationBaseLink : $this->Link();
@ -799,13 +828,21 @@ JS
}
public function PrevLink() {
$currentStart = isset($_REQUEST['ctf'][$this->getName()]['start']) ? $_REQUEST['ctf'][$this->getName()]['start'] : 0;
if(isset($_REQUEST['ctf'][$this->getName()]['start'])) {
$currentStart = $_REQUEST['ctf'][$this->getName()]['start'];
} else {
$currentStart = 0;
}
if($currentStart == 0) {
return null;
}
$start = ($_REQUEST['ctf'][$this->getName()]['start'] - $this->pageSize < 0) ? 0 : $_REQUEST['ctf'][$this->getName()]['start'] - $this->pageSize;
if($_REQUEST['ctf'][$this->getName()]['start'] - $this->pageSize < 0) {
$start = 0;
} else {
$start = $_REQUEST['ctf'][$this->getName()]['start'] - $this->pageSize;
}
$baseLink = ($this->paginationBaseLink) ? $this->paginationBaseLink : $this->Link();
$link = Controller::join_links($baseLink, "?ctf[{$this->getName()}][start]={$start}");
@ -825,8 +862,12 @@ JS
public function NextLink() {
$currentStart = isset($_REQUEST['ctf'][$this->getName()]['start']) ? $_REQUEST['ctf'][$this->getName()]['start'] : 0;
$start = ($currentStart + $this->pageSize < $this->TotalCount()) ? $currentStart + $this->pageSize : $this->TotalCount() % $this->pageSize > 0;
$currentStart = isset($_REQUEST['ctf'][$this->getName()]['start'])
? $_REQUEST['ctf'][$this->getName()]['start']
: 0;
$start = ($currentStart + $this->pageSize < $this->TotalCount())
? $currentStart + $this->pageSize
: $this->TotalCount() % $this->pageSize > 0;
if($currentStart >= $start-1) {
return null;
}
@ -847,10 +888,14 @@ JS
}
public function LastLink() {
$pageSize = ($this->TotalCount() % $this->pageSize > 0) ? $this->TotalCount() % $this->pageSize : $this->pageSize;
$pageSize = ($this->TotalCount() % $this->pageSize > 0)
? $this->TotalCount() % $this->pageSize
: $this->pageSize;
$start = $this->TotalCount() - $pageSize;
// Check if there is only one page, or if we are on last page
if($this->TotalCount() <= $pageSize || (isset($_REQUEST['ctf'][$this->getName()]['start']) && $_REQUEST['ctf'][$this->getName()]['start'] >= $start)) {
if($this->TotalCount() <= $pageSize || (isset($_REQUEST['ctf'][$this->getName()]['start'])
&& $_REQUEST['ctf'][$this->getName()]['start'] >= $start)) {
return null;
}
@ -872,12 +917,15 @@ JS
public function FirstItem() {
if ($this->TotalCount() < 1) return 0;
return isset($_REQUEST['ctf'][$this->getName()]['start']) ? $_REQUEST['ctf'][$this->getName()]['start'] + 1 : 1;
return isset($_REQUEST['ctf'][$this->getName()]['start'])
? $_REQUEST['ctf'][$this->getName()]['start'] + 1
: 1;
}
public function LastItem() {
if(isset($_REQUEST['ctf'][$this->getName()]['start'])) {
return $_REQUEST['ctf'][$this->getName()]['start'] + min($this->pageSize, $this->TotalCount() - $_REQUEST['ctf'][$this->getName()]['start']);
$pageStep = min($this->pageSize, $this->TotalCount() - $_REQUEST['ctf'][$this->getName()]['start']);
return $_REQUEST['ctf'][$this->getName()]['start'] + $pageStep;
} else {
return min($this->pageSize, $this->TotalCount());
}
@ -1077,7 +1125,8 @@ JS
public function PrintLink() {
$link = Controller::join_links($this->Link(), 'printall');
if(isset($_REQUEST['ctf'][$this->getName()]['sort'])) {
$link = HTTP::setGetVar("ctf[{$this->getName()}][sort]",Convert::raw2xml($_REQUEST['ctf'][$this->getName()]['sort']), $link);
$link = HTTP::setGetVar("ctf[{$this->getName()}][sort]",
Convert::raw2xml($_REQUEST['ctf'][$this->getName()]['sort']), $link);
}
return $link;
}
@ -1165,8 +1214,12 @@ JS
public function CurrentLink() {
$link = $this->Link();
if(isset($_REQUEST['ctf'][$this->getName()]['start']) && is_numeric($_REQUEST['ctf'][$this->getName()]['start'])) {
$start = ($_REQUEST['ctf'][$this->getName()]['start'] < 0) ? 0 : $_REQUEST['ctf'][$this->getName()]['start'];
if(isset($_REQUEST['ctf'][$this->getName()]['start'])
&& is_numeric($_REQUEST['ctf'][$this->getName()]['start'])) {
$start = ($_REQUEST['ctf'][$this->getName()]['start'] < 0)
? 0
: $_REQUEST['ctf'][$this->getName()]['start'];
$link = Controller::join_links($link, "?ctf[{$this->getName()}][start]={$start}");
}
@ -1204,7 +1257,9 @@ JS
/**
* Helper method to determine permissions for a scaffolded
* TableListField (or subclasses) - currently used in {@link ModelAdmin} and {@link DataObject->scaffoldFormFields()}.
* TableListField (or subclasses) - currently used in {@link ModelAdmin} and
* {@link DataObject->scaffoldFormFields()}.
*
* Returns true for each permission that doesn't have an explicit getter.
*
* @todo Temporary method, implement directly in FormField subclasses with object-level permissions.
@ -1346,7 +1401,9 @@ class TableListField_Item extends ViewableData {
// This supports simple FieldName syntax
if(strpos($fieldName,'.') === false) {
$value = ($this->item->XML_val($fieldName) && $xmlSafe) ? $this->item->XML_val($fieldName) : $this->item->RAW_val($fieldName);
$value = ($this->item->XML_val($fieldName) && $xmlSafe)
? $this->item->XML_val($fieldName)
: $this->item->RAW_val($fieldName);
// This support the syntax fieldName = Relation.RelatedField
} else {
$fieldNameParts = explode('.', $fieldName) ;
@ -1477,14 +1534,15 @@ class TableListField_Item extends ViewableData {
$name = $this->parent->getName() . '[]';
if($this->parent->isReadonly())
return "<input class=\"checkbox\" type=\"checkbox\" name=\"$name\" value=\"{$this->item->ID}\" disabled=\"disabled\" />";
return "<input class=\"checkbox\" type=\"checkbox\" name=\"$name\" value=\"{$this->item->ID}\""
. " disabled=\"disabled\" />";
else
return "<input class=\"checkbox\" type=\"checkbox\" name=\"$name\" value=\"{$this->item->ID}\" />";
}
/**
* According to {@link TableListField->selectOptions}, each record will check if the options' key on the object is true,
* if it is true, add the key as a class to the record
* According to {@link TableListField->selectOptions}, each record will check if the options' key on the object is
* true, if it is true, add the key as a class to the record
*
* @return string Value for a 'class' HTML attribute.
*/

View File

@ -40,7 +40,9 @@ class TextareaField extends FormField {
* @param $value The current value
*/
public function __construct($name, $title = null, $value = '') {
if(count(func_get_args()) > 3) Deprecation::notice('3.0', 'Use setRows() and setColumns() instead of constructor arguments');
if(count(func_get_args()) > 3) {
Deprecation::notice('3.0', 'Use setRows() and setColumns() instead of constructor arguments');
}
parent::__construct($name, $title, $value);
}

View File

@ -85,7 +85,9 @@ class TreeDropdownField extends FormField {
* @param bool $showSearch enable the ability to search the tree by
* entering the text in the input field.
*/
public function __construct($name, $title = null, $sourceObject = 'Group', $keyField = 'ID', $labelField = 'Title', $showSearch = false) {
public function __construct($name, $title = null, $sourceObject = 'Group', $keyField = 'ID', $labelField = 'Title',
$showSearch = false) {
$this->sourceObject = $sourceObject;
$this->keyField = $keyField;
$this->labelField = $labelField;
@ -148,9 +150,11 @@ class TreeDropdownField extends FormField {
}
/**
* @param $method The parameter to ChildrenMethod to use when calling Hierarchy->getChildrenAsUL in {@link Hierarchy}.
* The method specified determined the structure of the returned list. Use "ChildFolders" in place of the default
* to get a drop-down listing with only folders, i.e. not including the child elements in the currently selected folder.
* @param $method The parameter to ChildrenMethod to use when calling Hierarchy->getChildrenAsUL in
* {@link Hierarchy}. The method specified determined the structure of the returned list. Use "ChildFolders"
* in place of the default to get a drop-down listing with only folders, i.e. not including the child elements in
* the currently selected folder.
*
* See {@link Hierarchy} for a complete list of possible methods.
*/
public function setChildrenMethod($method) {
@ -221,7 +225,10 @@ class TreeDropdownField extends FormField {
$isSubTree = false;
$this->search = Convert::Raw2SQL($request->requestVar('search'));
$ID = (is_numeric($request->latestparam('ID'))) ? (int)$request->latestparam('ID') : (int)$request->requestVar('ID');
$ID = (is_numeric($request->latestparam('ID')))
? (int)$request->latestparam('ID')
: (int)$request->requestVar('ID');
$forceFullTree = $request->requestVar('forceFullTree')?$request->requestVar('forceFullTree'):false;
if($ID && !$forceFullTree) {
$obj = DataObject::get_by_id($this->sourceObject, $ID);
@ -257,8 +264,9 @@ class TreeDropdownField extends FormField {
$obj->markToExpose($this->objectForKey($value));
}
}
$eval = '"<li id=\"selector-' . $this->getName() . '-{$child->' . $this->keyField . '}\" data-id=\"$child->' . $this->keyField . '\" class=\"class-$child->class"' .
' . $child->markingClasses() . "\"><a rel=\"$child->ID\">" . $child->' . $this->labelField . ' . "</a>"';
$eval = '"<li id=\"selector-' . $this->getName() . '-{$child->' . $this->keyField . '}\" data-id=\"$child->'
. $this->keyField . '\" class=\"class-$child->class"'
. ' . $child->markingClasses() . "\"><a rel=\"$child->ID\">" . $child->' . $this->labelField . ' . "</a>"';
if($isSubTree) {
return substr(trim($obj->getChildrenAsUL('', $eval, null, true, $this->childrenMethod)), 4, -5);
@ -268,9 +276,9 @@ class TreeDropdownField extends FormField {
}
/**
* Marking public function for the tree, which combines different filters sensibly. If a filter function has been set,
* that will be called. If the source is a folder, automatically filter folder. And if search text is set, filter on that
* too. Return true if all applicable conditions are true, false otherwise.
* Marking public function for the tree, which combines different filters sensibly. If a filter function has been
* set, that will be called. If the source is a folder, automatically filter folder. And if search text is set,
* filter on that too. Return true if all applicable conditions are true, false otherwise.
* @param $node
* @return unknown_type
*/
@ -303,7 +311,8 @@ class TreeDropdownField extends FormField {
$this->searchIds[$row->ID] = true;
}
while (!empty($parents)) {
$res = DB::query('SELECT "ParentID", "ID" FROM "' . $this->sourceObject . '" WHERE "ID" in ('.implode(',',array_keys($parents)).')');
$res = DB::query('SELECT "ParentID", "ID" FROM "' . $this->sourceObject
. '" WHERE "ID" in ('.implode(',',array_keys($parents)).')');
$parents = array();
foreach($res as $row) {
@ -333,7 +342,8 @@ class TreeDropdownField extends FormField {
* Changes this field to the readonly field.
*/
public function performReadonlyTransformation() {
return new TreeDropdownField_Readonly($this->name, $this->title, $this->sourceObject, $this->keyField, $this->labelField);
return new TreeDropdownField_Readonly($this->name, $this->title, $this->sourceObject, $this->keyField,
$this->labelField);
}
}

View File

@ -3,16 +3,24 @@
* This formfield represents many-many joins using a tree selector shown in a dropdown styled element
* which can be added to any form usually in the CMS.
*
* This form class allows you to represent Many-Many Joins in a handy single field. The field has javascript which generates a AJAX tree of the site structure allowing you to save selected options to a component set on a given {@link DataObject}.
* This form class allows you to represent Many-Many Joins in a handy single field. The field has javascript which
* generates a AJAX tree of the site structure allowing you to save selected options to a component set on a given
* {@link DataObject}.
*
* <b>Saving</b>
*
* This field saves a {@link ComponentSet} object which is present on the {@link DataObject} passed by the form, returned by calling a function with the same name as the field. The Join is updated by running setByIDList on the {@link ComponentSet}
* This field saves a {@link ComponentSet} object which is present on the {@link DataObject} passed by the form,
* returned by calling a function with the same name as the field. The Join is updated by running setByIDList on the
* {@link ComponentSet}
*
* <b>Customizing Save Behaviour</b>
*
* Before the data is saved, you can modify the ID list sent to the {@link ComponentSet} by specifying a function on the {@link DataObject} called "onChange[fieldname](&items)". This will be passed by reference the IDlist (an array of ID's) from the Treefield to be saved to the component set.
* Returning false on this method will prevent treemultiselect from saving to the {@link ComponentSet} of the given {@link DataObject}
* Before the data is saved, you can modify the ID list sent to the {@link ComponentSet} by specifying a function on
* the {@link DataObject} called "onChange[fieldname](&items)". This will be passed by reference the IDlist (an array
* of ID's) from the Treefield to be saved to the component set.
*
* Returning false on this method will prevent treemultiselect from saving to the {@link ComponentSet} of the given
* {@link DataObject}
*
* <code>
* // Called when we try and set the Parents() component set
@ -111,7 +119,8 @@ class TreeMultiselectField extends TreeDropdownField {
'div',
array (
'id' => "TreeDropdownField_{$this->id()}",
'class' => 'TreeDropdownField multiple' . ($this->extraClass() ? " {$this->extraClass()}" : '') . ($this->showSearch ? " searchable" : ''),
'class' => 'TreeDropdownField multiple' . ($this->extraClass() ? " {$this->extraClass()}" : '')
. ($this->showSearch ? " searchable" : ''),
'data-url-tree' => $this->form ? $this->Link('tree') : "",
'data-title' => $title,
'title' => $this->getDescription()
@ -140,7 +149,10 @@ class TreeMultiselectField extends TreeDropdownField {
$fieldName = $this->name;
$saveDest = $record->$fieldName();
if(!$saveDest) user_error("TreeMultiselectField::saveInto() Field '$fieldName' not found on $record->class.$record->ID", E_USER_ERROR);
if(!$saveDest) {
user_error("TreeMultiselectField::saveInto() Field '$fieldName' not found on"
. " $record->class.$record->ID", E_USER_ERROR);
}
if($this->value) {
$items = preg_split("/ *, */", trim($this->value));
@ -163,10 +175,13 @@ class TreeMultiselectField extends TreeDropdownField {
* Changes this field to the readonly field.
*/
public function performReadonlyTransformation() {
$field = new TreeMultiselectField_Readonly($this->name, $this->title, $this->sourceObject, $this->keyField, $this->labelField);
$field = new TreeMultiselectField_Readonly($this->name, $this->title, $this->sourceObject,
$this->keyField, $this->labelField);
$field->addExtraClass($this->extraClass());
$field->setForm($this->form);
$field->setValue($this->value);
return $field;
}
}

View File

@ -11,7 +11,8 @@
* - Image thumbnail/file icons even before upload finished
* - Saving into relations
* - Edit file
* - allowedExtensions is by default File::$allowed_extensions<li>maxFileSize the value of min(upload_max_filesize, post_max_size) from php.ini
* - allowedExtensions is by default File::$allowed_extensions<li>maxFileSize the value of min(upload_max_filesize,
* post_max_size) from php.ini
*
* @example <code>
* $UploadField = new UploadField('myFiles', 'Please upload some images <span>(max. 5 files)</span>');
@ -65,7 +66,8 @@ class UploadField extends FileField {
protected $items;
/**
* Config for this field used in both, php and javascript (will be merged into the config of the javascript file upload plugin)
* Config for this field used in both, php and javascript (will be merged into the config of the javascript file
* upload plugin)
* @var array
*/
protected $ufConfig = array(
@ -74,8 +76,8 @@ class UploadField extends FileField {
*/
'autoUpload' => true,
/**
* php validation of allowedMaxFileNumber only works when a db relation is available, set to null to allow unlimited
* if record has a has_one and allowedMaxFileNumber is null, it will be set to 1
* php validation of allowedMaxFileNumber only works when a db relation is available, set to null to allow
* unlimited if record has a has_one and allowedMaxFileNumber is null, it will be set to 1
* @var int
*/
'allowedMaxFileNumber' => null,
@ -122,8 +124,8 @@ class UploadField extends FileField {
/**
* @param string $name The internal field name, passed to forms.
* @param string $title The field label.
* @param SS_List $items If no items are defined, the field will try to auto-detect an existing relation on {@link $record},
* with the same name as the field name.
* @param SS_List $items If no items are defined, the field will try to auto-detect an existing relation on
* @link $record}, with the same name as the field name.
* @param Form $form Reference to the container form
*/
public function __construct($name, $title = null, SS_List $items = null) {
@ -135,8 +137,11 @@ class UploadField extends FileField {
if($items) $this->setItems($items);
$this->getValidator()->setAllowedExtensions(array_filter(File::$allowed_extensions)); // filter out '' since this would be a regex problem on JS end
$this->getValidator()->setAllowedMaxFileSize(min(File::ini2bytes(ini_get('upload_max_filesize')), File::ini2bytes(ini_get('post_max_size')))); // get the lower max size
// filter out '' since this would be a regex problem on JS end
$this->getValidator()->setAllowedExtensions(array_filter(File::$allowed_extensions));
// get the lower max size
$this->getValidator()->setAllowedMaxFileSize(min(File::ini2bytes(ini_get('upload_max_filesize')),
File::ini2bytes(ini_get('post_max_size'))));
}
/**
@ -182,7 +187,8 @@ class UploadField extends FileField {
return $this;
}
/**
* Get the record to use as "Parent" for uploaded Files (eg a Page with a has_one to File) If none is set, it will use Form->getRecord() or Form->Controller()->data()
* Get the record to use as "Parent" for uploaded Files (eg a Page with a has_one to File) If none is set, it will
* use Form->getRecord() or Form->Controller()->data()
* @return DataObject
*/
public function getRecord() {
@ -216,7 +222,8 @@ class UploadField extends FileField {
// Try to auto-detect relationship
if ($record && $record->exists()) {
if ($record->has_many($name) || $record->many_many($name)) {
// Ensure relationship is cast to an array, as we can't alter the items of a DataList/RelationList (see below)
// Ensure relationship is cast to an array, as we can't alter the items of a DataList/RelationList
// (see below)
$this->items = $record->{$name}()->toArray();
} elseif($record->has_one($name)) {
$item = $record->{$name}();
@ -225,7 +232,8 @@ class UploadField extends FileField {
}
}
$this->items = new ArrayList($this->items);
// hack to provide $UploadFieldThumbnailURL, $hasRelation and $UploadFieldEditLink in template for each file
// hack to provide $UploadFieldThumbnailURL, $hasRelation and $UploadFieldEditLink in template for each
// file
if ($this->items->exists()) {
foreach ($this->items as $i=>$file) {
$this->items[$i] = $this->customiseFile($file);
@ -286,11 +294,14 @@ class UploadField extends FileField {
protected function getThumbnailURLForFile(File $file) {
if ($file && $file->exists() && file_exists(Director::baseFolder() . '/' . $file->getFilename())) {
if ($file->hasMethod('getThumbnail')) {
return $file->getThumbnail($this->getConfig('previewMaxWidth'), $this->getConfig('previewMaxHeight'))->getURL();
return $file->getThumbnail($this->getConfig('previewMaxWidth'),
$this->getConfig('previewMaxHeight'))->getURL();
} elseif ($file->hasMethod('getThumbnailURL')) {
return $file->getThumbnailURL($this->getConfig('previewMaxWidth'), $this->getConfig('previewMaxHeight'));
return $file->getThumbnailURL($this->getConfig('previewMaxWidth'),
$this->getConfig('previewMaxHeight'));
} elseif ($file->hasMethod('SetRatioSize')) {
return $file->SetRatioSize($this->getConfig('previewMaxWidth'), $this->getConfig('previewMaxHeight'))->getURL();
return $file->SetRatioSize($this->getConfig('previewMaxWidth'),
$this->getConfig('previewMaxHeight'))->getURL();
} else {
return $file->Icon();
}
@ -713,7 +724,9 @@ class UploadField_ItemHandler extends RequestHandler {
$record = $this->parent->getRecord();
$id = $this->getItem()->ID;
if ($id && $record && $record->exists()) {
if (($record->has_many($fieldName) || $record->many_many($fieldName)) && $file = $record->{$fieldName}()->byID($id)) {
if (($record->has_many($fieldName) || $record->many_many($fieldName))
&& $file = $record->{$fieldName}()->byID($id)) {
$record->{$fieldName}()->remove($file);
$response->setStatusCode(200);
} elseif($record->has_one($fieldName) && $record->{$fieldName . 'ID'} == $id) {

View File

@ -75,7 +75,8 @@ abstract class Validator extends Object {
*
* @param $fieldName name of the field
* @param $message error message to display
* @param $messageType optional parameter, gets loaded into the HTML class attribute in the rendered output. See {@link getErrors()} for details.
* @param $messageType optional parameter, gets loaded into the HTML class attribute in the rendered output.
* See {@link getErrors()} for details.
*/
public function validationError($fieldName, $message, $messageType='') {
$this->errors[] = array(
@ -103,10 +104,14 @@ abstract class Validator extends Object {
public function requireField($fieldName, $data) {
if(is_array($data[$fieldName]) && count($data[$fieldName])) {
foreach($data[$fieldName] as $componentkey => $componentVal){
if(!strlen($componentVal)) $this->validationError($fieldName, "$fieldName $componentkey is required", "required");
if(!strlen($componentVal)) {
$this->validationError($fieldName, "$fieldName $componentkey is required", "required");
}
}
}else if(!strlen($data[$fieldName])) $this->validationError($fieldName, "$fieldName is required", "required");
} else if(!strlen($data[$fieldName])) {
$this->validationError($fieldName, "$fieldName is required", "required");
}
}
/**

View File

@ -29,13 +29,22 @@ class GridField extends FormField {
'gridFieldAlterAction'
);
/** @var SS_List - the datasource */
/**
* The datasource
* @var SS_List
*/
protected $list = null;
/** @var string - the classname of the DataObject that the GridField will display. Defaults to the value of $this->list->dataClass */
/**
* The classname of the DataObject that the GridField will display. Defaults to the value of $this->list->dataClass
* @var string
*/
protected $modelClassName = '';
/** @var GridState - the current state of the GridField */
/**
* the current state of the GridField
* @var GridState
*/
protected $state = null;
/**
@ -118,7 +127,8 @@ class GridField extends FormField {
if($class) return $class;
}
throw new LogicException('GridField doesn\'t have a modelClassName, so it doesn\'t know the columns of this grid.');
throw new LogicException('GridField doesn\'t have a modelClassName,'
. ' so it doesn\'t know the columns of this grid.');
}
/**
@ -281,7 +291,8 @@ class GridField extends FormField {
$fragmentDefined[$fragmentName] = true;
$fragment = isset($content[$fragmentName]) ? $content[$fragmentName] : "";
// If the fragment still has a fragment definition in it, when we should defer this item until later.
// If the fragment still has a fragment definition in it, when we should defer this item until
// later.
if(preg_match('/\$DefineFragment\(([a-z0-9\-_]+)\)/i', $fragment, $matches)) {
// If we've already deferred this fragment, then we have a circular dependency
if(isset($fragmentDeferred[$k]) && $fragmentDeferred[$k] > 5) {
@ -299,7 +310,8 @@ class GridField extends FormField {
}
break;
} else {
$content[$k] = preg_replace('/\$DefineFragment\(' . $fragmentName . '\)/i', $fragment, $content[$k]);
$content[$k] = preg_replace('/\$DefineFragment\(' . $fragmentName . '\)/i', $fragment,
$content[$k]);
}
}
}
@ -308,8 +320,10 @@ class GridField extends FormField {
// Check for any undefined fragments, and if so throw an exception
// While we're at it, trim whitespace off the elements
foreach($content as $k => $v) {
if(empty($fragmentDefined[$k])) throw new LogicException("GridField HTML fragment '$k' was given content, " .
"but not defined. Perhaps there is a supporting GridField component you need to add?");
if(empty($fragmentDefined[$k])) {
throw new LogicException("GridField HTML fragment '$k' was given content,"
. " but not defined. Perhaps there is a supporting GridField component you need to add?");
}
}
$total = $list->count();
@ -351,14 +365,23 @@ class GridField extends FormField {
$content['body'] = $this->createTag(
'tr',
array("class" => 'ss-gridfield-item ss-gridfield-no-items'),
$this->createTag('td', array('colspan' => count($columns)), _t('GridField.NoItemsFound', 'No items found'))
$this->createTag(
'td',
array('colspan' => count($columns)),
_t('GridField.NoItemsFound', 'No items found'))
);
}
// Turn into the relevant parts of a table
$head = $content['header'] ? $this->createTag('thead', array(), $content['header']) : '';
$body = $content['body'] ? $this->createTag('tbody', array('class' => 'ss-gridfield-items'), $content['body']) : '';
$foot = $content['footer'] ? $this->createTag('tfoot', array(), $content['footer']) : '';
$head = $content['header']
? $this->createTag('thead', array(), $content['header'])
: '';
$body = $content['body']
? $this->createTag('tbody', array('class' => 'ss-gridfield-items'), $content['body'])
: '';
$foot = $content['footer']
? $this->createTag('tfoot', array(), $content['footer'])
: '';
$this->addExtraClass('ss-gridfield field');
$attrs = array_diff_key(
@ -483,10 +506,12 @@ class GridField extends FormField {
foreach($this->columnDispatch[$column] as $handler) {
$column_attrs = $handler->getColumnAttributes($this, $record, $column);
if(is_array($column_attrs))
if(is_array($column_attrs)) {
$attrs = array_merge($attrs, $column_attrs);
elseif($column_attrs)
throw new LogicException("Non-array response from " . get_class($handler) . "::getColumnAttributes()");
} elseif($column_attrs) {
$methodSignature = get_class($handler) . "::getColumnAttributes()";
throw new LogicException("Non-array response from $methodSignature.");
}
}
return $attrs;
@ -515,10 +540,12 @@ class GridField extends FormField {
foreach($this->columnDispatch[$column] as $handler) {
$column_metadata = $handler->getColumnMetadata($this, $column);
if(is_array($column_metadata))
if(is_array($column_metadata)) {
$metadata = array_merge($metadata, $column_metadata);
else
throw new LogicException("Non-array response from " . get_class($handler) . "::getColumnMetadata()");
} else {
$methodSignature = get_class($handler) . "::getColumnMetadata()";
throw new LogicException("Non-array response from $methodSignature.");
}
}
@ -665,7 +692,9 @@ class GridField extends FormField {
return $result;
}
if($this !== $result && !$request->isEmptyPattern($rule) && is_object($result) && $result instanceof RequestHandler) {
if($this !== $result && !$request->isEmptyPattern($rule) && is_object($result)
&& $result instanceof RequestHandler) {
$returnValue = $result->handleRequest($request, $model);
if(is_array($returnValue)) {
@ -678,9 +707,10 @@ class GridField extends FormField {
} else if($request->allParsed()) {
return $result;
// But if we have more content on the URL and we don't know what to do with it, return an error.
// But if we have more content on the URL and we don't know what to do with it, return an error
} else {
return $this->httpError(404, "I can't handle sub-URLs of a " . get_class($result) . " object.");
return $this->httpError(404,
"I can't handle sub-URLs of a " . get_class($result) . " object.");
}
}
}

View File

@ -7,7 +7,8 @@
* Often used alongside {@link GridFieldRemoveButton} for detaching existing records from a relatinship.
* For easier setup, have a look at a sample configuration in {@link GridFieldConfig_RelationEditor}.
*/
class GridFieldAddExistingAutocompleter implements GridField_HTMLProvider, GridField_ActionProvider, GridField_DataManipulator, GridField_URLHandler {
class GridFieldAddExistingAutocompleter
implements GridField_HTMLProvider, GridField_ActionProvider, GridField_DataManipulator, GridField_URLHandler {
/**
* Which template to use for rendering
@ -72,18 +73,25 @@ class GridFieldAddExistingAutocompleter implements GridField_HTMLProvider, GridF
$forTemplate = new ArrayData(array());
$forTemplate->Fields = new ArrayList();
$searchFields = ($this->getSearchFields()) ? $this->getSearchFields() : $this->scaffoldSearchFields($dataClass);
$searchFields = ($this->getSearchFields())
? $this->getSearchFields()
: $this->scaffoldSearchFields($dataClass);
$value = $this->findSingleEntry($gridField, $searchFields, $searchState, $dataClass);
$searchField = new TextField('gridfield_relationsearch', _t('GridField.RelationSearch', "Relation search"), $value);
$searchField = new TextField('gridfield_relationsearch',
_t('GridField.RelationSearch', "Relation search"), $value);
// Apparently the data-* needs to be double qouted for the jQuery.meta data plugin
$searchField->setAttribute('data-search-url', '\''.Controller::join_links($gridField->Link('search').'\''));
$searchField->setAttribute('placeholder', $this->getPlaceholderText($dataClass));
$searchField->addExtraClass('relation-search no-change-track');
$findAction = new GridField_FormAction($gridField, 'gridfield_relationfind', _t('GridField.Find', "Find"), 'find', 'find');
$findAction = new GridField_FormAction($gridField, 'gridfield_relationfind',
_t('GridField.Find', "Find"), 'find', 'find');
$findAction->setAttribute('data-icon', 'relationfind');
$addAction = new GridField_FormAction($gridField, 'gridfield_relationadd', _t('GridField.LinkExisting', "Link Existing"), 'addto', 'addto');
$addAction = new GridField_FormAction($gridField, 'gridfield_relationadd',
_t('GridField.LinkExisting', "Link Existing"), 'addto', 'addto');
$addAction->setAttribute('data-icon', 'chain--plus');
// If an object is not found, disable the action
@ -175,17 +183,20 @@ class GridFieldAddExistingAutocompleter implements GridField_HTMLProvider, GridF
$dataClass = $gridField->getList()->dataClass();
$allList = $this->searchList ? $this->searchList : DataList::create($dataClass);
$searchFields = ($this->getSearchFields()) ? $this->getSearchFields() : $this->scaffoldSearchFields($dataClass);
$searchFields = ($this->getSearchFields())
? $this->getSearchFields()
: $this->scaffoldSearchFields($dataClass);
if(!$searchFields) {
throw new LogicException(
sprintf('GridFieldAddExistingAutocompleter: No searchable fields could be found for class "%s"', $dataClass)
);
sprintf('GridFieldAddExistingAutocompleter: No searchable fields could be found for class "%s"',
$dataClass));
}
// TODO Replace with DataList->filterAny() once it correctly supports OR connectives
$stmts = array();
foreach($searchFields as $searchField) {
$stmts[] .= sprintf('"%s" LIKE \'%s%%\'', $searchField, Convert::raw2sql($request->getVar('gridfield_relationsearch')));
$stmts[] .= sprintf('"%s" LIKE \'%s%%\'', $searchField,
Convert::raw2sql($request->getVar('gridfield_relationsearch')));
}
$results = $allList->where(implode(' OR ', $stmts))->subtract($gridField->getList());
$results = $results->sort($searchFields[0], 'ASC');
@ -260,7 +271,9 @@ class GridFieldAddExistingAutocompleter implements GridField_HTMLProvider, GridF
* @return String
*/
public function getPlaceholderText($dataClass) {
$searchFields = ($this->getSearchFields()) ? $this->getSearchFields() : $this->scaffoldSearchFields($dataClass);
$searchFields = ($this->getSearchFields())
? $this->getSearchFields()
: $this->scaffoldSearchFields($dataClass);
if($this->placeholderText) {
return $this->placeholderText;

View File

@ -23,7 +23,8 @@ class GridFieldAddNewButton implements GridField_HTMLProvider {
public function getHTMLFragments($gridField) {
if(!$this->buttonName) {
// provide a default button name, can be changed by calling {@link setButtonName()} on this component
$this->buttonName = _t('GridField.Add', 'Add {name}', array('name' => singleton($gridField->getModelClass())->i18n_singular_name()));
$objectName = singleton($gridField->getModelClass())->i18n_singular_name();
$this->buttonName = _t('GridField.Add', 'Add {name}', array('name' => $objectName));
}
$data = new ArrayData(array(

View File

@ -52,7 +52,8 @@ class GridFieldDataColumns implements GridField_ColumnProvider {
*/
public function setDisplayFields($fields) {
if(!is_array($fields)) {
throw new InvalidArgumentException('Arguments passed to GridFieldDataColumns::setDisplayFields() must be an array');
throw new InvalidArgumentException('
Arguments passed to GridFieldDataColumns::setDisplayFields() must be an array');
}
$this->displayFields = $fields;
return $this;

View File

@ -98,7 +98,8 @@ class GridFieldDeleteAction implements GridField_ColumnProvider, GridField_Actio
*/
public function getColumnContent($gridField, $record, $columnName) {
if($this->removeRelation) {
$field = GridField_FormAction::create($gridField, 'UnlinkRelation'.$record->ID, false, "unlinkrelation", array('RecordID' => $record->ID))
$field = GridField_FormAction::create($gridField, 'UnlinkRelation'.$record->ID, false,
"unlinkrelation", array('RecordID' => $record->ID))
->addExtraClass('gridfield-button-unlink')
->setAttribute('title', _t('GridAction.UnlinkRelation', "Unlink"))
->setAttribute('data-icon', 'chain--minus');
@ -106,7 +107,8 @@ class GridFieldDeleteAction implements GridField_ColumnProvider, GridField_Actio
if(!$record->canDelete()) {
return;
}
$field = GridField_FormAction::create($gridField, 'DeleteRecord'.$record->ID, false, "deleterecord", array('RecordID' => $record->ID))
$field = GridField_FormAction::create($gridField, 'DeleteRecord'.$record->ID, false, "deleterecord",
array('RecordID' => $record->ID))
->addExtraClass('gridfield-button-delete')
->setAttribute('title', _t('GridAction.Delete', "Delete"))
->setAttribute('data-icon', 'cross-circle')
@ -131,7 +133,8 @@ class GridFieldDeleteAction implements GridField_ColumnProvider, GridField_Actio
return;
}
if($actionName == 'deleterecord' && !$item->canDelete()) {
throw new ValidationException(_t('GridFieldAction_Delete.DeletePermissionsFailure',"No delete permissions"),0);
throw new ValidationException(
_t('GridFieldAction_Delete.DeletePermissionsFailure',"No delete permissions"),0);
}
if($actionName == 'deleterecord') {
$item->delete();

View File

@ -223,7 +223,8 @@ class GridFieldDetailForm_ItemRequest extends RequestHandler {
}
public function Link($action = null) {
return Controller::join_links($this->gridField->Link('item'), $this->record->ID ? $this->record->ID : 'new', $action);
return Controller::join_links($this->gridField->Link('item'),
$this->record->ID ? $this->record->ID : 'new', $action);
}
public function view($request) {
@ -290,22 +291,29 @@ class GridFieldDetailForm_ItemRequest extends RequestHandler {
$actions = new FieldList();
if($this->record->ID !== 0) {
$actions->push(FormAction::create('doSave', _t('GridFieldDetailForm.Save', 'Save'))
->setUseButtonTag(true)->addExtraClass('ss-ui-action-constructive')->setAttribute('data-icon', 'accept'));
->setUseButtonTag(true)
->addExtraClass('ss-ui-action-constructive')
->setAttribute('data-icon', 'accept'));
$actions->push(FormAction::create('doDelete', _t('GridFieldDetailForm.Delete', 'Delete'))
->addExtraClass('ss-ui-action-destructive'));
}else{ // adding new record
//Change the Save label to 'Create'
$actions->push(FormAction::create('doSave', _t('GridFieldDetailForm.Create', 'Create'))
->setUseButtonTag(true)->addExtraClass('ss-ui-action-constructive')->setAttribute('data-icon', 'add'));
->setUseButtonTag(true)
->addExtraClass('ss-ui-action-constructive')
->setAttribute('data-icon', 'add'));
// Add a Cancel link which is a button-like link and link back to one level up.
$curmbs = $this->Breadcrumbs();
if($curmbs && $curmbs->count()>=2){
$one_level_up = $curmbs->offsetGet($curmbs->count()-2);
$text = sprintf(
"<a class=\"crumb ss-ui-button ss-ui-action-destructive cms-panel-link ui-corner-all\" href=\"%s\">%s</a>",
$one_level_up->Link,
_t('GridFieldDetailForm.CancelBtn', 'Cancel')
"<a class=\"%s\" href=\"%s\">%s</a>",
"crumb ss-ui-button ss-ui-action-destructive cms-panel-link ui-corner-all", // CSS classes
$one_level_up->Link, // url
_t('GridFieldDetailForm.CancelBtn', 'Cancel') // label
);
$actions->push(new LiteralField('cancelbutton', $text));
}
@ -419,7 +427,8 @@ class GridFieldDetailForm_ItemRequest extends RequestHandler {
try {
$toDelete = $this->record;
if (!$toDelete->canDelete()) {
throw new ValidationException(_t('GridFieldDetailForm.DeletePermissionsFailure',"No delete permissions"),0);
throw new ValidationException(
_t('GridFieldDetailForm.DeletePermissionsFailure',"No delete permissions"),0);
}
$toDelete->delete();

View File

@ -99,7 +99,9 @@ class GridFieldExportButton implements GridField_HTMLProvider, GridField_ActionP
*/
public function generateExportFileData($gridField) {
$separator = $this->csvSeparator;
$csvColumns = ($this->exportColumns) ? $this->exportColumns : singleton($gridField->getModelClass())->summaryFields();
$csvColumns = ($this->exportColumns)
? $this->exportColumns
: singleton($gridField->getModelClass())->summaryFields();
$fileData = '';
$columnData = array();
$fieldItems = new ArrayList();

View File

@ -43,7 +43,8 @@ class GridFieldFilterHeader implements GridField_HTMLProvider, GridField_DataMan
return true;
} else {
if($this->throwExceptionOnBadDataType) {
throw new LogicException(get_class($this) . " expects an SS_Filterable list to be passed to the GridField.");
throw new LogicException(
get_class($this) . " expects an SS_Filterable list to be passed to the GridField.");
}
return false;
}
@ -123,7 +124,8 @@ class GridFieldFilterHeader implements GridField_HTMLProvider, GridField_DataMan
$field->addExtraClass('ss-gridfield-sort');
$field->addExtraClass('no-change-track');
$field->setAttribute('placeholder', _t('GridField.FilterBy', "Filter by ") . _t('GridField.'.$metadata['title'], $metadata['title']));
$field->setAttribute('placeholder',
_t('GridField.FilterBy', "Filter by ") . _t('GridField.'.$metadata['title'], $metadata['title']));
$field = new FieldGroup(
$field,

View File

@ -62,7 +62,8 @@ class GridFieldPaginator implements GridField_HTMLProvider, GridField_DataManipu
return true;
} else {
if($this->throwExceptionOnBadDataType) {
throw new LogicException(get_class($this) . " expects an SS_Limitable list to be passed to the GridField.");
throw new LogicException(
get_class($this) . " expects an SS_Limitable list to be passed to the GridField.");
}
return false;
}
@ -170,14 +171,16 @@ class GridFieldPaginator implements GridField_HTMLProvider, GridField_DataManipu
// Previous page button
$previousPageNum = $state->currentPage <= 1 ? 1 : $state->currentPage - 1;
$previousPage = new GridField_FormAction($gridField, 'pagination_prev', 'Previous', 'paginate', $previousPageNum);
$previousPage = new GridField_FormAction($gridField, 'pagination_prev', 'Previous',
'paginate', $previousPageNum);
$previousPage->addExtraClass('ss-gridfield-previouspage');
if($state->currentPage == 1)
$previousPage = $previousPage->performDisabledTransformation();
// Next page button
$nextPageNum = $state->currentPage >= $totalPages ? $totalPages : $state->currentPage + 1;
$nextPage = new GridField_FormAction($gridField, 'pagination_next', 'Next', 'paginate', $nextPageNum);
$nextPage = new GridField_FormAction($gridField, 'pagination_next', 'Next',
'paginate', $nextPageNum);
$nextPage->addExtraClass('ss-gridfield-nextpage');
if($state->currentPage == $totalPages)
$nextPage = $nextPage->performDisabledTransformation();
@ -204,7 +207,9 @@ class GridFieldPaginator implements GridField_HTMLProvider, GridField_DataManipu
));
}
return array(
'footer' => $forTemplate->renderWith('GridFieldPaginator_Row', array('Colspan'=>count($gridField->getColumns()))),
'footer' => $forTemplate->renderWith('GridFieldPaginator_Row', array(
'Colspan'=>count($gridField->getColumns())
)),
);
}

View File

@ -91,7 +91,9 @@ class GridFieldPrintButton implements GridField_HTMLProvider, GridField_ActionPr
* Export core.
*/
public function generatePrintData($gridField) {
$printColumns = ($this->printColumns) ? $this->printColumns : singleton($gridField->getModelClass())->summaryFields();
$printColumns = ($this->printColumns)
? $this->printColumns
: singleton($gridField->getModelClass())->summaryFields();
$header = null;
if($this->printHasHeader){
$header = new ArrayList();

View File

@ -46,7 +46,8 @@ class GridFieldSortableHeader implements GridField_HTMLProvider, GridField_DataM
return true;
} else {
if($this->throwExceptionOnBadDataType) {
throw new LogicException(get_class($this) . " expects an SS_Sortable list to be passed to the GridField.");
throw new LogicException(
get_class($this) . " expects an SS_Sortable list to be passed to the GridField.");
}
return false;
}
@ -86,7 +87,9 @@ class GridFieldSortableHeader implements GridField_HTMLProvider, GridField_DataM
$currentColumn++;
$metadata = $gridField->getColumnMetadata($columnField);
$title = $metadata['title'];
if(isset($this->fieldSorting[$columnField]) && $this->fieldSorting[$columnField]) $columnField = $this->fieldSorting[$columnField];
if(isset($this->fieldSorting[$columnField]) && $this->fieldSorting[$columnField]) {
$columnField = $this->fieldSorting[$columnField];
}
if($title && $gridField->getList()->canSortBy($columnField)) {
$dir = 'asc';
if($state->SortColumn == $columnField && $state->SortDirection == 'asc') {
@ -107,8 +110,11 @@ class GridFieldSortableHeader implements GridField_HTMLProvider, GridField_DataM
$field->addExtraClass('ss-gridfield-sorted-desc');
}
} else {
if($currentColumn == count($columns) && $gridField->getConfig()->getComponentByType('GridFieldFilterHeader')){
$field = new LiteralField($columnField, '<button name="showFilter" class="ss-gridfield-button-filter trigger"></button>');
if($currentColumn == count($columns)
&& $gridField->getConfig()->getComponentByType('GridFieldFilterHeader')){
$field = new LiteralField($columnField,
'<button name="showFilter" class="ss-gridfield-button-filter trigger"></button>');
} else {
$field = new LiteralField($columnField, '<span class="non-sortable">' . $title . '</span>');
}

View File

@ -646,7 +646,8 @@ class i18n extends Object implements TemplateGlobalProvider {
'sq' => array('Albanian', 'shqip'),
'ar' => array('Arabic', '&#1575;&#1604;&#1593;&#1585;&#1576;&#1610;&#1577;'),
'eu' => array('Basque', 'euskera'),
'be' => array('Belarusian', '&#1041;&#1077;&#1083;&#1072;&#1088;&#1091;&#1089;&#1082;&#1072;&#1103; &#1084;&#1086;&#1074;&#1072;'),
'be' => array('Belarusian',
'&#1041;&#1077;&#1083;&#1072;&#1088;&#1091;&#1089;&#1082;&#1072;&#1103; &#1084;&#1086;&#1074;&#1072;'),
'bn' => array('Bengali', '&#2476;&#2494;&#2434;&#2482;&#2494;'),
'bg' => array('Bulgarian', '&#1073;&#1098;&#1083;&#1075;&#1072;&#1088;&#1089;&#1082;&#1080;'),
'ca' => array('Catalan', 'catal&agrave;'),
@ -738,7 +739,8 @@ class i18n extends Object implements TemplateGlobalProvider {
'sq_AL' => array('Albanian', 'shqip'),
'ar_EG' => array('Arabic', '&#1575;&#1604;&#1593;&#1585;&#1576;&#1610;&#1577;'),
'eu_ES' => array('Basque', 'euskera'),
'be_BY' => array('Belarusian', '&#1041;&#1077;&#1083;&#1072;&#1088;&#1091;&#1089;&#1082;&#1072;&#1103; &#1084;&#1086;&#1074;&#1072;'),
'be_BY' => array('Belarusian',
'&#1041;&#1077;&#1083;&#1072;&#1088;&#1091;&#1089;&#1082;&#1072;&#1103; &#1084;&#1086;&#1074;&#1072;'),
'bn_BD' => array('Bengali', '&#2476;&#2494;&#2434;&#2482;&#2494;'),
'bg_BG' => array('Bulgarian', '&#1073;&#1098;&#1083;&#1075;&#1072;&#1088;&#1089;&#1082;&#1080;'),
'ca_ES' => array('Catalan', 'catal&agrave;'),
@ -1451,15 +1453,20 @@ class i18n extends Object implements TemplateGlobalProvider {
);
/**
* This is the main translator function. Returns the string defined by $class and $entity according to the currently set locale.
* This is the main translator function. Returns the string defined by $class and $entity according to the
* currently set locale.
*
* @param string $entity Entity that identifies the string. It must be in the form "Namespace.Entity" where Namespace will be usually
* the class name where this string is used and Entity identifies the string inside the namespace.
* @param string $string The original string itself. In a usual call this is a mandatory parameter, but if you are reusing a string which
* has already been "declared" (using another call to this function, with the same class and entity), you can omit it.
* @param string $context (optional) If the string can be difficult to translate by any reason, you can help translators with some more info using this param
* @param string injectionArray (optional) array of key value pairs that are used to replace corresponding expressions in {curly brackets} in the $string.
* The injection array can also be used as the their argument to the _t() function
* @param string $entity Entity that identifies the string. It must be in the form "Namespace.Entity" where
* Namespace will be usually the class name where this string is used and Entity identifies
* the string inside the namespace.
* @param string $string The original string itself. In a usual call this is a mandatory parameter, but if you are
* reusing a string which has already been "declared" (using another call to this function,
* with the same class and entity), you can omit it.
* @param string $context (optional) If the string can be difficult to translate by any reason, you can help
* translators with some more info using this param
* @param string injectionArray (optional) array of key value pairs that are used to replace corresponding
* expressions in {curly brackets} in the $string. The injection array can also be
* used as the their argument to the _t() function
* @return string The translated string, according to the currently set locale {@link i18n::set_locale()}
*/
public static function _t($entity, $string = "", $context = "", $injection = "") {
@ -1772,7 +1779,8 @@ class i18n extends Object implements TemplateGlobalProvider {
if(
is_dir($moduleDir)
&& is_file($moduleDir . DIRECTORY_SEPARATOR . "_config.php")
&& is_file($moduleDir . DIRECTORY_SEPARATOR . "lang" . DIRECTORY_SEPARATOR . self::$default_locale . ".php")
&& is_file($moduleDir . DIRECTORY_SEPARATOR . "lang" . DIRECTORY_SEPARATOR
. self::$default_locale . ".php")
) {
$translatableModules[] = $module;
}
@ -1874,7 +1882,9 @@ class i18n extends Object implements TemplateGlobalProvider {
* for example in the {@link CMSMain} interface the Member locale
* overrules the global locale value set here.
*
* @param string $locale Locale to be set. See http://unicode.org/cldr/data/diff/supplemental/languages_and_territories.html for a list of possible locales.
* @param string $locale Locale to be set. See
* http://unicode.org/cldr/data/diff/supplemental/languages_and_territories.html for a list
* of possible locales.
*/
public static function set_locale($locale) {
if ($locale) self::$current_locale = $locale;
@ -1996,7 +2006,8 @@ class i18n extends Object implements TemplateGlobalProvider {
// and the next invocation of include_by_locale() doesn't cause a new reparse.
$adapter->addTranslation(
array(
'content' => array($locale => $locale), // Cached by content hash, so needs to be locale dependent
// Cached by content hash, so needs to be locale dependent
'content' => array($locale => $locale),
'locale' => $locale,
'usetranslateadapter' => true
)

View File

@ -87,7 +87,10 @@ class i18nTextCollector extends Object {
$themes = scandir($this->basePath."/themes");
if(count($themes)){
foreach($themes as $theme) {
if(is_dir($this->basePath."/themes/".$theme) && substr($theme,0,1) != '.' && is_dir($this->basePath."/themes/".$theme."/templates")){
if(is_dir($this->basePath."/themes/".$theme)
&& substr($theme,0,1) != '.'
&& is_dir($this->basePath."/themes/".$theme."/templates")){
$themeFolders[] = 'themes/'.$theme;
}
}
@ -103,7 +106,8 @@ class i18nTextCollector extends Object {
$modules = array_merge($modules, $themeFolders);
foreach($modules as $module) {
// Only search for calls in folder with a _config.php file (which means they are modules, including themes folder)
// Only search for calls in folder with a _config.php file (which means they are modules, including
// themes folder)
$isValidModuleFolder = (
is_dir("$this->basePath/$module")
&& is_file("$this->basePath/$module/_config.php")
@ -297,7 +301,10 @@ class i18nTextCollector extends Object {
if(!$filePath) $filePath = SSViewer::getTemplateFileByType($includeName, 'main');
if($filePath) {
$includeContent = file_get_contents($filePath);
$entities = array_merge($entities,(array)$this->collectFromTemplate($includeContent, $module, $includeFileName));
$entities = array_merge(
$entities,
(array)$this->collectFromTemplate($includeContent, $module, $includeFileName)
);
}
// @todo Will get massively confused if you include the includer -> infinite loop
}
@ -431,11 +438,12 @@ class i18nTextCollector extends Object {
*/
interface i18nTextCollector_Writer {
/**
* @param Array $entities Map of entity names (incl. namespace) to an numeric array,
* with at least one element, the original string, and an optional second element, the context.
* @param Array $entities Map of entity names (incl. namespace) to an numeric array, with at least one element,
* the original string, and an optional second element, the context.
* @param String $locale
* @param String $path The directory base on which the collector should create new lang folders and files.
* Usually the webroot set through {@link Director::baseFolder()}. Can be overwritten for testing or export purposes.
* Usually the webroot set through {@link Director::baseFolder()}. Can be overwritten for
* testing or export purposes.
* @return Boolean success
*/
public function write($entities, $locale, $path);
@ -467,14 +475,16 @@ class i18nTextCollector_Writer_Php implements i18nTextCollector_Writer {
try{
eval($php);
} catch(Exception $e) {
throw new LogicException('i18nTextCollector->writeMasterStringFile(): Invalid PHP language file. Error: ' . $e->toString());
throw new LogicException(
'i18nTextCollector->writeMasterStringFile(): Invalid PHP language file. Error: ' . $e->toString());
}
fwrite($fh, "<"."?php{$eol}{$eol}global \$lang;{$eol}{$eol}" . $php . "{$eol}");
fclose($fh);
} else {
throw new LogicException("Cannot write language file! Please check permissions of $langFolder/" . $locale . ".php");
throw new LogicException("Cannot write language file! Please check permissions of $langFolder/"
. $locale . ".php");
}
return true;
@ -497,7 +507,11 @@ class i18nTextCollector_Writer_Php implements i18nTextCollector_Writer {
// namespace might contain dots, so we implode back
$namespace = implode('.',$entityParts);
} else {
user_error("i18nTextCollector::langArrayCodeForEntitySpec(): Wrong entity format for $entityFullName with values" . var_export($entitySpec, true), E_USER_WARNING);
user_error(
"i18nTextCollector::langArrayCodeForEntitySpec(): Wrong entity format for $entityFullName with values "
. var_export($entitySpec, true),
E_USER_WARNING
);
return false;
}
@ -541,7 +555,8 @@ class i18nTextCollector_Writer_RailsYaml implements i18nTextCollector_Writer {
public function getYaml($entities, $locale) {
// Use the Zend copy of this script to prevent class conflicts when RailsYaml is included
require_once 'thirdparty/zend_translate_railsyaml/library/Translate/Adapter/thirdparty/sfYaml/lib/sfYamlDumper.php';
require_once 'thirdparty/zend_translate_railsyaml/library/Translate/Adapter/thirdparty/sfYaml/lib'
. '/sfYamlDumper.php';
// Unflatten array
$entitiesNested = array();

View File

@ -10,6 +10,7 @@ if(typeof(ss) == 'undefined' || typeof(ss.i18n) == 'undefined') {
'UNIQUEFIELD.ENTERNEWVALUE': 'U zult een nieuwe waarde voor dit veld moeten invoeren',
'UNIQUEFIELD.CANNOTLEAVEEMPTY': 'Dit veld mag niet leeg blijven',
'RESTRICTEDTEXTFIELD.CHARCANTBEUSED': "Het karakter '%s' mag niet gebruikt worden in dit veld",
'UPDATEURL.CONFIRM': 'Wilt u de URL wijzigen naar:\n\n%s/\n\nKlik Ok om de URL te wijzigen, Klik Cancel om het te laten zoals het is:\n\n%s'
'UPDATEURL.CONFIRM': 'Wilt u de URL wijzigen naar:\n\n%s/\n\nKlik Ok om de URL te wijzigen, Klik Cancel om het'
+ ' te laten zoals het is:\n\n%s'
});
}

View File

@ -139,7 +139,8 @@ class Aggregate_Relationship extends Aggregate {
*
* @param DataObject $object The object that has_many somethings that we're calculating the aggregate for
* @param string $relationship The name of the relationship
* @param string $filter (optional) An SQL filter to apply to the relationship rows before calculating the aggregate
* @param string $filter (optional) An SQL filter to apply to the relationship rows before calculating the
* aggregate
*/
public function __construct($object, $relationship, $filter = '') {
$this->object = $object;
@ -148,7 +149,10 @@ class Aggregate_Relationship extends Aggregate {
$this->has_many = $object->has_many($relationship);
$this->many_many = $object->many_many($relationship);
if (!$this->has_many && !$this->many_many) user_error("Could not find relationship $relationship on object class {$object->class} in Aggregate Relationship", E_USER_ERROR);
if (!$this->has_many && !$this->many_many) {
user_error("Could not find relationship $relationship on object class {$object->class} in"
. " Aggregate Relationship", E_USER_ERROR);
}
parent::__construct($this->has_many ? $this->has_many : $this->many_many[1], $filter);
}

View File

@ -107,7 +107,8 @@ class ArrayList extends ViewableData implements SS_List, SS_Filterable, SS_Sorta
* @return ArrayList
*/
public function getRange($offset, $length) {
Deprecation::notice("3.0", 'getRange($offset, $length) is deprecated. Use limit($length, $offset) instead. Note the new argument order.');
Deprecation::notice("3.0", 'getRange($offset, $length) is deprecated. Use limit($length, $offset) instead.'
. ' Note the new argument order.');
return $this->limit($length, $offset);
}
@ -331,7 +332,9 @@ class ArrayList extends ViewableData implements SS_List, SS_Filterable, SS_Sorta
// One argument and it's a string
if(count($args)==1 && is_string($args[0])){
$column = $args[0];
if(strpos($column, ' ') !== false) throw new InvalidArgumentException("You can't pass SQL fragments to sort()");
if(strpos($column, ' ') !== false) {
throw new InvalidArgumentException("You can't pass SQL fragments to sort()");
}
$columnsToSort[$column] = SORT_ASC;
} else if(count($args)==2){
@ -391,7 +394,8 @@ class ArrayList extends ViewableData implements SS_List, SS_Filterable, SS_Sorta
* @example $list->filter('Name', array('aziz', 'bob'); // aziz and bob in list
* @example $list->filter(array('Name'=>'bob, 'Age'=>21)); // bob with the Age 21 in list
* @example $list->filter(array('Name'=>'bob, 'Age'=>array(21, 43))); // bob with the Age 21 or 43
* @example $list->filter(array('Name'=>array('aziz','bob'), 'Age'=>array(21, 43))); // aziz with the age 21 or 43 and bob with the Age 21 or 43
* @example $list->filter(array('Name'=>array('aziz','bob'), 'Age'=>array(21, 43)));
* // aziz with the age 21 or 43 and bob with the Age 21 or 43
*/
public function filter() {
if(count(func_get_args())>2){
@ -447,7 +451,8 @@ class ArrayList extends ViewableData implements SS_List, SS_Filterable, SS_Sorta
* @example $list->exclude('Name', array('aziz', 'bob'); // exclude aziz and bob from list
* @example $list->exclude(array('Name'=>'bob, 'Age'=>21)); // exclude bob that has Age 21
* @example $list->exclude(array('Name'=>'bob, 'Age'=>array(21, 43))); // exclude bob with Age 21 or 43
* @example $list->exclude(array('Name'=>array('bob','phil'), 'Age'=>array(21, 43))); // bob age 21 or 43, phil age 21 or 43 would be excluded
* @example $list->exclude(array('Name'=>array('bob','phil'), 'Age'=>array(21, 43)));
* // bob age 21 or 43, phil age 21 or 43 would be excluded
*/
public function exclude() {
if(count(func_get_args())>2){

View File

@ -4,6 +4,7 @@
*/
class ComponentSet extends DataObjectSet {
public function setComponentInfo($type, $ownerObj, $ownerClass, $tableName, $childClass, $joinField = null) {
Deprecation::notice('3.0', 'ComponentSet is deprecated. Use ManyManyList or HasManyList instead.', Deprecation::SCOPE_CLASS);
Deprecation::notice('3.0', 'ComponentSet is deprecated. Use ManyManyList or HasManyList instead.',
Deprecation::SCOPE_CLASS);
}
}

View File

@ -79,7 +79,8 @@ class DB {
* Connect to a database.
* Given the database configuration, this method will create the correct subclass of SS_Database,
* and set it as the global connection.
* @param array $database A map of options. The 'type' is the name of the subclass of SS_Database to use. For the rest of the options, see the specific class.
* @param array $database A map of options. The 'type' is the name of the subclass of SS_Database to use. For the
* rest of the options, see the specific class.
*/
public static function connect($databaseConfig) {
// This is used by TestRunner::startsession() to test up a test session using an alt
@ -242,7 +243,9 @@ class DB {
* control over the index.
* @param string $options SQL statement to append to the CREATE TABLE call.
*/
public static function requireTable($table, $fieldSchema = null, $indexSchema = null, $hasAutoIncPK=true, $options = null, $extensions=null) {
public static function requireTable($table, $fieldSchema = null, $indexSchema = null, $hasAutoIncPK=true,
$options = null, $extensions=null) {
return self::getConn()->requireTable($table, $fieldSchema, $indexSchema, $hasAutoIncPK, $options, $extensions);
}

View File

@ -1,6 +1,6 @@
<?php
/**
An extension that adds additional functionality to a {@link DataObject}.
* An extension that adds additional functionality to a {@link DataObject}.
*
* @package framework
* @subpackage model
@ -41,7 +41,9 @@ abstract class DataExtension extends Extension {
$statics = Injector::inst()->get($extension, true, $args)->$extraStaticsMethod($class, $extension);
if ($statics) {
Deprecation::notice('3.1.0', "$extraStaticsMethod deprecated. Just define statics on your extension, or use get_extra_config", Deprecation::SCOPE_GLOBAL);
Deprecation::notice('3.1.0',
"$extraStaticsMethod deprecated. Just define statics on your extension, or use get_extra_config",
Deprecation::SCOPE_GLOBAL);
return $statics;
}
}

View File

@ -3,12 +3,14 @@
* Implements a "lazy loading" DataObjectSet.
* Uses {@link DataQuery} to do the actual query generation.
*
* todo 3.1: In 3.0 the below is not currently true for backwards compatible reasons, but code should not rely on current behaviour
* todo 3.1: In 3.0 the below is not currently true for backwards compatible reasons, but code should not rely on
* current behaviour.
*
* DataLists have two sets of methods.
*
* 1). Selection methods (SS_Filterable, SS_Sortable, SS_Limitable) change the way the list is built, but does not
* alter underlying data. There are no external affects from selection methods once this list instance is destructed.
* alter underlying data. There are no external affects from selection methods once this list instance is
* destructed.
*
* 2). Mutation methods change the underlying data. The change persists into the underlying data storage layer.
*
@ -83,7 +85,9 @@ class DataList extends ViewableData implements SS_List, SS_Filterable, SS_Sortab
/**
* Return a copy of the internal {@link DataQuery} object
*
* todo 3.1: In 3.0 the below is not currently true for backwards compatible reasons, but code should not rely on this
* todo 3.1: In 3.0 the below is not currently true for backwards compatible reasons, but code should not rely on
* this
*
* Because the returned value is a copy, modifying it won't affect this list's contents. If
* you want to alter the data query directly, use the alterDataQuery method
*
@ -254,7 +258,8 @@ class DataList extends ViewableData implements SS_List, SS_Filterable, SS_Sortab
return $this;
}
if($limit && !is_numeric($limit)) {
Deprecation::notice('3.0', 'Please pass limits as 2 arguments, rather than an array or SQL fragment.', Deprecation::SCOPE_GLOBAL);
Deprecation::notice('3.0', 'Please pass limits as 2 arguments, rather than an array or SQL fragment.',
Deprecation::SCOPE_GLOBAL);
}
return $this->alterDataQuery_30(function($query) use ($limit, $offset){
$query->limit($limit, $offset);
@ -319,7 +324,8 @@ class DataList extends ViewableData implements SS_List, SS_Filterable, SS_Sortab
$query->sort(null, null); // wipe the sort
foreach($sort as $col => $dir) {
// Convert column expressions to SQL fragment, while still allowing the passing of raw SQL fragments.
// Convert column expressions to SQL fragment, while still allowing the passing of raw SQL
// fragments.
try {
$relCol = $list->getRelationName($col);
} catch(InvalidArgumentException $e) {
@ -340,7 +346,8 @@ class DataList extends ViewableData implements SS_List, SS_Filterable, SS_Sortab
* @example $list = $list->filter('Name', array('aziz', 'bob'); // aziz and bob in list
* @example $list = $list->filter(array('Name'=>'bob, 'Age'=>21)); // bob with the age 21
* @example $list = $list->filter(array('Name'=>'bob, 'Age'=>array(21, 43))); // bob with the Age 21 or 43
* @example $list = $list->filter(array('Name'=>array('aziz','bob'), 'Age'=>array(21, 43))); // aziz with the age 21 or 43 and bob with the Age 21 or 43
* @example $list = $list->filter(array('Name'=>array('aziz','bob'), 'Age'=>array(21, 43)));
* // aziz with the age 21 or 43 and bob with the Age 21 or 43
*
* @todo extract the sql from $customQuery into a SQLGenerator class
*
@ -403,14 +410,16 @@ class DataList extends ViewableData implements SS_List, SS_Filterable, SS_Sortab
/**
* Filter this DataList by a callback function.
* The function will be passed each record of the DataList in turn, and must return true for the record to be included.
* Returns the filtered list.
* The function will be passed each record of the DataList in turn, and must return true for the record to be
* included. Returns the filtered list.
*
* Note that, in the current implementation, the filtered list will be an ArrayList, but this may change in a future
* implementation.
* Note that, in the current implementation, the filtered list will be an ArrayList, but this may change in a
* future implementation.
*/
public function filterByCallback($callback) {
if(!is_callable($callback)) throw new LogicException("DataList::filterByCallback() must be passed something callable.");
if(!is_callable($callback)) {
throw new LogicException("DataList::filterByCallback() must be passed something callable.");
}
$output = new ArrayList;
foreach($this as $item) {
@ -432,7 +441,8 @@ class DataList extends ViewableData implements SS_List, SS_Filterable, SS_Sortab
}
if (!$this->inAlterDataQueryCall) {
Deprecation::notice('3.1', 'getRelationName is mutating, and must be called inside an alterDataQuery block');
Deprecation::notice('3.1',
'getRelationName is mutating, and must be called inside an alterDataQuery block');
}
if(strpos($field,'.') === false) {
@ -470,7 +480,8 @@ class DataList extends ViewableData implements SS_List, SS_Filterable, SS_Sortab
* @example $list = $list->exclude('Name', array('aziz', 'bob'); // exclude aziz and bob from list
* @example $list = $list->exclude(array('Name'=>'bob, 'Age'=>21)); // exclude bob that has Age 21
* @example $list = $list->exclude(array('Name'=>'bob, 'Age'=>array(21, 43))); // exclude bob with Age 21 or 43
* @example $list = $list->exclude(array('Name'=>array('bob','phil'), 'Age'=>array(21, 43))); // bob age 21 or 43, phil age 21 or 43 would be excluded
* @example $list = $list->exclude(array('Name'=>array('bob','phil'), 'Age'=>array(21, 43)));
* // bob age 21 or 43, phil age 21 or 43 would be excluded
*
* @todo extract the sql from this method into a SQLGenerator class
*
@ -1024,7 +1035,8 @@ class DataList extends ViewableData implements SS_List, SS_Filterable, SS_Sortab
*
*/
public function removeDuplicates() {
user_error("Can't call DataList::removeDuplicates() because its data comes from a specific query.", E_USER_ERROR);
user_error("Can't call DataList::removeDuplicates() because its data comes from a specific query.",
E_USER_ERROR);
}
/**

View File

@ -308,8 +308,8 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
*
* @param array|null $record This will be null for a new database record. Alternatively, you can pass an array of
* field values. Normally this contructor is only used by the internal systems that get objects from the database.
* @param boolean $isSingleton This this to true if this is a singleton() object, a stub for calling methods. Singletons
* don't have their defaults set.
* @param boolean $isSingleton This this to true if this is a singleton() object, a stub for calling methods.
* Singletons don't have their defaults set.
*/
public function __construct($record = null, $isSingleton = false, $model = null) {
@ -328,8 +328,9 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
if(is_object($record)) $passed = "an object of type '$record->class'";
else $passed = "The value '$record'";
user_error("DataObject::__construct passed $passed. It's supposed to be passed an array,
taken straight from the database. Perhaps you should use DataList::create()->First(); instead?", E_USER_WARNING);
user_error("DataObject::__construct passed $passed. It's supposed to be passed an array,"
. " taken straight from the database. Perhaps you should use DataList::create()->First(); instead?",
E_USER_WARNING);
$record = null;
}
@ -397,7 +398,8 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
* Create a duplicate of this node.
* Note: now also duplicates relations.
*
* @param $doWrite Perform a write() operation before returning the object. If this is true, it will create the duplicate in the database.
* @param $doWrite Perform a write() operation before returning the object. If this is true, it will create the
* duplicate in the database.
* @return DataObject A duplicate of this node. The exact type will be the type of this node.
*/
public function duplicate($doWrite = true) {
@ -417,23 +419,28 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
/**
* Copies the many_many and belongs_many_many relations from one object to another instance of the name of object
* The destinationObject must be written to the database already and have an ID. Writing is performed automatically when adding the new relations.
* The destinationObject must be written to the database already and have an ID. Writing is performed
* automatically when adding the new relations.
*
* @param $sourceObject the source object to duplicate from
* @param $destinationObject the destination object to populate with the duplicated relations
* @return DataObject with the new many_many relations copied in
*/
protected function duplicateManyManyRelations($sourceObject, $destinationObject) {
if (!$destinationObject || $destinationObject->ID < 1) user_error("Can't duplicate relations for an object that has not been written to the database", E_USER_ERROR);
if (!$destinationObject || $destinationObject->ID < 1) {
user_error("Can't duplicate relations for an object that has not been written to the database",
E_USER_ERROR);
}
//duplicate complex relations
// DO NOT copy has_many relations, because copying the relation would result in us changing the has_one relation
// on the other side of this relation to point at the copy and no longer the original (being a has_one, it can
// only point at one thing at a time). So, all relations except has_many can and are copied
// DO NOT copy has_many relations, because copying the relation would result in us changing the has_one
// relation on the other side of this relation to point at the copy and no longer the original (being a
// has_one, it can only point at one thing at a time). So, all relations except has_many can and are copied
if ($sourceObject->has_one()) foreach($sourceObject->has_one() as $name => $type) {
$this->duplicateRelations($sourceObject, $destinationObject, $name);
}
if ($sourceObject->many_many()) foreach($sourceObject->many_many() as $name => $type) { //many_many include belongs_many_many
if ($sourceObject->many_many()) foreach($sourceObject->many_many() as $name => $type) {
//many_many include belongs_many_many
$this->duplicateRelations($sourceObject, $destinationObject, $name);
}
@ -538,7 +545,8 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
if($this->extension_instances) foreach($this->extension_instances as $i => $instance) {
if(!$instance->class) {
$class = get_class($instance);
user_error("DataObject::defineMethods(): Please ensure {$class}::__construct() calls parent::__construct()", E_USER_ERROR);
user_error("DataObject::defineMethods(): Please ensure {$class}::__construct() calls"
. " parent::__construct()", E_USER_ERROR);
}
}
@ -834,7 +842,9 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
foreach($manyMany as $relationship => $class) {
$leftComponents = $leftObj->getManyManyComponents($relationship);
$rightComponents = $rightObj->getManyManyComponents($relationship);
if($rightComponents && $rightComponents->exists()) $leftComponents->addMany($rightComponents->column('ID'));
if($rightComponents && $rightComponents->exists()) {
$leftComponents->addMany($rightComponents->column('ID'));
}
$leftComponents->write();
}
}
@ -843,7 +853,9 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
foreach($hasMany as $relationship => $class) {
$leftComponents = $leftObj->getComponents($relationship);
$rightComponents = $rightObj->getComponents($relationship);
if($rightComponents && $rightComponents->exists()) $leftComponents->addMany($rightComponents->column('ID'));
if($rightComponents && $rightComponents->exists()) {
$leftComponents->addMany($rightComponents->column('ID'));
}
$leftComponents->write();
}
@ -873,7 +885,9 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
$this->loadLazyFields();
// $this->record might not contain the blank values so we loop on $this->inheritedDatabaseFields() as well
$fieldNames = array_unique(array_merge(array_keys($this->record), array_keys($this->inheritedDatabaseFields())));
$fieldNames = array_unique(array_merge(
array_keys($this->record),
array_keys($this->inheritedDatabaseFields())));
foreach($fieldNames as $fieldName) {
if(!isset($this->changed[$fieldName])) $this->changed[$fieldName] = 1;
@ -894,8 +908,8 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
* Invalid objects won't be able to be written - a warning will be thrown and no write will occur. onBeforeWrite()
* and onAfterWrite() won't get called either.
*
* It is expected that you call validate() in your own application to test that an object is valid before attempting
* a write, and respond appropriately if it isnt'.
* It is expected that you call validate() in your own application to test that an object is valid before
* attempting a write, and respond appropriately if it isn't.
*
* @return A {@link ValidationResult} object
*/
@ -1003,9 +1017,8 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
* @param boolean $forceInsert Run INSERT command rather than UPDATE, even if record already exists
* @param boolean $forceWrite Write to database even if there are no changes
* @param boolean $writeComponents Call write() on all associated component instances which were previously
* retrieved through {@link getComponent()}, {@link getComponents()} or {@link getManyManyComponents()}
* (Default: false)
*
* retrieved through {@link getComponent()}, {@link getComponents()} or
* {@link getManyManyComponents()} (Default: false)
* @return int The ID of the record
* @throws ValidationException Exception that can be caught and handled by the calling function
*/
@ -1043,7 +1056,8 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
$this->onBeforeWrite();
if($this->brokenOnWrite) {
user_error("$this->class has a broken onBeforeWrite() function. Make sure that you call parent::onBeforeWrite().", E_USER_ERROR);
user_error("$this->class has a broken onBeforeWrite() function."
. " Make sure that you call parent::onBeforeWrite().", E_USER_ERROR);
}
// New record = everything has changed
@ -1109,7 +1123,9 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
$classSingleton = singleton($class);
foreach($this->record as $fieldName => $fieldValue) {
if(isset($this->changed[$fieldName]) && $this->changed[$fieldName] && $fieldType = $classSingleton->hasOwnTableDatabaseField($fieldName)) {
if(isset($this->changed[$fieldName]) && $this->changed[$fieldName]
&& $fieldType = $classSingleton->hasOwnTableDatabaseField($fieldName)) {
$fieldObj = $this->dbObject($fieldName);
if(!isset($manipulation[$class])) $manipulation[$class] = array();
@ -1132,7 +1148,8 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
if($dbCommand == 'insert') {
$manipulation[$class]['fields']["Created"] = "'".SS_Datetime::now()->Rfc2822()."'";
//echo "<li>$this->class - " .get_class($this);
$manipulation[$class]['fields']["ClassName"] = DB::getConn()->prepStringForDB($this->class);
$manipulation[$class]['fields']["ClassName"]
= DB::getConn()->prepStringForDB($this->class);
}
}
@ -1183,7 +1200,8 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
}
/**
* Write the cached components to the database. Cached components could refer to two different instances of the same record.
* Write the cached components to the database. Cached components could refer to two different instances of the
* same record.
*
* @param $recursive Recursively write components
*/
@ -1205,7 +1223,8 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
$this->brokenOnDelete = true;
$this->onBeforeDelete();
if($this->brokenOnDelete) {
user_error("$this->class has a broken onBeforeDelete() function. Make sure that you call parent::onBeforeDelete().", E_USER_ERROR);
user_error("$this->class has a broken onBeforeDelete() function."
. " Make sure that you call parent::onBeforeDelete().", E_USER_ERROR);
}
// Deleting a record without an ID shouldn't do anything
@ -1313,8 +1332,10 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
*
* @param string $componentName Name of the component
* @param string $filter A filter to be inserted into the WHERE clause
* @param string|array $sort A sort expression to be inserted into the ORDER BY clause. If omitted, the static field $default_sort on the component class will be used.
* @param string $join A single join clause. This can be used for filtering, only 1 instance of each DataObject will be returned.
* @param string|array $sort A sort expression to be inserted into the ORDER BY clause. If omitted, the static
* field $default_sort on the component class will be used.
* @param string $join A single join clause. This can be used for filtering, only 1 instance of each DataObject
* will be returned.
* @param string|array $limit A limit expression to be inserted into the LIMIT clause
*
* @return HasManyList The components of the one-to-many relationship.
@ -1323,7 +1344,8 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
$result = null;
if(!$componentClass = $this->has_many($componentName)) {
user_error("DataObject::getComponents(): Unknown 1-to-many component '$componentName' on class '$this->class'", E_USER_ERROR);
user_error("DataObject::getComponents(): Unknown 1-to-many component '$componentName'"
. " on class '$this->class'", E_USER_ERROR);
}
$joinField = $this->getRemoteJoinField($componentName, 'has_many');
@ -1350,7 +1372,8 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
*/
public function getComponentsQuery($componentName, $filter = "", $sort = "", $join = "", $limit = "") {
if(!$componentClass = $this->has_many($componentName)) {
user_error("DataObject::getComponentsQuery(): Unknown 1-to-many component '$componentName' on class '$this->class'", E_USER_ERROR);
user_error("DataObject::getComponentsQuery(): Unknown 1-to-many component '$componentName'"
. " on class '$this->class'", E_USER_ERROR);
}
$joinField = $this->getRemoteJoinField($componentName, 'has_many');
@ -1460,11 +1483,13 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
}
/**
* Return the class of a one-to-one component. If $component is null, return all of the one-to-one components and their classes.
* Return the class of a one-to-one component. If $component is null, return all of the one-to-one components and
* their classes.
*
* @param string $component Name of component
*
* @return string|array The class of the one-to-one component, or an array of all one-to-one components and their classes.
* @return string|array The class of the one-to-one component, or an array of all one-to-one components and their
* classes.
*/
public function has_one($component = null) {
$classes = ClassInfo::ancestry($this);
@ -1483,8 +1508,11 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
$newItems = (array)Config::inst()->get($class, 'has_one', Config::UNINHERITED);
// Validate the data
foreach($newItems as $k => $v) {
if(!is_string($k) || is_numeric($k) || !is_string($v)) user_error("$class::\$has_one has a bad entry: "
. var_export($k,true). " => " . var_export($v,true) . ". Each map key should be a relationship name, and the map value should be the data class to join to.", E_USER_ERROR);
if(!is_string($k) || is_numeric($k) || !is_string($v)) {
user_error("$class::\$has_one has a bad entry: "
. var_export($k,true). " => " . var_export($v,true) . ". Each map key should be a"
. " relationship name, and the map value should be the data class to join to.", E_USER_ERROR);
}
}
$items = isset($items) ? array_merge($newItems, (array)$items) : $newItems;
}
@ -1550,8 +1578,11 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
$newItems = (array)Config::inst()->get($class, 'db', Config::UNINHERITED);
// Validate the data
foreach($newItems as $k => $v) {
if(!is_string($k) || is_numeric($k) || !is_string($v)) user_error("$class::\$db has a bad entry: "
. var_export($k,true). " => " . var_export($v,true) . ". Each map key should be a property name, and the map value should be the property type.", E_USER_ERROR);
if(!is_string($k) || is_numeric($k) || !is_string($v)) {
user_error("$class::\$db has a bad entry: "
. var_export($k,true). " => " . var_export($v,true) . ". Each map key should be a"
. " property name, and the map value should be the property type.", E_USER_ERROR);
}
}
$items = isset($items) ? array_merge((array)$items, $newItems) : $newItems;
}
@ -1721,7 +1752,8 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
$parentField = $candidateClass . "ID";
}
return array($class, $candidate, $parentField, $childField, "{$candidate}_$inverseComponentName");
return array($class, $candidate, $parentField, $childField,
"{$candidate}_$inverseComponentName");
}
}
user_error("Orphaned \$belongs_many_many value for $this->class.$component", E_USER_ERROR);
@ -1730,16 +1762,22 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
$newItems = (array)Config::inst()->get($class, 'many_many', Config::UNINHERITED);
// Validate the data
foreach($newItems as $k => $v) {
if(!is_string($k) || is_numeric($k) || !is_string($v)) user_error("$class::\$many_many has a bad entry: "
. var_export($k,true). " => " . var_export($v,true) . ". Each map key should be a relationship name, and the map value should be the data class to join to.", E_USER_ERROR);
if(!is_string($k) || is_numeric($k) || !is_string($v)) {
user_error("$class::\$many_many has a bad entry: "
. var_export($k,true). " => " . var_export($v,true) . ". Each map key should be a"
. " relationship name, and the map value should be the data class to join to.", E_USER_ERROR);
}
}
$items = isset($items) ? array_merge($newItems, $items) : $newItems;
$newItems = (array)Config::inst()->get($class, 'belongs_many_many', Config::UNINHERITED);
// Validate the data
foreach($newItems as $k => $v) {
if(!is_string($k) || is_numeric($k) || !is_string($v)) user_error("$class::\$belongs_many_many has a bad entry: "
. var_export($k,true). " => " . var_export($v,true) . ". Each map key should be a relationship name, and the map value should be the data class to join to.", E_USER_ERROR);
if(!is_string($k) || is_numeric($k) || !is_string($v)) {
user_error("$class::\$belongs_many_many has a bad entry: "
. var_export($k,true). " => " . var_export($v,true) . ". Each map key should be a"
. " relationship name, and the map value should be the data class to join to.", E_USER_ERROR);
}
}
$items = isset($items) ? array_merge($newItems, $items) : $newItems;
@ -1823,7 +1861,8 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
// Otherwise we have a bug
} else {
user_error("Bad value for searchable_fields, 'field' value: " . var_export($spec['field'], true), E_USER_WARNING);
user_error("Bad value for searchable_fields, 'field' value: "
. var_export($spec['field'], true), E_USER_WARNING);
}
// Otherwise, use the database field's scaffolder
@ -2171,7 +2210,9 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
// At the very least, the type has changed
$this->changed[$fieldName] = 1;
if((!isset($this->record[$fieldName]) && $val) || (isset($this->record[$fieldName]) && $this->record[$fieldName] != $val)) {
if((!isset($this->record[$fieldName]) && $val) || (isset($this->record[$fieldName])
&& $this->record[$fieldName] != $val)) {
// Value has changed as well, not just the type
$this->changed[$fieldName] = 2;
}
@ -2267,7 +2308,8 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
return 'Int';
}
// get cached fieldmap
$fieldMap = isset(DataObject::$cache_has_own_table_field[$this->class]) ? DataObject::$cache_has_own_table_field[$this->class] : null;
$fieldMap = isset(DataObject::$cache_has_own_table_field[$this->class])
? DataObject::$cache_has_own_table_field[$this->class] : null;
// if no fieldmap is cached, get all fields
if(!$fieldMap) {
@ -2313,7 +2355,9 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
if(get_parent_class($dataClass) == 'DataObject') {
DataObject::$cache_has_own_table[$dataClass] = true;
} else {
DataObject::$cache_has_own_table[$dataClass] = Config::inst()->get($dataClass, 'db', Config::UNINHERITED) || Config::inst()->get($dataClass, 'has_one', Config::UNINHERITED);
DataObject::$cache_has_own_table[$dataClass]
= Config::inst()->get($dataClass, 'db', Config::UNINHERITED)
|| Config::inst()->get($dataClass, 'has_one', Config::UNINHERITED);
}
}
return DataObject::$cache_has_own_table[$dataClass];
@ -2370,7 +2414,8 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
$unsecuredPages = $query->execute()->column();
if($permissionCache[$memberID][$perm]) {
$permissionCache[$memberID][$perm] = array_merge($permissionCache[$memberID][$perm], $unsecuredPages);
$permissionCache[$memberID][$perm]
= array_merge($permissionCache[$memberID][$perm], $unsecuredPages);
} else {
$permissionCache[$memberID][$perm] = $unsecuredPages;
}
@ -2546,7 +2591,8 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
if (!($object instanceof DBField) && !($object instanceof DataList)) {
// Todo: come up with a broader range of exception objects to describe differnet kinds of errors programatically
// Todo: come up with a broader range of exception objects to describe differnet kinds of errors
// programatically
throw new Exception("Unable to traverse to related object field [$fieldPath] on [$this->class]");
}
return $object;
@ -2627,14 +2673,18 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
*
* @param string $callerClass The class of objects to be returned
* @param string $filter A filter to be inserted into the WHERE clause.
* @param string|array $sort A sort expression to be inserted into the ORDER BY clause. If omitted, self::$default_sort will be used.
* @param string $join A single join clause. This can be used for filtering, only 1 instance of each DataObject will be returned.
* @param string|array $sort A sort expression to be inserted into the ORDER BY clause. If omitted,
* self::$default_sort will be used.
* @param string $join A single join clause. This can be used for filtering, only 1 instance of each DataObject
* will be returned.
* @param string|array $limit A limit expression to be inserted into the LIMIT clause.
* @param string $containerClass The container class to return the results in.
*
* @return mixed The objects matching the filter, in the class specified by $containerClass
*/
public static function get($callerClass = null, $filter = "", $sort = "", $join = "", $limit = null, $containerClass = 'DataList') {
public static function get($callerClass = null, $filter = "", $sort = "", $join = "", $limit = null,
$containerClass = 'DataList') {
if($callerClass == null) {
$callerClass = get_called_class();
if($callerClass == 'DataObject') {
@ -2642,7 +2692,8 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
}
if($filter || $sort || $join || $limit || ($containerClass != 'DataList')) {
throw new \InvalidArgumentException('If calling <classname>::get() then you shouldn\'t pass any other arguments');
throw new \InvalidArgumentException('If calling <classname>::get() then you shouldn\'t pass any other'
. ' arguments');
}
$result = DataList::create(get_called_class());
@ -2653,7 +2704,8 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
// Todo: Determine if we can deprecate for 3.0.0 and use DI or something instead
// Todo: Make the $containerClass method redundant
if($containerClass != 'DataList') {
Deprecation::notice('3.0', 'DataObject::get() - $containerClass argument is deprecated.', Deprecation::SCOPE_GLOBAL);
Deprecation::notice('3.0', 'DataObject::get() - $containerClass argument is deprecated.',
Deprecation::SCOPE_GLOBAL);
}
$result = DataList::create($callerClass)->where($filter)->sort($sort);
@ -2675,9 +2727,9 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
* @deprecated 3.0 Use DataList::create and DataList to do your querying
*/
public function Aggregate($class = null) {
Deprecation::notice('3.0', 'Call aggregate methods on a DataList directly instead. In templates ' .
'an example of the new syntax is &lt% cached List(Member).max(LastEdited) %&gt instead (check partial-caching.md documentation ' .
'for more details.)');
Deprecation::notice('3.0', 'Call aggregate methods on a DataList directly instead. In templates'
. ' an example of the new syntax is &lt% cached List(Member).max(LastEdited) %&gt instead'
. ' (check partial-caching.md documentation for more details.)');
if($class) {
$list = new DataList($class);
@ -2685,8 +2737,10 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
} else if(isset($this)) {
$list = new DataList(get_class($this));
$list->setDataModel($this->model);
} else {
throw new InvalidArgumentException("DataObject::aggregate() must be called as an instance method or passed"
. " a classname");
}
else throw new InvalidArgumentException("DataObject::aggregate() must be called as an instance method or passed a classname");
return $list;
}
@ -2705,8 +2759,10 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
* @deprecated 3.0 Use DataList::create and DataList to do your querying
*
* @param string $filter A filter to be inserted into the WHERE clause.
* @param string $sort A sort expression to be inserted into the ORDER BY clause. If omitted, self::$default_sort will be used.
* @param string $join A single join clause. This can be used for filtering, only 1 instance of each DataObject will be returned.
* @param string $sort A sort expression to be inserted into the ORDER BY clause. If omitted, self::$default_sort
* will be used.
* @param string $join A single join clause. This can be used for filtering, only 1 instance of each DataObject
* will be returned.
* @param string $limit A limit expression to be inserted into the LIMIT clause.
* @param string $containerClass The container class to return the results in.
*
@ -2774,7 +2830,10 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
$cacheKey = md5($cacheKey);
// Flush destroyed items out of the cache
if($cache && isset(DataObject::$_cache_get_one[$callerClass][$cacheKey]) && DataObject::$_cache_get_one[$callerClass][$cacheKey] instanceof DataObject && DataObject::$_cache_get_one[$callerClass][$cacheKey]->destroyed) {
if($cache && isset(DataObject::$_cache_get_one[$callerClass][$cacheKey])
&& DataObject::$_cache_get_one[$callerClass][$cacheKey] instanceof DataObject
&& DataObject::$_cache_get_one[$callerClass][$cacheKey]->destroyed) {
DataObject::$_cache_get_one[$callerClass][$cacheKey
] = false;
}
@ -2855,7 +2914,8 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
* @return DataObject The first item matching the query
*/
public function instance_get_one($filter, $orderby = null) {
Deprecation::notice('3.0', 'Use DataList::create($this->class)->where($filter)->sort($orderby)->First() instead.');
Deprecation::notice('3.0',
'Use DataList::create($this->class)->where($filter)->sort($orderby)->First() instead.');
return DataObject::get_one($this->class, $filter, true, $orderby);
}
@ -2938,7 +2998,8 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
if($fields) {
$hasAutoIncPK = ($this->class == ClassInfo::baseDataClass($this->class));
DB::requireTable($this->class, $fields, $indexes, $hasAutoIncPK, $this->stat('create_table_options'), $extensions);
DB::requireTable($this->class, $fields, $indexes, $hasAutoIncPK, $this->stat('create_table_options'),
$extensions);
} else {
DB::dontRequireTable($this->class);
}
@ -2962,7 +3023,8 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
(($this->class == $childClass) ? "ChildID" : "{$childClass}ID") => true,
);
DB::requireTable("{$this->class}_$relationship", $manymanyFields, $manymanyIndexes, true, null, $extensions);
DB::requireTable("{$this->class}_$relationship", $manymanyFields, $manymanyIndexes, true, null,
$extensions);
}
}
@ -3084,7 +3146,8 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
);
}
if(!isset($rewrite[$identifer]['title'])) {
$rewrite[$identifer]['title'] = (isset($labels[$identifer])) ? $labels[$identifer] : FormField::name_to_label($identifer);
$rewrite[$identifer]['title'] = (isset($labels[$identifer]))
? $labels[$identifer] : FormField::name_to_label($identifer);
}
if(!isset($rewrite[$identifer]['filter'])) {
$rewrite[$identifer]['filter'] = 'PartialMatchFilter';
@ -3313,8 +3376,8 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
* This is a map from fieldname to default value.
*
* - If you would like to change a default value in a sub-class, just specify it.
* - If you would like to disable the default value given by a parent class, set the default value to 0,'',or false in your
* subclass. Setting it to null won't work.
* - If you would like to disable the default value given by a parent class, set the default value to 0,'',
* or false in your subclass. Setting it to null won't work.
*
* @var array
*/
@ -3482,7 +3545,8 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
$entities["{$this->class}.PLURALNAME"] = array(
$this->plural_name(),
'Pural name of the object, used in dropdowns and to generally identify a collection of this object in the interface'
'Pural name of the object, used in dropdowns and to generally identify a collection of this object in the'
. ' interface'
);
return $entities;

View File

@ -7,7 +7,8 @@
abstract class DataObjectDecorator extends DataExtension {
public function __construct() {
Deprecation::notice('3.0', 'DataObjectDecorator is deprecated. Use DataExtension instead.', Deprecation::SCOPE_CLASS);
Deprecation::notice('3.0', 'DataObjectDecorator is deprecated. Use DataExtension instead.',
Deprecation::SCOPE_CLASS);
parent::__construct();
}

View File

@ -1,7 +1,7 @@
<?php
/**
* DataObjectInterface is an interface that other data systems in your application can implement in order to behave in a manner
* similar to DataObject.
* DataObjectInterface is an interface that other data systems in your application can implement in order to behave in
* a manner similar to DataObject.
*
* In addition to the methods defined below, the data of the object should be directly accessible as fields.
* @package framework
@ -9,8 +9,8 @@
*/
interface DataObjectInterface {
/**
* Create a new data object, not yet in the database. To load an object into the database, a null object should be constructed,
* its fields set, and the write() method called.
* Create a new data object, not yet in the database. To load an object into the database, a null object should be
* constructed, its fields set, and the write() method called.
*/
public function __construct();
@ -35,8 +35,9 @@ interface DataObjectInterface {
public function instance_get_one($filter, $sort = "");
/**
* Write the current object back to the database. It should know whether this is a new object, in which case this would
* be an insert command, or if this is an existing object queried from the database, in which case thes would be
* Write the current object back to the database. It should know whether this is a new object, in which case this
* would be an insert command, or if this is an existing object queried from the database, in which case thes would
* be
*/
public function write();

View File

@ -10,7 +10,8 @@ class DataObjectSet extends ArrayList {
* @deprecated 3.0
*/
public function __construct($items = array()) {
Deprecation::notice('3.0', 'DataObjectSet is deprecated. Use DataList or ArrayList instead', Deprecation::SCOPE_CLASS);
Deprecation::notice('3.0', 'DataObjectSet is deprecated. Use DataList or ArrayList instead',
Deprecation::SCOPE_CLASS);
if ($items) {
if (!is_array($items) || func_num_args() > 1) {

View File

@ -105,9 +105,12 @@ class DataQuery {
// Error checking
if(!$tableClasses) {
if(!SS_ClassLoader::instance()->hasManifest()) {
user_error("DataObjects have been requested before the manifest is loaded. Please ensure you are not querying the database in _config.php.", E_USER_ERROR);
user_error("DataObjects have been requested before the manifest is loaded. Please ensure you are not"
. " querying the database in _config.php.", E_USER_ERROR);
} else {
user_error("DataObject::buildSQL: Can't find data classes (classes linked to tables) for $this->dataClass. Please ensure you run dev/build after creating a new DataObject.", E_USER_ERROR);
user_error("DataObject::buildSQL: Can't find data classes (classes linked to tables) for"
. " $this->dataClass. Please ensure you run dev/build after creating a new DataObject.",
E_USER_ERROR);
}
}
@ -223,9 +226,11 @@ class DataQuery {
}
$query->selectField("\"$baseClass\".\"ID\"", "ID");
$query->selectField("CASE WHEN \"$baseClass\".\"ClassName\" IS NOT NULL THEN \"$baseClass\".\"ClassName\" ELSE ".DB::getConn()->prepStringForDB($baseClass)." END", "RecordClassName");
$query->selectField("CASE WHEN \"$baseClass\".\"ClassName\" IS NOT NULL THEN \"$baseClass\".\"ClassName\""
. " ELSE ".DB::getConn()->prepStringForDB($baseClass)." END", "RecordClassName");
// TODO: Versioned, Translatable, SiteTreeSubsites, etc, could probably be better implemented as subclasses of DataQuery
// TODO: Versioned, Translatable, SiteTreeSubsites, etc, could probably be better implemented as subclasses
// of DataQuery
$obj = Injector::inst()->get($this->dataClass);
$obj->extend('augmentSQL', $query, $this);
@ -589,11 +594,12 @@ public function max($field) {
if ($component = $model->has_one($rel)) {
if(!$this->query->isJoinedTo($component)) {
$foreignKey = $model->getReverseAssociation($component);
$this->query->addLeftJoin($component, "\"$component\".\"ID\" = \"{$modelClass}\".\"{$foreignKey}ID\"");
$this->query->addLeftJoin($component,
"\"$component\".\"ID\" = \"{$modelClass}\".\"{$foreignKey}ID\"");
/**
* add join clause to the component's ancestry classes so that the search filter could search on its
* ancester fields.
* add join clause to the component's ancestry classes so that the search filter could search on
* its ancestor fields.
*/
$ancestry = ClassInfo::ancestry($component, true);
if(!empty($ancestry)){
@ -611,10 +617,11 @@ public function max($field) {
if(!$this->query->isJoinedTo($component)) {
$ancestry = $model->getClassAncestry();
$foreignKey = $model->getRemoteJoinField($rel);
$this->query->addLeftJoin($component, "\"$component\".\"{$foreignKey}\" = \"{$ancestry[0]}\".\"ID\"");
$this->query->addLeftJoin($component,
"\"$component\".\"{$foreignKey}\" = \"{$ancestry[0]}\".\"ID\"");
/**
* add join clause to the component's ancestry classes so that the search filter could search on its
* ancestor fields.
* add join clause to the component's ancestry classes so that the search filter could search on
* its ancestor fields.
*/
$ancestry = ClassInfo::ancestry($component, true);
if(!empty($ancestry)){
@ -632,10 +639,13 @@ public function max($field) {
list($parentClass, $componentClass, $parentField, $componentField, $relationTable) = $component;
$parentBaseClass = ClassInfo::baseDataClass($parentClass);
$componentBaseClass = ClassInfo::baseDataClass($componentClass);
$this->query->addInnerJoin($relationTable, "\"$relationTable\".\"$parentField\" = \"$parentBaseClass\".\"ID\"");
$this->query->addLeftJoin($componentBaseClass, "\"$relationTable\".\"$componentField\" = \"$componentBaseClass\".\"ID\"");
$this->query->addInnerJoin($relationTable,
"\"$relationTable\".\"$parentField\" = \"$parentBaseClass\".\"ID\"");
$this->query->addLeftJoin($componentBaseClass,
"\"$relationTable\".\"$componentField\" = \"$componentBaseClass\".\"ID\"");
if(ClassInfo::hasTable($componentClass)) {
$this->query->addLeftJoin($componentClass, "\"$relationTable\".\"$componentField\" = \"$componentClass\".\"ID\"");
$this->query->addLeftJoin($componentClass,
"\"$relationTable\".\"$componentField\" = \"$componentClass\".\"ID\"");
}
$modelClass = $componentClass;

View File

@ -78,12 +78,14 @@ abstract class SS_Database {
* - 'temporary' - If true, then a temporary table will be created
* @return The table name generated. This may be different from the table name, for example with temporary tables.
*/
abstract public function createTable($table, $fields = null, $indexes = null, $options = null, $advancedOptions = null);
abstract public function createTable($table, $fields = null, $indexes = null, $options = null,
$advancedOptions = null);
/**
* Alter a table's schema.
*/
abstract public function alterTable($table, $newFields = null, $newIndexes = null, $alteredFields = null, $alteredIndexes = null, $alteredOptions=null, $advancedOptions=null);
abstract public function alterTable($table, $newFields = null, $newIndexes = null, $alteredFields = null,
$alteredIndexes = null, $alteredOptions=null, $advancedOptions=null);
/**
* Rename a table.
@ -207,12 +209,14 @@ abstract class SS_Database {
foreach($this->schemaUpdateTransaction as $tableName => $changes) {
switch($changes['command']) {
case 'create':
$this->createTable($tableName, $changes['newFields'], $changes['newIndexes'], $changes['options'], @$changes['advancedOptions']);
$this->createTable($tableName, $changes['newFields'], $changes['newIndexes'], $changes['options'],
@$changes['advancedOptions']);
break;
case 'alter':
$this->alterTable($tableName, $changes['newFields'], $changes['newIndexes'],
$changes['alteredFields'], $changes['alteredIndexes'], $changes['alteredOptions'], @$changes['advancedOptions']);
$changes['alteredFields'], $changes['alteredIndexes'], $changes['alteredOptions'],
@$changes['advancedOptions']);
break;
}
}
@ -240,7 +244,13 @@ abstract class SS_Database {
* @param string $options
*/
public function transCreateTable($table, $options = null, $advanced_options = null) {
$this->schemaUpdateTransaction[$table] = array('command' => 'create', 'newFields' => array(), 'newIndexes' => array(), 'options' => $options, 'advancedOptions' => $advanced_options);
$this->schemaUpdateTransaction[$table] = array(
'command' => 'create',
'newFields' => array(),
'newIndexes' => array(),
'options' => $options,
'advancedOptions' => $advanced_options
);
}
/**
@ -298,7 +308,8 @@ abstract class SS_Database {
* @param string $indexSchema A list of indexes to create. See {@link requireIndex()}
* @param array $options
*/
public function requireTable($table, $fieldSchema = null, $indexSchema = null, $hasAutoIncPK=true, $options = Array(), $extensions=false) {
public function requireTable($table, $fieldSchema = null, $indexSchema = null, $hasAutoIncPK=true,
$options = Array(), $extensions=false) {
if(!isset($this->tableList[strtolower($table)])) {
$this->transCreateTable($table, $options, $extensions);
@ -423,11 +434,13 @@ abstract class SS_Database {
if($newTable || !isset($this->indexList[$table][$index_alt])) {
$this->transCreateIndex($table, $index, $spec);
$this->alterationMessage("Index $table.$index: created as " . DB::getConn()->convertIndexSpec($spec),"created");
$this->alterationMessage("Index $table.$index: created as "
. DB::getConn()->convertIndexSpec($spec),"created");
} else if($array_spec != DB::getConn()->convertIndexSpec($spec)) {
$this->transAlterIndex($table, $index, $spec);
$spec_msg=DB::getConn()->convertIndexSpec($spec);
$this->alterationMessage("Index $table.$index: changed to $spec_msg <i style=\"color: #AAA\">(from {$array_spec})</i>","changed");
$this->alterationMessage("Index $table.$index: changed to $spec_msg"
. " <i style=\"color: #AAA\">(from {$array_spec})</i>","changed");
}
}
@ -545,14 +558,16 @@ abstract class SS_Database {
$query .= "'{$holder[$i]}')";
DB::query($query);
$amount = DB::affectedRows();
$this->alterationMessage("Changed $amount rows to default value of field $field (Value: $default)");
$this->alterationMessage("Changed $amount rows to default value of field $field"
. " (Value: $default)");
}
}
}
Profiler::mark('alterField');
$this->transAlterField($table, $field, $spec_orig);
Profiler::unmark('alterField');
$this->alterationMessage("Field $table.$field: changed to $specValue <i style=\"color: #AAA\">(from {$fieldValue})</i>","changed");
$this->alterationMessage("Field $table.$field: changed to $specValue"
. " <i style=\"color: #AAA\">(from {$fieldValue})</i>","changed");
}
Profiler::unmark('requireField');
}
@ -571,7 +586,8 @@ abstract class SS_Database {
$suffix = $suffix ? ($suffix+1) : 2;
}
$this->renameField($table, $fieldName, "_obsolete_{$fieldName}$suffix");
$this->alterationMessage("Field $table.$fieldName: renamed to $table._obsolete_{$fieldName}$suffix","obsolete");
$this->alterationMessage("Field $table.$fieldName: renamed to $table._obsolete_{$fieldName}$suffix",
"obsolete");
}
}
@ -627,7 +643,8 @@ abstract class SS_Database {
default:
$sql = null;
user_error("SS_Database::manipulate() Can't recognise command '$writeInfo[command]'", E_USER_ERROR);
user_error("SS_Database::manipulate() Can't recognise command '$writeInfo[command]'",
E_USER_ERROR);
}
}
}
@ -799,9 +816,14 @@ abstract class SS_Database {
// Pass limit as array or SQL string value
if(is_array($limit)) {
if(!array_key_exists('limit', $limit)) throw new InvalidArgumentException('Database::sqlLimitToString(): Wrong format for $limit: ' . var_export($limit, true));
if(!array_key_exists('limit', $limit)) {
throw new InvalidArgumentException('Database::sqlLimitToString(): Wrong format for $limit: '
. var_export($limit, true));
}
if(isset($limit['start']) && is_numeric($limit['start']) && isset($limit['limit'])
&& is_numeric($limit['limit'])) {
if(isset($limit['start']) && is_numeric($limit['start']) && isset($limit['limit']) && is_numeric($limit['limit'])) {
$combinedLimit = $limit['start'] ? "$limit[limit] OFFSET $limit[start]" : "$limit[limit]";
} elseif(isset($limit['limit']) && is_numeric($limit['limit'])) {
$combinedLimit = (int) $limit['limit'];
@ -856,7 +878,8 @@ abstract class SS_Database {
/**
* function to return an SQL datetime expression that can be used with the adapter in use
* used for querying a datetime in a certain format
* @param string $date to be formated, can be either 'now', literal datetime like '1973-10-14 10:30:00' or field name, e.g. '"SiteTree"."Created"'
* @param string $date to be formated, can be either 'now', literal datetime like '1973-10-14 10:30:00' or
* field name, e.g. '"SiteTree"."Created"'
* @param string $format to be used, supported specifiers:
* %Y = Year (four digits)
* %m = Month (01..12)
@ -872,8 +895,10 @@ abstract class SS_Database {
/**
* function to return an SQL datetime expression that can be used with the adapter in use
* used for querying a datetime addition
* @param string $date, can be either 'now', literal datetime like '1973-10-14 10:30:00' or field name, e.g. '"SiteTree"."Created"'
* @param string $interval to be added, use the format [sign][integer] [qualifier], e.g. -1 Day, +15 minutes, +1 YEAR
* @param string $date, can be either 'now', literal datetime like '1973-10-14 10:30:00' or field name,
* e.g. '"SiteTree"."Created"'
* @param string $interval to be added, use the format [sign][integer] [qualifier], e.g. -1 Day, +15 minutes,
* +1 YEAR
* supported qualifiers:
* - years
* - months
@ -882,16 +907,20 @@ abstract class SS_Database {
* - minutes
* - seconds
* This includes the singular forms as well
* @return string SQL datetime expression to query for a datetime (YYYY-MM-DD hh:mm:ss) which is the result of the addition
* @return string SQL datetime expression to query for a datetime (YYYY-MM-DD hh:mm:ss) which is the result of
* the addition
*/
abstract public function datetimeIntervalClause($date, $interval);
/**
* function to return an SQL datetime expression that can be used with the adapter in use
* used for querying a datetime substraction
* @param string $date1, can be either 'now', literal datetime like '1973-10-14 10:30:00' or field name, e.g. '"SiteTree"."Created"'
* @param string $date2 to be substracted of $date1, can be either 'now', literal datetime like '1973-10-14 10:30:00' or field name, e.g. '"SiteTree"."Created"'
* @return string SQL datetime expression to query for the interval between $date1 and $date2 in seconds which is the result of the substraction
* @param string $date1, can be either 'now', literal datetime like '1973-10-14 10:30:00' or field name
* e.g. '"SiteTree"."Created"'
* @param string $date2 to be substracted of $date1, can be either 'now', literal datetime
* like '1973-10-14 10:30:00' or field name, e.g. '"SiteTree"."Created"'
* @return string SQL datetime expression to query for the interval between $date1 and $date2 in seconds which
* is the result of the substraction
*/
abstract public function datetimeDifferenceClause($date1, $date2);
@ -912,7 +941,8 @@ abstract class SS_Database {
/*
* Start a prepared transaction
* See http://developer.postgresql.org/pgdocs/postgres/sql-set-transaction.html for details on transaction isolation options
* See http://developer.postgresql.org/pgdocs/postgres/sql-set-transaction.html for details on
* transaction isolation options
*/
abstract public function transactionStart($transaction_mode=false, $session_characteristics=false);
@ -990,7 +1020,8 @@ abstract class SS_Database {
/**
* Abstract query-result class.
* Once again, this should be subclassed by an actual database implementation. It will only
* ever be constructed by a subclass of SS_Database. The result of a database query - an iteratable object that's returned by DB::SS_Query
* ever be constructed by a subclass of SS_Database. The result of a database query - an iteratable object
* that's returned by DB::SS_Query
*
* Primarily, the SS_Query class takes care of the iterator plumbing, letting the subclasses focusing
* on providing the specific data-access methods that are required: {@link nextRecord()}, {@link numRecords()}

View File

@ -99,7 +99,8 @@ class DatabaseAdmin extends Controller {
echo "<p>Done!</p>";
$this->redirect($_GET['returnURL']);
} else {
$this->doBuild(isset($_REQUEST['quiet']) || isset($_REQUEST['from_installer']), !isset($_REQUEST['dont_populate']));
$this->doBuild(isset($_REQUEST['quiet']) || isset($_REQUEST['from_installer']),
!isset($_REQUEST['dont_populate']));
}
}
@ -163,8 +164,11 @@ class DatabaseAdmin extends Controller {
$dbVersion = $conn->getVersion();
$databaseName = (method_exists($conn, 'currentDatabase')) ? $conn->currentDatabase() : "";
if(Director::is_cli()) echo sprintf("\n\nBuilding database %s using %s %s\n\n", $databaseName, $dbType, $dbVersion);
else echo sprintf("<h2>Building database %s using %s %s</h2>", $databaseName, $dbType, $dbVersion);
if(Director::is_cli()) {
echo sprintf("\n\nBuilding database %s using %s %s\n\n", $databaseName, $dbType, $dbVersion);
} else {
echo sprintf("<h2>Building database %s using %s %s</h2>", $databaseName, $dbType, $dbVersion);
}
}
// Set up the initial database
@ -180,7 +184,8 @@ class DatabaseAdmin extends Controller {
$database = $parameters['database'];
if(!$database) {
user_error("No database name given; please give a value for \$databaseConfig['database']", E_USER_ERROR);
user_error("No database name given; please give a value for \$databaseConfig['database']",
E_USER_ERROR);
}
DB::createDatabase($connect, $username, $password, $database);

View File

@ -25,7 +25,8 @@ interface SS_Filterable {
* @example $list = $list->filter('Name', array('aziz', 'bob'); // aziz and bob in list
* @example $list = $list->filter(array('Name'=>'bob, 'Age'=>21)); // bob with the age 21
* @example $list = $list->filter(array('Name'=>'bob, 'Age'=>array(21, 43))); // bob with the Age 21 or 43
* @example $list = $list->filter(array('Name'=>array('aziz','bob'), 'Age'=>array(21, 43))); // aziz with the age 21 or 43 and bob with the Age 21 or 43
* @example $list = $list->filter(array('Name'=>array('aziz','bob'), 'Age'=>array(21, 43)));
* // aziz with the age 21 or 43 and bob with the Age 21 or 43
*/
public function filter();
@ -36,7 +37,8 @@ interface SS_Filterable {
* @example $list = $list->exclude('Name', array('aziz', 'bob'); // exclude aziz and bob from list
* @example $list = $list->exclude(array('Name'=>'bob, 'Age'=>21)); // exclude bob that has Age 21
* @example $list = $list->exclude(array('Name'=>'bob, 'Age'=>array(21, 43))); // exclude bob with Age 21 or 43
* @example $list = $list->exclude(array('Name'=>array('bob','phil'), 'Age'=>array(21, 43))); // bob age 21 or 43, phil age 21 or 43 would be excluded
* @example $list = $list->exclude(array('Name'=>array('bob','phil'), 'Age'=>array(21, 43)));
* // bob age 21 or 43, phil age 21 or 43 would be excluded
*/
public function exclude();

View File

@ -29,9 +29,9 @@ class SS_HTMLValue extends ViewableData {
* @return string
*/
public function getContent() {
// strip any surrounding tags before the <body> and after the </body> which are automatically added by DOMDocument
// note that we can't use the argument to saveHTML() as it's only supported in PHP 5.3.6+, we support 5.3.2 as a minimum
// in addition to the above, trim any surrounding newlines from the output
// strip any surrounding tags before the <body> and after the </body> which are automatically added by
// DOMDocument. Note that we can't use the argument to saveHTML() as it's only supported in PHP 5.3.6+,
// we support 5.3.2 as a minimum in addition to the above, trim any surrounding newlines from the output
return trim(
preg_replace(
array(

View File

@ -38,8 +38,11 @@ class HasManyList extends RelationList {
* @param $item The DataObject to be added, or its ID
*/
public function add($item) {
if(is_numeric($item)) $item = DataObject::get_by_id($this->dataClass, $item);
else if(!($item instanceof $this->dataClass)) user_error("HasManyList::add() expecting a $this->dataClass object, or ID value", E_USER_ERROR);
if(is_numeric($item)) {
$item = DataObject::get_by_id($this->dataClass, $item);
} else if(!($item instanceof $this->dataClass)) {
user_error("HasManyList::add() expecting a $this->dataClass object, or ID value", E_USER_ERROR);
}
// Validate foreignID
if(!$this->foreignID) {
@ -74,7 +77,10 @@ class HasManyList extends RelationList {
* @todo Maybe we should delete the object instead?
*/
public function remove($item) {
if(!($item instanceof $this->dataClass)) throw new InvalidArgumentException("HasManyList::remove() expecting a $this->dataClass object, or ID value", E_USER_ERROR);
if(!($item instanceof $this->dataClass)) {
throw new InvalidArgumentException("HasManyList::remove() expecting a $this->dataClass object, or ID",
E_USER_ERROR);
}
$fk = $this->foreignKey;
$item->$fk = null;

View File

@ -35,9 +35,12 @@ class Hierarchy extends DataExtension {
* Validate the owner object - check for existence of infinite loops.
*/
public function validate(ValidationResult $validationResult) {
if (!$this->owner->ID) return; // The object is new, won't be looping.
if (!$this->owner->ParentID) return; // The object has no parent, won't be looping.
if (!$this->owner->isChanged('ParentID')) return; // The parent has not changed, skip the check for performance reasons.
// The object is new, won't be looping.
if (!$this->owner->ID) return;
// The object has no parent, won't be looping.
if (!$this->owner->ParentID) return;
// The parent has not changed, skip the check for performance reasons.
if (!$this->owner->isChanged('ParentID')) return;
// Walk the hierarchy upwards until we reach the top, or until we reach the originating node again.
$node = $this->owner;
@ -69,11 +72,15 @@ class Hierarchy extends DataExtension {
* @param string $extraArg Extra arguments that will be passed on to children, for if they overload this function.
* @param boolean $limitToMarked Display only marked children.
* @param string $childrenMethod The name of the method used to get children from each object
* @param boolean $rootCall Set to true for this first call, and then to false for calls inside the recursion. You should not change this.
* @param boolean $rootCall Set to true for this first call, and then to false for calls inside the recursion. You
* should not change this.
* @param int $minNodeCount
* @return string
*/
public function getChildrenAsUL($attributes = "", $titleEval = '"<li>" . $child->Title', $extraArg = null, $limitToMarked = false, $childrenMethod = "AllChildrenIncludingDeleted", $numChildrenMethod = "numChildren", $rootCall = true, $minNodeCount = 30) {
public function getChildrenAsUL($attributes = "", $titleEval = '"<li>" . $child->Title', $extraArg = null,
$limitToMarked = false, $childrenMethod = "AllChildrenIncludingDeleted",
$numChildrenMethod = "numChildren", $rootCall = true, $minNodeCount = 30) {
if($limitToMarked && $rootCall) {
$this->markingFinished($numChildrenMethod);
}
@ -97,7 +104,8 @@ class Hierarchy extends DataExtension {
$foundAChild = true;
$output .= (is_callable($titleEval)) ? $titleEval($child) : eval("return $titleEval;");
$output .= "\n" .
$child->getChildrenAsUL("", $titleEval, $extraArg, $limitToMarked, $childrenMethod, $numChildrenMethod, false, $minNodeCount) . "</li>\n";
$child->getChildrenAsUL("", $titleEval, $extraArg, $limitToMarked, $childrenMethod,
$numChildrenMethod, false, $minNodeCount) . "</li>\n";
}
}
@ -120,7 +128,9 @@ class Hierarchy extends DataExtension {
* @param int $minNodeCount The minimum amount of nodes to mark.
* @return int The actual number of nodes marked.
*/
public function markPartialTree($minNodeCount = 30, $context = null, $childrenMethod = "AllChildrenIncludingDeleted", $numChildrenMethod = "numChildren") {
public function markPartialTree($minNodeCount = 30, $context = null,
$childrenMethod = "AllChildrenIncludingDeleted", $numChildrenMethod = "numChildren") {
if(!is_numeric($minNodeCount)) $minNodeCount = 30;
$this->markedNodes = array($this->owner->ID => $this->owner);
@ -191,7 +201,8 @@ class Hierarchy extends DataExtension {
* Mark all children of the given node that match the marking filter.
* @param DataObject $node Parent node.
*/
public function markChildren($node, $context = null, $childrenMethod = "AllChildrenIncludingDeleted", $numChildrenMethod = "numChildren") {
public function markChildren($node, $context = null, $childrenMethod = "AllChildrenIncludingDeleted",
$numChildrenMethod = "numChildren") {
if($node->hasMethod($childrenMethod)) {
$children = $node->$childrenMethod($context);
} else {
@ -486,7 +497,8 @@ class Hierarchy extends DataExtension {
$this->owner->extend("augmentAllChildrenIncludingDeleted", $stageChildren, $context);
} else {
user_error("Hierarchy::AllChildren() Couldn't determine base class for '{$this->owner->class}'", E_USER_ERROR);
user_error("Hierarchy::AllChildren() Couldn't determine base class for '{$this->owner->class}'",
E_USER_ERROR);
}
return $stageChildren;
@ -497,7 +509,9 @@ class Hierarchy extends DataExtension {
* from both stage & live.
*/
public function AllHistoricalChildren() {
if(!$this->owner->hasExtension('Versioned')) throw new Exception('Hierarchy->AllHistoricalChildren() only works with Versioned extension applied');
if(!$this->owner->hasExtension('Versioned')) {
throw new Exception('Hierarchy->AllHistoricalChildren() only works with Versioned extension applied');
}
$baseClass=ClassInfo::baseDataClass($this->owner->class);
return Versioned::get_including_deleted($baseClass,
@ -508,7 +522,9 @@ class Hierarchy extends DataExtension {
* Return the number of children that this page ever had, including pages that were deleted
*/
public function numHistoricalChildren() {
if(!$this->owner->hasExtension('Versioned')) throw new Exception('Hierarchy->AllHistoricalChildren() only works with Versioned extension applied');
if(!$this->owner->hasExtension('Versioned')) {
throw new Exception('Hierarchy->AllHistoricalChildren() only works with Versioned extension applied');
}
return Versioned::get_including_deleted(ClassInfo::baseDataClass($this->owner->class),
"\"ParentID\" = " . (int)$this->owner->ID)->count();
@ -567,12 +583,15 @@ class Hierarchy extends DataExtension {
* @return SS_List
*/
public function liveChildren($showAll = false, $onlyDeletedFromStage = false) {
if(!$this->owner->hasExtension('Versioned')) throw new Exception('Hierarchy->liveChildren() only works with Versioned extension applied');
if(!$this->owner->hasExtension('Versioned')) {
throw new Exception('Hierarchy->liveChildren() only works with Versioned extension applied');
}
$baseClass = ClassInfo::baseDataClass($this->owner->class);
$id = $this->owner->ID;
$children = DataObject::get($baseClass)->where("\"{$baseClass}\".\"ParentID\" = $id AND \"{$baseClass}\".\"ID\" != $id");
$children = DataObject::get($baseClass)
->where("\"{$baseClass}\".\"ParentID\" = $id AND \"{$baseClass}\".\"ID\" != $id");
if(!$showAll) $children = $children->where('"ShowInMenus" = 1');
// Query the live site
@ -656,11 +675,14 @@ class Hierarchy extends DataExtension {
$nextNode = null;
$baseClass = ClassInfo::baseDataClass($this->owner->class);
$children = DataObject::get(ClassInfo::baseDataClass($this->owner->class), "\"$baseClass\".\"ParentID\"={$this->owner->ID}" . ( ( $afterNode ) ? " AND \"Sort\" > " . sprintf( '%d', $afterNode->Sort ) : "" ), '"Sort" ASC');
$children = DataObject::get(ClassInfo::baseDataClass($this->owner->class),
"\"$baseClass\".\"ParentID\"={$this->owner->ID}" . ( ( $afterNode ) ? " AND \"Sort\" > "
. sprintf( '%d', $afterNode->Sort ) : "" ), '"Sort" ASC');
// Try all the siblings of this node after the given node
/*if( $siblings = DataObject::get( ClassInfo::baseDataClass($this->owner->class), "\"ParentID\"={$this->owner->ParentID}" . ( $afterNode ) ? "\"Sort\" > {$afterNode->Sort}" : "" , '\"Sort\" ASC' ) )
$searchNodes->merge( $siblings );*/
/*if( $siblings = DataObject::get( ClassInfo::baseDataClass($this->owner->class),
"\"ParentID\"={$this->owner->ParentID}" . ( $afterNode ) ? "\"Sort\"
> {$afterNode->Sort}" : "" , '\"Sort\" ASC' ) ) $searchNodes->merge( $siblings );*/
if($children) {
foreach($children as $node) {
@ -675,7 +697,9 @@ class Hierarchy extends DataExtension {
}
// if this is not an instance of the root class or has the root id, search the parent
if(!(is_numeric($root) && $root == $this->owner->ID || $root == $this->owner->class) && ($parent = $this->owner->Parent())) {
if(!(is_numeric($root) && $root == $this->owner->ID || $root == $this->owner->class)
&& ($parent = $this->owner->Parent())) {
return $parent->naturalNext( $className, $root, $this->owner );
}

View File

@ -113,7 +113,9 @@ class Image extends File {
if($this->Title) {
$title = Convert::raw2att($this->Title);
} else {
if(preg_match("/([^\/]*)\.[a-zA-Z0-9]{1,6}$/", $title, $matches)) $title = Convert::raw2att($matches[1]);
if(preg_match("/([^\/]*)\.[a-zA-Z0-9]{1,6}$/", $title, $matches)) {
$title = Convert::raw2att($matches[1]);
}
}
return "<img src=\"$url\" alt=\"$title\" />";
}
@ -134,7 +136,8 @@ class Image extends File {
*/
public function loadUploadedImage($tmpFile) {
if(!is_array($tmpFile)) {
user_error("Image::loadUploadedImage() Not passed an array. Most likely, the form hasn't got the right enctype", E_USER_ERROR);
user_error("Image::loadUploadedImage() Not passed an array. Most likely, the form hasn't got the right"
. "enctype", E_USER_ERROR);
}
if(!$tmpFile['size']) {
@ -183,7 +186,9 @@ class Image extends File {
}
public function SetSize($width, $height) {
return (($this->getWidth() == $width) && ($this->getHeight() == $height)) ? $this : $this->getFormattedImage('SetSize', $width, $height);
return (($this->getWidth() == $width) && ($this->getHeight() == $height))
? $this
: $this->getFormattedImage('SetSize', $width, $height);
}
public function SetRatioSize($width, $height) {
@ -331,7 +336,8 @@ class Image extends File {
*/
public function generateResizedImage($gd, $width, $height) {
if(is_numeric($gd) || !$gd){
user_error("Image::generateFormattedImage - generateResizedImage is being called by legacy code or gd is not set.",E_USER_WARNING);
user_error("Image::generateFormattedImage - generateResizedImage is being called by legacy code"
. " or gd is not set.",E_USER_WARNING);
}else{
return $gd->resize($width, $height);
}
@ -466,8 +472,8 @@ class Image_Cached extends Image {
/**
* Create a new cached image.
* @param string $filename The filename of the image.
* @param boolean $isSingleton This this to true if this is a singleton() object, a stub for calling methods. Singletons
* don't have their defaults set.
* @param boolean $isSingleton This this to true if this is a singleton() object, a stub for calling methods.
* Singletons don't have their defaults set.
*/
public function __construct($filename = null, $isSingleton = false) {
parent::__construct(array(), $isSingleton);

View File

@ -69,7 +69,10 @@ class ManyManyList extends RelationList {
public function add($item, $extraFields = null) {
if(is_numeric($item)) $itemID = $item;
else if($item instanceof $this->dataClass) $itemID = $item->ID;
else throw new InvalidArgumentException("ManyManyList::add() expecting a $this->dataClass object, or ID value", E_USER_ERROR);
else {
throw new InvalidArgumentException("ManyManyList::add() expecting a $this->dataClass object, or ID value",
E_USER_ERROR);
}
// Validate foreignID
if(!$this->foreignID) {
@ -101,7 +104,9 @@ class ManyManyList extends RelationList {
* @param $itemID The ID of the item to remove.
*/
public function remove($item) {
if(!($item instanceof $this->dataClass)) throw new InvalidArgumentException("ManyManyList::remove() expecting a $this->dataClass object");
if(!($item instanceof $this->dataClass)) {
throw new InvalidArgumentException("ManyManyList::remove() expecting a $this->dataClass object");
}
return $this->removeByID($item->ID);
}
@ -165,7 +170,8 @@ class ManyManyList extends RelationList {
// @todo Optimize into a single query instead of one per extra field
if($this->extraFields) {
foreach($this->extraFields as $fieldName => $dbFieldSpec) {
$query = DB::query("SELECT \"$fieldName\" FROM \"$this->tableName\" WHERE \"$parentField\" = {$this->ownerObj->ID} AND \"$childField\" = {$childID}");
$query = DB::query("SELECT \"$fieldName\" FROM \"$this->tableName\" "
. "WHERE \"$parentField\" = {$this->ownerObj->ID} AND \"$childField\" = {$childID}");
$value = $query->value();
$result[$fieldName] = $value;
}

View File

@ -199,11 +199,13 @@ class SS_Map_Iterator implements Iterator {
} else {
if(!isset($this->firstItems[$this->firstItemIdx-1])) $this->items->next();
if($this->excludedItems) while(($c = $this->items->current()) && in_array($c->{$this->keyField}, $this->excludedItems, true)) {
if($this->excludedItems) {
while(($c = $this->items->current()) && in_array($c->{$this->keyField}, $this->excludedItems, true)) {
$this->items->next();
}
}
}
}
public function valid() {
return $this->items->valid();

View File

@ -69,7 +69,9 @@ class MySQLDatabase extends SS_Database {
$this->active = $this->dbConn->select_db($parameters['database']);
$this->database = $parameters['database'];
if(isset($parameters['timezone'])) $this->query(sprintf("SET SESSION time_zone = '%s'", $parameters['timezone']));
if(isset($parameters['timezone'])) {
$this->query(sprintf("SET SESSION time_zone = '%s'", $parameters['timezone']));
}
}
/**
@ -108,7 +110,9 @@ class MySQLDatabase extends SS_Database {
}
public function query($sql, $errorLevel = E_USER_ERROR) {
if(isset($_REQUEST['previewwrite']) && in_array(strtolower(substr($sql,0,strpos($sql,' '))), array('insert','update','delete','replace'))) {
if(isset($_REQUEST['previewwrite']) && in_array(strtolower(substr($sql,0,strpos($sql,' '))),
array('insert','update','delete','replace'))) {
Debug::message("Will execute: $sql");
return;
}
@ -124,7 +128,9 @@ class MySQLDatabase extends SS_Database {
Debug::message("\n$sql\n{$endtime}ms\n", false);
}
if(!$handle && $errorLevel) $this->databaseError("Couldn't run query: $sql | " . $this->dbConn->error, $errorLevel);
if(!$handle && $errorLevel) {
$this->databaseError("Couldn't run query: $sql | " . $this->dbConn->error, $errorLevel);
}
return new MySQLQuery($this, $handle);
}
@ -244,7 +250,9 @@ class MySQLDatabase extends SS_Database {
* @param $alteredIndexes Updated indexes, a map of index name => index type
* @param $alteredOptions
*/
public function alterTable($tableName, $newFields = null, $newIndexes = null, $alteredFields = null, $alteredIndexes = null, $alteredOptions = null, $advancedOptions = null) {
public function alterTable($tableName, $newFields = null, $newIndexes = null, $alteredFields = null,
$alteredIndexes = null, $alteredOptions = null, $advancedOptions = null) {
if($this->isView($tableName)) {
DB::alteration_message(
sprintf("Table %s not changed as it is a view", $tableName),
@ -277,7 +285,8 @@ class MySQLDatabase extends SS_Database {
}
if($skip) {
DB::alteration_message(
sprintf("Table %s options not changed to %s due to fulltextsearch index", $tableName, $alteredOptions[get_class($this)]),
sprintf("Table %s options not changed to %s due to fulltextsearch index",
$tableName, $alteredOptions[get_class($this)]),
"changed"
);
} else {
@ -381,7 +390,8 @@ class MySQLDatabase extends SS_Database {
if($field['Collation'] && $field['Collation'] != 'NULL') {
// Cache collation info to cut down on database traffic
if(!isset(self::$_cache_collation_info[$field['Collation']])) {
self::$_cache_collation_info[$field['Collation']] = DB::query("SHOW COLLATION LIKE '$field[Collation]'")->record();
self::$_cache_collation_info[$field['Collation']]
= DB::query("SHOW COLLATION LIKE '$field[Collation]'")->record();
}
$collInfo = self::$_cache_collation_info[$field['Collation']];
$fieldSpec .= " character set $collInfo[Charset] collate $field[Collation]";
@ -405,7 +415,8 @@ class MySQLDatabase extends SS_Database {
*
* @param string $tableName The name of the table.
* @param string $indexName The name of the index.
* @param string $indexSpec The specification of the index, see {@link SS_Database::requireIndex()} for more details.
* @param string $indexSpec The specification of the index, see {@link SS_Database::requireIndex()} for more
* details.
*/
public function createIndex($tableName, $indexName, $indexSpec) {
$this->query("ALTER TABLE \"$tableName\" ADD " . $this->getIndexSqlDefinition($indexName, $indexSpec));
@ -480,7 +491,8 @@ class MySQLDatabase extends SS_Database {
* Alter an index on a table.
* @param string $tableName The name of the table.
* @param string $indexName The name of the index.
* @param string $indexSpec The specification of the index, see {@link SS_Database::requireIndex()} for more details.
* @param string $indexSpec The specification of the index, see {@link SS_Database::requireIndex()}
* for more details.
*/
public function alterIndex($tableName, $indexName, $indexSpec) {
@ -576,8 +588,10 @@ class MySQLDatabase extends SS_Database {
*/
public function boolean($values){
//For reference, this is what typically gets passed to this function:
//$parts=Array('datatype'=>'tinyint', 'precision'=>1, 'sign'=>'unsigned', 'null'=>'not null', 'default'=>$this->default);
//DB::requireField($this->tableName, $this->name, "tinyint(1) unsigned not null default '{$this->defaultVal}'");
//$parts=Array('datatype'=>'tinyint', 'precision'=>1, 'sign'=>'unsigned', 'null'=>'not null',
//'default'=>$this->default);
//DB::requireField($this->tableName, $this->name, "tinyint(1) unsigned not null default
//'{$this->defaultVal}'");
return 'tinyint(1) unsigned not null default ' . (int)$values['default'];
}
@ -632,10 +646,13 @@ class MySQLDatabase extends SS_Database {
*/
public function enum($values){
//For reference, this is what typically gets passed to this function:
//$parts=Array('datatype'=>'enum', 'enums'=>$this->enum, 'character set'=>'utf8', 'collate'=> 'utf8_general_ci', 'default'=>$this->default);
//DB::requireField($this->tableName, $this->name, "enum('" . implode("','", $this->enum) . "') character set utf8 collate utf8_general_ci default '{$this->default}'");
//$parts=Array('datatype'=>'enum', 'enums'=>$this->enum, 'character set'=>'utf8', 'collate'=>
// 'utf8_general_ci', 'default'=>$this->default);
//DB::requireField($this->tableName, $this->name, "enum('" . implode("','", $this->enum) . "') character set
// utf8 collate utf8_general_ci default '{$this->default}'");
return 'enum(\'' . implode('\',\'', $values['enums']) . '\') character set utf8 collate utf8_general_ci default \'' . $values['default'] . '\'';
return 'enum(\'' . implode('\',\'', $values['enums']) . '\')'
. ' character set utf8 collate utf8_general_ci default \'' . $values['default'] . '\'';
}
/**
@ -646,10 +663,13 @@ class MySQLDatabase extends SS_Database {
*/
public function set($values){
//For reference, this is what typically gets passed to this function:
//$parts=Array('datatype'=>'enum', 'enums'=>$this->enum, 'character set'=>'utf8', 'collate'=> 'utf8_general_ci', 'default'=>$this->default);
//DB::requireField($this->tableName, $this->name, "enum('" . implode("','", $this->enum) . "') character set utf8 collate utf8_general_ci default '{$this->default}'");
//$parts=Array('datatype'=>'enum', 'enums'=>$this->enum, 'character set'=>'utf8', 'collate'=>
// 'utf8_general_ci', 'default'=>$this->default);
//DB::requireField($this->tableName, $this->name, "enum('" . implode("','", $this->enum) . "') character set
//utf8 collate utf8_general_ci default '{$this->default}'");
$default = empty($values['default']) ? '' : " default '$values[default]'";
return 'set(\'' . implode('\',\'', $values['enums']) . '\') character set utf8 collate utf8_general_ci' . $default;
return 'set(\'' . implode('\',\'', $values['enums']) . '\') character set utf8 collate utf8_general_ci'
. $default;
}
/**
@ -733,8 +753,10 @@ class MySQLDatabase extends SS_Database {
*/
public function varchar($values){
//For reference, this is what typically gets passed to this function:
//$parts=Array('datatype'=>'varchar', 'precision'=>$this->size, 'character set'=>'utf8', 'collate'=>'utf8_general_ci');
//DB::requireField($this->tableName, $this->name, "varchar($this->size) character set utf8 collate utf8_general_ci");
//$parts=Array('datatype'=>'varchar', 'precision'=>$this->size, 'character set'=>'utf8', 'collate'=>
//'utf8_general_ci');
//DB::requireField($this->tableName, $this->name, "varchar($this->size) character set utf8 collate
// utf8_general_ci");
return 'varchar(' . $values['precision'] . ') character set utf8 collate utf8_general_ci';
}
@ -793,7 +815,9 @@ class MySQLDatabase extends SS_Database {
*
* @param string $keywords Keywords as a string.
*/
public function searchEngine($classesToSearch, $keywords, $start, $pageLength, $sortBy = "Relevance DESC", $extraFilter = "", $booleanSearch = false, $alternativeFileFilter = "", $invertedMatch = false) {
public function searchEngine($classesToSearch, $keywords, $start, $pageLength, $sortBy = "Relevance DESC",
$extraFilter = "", $booleanSearch = false, $alternativeFileFilter = "", $invertedMatch = false) {
if(!class_exists('SiteTree')) throw new Exception('MySQLDatabase->searchEngine() requires "SiteTree" class');
if(!class_exists('File')) throw new Exception('MySQLDatabase->searchEngine() requires "File" class');
@ -825,15 +849,19 @@ class MySQLDatabase extends SS_Database {
$notMatch = $invertedMatch ? "NOT " : "";
if($keywords) {
$match['SiteTree'] = "
MATCH (Title, MenuTitle, Content, MetaTitle, MetaDescription, MetaKeywords) AGAINST ('$keywords' $boolean)
+ MATCH (Title, MenuTitle, Content, MetaTitle, MetaDescription, MetaKeywords) AGAINST ('$htmlEntityKeywords' $boolean)
MATCH (Title, MenuTitle, Content, MetaTitle, MetaDescription, MetaKeywords)
AGAINST ('$keywords' $boolean)
+ MATCH (Title, MenuTitle, Content, MetaTitle, MetaDescription, MetaKeywords)
AGAINST ('$htmlEntityKeywords' $boolean)
";
$match['File'] = "MATCH (Filename, Title, Content) AGAINST ('$keywords' $boolean) AND ClassName = 'File'";
// We make the relevance search by converting a boolean mode search into a normal one
$relevanceKeywords = str_replace(array('*','+','-'),'',$keywords);
$htmlEntityRelevanceKeywords = str_replace(array('*','+','-'),'',$htmlEntityKeywords);
$relevance['SiteTree'] = "MATCH (Title, MenuTitle, Content, MetaTitle, MetaDescription, MetaKeywords) AGAINST ('$relevanceKeywords') + MATCH (Title, MenuTitle, Content, MetaTitle, MetaDescription, MetaKeywords) AGAINST ('$htmlEntityRelevanceKeywords')";
$relevance['SiteTree'] = "MATCH (Title, MenuTitle, Content, MetaTitle, MetaDescription, MetaKeywords)"
. " AGAINST ('$relevanceKeywords') + MATCH (Title, MenuTitle, Content, MetaTitle, MetaDescription,"
. " MetaKeywords) AGAINST ('$htmlEntityRelevanceKeywords')";
$relevance['File'] = "MATCH (Filename, Title, Content) AGAINST ('$relevanceKeywords')";
} else {
$relevance['SiteTree'] = $relevance['File'] = 1;
@ -850,8 +878,20 @@ class MySQLDatabase extends SS_Database {
// Make column selection lists
$select = array(
'SiteTree' => array("ClassName","$baseClasses[SiteTree].\"ID\"","ParentID","Title","MenuTitle","URLSegment","Content","LastEdited","Created","Filename" => "_utf8''", "Name" => "_utf8''", "Relevance" => $relevance['SiteTree'], "CanViewType"),
'File' => array("ClassName","$baseClasses[File].\"ID\"","ParentID" => "_utf8''","Title","MenuTitle" => "_utf8''","URLSegment" => "_utf8''","Content","LastEdited","Created","Filename","Name", "Relevance" => $relevance['File'], "CanViewType" => "NULL"),
'SiteTree' => array(
"ClassName", "$baseClasses[SiteTree].\"ID\"", "ParentID",
"Title", "MenuTitle", "URLSegment", "Content",
"LastEdited", "Created",
"Filename" => "_utf8''", "Name" => "_utf8''",
"Relevance" => $relevance['SiteTree'], "CanViewType"
),
'File' => array(
"ClassName", "$baseClasses[File].\"ID\"", "ParentID" => "_utf8''",
"Title", "MenuTitle" => "_utf8''", "URLSegment" => "_utf8''", "Content",
"LastEdited", "Created",
"Filename", "Name",
"Relevance" => $relevance['File'], "CanViewType" => "NULL"
),
);
// Process and combine queries
@ -947,7 +987,8 @@ class MySQLDatabase extends SS_Database {
$SQL_keywords = Convert::raw2sql($keywords);
$SQL_htmlEntityKeywords = Convert::raw2sql(htmlentities($keywords, ENT_NOQUOTES, 'UTF-8'));
return "(MATCH ($fieldNames) AGAINST ('$SQL_keywords' $boolean) + MATCH ($fieldNames) AGAINST ('$SQL_htmlEntityKeywords' $boolean))";
return "(MATCH ($fieldNames) AGAINST ('$SQL_keywords' $boolean) + MATCH ($fieldNames)"
. " AGAINST ('$SQL_htmlEntityKeywords' $boolean))";
}
/*
@ -974,7 +1015,8 @@ class MySQLDatabase extends SS_Database {
/*
* Start a prepared transaction
* See http://developer.postgresql.org/pgdocs/postgres/sql-set-transaction.html for details on transaction isolation options
* See http://developer.postgresql.org/pgdocs/postgres/sql-set-transaction.html for details on transaction
* isolation options
*/
public function transactionStart($transaction_mode=false, $session_characteristics=false){
// This sets the isolation level for the NEXT transaction, not the current one.
@ -1019,7 +1061,8 @@ class MySQLDatabase extends SS_Database {
/**
* function to return an SQL datetime expression that can be used with MySQL
* used for querying a datetime in a certain format
* @param string $date to be formated, can be either 'now', literal datetime like '1973-10-14 10:30:00' or field name, e.g. '"SiteTree"."Created"'
* @param string $date to be formated, can be either 'now', literal datetime like '1973-10-14 10:30:00' or
* field name, e.g. '"SiteTree"."Created"'
* @param string $format to be used, supported specifiers:
* %Y = Year (four digits)
* %m = Month (01..12)
@ -1033,7 +1076,9 @@ class MySQLDatabase extends SS_Database {
public function formattedDatetimeClause($date, $format) {
preg_match_all('/%(.)/', $format, $matches);
foreach($matches[1] as $match) if(array_search($match, array('Y','m','d','H','i','s','U')) === false) user_error('formattedDatetimeClause(): unsupported format character %' . $match, E_USER_WARNING);
foreach($matches[1] as $match) if(array_search($match, array('Y','m','d','H','i','s','U')) === false) {
user_error('formattedDatetimeClause(): unsupported format character %' . $match, E_USER_WARNING);
}
if(preg_match('/^now$/i', $date)) {
$date = "NOW()";
@ -1050,8 +1095,10 @@ class MySQLDatabase extends SS_Database {
/**
* function to return an SQL datetime expression that can be used with MySQL
* used for querying a datetime addition
* @param string $date, can be either 'now', literal datetime like '1973-10-14 10:30:00' or field name, e.g. '"SiteTree"."Created"'
* @param string $interval to be added, use the format [sign][integer] [qualifier], e.g. -1 Day, +15 minutes, +1 YEAR
* @param string $date, can be either 'now', literal datetime like '1973-10-14 10:30:00' or field name,
* e.g. '"SiteTree"."Created"'
* @param string $interval to be added, use the format [sign][integer] [qualifier],
* e.g. -1 Day, +15 minutes, +1 YEAR
* supported qualifiers:
* - years
* - months
@ -1060,7 +1107,8 @@ class MySQLDatabase extends SS_Database {
* - minutes
* - seconds
* This includes the singular forms as well
* @return string SQL datetime expression to query for a datetime (YYYY-MM-DD hh:mm:ss) which is the result of the addition
* @return string SQL datetime expression to query for a datetime (YYYY-MM-DD hh:mm:ss) which is the result of
* the addition
*/
public function datetimeIntervalClause($date, $interval) {
@ -1078,9 +1126,12 @@ class MySQLDatabase extends SS_Database {
/**
* function to return an SQL datetime expression that can be used with MySQL
* used for querying a datetime substraction
* @param string $date1, can be either 'now', literal datetime like '1973-10-14 10:30:00' or field name, e.g. '"SiteTree"."Created"'
* @param string $date2 to be substracted of $date1, can be either 'now', literal datetime like '1973-10-14 10:30:00' or field name, e.g. '"SiteTree"."Created"'
* @return string SQL datetime expression to query for the interval between $date1 and $date2 in seconds which is the result of the substraction
* @param string $date1, can be either 'now', literal datetime like '1973-10-14 10:30:00' or field name,
* e.g. '"SiteTree"."Created"'
* @param string $date2 to be substracted of $date1, can be either 'now', literal datetime like
* '1973-10-14 10:30:00' or field name, e.g. '"SiteTree"."Created"'
* @return string SQL datetime expression to query for the interval between $date1 and $date2 in seconds which
* is the result of the substraction
*/
public function datetimeDifferenceClause($date1, $date2) {

View File

@ -99,7 +99,9 @@ class SQLQuery {
* @param array $having An array of HAVING clauses.
* @param array|string $limit A LIMIT clause or array with limit and offset keys
*/
public function __construct($select = "*", $from = array(), $where = array(), $orderby = array(), $groupby = array(), $having = array(), $limit = array()) {
public function __construct($select = "*", $from = array(), $where = array(), $orderby = array(),
$groupby = array(), $having = array(), $limit = array()) {
$this->setSelect($select);
$this->setFrom($from);
$this->setWhere($where);
@ -126,8 +128,12 @@ class SQLQuery {
public function __set($field, $value) {
if(strtolower($field) == 'select') Deprecation::notice('3.0', 'Please use setSelect() or addSelect() instead');
if(strtolower($field) == 'from') Deprecation::notice('3.0', 'Please use setFrom() or addFrom() instead');
if(strtolower($field) == 'groupby') Deprecation::notice('3.0', 'Please use setGroupBy() or addGroupBy() instead');
if(strtolower($field) == 'orderby') Deprecation::notice('3.0', 'Please use setOrderBy() or addOrderBy() instead');
if(strtolower($field) == 'groupby') {
Deprecation::notice('3.0', 'Please use setGroupBy() or addGroupBy() instead');
}
if(strtolower($field) == 'orderby') {
Deprecation::notice('3.0', 'Please use setOrderBy() or addOrderBy() instead');
}
if(strtolower($field) == 'having') Deprecation::notice('3.0', 'Please use setHaving() or addHaving() instead');
if(strtolower($field) == 'limit') Deprecation::notice('3.0', 'Please use setLimit() instead');
if(strtolower($field) == 'delete') Deprecation::notice('3.0', 'Please use setDelete() instead');
@ -348,9 +354,13 @@ class SQLQuery {
$tables = array();
foreach($this->from as $key => $tableClause) {
if(is_array($tableClause)) $table = '"'.$tableClause['table'].'"';
else if(is_string($tableClause) && preg_match('/JOIN +("[^"]+") +(AS|ON) +/i', $tableClause, $matches)) $table = $matches[1];
else $table = $tableClause;
if(is_array($tableClause)) {
$table = '"'.$tableClause['table'].'"';
} else if(is_string($tableClause) && preg_match('/JOIN +("[^"]+") +(AS|ON) +/i', $tableClause, $matches)) {
$table = $matches[1];
} else {
$table = $tableClause;
}
// Handle string replacements
if($this->replacementsOld) $table = str_replace($this->replacementsOld, $this->replacementsNew, $table);
@ -713,7 +723,8 @@ class SQLQuery {
$args = func_get_args();
if(isset($args[1])) {
Deprecation::notice('3.0', 'Multiple arguments to where is deprecated. Pleas use where("Column = Something") syntax instead');
Deprecation::notice('3.0',
'Multiple arguments to where is deprecated. Pleas use where("Column = Something") syntax instead');
}
return $this->addWhere($where);
@ -848,7 +859,8 @@ class SQLQuery {
/**
* Return an itemised select list as a map, where keys are the aliases, and values are the column sources.
* Aliases will always be provided (if the alias is implicit, the alias value will be inferred), and won't be quoted.
* Aliases will always be provided (if the alias is implicit, the alias value will be inferred), and won't be
* quoted.
* E.g., 'Title' => '"SiteTree"."Title"'.
*/
public function getSelect() {
@ -861,20 +873,23 @@ class SQLQuery {
* @return string
*/
public function sql() {
// TODO: Don't require this internal-state manipulate-and-preserve - let sqlQueryToString() handle the new syntax
// TODO: Don't require this internal-state manipulate-and-preserve - let sqlQueryToString() handle the new
// syntax
$origFrom = $this->from;
// Build from clauses
foreach($this->from as $alias => $join) {
// $join can be something like this array structure
// array('type' => 'inner', 'table' => 'SiteTree', 'filter' => array("SiteTree.ID = 1", "Status = 'approved'"))
// array('type' => 'inner', 'table' => 'SiteTree', 'filter' => array("SiteTree.ID = 1",
// "Status = 'approved'"))
if(is_array($join)) {
if(is_string($join['filter'])) $filter = $join['filter'];
else if(sizeof($join['filter']) == 1) $filter = $join['filter'][0];
else $filter = "(" . implode(") AND (", $join['filter']) . ")";
$aliasClause = ($alias != $join['table']) ? " AS \"" . Convert::raw2sql($alias) . "\"" : "";
$this->from[$alias] = strtoupper($join['type']) . " JOIN \"" . Convert::raw2sql($join['table']) . "\"$aliasClause ON $filter";
$this->from[$alias] = strtoupper($join['type']) . " JOIN \"" . Convert::raw2sql($join['table'])
. "\"$aliasClause ON $filter";
}
}

View File

@ -42,8 +42,8 @@ class SS_Transliterator extends Object {
'ô'=>'o', 'õ'=>'o', 'ö'=>'oe', 'ø'=>'o', 'ù'=>'u', 'ú'=>'u', 'û'=>'u', 'ü'=>'ue', 'ý'=>'y', 'ý'=>'y',
'þ'=>'b', 'ÿ'=>'y', 'Ŕ'=>'R', 'ŕ'=>'r',
'Ā'=>'A', 'ā'=>'a', 'Ē'=>'E', 'ē'=>'e', 'Ī'=>'I', 'ī'=>'i', 'Ō'=>'O', 'ō'=>'o', 'Ū'=>'U', 'ū'=>'u',
'œ'=>'oe', 'ß'=>'ss', 'ij'=>'ij',
'ą'=>'a','ę'=>'e', 'ė'=>'e', 'į'=>'i','ų'=>'u','ū'=>'u', 'Ą'=>'A','Ę'=>'E', 'Ė'=>'E', 'Į'=>'I','Ų'=>'U','Ū'=>'u'
'œ'=>'oe', 'ß'=>'ss', 'ij'=>'ij', 'ą'=>'a','ę'=>'e', 'ė'=>'e', 'į'=>'i','ų'=>'u','ū'=>'u', 'Ą'=>'A',
'Ę'=>'E', 'Ė'=>'E', 'Į'=>'I','Ų'=>'U','Ū'=>'u'
);
return strtr($source, $table);

View File

@ -66,7 +66,9 @@ class URLSegmentFilter extends Object {
$replacements = $this->getReplacements();
// Unset automated removal of non-ASCII characters, and don't try to transliterate
if($this->getAllowMultibyte() && isset($replacements['/[^A-Za-z0-9+.-]+/u'])) unset($replacements['/[^A-Za-z0-9+.-]+/u']);
if($this->getAllowMultibyte() && isset($replacements['/[^A-Za-z0-9+.-]+/u'])) {
unset($replacements['/[^A-Za-z0-9+.-]+/u']);
}
foreach($replacements as $regex => $replace) {
$name = preg_replace($regex, $replace, $name);

View File

@ -40,7 +40,8 @@ class ValidationResult extends Object {
if(!is_numeric($code)) {
$this->errorList[$code] = $message;
} else {
user_error("ValidationResult::error() - Don't use a numeric code '$code'. Use a string. I'm going to ignore it.", E_USER_WARNING);
user_error("ValidationResult::error() - Don't use a numeric code '$code'. Use a string."
. "I'm going to ignore it.", E_USER_WARNING);
$this->errorList[$code] = $message;
}
} else {

View File

@ -122,7 +122,9 @@ class Versioned extends DataExtension {
$dataQuery->setQueryParam('Versioned.mode', 'archive');
$dataQuery->setQueryParam('Versioned.date', $parts[1]);
} else if($parts[0] == 'Stage' && $parts[1] != $this->defaultStage && array_search($parts[1],$this->stages) !== false) {
} else if($parts[0] == 'Stage' && $parts[1] != $this->defaultStage
&& array_search($parts[1],$this->stages) !== false) {
$dataQuery->setQueryParam('Versioned.mode', 'stage');
$dataQuery->setQueryParam('Versioned.stage', $parts[1]);
}
@ -197,7 +199,8 @@ class Versioned extends DataExtension {
case 'stage_unique':
$stage = $dataQuery->getQueryParam('Versioned.stage');
// Recurse to do the default stage behavior (must be first, we rely on stage renaming happening before below)
// Recurse to do the default stage behavior (must be first, we rely on stage renaming happening before
// below)
$dataQuery->setQueryParam('Versioned.mode', 'stage');
$this->augmentSQL($query, $dataQuery);
@ -219,7 +222,8 @@ class Versioned extends DataExtension {
case 'latest_versions':
foreach($query->getFrom() as $alias => $join) {
if($alias != $baseTable) {
$query->setJoinFilter($alias, "\"$alias\".\"RecordID\" = \"{$baseTable}_versions\".\"RecordID\" AND \"$alias\".\"Version\" = \"{$baseTable}_versions\".\"Version\"");
$query->setJoinFilter($alias, "\"$alias\".\"RecordID\" = \"{$baseTable}_versions\".\"RecordID\""
. " AND \"$alias\".\"Version\" = \"{$baseTable}_versions\".\"Version\"");
}
$query->renameTable($alias, $alias . '_versions');
}
@ -249,7 +253,8 @@ class Versioned extends DataExtension {
}
break;
default:
throw new InvalidArgumentException("Bad value for query parameter Versioned.mode: " . $dataQuery->getQueryParam('Versioned.mode'));
throw new InvalidArgumentException("Bad value for query parameter Versioned.mode: "
. $dataQuery->getQueryParam('Versioned.mode'));
}
}
@ -333,7 +338,8 @@ class Versioned extends DataExtension {
// Create tables for other stages
foreach($this->stages as $stage) {
// Extra tables for _Live, etc.
//Change unique indexes to 'index'. Versioned tables may run into unique indexing difficulties otherwise.
// Change unique indexes to 'index'. Versioned tables may run into unique indexing difficulties
// otherwise.
foreach($indexes as $key=>$index){
if(is_array($index) && $index['type']=='unique'){
$indexes[$key]['type']='index';
@ -435,7 +441,8 @@ class Versioned extends DataExtension {
if(is_array($effectedIDs)) {
foreach($effectedIDs as $key => $value) {
DB::query("DELETE FROM \"{$table}_versions\" WHERE \"{$table}_versions\".\"ID\" = '$value'");
DB::query("DELETE FROM \"{$table}_versions\""
. " WHERE \"{$table}_versions\".\"ID\" = '$value'");
}
}
}
@ -490,7 +497,9 @@ class Versioned extends DataExtension {
// Add any extra, unchanged fields to the version record.
$data = DB::query("SELECT * FROM \"$table\" WHERE \"ID\" = $id")->record();
if($data) foreach($data as $k => $v) {
if (!isset($newManipulation['fields'][$k])) $newManipulation['fields'][$k] = "'" . Convert::raw2sql($v) . "'";
if (!isset($newManipulation['fields'][$k])) {
$newManipulation['fields'][$k] = "'" . Convert::raw2sql($v) . "'";
}
}
// Set up a new entry in (table)_versions
@ -501,7 +510,10 @@ class Versioned extends DataExtension {
if (isset($version_table[$table])) $nextVersion = $version_table[$table];
else unset($nextVersion);
if($rid && !isset($nextVersion)) $nextVersion = DB::query("SELECT MAX(\"Version\") + 1 FROM \"{$baseDataClass}_versions\" WHERE \"RecordID\" = $rid")->value();
if($rid && !isset($nextVersion)) {
$nextVersion = DB::query("SELECT MAX(\"Version\") + 1 FROM \"{$baseDataClass}_versions\""
. " WHERE \"RecordID\" = $rid")->value();
}
$newManipulation['fields']['Version'] = $nextVersion ? $nextVersion : 1;
@ -527,7 +539,9 @@ class Versioned extends DataExtension {
if(!$this->hasVersionField($table)) unset($manipulation[$table]['fields']['Version']);
// Grab a version number - it should be the same across all tables.
if(isset($manipulation[$table]['fields']['Version'])) $thisVersion = $manipulation[$table]['fields']['Version'];
if(isset($manipulation[$table]['fields']['Version'])) {
$thisVersion = $manipulation[$table]['fields']['Version'];
}
// If we're editing Live, then use (table)_Live instead of (table)
if(Versioned::current_stage() && Versioned::current_stage() != $this->defaultStage) {
@ -624,14 +638,17 @@ class Versioned extends DataExtension {
$table2 = $table1 . "_$this->liveStage";
return DB::query("SELECT \"$table1\".\"Version\" = \"$table2\".\"Version\" FROM \"$table1\" INNER JOIN \"$table2\" ON \"$table1\".\"ID\" = \"$table2\".\"ID\" WHERE \"$table1\".\"ID\" = ". $this->owner->ID)->value();
return DB::query("SELECT \"$table1\".\"Version\" = \"$table2\".\"Version\" FROM \"$table1\""
. " INNER JOIN \"$table2\" ON \"$table1\".\"ID\" = \"$table2\".\"ID\""
. " WHERE \"$table1\".\"ID\" = ". $this->owner->ID)->value();
}
/**
* Move a database record from one stage to the other.
* @param fromStage Place to copy from. Can be either a stage name or a version number.
* @param toStage Place to copy to. Must be a stage name.
* @param createNewVersion Set this to true to create a new version number. By default, the existing version number will be copied over.
* @param createNewVersion Set this to true to create a new version number. By default, the existing version
* number will be copied over.
*/
public function publish($fromStage, $toStage, $createNewVersion = false) {
$this->owner->extend('onBeforeVersionedPublish', $fromStage, $toStage, $createNewVersion);
@ -658,7 +675,8 @@ class Versioned extends DataExtension {
}
// Mark this version as having been published at some stage
DB::query("UPDATE \"{$extTable}_versions\" SET \"WasPublished\" = '1', \"PublisherID\" = $publisherID WHERE \"RecordID\" = $from->ID AND \"Version\" = $from->Version");
DB::query("UPDATE \"{$extTable}_versions\" SET \"WasPublished\" = '1', \"PublisherID\" = $publisherID"
. " WHERE \"RecordID\" = $from->ID AND \"Version\" = $from->Version");
$oldMode = Versioned::get_reading_mode();
Versioned::reading_stage($toStage);
@ -700,7 +718,9 @@ class Versioned extends DataExtension {
// We test for equality - if one of the versions doesn't exist, this will be false
//TODO: DB Abstraction: if statement here:
$stagesAreEqual = DB::query("SELECT CASE WHEN \"$table1\".\"Version\"=\"$table2\".\"Version\" THEN 1 ELSE 0 END FROM \"$table1\" INNER JOIN \"$table2\" ON \"$table1\".\"ID\" = \"$table2\".\"ID\" AND \"$table1\".\"ID\" = {$this->owner->ID}")->value();
$stagesAreEqual = DB::query("SELECT CASE WHEN \"$table1\".\"Version\"=\"$table2\".\"Version\""
. " THEN 1 ELSE 0 END FROM \"$table1\" INNER JOIN \"$table2\" ON \"$table1\".\"ID\" = \"$table2\".\"ID\""
. " AND \"$table1\".\"ID\" = {$this->owner->ID}")->value();
return !$stagesAreEqual;
}
@ -726,7 +746,10 @@ class Versioned extends DataExtension {
if(is_string($tableJoin) && $tableJoin[0] == '"') {
$baseTable = str_replace('"','',$tableJoin);
} elseif(is_string($tableJoin) && substr($tableJoin,0,5) != 'INNER') {
$query->setFrom(array($table => "LEFT JOIN \"$table\" ON \"$table\".\"RecordID\" = \"{$baseTable}_versions\".\"RecordID\" AND \"$table\".\"Version\" = \"{$baseTable}_versions\".\"Version\""));
$query->setFrom(array(
$table => "LEFT JOIN \"$table\" ON \"$table\".\"RecordID\"=\"{$baseTable}_versions\".\"RecordID\""
. " AND \"$table\".\"Version\" = \"{$baseTable}_versions\".\"Version\""
));
}
$query->renameTable($table, $table . '_versions');
}
@ -737,7 +760,8 @@ class Versioned extends DataExtension {
}
$query->addWhere("\"{$baseTable}_versions\".\"RecordID\" = '{$this->owner->ID}'");
$query->setOrderBy(($sort) ? $sort : "\"{$baseTable}_versions\".\"LastEdited\" DESC, \"{$baseTable}_versions\".\"Version\" DESC");
$query->setOrderBy(($sort) ? $sort
: "\"{$baseTable}_versions\".\"LastEdited\" DESC, \"{$baseTable}_versions\".\"Version\" DESC");
$records = $query->execute();
$versions = new ArrayList();
@ -915,7 +939,9 @@ class Versioned extends DataExtension {
// cache value (if required)
if($cache) {
if(!isset(self::$cache_versionnumber[$baseClass])) self::$cache_versionnumber[$baseClass] = array();
if(!isset(self::$cache_versionnumber[$baseClass][$stage])) self::$cache_versionnumber[$baseClass][$stage] = array();
if(!isset(self::$cache_versionnumber[$baseClass][$stage])) {
self::$cache_versionnumber[$baseClass][$stage] = array();
}
self::$cache_versionnumber[$baseClass][$stage][$id] = $version;
}
@ -930,7 +956,10 @@ class Versioned extends DataExtension {
$filter = "";
if($idList) {
// Validate the ID list
foreach($idList as $id) if(!is_numeric($id)) user_error("Bad ID passed to Versioned::prepopulate_versionnumber_cache() in \$idList: " . $id, E_USER_ERROR);
foreach($idList as $id) if(!is_numeric($id)) {
user_error("Bad ID passed to Versioned::prepopulate_versionnumber_cache() in \$idList: " . $id,
E_USER_ERROR);
}
$filter = "WHERE \"ID\" IN(" .implode(", ", $idList) . ")";
}
@ -955,7 +984,9 @@ class Versioned extends DataExtension {
* @param string $containerClass The container class for the result set (default is DataList)
* @return SS_List
*/
public static function get_by_stage($class, $stage, $filter = '', $sort = '', $join = '', $limit = '', $containerClass = 'DataList') {
public static function get_by_stage($class, $stage, $filter = '', $sort = '', $join = '', $limit = '',
$containerClass = 'DataList') {
$result = DataObject::get($class, $filter, $sort, $join, $limit, $containerClass);
$dq = $result->dataQuery();
$dq->setQueryParam('Versioned.mode', 'stage');
@ -1045,7 +1076,9 @@ class Versioned extends DataExtension {
*/
public static function get_version($class, $id, $version) {
$baseClass = ClassInfo::baseDataClass($class);
$list = DataList::create($baseClass)->where("\"$baseClass\".\"RecordID\" = $id")->where("\"$baseClass\".\"Version\" = " . (int)$version);
$list = DataList::create($baseClass)
->where("\"$baseClass\".\"RecordID\" = $id")
->where("\"$baseClass\".\"Version\" = " . (int)$version);
$list->dataQuery()->setQueryParam('Versioned.mode', 'all_versions');
return $list->First();
}

View File

@ -14,7 +14,14 @@ class Boolean extends DBField {
}
public function requireField() {
$parts=Array('datatype'=>'tinyint', 'precision'=>1, 'sign'=>'unsigned', 'null'=>'not null', 'default'=>$this->defaultVal, 'arrayValue'=>$this->arrayValue);
$parts=Array(
'datatype'=>'tinyint',
'precision'=>1,
'sign'=>'unsigned',
'null'=>'not null',
'default'=>$this->defaultVal,
'arrayValue'=>$this->arrayValue
);
$values=Array('type'=>'boolean', 'parts'=>$parts);
DB::requireField($this->tableName, $this->name, $values);
}

View File

@ -25,13 +25,15 @@
* if($this->getStreetName()) {
* $manipulation['fields']["{$this->name}Name"] = $this->prepValueForDB($this->getStreetName());
* } else {
* $manipulation['fields']["{$this->name}Name"] = DBField::create_field('Varchar', $this->getStreetName())->nullValue();
* $manipulation['fields']["{$this->name}Name"] = DBField::create_field('Varchar', $this->getStreetName())
* ->nullValue();
* }
*
* if($this->getStreetNumber()) {
* $manipulation['fields']["{$this->name}Number"] = $this->prepValueForDB($this->getStreetNumber());
* } else {
* $manipulation['fields']["{$this->name}Number"] = DBField::create_field('Int', $this->getStreetNumber())->nullValue();
* $manipulation['fields']["{$this->name}Number"] = DBField::create_field('Int', $this->getStreetNumber())
* ->nullValue();
* }
* }
*

View File

@ -5,15 +5,16 @@
*
* <b>Multi-value DBField objects</b>
*
* Sometimes you will want to make DBField classes that don't have a 1-1 match to database fields. To do this, there are a
* number of fields for you to overload.
* Sometimes you will want to make DBField classes that don't have a 1-1 match to database fields. To do this, there
* are a number of fields for you to overload.
* - Overload {@link writeToManipulation} to add the appropriate references to the INSERT or UPDATE command
* - Overload {@link addToQuery} to add the appropriate items to a SELECT query's field list
* - Add appropriate accessor methods
*
* <b>Subclass Example</b>
*
* The class is easy to overload with custom types, e.g. the MySQL "BLOB" type (http://dev.mysql.com/doc/refman/5.0/en/blob.html).
* The class is easy to overload with custom types, e.g. the MySQL "BLOB" type
* (http://dev.mysql.com/doc/refman/5.0/en/blob.html).
*
* <code>
* class Blob extends DBField {
@ -65,7 +66,8 @@ abstract class DBField extends ViewableData {
public static function create() {
Deprecation::notice('3.0', 'DBField::create() is deprecated as it clashes with Object::create(). Use DBField::create_field() instead.');
Deprecation::notice('3.0', 'DBField::create() is deprecated as it clashes with Object::create().'
. 'Use DBField::create_field() instead.');
return call_user_func_array(array('DBField', 'create_field'), func_get_args());
}
@ -87,7 +89,8 @@ abstract class DBField extends ViewableData {
*/
public function setName($name) {
if($this->name) {
user_error("DBField::setName() shouldn't be called once a DBField already has a name. It's partially immutable - it shouldn't be altered after it's given a value.", E_USER_WARNING);
user_error("DBField::setName() shouldn't be called once a DBField already has a name."
. "It's partially immutable - it shouldn't be altered after it's given a value.", E_USER_WARNING);
}
$this->name = $name;
}
@ -161,7 +164,8 @@ abstract class DBField extends ViewableData {
* @param array $manipulation
*/
public function writeToManipulation(&$manipulation) {
$manipulation['fields'][$this->name] = $this->exists() ? $this->prepValueForDB($this->value) : $this->nullValue();
$manipulation['fields'][$this->name] = $this->exists()
? $this->prepValueForDB($this->value) : $this->nullValue();
}
/**
@ -274,7 +278,8 @@ abstract class DBField extends ViewableData {
* @todo documentation
*
* @todo figure out how we pass configuration parameters to
* search filters (note: parameter hack now in place to pass in the required full path - using $this->name won't work)
* search filters (note: parameter hack now in place to pass in the required full path - using $this->name
* won't work)
*
* @return SearchFilter
*/

View File

@ -22,7 +22,8 @@ class Date extends DBField {
public function setValue($value, $record = null) {
if($value === false || $value === null || (is_string($value) && !strlen($value))) {
// don't try to evaluate empty values with strtotime() below, as it returns "1970-01-01" when it should be saved as NULL in database
// don't try to evaluate empty values with strtotime() below, as it returns "1970-01-01" when it should be
// saved as NULL in database
$this->value = null;
return;
}

View File

@ -27,7 +27,8 @@ class SS_Datetime extends Date implements TemplateGlobalProvider {
public function setValue($value, $record = null) {
if($value === false || $value === null || (is_string($value) && !strlen($value))) {
// don't try to evaluate empty values with strtotime() below, as it returns "1970-01-01" when it should be saved as NULL in database
// don't try to evaluate empty values with strtotime() below, as it returns "1970-01-01" when it should be
// saved as NULL in database
$this->value = null;
return;
}

View File

@ -26,7 +26,12 @@ class Decimal extends DBField {
}
public function requireField() {
$parts=Array('datatype'=>'decimal', 'precision'=>"$this->wholeSize,$this->decimalSize", 'default'=>(double)$this->defaultValue, 'arrayValue'=>$this->arrayValue);
$parts=Array(
'datatype'=>'decimal',
'precision'=>"$this->wholeSize,$this->decimalSize",
'default'=>(double)$this->defaultValue,
'arrayValue'=>$this->arrayValue);
$values=Array('type'=>'decimal', 'parts'=>$parts);
DB::requireField($this->tableName, $this->name, $values);
}

View File

@ -48,7 +48,8 @@ class Enum extends StringField {
if(in_array($default, $enum)) {
$this->default = $default;
} else {
user_error("Enum::__construct() The default value '$default' does not match any item in the enumeration", E_USER_ERROR);
user_error("Enum::__construct() The default value '$default' does not match any item in the"
. " enumeration", E_USER_ERROR);
}
// By default, set the default value to the first item
@ -87,7 +88,9 @@ class Enum extends StringField {
*
* @return DropdownField
*/
public function formField($title = null, $name = null, $hasEmpty = false, $value = "", $form = null, $emptyString = null) {
public function formField($title = null, $name = null, $hasEmpty = false, $value = "", $form = null,
$emptyString = null) {
if(!$title) $title = $this->name;
if(!$name) $name = $this->name;
@ -123,6 +126,8 @@ class Enum extends StringField {
* @return array
*/
public function enumValues($hasEmpty = false) {
return ($hasEmpty) ? array_merge(array('' => ''), ArrayLib::valuekey($this->enum)) : ArrayLib::valuekey($this->enum);
return ($hasEmpty)
? array_merge(array('' => ''), ArrayLib::valuekey($this->enum))
: ArrayLib::valuekey($this->enum);
}
}

View File

@ -14,7 +14,12 @@ class Float extends DBField {
}
public function requireField() {
$parts=Array('datatype'=>'float', 'null'=>'not null', 'default'=>$this->defaultVal, 'arrayValue'=>$this->arrayValue);
$parts=Array(
'datatype'=>'float',
'null'=>'not null',
'default'=>$this->defaultVal,
'arrayValue'=>$this->arrayValue);
$values=Array('type'=>'float', 'parts'=>$parts);
DB::requireField($this->tableName, $this->name, $values);
}

View File

@ -37,7 +37,8 @@ class HTMLText extends Text {
* 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
* 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
@ -53,11 +54,13 @@ class HTMLText extends Text {
if (class_exists('SimpleXMLElement')) {
$doc = new DOMDocument();
/* Catch warnings thrown by loadHTML and turn them into a failure boolean rather than a SilverStripe error */
// 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);
// Nonbreaking spaces get converted into weird characters, so strip them
$value = str_replace('&nbsp;', ' ', $this->value);
try { $res = $doc->loadHTML('<meta content="text/html; charset=utf-8" http-equiv="Content-type"/>' . $value); }
try {
$res = $doc->loadHTML('<meta content="text/html; charset=utf-8" http-equiv="Content-type"/>' . $value);
}
catch (Exception $e) { $res = false; }
restore_error_handler();
@ -68,8 +71,8 @@ class HTMLText extends Text {
}
}
/* 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 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*/
@ -81,28 +84,32 @@ class HTMLText extends Text {
if (!$str) $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 &lt; or &gt; */
/* Take out anything that is obviously a tag */
$str = preg_replace('{</?[a-zA-Z]+[^<>]*>}', '', $str);
/* Strip out any left over looking bits. Textual < or > should already be encoded to &lt; or &gt; */
$str = preg_replace('{</|<|>}', '', $str);
}
/* Now split into words. If we are under the maxWords limit, just return the whole string (re-implode for whitespace normalization) */
/* 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) */
/* 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 */
// 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()
* 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
*
@ -120,7 +127,8 @@ class HTMLText extends Text {
}
}
/* 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 */
/* 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();
}

View File

@ -25,7 +25,13 @@ class Int extends DBField {
}
public function requireField() {
$parts=Array('datatype'=>'int', 'precision'=>11, 'null'=>'not null', 'default'=>$this->defaultVal, 'arrayValue'=>$this->arrayValue);
$parts=Array(
'datatype'=>'int',
'precision'=>11,
'null'=>'not null',
'default'=>$this->defaultVal,
'arrayValue'=>$this->arrayValue);
$values=Array('type'=>'int', 'parts'=>$parts);
DB::requireField($this->tableName, $this->name, $values);
}

View File

@ -84,13 +84,15 @@ class Money extends DBField implements CompositeDBField {
if($this->getCurrency()) {
$manipulation['fields'][$this->name.'Currency'] = $this->prepValueForDB($this->getCurrency());
} else {
$manipulation['fields'][$this->name.'Currency'] = DBField::create_field('Varchar', $this->getCurrency())->nullValue();
$manipulation['fields'][$this->name.'Currency']
= DBField::create_field('Varchar', $this->getCurrency())->nullValue();
}
if($this->getAmount()) {
$manipulation['fields'][$this->name.'Amount'] = $this->getAmount();
} else {
$manipulation['fields'][$this->name.'Amount'] = DBField::create_field('Decimal', $this->getAmount())->nullValue();
$manipulation['fields'][$this->name.'Amount']
= DBField::create_field('Decimal', $this->getAmount())->nullValue();
}
}
@ -108,8 +110,11 @@ class Money extends DBField implements CompositeDBField {
if($markChanged) $this->isChanged = true;
} else if($record && isset($record[$this->name . 'Amount'])) {
if($record[$this->name . 'Amount']) {
if(!empty($record[$this->name . 'Currency'])) $this->setCurrency($record[$this->name . 'Currency'], $markChanged);
else if($currency = (string)$this->config()->get('default_currency')) $this->setCurrency($currency, $markChanged);
if(!empty($record[$this->name . 'Currency'])) {
$this->setCurrency($record[$this->name . 'Currency'], $markChanged);
} else if($currency = (string)$this->config()->get('default_currency')) {
$this->setCurrency($currency, $markChanged);
}
$this->setAmount($record[$this->name . 'Amount'], $markChanged);
} else {
@ -137,7 +142,9 @@ class Money extends DBField implements CompositeDBField {
$amount = $this->getAmount();
if(!isset($options['display'])) $options['display'] = Zend_Currency::USE_SYMBOL;
if(!isset($options['currency'])) $options['currency'] = $this->getCurrency();
if(!isset($options['symbol'])) $options['symbol'] = $this->currencyLib->getSymbol($this->getCurrency(), $this->getLocale());
if(!isset($options['symbol'])) {
$options['symbol'] = $this->currencyLib->getSymbol($this->getCurrency(), $this->getLocale());
}
return (is_numeric($amount)) ? $this->currencyLib->toCurrency($amount, $options) : '';
}

View File

@ -32,7 +32,18 @@ class MultiEnum extends Enum {
public function requireField(){
$values=array('type'=>'set', 'parts'=>array('enums'=>$this->enum, 'character set'=>'utf8', 'collate'=> 'utf8_general_ci', 'default'=>Convert::raw2sql($this->default), 'table'=>$this->tableName, 'arrayValue'=>$this->arrayValue));
$values=array(
'type'=>'set',
'parts'=>array(
'enums'=>$this->enum,
'character set'=>'utf8',
'collate'=> 'utf8_general_ci',
'default'=>Convert::raw2sql($this->default),
'table'=>$this->tableName,
'arrayValue'=>$this->arrayValue
)
);
DB::requireField($this->tableName, $this->name, $values);
}
@ -41,7 +52,9 @@ class MultiEnum extends Enum {
/**
* Return a {@link CheckboxSetField} suitable for editing this field
*/
public function formField($title = null, $name = null, $hasEmpty = false, $value = "", $form = null, $emptyString = null) {
public function formField($title = null, $name = null, $hasEmpty = false, $value = "", $form = null,
$emptyString = null) {
if(!$title) $title = $this->name;
if(!$name) $name = $this->name;

View File

@ -20,10 +20,12 @@ abstract class StringField extends DBField {
/**
* Construct a string type field with a set of optional parameters
* @param $name string The name of the field
* @param $options array An array of options e.g. array('nullifyEmpty'=>false). See {@link StringField::setOptions()} for information on the available options
* @param $options array An array of options e.g. array('nullifyEmpty'=>false). See
* {@link StringField::setOptions()} for information on the available options
*/
public function __construct($name = null, $options = array()) {
// Workaround: The singleton pattern calls this constructor with true/1 as the second parameter, so we must ignore it
// Workaround: The singleton pattern calls this constructor with true/1 as the second parameter, so we
// must ignore it
if(is_array($options)){
$this->setOptions($options);
}
@ -36,8 +38,8 @@ abstract class StringField extends DBField {
* The options allowed are:
* <ul><li>"nullifyEmpty"
* This is a boolean flag.
* True (the default) means that empty strings are automatically converted to nulls to be stored in the database.
* Set it to false to ensure that nulls and empty strings are kept intact in the database.
* True (the default) means that empty strings are automatically converted to nulls to be stored in
* the database. Set it to false to ensure that nulls and empty strings are kept intact in the database.
* </li></ul>
* @return unknown_type
*/

View File

@ -38,7 +38,12 @@ class Text extends StringField {
* @see DBField::requireField()
*/
public function requireField() {
$parts=Array('datatype'=>'mediumtext', 'character set'=>'utf8', 'collate'=>'utf8_general_ci', 'arrayValue'=>$this->arrayValue);
$parts=Array(
'datatype'=>'mediumtext',
'character set'=>'utf8',
'collate'=>'utf8_general_ci',
'arrayValue'=>$this->arrayValue
);
$values=Array('type'=>'text', 'parts'=>$parts);
DB::requireField($this->tableName, $this->name, $values, $this->default);
}
@ -269,7 +274,8 @@ class Text extends StringField {
*
* @return string
*/
public function ContextSummary($characters = 500, $string = false, $striphtml = true, $highlight = true, $prefix = "... ", $suffix = "...") {
public function ContextSummary($characters = 500, $string = false, $striphtml = true, $highlight = true,
$prefix = "... ", $suffix = "...") {
if(!$string) $string = $_REQUEST['Search']; // Use the default "Search" request variable (from SearchForm)
@ -284,7 +290,8 @@ class Text extends StringField {
if($position > 0) {
// We don't want to start mid-word
$position = max((int) strrpos(substr($text, 0, $position), ' '), (int) strrpos(substr($text, 0, $position), "\n"));
$position = max((int) strrpos(substr($text, 0, $position), ' '),
(int) strrpos(substr($text, 0, $position), "\n"));
}
$summary = substr($text, $position, $characters);
@ -296,7 +303,8 @@ class Text extends StringField {
foreach($stringPieces as $stringPiece) {
if(strlen($stringPiece) > 2) {
$summary = str_ireplace($stringPiece, "<span class=\"highlight\">$stringPiece</span>", $summary);
$summary = str_ireplace($stringPiece, "<span class=\"highlight\">$stringPiece</span>",
$summary);
}
}
}
@ -322,7 +330,8 @@ class Text extends StringField {
} else {
// Fallback to using raw2xml and show a warning
// TODO Don't kill script execution, we can continue without losing complete control of the app
user_error("Couldn't find an appropriate TextParser sub-class to create (Looked for '$parser'). Make sure it sub-classes TextParser and that you've done ?flush=1.", E_USER_WARNING);
user_error("Couldn't find an appropriate TextParser sub-class to create (Looked for '$parser')."
. "Make sure it sub-classes TextParser and that you've done ?flush=1.", E_USER_WARNING);
return Convert::raw2xml($this->value);
}
}

View File

@ -23,7 +23,8 @@ class Varchar extends StringField {
*
* @param $name string The name of the field
* @param $size int The maximum size of the field, in terms of characters
* @param $options array Optional parameters, e.g. array("nullifyEmpty"=>false). See {@link StringField::setOptions()} for information on the available options
* @param $options array Optional parameters, e.g. array("nullifyEmpty"=>false).
* See {@link StringField::setOptions()} for information on the available options
* @return unknown_type
*/
public function __construct($name = null, $size = 50, $options = array()) {

View File

@ -94,7 +94,11 @@ class Oembed {
$body = $body->getBody();
// Look within the body for an oembed link.
if(preg_match_all('#<link[^>]+?(?:href=[\'"](.+?)[\'"][^>]+?)?type=["\']application/json\+oembed["\'](?:[^>]+?href=[\'"](.+?)[\'"])?#', $body, $matches, PREG_SET_ORDER)) {
$pcreOmbed = '#<link[^>]+?(?:href=[\'"](.+?)[\'"][^>]+?)'
. '?type=["\']application/json\+oembed["\']'
. '(?:[^>]+?href=[\'"](.+?)[\'"])?#';
if(preg_match_all($pcreOmbed, $body, $matches, PREG_SET_ORDER)) {
$match = $matches[0];
if(!empty($match[1])) {
return html_entity_decode($match[1]);
@ -253,7 +257,9 @@ class Oembed_Result extends ViewableData {
$data['provider_url'] = $protocoll.$host;
$data['width'] = imagesx($image);
$data['height'] = imagesy($image);
$data['info'] = _t('UploadField.HOTLINKINFO', 'Info: This image will be hotlinked. Please ensure you have permissions from the original site creator to do so.');
$data['info'] = _t('UploadField.HOTLINKINFO',
'Info: This image will be hotlinked. Please ensure you have permissions from the'
. ' original site creator to do so.');
}
}

View File

@ -2,7 +2,9 @@
/**
* A simple parser that allows you to map BBCode-like "shortcodes" to an arbitrary callback.
*
* * The Shortcode API (new in 2.4) is a simple regex based parser that allows you to replace simple bbcode-like tags within a HTMLText or HTMLVarchar field when rendered into a template. It is inspired by and very similar to the [Wordpress implementation](http://codex.wordpress.org/Shortcode_API) of shortcodes. Examples of shortcode tags are:
* * The Shortcode API (new in 2.4) is a simple regex based parser that allows you to replace simple bbcode-like tags
* within a HTMLText or HTMLVarchar field when rendered into a template. It is inspired by and very similar to the
* [Wordpress implementation](http://codex.wordpress.org/Shortcode_API) of shortcodes. Examples of shortcode tags are:
*
* <code>
* [shortcode]
@ -13,7 +15,8 @@
*
* <b>Defining Custom Shortcodes</b>
*
* All you need to do to define a shortcode is to register a callback with the parser that will be called whenever a shortcode is encountered. This callback will return a string to replace the shortcode with.
* All you need to do to define a shortcode is to register a callback with the parser that will be called whenever a
* shortcode is encountered. This callback will return a string to replace the shortcode with.
*
* To register a shortcode you call:
*
@ -22,18 +25,22 @@
* </code>
*
* These parameters are passed to the callback:
* * Any parameters attached to the shortcode as an associative array (keys are lower-case).
* * Any content enclosed within the shortcode (if it is an enclosing shortcode). Note that any content within this will not have been parsed, and can optionally be fed back into the parser.
* * The ShortcodeParser instance used to parse the content.
* * The shortcode tag name that was matched within the parsed content.
* - Any parameters attached to the shortcode as an associative array (keys are lower-case).
* - Any content enclosed within the shortcode (if it is an enclosing shortcode). Note that any content within this
* will not have been parsed, and can optionally be fed back into the parser.
* - The ShortcodeParser instance used to parse the content.
* - The shortcode tag name that was matched within the parsed content.
*
* <b>Inbuilt Shortcodes</b>
*
* From 2.4 onwards links inserted via the CMS into a content field are in the form ''<a href="[sitetree_link id=n]">'', and from 3.0 the comma is used as a separator instead ''<a href="[sitetree_link,id=n]">''. At runtime this is replaced by a plain link to the page with the ID in question.
* From 2.4 onwards links inserted via the CMS into a content field are in the form ''<a href="[sitetree_link id=n]">
* '', and from 3.0 the comma is used as a separator instead ''<a href="[sitetree_link,id=n]">''. At runtime this is
* replaced by a plain link to the page with the ID in question.
*
* <b>Limitations</b>
*
* Since the shortcode parser is based on a simple regular expression it cannot properly handle nested shortcodes. For example the below code will not work as expected:
* Since the shortcode parser is based on a simple regular expression it cannot properly handle nested shortcodes. For
* example the below code will not work as expected:
*
* <code>
* [shortcode]
@ -58,11 +65,11 @@ class ShortcodeParser {
private static $active_instance = 'default';
// -----------------------------------------------------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------
protected $shortcodes = array();
// -----------------------------------------------------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------
/**
* Get the {@link ShortcodeParser} instance that is attached to a particular identifier.
@ -96,7 +103,7 @@ class ShortcodeParser {
self::$active_instance = (string) $identifier;
}
// -----------------------------------------------------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------
/**
* Register a shortcode, and attach it to a PHP callback.
@ -141,7 +148,7 @@ class ShortcodeParser {
$this->shortcodes = array();
}
// -----------------------------------------------------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------
/**
* Parse a string, and replace any registered shortcodes within it with the result of the mapped callback.
@ -197,7 +204,9 @@ class ShortcodeParser {
}
}
return call_user_func($this->shortcodes[$shortcode], $attributes, isset($matches[4][0]) ? $matches[4][0] : '', $this, $shortcode);
return call_user_func(
$this->shortcodes[$shortcode],
$attributes, isset($matches[4][0]) ? $matches[4][0] : '', $this, $shortcode);
}
}

View File

@ -55,7 +55,8 @@ class FulltextSearchable extends DataExtension {
Config::inst()->update($class, 'create_table_options', array('MySQLDatabase' => 'ENGINE=MyISAM'));
Object::add_extension($class, "FulltextSearchable('{$defaultColumns[$class]}')");
} else {
throw new Exception("FulltextSearchable::enable() I don't know the default search columns for class '$class'");
throw new Exception(
"FulltextSearchable::enable() I don't know the default search columns for class '$class'");
}
}
self::$searchable_classes = $searchableClasses;

View File

@ -116,15 +116,23 @@ class SearchContext extends Object {
*/
public function getQuery($searchParams, $sort = false, $limit = false, $existingQuery = null) {
if($existingQuery) {
if(!($existingQuery instanceof DataList)) throw new InvalidArgumentException("existingQuery must be DataList");
if($existingQuery->dataClass() != $this->modelClass) throw new InvalidArgumentException("existingQuery's dataClass is " . $existingQuery->dataClass() . ", $this->modelClass expected.");
if(!($existingQuery instanceof DataList)) {
throw new InvalidArgumentException("existingQuery must be DataList");
}
if($existingQuery->dataClass() != $this->modelClass) {
throw new InvalidArgumentException("existingQuery's dataClass is " . $existingQuery->dataClass()
. ", $this->modelClass expected.");
}
$query = $existingQuery;
} else {
$query = DataList::create($this->modelClass);
}
if(is_array($limit)) {
$query = $query->limit(isset($limit['limit']) ? $limit['limit'] : null, isset($limit['start']) ? $limit['start'] : null);
$query = $query->limit(
isset($limit['limit']) ? $limit['limit'] : null,
isset($limit['start']) ? $limit['start'] : null
);
} else {
$query = $query->limit($limit);
}
@ -150,7 +158,9 @@ class SearchContext extends Object {
}
}
if($this->connective != "AND") throw new Exception("SearchContext connective '$this->connective' not supported after ORM-rewrite.");
if($this->connective != "AND") {
throw new Exception("SearchContext connective '$this->connective' not supported after ORM-rewrite.");
}
return $query;
}

View File

@ -142,10 +142,15 @@ abstract class SearchFilter extends Object {
// Todo: move to somewhere more appropriate, such as DataMapper, the magical class-to-be?
$candidateClass = $this->model;
while($candidateClass != 'DataObject') {
if(DataObject::has_own_table($candidateClass) && singleton($candidateClass)->hasOwnTableDatabaseField($this->name)) break;
if(DataObject::has_own_table($candidateClass)
&& singleton($candidateClass)->hasOwnTableDatabaseField($this->name)) {
break;
}
$candidateClass = get_parent_class($candidateClass);
}
if($candidateClass == 'DataObject') user_error("Couldn't find field $this->name in any of $this->model's tables.", E_USER_ERROR);
if($candidateClass == 'DataObject') {
user_error("Couldn't find field $this->name in any of $this->model's tables.", E_USER_ERROR);
}
return "\"$candidateClass\".\"$this->name\"";
}

View File

@ -111,7 +111,10 @@ class ChangePasswordForm extends Form {
}
else {
// Redirect to default location - the login form saying "You are logged in as..."
$redirectURL = HTTP::setGetVar('BackURL', Director::absoluteBaseURL(), $this->controller->Link('login'));
$redirectURL = HTTP::setGetVar(
'BackURL',
Director::absoluteBaseURL(), $this->controller->Link('login')
);
$this->controller->redirect($redirectURL);
}
} else {

View File

@ -144,7 +144,8 @@ class Group extends DataObject {
"<p>" .
_t(
'SecurityAdmin.ROLESDESCRIPTION',
"Roles are predefined sets of permissions, and can be assigned to groups.<br />They are inherited from parent groups if required."
"Roles are predefined sets of permissions, and can be assigned to groups.<br />"
. "They are inherited from parent groups if required."
) . '<br />' .
sprintf(
'<a href="%s" class="add-role">%s</a>',
@ -158,7 +159,9 @@ class Group extends DataObject {
);
// Add roles (and disable all checkboxes for inherited roles)
$allRoles = Permission::check('ADMIN') ? DataObject::get('PermissionRole') : DataObject::get('PermissionRole', 'OnlyAdminCanApply = 0');
$allRoles = Permission::check('ADMIN')
? DataObject::get('PermissionRole')
: DataObject::get('PermissionRole', 'OnlyAdminCanApply = 0');
if($this->ID) {
$groupRoles = $this->Roles();
$inheritedRoles = new ArrayList();
@ -179,7 +182,9 @@ class Group extends DataObject {
->setDefaultItems($groupRoleIDs)
->setAttribute('data-placeholder', _t('Group.AddRole', 'Add a role for this group'))
->setDisabledItems($inheritedRoleIDs);
if(!$allRoles->Count()) $rolesField->setAttribute('data-placeholder', _t('Group.NoRoles', 'No roles found'));
if(!$allRoles->Count()) {
$rolesField->setAttribute('data-placeholder', _t('Group.NoRoles', 'No roles found'));
}
$fields->addFieldToTab('Root.Roles', $rolesField);
}
@ -230,13 +235,16 @@ class Group extends DataObject {
*/
public function Members($filter = "", $sort = "", $join = "", $limit = "") {
if($sort || $join || $limit) {
Deprecation::notice('3.0', "The sort, join, and limit arguments are deprcated, use sort(), join() and limit() on the resulting DataList instead.");
Deprecation::notice('3.0',
"The sort, join, and limit arguments are deprcated, use sort(), join() and limit() on the resulting"
. " DataList instead.");
}
// First get direct members as a base result
$result = $this->DirectMembers();
// Remove the default foreign key filter in prep for re-applying a filter containing all children groups.
// Filters are conjunctive in DataQuery by default, so this filter would otherwise overrule any less specific ones.
// Filters are conjunctive in DataQuery by default, so this filter would otherwise overrule any less specific
// ones.
$result->dataQuery()->removeFilterOn('Group_Members');
// Now set all children groups as a new foreign key
$groups = Group::get()->byIDs($this->collateFamilyIDs());
@ -308,7 +316,11 @@ class Group extends DataObject {
* Override this so groups are ordered in the CMS
*/
public function stageChildren() {
return DataObject::get('Group', "\"Group\".\"ParentID\" = " . (int)$this->ID . " AND \"Group\".\"ID\" != " . (int)$this->ID, '"Sort"');
return DataObject::get(
'Group',
"\"Group\".\"ParentID\" = " . (int)$this->ID . " AND \"Group\".\"ID\" != " . (int)$this->ID,
'"Sort"'
);
}
/**

View File

@ -33,7 +33,8 @@ abstract class LoginForm extends Form {
public function getAuthenticator() {
if(!class_exists($this->authenticator_class) || !is_subclass_of($this->authenticator_class, 'Authenticator')) {
user_error("The form uses an invalid authenticator class! '{$this->authenticator_class}' is not a subclass of 'Authenticator'", E_USER_ERROR);
user_error("The form uses an invalid authenticator class! '{$this->authenticator_class}'"
. " is not a subclass of 'Authenticator'", E_USER_ERROR);
return;
}

View File

@ -50,7 +50,8 @@ class Member extends DataObject implements TemplateGlobalProvider {
static $indexes = array(
'Email' => true,
//'AutoLoginHash' => Array('type'=>'unique', 'value'=>'AutoLoginHash', 'ignoreNulls'=>true) //Removed due to duplicate null values causing MSSQL problems
//Removed due to duplicate null values causing MSSQL problems
//'AutoLoginHash' => Array('type'=>'unique', 'value'=>'AutoLoginHash', 'ignoreNulls'=>true)
);
static $notify_password_change = false;
@ -455,7 +456,10 @@ class Member extends DataObject implements TemplateGlobalProvider {
public static function member_from_autologinhash($RAW_hash, $login = false) {
$SQL_hash = Convert::raw2sql($RAW_hash);
$member = DataObject::get_one('Member',"\"AutoLoginHash\"='" . $SQL_hash . "' AND \"AutoLoginExpired\" > " . DB::getConn()->now());
$member = DataObject::get_one(
'Member',
"\"AutoLoginHash\"='" . $SQL_hash . "' AND \"AutoLoginExpired\" > " . DB::getConn()->now()
);
if($login && $member)
$member->logIn();
@ -471,7 +475,8 @@ class Member extends DataObject implements TemplateGlobalProvider {
* @param array $data Additional data to pass to the email (can be used in the template)
*/
public function sendInfo($type = 'signup', $data = null) {
Deprecation::notice('3.0', 'Please use Member_ChangePasswordEmail or Member_ForgotPasswordEmail directly instead');
Deprecation::notice('3.0',
'Please use Member_ChangePasswordEmail or Member_ForgotPasswordEmail directly instead');
switch($type) {
case "changePassword":
@ -627,7 +632,7 @@ class Member extends DataObject implements TemplateGlobalProvider {
throw new ValidationException(new ValidationResult(false, _t(
'Member.ValidationIdentifierFailed',
'Can\'t overwrite existing member #{id} with identical identifier ({name} = {value}))',
'The values in brackets show a fieldname mapped to a value, usually denoting an existing email address',
'Values in brackets show "fieldname = value", usually denoting an existing email address',
array(
'id' => $existingRecord->ID,
'name' => $identifierField,
@ -1049,7 +1054,11 @@ class Member extends DataObject implements TemplateGlobalProvider {
$SQL_perms = "'" . implode("', '", Convert::raw2sql($perms)) . "'";
$groups = DataObject::get('Group')->innerJoin("Permission", "\"Permission\".\"GroupID\" = \"Group\".\"ID\" AND \"Permission\".\"Code\" IN ($SQL_perms)");
$groups = DataObject::get('Group')
->innerJoin(
"Permission",
"\"Permission\".\"GroupID\" = \"Group\".\"ID\" AND \"Permission\".\"Code\" IN ($SQL_perms)"
);
}
$groupIDList = array();
@ -1244,7 +1253,8 @@ class Member extends DataObject implements TemplateGlobalProvider {
$labels['DateFormat'] = _t('Member.DATEFORMAT', 'Date format');
$labels['TimeFormat'] = _t('Member.TIMEFORMAT', 'Time format');
if($includerelations){
$labels['Groups'] = _t('Member.belongs_many_many_Groups', 'Groups', 'Security Groups this member belongs to');
$labels['Groups'] = _t('Member.belongs_many_many_Groups', 'Groups',
'Security Groups this member belongs to');
}
return $labels;
}

View File

@ -75,7 +75,10 @@ class MemberAuthenticator extends Authenticator {
$member->extend('authenticated');
} else {
// failed login - we're trying to see if a user exists with this email (disregarding wrong passwords)
$existingMember = DataObject::get_one("Member", "\"" . Member::get_unique_identifier_field() . "\" = '$SQL_user'");
$existingMember = DataObject::get_one(
"Member",
"\"" . Member::get_unique_identifier_field() . "\" = '$SQL_user'"
);
if($existingMember) {
$attempt->MemberID = $existingMember->ID;

View File

@ -61,7 +61,8 @@ class MemberLoginForm extends LoginForm {
$label=singleton('Member')->fieldLabel(Member::get_unique_identifier_field());
$fields = new FieldList(
new HiddenField("AuthenticationMethod", null, $this->authenticator_class, $this),
//Regardless of what the unique identifer field is (usually 'Email'), it will be held in the 'Email' value, below:
// Regardless of what the unique identifer field is (usually 'Email'), it will be held in the
// 'Email' value, below:
new TextField("Email", $label, Session::get('SessionForms.MemberLoginForm.Email'), null, $this),
new PasswordField("Password", _t('Member.PASSWORD', 'Password'))
);
@ -77,7 +78,8 @@ class MemberLoginForm extends LoginForm {
new FormAction('dologin', _t('Member.BUTTONLOGIN', "Log in")),
new LiteralField(
'forgotPassword',
'<p id="ForgotPassword"><a href="Security/lostpassword">' . _t('Member.BUTTONLOSTPASSWORD', "I've lost my password") . '</a></p>'
'<p id="ForgotPassword"><a href="Security/lostpassword">'
. _t('Member.BUTTONLOSTPASSWORD', "I've lost my password") . '</a></p>'
)
);
}

View File

@ -113,7 +113,8 @@ abstract class PasswordEncryptor {
* @deprecated 3.0 - Use PasswordEncryptor::check() instead.
*/
public function compare($hash1, $hash2) {
Deprecation::notice('3.0.0', 'PasswordEncryptor::compare() is deprecated, replaced by PasswordEncryptor::check().');
Deprecation::notice('3.0.0',
'PasswordEncryptor::compare() is deprecated, replaced by PasswordEncryptor::check().');
return ($hash1 === $hash2);
}
@ -256,9 +257,13 @@ class PasswordEncryptor_Blowfish extends PasswordEncryptor {
* so we need to test it.
*/
public function checkAEncryptionLevel() {
// Test hashes taken from http://cvsweb.openwall.com/cgi/cvsweb.cgi/~checkout~/Owl/packages/glibc/crypt_blowfish/wrapper.c?rev=1.9.2.1;content-type=text%2Fplain
$xOrY = crypt("\xff\xa334\xff\xff\xff\xa3345", '$2a$05$/OK.fbVrR/bpIqNJ5ianF.o./n25XVfn6oAPaUvHe.Csk4zRfsYPi') == '$2a$05$/OK.fbVrR/bpIqNJ5ianF.o./n25XVfn6oAPaUvHe.Csk4zRfsYPi';
$yOrA = crypt("\xa3", '$2a$05$/OK.fbVrR/bpIqNJ5ianF.Sa7shbm4.OzKpvFnX1pQLmQW96oUlCq') == '$2a$05$/OK.fbVrR/bpIqNJ5ianF.Sa7shbm4.OzKpvFnX1pQLmQW96oUlCq';
// Test hashes taken from
// http://cvsweb.openwall.com/cgi/cvsweb.cgi/~checkout~/Owl/packages/glibc
// /crypt_blowfish/wrapper.c?rev=1.9.2.1;content-type=text%2Fplain
$xOrY = crypt("\xff\xa334\xff\xff\xff\xa3345", '$2a$05$/OK.fbVrR/bpIqNJ5ianF.o./n25XVfn6oAPaUvHe.Csk4zRfsYPi')
== '$2a$05$/OK.fbVrR/bpIqNJ5ianF.o./n25XVfn6oAPaUvHe.Csk4zRfsYPi';
$yOrA = crypt("\xa3", '$2a$05$/OK.fbVrR/bpIqNJ5ianF.Sa7shbm4.OzKpvFnX1pQLmQW96oUlCq')
== '$2a$05$/OK.fbVrR/bpIqNJ5ianF.Sa7shbm4.OzKpvFnX1pQLmQW96oUlCq';
if($xOrY && $yOrA) {
return 'y';
@ -352,7 +357,8 @@ class PasswordEncryptor_LegacyPHPHash extends PasswordEncryptor_PHPHash {
}
public function compare($hash1, $hash2) {
Deprecation::notice('3.0.0', 'PasswordEncryptor::compare() is deprecated, replaced by PasswordEncryptor::check().');
Deprecation::notice('3.0.0',
'PasswordEncryptor::compare() is deprecated, replaced by PasswordEncryptor::check().');
// Due to flawed base_convert() floating poing precision,
// only the first 10 characters are consistently useful for comparisons.

View File

@ -61,7 +61,12 @@ class PasswordValidator extends Object {
$valid = new ValidationResult();
if($this->minLength) {
if(strlen($password) < $this->minLength) $valid->error(sprintf("Password is too short, it must be %s or more characters long.", $this->minLength), "TOO_SHORT");
if(strlen($password) < $this->minLength) {
$valid->error(
sprintf("Password is too short, it must be %s or more characters long.", $this->minLength),
"TOO_SHORT"
);
}
}
if($this->minScore) {
@ -73,15 +78,28 @@ class PasswordValidator extends Object {
}
if($score < $this->minScore) {
$valid->error("You need to increase the strength of your passwords by adding some of the following characters: " . implode(", ", $missedTests), "LOW_CHARACTER_STRENGTH");
$valid->error(
"You need to increase the strength of your passwords by adding some of the following characters: "
. implode(", ", $missedTests),
"LOW_CHARACTER_STRENGTH"
);
}
}
if($this->historicalPasswordCount) {
$previousPasswords = DataObject::get("MemberPassword", "\"MemberID\" = $member->ID", "\"Created\" DESC, \"ID\" Desc", "", $this->historicalPasswordCount);
$previousPasswords = DataObject::get(
"MemberPassword",
"\"MemberID\" = $member->ID",
"\"Created\" DESC, \"ID\" Desc",
"",
$this->historicalPasswordCount
);
if($previousPasswords) foreach($previousPasswords as $previousPasswords) {
if($previousPasswords->checkPassword($password)) {
$valid->error("You've already used that password in the past, please choose a new password", "PREVIOUS_PASSWORD");
$valid->error(
"You've already used that password in the past, please choose a new password",
"PREVIOUS_PASSWORD"
);
break;
}
}

View File

@ -410,7 +410,8 @@ class Permission extends DataObject implements TemplateGlobalProvider {
$SQL_codes = join("','", $SQLa_codes);
// Via Roles are groups that have the permission via a role
return DataObject::get('Group')->where("\"PermissionRoleCode\".\"Code\" IN ('$SQL_codes') OR \"Permission\".\"Code\" IN ('$SQL_codes')")
return DataObject::get('Group')
->where("\"PermissionRoleCode\".\"Code\" IN ('$SQL_codes') OR \"Permission\".\"Code\" IN ('$SQL_codes')")
->leftJoin('Permission', "\"Permission\".\"GroupID\" = \"Group\".\"ID\"")
->leftJoin('Group_Roles', "\"Group_Roles\".\"GroupID\" = \"Group\".\"ID\"")
->leftJoin('PermissionRole', "\"Group_Roles\".\"PermissionRoleID\" = \"PermissionRole\".\"ID\"")
@ -453,8 +454,10 @@ class Permission extends DataObject implements TemplateGlobalProvider {
foreach($someCodes as $k => $v) {
if (is_array($v)) {
// There must be a category and name key.
if (!isset($v['category'])) user_error("The permission $k must have a category key", E_USER_WARNING);
if (!isset($v['name'])) user_error("The permission $k must have a name key", E_USER_WARNING);
if (!isset($v['category'])) user_error("The permission $k must have a category key",
E_USER_WARNING);
if (!isset($v['name'])) user_error("The permission $k must have a name key",
E_USER_WARNING);
if (!isset($allCodes[$v['category']])) $allCodes[$v['category']] = array();
@ -600,8 +603,7 @@ class Permission extends DataObject implements TemplateGlobalProvider {
* @param $list List of permissions in the structure. The result will be
* written to this array.
*/
protected static function traverse_declared_permissions($declared,
&$list) {
protected static function traverse_declared_permissions($declared, &$list) {
if(!is_array($declared))
return;

View File

@ -47,7 +47,8 @@ class PermissionCheckboxSetField extends FormField {
} elseif($records instanceof Group) {
$this->records = new ArrayList(array($records));
} elseif($records) {
throw new InvalidArgumentException('$record should be either a Group record, or a SS_List of Group records');
throw new InvalidArgumentException(
'$record should be either a Group record, or a SS_List of Group records');
}
// Get all available codes in the system as a categorized nested array
@ -140,7 +141,9 @@ class PermissionCheckboxSetField extends FormField {
}
if ($parent->Permissions()->Count()) {
foreach($parent->Permissions() as $permission) {
if (!isset($inheritedCodes[$permission->Code])) $inheritedCodes[$permission->Code] = array();
if (!isset($inheritedCodes[$permission->Code])) {
$inheritedCodes[$permission->Code] = array();
}
$inheritedCodes[$permission->Code][] =
_t(
'PermissionCheckboxSetField.FromGroup',
@ -172,11 +175,16 @@ class PermissionCheckboxSetField extends FormField {
$extraClass .= ' val' . str_replace(' ', '', $code);
$itemID = $this->id() . '_' . preg_replace('/[^a-zA-Z0-9]+/', '', $code);
$checked = $disabled = $inheritMessage = '';
$checked = (isset($uninheritedCodes[$code]) || isset($inheritedCodes[$code])) ? ' checked="checked"' : '';
$title = $permission['help'] ? 'title="' . htmlentities($permission['help'], ENT_COMPAT, 'UTF-8') . '" ' : '';
$checked = (isset($uninheritedCodes[$code]) || isset($inheritedCodes[$code]))
? ' checked="checked"'
: '';
$title = $permission['help']
? 'title="' . htmlentities($permission['help'], ENT_COMPAT, 'UTF-8') . '" '
: '';
if (isset($inheritedCodes[$code])) {
// disable inherited codes, as any saving logic would be too complicate to express in this interface
// disable inherited codes, as any saving logic would be too complicate to express in this
// interface
$disabled = ' disabled="true"';
$inheritMessage = ' (' . join(', ', $inheritedCodes[$code]) . ')';
} elseif($this->records && $this->records->Count() > 1 && isset($uninheritedCodes[$code])) {
@ -192,15 +200,19 @@ class PermissionCheckboxSetField extends FormField {
// If the field is readonly, add a span that will replace the disabled checkbox input
if($this->readonly) {
$options .= "<li class=\"$extraClass\">" .
"<input id=\"$itemID\"$disabled name=\"$this->name[$code]\" type=\"checkbox\" value=\"$code\"$checked class=\"checkbox\" />" .
"<label {$title}for=\"$itemID\"><span class=\"ui-button-icon-primary ui-icon btn-icon-accept\"></span>$value$inheritMessage</label>" .
"</li>\n";
$options .= "<li class=\"$extraClass\">"
. "<input id=\"$itemID\"$disabled name=\"$this->name[$code]\" type=\"checkbox\""
. " value=\"$code\"$checked class=\"checkbox\" />"
. "<label {$title}for=\"$itemID\">"
. "<span class=\"ui-button-icon-primary ui-icon btn-icon-accept\"></span>"
. "$value$inheritMessage</label>"
. "</li>\n";
} else {
$options .= "<li class=\"$extraClass\">" .
"<input id=\"$itemID\"$disabled name=\"$this->name[$code]\" type=\"checkbox\" value=\"$code\"$checked class=\"checkbox\" />" .
"<label {$title}for=\"$itemID\">$value$inheritMessage</label>" .
"</li>\n";
$options .= "<li class=\"$extraClass\">"
. "<input id=\"$itemID\"$disabled name=\"$this->name[$code]\" type=\"checkbox\""
. " value=\"$code\"$checked class=\"checkbox\" />"
. "<label {$title}for=\"$itemID\">$value$inheritMessage</label>"
. "</li>\n";
}
}
}
@ -210,7 +222,8 @@ class PermissionCheckboxSetField extends FormField {
"<li class=\"help\">" .
_t(
'Permissions.UserPermissionsIntro',
'Assigning groups to this user will adjust the permissions they have. See the groups section for details of permissions on individual groups.'
'Assigning groups to this user will adjust the permissions they have.'
. ' See the groups section for details of permissions on individual groups.'
) .
"</li>" .
$options .

View File

@ -1,7 +1,8 @@
<?php
/**
* Used to let classes provide new permission codes.
* Every implementor of PermissionProvider is accessed and providePermissions() called to get the full list of permission codes.
* Every implementor of PermissionProvider is accessed and providePermissions() called to get the full list of
* permission codes.
* @package framework
* @subpackage security
*/

View File

@ -353,7 +353,8 @@ class Security extends Controller {
$content = '';
$forms = $this->GetLoginForms();
if(!count($forms)) {
user_error('No login-forms found, please use Authenticator::register_authenticator() to add one', E_USER_ERROR);
user_error('No login-forms found, please use Authenticator::register_authenticator() to add one',
E_USER_ERROR);
}
// only display tabs when more than one authenticator is provided
@ -380,8 +381,12 @@ class Security extends Controller {
$content_forms = '';
foreach($forms as $form) {
$content .= "<li><a href=\"#{$form->FormName()}_tab\">{$form->getAuthenticator()->get_name()}</a></li>\n";
$content_forms .= '<div class="tab" id="' . $form->FormName() . '_tab">' . $form->forTemplate() . "</div>\n";
$content .= "<li><a href=\"#{$form->FormName()}_tab\">"
. $form->getAuthenticator()->get_name()
. "</a></li>\n";
$content_forms .= '<div class="tab" id="' . $form->FormName() . '_tab">'
. $form->forTemplate() . "</div>\n";
}
$content .= "</ul>\n" . $content_forms . "\n</div>\n</div>\n";
@ -410,7 +415,9 @@ class Security extends Controller {
Session::clear('Security.Message');
// custom processing
return $customisedController->renderWith(array('Security_login', 'Security', $this->stat('template_main'), 'BlankPage'));
return $customisedController->renderWith(
array('Security_login', 'Security', $this->stat('template_main'), 'BlankPage')
);
}
public function basicauthlogin() {
@ -447,7 +454,9 @@ class Security extends Controller {
));
//Controller::$currentController = $controller;
return $customisedController->renderWith(array('Security_lostpassword', 'Security', $this->stat('template_main'), 'BlankPage'));
return $customisedController->renderWith(
array('Security_lostpassword', 'Security', $this->stat('template_main'), 'BlankPage')
);
}
@ -495,16 +504,22 @@ class Security extends Controller {
$email = Convert::raw2xml(rawurldecode($request->param('ID')) . '.' . $request->getExtension());
$customisedController = $controller->customise(array(
'Title' => _t('Security.PASSWORDSENTHEADER', "Password reset link sent to '{email}'", array('email' => $email)),
'Title' => _t('Security.PASSWORDSENTHEADER', "Password reset link sent to '{email}'",
array('email' => $email)),
'Content' =>
"<p>" .
_t('Security.PASSWORDSENTTEXT', "Thank you! A reset link has been sent to '{email}', provided an account exists for this email address.", array('email' => $email)) .
"</p>",
"<p>"
. _t('Security.PASSWORDSENTTEXT',
"Thank you! A reset link has been sent to '{email}', provided an account exists for this email"
. " address.",
array('email' => $email))
. "</p>",
'Email' => $email
));
//Controller::$currentController = $controller;
return $customisedController->renderWith(array('Security_passwordsent', 'Security', $this->stat('template_main'), 'BlankPage'));
return $customisedController->renderWith(
array('Security_passwordsent', 'Security', $this->stat('template_main'), 'BlankPage')
);
}
@ -563,7 +578,8 @@ class Security extends Controller {
} elseif(Member::currentUser()) {
// let a logged in user change his password
$customisedController = $controller->customise(array(
'Content' => '<p>' . _t('Security.CHANGEPASSWORDBELOW', 'You can change your password below.') . '</p>',
'Content' => '<p>'
. _t('Security.CHANGEPASSWORDBELOW', 'You can change your password below.') . '</p>',
'Form' => $this->ChangePasswordForm()));
} else {
@ -574,7 +590,9 @@ class Security extends Controller {
array('Content' =>
_t(
'Security.NOTERESETLINKINVALID',
'<p>The password reset link is invalid or expired.</p><p>You can request a new one <a href="{link1}">here</a> or change your password after you <a href="{link2}">logged in</a>.</p>',
'<p>The password reset link is invalid or expired.</p>'
. '<p>You can request a new one <a href="{link1}">here</a> or change your password after'
. ' you <a href="{link2}">logged in</a>.</p>',
array('link1' => $this->Link('lostpassword'), 'link2' => $this->link('login'))
)
)
@ -588,7 +606,9 @@ class Security extends Controller {
}
}
return $customisedController->renderWith(array('Security_changepassword', 'Security', $this->stat('template_main'), 'BlankPage'));
return $customisedController->renderWith(
array('Security_changepassword', 'Security', $this->stat('template_main'), 'BlankPage')
);
}
/**
@ -624,8 +644,11 @@ class Security extends Controller {
$member = null;
// find a group with ADMIN permission
$adminGroup = DataObject::get('Group')->where("\"Permission\".\"Code\" = 'ADMIN'")
->sort("\"Group\".\"ID\"")->innerJoin("Permission", "\"Group\".\"ID\"=\"Permission\".\"GroupID\"")->First();
$adminGroup = DataObject::get('Group')
->where("\"Permission\".\"Code\" = 'ADMIN'")
->sort("\"Group\".\"ID\"")
->innerJoin("Permission", "\"Group\".\"ID\"=\"Permission\".\"GroupID\"")
->First();
if(is_callable('Subsite::changeSubsite')) {
Subsite::changeSubsite($origSubsite);

View File

@ -15,7 +15,9 @@
class EncryptAllPasswordsTask extends BuildTask {
protected $title = 'Encrypt all passwords tasks';
protected $description = 'Convert all plaintext passwords on the Member table to the default encryption/hashing algorithm. Note: This mainly applies to passwords in SilverStripe 2.1 or earlier, passwords in newer versions are hashed by default.';
protected $description = 'Convert all plaintext passwords on the Member table to the default encryption/hashing
algorithm. Note: This mainly applies to passwords in SilverStripe 2.1 or earlier, passwords in newer versions
are hashed by default.';
public function init() {
parent::init();

View File

@ -39,16 +39,16 @@
*
* <code>
* # Quarter-hourly task (every hour at 25 minutes past) (remove space between first * and /15)
* * /15 * * * * www-data /my/webroot/framework/cli-script.php /QuarterlyHourlyTask > /var/log/silverstripe_quarterhourlytask.log
* * /15 * * * * www-data /webroot/framework/cli-script.php /QuarterlyHourlyTask > /var/log/quarterhourlytask.log
*
* # HourlyTask (every hour at 25 minutes past)
* 25 * * * * www-data /my/webroot/framework/cli-script.php /HourlyTask > /var/log/silverstripe_hourlytask.log
* 25 * * * * www-data /webroot/framework/cli-script.php /HourlyTask > /var/log/hourlytask.log
*
* # DailyTask (every day at 6:25am)
* 25 6 * * * www-data /my/webroot/framework/cli-script.php /DailyTask > /var/log/silverstripe_dailytask.log
* 25 6 * * * www-data /webroot/framework/cli-script.php /DailyTask > /var/log/dailytask.log
*
* # WeelkyTask (every Monday at 6:25am)
* 25 6 1 * * www-data /my/webroot/framework/cli-script.php /WeeklyTask > /var/log/silverstripe_weeklytask.log
* 25 6 1 * * www-data /webroot/framework/cli-script.php /WeeklyTask > /var/log/weeklytask.log
* </code>
*
* @todo Improve documentation

View File

@ -11,7 +11,9 @@ class FakeController extends Controller {
$this->pushCurrent();
$this->request = new SS_HTTPRequest(
(isset($_SERVER['X-HTTP-Method-Override'])) ? $_SERVER['X-HTTP-Method-Override'] : $_SERVER['REQUEST_METHOD'],
(isset($_SERVER['X-HTTP-Method-Override']))
? $_SERVER['X-HTTP-Method-Override']
: $_SERVER['REQUEST_METHOD'],
'/'
);

View File

@ -10,7 +10,7 @@
class PhpSyntaxTest extends SapphireTest {
public function setUp() {
parent::setUp();
$this->markTestSkipped('This needs to be written to include only core SS php files, not test or thirdparty files');
$this->markTestSkipped('This needs to be written to include only core php files, not test/thirdparty files');
}
public function testShortTagsOffWillWork() {
@ -32,8 +32,10 @@ class PhpSyntaxTest extends SapphireTest {
$returnCode = 0;
$output = array();
exec("php -l -d $settingTest $CLI_file", $output, $returnCode);
$hasErrors = ($returnCode != 0 && strpos('No syntax errors detected', implode("\n", $output)) === FALSE);
$this->assertFalse($hasErrors, "Syntax error parsing $CLI_file with setting $settingTest:\n" . implode("\n", $output) . " (Returned: {$returnCode})");
$hasErrors = ($returnCode != 0
&& strpos('No syntax errors detected', implode("\n", $output)) === FALSE);
$this->assertFalse($hasErrors, "Syntax error parsing $CLI_file with setting $settingTest:\n"
. implode("\n", $output) . " (Returned: {$returnCode})");
}
}
}

View File

@ -31,7 +31,8 @@ class RSSFeedTest extends SapphireTest {
// Feed #2 - put Content() into <title> and AltContent() into <description>
$rssFeed = new RSSFeed($list, "http://www.example.com", "Test RSS Feed", "Test RSS Feed Description", "Content", "AltContent");
$rssFeed = new RSSFeed($list, "http://www.example.com", "Test RSS Feed", "Test RSS Feed Description",
"Content", "AltContent");
$content = $rssFeed->outputToBrowser();
$this->assertContains('<title>ItemA Content</title>', $content);

View File

@ -32,11 +32,13 @@ class XMLDataFormatterTest extends SapphireTest {
$this->assertEquals('Test Company', (string) $xml->Company);
$this->assertEquals($obj->ID, (int) $xml->ID);
$this->assertEquals(
'<Content><![CDATA[<a href="http://mysite.com">mysite.com</a> is a link in this HTML content. <![CDATA[this is some nested CDATA]]]]><![CDATA[>]]></Content>',
'<Content><![CDATA[<a href="http://mysite.com">mysite.com</a> is a link in this HTML content.'
. ' <![CDATA[this is some nested CDATA]]]]><![CDATA[>]]></Content>',
$xml->Content->asXML()
);
$this->assertEquals(
'<a href="http://mysite.com">mysite.com</a> is a link in this HTML content. <![CDATA[this is some nested CDATA]]>',
'<a href="http://mysite.com">mysite.com</a> is a link in this HTML content.'
. ' <![CDATA[this is some nested CDATA]]>',
(string) $xml->Content
);
}

View File

@ -13,8 +13,8 @@ class ControllerTest extends FunctionalTest {
}
public function testMethodActions() {
/* The Action can refer to a method that is called on the object. If a method returns an array, then it will be
used to customise the template data */
/* The Action can refer to a method that is called on the object. If a method returns an array, then it
* will be used to customise the template data */
$response = $this->get("ControllerTest_Controller/methodaction");
$this->assertRegExp("/This is the main template. Content is 'methodaction content'./", $response->getBody());
@ -26,7 +26,8 @@ class ControllerTest extends FunctionalTest {
public function testTemplateActions() {
/* If there is no method, it can be used to point to an alternative template. */
$response = $this->get("ControllerTest_Controller/templateaction");
$this->assertRegExp("/This is the template for templateaction. Content is 'default content'./", $response->getBody());
$this->assertRegExp("/This is the template for templateaction. Content is 'default content'./",
$response->getBody());
}
public function testUndefinedActions() {
@ -85,23 +86,27 @@ class ControllerTest extends FunctionalTest {
* Test Controller::join_links()
*/
public function testJoinLinks() {
/* Controller::join_links() will reliably join two URL-segments together so that they will be appropriately parsed by the URL parser */
/* Controller::join_links() will reliably join two URL-segments together so that they will be
* appropriately parsed by the URL parser */
$this->assertEquals("admin/crm/MyForm", Controller::join_links("admin/crm", "MyForm"));
$this->assertEquals("admin/crm/MyForm", Controller::join_links("admin/crm/", "MyForm"));
/* It will also handle appropriate combination of querystring variables */
$this->assertEquals("admin/crm/MyForm?flush=1", Controller::join_links("admin/crm/?flush=1", "MyForm"));
$this->assertEquals("admin/crm/MyForm?flush=1", Controller::join_links("admin/crm/", "MyForm?flush=1"));
$this->assertEquals("admin/crm/MyForm?field=1&other=1", Controller::join_links("admin/crm/?field=1", "MyForm?other=1"));
$this->assertEquals("admin/crm/MyForm?field=1&other=1",
Controller::join_links("admin/crm/?field=1", "MyForm?other=1"));
/* It can handle arbitrary numbers of components, and will ignore empty ones */
$this->assertEquals("admin/crm/MyForm/", Controller::join_links("admin/", "crm", "", "MyForm/"));
$this->assertEquals("admin/crm/MyForm/?a=1&b=2", Controller::join_links("admin/?a=1", "crm", "", "MyForm/?b=2"));
$this->assertEquals("admin/crm/MyForm/?a=1&b=2",
Controller::join_links("admin/?a=1", "crm", "", "MyForm/?b=2"));
/* It can also be used to attach additional get variables to a link */
$this->assertEquals("admin/crm?flush=1", Controller::join_links("admin/crm", "?flush=1"));
$this->assertEquals("admin/crm?existing=1&flush=1", Controller::join_links("admin/crm?existing=1", "?flush=1"));
$this->assertEquals("admin/crm/MyForm?a=1&b=2&c=3", Controller::join_links("?a=1", "admin/crm", "?b=2", "MyForm?c=3"));
$this->assertEquals("admin/crm/MyForm?a=1&b=2&c=3",
Controller::join_links("?a=1", "admin/crm", "?b=2", "MyForm?c=3"));
/* Note, however, that it doesn't deal with duplicates very well. */
$this->assertEquals("admin/crm?flush=1&flush=1", Controller::join_links("admin/crm?flush=1", "?flush=1"));
@ -116,7 +121,8 @@ class ControllerTest extends FunctionalTest {
$this->assertEquals("my-page?arg=var#subsection", Controller::join_links("my-page#subsection", "?arg=var"));
/* If there are multiple, it takes the last one */
$this->assertEquals("my-page?arg=var#second-section", Controller::join_links("my-page#subsection", "?arg=var", "#second-section"));
$this->assertEquals("my-page?arg=var#second-section",
Controller::join_links("my-page#subsection", "?arg=var", "#second-section"));
/* Does type-safe checks for zero value */
$this->assertEquals("my-page/0", Controller::join_links("my-page", 0));
@ -129,7 +135,8 @@ class ControllerTest extends FunctionalTest {
$controller = new ControllerTest_HasAction();
$this->assertFalse($controller->hasAction('1'), 'Numeric actions do not slip through.');
//$this->assertFalse($controller->hasAction('lowercase_permission'), 'Lowercase permission does not slip through.');
//$this->assertFalse($controller->hasAction('lowercase_permission'),
//'Lowercase permission does not slip through.');
$this->assertFalse($controller->hasAction('undefined'), 'undefined actions do not exist');
$this->assertTrue($controller->hasAction('allowed_action'), 'allowed actions are recognised');
$this->assertTrue($controller->hasAction('template_action'), 'action-specific templates are recognised');
@ -142,7 +149,8 @@ class ControllerTest extends FunctionalTest {
);
}
/* Controller::BaseURL no longer exists, but was just a direct call to Director::BaseURL, so not sure what this code was supposed to test
/* Controller::BaseURL no longer exists, but was just a direct call to Director::BaseURL, so not sure what this
* code was supposed to test
public function testBaseURL() {
Director::setBaseURL('/baseurl/');
$this->assertEquals(Controller::BaseURL(), Director::BaseURL());
@ -151,21 +159,24 @@ class ControllerTest extends FunctionalTest {
public function testRedirectBackByReferer() {
$internalRelativeUrl = '/some-url';
$response = $this->get('ControllerTest_Controller/redirectbacktest', null, array('Referer' => $internalRelativeUrl));
$response = $this->get('ControllerTest_Controller/redirectbacktest', null,
array('Referer' => $internalRelativeUrl));
$this->assertEquals(302, $response->getStatusCode());
$this->assertEquals($internalRelativeUrl, $response->getHeader('Location'),
"Redirects on internal relative URLs"
);
$internalAbsoluteUrl = Director::absoluteBaseURL() . '/some-url';
$response = $this->get('ControllerTest_Controller/redirectbacktest', null, array('Referer' => $internalAbsoluteUrl));
$response = $this->get('ControllerTest_Controller/redirectbacktest', null,
array('Referer' => $internalAbsoluteUrl));
$this->assertEquals(302, $response->getStatusCode());
$this->assertEquals($internalAbsoluteUrl, $response->getHeader('Location'),
"Redirects on internal absolute URLs"
);
$externalAbsoluteUrl = 'http://myhost.com/some-url';
$response = $this->get('ControllerTest_Controller/redirectbacktest', null, array('Referer' => $externalAbsoluteUrl));
$response = $this->get('ControllerTest_Controller/redirectbacktest', null,
array('Referer' => $externalAbsoluteUrl));
$this->assertEquals(200, $response->getStatusCode(),
"Doesn't redirect on external URLs"
);

View File

@ -60,7 +60,8 @@ class DirectorTest extends SapphireTest {
Director::setBaseURL('/relativebase/');
$this->assertEquals('/relativebase/', Director::baseURL());
$this->assertEquals(Director::protocolAndHost() . '/relativebase/', Director::absoluteBaseURL());
$this->assertEquals(Director::protocolAndHost() . '/relativebase/subfolder/test', Director::absoluteURL('subfolder/test'));
$this->assertEquals(Director::protocolAndHost() . '/relativebase/subfolder/test',
Director::absoluteURL('subfolder/test'));
// absolute base URLs - you should end them in a /
Director::setBaseURL('http://www.example.org/');
@ -72,7 +73,8 @@ class DirectorTest extends SapphireTest {
Director::setBaseURL(false);
$this->assertEquals(BASE_URL.'/', Director::baseURL());
$this->assertEquals(Director::protocolAndHost().BASE_URL.'/', Director::absoluteBaseURL(BASE_URL));
$this->assertEquals(Director::protocolAndHost().BASE_URL . '/subfolder/test', Director::absoluteURL('subfolder/test'));
$this->assertEquals(Director::protocolAndHost().BASE_URL . '/subfolder/test',
Director::absoluteURL('subfolder/test'));
}
/**
@ -155,18 +157,23 @@ class DirectorTest extends SapphireTest {
$_POST = array('somekey' => 'postvalue');
$_COOKIE = array('somekey' => 'cookievalue');
$getresponse = Director::test('errorpage?somekey=sometestgetvalue', array('somekey' => 'sometestpostvalue'), null, null, null, null, array('somekey' => 'sometestcookievalue'));
$getresponse = Director::test('errorpage?somekey=sometestgetvalue', array('somekey' => 'sometestpostvalue'),
null, null, null, null, array('somekey' => 'sometestcookievalue'));
$this->assertEquals('getvalue', $_GET['somekey'], '$_GET reset to original value after Director::test()');
$this->assertEquals('postvalue', $_POST['somekey'], '$_POST reset to original value after Director::test()');
$this->assertEquals('cookievalue', $_COOKIE['somekey'], '$_COOKIE reset to original value after Director::test()');
$this->assertEquals('getvalue', $_GET['somekey'],
'$_GET reset to original value after Director::test()');
$this->assertEquals('postvalue', $_POST['somekey'],
'$_POST reset to original value after Director::test()');
$this->assertEquals('cookievalue', $_COOKIE['somekey'],
'$_COOKIE reset to original value after Director::test()');
}
public function testTestRequestCarriesGlobals() {
$fixture = array('somekey' => 'sometestvalue');
foreach(array('get', 'post') as $method) {
foreach(array('return%sValue', 'returnRequestValue', 'returnCookieValue') as $testfunction) {
$url = 'DirectorTestRequest_Controller/' . sprintf($testfunction, ucfirst($method)) . '?' . http_build_query($fixture);
$url = 'DirectorTestRequest_Controller/' . sprintf($testfunction, ucfirst($method))
. '?' . http_build_query($fixture);
$getresponse = Director::test($url, $fixture, null, strtoupper($method), null, null, $fixture);
$this->assertInstanceOf('SS_HTTPResponse', $getresponse, 'Director::test() returns SS_HTTPResponse');

View File

@ -116,7 +116,8 @@ class HTTPTest extends SapphireTest {
$this->assertEquals('text/html', HTTP::get_mime_type(FRAMEWORK_DIR.'/tests/control/files/file.html'));
$this->assertEquals('image/jpeg', HTTP::get_mime_type(FRAMEWORK_DIR.'/tests/control/files/file.jpg'));
$this->assertEquals('image/png', HTTP::get_mime_type(FRAMEWORK_DIR.'/tests/control/files/file.png'));
$this->assertEquals('image/vnd.adobe.photoshop', HTTP::get_mime_type(FRAMEWORK_DIR.'/tests/control/files/file.psd'));
$this->assertEquals('image/vnd.adobe.photoshop',
HTTP::get_mime_type(FRAMEWORK_DIR.'/tests/control/files/file.psd'));
$this->assertEquals('audio/x-wav', HTTP::get_mime_type(FRAMEWORK_DIR.'/tests/control/files/file.wav'));
}
}

View File

@ -38,7 +38,8 @@ class RequestHandlingTest extends FunctionalTest {
$response = Director::test("testGoodBase1/method/1/2");
$this->assertEquals("This is a method on the controller: 1, 2", $response->getBody());
/* In addition, these values are availalbe in $controller->urlParams. This is mainly for backward compatability. */
/* In addition, these values are availalbe in $controller->urlParams. This is mainly for backward
* compatability. */
$response = Director::test("testGoodBase1/legacymethod/3/4");
$this->assertEquals("\$this->urlParams can be used, for backward compatibility: 3, 4", $response->getBody());
}
@ -64,9 +65,9 @@ class RequestHandlingTest extends FunctionalTest {
}
public function testBadBase() {
/* Without a double-slash indicator in the URL, the entire URL is popped off the stack. The controller's default
action handlers have been designed for this to an extend: simple actions can still be called. This is the set-up
of URL rules written before this new request handler. */
/* Without a double-slash indicator in the URL, the entire URL is popped off the stack. The controller's
* default action handlers have been designed for this to an extend: simple actions can still be called.
* This is the set-up of URL rules written before this new request handler. */
$response = Director::test("testBadBase/method/1/2");
$this->assertEquals("This is a method on the controller: 1, 2", $response->getBody());
@ -74,7 +75,7 @@ class RequestHandlingTest extends FunctionalTest {
$this->assertEquals("Form posted", $response->getBody());
/* It won't, however, let you chain requests to access methods on forms, or form fields. In order to do that,
you need to have a // marker in your URL parsing rule */
* you need to have a // marker in your URL parsing rule */
$response = Director::test("testBadBase/TestForm/fields/MyField");
$this->assertNotEquals("MyField requested", $response->getBody());
}
@ -166,7 +167,8 @@ class RequestHandlingTest extends FunctionalTest {
$data = array('action_disallowedcontrollermethod' => 1, 'SecurityID' => $securityId);
$response = $this->post('RequestHandlingTest_FormActionController/Form', $data);
$this->assertEquals(403, $response->getStatusCode(),
'Should fail: Invocation through POST form handler, controller action instead of form action, not contained in $allowed_actions, with CSRF token'
'Should fail: Invocation through POST form handler, controller action instead of form action,'
.' not contained in $allowed_actions, with CSRF token'
);
$data = array('action_formaction' => 1, 'SecurityID' => $securityId);
@ -179,7 +181,8 @@ class RequestHandlingTest extends FunctionalTest {
$data = array('action_controlleraction' => 1, 'SecurityID' => $securityId);
$response = $this->post('RequestHandlingTest_FormActionController/Form', $data);
$this->assertEquals(200, $response->getStatusCode(),
'Should pass: Invocation through POST form handler, controller action instead of form action, contained in $allowed_actions, with CSRF token'
'Should pass: Invocation through POST form handler, controller action instead of form action, contained in'
. ' $allowed_actions, with CSRF token'
);
$data = array('action_formactionInAllowedActions' => 1);
@ -301,7 +304,8 @@ class RequestHandlingTest_Controller extends Controller implements TestOnly {
}
public function legacymethod($request) {
return "\$this->urlParams can be used, for backward compatibility: " . $this->urlParams['ID'] . ', ' . $this->urlParams['OtherID'];
return "\$this->urlParams can be used, for backward compatibility: " . $this->urlParams['ID'] . ', '
. $this->urlParams['OtherID'];
}
public function virtualfile($request) {

View File

@ -55,25 +55,32 @@ class ConfigStaticTest_Combined3 extends ConfigStaticTest_Combined2 {
class ConfigTest extends SapphireTest {
public function testUpdateStatic() {
$this->assertEquals(Config::inst()->get('ConfigStaticTest_First', 'first', Config::FIRST_SET), array('test_1'));
$this->assertEquals(Config::inst()->get('ConfigStaticTest_Second', 'first', Config::FIRST_SET), array('test_2'));
$this->assertEquals(Config::inst()->get('ConfigStaticTest_Third', 'first', Config::FIRST_SET), array('test_3'));
$this->assertEquals(Config::inst()->get('ConfigStaticTest_First', 'first', Config::FIRST_SET),
array('test_1'));
$this->assertEquals(Config::inst()->get('ConfigStaticTest_Second', 'first', Config::FIRST_SET),
array('test_2'));
$this->assertEquals(Config::inst()->get('ConfigStaticTest_Third', 'first', Config::FIRST_SET),
array('test_3'));
Config::inst()->update('ConfigStaticTest_First', 'first', array('test_1_2'));
Config::inst()->update('ConfigStaticTest_Third', 'first', array('test_3_2'));
Config::inst()->update('ConfigStaticTest_Fourth', 'first', array('test_4'));
$this->assertEquals(Config::inst()->get('ConfigStaticTest_First', 'first', Config::FIRST_SET), array('test_1_2', 'test_1'));
$this->assertEquals(Config::inst()->get('ConfigStaticTest_First', 'first', Config::FIRST_SET),
array('test_1_2', 'test_1'));
Config::inst()->update('ConfigStaticTest_Fourth', 'second', array('test_4'));
Config::inst()->update('ConfigStaticTest_Third', 'second', array('test_3_2'));
$this->assertEquals(Config::inst()->get('ConfigStaticTest_Fourth', 'second', Config::FIRST_SET), array('test_4'));
$this->assertEquals(Config::inst()->get('ConfigStaticTest_Third', 'second', Config::FIRST_SET), array('test_3_2', 'test_3'));
$this->assertEquals(Config::inst()->get('ConfigStaticTest_Fourth', 'second', Config::FIRST_SET),
array('test_4'));
$this->assertEquals(Config::inst()->get('ConfigStaticTest_Third', 'second', Config::FIRST_SET),
array('test_3_2', 'test_3'));
Config::inst()->remove('ConfigStaticTest_Third', 'second');
Config::inst()->update('ConfigStaticTest_Third', 'second', array('test_3_2'));
$this->assertEquals(Config::inst()->get('ConfigStaticTest_Third', 'second', Config::FIRST_SET), array('test_3_2'));
$this->assertEquals(Config::inst()->get('ConfigStaticTest_Third', 'second', Config::FIRST_SET),
array('test_3_2'));
}
public function testUninheritedStatic() {
@ -84,12 +91,16 @@ class ConfigTest extends SapphireTest {
Config::inst()->update('ConfigStaticTest_Second', 'first', array('test_2b'));
// Check that it can be applied to parent and subclasses, and queried directly
$this->assertContains('test_1b', Config::inst()->get('ConfigStaticTest_First', 'first', Config::UNINHERITED));
$this->assertContains('test_2b', Config::inst()->get('ConfigStaticTest_Second', 'first', Config::UNINHERITED));
$this->assertContains('test_1b',
Config::inst()->get('ConfigStaticTest_First', 'first', Config::UNINHERITED));
$this->assertContains('test_2b',
Config::inst()->get('ConfigStaticTest_Second', 'first', Config::UNINHERITED));
// But it won't affect subclasses - this is *uninherited* static
$this->assertNotContains('test_2b', Config::inst()->get('ConfigStaticTest_Third', 'first', Config::UNINHERITED));
$this->assertNotContains('test_2b', Config::inst()->get('ConfigStaticTest_Fourth', 'first', Config::UNINHERITED));
$this->assertNotContains('test_2b',
Config::inst()->get('ConfigStaticTest_Third', 'first', Config::UNINHERITED));
$this->assertNotContains('test_2b',
Config::inst()->get('ConfigStaticTest_Fourth', 'first', Config::UNINHERITED));
// Subclasses that don't have the static explicitly defined should allow definition, also
// This also checks that set can be called after the first uninherited get()
@ -99,10 +110,12 @@ class ConfigTest extends SapphireTest {
}
public function testCombinedStatic() {
$this->assertEquals(Config::inst()->get('ConfigStaticTest_Combined3', 'first'), array('test_3', 'test_2', 'test_1'));
$this->assertEquals(Config::inst()->get('ConfigStaticTest_Combined3', 'first'),
array('test_3', 'test_2', 'test_1'));
// test that null values are ignored, but values on either side are still merged
$this->assertEquals(Config::inst()->get('ConfigStaticTest_Combined3', 'second'), array('test_3', 'test_1'));
$this->assertEquals(Config::inst()->get('ConfigStaticTest_Combined3', 'second'),
array('test_3', 'test_1'));
}
public function testMerges() {
@ -124,11 +137,13 @@ class ConfigTest extends SapphireTest {
$result = array('A' => 1, 'B' => 2, 'C' => array('Foo' => 1, 'Bar' => 2), 'D' => 3);
Config::merge_array_low_into_high($result, array('C' => array('Bar' => 3, 'Baz' => 4)));
$this->assertEquals($result, array('A' => 1, 'B' => 2, 'C' => array('Foo' => 1, 'Bar' => 2, 'Baz' => 4), 'D' => 3));
$this->assertEquals($result,
array('A' => 1, 'B' => 2, 'C' => array('Foo' => 1, 'Bar' => 2, 'Baz' => 4), 'D' => 3));
$result = array('A' => 1, 'B' => 2, 'C' => array('Foo' => 1, 'Bar' => 2), 'D' => 3);
Config::merge_array_high_into_low($result, array('C' => array('Bar' => 3, 'Baz' => 4)));
$this->assertEquals($result, array('A' => 1, 'B' => 2, 'C' => array('Foo' => 1, 'Bar' => 3, 'Baz' => 4), 'D' => 3));
$this->assertEquals($result,
array('A' => 1, 'B' => 2, 'C' => array('Foo' => 1, 'Bar' => 3, 'Baz' => 4), 'D' => 3));
}
public function testStaticLookup() {

View File

@ -11,10 +11,12 @@ class ConvertTest extends SapphireTest {
*/
public function testRaw2Att() {
$val1 = '<input type="text">';
$this->assertEquals('&lt;input type=&quot;text&quot;&gt;', Convert::raw2att($val1), 'Special characters are escaped');
$this->assertEquals('&lt;input type=&quot;text&quot;&gt;', Convert::raw2att($val1),
'Special characters are escaped');
$val2 = 'This is some normal text.';
$this->assertEquals('This is some normal text.', Convert::raw2att($val2), 'Normal text is not escaped');
$this->assertEquals('This is some normal text.', Convert::raw2att($val2),
'Normal text is not escaped');
}
/**
@ -22,35 +24,44 @@ class ConvertTest extends SapphireTest {
*/
public function testRaw2HtmlAtt() {
$val1 = '<input type="text">';
$this->assertEquals('&lt;input type=&quot;text&quot;&gt;', Convert::raw2htmlatt($val1), 'Special characters are escaped');
$this->assertEquals('&lt;input type=&quot;text&quot;&gt;', Convert::raw2htmlatt($val1),
'Special characters are escaped');
$val2 = 'This is some normal text.';
$this->assertEquals('This is some normal text.', Convert::raw2htmlatt($val2), 'Normal text is not escaped');
$this->assertEquals('This is some normal text.', Convert::raw2htmlatt($val2),
'Normal text is not escaped');
}
public function testHtml2raw() {
$val1 = 'This has a <strong>strong tag</strong>.';
$this->assertEquals('This has a *strong tag*.', Convert::xml2raw($val1), 'Strong tags are replaced with asterisks');
$this->assertEquals('This has a *strong tag*.', Convert::xml2raw($val1),
'Strong tags are replaced with asterisks');
$val1 = 'This has a <b class="test" style="font-weight: bold">b tag with attributes</b>.';
$this->assertEquals('This has a *b tag with attributes*.', Convert::xml2raw($val1), 'B tags with attributes are replaced with asterisks');
$this->assertEquals('This has a *b tag with attributes*.', Convert::xml2raw($val1),
'B tags with attributes are replaced with asterisks');
$val2 = 'This has a <strong class="test" style="font-weight: bold">strong tag with attributes</STRONG>.';
$this->assertEquals('This has a *strong tag with attributes*.', Convert::xml2raw($val2), 'Strong tags with attributes are replaced with asterisks');
$this->assertEquals('This has a *strong tag with attributes*.', Convert::xml2raw($val2),
'Strong tags with attributes are replaced with asterisks');
$val3 = '<script type="text/javascript">Some really nasty javascript here</script>';
$this->assertEquals('', Convert::xml2raw($val3), 'Script tags are completely removed');
$this->assertEquals('', Convert::xml2raw($val3),
'Script tags are completely removed');
$val4 = '<style type="text/css">Some really nasty CSS here</style>';
$this->assertEquals('', Convert::xml2raw($val4), 'Style tags are completely removed');
$this->assertEquals('', Convert::xml2raw($val4),
'Style tags are completely removed');
$val5 = '<script type="text/javascript">Some really nasty
multiline javascript here</script>';
$this->assertEquals('', Convert::xml2raw($val5), 'Multiline script tags are completely removed');
$this->assertEquals('', Convert::xml2raw($val5),
'Multiline script tags are completely removed');
$val6 = '<style type="text/css">Some really nasty
multiline CSS here</style>';
$this->assertEquals('', Convert::xml2raw($val6), 'Multiline style tags are completely removed');
$this->assertEquals('', Convert::xml2raw($val6),
'Multiline style tags are completely removed');
}
/**
@ -58,13 +69,16 @@ class ConvertTest extends SapphireTest {
*/
public function testRaw2Xml() {
$val1 = '<input type="text">';
$this->assertEquals('&lt;input type=&quot;text&quot;&gt;', Convert::raw2xml($val1), 'Special characters are escaped');
$this->assertEquals('&lt;input type=&quot;text&quot;&gt;', Convert::raw2xml($val1),
'Special characters are escaped');
$val2 = 'This is some normal text.';
$this->assertEquals('This is some normal text.', Convert::raw2xml($val2), 'Normal text is not escaped');
$this->assertEquals('This is some normal text.', Convert::raw2xml($val2),
'Normal text is not escaped');
$val3 = "This is test\nNow on a new line.";
$this->assertEquals("This is test\nNow on a new line.", Convert::raw2xml($val3), 'Newlines are retained. They should not be replaced with <br /> as it is not XML valid');
$this->assertEquals("This is test\nNow on a new line.", Convert::raw2xml($val3),
'Newlines are retained. They should not be replaced with <br /> as it is not XML valid');
}
public function testRaw2HtmlName() {
@ -92,7 +106,8 @@ class ConvertTest extends SapphireTest {
)
);
$encoded = Convert::array2json($val);
$this->assertEquals('{"Joe":"Bloggs","Tom":"Jones","My":{"Complicated":"Structure"}}', $encoded, 'Array is encoded in JSON');
$this->assertEquals('{"Joe":"Bloggs","Tom":"Jones","My":{"Complicated":"Structure"}}', $encoded,
'Array is encoded in JSON');
}
public function testJSON2Array() {

View File

@ -20,13 +20,16 @@ class CoreTest extends SapphireTest {
$this->assertEquals(getTempFolder(BASE_PATH), $this->tempPath);
} else {
// A typical Windows location for where sites are stored on IIS
$this->assertEquals(getTempFolder('C:\\inetpub\\wwwroot\\silverstripe-test-project'), sys_get_temp_dir() . '/silverstripe-cacheC--inetpub-wwwroot-silverstripe-test-project');
$this->assertEquals(getTempFolder('C:\\inetpub\\wwwroot\\silverstripe-test-project'),
sys_get_temp_dir() . '/silverstripe-cacheC--inetpub-wwwroot-silverstripe-test-project');
// A typical Mac OS X location for where sites are stored
$this->assertEquals(getTempFolder('/Users/joebloggs/Sites/silverstripe-test-project'), sys_get_temp_dir() . '/silverstripe-cache-Users-joebloggs-Sites-silverstripe-test-project');
$this->assertEquals(getTempFolder('/Users/joebloggs/Sites/silverstripe-test-project'),
sys_get_temp_dir() . '/silverstripe-cache-Users-joebloggs-Sites-silverstripe-test-project');
// A typical Linux location for where sites are stored
$this->assertEquals(getTempFolder('/var/www/silverstripe-test-project'), sys_get_temp_dir() . '/silverstripe-cache-var-www-silverstripe-test-project');
$this->assertEquals(getTempFolder('/var/www/silverstripe-test-project'),
sys_get_temp_dir() . '/silverstripe-cache-var-www-silverstripe-test-project');
}
}

View File

@ -54,16 +54,21 @@ class ObjectTest extends SapphireTest {
public function testSingletonCreation() {
$myObject = singleton('ObjectTest_MyObject');
$this->assertEquals($myObject->class, 'ObjectTest_MyObject', 'singletons are creating a correct class instance');
$this->assertEquals(get_class($myObject), 'ObjectTest_MyObject', 'singletons are creating a correct class instance');
$this->assertEquals($myObject->class, 'ObjectTest_MyObject',
'singletons are creating a correct class instance');
$this->assertEquals(get_class($myObject), 'ObjectTest_MyObject',
'singletons are creating a correct class instance');
$mySubObject = singleton('ObjectTest_MySubObject');
$this->assertEquals($mySubObject->class, 'ObjectTest_MySubObject', 'singletons are creating a correct subclass instance');
$this->assertEquals(get_class($mySubObject), 'ObjectTest_MySubObject', 'singletons are creating a correct subclass instance');
$this->assertEquals($mySubObject->class, 'ObjectTest_MySubObject',
'singletons are creating a correct subclass instance');
$this->assertEquals(get_class($mySubObject), 'ObjectTest_MySubObject',
'singletons are creating a correct subclass instance');
$myFirstObject = singleton('ObjectTest_MyObject');
$mySecondObject = singleton('ObjectTest_MyObject');
$this->assertTrue($myFirstObject === $mySecondObject, 'singletons are using the same object on subsequent calls');
$this->assertTrue($myFirstObject === $mySecondObject,
'singletons are using the same object on subsequent calls');
}
public function testStaticGetterMethod() {
@ -196,25 +201,30 @@ class ObjectTest extends SapphireTest {
);
$this->assertTrue(
singleton('ObjectTest_ExtensionTest')->hasExtension('ObjectTest_ExtendTest1'),
"Extensions are detected when set on Object::\$extensions on instance hasExtension() without case-sensitivity"
"Extensions are detected when set on Object::\$extensions on instance hasExtension() without"
. " case-sensitivity"
);
// ObjectTest_ExtendTest2 is built in via $extensions (with parameters)
$this->assertTrue(
Object::has_extension('ObjectTest_ExtensionTest', 'ObjectTest_ExtendTest2'),
"Extensions are detected with static has_extension() when set on Object::\$extensions with additional parameters"
"Extensions are detected with static has_extension() when set on Object::\$extensions with"
. " additional parameters"
);
$this->assertTrue(
singleton('ObjectTest_ExtensionTest')->hasExtension('ObjectTest_ExtendTest2'),
"Extensions are detected with instance hasExtension() when set on Object::\$extensions with additional parameters"
"Extensions are detected with instance hasExtension() when set on Object::\$extensions with"
. " additional parameters"
);
$this->assertFalse(
Object::has_extension('ObjectTest_ExtensionTest', 'ObjectTest_ExtendTest3'),
"Other extensions available in the system are not present unless explicitly added to this object when checking through has_extension()"
"Other extensions available in the system are not present unless explicitly added to this object"
. " when checking through has_extension()"
);
$this->assertFalse(
singleton('ObjectTest_ExtensionTest')->hasExtension('ObjectTest_ExtendTest3'),
"Other extensions available in the system are not present unless explicitly added to this object when checking through instance hasExtension()"
"Other extensions available in the system are not present unless explicitly added to this object"
. " when checking through instance hasExtension()"
);
// ObjectTest_ExtendTest3 is added manually
@ -345,8 +355,10 @@ class ObjectTest extends SapphireTest {
);
// Nested array
$this->assertEquals(
array('Enum',array(array('Accepted', 'Pending', 'Declined', array('UnsubmittedA','UnsubmittedB')), 'Unsubmitted')),
Object::parse_class_spec("Enum(array('Accepted', 'Pending', 'Declined', array('UnsubmittedA','UnsubmittedB')), 'Unsubmitted')")
array('Enum',array(array('Accepted', 'Pending', 'Declined', array('UnsubmittedA','UnsubmittedB')),
'Unsubmitted')),
Object::parse_class_spec(
"Enum(array('Accepted', 'Pending', 'Declined', array('UnsubmittedA','UnsubmittedB')), 'Unsubmitted')")
);
}
}

View File

@ -44,13 +44,15 @@ class ConfigManifestTest extends SapphireTest {
array('module' => 'qux', 'file' => 'delta', 'name' => '4')
), 'before');
// Wildcard should match any module even if there is an opposing rule, if opposing rule doesn't match, no matter how many opposing rules
// Wildcard should match any module even if there is an opposing rule, if opposing rule doesn't match, no
// matter how many opposing rules
$this->assertEquals($accessor->relativeOrder(
$beforeWildcardedAfterExplicit,
array('module' => 'qux', 'file' => 'delta', 'name' => '4')
), 'before');
// Wildcard should match any module even if there is an opposing rule, if opposing rule doesn't match (even if some portions do)
// Wildcard should match any module even if there is an opposing rule, if opposing rule doesn't match
// (even if some portions do)
$this->assertEquals($accessor->relativeOrder(
$beforeWildcardedAfterExplicit,
array('module' => 'bar', 'file' => 'beta', 'name' => 'nomatchy')

View File

@ -49,7 +49,9 @@ class NamespacedClassManifestTest extends SapphireTest {
public function testGetClassNames() {
$this->assertEquals(
array('silverstripe\test\classa', 'silverstripe\test\classb', 'silverstripe\test\classc', 'silverstripe\test\classd', 'silverstripe\test\classe', 'silverstripe\test\classf', 'silverstripe\test\classg', 'silverstripe\test\classh'),
array('silverstripe\test\classa', 'silverstripe\test\classb', 'silverstripe\test\classc',
'silverstripe\test\classd', 'silverstripe\test\classe', 'silverstripe\test\classf',
'silverstripe\test\classg', 'silverstripe\test\classh'),
$this->manifest->getClassNames());
}

View File

@ -19,7 +19,8 @@ class CSVParserTest extends SapphireTest {
$this->assertEquals(array(
"He's a good guy",
"She is awesome." . PHP_EOL . "So awesome that she gets multiple rows and \"escaped\" strings in her biography",
"She is awesome." . PHP_EOL
. "So awesome that she gets multiple rows and \"escaped\" strings in her biography",
"Pretty old, with an escaped comma",
"Unicode FTW"), $biographies);
$this->assertEquals(array("31/01/1988","31/01/1982","31/01/1882","31/06/1982"), $birthdays);
@ -38,7 +39,8 @@ class CSVParserTest extends SapphireTest {
$firstNames = $birthdays = $biographies = $registered = array();
foreach($csv as $record) {
/* Each row in the CSV file will be keyed with the renamed columns. Any unmapped column names will be left as-is. */
/* Each row in the CSV file will be keyed with the renamed columns. Any unmapped column names will be
* left as-is. */
$this->assertEquals(array('__fn','__BG','Birthday','IsRegistered'), array_keys($record));
$firstNames[] = $record['__fn'];
$biographies[] = $record['__BG'];
@ -49,7 +51,8 @@ class CSVParserTest extends SapphireTest {
$this->assertEquals(array('John','Jane','Jamie','Järg'), $firstNames);
$this->assertEquals(array(
"He's a good guy",
"She is awesome." . PHP_EOL . "So awesome that she gets multiple rows and \"escaped\" strings in her biography",
"She is awesome."
. PHP_EOL . "So awesome that she gets multiple rows and \"escaped\" strings in her biography",
"Pretty old, with an escaped comma",
"Unicode FTW"), $biographies);
$this->assertEquals(array("31/01/1988","31/01/1982","31/01/1882","31/06/1982"), $birthdays);
@ -77,7 +80,8 @@ class CSVParserTest extends SapphireTest {
$this->assertEquals(array(
'Biography',
"He's a good guy",
"She is awesome." . PHP_EOL . "So awesome that she gets multiple rows and \"escaped\" strings in her biography",
"She is awesome." . PHP_EOL
. "So awesome that she gets multiple rows and \"escaped\" strings in her biography",
"Pretty old, with an escaped comma",
"Unicode FTW"), $biographies);
$this->assertEquals(array("Birthday","31/01/1988","31/01/1982","31/01/1882","31/06/1982"), $birthdays);

View File

@ -48,10 +48,13 @@ class CsvBulkLoaderTest extends SapphireTest {
$results1 = $loader->load($filepath);
$this->assertEquals(4, $results1->Count(), 'Test correct count of imported data on first load');
$results2 = $loader->load($filepath, '512MB', true); //delete existing data before doing second CSV import
$resultDataObject = DataObject::get('CsvBulkLoaderTest_Player'); //get all instances of the loaded DataObject from the database and count them
//delete existing data before doing second CSV import
$results2 = $loader->load($filepath, '512MB', true);
//get all instances of the loaded DataObject from the database and count them
$resultDataObject = DataObject::get('CsvBulkLoaderTest_Player');
$this->assertEquals(4, $resultDataObject->Count(), 'Test if existing data is deleted before new data is added');
$this->assertEquals(4, $resultDataObject->Count(),
'Test if existing data is deleted before new data is added');
}
/**
@ -132,7 +135,8 @@ class CsvBulkLoaderTest extends SapphireTest {
// Test nested setting of relation properties
$contractAmount = DBField::create_field('Currency', $compareRow[5])->RAW();
$this->assertEquals($testPlayer->Contract()->Amount, $contractAmount, 'Setting nested values in a relation works');
$this->assertEquals($testPlayer->Contract()->Amount, $contractAmount,
'Setting nested values in a relation works');
fclose($file);
}
@ -154,7 +158,8 @@ class CsvBulkLoaderTest extends SapphireTest {
$player = $createdPlayers->First();
$this->assertEquals($player->FirstName, 'John');
$this->assertEquals($player->Biography, 'He\'s a good guy', 'test updating of duplicate imports within the same import works');
$this->assertEquals($player->Biography, 'He\'s a good guy',
'test updating of duplicate imports within the same import works');
// load with updated data
$filepath = FRAMEWORK_PATH . '/tests/dev/CsvBulkLoaderTest_PlayersWithIdUpdated.csv';
@ -163,7 +168,8 @@ class CsvBulkLoaderTest extends SapphireTest {
// HACK need to update the loaded record from the database
$player = DataObject::get_by_id('CsvBulkLoaderTest_Player', $player->ID);
$this->assertEquals($player->FirstName, 'JohnUpdated', 'Test updating of existing records works');
$this->assertEquals($player->Biography, 'He\'s a good guy', 'Test retaining of previous information on duplicate when overwriting with blank field');
$this->assertEquals($player->Biography, 'He\'s a good guy',
'Test retaining of previous information on duplicate when overwriting with blank field');
}
public function testLoadWithCustomImportMethods() {

View File

@ -19,7 +19,8 @@ class FileTest extends SapphireTest {
$fileEnclosed = sprintf('[file_link id=%d]Example Content[/file_link]', $testFile->ID);
$fileShortcodeExpected = $testFile->Link();
$fileEnclosedExpected = sprintf('<a href="%s" class="file" data-type="txt" data-size="977 KB">Example Content</a>', $testFile->Link());
$fileEnclosedExpected = sprintf(
'<a href="%s" class="file" data-type="txt" data-size="977 KB">Example Content</a>', $testFile->Link());
$this->assertEquals($fileShortcodeExpected, $parser->parse($fileShortcode), 'Test that simple linking works.');
$this->assertEquals($fileEnclosedExpected, $parser->parse($fileEnclosed), 'Test enclosed content is linked.');
@ -45,7 +46,8 @@ class FileTest extends SapphireTest {
$parser->parse($fileEnclosed)
);
} else {
$this->assertEquals('', $parser->parse($fileShortcode), 'Short code is removed if file record is not present.');
$this->assertEquals('', $parser->parse($fileShortcode),
'Short code is removed if file record is not present.');
$this->assertEquals('', $parser->parse($fileEnclosed));
}
}
@ -66,22 +68,28 @@ class FileTest extends SapphireTest {
$file->ParentID = $folder->ID;
$file->write();
$this->assertEquals('CreateWithFilenameHasCorrectPath.txt', $file->Name, '"Name" property is automatically set from "Filename"');
$this->assertEquals($testfilePath, $file->Filename, '"Filename" property remains unchanged');
$this->assertEquals('CreateWithFilenameHasCorrectPath.txt', $file->Name,
'"Name" property is automatically set from "Filename"');
$this->assertEquals($testfilePath, $file->Filename,
'"Filename" property remains unchanged');
// TODO This should be auto-detected, see File->updateFilesystem()
// $this->assertInstanceOf('Folder', $file->Parent(), 'Parent folder is created in database');
// $this->assertFileExists($file->Parent()->getFullPath(), 'Parent folder is created on filesystem');
// $this->assertEquals('FileTest', $file->Parent()->Name);
// $this->assertInstanceOf('Folder', $file->Parent()->Parent(), 'Grandparent folder is created in database');
// $this->assertFileExists($file->Parent()->Parent()->getFullPath(), 'Grandparent folder is created on filesystem');
// $this->assertFileExists($file->Parent()->Parent()->getFullPath(),
// 'Grandparent folder is created on filesystem');
// $this->assertEquals('assets', $file->Parent()->Parent()->Name);
}
public function testGetExtension() {
$this->assertEquals('', File::get_file_extension('myfile'), 'No extension');
$this->assertEquals('txt', File::get_file_extension('myfile.txt'), 'Simple extension');
$this->assertEquals('gz', File::get_file_extension('myfile.tar.gz'), 'Double-barrelled extension only returns last bit');
$this->assertEquals('', File::get_file_extension('myfile'),
'No extension');
$this->assertEquals('txt', File::get_file_extension('myfile.txt'),
'Simple extension');
$this->assertEquals('gz', File::get_file_extension('myfile.tar.gz'),
'Double-barrelled extension only returns last bit');
}
public function testValidateExtension() {
@ -117,8 +125,10 @@ class FileTest extends SapphireTest {
// Before write()
$file->Name = 'renamed.txt';
$this->assertFileExists($oldPath, 'Old path is still present');
$this->assertFileNotExists($file->getFullPath(), 'New path is updated in memory, not written before write() is called');
$this->assertFileExists($oldPath,
'Old path is still present');
$this->assertFileNotExists($file->getFullPath(),
'New path is updated in memory, not written before write() is called');
$file->write();
@ -137,15 +147,19 @@ class FileTest extends SapphireTest {
$file->ParentID = $subfolder->ID;
// Before write()
$this->assertFileExists($oldPath, 'Old path is still present');
$this->assertFileNotExists($file->getFullPath(), 'New path is updated in memory, not written before write() is called');
$this->assertFileExists($oldPath,
'Old path is still present');
$this->assertFileNotExists($file->getFullPath(),
'New path is updated in memory, not written before write() is called');
$file->write();
// After write()
clearstatcache();
$this->assertFileNotExists($oldPath, 'Old path is removed after write()');
$this->assertFileExists($file->getFullPath(), 'New path is created after write()');
$this->assertFileNotExists($oldPath,
'Old path is removed after write()');
$this->assertFileExists($file->getFullPath(),
'New path is created after write()');
}
/**
@ -181,10 +195,13 @@ class FileTest extends SapphireTest {
$this->assertEquals('assets/FileTest.txt', $rootfile->getRelativePath(), 'File in assets/ folder');
$subfolderfile = $this->objFromFixture('File', 'subfolderfile');
$this->assertEquals('assets/FileTest-subfolder/FileTestSubfolder.txt', $subfolderfile->getRelativePath(), 'File in subfolder within assets/ folder, with existing Filename');
$this->assertEquals('assets/FileTest-subfolder/FileTestSubfolder.txt', $subfolderfile->getRelativePath(),
'File in subfolder within assets/ folder, with existing Filename');
$subfolderfilesetfromname = $this->objFromFixture('File', 'subfolderfile-setfromname');
$this->assertEquals('assets/FileTest-subfolder/FileTestSubfolder2.txt', $subfolderfilesetfromname->getRelativePath(), 'File in subfolder within assets/ folder, with Filename generated through setName()');
$this->assertEquals('assets/FileTest-subfolder/FileTestSubfolder2.txt',
$subfolderfilesetfromname->getRelativePath(),
'File in subfolder within assets/ folder, with Filename generated through setName()');
}
public function testGetFullPath() {
@ -277,7 +294,8 @@ class FileTest extends SapphireTest {
//get folder again and see if the filename has changed
$folder = DataObject::get_by_id('Folder',$folderID);
$this->assertEquals($folder->Filename, ASSETS_DIR ."/". $newTitle ."/", "Folder Filename updated after rename of Title");
$this->assertEquals($folder->Filename, ASSETS_DIR ."/". $newTitle ."/",
"Folder Filename updated after rename of Title");
//rename a folder's name
@ -287,7 +305,8 @@ class FileTest extends SapphireTest {
//get folder again and see if the Title has changed
$folder = DataObject::get_by_id('Folder',$folderID);
$this->assertEquals($folder->Title, $newTitle2, "Folder Title updated after rename of Name");
$this->assertEquals($folder->Title, $newTitle2,
"Folder Title updated after rename of Name");
//rename a folder's Filename
@ -297,7 +316,8 @@ class FileTest extends SapphireTest {
//get folder again and see if the Title has changed
$folder = DataObject::get_by_id('Folder',$folderID);
$this->assertEquals($folder->Title, $newTitle3, "Folder Title updated after rename of Filename");
$this->assertEquals($folder->Title, $newTitle3,
"Folder Title updated after rename of Filename");
}
@ -407,7 +427,9 @@ class FileTest extends SapphireTest {
$folderIDs = $this->allFixtureIDs('Folder');
foreach($folderIDs as $folderID) {
$folder = DataObject::get_by_id('Folder', $folderID);
if($folder && file_exists(BASE_PATH."/$folder->Filename")) Filesystem::removeFolder(BASE_PATH."/$folder->Filename");
if($folder && file_exists(BASE_PATH."/$folder->Filename")) {
Filesystem::removeFolder(BASE_PATH."/$folder->Filename");
}
}
// Remove left over folders and any files that may exist
@ -415,9 +437,15 @@ class FileTest extends SapphireTest {
if(file_exists('../assets/FileTest-subfolder')) Filesystem::removeFolder('../assets/FileTest-subfolder');
if(file_exists('../assets/FileTest.txt')) unlink('../assets/FileTest.txt');
if (file_exists("../assets/FileTest-folder-renamed1")) Filesystem::removeFolder("../assets/FileTest-folder-renamed1");
if (file_exists("../assets/FileTest-folder-renamed2")) Filesystem::removeFolder("../assets/FileTest-folder-renamed2");
if (file_exists("../assets/FileTest-folder-renamed3")) Filesystem::removeFolder("../assets/FileTest-folder-renamed3");
if (file_exists("../assets/FileTest-folder-renamed1")) {
Filesystem::removeFolder("../assets/FileTest-folder-renamed1");
}
if (file_exists("../assets/FileTest-folder-renamed2")) {
Filesystem::removeFolder("../assets/FileTest-folder-renamed2");
}
if (file_exists("../assets/FileTest-folder-renamed3")) {
Filesystem::removeFolder("../assets/FileTest-folder-renamed3");
}
}
}

View File

@ -70,11 +70,14 @@ class FolderTest extends SapphireTest {
// Before write()
$folder1->Name = 'FileTest-folder1-renamed';
$this->assertFileExists($oldPathFolder1, 'Old path is still present');
$this->assertFileNotExists($folder1->getFullPath(), 'New path is updated in memory, not written before write() is called');
$this->assertFileNotExists($folder1->getFullPath(),
'New path is updated in memory, not written before write() is called');
$this->assertFileExists($oldPathFile1, 'Old file is still present');
// TODO setters currently can't update in-memory
// $this->assertFileNotExists($file1->getFullPath(), 'New path on contained files is updated in memory, not written before write() is called');
// $this->assertFileNotExists($subfolder1->getFullPath(), 'New path on subfolders is updated in memory, not written before write() is called');
// $this->assertFileNotExists($file1->getFullPath(),
// 'New path on contained files is updated in memory, not written before write() is called');
// $this->assertFileNotExists($subfolder1->getFullPath(),
// 'New path on subfolders is updated in memory, not written before write() is called');
$folder1->write();
@ -111,7 +114,8 @@ class FolderTest extends SapphireTest {
// Before write()
$this->assertFileExists($oldPathFolder1, 'Old path is still present');
$this->assertFileNotExists($folder1->getFullPath(), 'New path is updated in memory, not written before write() is called');
$this->assertFileNotExists($folder1->getFullPath(),
'New path is updated in memory, not written before write() is called');
$folder1->write();
@ -136,7 +140,8 @@ class FolderTest extends SapphireTest {
// Check if the file in the folder moved along
$file1 = DataObject::get_by_id('File', $this->idFromFixture('File', 'file1-folder1'), false);
$this->assertFileExists($file1->getFullPath());
$this->assertEquals($file1->Filename, 'assets/FileTest-folder2/FileTest-folder1/File1.txt', 'The file DataObject has updated path');
$this->assertEquals($file1->Filename, 'assets/FileTest-folder2/FileTest-folder1/File1.txt',
'The file DataObject has updated path');
}
/**
@ -152,7 +157,8 @@ class FolderTest extends SapphireTest {
// Check if the file in the folder moved along
$file1 = DataObject::get_by_id('File', $this->idFromFixture('File', 'file1-folder1'), false);
$this->assertFileExists($file1->getFullPath());
$this->assertEquals($file1->Filename, 'assets/FileTest-folder1-changed/File1.txt', 'The file DataObject path uses renamed folder');
$this->assertEquals($file1->Filename, 'assets/FileTest-folder1-changed/File1.txt',
'The file DataObject path uses renamed folder');
}
/**
@ -280,7 +286,9 @@ class FolderTest extends SapphireTest {
foreach($folderIDs as $folderID) {
$folder = DataObject::get_by_id('Folder', $folderID);
// Might have been removed during test
if($folder && file_exists(BASE_PATH."/$folder->Filename")) Filesystem::removeFolder(BASE_PATH."/$folder->Filename");
if($folder && file_exists(BASE_PATH."/$folder->Filename")) {
Filesystem::removeFolder(BASE_PATH."/$folder->Filename");
}
}
parent::tearDown();

View File

@ -122,16 +122,19 @@ class CheckboxFieldTest extends SapphireTest {
// Test 1: a checked checkbox goes to "Yes"
$field1 = new CheckboxField('IsChecked', 'Checked');
$field1->setValue('on');
$this->assertEquals(_t('CheckboxField.YES', 'Yes'), trim(strip_tags($field1->performReadonlyTransformation()->Field())));
$this->assertEquals(_t('CheckboxField.YES', 'Yes'),
trim(strip_tags($field1->performReadonlyTransformation()->Field())));
// Test 2: an checkbox with the value set to false to "No"
$field2 = new CheckboxField('IsChecked', 'Checked');
$field2->setValue(false);
$this->assertEquals(_t('CheckboxField.NO', 'No'), trim(strip_tags($field2->performReadonlyTransformation()->Field())));
$this->assertEquals(_t('CheckboxField.NO', 'No'),
trim(strip_tags($field2->performReadonlyTransformation()->Field())));
// Test 3: an checkbox with no value ever set goes to "No"
$field3 = new CheckboxField('IsChecked', 'Checked');
$this->assertEquals(_t('CheckboxField.NO', 'No'), trim(strip_tags($field3->performReadonlyTransformation()->Field())));
$this->assertEquals(_t('CheckboxField.NO', 'No'),
trim(strip_tags($field3->performReadonlyTransformation()->Field())));
}

View File

@ -119,7 +119,7 @@ class CheckboxSetFieldTest extends SapphireTest {
$tag2->ID => $tag2->ID
),
$field->Value(),
'CheckboxSetField properly loads data from a manymany relationship in an object through Form->loadDataFrom()'
'CheckboxSetField loads data from a manymany relationship in an object through Form->loadDataFrom()'
);
}
}

View File

@ -77,8 +77,10 @@ class ComplexTableFieldTest extends FunctionalTest {
$newSponsor = DataObject::get_one('ComplexTableFieldTest_Sponsor', "\"Name\" = 'Jim Beam'");
$this->assertNotNull($newSponsor, 'A new ComplexTableFieldTest_Sponsor record was created, Name = "Jim Beam"');
$this->assertEquals($newSponsor->TeamID, $team->ID, 'Automatic has-many/has-one relation was set correctly on the sponsor');
$this->assertEquals($newSponsor->getComponent('Team')->ID, $team->ID, 'Automatic has-many/has-one relation was set correctly on the sponsor');
$this->assertEquals($newSponsor->TeamID, $team->ID,
'Automatic has-many/has-one relation was set correctly on the sponsor');
$this->assertEquals($newSponsor->getComponent('Team')->ID, $team->ID,
'Automatic has-many/has-one relation was set correctly on the sponsor');
$team = DataObject::get_by_id('ComplexTableFieldTest_Team', $team->ID);
$sponsor = DataObject::get_by_id('ComplexTableFieldTest_Sponsor', $newSponsor->ID);

View File

@ -19,13 +19,17 @@ class ConfirmedPasswordFieldTest extends SapphireTest {
//hide by default and display show/hide toggle button
$field = new ConfirmedPasswordField('Test', 'Testing', 'valueA', null, true);
$fieldHTML = $field->Field();
$this->assertContains("showOnClickContainer",$fieldHTML,"Test class for hiding/showing the form contents is set");
$this->assertContains("showOnClick",$fieldHTML,"Test class for hiding/showing the form contents is set");
$this->assertContains("showOnClickContainer", $fieldHTML,
"Test class for hiding/showing the form contents is set");
$this->assertContains("showOnClick", $fieldHTML,
"Test class for hiding/showing the form contents is set");
//show all by default
$field = new ConfirmedPasswordField('Test', 'Testing', 'valueA', null, false);
$fieldHTML = $field->Field();
$this->assertNotContains("showOnClickContainer",$fieldHTML,"Test class for hiding/showing the form contents is set");
$this->assertNotContains("showOnClick",$fieldHTML,"Test class for hiding/showing the form contents is set");
$this->assertNotContains("showOnClickContainer", $fieldHTML,
"Test class for hiding/showing the form contents is set");
$this->assertNotContains("showOnClick", $fieldHTML,
"Test class for hiding/showing the form contents is set");
}
}

View File

@ -122,10 +122,12 @@ class DatetimeFieldTest extends SapphireTest {
// Berlin and Auckland have 12h time difference in northern hemisphere winter
$f = new DatetimeField('Datetime', 'Datetime', '2003-12-24 23:59:59');
$f->setConfig('usertimezone', 'Pacific/Auckland');
$this->assertEquals('25/12/2003 11:59:59', $f->Value(), 'User value is formatted, and in user timezone');
$this->assertEquals('25/12/2003 11:59:59', $f->Value(),
'User value is formatted, and in user timezone');
$this->assertEquals('25/12/2003', $f->getDateField()->Value());
$this->assertEquals('11:59:59', $f->getTimeField()->Value());
$this->assertEquals('2003-12-24 23:59:59', $f->dataValue(), 'Data value is unformatted, and in server timezone');
$this->assertEquals('2003-12-24 23:59:59', $f->dataValue(),
'Data value is unformatted, and in server timezone');
date_default_timezone_set($oldTz);
}

View File

@ -89,7 +89,8 @@ class DropdownFieldTest extends SapphireTest {
/* There is one selected option, since a dropdown can only possibly have one! */
$selectedOptions = $this->findSelectedOptionElements($field->Field());
$this->assertEquals(count($selectedOptions), 1, 'We only have 1 selected option, since a dropdown can only possibly have one!');
$this->assertEquals(count($selectedOptions), 1,
'We only have 1 selected option, since a dropdown can only possibly have one!');
/* Create a field without a blank value */
$field = $this->testDropdownField();
@ -142,7 +143,8 @@ class DropdownFieldTest extends SapphireTest {
$selectedOptions = $this->findSelectedOptionElements($field->Field());
/* The selected option is "Cats and Kittens" */
$this->assertEquals((string) $selectedOptions[0], 'Cats and Kittens', 'The selected option is "Cats and Kittens"');
$this->assertEquals((string) $selectedOptions[0], 'Cats and Kittens',
'The selected option is "Cats and Kittens"');
}
/**

View File

@ -30,10 +30,15 @@ class EmailFieldTest extends FunctionalTest {
$val = new EmailFieldTest_Validator();
try {
$field->validate($val);
if (!$expectSuccess) $this->assertTrue(false, $checkText . " (/$email/ passed validation, but not expected to)");
if (!$expectSuccess) {
$this->assertTrue(false,$checkText . " (/$email/ passed validation, but not expected to)");
}
} catch (Exception $e) {
if ($e instanceof PHPUnit_Framework_AssertionFailedError) throw $e; // re-throw assertion failure
else if ($expectSuccess) $this->assertTrue(false, $checkText . ": " . $e->GetMessage() . " (/$email/ did not pass validation, but was expected to)");
else if ($expectSuccess) {
$this->assertTrue(false,
$checkText . ": " . $e->GetMessage() . " (/$email/ did not pass validation, but was expected to)");
}
}
}
@ -76,7 +81,12 @@ class EmailFieldTest_Controller extends Controller implements TestOnly {
protected $template = 'BlankPage';
function Link($action = null) {
return Controller::join_links('EmailFieldTest_Controller', $this->request->latestParam('Action'), $this->request->latestParam('ID'), $action);
return Controller::join_links(
'EmailFieldTest_Controller',
$this->request->latestParam('Action'),
$this->request->latestParam('ID'),
$action
);
}
function Form() {

View File

@ -200,7 +200,8 @@ class FieldListTest extends SapphireTest {
/* The field "A" gets added to the FieldList we just created created */
$FieldList->addFieldToTab('Root.Other', $newA = new TextField('A', 'New Title'));
/* The field named "A" has been removed from the Main tab to make way for our new field named "A" in Other tab. */
/* The field named "A" has been removed from the Main tab to make way for our new field named "A" in
* Other tab. */
$this->assertEquals(1, $main->Fields()->Count());
$this->assertEquals(3, $other->Fields()->Count());
}
@ -473,8 +474,8 @@ class FieldListTest extends SapphireTest {
)
);
/* If you have a tab with the same name as the field, then technically it's a duplicate. However, it's allowed because
tab isn't a data field. Only duplicate data fields are problematic */
/* If you have a tab with the same name as the field, then technically it's a duplicate. However, it's
* allowed because tab isn't a data field. Only duplicate data fields are problematic */
$FieldList->addFieldToTab("Root.MyName", $myName = new TextField("MyName"));
$this->assertNotNull($FieldList->fieldByName('Root')->fieldByName('MyName'));
$this->assertSame($myName, $FieldList->fieldByName('Root')->fieldByName('MyName')->Fields()->First());

View File

@ -68,16 +68,16 @@ class FormFieldTest extends SapphireTest {
$this->assertEquals(
$isReadonlyBefore,
$instance->isReadonly(),
"FormField class '{$fieldClass} retains its readonly state after calling performReadonlyTransformation()"
"FormField class {$fieldClass} retains its readonly state after calling performReadonlyTransformation()"
);
$this->assertTrue(
$readonlyInstance->isReadonly(),
"FormField class '{$fieldClass} returns a valid readonly representation as of isReadonly()"
"FormField class {$fieldClass} returns a valid readonly representation as of isReadonly()"
);
$this->assertNotSame(
$readonlyInstance,
$instance,
"FormField class '{$fieldClass} returns a valid cloned readonly representation"
"FormField class {$fieldClass} returns a valid cloned readonly representation"
);
}
}
@ -102,16 +102,16 @@ class FormFieldTest extends SapphireTest {
$this->assertEquals(
$isDisabledBefore,
$instance->isDisabled(),
"FormField class '{$fieldClass} retains its disabled state after calling performDisabledTransformation()"
"FormField class {$fieldClass} retains its disabled state after calling performDisabledTransformation()"
);
$this->assertTrue(
$disabledInstance->isDisabled(),
"FormField class '{$fieldClass} returns a valid disabled representation as of isDisabled()"
"FormField class {$fieldClass} returns a valid disabled representation as of isDisabled()"
);
$this->assertNotSame(
$disabledInstance,
$instance,
"FormField class '{$fieldClass} returns a valid cloned disabled representation"
"FormField class {$fieldClass} returns a valid cloned disabled representation"
);
}
}

View File

@ -24,11 +24,16 @@ class FormScaffolderTest extends SapphireTest {
$form = new Form(new Controller(), 'TestForm', $fields, new FieldList());
$form->loadDataFrom($article);
$this->assertTrue($fields->hasTabSet(), 'getCMSFields() produces a TabSet');
$this->assertNotNull($fields->dataFieldByName('Title'), 'getCMSFields() includes db fields');
$this->assertNotNull($fields->dataFieldByName('Content'), 'getCMSFields() includes db fields');
$this->assertNotNull($fields->dataFieldByName('AuthorID'), 'getCMSFields() includes has_one fields on singletons');
$this->assertNull($fields->dataFieldByName('Tags'), 'getCMSFields() doesnt include many_many fields if no ID is present');
$this->assertTrue($fields->hasTabSet(),
'getCMSFields() produces a TabSet');
$this->assertNotNull($fields->dataFieldByName('Title'),
'getCMSFields() includes db fields');
$this->assertNotNull($fields->dataFieldByName('Content'),
'getCMSFields() includes db fields');
$this->assertNotNull($fields->dataFieldByName('AuthorID'),
'getCMSFields() includes has_one fields on singletons');
$this->assertNull($fields->dataFieldByName('Tags'),
'getCMSFields() doesnt include many_many fields if no ID is present');
}
public function testGetCMSFieldsInstance() {
@ -38,8 +43,10 @@ class FormScaffolderTest extends SapphireTest {
$form = new Form(new Controller(), 'TestForm', $fields, new FieldList());
$form->loadDataFrom($article1);
$this->assertNotNull($fields->dataFieldByName('AuthorID'), 'getCMSFields() includes has_one fields on instances');
$this->assertNotNull($fields->dataFieldByName('Tags'), 'getCMSFields() includes many_many fields if ID is present on instances');
$this->assertNotNull($fields->dataFieldByName('AuthorID'),
'getCMSFields() includes has_one fields on instances');
$this->assertNotNull($fields->dataFieldByName('Tags'),
'getCMSFields() includes many_many fields if ID is present on instances');
}
public function testUpdateCMSFields() {
@ -64,8 +71,10 @@ class FormScaffolderTest extends SapphireTest {
$form = new Form(new Controller(), 'TestForm', $fields, new FieldList());
$form->loadDataFrom($article1);
$this->assertNotNull($fields->dataFieldByName('Title'), 'scaffoldCMSFields() includes explitly defined "restrictFields"');
$this->assertNull($fields->dataFieldByName('Content'), 'getCMSFields() doesnt include fields left out in a "restrictFields" definition');
$this->assertNotNull($fields->dataFieldByName('Title'),
'scaffoldCMSFields() includes explitly defined "restrictFields"');
$this->assertNull($fields->dataFieldByName('Content'),
'getCMSFields() doesnt include fields left out in a "restrictFields" definition');
}
public function testFieldClassesOnGetCMSFields() {

View File

@ -419,7 +419,8 @@ class FormTest_Controller extends Controller implements TestOnly {
protected $template = 'BlankPage';
public function Link($action = null) {
return Controller::join_links('FormTest_Controller', $this->request->latestParam('Action'), $this->request->latestParam('ID'), $action);
return Controller::join_links('FormTest_Controller', $this->request->latestParam('Action'),
$this->request->latestParam('ID'), $action);
}
public function Form() {
@ -480,7 +481,8 @@ class FormTest_ControllerWithSecurityToken extends Controller implements TestOnl
protected $template = 'BlankPage';
public function Link($action = null) {
return Controller::join_links('FormTest_ControllerWithSecurityToken', $this->request->latestParam('Action'), $this->request->latestParam('ID'), $action);
return Controller::join_links('FormTest_ControllerWithSecurityToken', $this->request->latestParam('Action'),
$this->request->latestParam('ID'), $action);
}
public function Form() {

View File

@ -298,7 +298,8 @@ class GridFieldTest extends SapphireTest {
$config = GridFieldConfig::create()->addComponents(
new GridFieldTest_HTMLFragments(array(
"header" => "<tr><td><div class=\"right\">\$DefineFragment(header-right-actions)</div><div class=\"left\">\$DefineFragment(header-left-actions)</div></td></tr>",
"header" => "<tr><td><div class=\"right\">\$DefineFragment(header-right-actions)</div>"
. "<div class=\"left\">\$DefineFragment(header-left-actions)</div></td></tr>",
)),
new GridFieldTest_HTMLFragments(array(
"header-left-actions" => "left",
@ -371,9 +372,12 @@ class GridFieldTest extends SapphireTest {
public function testCanViewOnlyOddIDs() {
$this->logInWithPermission();
$list = new ArrayList(array(
new GridFieldTest_Permissions(array("ID" => 1, "Email" => "ongi.schwimmer@example.org", 'Name' => 'Ongi Schwimmer')),
new GridFieldTest_Permissions(array("ID" => 2, "Email" => "klaus.lozenge@example.org", 'Name' => 'Klaus Lozenge')),
new GridFieldTest_Permissions(array("ID" => 3, "Email" => "otto.fischer@example.org", 'Name' => 'Otto Fischer'))
new GridFieldTest_Permissions(array("ID" => 1, "Email" => "ongi.schwimmer@example.org",
'Name' => 'Ongi Schwimmer')),
new GridFieldTest_Permissions(array("ID" => 2, "Email" => "klaus.lozenge@example.org",
'Name' => 'Klaus Lozenge')),
new GridFieldTest_Permissions(array("ID" => 3, "Email" => "otto.fischer@example.org",
'Name' => 'Otto Fischer'))
));
$config = new GridFieldConfig();
@ -386,11 +390,15 @@ class GridFieldTest extends SapphireTest {
$this->assertEquals(2, count($members));
$this->assertEquals((string)$members[0]->td[0], 'Ongi Schwimmer', 'First object Name should be Ongi Schwimmer');
$this->assertEquals((string)$members[0]->td[1], 'ongi.schwimmer@example.org', 'First object Email should be ongi.schwimmer@example.org');
$this->assertEquals((string)$members[0]->td[0], 'Ongi Schwimmer',
'First object Name should be Ongi Schwimmer');
$this->assertEquals((string)$members[0]->td[1], 'ongi.schwimmer@example.org',
'First object Email should be ongi.schwimmer@example.org');
$this->assertEquals((string)$members[1]->td[0], 'Otto Fischer', 'Second object Name should be Otto Fischer');
$this->assertEquals((string)$members[1]->td[1], 'otto.fischer@example.org', 'Second object Email should be otto.fischer@example.org');
$this->assertEquals((string)$members[1]->td[0], 'Otto Fischer',
'Second object Name should be Otto Fischer');
$this->assertEquals((string)$members[1]->td[1], 'otto.fischer@example.org',
'Second object Email should be otto.fischer@example.org');
}
public function testChainedDataManipulators() {

View File

@ -8,7 +8,8 @@ class ListboxFieldTest extends SapphireTest {
static $fixture_file = 'ListboxFieldTest.yml';
protected $extraDataObjects = array('ListboxFieldTest_DataObject', 'ListboxFieldTest_Article', 'ListboxFieldTest_Tag');
protected $extraDataObjects = array('ListboxFieldTest_DataObject', 'ListboxFieldTest_Article',
'ListboxFieldTest_Tag');
public function testFieldWithManyManyRelationship() {
$articleWithTags = $this->objFromFixture('ListboxFieldTest_Article', 'articlewithtags');

View File

@ -44,8 +44,8 @@ class LookupFieldTest extends SapphireTest {
$source = array('one' => 'one val', 'two' => 'two val', 'three' => 'three val');
$f = new LookupField('test', 'test', $source);
$f->setValue(array('one','two'));
$this->assertEquals(
'<span class="readonly" id="test">one val, two val</span><input type="hidden" name="test" value="one, two" />',
$this->assertEquals('<span class="readonly" id="test">one val, two val</span>'
. '<input type="hidden" name="test" value="one, two" />',
$f->Field()
);
}
@ -71,7 +71,8 @@ class LookupFieldTest extends SapphireTest {
$f->setValue(array($member1->ID, $member2->ID));
$this->assertEquals(
sprintf(
'<span class="readonly" id="test">member1, member2</span><input type="hidden" name="test" value="%s, %s" />',
'<span class="readonly" id="test">member1, member2</span>'
. '<input type="hidden" name="test" value="%s, %s" />',
$member1->ID,
$member2->ID
),

View File

@ -45,7 +45,8 @@ class MemberDatetimeOptionsetFieldTest extends SapphireTest {
public function testDateFormatDefaultCheckedInFormField() {
$field = $this->createDateFormatFieldForMember($this->objFromFixture('Member', 'noformatmember'));
$field->setForm(new Form(new MemberDatetimeOptionsetFieldTest_Controller(), 'Form', new FieldList(), new FieldList())); // fake form
$field->setForm(new Form(new MemberDatetimeOptionsetFieldTest_Controller(), 'Form', new FieldList(),
new FieldList())); // fake form
$parser = new CSSContentParser($field->Field());
$xmlArr = $parser->getBySelector('#Form_Form_DateFormat_MMM_d__y');
$this->assertEquals('checked', (string) $xmlArr[0]['checked']);
@ -53,7 +54,8 @@ class MemberDatetimeOptionsetFieldTest extends SapphireTest {
public function testTimeFormatDefaultCheckedInFormField() {
$field = $this->createTimeFormatFieldForMember($this->objFromFixture('Member', 'noformatmember'));
$field->setForm(new Form(new MemberDatetimeOptionsetFieldTest_Controller(), 'Form', new FieldList(), new FieldList())); // fake form
$field->setForm(new Form(new MemberDatetimeOptionsetFieldTest_Controller(), 'Form', new FieldList(),
new FieldList())); // fake form
$parser = new CSSContentParser($field->Field());
$xmlArr = $parser->getBySelector('#Form_Form_TimeFormat_h_mm_ss_a');
$this->assertEquals('checked', (string) $xmlArr[0]['checked']);
@ -63,7 +65,8 @@ class MemberDatetimeOptionsetFieldTest extends SapphireTest {
$member = $this->objFromFixture('Member', 'noformatmember');
$member->setField('DateFormat', 'MM/dd/yyyy');
$field = $this->createDateFormatFieldForMember($member);
$field->setForm(new Form(new MemberDatetimeOptionsetFieldTest_Controller(), 'Form', new FieldList(), new FieldList())); // fake form
$field->setForm(new Form(new MemberDatetimeOptionsetFieldTest_Controller(), 'Form', new FieldList(),
new FieldList())); // fake form
$parser = new CSSContentParser($field->Field());
$xmlArr = $parser->getBySelector('#Form_Form_DateFormat_MM_dd_yyyy');
$this->assertEquals('checked', (string) $xmlArr[0]['checked']);
@ -73,7 +76,8 @@ class MemberDatetimeOptionsetFieldTest extends SapphireTest {
$member = $this->objFromFixture('Member', 'noformatmember');
$member->setField('DateFormat', 'dd MM yy');
$field = $this->createDateFormatFieldForMember($member);
$field->setForm(new Form(new MemberDatetimeOptionsetFieldTest_Controller(), 'Form', new FieldList(), new FieldList())); // fake form
$field->setForm(new Form(new MemberDatetimeOptionsetFieldTest_Controller(), 'Form', new FieldList(),
new FieldList())); // fake form
$parser = new CSSContentParser($field->Field());
$xmlInputArr = $parser->getBySelector('.valCustom input');
$xmlPreview = $parser->getBySelector('.preview');

View File

@ -108,21 +108,28 @@ class RequirementsTest extends SapphireTest {
$html = $backend->includeInHTML(false, self::$html_template);
/* COMBINED JAVASCRIPT FILE IS INCLUDED IN HTML HEADER */
$this->assertTrue((bool)preg_match('/src=".*\/RequirementsTest_bc\.js/', $html), 'combined javascript file is included in html header');
$this->assertTrue((bool)preg_match('/src=".*\/RequirementsTest_bc\.js/', $html),
'combined javascript file is included in html header');
/* COMBINED JAVASCRIPT FILE EXISTS */
$this->assertTrue(file_exists($combinedFilePath), 'combined javascript file exists');
$this->assertTrue(file_exists($combinedFilePath),
'combined javascript file exists');
/* COMBINED JAVASCRIPT HAS CORRECT CONTENT */
$this->assertTrue((strpos(file_get_contents($combinedFilePath), "alert('b')") !== false), 'combined javascript has correct content');
$this->assertTrue((strpos(file_get_contents($combinedFilePath), "alert('c')") !== false), 'combined javascript has correct content');
$this->assertTrue((strpos(file_get_contents($combinedFilePath), "alert('b')") !== false),
'combined javascript has correct content');
$this->assertTrue((strpos(file_get_contents($combinedFilePath), "alert('c')") !== false),
'combined javascript has correct content');
/* COMBINED FILES ARE NOT INCLUDED TWICE */
$this->assertFalse((bool)preg_match('/src=".*\/RequirementsTest_b\.js/', $html), 'combined files are not included twice');
$this->assertFalse((bool)preg_match('/src=".*\/RequirementsTest_c\.js/', $html), 'combined files are not included twice');
$this->assertFalse((bool)preg_match('/src=".*\/RequirementsTest_b\.js/', $html),
'combined files are not included twice');
$this->assertFalse((bool)preg_match('/src=".*\/RequirementsTest_c\.js/', $html),
'combined files are not included twice');
/* NORMAL REQUIREMENTS ARE STILL INCLUDED */
$this->assertTrue((bool)preg_match('/src=".*\/RequirementsTest_a\.js/', $html), 'normal requirements are still included');
$this->assertTrue((bool)preg_match('/src=".*\/RequirementsTest_a\.js/', $html),
'normal requirements are still included');
$backend->delete_combined_files('RequirementsTest_bc.js');
@ -138,18 +145,24 @@ class RequirementsTest extends SapphireTest {
$html = $backend->includeInHTML(false, self::$html_template);
/* COMBINED JAVASCRIPT FILE IS INCLUDED IN HTML HEADER */
$this->assertTrue((bool)preg_match('/src=".*\/RequirementsTest_bc\.js/', $html), 'combined javascript file is included in html header');
$this->assertTrue((bool)preg_match('/src=".*\/RequirementsTest_bc\.js/', $html),
'combined javascript file is included in html header');
/* COMBINED JAVASCRIPT FILE EXISTS */
$this->assertTrue(file_exists($combinedFilePath), 'combined javascript file exists');
$this->assertTrue(file_exists($combinedFilePath),
'combined javascript file exists');
/* COMBINED JAVASCRIPT HAS CORRECT CONTENT */
$this->assertTrue((strpos(file_get_contents($combinedFilePath), "alert('b')") !== false), 'combined javascript has correct content');
$this->assertTrue((strpos(file_get_contents($combinedFilePath), "alert('c')") !== false), 'combined javascript has correct content');
$this->assertTrue((strpos(file_get_contents($combinedFilePath), "alert('b')") !== false),
'combined javascript has correct content');
$this->assertTrue((strpos(file_get_contents($combinedFilePath), "alert('c')") !== false),
'combined javascript has correct content');
/* COMBINED FILES ARE NOT INCLUDED TWICE */
$this->assertFalse((bool)preg_match('/src=".*\/RequirementsTest_b\.js/', $html), 'combined files are not included twice');
$this->assertFalse((bool)preg_match('/src=".*\/RequirementsTest_c\.js/', $html), 'combined files are not included twice');
$this->assertFalse((bool)preg_match('/src=".*\/RequirementsTest_b\.js/', $html),
'combined files are not included twice');
$this->assertFalse((bool)preg_match('/src=".*\/RequirementsTest_c\.js/', $html),
'combined files are not included twice');
$backend->delete_combined_files('RequirementsTest_bc.js');
}
@ -170,7 +183,8 @@ class RequirementsTest extends SapphireTest {
clearstatcache(); // needed to get accurate file_exists() results
$html = $backend->includeInHTML(false, self::$html_template);
$this->assertFalse((bool)preg_match('/src=".*\/RequirementsTest_bc\.js/', $html), 'blocked combined files are not included ');
$this->assertFalse((bool)preg_match('/src=".*\/RequirementsTest_bc\.js/', $html),
'blocked combined files are not included ');
$backend->unblock('RequirementsTest_bc.js');
/* BLOCKED UNCOMBINED FILES ARE NOT INCLUDED */
@ -179,7 +193,8 @@ class RequirementsTest extends SapphireTest {
$backend->delete_combined_files('RequirementsTest_bc.js');
clearstatcache(); // needed to get accurate file_exists() results
$html = $backend->includeInHTML(false, self::$html_template);
$this->assertFalse((strpos(file_get_contents($combinedFilePath), "alert('b')") !== false), 'blocked uncombined files are not included');
$this->assertFalse((strpos(file_get_contents($combinedFilePath), "alert('b')") !== false),
'blocked uncombined files are not included');
$backend->unblock('RequirementsTest_b.js');
/* A SINGLE FILE CAN'T BE INCLUDED IN TWO COMBINED FILES */
@ -218,10 +233,12 @@ class RequirementsTest extends SapphireTest {
$html = $backend->includeInHTML(false, self::$html_template);
/* Javascript has correct path */
$this->assertTrue((bool)preg_match('/src=".*\/RequirementsTest_a\.js\?m=\d\d+&test=1&test=2&test=3/', $html), 'javascript has correct path');
$this->assertTrue((bool)preg_match('/src=".*\/RequirementsTest_a\.js\?m=\d\d+&test=1&test=2&test=3/', $html),
'javascript has correct path');
/* CSS has correct path */
$this->assertTrue((bool)preg_match('/href=".*\/RequirementsTest_a\.css\?m=\d\d+&test=1&test=2&test=3/', $html), 'css has correct path');
$this->assertTrue((bool)preg_match('/href=".*\/RequirementsTest_a\.css\?m=\d\d+&test=1&test=2&test=3/',$html),
'css has correct path');
}
public function testRequirementsBackend() {
@ -230,28 +247,38 @@ class RequirementsTest extends SapphireTest {
$backend = new Requirements_Backend();
$backend->javascript($basePath . '/a.js');
$this->assertTrue(count($backend->get_javascript()) == 1, "There should be only 1 file included in required javascript.");
$this->assertTrue(in_array($basePath . '/a.js', $backend->get_javascript()), "a.js should be included in required javascript.");
$this->assertTrue(count($backend->get_javascript()) == 1,
"There should be only 1 file included in required javascript.");
$this->assertTrue(in_array($basePath . '/a.js', $backend->get_javascript()),
"a.js should be included in required javascript.");
$backend->javascript($basePath . '/b.js');
$this->assertTrue(count($backend->get_javascript()) == 2, "There should be 2 files included in required javascript.");
$this->assertTrue(count($backend->get_javascript()) == 2,
"There should be 2 files included in required javascript.");
$backend->block($basePath . '/a.js');
$this->assertTrue(count($backend->get_javascript()) == 1, "There should be only 1 file included in required javascript.");
$this->assertFalse(in_array($basePath . '/a.js', $backend->get_javascript()), "a.js should not be included in required javascript after it has been blocked.");
$this->assertTrue(in_array($basePath . '/b.js', $backend->get_javascript()), "b.js should be included in required javascript.");
$this->assertTrue(count($backend->get_javascript()) == 1,
"There should be only 1 file included in required javascript.");
$this->assertFalse(in_array($basePath . '/a.js', $backend->get_javascript()),
"a.js should not be included in required javascript after it has been blocked.");
$this->assertTrue(in_array($basePath . '/b.js', $backend->get_javascript()),
"b.js should be included in required javascript.");
$backend->css($basePath . '/a.css');
$this->assertTrue(count($backend->get_css()) == 1, "There should be only 1 file included in required css.");
$this->assertArrayHasKey($basePath . '/a.css', $backend->get_css(), "a.css should be in required css.");
$this->assertTrue(count($backend->get_css()) == 1,
"There should be only 1 file included in required css.");
$this->assertArrayHasKey($basePath . '/a.css', $backend->get_css(),
"a.css should be in required css.");
$backend->block($basePath . '/a.css');
$this->assertTrue(count($backend->get_css()) == 0, "There should be nothing in required css after file has been blocked.");
$this->assertTrue(count($backend->get_css()) == 0,
"There should be nothing in required css after file has been blocked.");
}
public function testConditionalTemplateRequire() {
$basePath = $this->getCurrentRelativePath();
// we're asserting "framework", so set the relative path accordingly in case FRAMEWORK_DIR was changed to something else
// we're asserting "framework", so set the relative path accordingly in case FRAMEWORK_DIR was changed
// to something else
$basePath = 'framework' . substr($basePath, strlen(FRAMEWORK_DIR));
$backend = new RequirementsTest_Backend();
@ -262,18 +289,22 @@ class RequirementsTest extends SapphireTest {
));
$data->renderWith('RequirementsTest_Conditionals');
$backend->assertFileIncluded('css', $basePath .'/RequirementsTest_a.css');
$backend->assertFileIncluded('js', array($basePath .'/RequirementsTest_b.js', $basePath .'/RequirementsTest_c.js'));
$backend->assertFileIncluded('js',
array($basePath .'/RequirementsTest_b.js', $basePath .'/RequirementsTest_c.js'));
$backend->assertFileNotIncluded('js', $basePath .'/RequirementsTest_a.js');
$backend->assertFileNotIncluded('css', array($basePath .'/RequirementsTest_b.css', $basePath .'/RequirementsTest_c.css'));
$backend->assertFileNotIncluded('css',
array($basePath .'/RequirementsTest_b.css', $basePath .'/RequirementsTest_c.css'));
$backend->clear();
$data = new ArrayData(array(
'FailTest' => false,
));
$data->renderWith('RequirementsTest_Conditionals');
$backend->assertFileNotIncluded('css', $basePath .'/RequirementsTest_a.css');
$backend->assertFileNotIncluded('js', array($basePath .'/RequirementsTest_b.js', $basePath .'/RequirementsTest_c.js'));
$backend->assertFileNotIncluded('js',
array($basePath .'/RequirementsTest_b.js', $basePath .'/RequirementsTest_c.js'));
$backend->assertFileIncluded('js', $basePath .'/RequirementsTest_a.js');
$backend->assertFileIncluded('css', array($basePath .'/RequirementsTest_b.css', $basePath .'/RequirementsTest_c.css'));
$backend->assertFileIncluded('css',
array($basePath .'/RequirementsTest_b.css', $basePath .'/RequirementsTest_c.css'));
Requirements::set_backend($holder);
}
@ -364,8 +395,8 @@ class RequirementsTest_Backend extends Requirements_Backend implements TestOnly
} else {
if(array_key_exists($files, $this->$var)) {
throw new PHPUnit_Framework_AssertionFailedError(
"Failed asserting the $type file '$files' does not have an exact match in the required elements:\n'"
. implode("'\n'", array_keys($this->$var)) . "'"
"Failed asserting the $type file '$files' does not have an exact match in the required elements:"
. "\n'" . implode("'\n'", array_keys($this->$var)) . "'"
);
}
}

View File

@ -232,7 +232,8 @@ class TableListFieldTest extends SapphireTest {
$table = $form->Fields()->dataFieldByName('Tester');
$this->assertEquals(
$table->Link('test'),
sprintf('TableListFieldTest_TestController/TestForm/field/Tester/test?SecurityID=%s', $form->Fields()->dataFieldByName('SecurityID')->Value())
sprintf('TableListFieldTest_TestController/TestForm/field/Tester/test?SecurityID=%s',
$form->Fields()->dataFieldByName('SecurityID')->Value())
);
}

View File

@ -31,7 +31,8 @@ class TextareaFieldTest extends SapphireTest {
$field = new TextareaField("Test", "Test");
$field = $field->performReadonlyTransformation();
$field->setValue($inputText);
$this->assertContains('These are some special &lt;html&gt; chars including &#039;single&#039; &amp; &quot;double&quot; quotations', $field->Field());
$this->assertContains('These are some special &lt;html&gt; chars including &#039;single&#039; &amp;'
. ' &quot;double&quot; quotations', $field->Field());
}
}

View File

@ -15,7 +15,8 @@ class GridFieldAddExistingAutocompleterTest extends FunctionalTest {
$btns = $parser->getBySelector('.ss-gridfield #action_gridfield_relationfind');
$response = $this->post(
'GridFieldAddExistingAutocompleterTest_Controller/Form/field/testfield/search/?gridfield_relationsearch=Team 2',
'GridFieldAddExistingAutocompleterTest_Controller/Form/field/testfield/search'
. '/?gridfield_relationsearch=Team 2',
array(
(string)$btns[0]['name'] => 1
)
@ -26,7 +27,8 @@ class GridFieldAddExistingAutocompleterTest extends FunctionalTest {
$this->assertEquals(array($team2->ID => 'Team 2'), $result);
$response = $this->post(
'GridFieldAddExistingAutocompleterTest_Controller/Form/field/testfield/search/?gridfield_relationsearch=Unknown',
'GridFieldAddExistingAutocompleterTest_Controller/Form/field/testfield/search'
. '/?gridfield_relationsearch=Unknown',
array(
(string)$btns[0]['name'] => 1
)

View File

@ -55,6 +55,7 @@ class GridFieldDataColumnsTest extends SapphireTest {
$columns = $obj->getConfig()->getComponentByType('GridFieldDataColumns');
$this->assertEquals(array(), $columns->getFieldFormatting());
$columns->setFieldFormatting(array("myFieldName" => '<a href=\"custom-admin/$ID\">$ID</a>'));
$this->assertEquals(array("myFieldName" => '<a href=\"custom-admin/$ID\">$ID</a>'), $columns->getFieldFormatting());
$this->assertEquals(array("myFieldName" => '<a href=\"custom-admin/$ID\">$ID</a>'),
$columns->getFieldFormatting());
}
}

View File

@ -31,7 +31,8 @@ class GridFieldDeleteActionTest extends SapphireTest {
// Check that there are content
$this->assertEquals(4, count($content->getBySelector('.ss-gridfield-item')));
// Make sure that there are no delete buttons
$this->assertEquals(0, count($content->getBySelector('.gridfield-button-delete')), 'Delete buttons should not show when not logged in.');
$this->assertEquals(0, count($content->getBySelector('.gridfield-button-delete')),
'Delete buttons should not show when not logged in.');
}
public function testShowDeleteButtonsWithAdminPermission() {
@ -46,17 +47,22 @@ class GridFieldDeleteActionTest extends SapphireTest {
$this->setExpectedException('ValidationException');
$stateID = 'testGridStateActionField';
Session::set($stateID, array('grid'=>'', 'actionName'=>'deleterecord','args'=>array('RecordID'=>$this->idFromFixture('GridFieldAction_Delete_Team', 'team1'))));
$request = new SS_HTTPRequest('POST', 'url', array(), array('action_gridFieldAlterAction?StateID='.$stateID=>true));
Session::set($stateID, array('grid'=>'', 'actionName'=>'deleterecord',
'args'=>array('RecordID'=>$this->idFromFixture('GridFieldAction_Delete_Team', 'team1'))));
$request = new SS_HTTPRequest('POST', 'url', array(),
array('action_gridFieldAlterAction?StateID='.$stateID=>true));
$this->gridField->gridFieldAlterAction(array('StateID'=>$stateID), $this->form, $request);
$this->assertEquals(3, $this->list->count(), 'User should\'t be able to delete records without correct permissions.');
$this->assertEquals(3, $this->list->count(),
'User should\'t be able to delete records without correct permissions.');
}
public function testDeleteActionWithAdminPermission() {
$this->logInWithPermission('ADMIN');
$stateID = 'testGridStateActionField';
Session::set($stateID, array('grid'=>'', 'actionName'=>'deleterecord','args'=>array('RecordID'=>$this->idFromFixture('GridFieldAction_Delete_Team', 'team1'))));
$request = new SS_HTTPRequest('POST', 'url', array(), array('action_gridFieldAlterAction?StateID='.$stateID=>true));
Session::set($stateID, array('grid'=>'', 'actionName'=>'deleterecord',
'args'=>array('RecordID'=>$this->idFromFixture('GridFieldAction_Delete_Team', 'team1'))));
$request = new SS_HTTPRequest('POST', 'url', array(),
array('action_gridFieldAlterAction?StateID='.$stateID=>true));
$this->gridField->gridFieldAlterAction(array('StateID'=>$stateID), $this->form, $request);
$this->assertEquals(2, $this->list->count(), 'User should be able to delete records with ADMIN permission.');
}
@ -70,8 +76,10 @@ class GridFieldDeleteActionTest extends SapphireTest {
$form = new Form(new Controller(), 'mockform', new FieldList(array($this->gridField)), new FieldList());
$stateID = 'testGridStateActionField';
Session::set($stateID, array('grid'=>'', 'actionName'=>'deleterecord','args'=>array('RecordID'=>$this->idFromFixture('GridFieldAction_Delete_Team', 'team1'))));
$request = new SS_HTTPRequest('POST', 'url', array(), array('action_gridFieldAlterAction?StateID='.$stateID=>true));
Session::set($stateID, array('grid'=>'', 'actionName'=>'deleterecord',
'args'=>array('RecordID'=>$this->idFromFixture('GridFieldAction_Delete_Team', 'team1'))));
$request = new SS_HTTPRequest('POST', 'url', array(),
array('action_gridFieldAlterAction?StateID='.$stateID=>true));
$this->gridField->gridFieldAlterAction(array('StateID'=>$stateID), $this->form, $request);
$this->assertEquals(2, $this->list->count(), 'User should be able to delete records with ADMIN permission.');

View File

@ -118,7 +118,8 @@ class GridFieldDetailFormTest extends FunctionalTest {
$this->assertFalse($response->isError());
$parser = new CSSContentParser($response->getBody());
$groupEditLink = $parser->getByXpath('//tr[contains(@class, "ss-gridfield-item") and contains(@data-id, "' . $group->ID . '")]//a');
$groupEditLink = $parser->getByXpath('//tr[contains(@class, "ss-gridfield-item") and contains(@data-id, "'
. $group->ID . '")]//a');
$this->assertEquals(
'GridFieldDetailFormTest_GroupController/Form/field/testfield/item/' . $group->ID . '/edit',
(string)$groupEditLink[0]['href']
@ -128,9 +129,11 @@ class GridFieldDetailFormTest extends FunctionalTest {
$response = $this->get((string)$groupEditLink[0]['href']);
$this->assertFalse($response->isError());
$parser = new CSSContentParser($response->getBody());
$personEditLink = $parser->getByXpath('//fieldset[@id="Form_ItemEditForm_People"]//tr[contains(@class, "ss-gridfield-item") and contains(@data-id, "' . $person->ID . '")]//a');
$personEditLink = $parser->getByXpath('//fieldset[@id="Form_ItemEditForm_People"]' .
'//tr[contains(@class, "ss-gridfield-item") and contains(@data-id, "' . $person->ID . '")]//a');
$this->assertEquals(
sprintf('GridFieldDetailFormTest_GroupController/Form/field/testfield/item/%d/ItemEditForm/field/People/item/%d/edit', $group->ID, $person->ID),
sprintf('GridFieldDetailFormTest_GroupController/Form/field/testfield/item/%d/ItemEditForm/field/People'
. '/item/%d/edit', $group->ID, $person->ID),
(string)$personEditLink[0]['href']
);
@ -138,9 +141,11 @@ class GridFieldDetailFormTest extends FunctionalTest {
$response = $this->get((string)$personEditLink[0]['href']);
$this->assertFalse($response->isError());
$parser = new CSSContentParser($response->getBody());
$categoryEditLink = $parser->getByXpath('//fieldset[@id="Form_ItemEditForm_Categories"]//tr[contains(@class, "ss-gridfield-item") and contains(@data-id, "' . $category->ID . '")]//a');
$categoryEditLink = $parser->getByXpath('//fieldset[@id="Form_ItemEditForm_Categories"]'
. '//tr[contains(@class, "ss-gridfield-item") and contains(@data-id, "' . $category->ID . '")]//a');
$this->assertEquals(
sprintf('GridFieldDetailFormTest_GroupController/Form/field/testfield/item/%d/ItemEditForm/field/People/item/%d/ItemEditForm/field/Categories/item/%d/edit', $group->ID, $person->ID, $category->ID),
sprintf('GridFieldDetailFormTest_GroupController/Form/field/testfield/item/%d/ItemEditForm/field/People'
. '/item/%d/ItemEditForm/field/Categories/item/%d/edit', $group->ID, $person->ID, $category->ID),
(string)$categoryEditLink[0]['href']
);

View File

@ -32,7 +32,8 @@ class GridFieldEditButtonTest extends SapphireTest {
// Check that there are content
$this->assertEquals(3, count($content->getBySelector('.ss-gridfield-item')));
// Make sure that there are no edit links
$this->assertEquals(0, count($content->getBySelector('.edit-link')), 'Edit links should not show when not logged in.');
$this->assertEquals(0, count($content->getBySelector('.edit-link')),
'Edit links should not show when not logged in.');
}
public function testShowEditLinksWithAdminPermission() {

View File

@ -31,7 +31,8 @@ class GridFieldPaginatorTest extends FunctionalTest {
}
public function testThereIsNoPaginatorWhenOnlyOnePage() {
// We set the itemsPerPage to an reasonably big number so as to avoid test broke from small changes on the fixture YML file
// We set the itemsPerPage to an reasonably big number so as to avoid test broke from small changes
// on the fixture YML file
$total = $this->list->count();
$this->gridField->getConfig()->getComponentByType("GridFieldPaginator")->setItemsPerPage($total);
$fieldHolder = $this->gridField->FieldHolder();

View File

@ -280,7 +280,8 @@
'UploadFieldTest_Controller/Form/field/HasManyFiles/item/' . $fileNotOnRelationship->ID . '/delete',
array()
);
$this->assertEquals(403, $response->getStatusCode(), "Denies deleting files if they're not on the current relationship");
$this->assertEquals(403, $response->getStatusCode(),
"Denies deleting files if they're not on the current relationship");
}
public function testDeleteFromManyMany() {
@ -447,9 +448,14 @@
$this->assertFalse($response->isError());
$parser = new CSSContentParser($response->getBody());
$this->assertFalse((bool)$parser->getBySelector('#ReadonlyField .ss-uploadfield-files .ss-uploadfield-item .ss-ui-button'), 'Removes all buttons on items');
$this->assertFalse((bool)$parser->getBySelector('#ReadonlyField .ss-uploadfield-dropzone'), 'Removes dropzone');
$this->assertFalse((bool)$parser->getBySelector('#ReadonlyField .ss-uploadfield-addfile .ss-ui-button'), 'Removes all buttons from "add" area');
$this->assertFalse((bool)$parser->getBySelector(
'#ReadonlyField .ss-uploadfield-files .ss-uploadfield-item .ss-ui-button'),
'Removes all buttons on items');
$this->assertFalse((bool)$parser->getBySelector('#ReadonlyField .ss-uploadfield-dropzone'),
'Removes dropzone');
$this->assertFalse((bool)$parser->getBySelector(
'#ReadonlyField .ss-uploadfield-addfile .ss-ui-button'),
'Removes all buttons from "add" area');
}
public function testDisabled() {
@ -459,9 +465,14 @@
$this->assertFalse($response->isError());
$parser = new CSSContentParser($response->getBody());
$this->assertFalse((bool)$parser->getBySelector('#DisabledField .ss-uploadfield-files .ss-uploadfield-item .ss-ui-button'), 'Removes all buttons on items');
$this->assertFalse((bool)$parser->getBySelector('#DisabledField .ss-uploadfield-dropzone'), 'Removes dropzone');
$this->assertFalse((bool)$parser->getBySelector('#DisabledField .ss-uploadfield-addfile .ss-ui-button'), 'Removes all buttons from "add" area');
$this->assertFalse(
(bool)$parser->getBySelector('#DisabledField .ss-uploadfield-files .ss-uploadfield-item .ss-ui-button'),
'Removes all buttons on items');
$this->assertFalse((bool)$parser->getBySelector('#DisabledField .ss-uploadfield-dropzone'),
'Removes dropzone');
$this->assertFalse(
(bool)$parser->getBySelector('#DisabledField .ss-uploadfield-addfile .ss-ui-button'),
'Removes all buttons from "add" area');
}
@ -540,9 +551,12 @@
$this->assertFalse($response->isError());
$record = DataObject::get_by_id($record->class, $record->ID, false);
$this->assertContains($file1->ID, $record->HasManyFiles()->column('ID'), 'Attaches new relations');
$this->assertContains($file2->ID, $record->HasManyFiles()->column('ID'), 'Attaches new relations');
$this->assertContains($file3AlreadyAttached->ID, $record->HasManyFiles()->column('ID'), 'Does not detach existing relations');
$this->assertContains($file1->ID, $record->HasManyFiles()->column('ID'),
'Attaches new relations');
$this->assertContains($file2->ID, $record->HasManyFiles()->column('ID'),
'Attaches new relations');
$this->assertContains($file3AlreadyAttached->ID, $record->HasManyFiles()->column('ID'),
'Does not detach existing relations');
}
public function testAttachManyMany() {
@ -560,9 +574,12 @@
$this->assertFalse($response->isError());
$record = DataObject::get_by_id($record->class, $record->ID, false);
$this->assertContains($file1->ID, $record->ManyManyFiles()->column('ID'), 'Attaches new relations');
$this->assertContains($file2->ID, $record->ManyManyFiles()->column('ID'), 'Attaches new relations');
$this->assertContains($file5AlreadyAttached->ID, $record->ManyManyFiles()->column('ID'), 'Does not detach existing relations');
$this->assertContains($file1->ID, $record->ManyManyFiles()->column('ID'),
'Attaches new relations');
$this->assertContains($file2->ID, $record->ManyManyFiles()->column('ID'),
'Attaches new relations');
$this->assertContains($file5AlreadyAttached->ID, $record->ManyManyFiles()->column('ID'),
'Does not detach existing relations');
}
public function testManagesRelation() {
@ -648,7 +665,9 @@
$folderIDs = $this->allFixtureIDs('Folder');
foreach($folderIDs as $folderID) {
$folder = DataObject::get_by_id('Folder', $folderID);
if($folder && file_exists(BASE_PATH."/$folder->Filename")) Filesystem::removeFolder(BASE_PATH."/$folder->Filename");
if($folder && file_exists(BASE_PATH."/$folder->Filename")) {
Filesystem::removeFolder(BASE_PATH."/$folder->Filename");
}
}
// Remove left over folders and any files that may exist

View File

@ -264,19 +264,22 @@ class i18nTest extends SapphireTest {
$translated
);
$translated = i18n::_t($entity.'_DOES_NOT_EXIST', $default, array("name"=>"Mark", "greeting"=>"welcome", "goodbye"=>"bye"));
$translated = i18n::_t($entity.'_DOES_NOT_EXIST', $default,
array("name"=>"Mark", "greeting"=>"welcome", "goodbye"=>"bye"));
$this->assertContains(
"Hello Mark welcome. But it is late, bye",
$translated, "Testing fallback to the translation default (but using the injection array)"
);
$translated = i18n::_t($entity, $default, array("name"=>"Paul", "greeting"=>"good you are here", "goodbye"=>"see you"));
$translated = i18n::_t($entity, $default,
array("name"=>"Paul", "greeting"=>"good you are here", "goodbye"=>"see you"));
$this->assertContains(
"TRANS Hello Paul good you are here. But it is late, see you",
$translated, "Testing entity, default string and injection array"
);
$translated = i18n::_t($entity, $default, "New context (this should be ignored)", array("name"=>"Steffen", "greeting"=>"willkommen", "goodbye"=>"wiedersehen"));
$translated = i18n::_t($entity, $default, "New context (this should be ignored)",
array("name"=>"Steffen", "greeting"=>"willkommen", "goodbye"=>"wiedersehen"));
$this->assertContains(
"TRANS Hello Steffen willkommen. But it is late, wiedersehen",
$translated, "Full test of translation, using default, context and injection array"
@ -342,7 +345,8 @@ class i18nTest extends SapphireTest {
//test injected calls
$this->assertContains(
"TRANS Hello ".Director::absoluteBaseURL()." ".i18n::get_locale().". But it is late, global calls\n",
$parsedHtml, "Testing a translation with just entity and injection array, but with global variables injected in"
$parsedHtml,
"Testing a translation with just entity and injection array, but with global variables injected in"
);
i18n::set_locale($oldLocale);
@ -601,7 +605,8 @@ class i18nTest_Object extends Object implements TestOnly, i18nEntityProvider {
}
}
class i18nTest_CustomTranslatorAdapter extends Zend_Translate_Adapter implements TestOnly,i18nTranslateAdapterInterface {
class i18nTest_CustomTranslatorAdapter extends Zend_Translate_Adapter
implements TestOnly,i18nTranslateAdapterInterface {
protected function _loadTranslationData($filename, $locale, array $options = array()) {
return array(
$locale => array(
@ -620,7 +625,8 @@ class i18nTest_CustomTranslatorAdapter extends Zend_Translate_Adapter implements
}
}
class i18nTest_OtherCustomTranslatorAdapter extends Zend_Translate_Adapter implements TestOnly,i18nTranslateAdapterInterface {
class i18nTest_OtherCustomTranslatorAdapter extends Zend_Translate_Adapter
implements TestOnly,i18nTranslateAdapterInterface {
protected function _loadTranslationData($filename, $locale, array $options = array()) {
return array(
$locale => array(

View File

@ -76,9 +76,9 @@ PHP;
$html = <<<SS
<% _t('Test.SINGLEQUOTE','Single Quote'); %>
<%t i18nTestModule.NEWMETHODSIG "New _t method signature test" %>
<%t i18nTestModule.INJECTIONS_0 "Hello {name} {greeting}. But it is late, {goodbye}" name="Mark" greeting="welcome" goodbye="bye" %>
<%t i18nTestModule.INJECTIONS_1 "Hello {name} {greeting}. But it is late, {goodbye}" name="Paul" greeting="good you are here" goodbye="see you" %>
<%t i18nTestModule.INJECTIONS_2 "Hello {name} {greeting}. But it is late, {goodbye}" is "New context (this should be ignored)" name="Steffen" greeting="willkommen" goodbye="wiedersehen" %>
<%t i18nTestModule.INJECTIONS_0 "Hello {name} {greeting}, and {goodbye}" name="Mark" greeting="welcome" goodbye="bye" %>
<%t i18nTestModule.INJECTIONS_1 "Hello {name} {greeting}, and {goodbye}" name="Paul" greeting="welcome" goodbye="cya" %>
<%t i18nTestModule.INJECTIONS_2 "Hello {name} {greeting}" is "context (ignored)" name="Steffen" greeting="Wilkommen" %>
<%t i18nTestModule.INJECTIONS_3 name="Cat" greeting='meow' goodbye="meow" %>
<%t i18nTestModule.INJECTIONS_4 name=\$absoluteBaseURL greeting=\$get_locale goodbye="global calls" %>
SS;
@ -89,9 +89,9 @@ SS;
array(
'Test.SINGLEQUOTE' => array('Single Quote'),
'i18nTestModule.NEWMETHODSIG' => array("New _t method signature test",null,null),
'i18nTestModule.INJECTIONS_0' => array("Hello {name} {greeting}. But it is late, {goodbye}", null, null),
'i18nTestModule.INJECTIONS_1' => array("Hello {name} {greeting}. But it is late, {goodbye}", null, null),
'i18nTestModule.INJECTIONS_2' => array("Hello {name} {greeting}. But it is late, {goodbye}", null, "New context (this should be ignored)"),
'i18nTestModule.INJECTIONS_0' => array("Hello {name} {greeting}, and {goodbye}", null, null),
'i18nTestModule.INJECTIONS_1' => array("Hello {name} {greeting}, and {goodbye}", null, null),
'i18nTestModule.INJECTIONS_2' => array("Hello {name} {greeting}", null, "context (ignored)"),
'i18nTestModule.INJECTIONS_3' => array(null, null, null),
'i18nTestModule.INJECTIONS_4' => array(null, null, null),
)
@ -319,9 +319,13 @@ PHP;
$php = <<<PHP
_t('i18nTestModule.NEWMETHODSIG',"New _t method signature test");
_t('i18nTestModule.INJECTIONS1','_DOES_NOT_EXIST', "Hello {name} {greeting}. But it is late, {goodbye}", array("name"=>"Mark", "greeting"=>"welcome", "goodbye"=>"bye"));
_t('i18nTestModule.INJECTIONS2', "Hello {name} {greeting}. But it is late, {goodbye}", array("name"=>"Paul", "greeting"=>"good you are here", "goodbye"=>"see you"));
_t("i18nTestModule.INJECTIONS3", "Hello {name} {greeting}. But it is late, {goodbye}", "New context (this should be ignored)", array("name"=>"Steffen", "greeting"=>"willkommen", "goodbye"=>"wiedersehen"));
_t('i18nTestModule.INJECTIONS1','_DOES_NOT_EXIST', "Hello {name} {greeting}. But it is late, {goodbye}",
array("name"=>"Mark", "greeting"=>"welcome", "goodbye"=>"bye"));
_t('i18nTestModule.INJECTIONS2', "Hello {name} {greeting}. But it is late, {goodbye}",
array("name"=>"Paul", "greeting"=>"good you are here", "goodbye"=>"see you"));
_t("i18nTestModule.INJECTIONS3", "Hello {name} {greeting}. But it is late, {goodbye}",
"New context (this should be ignored)",
array("name"=>"Steffen", "greeting"=>"willkommen", "goodbye"=>"wiedersehen"));
_t('i18nTestModule.INJECTIONS4', array("name"=>"Cat", "greeting"=>"meow", "goodbye"=>"meow"));
PHP;
@ -329,9 +333,11 @@ PHP;
$expectedArray = (array(
'i18nTestModule.NEWMETHODSIG' => array("New _t method signature test"),
'i18nTestModule.INJECTIONS1' => array("_DOES_NOT_EXIST", "Hello {name} {greeting}. But it is late, {goodbye}"),
'i18nTestModule.INJECTIONS1' => array("_DOES_NOT_EXIST",
"Hello {name} {greeting}. But it is late, {goodbye}"),
'i18nTestModule.INJECTIONS2' => array("Hello {name} {greeting}. But it is late, {goodbye}"),
'i18nTestModule.INJECTIONS3' => array("Hello {name} {greeting}. But it is late, {goodbye}", "New context (this should be ignored)"),
'i18nTestModule.INJECTIONS3' => array("Hello {name} {greeting}. But it is late, {goodbye}",
"New context (this should be ignored)"),
));
ksort($expectedArray);
@ -353,7 +359,8 @@ PHP;
$this->assertEquals(
// single quotes should be properly escaped by the parser already
$c->langArrayCodeForEntitySpec('Test.ESCAPEDSINGLEQUOTES', array("Value with 'Escaped Single Quotes'"), 'en_US'),
$c->langArrayCodeForEntitySpec('Test.ESCAPEDSINGLEQUOTES',
array("Value with 'Escaped Single Quotes'"), 'en_US'),
"\$lang['en_US']['Test']['ESCAPEDSINGLEQUOTES'] = 'Value with \'Escaped Single Quotes\'';" . PHP_EOL
);
@ -370,7 +377,8 @@ PHP;
PHP;
$this->assertEquals(
$c->langArrayCodeForEntitySpec('Test.PRIOANDCOMMENT', array("Value with 'Single Quotes'","Comment with 'Single Quotes'"), 'en_US'),
$c->langArrayCodeForEntitySpec('Test.PRIOANDCOMMENT',
array("Value with 'Single Quotes'","Comment with 'Single Quotes'"), 'en_US'),
$php
);
@ -382,7 +390,8 @@ PHP;
PHP;
$this->assertEquals(
$c->langArrayCodeForEntitySpec('Test.PRIOANDCOMMENT', array('Value with "Double Quotes"','Comment with "Double Quotes"'), 'en_US'),
$c->langArrayCodeForEntitySpec('Test.PRIOANDCOMMENT',
array('Value with "Double Quotes"','Comment with "Double Quotes"'), 'en_US'),
$php
);
}
@ -623,7 +632,8 @@ YAML;
$theme1LangFileContent
);
$this->assertContains(
"\$lang['en']['i18nTestTheme1Include.ss']['SPRINTFINCLUDENONAMESPACE'] = 'Theme1 My include replacement no namespace: %s';",
"\$lang['en']['i18nTestTheme1Include.ss']['SPRINTFINCLUDENONAMESPACE'] ="
. " 'Theme1 My include replacement no namespace: %s';",
$theme1LangFileContent
);
@ -640,7 +650,7 @@ YAML;
);
i18n::set_locale($local); //set the locale to the US locale expected in the asserts
+ i18n::set_default_locale($defaultlocal);
i18n::set_default_locale($defaultlocal);
}
public function testCollectFromEntityProvidersInCustomObject() {

View File

@ -15,7 +15,7 @@ class InjectorTest extends SapphireTest {
public function testCorrectlyInitialised() {
$injector = Injector::inst();
$this->assertTrue($injector->getConfigLocator() instanceof SilverStripeServiceConfigurationLocator,
'If this fails, it is likely because the injector has been referenced BEFORE being initialised in Core.php');
'Failure most likely because the injector has been referenced BEFORE being initialised in Core.php');
}
public function testBasicInjector() {
@ -87,7 +87,8 @@ class InjectorTest extends SapphireTest {
$this->assertEquals(get_class($myObject->sampleService), 'SampleService');
// also tests that ID can be the key in the array
$config = array('SampleService' => array('src' => TEST_SERVICES . '/AnotherService.php')); // , 'id' => 'SampleService'));
$config = array('SampleService' => array('src' => TEST_SERVICES . '/AnotherService.php'));
// , 'id' => 'SampleService'));
// load
$injector->load($config);
@ -617,7 +618,9 @@ class SSObjectCreator extends InjectionCreator {
$argString = $token[1];
switch($argString[0]) {
case '"': $argString = stripcslashes(substr($argString,1,-1)); break;
case "'": $argString = str_replace(array("\\\\", "\\'"),array("\\", "'"), substr($argString,1,-1)); break;
case "'":
$argString = str_replace(array("\\\\", "\\'"),array("\\", "'"), substr($argString,1,-1));
break;
default: throw new Exception("Bad T_CONSTANT_ENCAPSED_STRING arg $argString");
}
$bucket[] = $argString;

View File

@ -361,7 +361,8 @@ class ArrayListTest extends SapphireTest {
array('Name' => 'John', 'ID' => 2)
));
$list->filter(array('Name'=>'Steve', 'ID'=>2));
$this->assertEquals(array((object)array('Name'=>'Steve', 'ID'=>2)), $list->toArray(), 'List should only contain object Steve');
$this->assertEquals(array((object)array('Name'=>'Steve', 'ID'=>2)), $list->toArray(),
'List should only contain object Steve');
}
/**

View File

@ -25,7 +25,8 @@ class CurrencyTest extends SapphireTest {
// Test scientific notation
'5.68434188608E-14' => array('$0.00', '$0'),
'5.68434188608E7' => array('$56,843,418.86', '$56,843,419'),
"Sometimes Es are still bad: 51 dollars, even though they\'re used in scientific notation" => array('$51.00', '$51'),
"Sometimes Es are still bad: 51 dollars, even though they\'re used in scientific notation"
=> array('$51.00', '$51'),
"What about 5.68434188608E7 in the middle of a string" => array('$56,843,418.86', '$56,843,419'),
);

View File

@ -40,7 +40,8 @@ class DataDifferencerTest extends SapphireTest {
$image1->Filename = FRAMEWORK_DIR . substr($image1->Filename, 9);
$image2->Filename = FRAMEWORK_DIR . substr($image2->Filename, 9);
$origUpdateFilesystem = File::$update_filesystem;
File::$update_filesystem = false; // we don't want the filesystem being updated on write, as we're only dealing with mock files
// we don't want the filesystem being updated on write, as we're only dealing with mock files
File::$update_filesystem = false;
$image1->write();
$image2->write();
File::$update_filesystem = $origUpdateFilesystem;
@ -56,7 +57,8 @@ class DataDifferencerTest extends SapphireTest {
$this->assertContains($image1->Filename, $obj1Diff->getField('Image'));
$this->assertContains($image2->Filename, $obj1Diff->getField('Image'));
$this->assertContains('<ins>obj2</ins><del>obj1</del>', str_replace(' ','',$obj1Diff->getField('HasOneRelationID')));
$this->assertContains('<ins>obj2</ins><del>obj1</del>',
str_replace(' ','',$obj1Diff->getField('HasOneRelationID')));
}
}

View File

@ -39,8 +39,10 @@ class DataExtensionTest extends SapphireTest {
$object = DataObject::get_one('DataExtensionTest_RelatedObject', "\"ContactID\" = {$contactID}");
$this->assertNotNull($object, 'Related object not null');
$this->assertInstanceOf('DataExtensionTest_Member', $object->Contact(), 'Related contact is a member dataobject');
$this->assertInstanceOf('DataExtensionTest_Member', $object->getComponent('Contact'), 'getComponent does the same thing as Contact()');
$this->assertInstanceOf('DataExtensionTest_Member', $object->Contact(),
'Related contact is a member dataobject');
$this->assertInstanceOf('DataExtensionTest_Member', $object->getComponent('Contact'),
'getComponent does the same thing as Contact()');
$this->assertInstanceOf('DataExtensionTest_RelatedObject', $contact->RelatedObjects()->First());
$this->assertEquals("Lorem ipsum dolor", $contact->RelatedObjects()->First()->FieldOne);

View File

@ -68,23 +68,42 @@ class DataListTest extends SapphireTest {
public function testSql() {
$db = DB::getConn();
$list = DataObjectTest_TeamComment::get();
$expected = 'SELECT DISTINCT "DataObjectTest_TeamComment"."ClassName", "DataObjectTest_TeamComment"."Created", "DataObjectTest_TeamComment"."LastEdited", "DataObjectTest_TeamComment"."Name", "DataObjectTest_TeamComment"."Comment", "DataObjectTest_TeamComment"."TeamID", "DataObjectTest_TeamComment"."ID", CASE WHEN "DataObjectTest_TeamComment"."ClassName" IS NOT NULL THEN "DataObjectTest_TeamComment"."ClassName" ELSE '.$db->prepStringForDB('DataObjectTest_TeamComment').' END AS "RecordClassName" FROM "DataObjectTest_TeamComment"';
$expected = 'SELECT DISTINCT "DataObjectTest_TeamComment"."ClassName", "DataObjectTest_TeamComment"."Created",'
. ' "DataObjectTest_TeamComment"."LastEdited", "DataObjectTest_TeamComment"."Name",'
. ' "DataObjectTest_TeamComment"."Comment", "DataObjectTest_TeamComment"."TeamID",'
. ' "DataObjectTest_TeamComment"."ID", CASE WHEN "DataObjectTest_TeamComment"."ClassName" IS NOT NULL'
. ' THEN "DataObjectTest_TeamComment"."ClassName" ELSE '.$db->prepStringForDB('DataObjectTest_TeamComment')
. ' END AS "RecordClassName" FROM "DataObjectTest_TeamComment"';
$this->assertEquals($expected, $list->sql());
}
public function testInnerJoin() {
$db = DB::getConn();
$list = DataObjectTest_TeamComment::get();
$list->innerJoin('DataObjectTest_Team', '"DataObjectTest_Team"."ID" = "DataObjectTest_TeamComment"."TeamID"', 'Team');
$expected = 'SELECT DISTINCT "DataObjectTest_TeamComment"."ClassName", "DataObjectTest_TeamComment"."Created", "DataObjectTest_TeamComment"."LastEdited", "DataObjectTest_TeamComment"."Name", "DataObjectTest_TeamComment"."Comment", "DataObjectTest_TeamComment"."TeamID", "DataObjectTest_TeamComment"."ID", CASE WHEN "DataObjectTest_TeamComment"."ClassName" IS NOT NULL THEN "DataObjectTest_TeamComment"."ClassName" ELSE '.$db->prepStringForDB('DataObjectTest_TeamComment').' END AS "RecordClassName" FROM "DataObjectTest_TeamComment" INNER JOIN "DataObjectTest_Team" AS "Team" ON "DataObjectTest_Team"."ID" = "DataObjectTest_TeamComment"."TeamID"';
$list->innerJoin('DataObjectTest_Team', '"DataObjectTest_Team"."ID" = "DataObjectTest_TeamComment"."TeamID"',
'Team');
$expected = 'SELECT DISTINCT "DataObjectTest_TeamComment"."ClassName", "DataObjectTest_TeamComment"."Created",'
. ' "DataObjectTest_TeamComment"."LastEdited", "DataObjectTest_TeamComment"."Name",'
. ' "DataObjectTest_TeamComment"."Comment", "DataObjectTest_TeamComment"."TeamID",'
. ' "DataObjectTest_TeamComment"."ID", CASE WHEN "DataObjectTest_TeamComment"."ClassName" IS NOT NULL'
. ' THEN "DataObjectTest_TeamComment"."ClassName" ELSE '.$db->prepStringForDB('DataObjectTest_TeamComment')
. ' END AS "RecordClassName" FROM "DataObjectTest_TeamComment" INNER JOIN "DataObjectTest_Team" AS "Team"'
. ' ON "DataObjectTest_Team"."ID" = "DataObjectTest_TeamComment"."TeamID"';
$this->assertEquals($expected, $list->sql());
}
public function testLeftJoin() {
$db = DB::getConn();
$list = DataObjectTest_TeamComment::get();
$list->leftJoin('DataObjectTest_Team', '"DataObjectTest_Team"."ID" = "DataObjectTest_TeamComment"."TeamID"', 'Team');
$expected = 'SELECT DISTINCT "DataObjectTest_TeamComment"."ClassName", "DataObjectTest_TeamComment"."Created", "DataObjectTest_TeamComment"."LastEdited", "DataObjectTest_TeamComment"."Name", "DataObjectTest_TeamComment"."Comment", "DataObjectTest_TeamComment"."TeamID", "DataObjectTest_TeamComment"."ID", CASE WHEN "DataObjectTest_TeamComment"."ClassName" IS NOT NULL THEN "DataObjectTest_TeamComment"."ClassName" ELSE '.$db->prepStringForDB('DataObjectTest_TeamComment').' END AS "RecordClassName" FROM "DataObjectTest_TeamComment" LEFT JOIN "DataObjectTest_Team" AS "Team" ON "DataObjectTest_Team"."ID" = "DataObjectTest_TeamComment"."TeamID"';
$list->leftJoin('DataObjectTest_Team', '"DataObjectTest_Team"."ID" = "DataObjectTest_TeamComment"."TeamID"',
'Team');
$expected = 'SELECT DISTINCT "DataObjectTest_TeamComment"."ClassName", "DataObjectTest_TeamComment"."Created",'
. ' "DataObjectTest_TeamComment"."LastEdited", "DataObjectTest_TeamComment"."Name",'
. ' "DataObjectTest_TeamComment"."Comment", "DataObjectTest_TeamComment"."TeamID",'
. ' "DataObjectTest_TeamComment"."ID", CASE WHEN "DataObjectTest_TeamComment"."ClassName" IS NOT NULL'
. ' THEN "DataObjectTest_TeamComment"."ClassName" ELSE '.$db->prepStringForDB('DataObjectTest_TeamComment')
. ' END AS "RecordClassName" FROM "DataObjectTest_TeamComment" LEFT JOIN "DataObjectTest_Team" AS "Team"'
. ' ON "DataObjectTest_Team"."ID" = "DataObjectTest_TeamComment"."TeamID"';
$this->assertEquals($expected, $list->sql());
}
@ -318,7 +337,8 @@ class DataListTest extends SapphireTest {
// public function testSimpleFilterLessThanFilter() {
// $list = DataObjectTest_TeamComment::get();
// $list = $list->filter('TeamID:LessThan', $this->idFromFixture('DataObjectTest_TeamComment', 'comment2'))->sort('Name');
// $list = $list->filter('TeamID:LessThan',
// $this->idFromFixture('DataObjectTest_TeamComment', 'comment2'))->sort('Name');
// $this->assertEquals(2, $list->count());
// $this->assertEquals('Bob', $list->first()->Name, 'First comment should be from Bob');
// $this->assertEquals('Joe', $list->Last()->Name, 'Last comment should be from Joe');
@ -398,7 +418,9 @@ class DataListTest extends SapphireTest {
*/
public function testFilterArrayInArray() {
$list = DataObjectTest_TeamComment::get();
$list = $list->filter(array('Name'=>array('Bob','Phil'), 'TeamID'=>array($this->idFromFixture('DataObjectTest_Team', 'team1'))));
$list = $list->filter(array(
'Name'=>array('Bob','Phil'),
'TeamID'=>array($this->idFromFixture('DataObjectTest_Team', 'team1'))));
$this->assertEquals(1, $list->count(), 'There should be one comments');
$this->assertEquals('Bob', $list->first()->Name, 'Only comment should be from Bob');
}
@ -470,7 +492,9 @@ class DataListTest extends SapphireTest {
$list = $list->filter('Comment', 'Phil is a unique guy, and comments on team2');
$list = $list->exclude('Name', 'Bob');
$this->assertContains('WHERE ("Comment" = \'Phil is a unique guy, and comments on team2\') AND ("Name" != \'Bob\')', $list->sql());
$this->assertContains(
'WHERE ("Comment" = \'Phil is a unique guy, and comments on team2\') AND ("Name" != \'Bob\')',
$list->sql());
}
/**
@ -502,7 +526,9 @@ class DataListTest extends SapphireTest {
*/
public function testMultipleExcludeWithNoExclusion() {
$list = DataObjectTest_TeamComment::get();
$list = $list->exclude(array('Name'=>array('Bob','Joe'), 'Comment' => 'Phil is a unique guy, and comments on team2'));
$list = $list->exclude(array(
'Name'=>array('Bob','Joe'),
'Comment' => 'Phil is a unique guy, and comments on team2'));
$this->assertEquals(3, $list->count());
}
@ -524,7 +550,9 @@ class DataListTest extends SapphireTest {
*/
public function testMultipleExcludeWithTwoArrayOneTeam() {
$list = DataObjectTest_TeamComment::get();
$list = $list->exclude(array('Name' => array('Bob', 'Phil'), 'TeamID' => array($this->idFromFixture('DataObjectTest_Team', 'team1'))));
$list = $list->exclude(array(
'Name' => array('Bob', 'Phil'),
'TeamID' => array($this->idFromFixture('DataObjectTest_Team', 'team1'))));
$list = $list->sort('Name');
$this->assertEquals(2, $list->count());
$this->assertEquals('Joe', $list->first()->Name, 'First comment should be from Joe');
@ -538,8 +566,10 @@ class DataListTest extends SapphireTest {
$list = DataObjectTest_TeamComment::get();
$list = $list->sort(array('Team.Title' => 'DESC'));
$this->assertEquals(3, $list->count());
$this->assertEquals($this->idFromFixture('DataObjectTest_Team', 'team2'), $list->first()->TeamID, 'First comment should be for Team 2');
$this->assertEquals($this->idFromFixture('DataObjectTest_Team', 'team1'), $list->last()->TeamID, 'Last comment should be for Team 1');
$this->assertEquals($this->idFromFixture('DataObjectTest_Team', 'team2'), $list->first()->TeamID,
'First comment should be for Team 2');
$this->assertEquals($this->idFromFixture('DataObjectTest_Team', 'team1'), $list->last()->TeamID,
'Last comment should be for Team 1');
}
public function testReverse() {
@ -554,7 +584,8 @@ class DataListTest extends SapphireTest {
public function testSortByComplexExpression() {
// Test an expression with both spaces and commas
// This test also tests that column() can be called with a complex sort expression, so keep using column() below
$list = DataObjectTest_Team::get()->sort('CASE WHEN "DataObjectTest_Team"."ClassName" = \'DataObjectTest_SubTeam\' THEN 0 ELSE 1 END, "Title" DESC');
$list = DataObjectTest_Team::get()->sort(
'CASE WHEN "DataObjectTest_Team"."ClassName" = \'DataObjectTest_SubTeam\' THEN 0 ELSE 1 END, "Title" DESC');
$this->assertEquals(array(
'Subteam 3',
'Subteam 2',

View File

@ -52,14 +52,21 @@ class DataObjectDuplicationTest extends SapphireTest {
$this->assertEquals($text2, $twoCopy->text);
$this->assertEquals($text3, $threeCopy->text);
$this->assertNotEquals($one->twos()->Count(), $oneCopy->twos()->Count(), "Many-to-one relation not copied (has_many)");
$this->assertEquals($one->threes()->Count(), $oneCopy->threes()->Count(), "Object has the correct number of relations");
$this->assertEquals($three->ones()->Count(), $threeCopy->ones()->Count(), "Object has the correct number of relations");
$this->assertNotEquals($one->twos()->Count(), $oneCopy->twos()->Count(),
"Many-to-one relation not copied (has_many)");
$this->assertEquals($one->threes()->Count(), $oneCopy->threes()->Count(),
"Object has the correct number of relations");
$this->assertEquals($three->ones()->Count(), $threeCopy->ones()->Count(),
"Object has the correct number of relations");
$this->assertEquals($one->ID, $twoCopy->one()->ID, "Match between relation of copy and the original");
$this->assertEquals(0, $oneCopy->twos()->Count(), "Many-to-one relation not copied (has_many)");
$this->assertEquals($three->ID, $oneCopy->threes()->First()->ID, "Match between relation of copy and the original");
$this->assertEquals($one->ID, $threeCopy->ones()->First()->ID, "Match between relation of copy and the original");
$this->assertEquals($one->ID, $twoCopy->one()->ID,
"Match between relation of copy and the original");
$this->assertEquals(0, $oneCopy->twos()->Count(),
"Many-to-one relation not copied (has_many)");
$this->assertEquals($three->ID, $oneCopy->threes()->First()->ID,
"Match between relation of copy and the original");
$this->assertEquals($one->ID, $threeCopy->ones()->First()->ID,
"Match between relation of copy and the original");
}
}

View File

@ -43,8 +43,8 @@ class DataObjectLazyLoadingTest extends SapphireTest {
'"DataObjectTest_Team"."LastEdited", "DataObjectTest_Team"."Title", ' .
'"DataObjectTest_SubTeam"."SubclassDatabaseField", "DataObjectTest_Team"."ID", CASE WHEN ' .
'"DataObjectTest_Team"."ClassName" IS NOT NULL THEN "DataObjectTest_Team"."ClassName" ELSE ' .
$db->prepStringForDB('DataObjectTest_Team').' END AS "RecordClassName" FROM "DataObjectTest_Team" LEFT JOIN ' .
'"DataObjectTest_SubTeam" ON "DataObjectTest_SubTeam"."ID" = "DataObjectTest_Team"."ID" WHERE ' .
$db->prepStringForDB('DataObjectTest_Team').' END AS "RecordClassName" FROM "DataObjectTest_Team" ' .
'LEFT JOIN "DataObjectTest_SubTeam" ON "DataObjectTest_SubTeam"."ID" = "DataObjectTest_Team"."ID" WHERE ' .
'("DataObjectTest_Team"."ClassName" IN ('.$db->prepStringForDB('DataObjectTest_SubTeam').')) ' .
'ORDER BY "DataObjectTest_Team"."Title" ASC';
$this->assertEquals($expected, $playerList->sql());
@ -70,9 +70,11 @@ class DataObjectLazyLoadingTest extends SapphireTest {
$expected = 'SELECT DISTINCT "DataObjectTest_Team"."ClassName", "DataObjectTest_Team"."Created", ' .
'"DataObjectTest_Team"."LastEdited", "DataObjectTest_SubTeam"."SubclassDatabaseField", ' .
'"DataObjectTest_Team"."ID", CASE WHEN "DataObjectTest_Team"."ClassName" IS NOT NULL THEN ' .
'"DataObjectTest_Team"."ClassName" ELSE '.$db->prepStringForDB('DataObjectTest_Team').' END AS "RecordClassName", "DataObjectTest_Team"."Title" FROM ' .
'"DataObjectTest_Team" LEFT JOIN "DataObjectTest_SubTeam" ON "DataObjectTest_SubTeam"."ID" = ' .
'"DataObjectTest_Team"."ID" WHERE ("DataObjectTest_Team"."ClassName" IN ('.$db->prepStringForDB('DataObjectTest_SubTeam').')) ' .
'"DataObjectTest_Team"."ClassName" ELSE '.$db->prepStringForDB('DataObjectTest_Team').' END ' .
'AS "RecordClassName", "DataObjectTest_Team"."Title" ' .
'FROM "DataObjectTest_Team" LEFT JOIN "DataObjectTest_SubTeam" ON "DataObjectTest_SubTeam"."ID" = ' .
'"DataObjectTest_Team"."ID" WHERE ("DataObjectTest_Team"."ClassName" IN (' .
$db->prepStringForDB('DataObjectTest_SubTeam').')) ' .
'ORDER BY "DataObjectTest_Team"."Title" ASC';
$this->assertEquals($expected, $playerList->sql());
}
@ -81,7 +83,9 @@ class DataObjectLazyLoadingTest extends SapphireTest {
// This queries all columns from base table
$playerList = new DataList('DataObjectTest_Team');
// Shouldn't be a left join in here.
$this->assertEquals(0, preg_match('/SELECT DISTINCT "DataObjectTest_Team"."ID" .* LEFT JOIN .* FROM "DataObjectTest_Team"/', $playerList->sql()));
$this->assertEquals(0,
preg_match('/SELECT DISTINCT "DataObjectTest_Team"."ID" .* LEFT JOIN .* FROM "DataObjectTest_Team"/',
$playerList->sql()));
}
public function testNoSpecificColumnNamesSubclassDataObjectQuery() {

View File

@ -9,7 +9,8 @@ class DataObjectSchemaGenerationTest extends SapphireTest {
public function setUpOnce() {
// enable fulltext option on this table
Config::inst()->update('DataObjectSchemaGenerationTest_IndexDO', 'create_table_options', array('MySQLDatabase' => 'ENGINE=MyISAM'));
Config::inst()->update('DataObjectSchemaGenerationTest_IndexDO', 'create_table_options',
array('MySQLDatabase' => 'ENGINE=MyISAM'));
parent::setUpOnce();
}

View File

@ -124,7 +124,8 @@ class DataObjectTest extends SapphireTest {
'DataObjectTest_TeamComment',
"\"DataObjectTest_Team\".\"Title\" = 'Team 1'",
"\"Name\" ASC",
"INNER JOIN \"DataObjectTest_Team\" ON \"DataObjectTest_TeamComment\".\"TeamID\" = \"DataObjectTest_Team\".\"ID\""
"INNER JOIN \"DataObjectTest_Team\""
. " ON \"DataObjectTest_TeamComment\".\"TeamID\" = \"DataObjectTest_Team\".\"ID\""
);
$this->assertEquals(2, $comments->Count());
@ -185,16 +186,22 @@ class DataObjectTest extends SapphireTest {
public function testGetRelationClass() {
$obj = new DataObjectTest_Player();
$this->assertEquals(singleton('DataObjectTest_Player')->getRelationClass('FavouriteTeam'), 'DataObjectTest_Team', 'has_one is properly inspected');
$this->assertEquals(singleton('DataObjectTest_Company')->getRelationClass('CurrentStaff'), 'DataObjectTest_Staff', 'has_many is properly inspected');
$this->assertEquals(singleton('DataObjectTest_Team')->getRelationClass('Players'), 'DataObjectTest_Player', 'many_many is properly inspected');
$this->assertEquals(singleton('DataObjectTest_Player')->getRelationClass('Teams'), 'DataObjectTest_Team', 'belongs_many_many is properly inspected');
$this->assertEquals(singleton('DataObjectTest_CEO')->getRelationClass('Company'), 'DataObjectTest_Company', 'belongs_to is properly inspected');
$this->assertEquals(singleton('DataObjectTest_Player')->getRelationClass('FavouriteTeam'),
'DataObjectTest_Team', 'has_one is properly inspected');
$this->assertEquals(singleton('DataObjectTest_Company')->getRelationClass('CurrentStaff'),
'DataObjectTest_Staff', 'has_many is properly inspected');
$this->assertEquals(singleton('DataObjectTest_Team')->getRelationClass('Players'), 'DataObjectTest_Player',
'many_many is properly inspected');
$this->assertEquals(singleton('DataObjectTest_Player')->getRelationClass('Teams'), 'DataObjectTest_Team',
'belongs_many_many is properly inspected');
$this->assertEquals(singleton('DataObjectTest_CEO')->getRelationClass('Company'), 'DataObjectTest_Company',
'belongs_to is properly inspected');
}
public function testGetHasOneRelations() {
$captain1 = $this->objFromFixture("DataObjectTest_Player", "captain1");
/* There will be a field called (relname)ID that contains the ID of the object linked to via the has_one relation */
/* There will be a field called (relname)ID that contains the ID of the object linked to via the
* has_one relation */
$this->assertEquals($this->idFromFixture('DataObjectTest_Team', 'team1'), $captain1->FavouriteTeamID);
/* There will be a method called $obj->relname() that returns the object itself */
$this->assertEquals($this->idFromFixture('DataObjectTest_Team', 'team1'), $captain1->FavouriteTeam()->ID);
@ -268,11 +275,15 @@ class DataObjectTest extends SapphireTest {
$team1->setField('CaptainID', $player1->ID);
$team1->write();
$this->assertEquals($player1->ID, $team1->Captain()->ID, 'The captain exists for team 1');
$this->assertEquals($player1->ID, $team1->getComponent('Captain')->ID, 'The captain exists through the component getter');
$this->assertEquals($player1->ID, $team1->Captain()->ID,
'The captain exists for team 1');
$this->assertEquals($player1->ID, $team1->getComponent('Captain')->ID,
'The captain exists through the component getter');
$this->assertEquals($team1->Captain()->FirstName, 'Player 1', 'Player 1 is the captain');
$this->assertEquals($team1->getComponent('Captain')->FirstName, 'Player 1', 'Player 1 is the captain');
$this->assertEquals($team1->Captain()->FirstName, 'Player 1',
'Player 1 is the captain');
$this->assertEquals($team1->getComponent('Captain')->FirstName, 'Player 1',
'Player 1 is the captain');
}
/**
@ -403,29 +414,33 @@ class DataObjectTest extends SapphireTest {
$captainID = $this->idFromFixture('DataObjectTest_Player', 'player1');
$team->CaptainID = $captainID;
$team->write();
$this->assertEquals($captainID, DB::query("SELECT \"CaptainID\" FROM \"DataObjectTest_Team\" WHERE \"ID\" = $team->ID")->value());
$this->assertEquals($captainID,
DB::query("SELECT \"CaptainID\" FROM \"DataObjectTest_Team\" WHERE \"ID\" = $team->ID")->value());
/* After giving it a value, you should also be able to set it back to null */
$team->CaptainID = '';
$team->write();
$this->assertEquals(0, DB::query("SELECT \"CaptainID\" FROM \"DataObjectTest_Team\" WHERE \"ID\" = $team->ID")->value());
$this->assertEquals(0,
DB::query("SELECT \"CaptainID\" FROM \"DataObjectTest_Team\" WHERE \"ID\" = $team->ID")->value());
/* You should also be able to save a blank to it when it's first created */
$team = new DataObjectTest_Team();
$team->CaptainID = '';
$team->write();
$this->assertEquals(0, DB::query("SELECT \"CaptainID\" FROM \"DataObjectTest_Team\" WHERE \"ID\" = $team->ID")->value());
$this->assertEquals(0,
DB::query("SELECT \"CaptainID\" FROM \"DataObjectTest_Team\" WHERE \"ID\" = $team->ID")->value());
/* Ditto for existing records without a value */
$existingTeam = $this->objFromFixture('DataObjectTest_Team', 'team1');
$existingTeam->CaptainID = '';
$existingTeam->write();
$this->assertEquals(0, DB::query("SELECT \"CaptainID\" FROM \"DataObjectTest_Team\" WHERE \"ID\" = $existingTeam->ID")->value());
$this->assertEquals(0,
DB::query("SELECT \"CaptainID\" FROM \"DataObjectTest_Team\" WHERE \"ID\" = $existingTeam->ID")->value());
}
public function testCanAccessHasOneObjectsAsMethods() {
/* If you have a has_one relation 'Captain' on $obj, and you set the $obj->CaptainID = (ID), then the object itself should
* be accessible as $obj->Captain() */
/* If you have a has_one relation 'Captain' on $obj, and you set the $obj->CaptainID = (ID), then the
* object itself should be accessible as $obj->Captain() */
$team = $this->objFromFixture('DataObjectTest_Team', 'team1');
$captainID = $this->idFromFixture('DataObjectTest_Player', 'captain1');
@ -443,9 +458,12 @@ class DataObjectTest extends SapphireTest {
$obj->write();
$this->assertNotNull($obj->ID);
$this->assertEquals('value1', DB::query("SELECT \"Data\" FROM \"DataObjectTest_Fixture\" WHERE \"ID\" = $obj->ID")->value());
$this->assertEquals('value2', DB::query("SELECT \"DbObject\" FROM \"DataObjectTest_Fixture\" WHERE \"ID\" = $obj->ID")->value());
$this->assertEquals('value3', DB::query("SELECT \"Duplicate\" FROM \"DataObjectTest_Fixture\" WHERE \"ID\" = $obj->ID")->value());
$this->assertEquals('value1',
DB::query("SELECT \"Data\" FROM \"DataObjectTest_Fixture\" WHERE \"ID\" = $obj->ID")->value());
$this->assertEquals('value2',
DB::query("SELECT \"DbObject\" FROM \"DataObjectTest_Fixture\" WHERE \"ID\" = $obj->ID")->value());
$this->assertEquals('value3',
DB::query("SELECT \"Duplicate\" FROM \"DataObjectTest_Fixture\" WHERE \"ID\" = $obj->ID")->value());
}
/**
@ -459,50 +477,84 @@ class DataObjectTest extends SapphireTest {
$subteamSingleton = singleton('DataObjectTest_SubTeam');
/* hasField() singleton checks */
$this->assertTrue($teamSingleton->hasField('ID'), 'hasField() finds built-in fields in singletons');
$this->assertTrue($teamSingleton->hasField('Title'), 'hasField() finds custom fields in singletons');
$this->assertTrue($teamSingleton->hasField('ID'),
'hasField() finds built-in fields in singletons');
$this->assertTrue($teamSingleton->hasField('Title'),
'hasField() finds custom fields in singletons');
/* hasField() instance checks */
$this->assertFalse($teamInstance->hasField('NonExistingField'), 'hasField() doesnt find non-existing fields in instances');
$this->assertTrue($teamInstance->hasField('ID'), 'hasField() finds built-in fields in instances');
$this->assertTrue($teamInstance->hasField('Created'), 'hasField() finds built-in fields in instances');
$this->assertTrue($teamInstance->hasField('DatabaseField'), 'hasField() finds custom fields in instances');
//$this->assertFalse($teamInstance->hasField('SubclassDatabaseField'), 'hasField() doesnt find subclass fields in parentclass instances');
$this->assertTrue($teamInstance->hasField('DynamicField'), 'hasField() finds dynamic getters in instances');
$this->assertTrue($teamInstance->hasField('HasOneRelationshipID'), 'hasField() finds foreign keys in instances');
$this->assertTrue($teamInstance->hasField('ExtendedDatabaseField'), 'hasField() finds extended fields in instances');
$this->assertTrue($teamInstance->hasField('ExtendedHasOneRelationshipID'), 'hasField() finds extended foreign keys in instances');
//$this->assertTrue($teamInstance->hasField('ExtendedDynamicField'), 'hasField() includes extended dynamic getters in instances');
$this->assertFalse($teamInstance->hasField('NonExistingField'),
'hasField() doesnt find non-existing fields in instances');
$this->assertTrue($teamInstance->hasField('ID'),
'hasField() finds built-in fields in instances');
$this->assertTrue($teamInstance->hasField('Created'),
'hasField() finds built-in fields in instances');
$this->assertTrue($teamInstance->hasField('DatabaseField'),
'hasField() finds custom fields in instances');
//$this->assertFalse($teamInstance->hasField('SubclassDatabaseField'),
//'hasField() doesnt find subclass fields in parentclass instances');
$this->assertTrue($teamInstance->hasField('DynamicField'),
'hasField() finds dynamic getters in instances');
$this->assertTrue($teamInstance->hasField('HasOneRelationshipID'),
'hasField() finds foreign keys in instances');
$this->assertTrue($teamInstance->hasField('ExtendedDatabaseField'),
'hasField() finds extended fields in instances');
$this->assertTrue($teamInstance->hasField('ExtendedHasOneRelationshipID'),
'hasField() finds extended foreign keys in instances');
//$this->assertTrue($teamInstance->hasField('ExtendedDynamicField'),
//'hasField() includes extended dynamic getters in instances');
/* hasField() subclass checks */
$this->assertTrue($subteamInstance->hasField('ID'), 'hasField() finds built-in fields in subclass instances');
$this->assertTrue($subteamInstance->hasField('Created'), 'hasField() finds built-in fields in subclass instances');
$this->assertTrue($subteamInstance->hasField('DatabaseField'), 'hasField() finds custom fields in subclass instances');
$this->assertTrue($subteamInstance->hasField('SubclassDatabaseField'), 'hasField() finds custom fields in subclass instances');
$this->assertTrue($subteamInstance->hasField('DynamicField'), 'hasField() finds dynamic getters in subclass instances');
$this->assertTrue($subteamInstance->hasField('HasOneRelationshipID'), 'hasField() finds foreign keys in subclass instances');
$this->assertTrue($subteamInstance->hasField('ExtendedDatabaseField'), 'hasField() finds extended fields in subclass instances');
$this->assertTrue($subteamInstance->hasField('ExtendedHasOneRelationshipID'), 'hasField() finds extended foreign keys in subclass instances');
$this->assertTrue($subteamInstance->hasField('ID'),
'hasField() finds built-in fields in subclass instances');
$this->assertTrue($subteamInstance->hasField('Created'),
'hasField() finds built-in fields in subclass instances');
$this->assertTrue($subteamInstance->hasField('DatabaseField'),
'hasField() finds custom fields in subclass instances');
$this->assertTrue($subteamInstance->hasField('SubclassDatabaseField'),
'hasField() finds custom fields in subclass instances');
$this->assertTrue($subteamInstance->hasField('DynamicField'),
'hasField() finds dynamic getters in subclass instances');
$this->assertTrue($subteamInstance->hasField('HasOneRelationshipID'),
'hasField() finds foreign keys in subclass instances');
$this->assertTrue($subteamInstance->hasField('ExtendedDatabaseField'),
'hasField() finds extended fields in subclass instances');
$this->assertTrue($subteamInstance->hasField('ExtendedHasOneRelationshipID'),
'hasField() finds extended foreign keys in subclass instances');
/* hasDatabaseField() singleton checks */
//$this->assertTrue($teamSingleton->hasDatabaseField('ID'), 'hasDatabaseField() finds built-in fields in singletons');
$this->assertTrue($teamSingleton->hasDatabaseField('Title'), 'hasDatabaseField() finds custom fields in singletons');
//$this->assertTrue($teamSingleton->hasDatabaseField('ID'),
//'hasDatabaseField() finds built-in fields in singletons');
$this->assertTrue($teamSingleton->hasDatabaseField('Title'),
'hasDatabaseField() finds custom fields in singletons');
/* hasDatabaseField() instance checks */
$this->assertFalse($teamInstance->hasDatabaseField('NonExistingField'), 'hasDatabaseField() doesnt find non-existing fields in instances');
//$this->assertTrue($teamInstance->hasDatabaseField('ID'), 'hasDatabaseField() finds built-in fields in instances');
$this->assertTrue($teamInstance->hasDatabaseField('Created'), 'hasDatabaseField() finds built-in fields in instances');
$this->assertTrue($teamInstance->hasDatabaseField('DatabaseField'), 'hasDatabaseField() finds custom fields in instances');
$this->assertFalse($teamInstance->hasDatabaseField('SubclassDatabaseField'), 'hasDatabaseField() doesnt find subclass fields in parentclass instances');
//$this->assertFalse($teamInstance->hasDatabaseField('DynamicField'), 'hasDatabaseField() doesnt dynamic getters in instances');
$this->assertTrue($teamInstance->hasDatabaseField('HasOneRelationshipID'), 'hasDatabaseField() finds foreign keys in instances');
$this->assertTrue($teamInstance->hasDatabaseField('ExtendedDatabaseField'), 'hasDatabaseField() finds extended fields in instances');
$this->assertTrue($teamInstance->hasDatabaseField('ExtendedHasOneRelationshipID'), 'hasDatabaseField() finds extended foreign keys in instances');
$this->assertFalse($teamInstance->hasDatabaseField('ExtendedDynamicField'), 'hasDatabaseField() doesnt include extended dynamic getters in instances');
$this->assertFalse($teamInstance->hasDatabaseField('NonExistingField'),
'hasDatabaseField() doesnt find non-existing fields in instances');
//$this->assertTrue($teamInstance->hasDatabaseField('ID'),
//'hasDatabaseField() finds built-in fields in instances');
$this->assertTrue($teamInstance->hasDatabaseField('Created'),
'hasDatabaseField() finds built-in fields in instances');
$this->assertTrue($teamInstance->hasDatabaseField('DatabaseField'),
'hasDatabaseField() finds custom fields in instances');
$this->assertFalse($teamInstance->hasDatabaseField('SubclassDatabaseField'),
'hasDatabaseField() doesnt find subclass fields in parentclass instances');
//$this->assertFalse($teamInstance->hasDatabaseField('DynamicField'),
//'hasDatabaseField() doesnt dynamic getters in instances');
$this->assertTrue($teamInstance->hasDatabaseField('HasOneRelationshipID'),
'hasDatabaseField() finds foreign keys in instances');
$this->assertTrue($teamInstance->hasDatabaseField('ExtendedDatabaseField'),
'hasDatabaseField() finds extended fields in instances');
$this->assertTrue($teamInstance->hasDatabaseField('ExtendedHasOneRelationshipID'),
'hasDatabaseField() finds extended foreign keys in instances');
$this->assertFalse($teamInstance->hasDatabaseField('ExtendedDynamicField'),
'hasDatabaseField() doesnt include extended dynamic getters in instances');
/* hasDatabaseField() subclass checks */
$this->assertTrue($subteamInstance->hasField('DatabaseField'), 'hasField() finds custom fields in subclass instances');
$this->assertTrue($subteamInstance->hasField('SubclassDatabaseField'), 'hasField() finds custom fields in subclass instances');
$this->assertTrue($subteamInstance->hasField('DatabaseField'),
'hasField() finds custom fields in subclass instances');
$this->assertTrue($subteamInstance->hasField('SubclassDatabaseField'),
'hasField() finds custom fields in subclass instances');
}
@ -527,7 +579,7 @@ class DataObjectTest extends SapphireTest {
'HasOneRelationshipID',
'ExtendedHasOneRelationshipID'
),
'inheritedDatabaseFields() contains all fields defined on instance, including base fields, extended fields and foreign keys'
'inheritedDatabaseFields() contains all fields defined on instance: base, extended and foreign keys'
);
$this->assertEquals(
@ -544,7 +596,7 @@ class DataObjectTest extends SapphireTest {
'HasOneRelationshipID',
'ExtendedHasOneRelationshipID'
),
'databaseFields() contains only fields defined on instance, including base fields, extended fields and foreign keys'
'databaseFields() contains only fields defined on instance, including base, extended and foreign keys'
);
$this->assertEquals(
@ -563,7 +615,7 @@ class DataObjectTest extends SapphireTest {
'HasOneRelationshipID',
'ExtendedHasOneRelationshipID',
),
'inheritedDatabaseFields() on subclass contains all fields defined on instance, including base fields, extended fields and foreign keys'
'inheritedDatabaseFields() on subclass contains all fields, including base, extended and foreign keys'
);
$this->assertEquals(
@ -577,7 +629,8 @@ class DataObjectTest extends SapphireTest {
}
public function testDataObjectUpdate() {
/* update() calls can use the dot syntax to reference has_one relations and other methods that return objects */
/* update() calls can use the dot syntax to reference has_one relations and other methods that return
* objects */
$team1 = $this->objFromFixture('DataObjectTest_Team', 'team1');
$team1->CaptainID = $this->idFromFixture('DataObjectTest_Player', 'captain1');
@ -591,13 +644,14 @@ class DataObjectTest extends SapphireTest {
/* Test the simple case of updating fields on the object itself */
$this->assertEquals('Something', $team1->DatabaseField);
/* Setting Captain.Email and Captain.FirstName will have updated DataObjectTest_Captain.captain1 in the database. Although update()
* doesn't usually write, it does write related records automatically. */
/* Setting Captain.Email and Captain.FirstName will have updated DataObjectTest_Captain.captain1 in
* the database. Although update() doesn't usually write, it does write related records automatically. */
$captain1 = $this->objFromFixture('DataObjectTest_Player', 'captain1');
$this->assertEquals('Jim', $captain1->FirstName);
$this->assertEquals('jim@example.com', $captain1->Email);
/* Jim's favourite team is team 1; we need to reload the object to the the change that setting Captain.FavouriteTeam.Title made */
/* Jim's favourite team is team 1; we need to reload the object to the the change that setting Captain.
* FavouriteTeam.Title made */
$reloadedTeam1 = $this->objFromFixture('DataObjectTest_Team', 'team1');
$this->assertEquals('New and improved team 1', $reloadedTeam1->Title);
}
@ -621,7 +675,8 @@ class DataObjectTest extends SapphireTest {
/* Creating a new object of a subclass should set the ClassName field correctly */
$obj = new DataObjectTest_SubTeam();
$obj->write();
$this->assertEquals("DataObjectTest_SubTeam", DB::query("SELECT \"ClassName\" FROM \"DataObjectTest_Team\" WHERE \"ID\" = $obj->ID")->value());
$this->assertEquals("DataObjectTest_SubTeam",
DB::query("SELECT \"ClassName\" FROM \"DataObjectTest_Team\" WHERE \"ID\" = $obj->ID")->value());
}
public function testForceInsert() {
@ -635,11 +690,14 @@ class DataObjectTest extends SapphireTest {
$obj->write(false, true);
if(method_exists($conn, 'allowPrimaryKeyEditing')) $conn->allowPrimaryKeyEditing('DataObjectTest_Team', false);
$this->assertEquals("DataObjectTest_SubTeam", DB::query("SELECT \"ClassName\" FROM \"DataObjectTest_Team\" WHERE \"ID\" = $obj->ID")->value());
$this->assertEquals("DataObjectTest_SubTeam",
DB::query("SELECT \"ClassName\" FROM \"DataObjectTest_Team\" WHERE \"ID\" = $obj->ID")->value());
/* Check that it actually saves to the database with the correct ID */
$this->assertEquals("1001", DB::query("SELECT \"ID\" FROM \"DataObjectTest_SubTeam\" WHERE \"SubclassDatabaseField\" = 'asdfasdf'")->value());
$this->assertEquals("1001", DB::query("SELECT \"ID\" FROM \"DataObjectTest_Team\" WHERE \"Title\" = 'asdfasdf'")->value());
$this->assertEquals("1001", DB::query(
"SELECT \"ID\" FROM \"DataObjectTest_SubTeam\" WHERE \"SubclassDatabaseField\" = 'asdfasdf'")->value());
$this->assertEquals("1001",
DB::query("SELECT \"ID\" FROM \"DataObjectTest_Team\" WHERE \"Title\" = 'asdfasdf'")->value());
}
public function TestHasOwnTable() {
@ -689,7 +747,8 @@ class DataObjectTest extends SapphireTest {
// test overwriteWithEmpty flag on empty left values
$left = $this->objFromFixture('DataObjectTest_SubTeam', 'subteam1');
$right = $this->objFromFixture('DataObjectTest_SubTeam', 'subteam2_with_player_relation'); // $SubclassDatabaseField is empty on here
// $SubclassDatabaseField is empty on here
$right = $this->objFromFixture('DataObjectTest_SubTeam', 'subteam2_with_player_relation');
$left->merge($right, 'right', false, true);
$this->assertEquals(
$left->SubclassDatabaseField,
@ -1002,9 +1061,12 @@ class DataObjectTest extends SapphireTest {
$this->assertArrayHasKey('Title', $map, 'Contains fields from parent class');
$this->assertArrayHasKey('SubclassDatabaseField', $map, 'Contains fields from concrete class');
$this->assertEquals($obj->ID, $map['ID'], 'Contains values from base fields');
$this->assertEquals($obj->Title, $map['Title'], 'Contains values from parent class fields');
$this->assertEquals($obj->SubclassDatabaseField, $map['SubclassDatabaseField'], 'Contains values from concrete class fields');
$this->assertEquals($obj->ID, $map['ID'],
'Contains values from base fields');
$this->assertEquals($obj->Title, $map['Title'],
'Contains values from parent class fields');
$this->assertEquals($obj->SubclassDatabaseField, $map['SubclassDatabaseField'],
'Contains values from concrete class fields');
$newObj = new DataObjectTest_SubTeam();
$this->assertArrayHasKey('Title', $map, 'Contains null fields');

View File

@ -7,22 +7,30 @@ class DataQueryTest extends SapphireTest {
public function testJoins() {
$dq = new DataQuery('Member');
$dq->innerJoin("Group_Members", "\"Group_Members\".\"MemberID\" = \"Member\".\"ID\"");
$this->assertContains("INNER JOIN \"Group_Members\" ON \"Group_Members\".\"MemberID\" = \"Member\".\"ID\"", $dq->sql());
$this->assertContains("INNER JOIN \"Group_Members\" ON \"Group_Members\".\"MemberID\" = \"Member\".\"ID\"",
$dq->sql());
$dq = new DataQuery('Member');
$dq->leftJoin("Group_Members", "\"Group_Members\".\"MemberID\" = \"Member\".\"ID\"");
$this->assertContains("LEFT JOIN \"Group_Members\" ON \"Group_Members\".\"MemberID\" = \"Member\".\"ID\"", $dq->sql());
$this->assertContains("LEFT JOIN \"Group_Members\" ON \"Group_Members\".\"MemberID\" = \"Member\".\"ID\"",
$dq->sql());
}
public function testRelationReturn() {
$dq = new DataQuery('DataQueryTest_C');
$this->assertEquals('DataQueryTest_A', $dq->applyRelation('TestA'), 'DataQuery::applyRelation should return the name of the related object.');
$this->assertEquals('DataQueryTest_A', $dq->applyRelation('TestAs'), 'DataQuery::applyRelation should return the name of the related object.');
$this->assertEquals('DataQueryTest_A', $dq->applyRelation('ManyTestAs'), 'DataQuery::applyRelation should return the name of the related object.');
$this->assertEquals('DataQueryTest_A', $dq->applyRelation('TestA'),
'DataQuery::applyRelation should return the name of the related object.');
$this->assertEquals('DataQueryTest_A', $dq->applyRelation('TestAs'),
'DataQuery::applyRelation should return the name of the related object.');
$this->assertEquals('DataQueryTest_A', $dq->applyRelation('ManyTestAs'),
'DataQuery::applyRelation should return the name of the related object.');
$this->assertEquals('DataQueryTest_B', $dq->applyRelation('TestB'), 'DataQuery::applyRelation should return the name of the related object.');
$this->assertEquals('DataQueryTest_B', $dq->applyRelation('TestBs'), 'DataQuery::applyRelation should return the name of the related object.');
$this->assertEquals('DataQueryTest_B', $dq->applyRelation('ManyTestBs'), 'DataQuery::applyRelation should return the name of the related object.');
$this->assertEquals('DataQueryTest_B', $dq->applyRelation('TestB'),
'DataQuery::applyRelation should return the name of the related object.');
$this->assertEquals('DataQueryTest_B', $dq->applyRelation('TestBs'),
'DataQuery::applyRelation should return the name of the related object.');
$this->assertEquals('DataQueryTest_B', $dq->applyRelation('ManyTestBs'),
'DataQuery::applyRelation should return the name of the related object.');
}
}

View File

@ -90,18 +90,22 @@ class DatabaseTest extends SapphireTest {
return $this->markTestSkipped('Tested database doesn\'t support application locks');
}
$this->assertTrue($db->getLock('DatabaseTest'), 'Can aquire lock');
$this->assertTrue($db->getLock('DatabaseTest'),
'Can aquire lock');
// $this->assertFalse($db->getLock('DatabaseTest'), 'Can\'t repeatedly aquire the same lock');
$this->assertTrue($db->getLock('DatabaseTest'), 'The same lock can be aquired multiple times in the same connection');
$this->assertTrue($db->getLock('DatabaseTest'),
'The same lock can be aquired multiple times in the same connection');
$this->assertTrue($db->getLock('DatabaseTestOtherLock'), 'Can aquire different lock');
$this->assertTrue($db->getLock('DatabaseTestOtherLock'),
'Can aquire different lock');
$db->releaseLock('DatabaseTestOtherLock');
// Release potentially stacked locks from previous getLock() invocations
$db->releaseLock('DatabaseTest');
$db->releaseLock('DatabaseTest');
$this->assertTrue($db->getLock('DatabaseTest'), 'Can aquire lock after releasing it');
$this->assertTrue($db->getLock('DatabaseTest'),
'Can aquire lock after releasing it');
$db->releaseLock('DatabaseTest');
}

View File

@ -20,7 +20,8 @@ class DbDatetimeTest extends FunctionalTest {
$time1 = is_numeric($date1) ? $date1 : strtotime($date1);
$time2 = is_numeric($date2) ? $date2 : strtotime($date2);
$this->assertTrue(abs($time1-$time2)<$allowedDifference, $comment . " (times differ by " . abs($time1-$time2) . " seconds)");
$this->assertTrue(abs($time1-$time2)<$allowedDifference,
$comment . " (times differ by " . abs($time1-$time2) . " seconds)");
}
private function getDbNow() {
@ -39,7 +40,8 @@ class DbDatetimeTest extends FunctionalTest {
$threshold = 5; // seconds
if($offset > 5) {
$this->markTestSkipped('The time of the database is out of sync with the webserver by ' . abs($offset) . ' seconds.');
$this->markTestSkipped('The time of the database is out of sync with the webserver by '
. abs($offset) . ' seconds.');
}
if(method_exists($this->adapter, 'supportsTimezoneOverride') && !$this->adapter->supportsTimezoneOverride()) {
@ -68,7 +70,8 @@ class DbDatetimeTest extends FunctionalTest {
$clause = $this->adapter->formattedDatetimeClause('1973-10-14 10:30:00', '%H:%i, %d/%m/%Y');
$result = DB::query('SELECT ' . $clause)->value();
$this->matchesRoughly($result, date('H:i, d/m/Y', strtotime('1973-10-14 10:30:00')), 'nice literal time', $offset);
$this->matchesRoughly($result, date('H:i, d/m/Y', strtotime('1973-10-14 10:30:00')), 'nice literal time',
$offset);
$clause = $this->adapter->formattedDatetimeClause('now', '%d');
$result = DB::query('SELECT ' . $clause)->value();
@ -76,7 +79,8 @@ class DbDatetimeTest extends FunctionalTest {
$clause = $this->adapter->formattedDatetimeClause('"Created"', '%U') . ' AS test FROM "DbDateTimeTest_Team"';
$result = DB::query('SELECT ' . $clause)->value();
$this->matchesRoughly($result, strtotime(DataObject::get_one('DbDateTimeTest_Team')->Created), 'fixture ->Created as timestamp', $offset);
$this->matchesRoughly($result, strtotime(DataObject::get_one('DbDateTimeTest_Team')->Created),
'fixture ->Created as timestamp', $offset);
}
public function testDbDatetimeInterval() {
@ -88,7 +92,8 @@ class DbDatetimeTest extends FunctionalTest {
$clause = $this->adapter->datetimeIntervalClause('now', '+1 Day');
$result = DB::query('SELECT ' . $clause)->value();
$this->matchesRoughly($result, date('Y-m-d H:i:s', strtotime('+1 Day', $this->getDbNow())), 'tomorrow', $offset);
$this->matchesRoughly($result, date('Y-m-d H:i:s', strtotime('+1 Day', $this->getDbNow())), 'tomorrow',
$offset);
$query = new SQLQuery();
$query->setSelect(array());
@ -97,7 +102,9 @@ class DbDatetimeTest extends FunctionalTest {
->setLimit(1);
$result = $query->execute()->value();
$this->matchesRoughly($result, date('Y-m-d H:i:s', strtotime(DataObject::get_one('DbDateTimeTest_Team')->Created) - 900), '15 Minutes before creating fixture', $offset);
$this->matchesRoughly($result,
date('Y-m-d H:i:s', strtotime(DataObject::get_one('DbDateTimeTest_Team')->Created) - 900),
'15 Minutes before creating fixture', $offset);
}
public function testDbDatetimeDifference() {
@ -111,7 +118,8 @@ class DbDatetimeTest extends FunctionalTest {
$result = DB::query('SELECT ' . $clause)->value();
$this->matchesRoughly($result, -15, '15 seconds ago - now', $offset);
$clause = $this->adapter->datetimeDifferenceClause('now', $this->adapter->datetimeIntervalClause('now', '+45 Minutes'));
$clause = $this->adapter->datetimeDifferenceClause('now',
$this->adapter->datetimeIntervalClause('now', '+45 Minutes'));
$result = DB::query('SELECT ' . $clause)->value();
$this->matchesRoughly($result, -45 * 60, 'now - 45 minutes ahead', $offset);
@ -124,7 +132,8 @@ class DbDatetimeTest extends FunctionalTest {
$result = $query->execute()->value();
$lastedited = Dataobject::get_one('DbDateTimeTest_Team')->LastEdited;
$created = Dataobject::get_one('DbDateTimeTest_Team')->Created;
$this->matchesRoughly($result, strtotime($lastedited) - strtotime($created), 'age of HomePage record in seconds since unix epoc', $offset);
$this->matchesRoughly($result, strtotime($lastedited) - strtotime($created),
'age of HomePage record in seconds since unix epoc', $offset);
}
}

View File

@ -19,19 +19,23 @@ class DecimalTest extends SapphireTest {
}
public function testDefaultValue() {
$this->assertEquals($this->testDataObject->MyDecimal1, 0, 'Database default for Decimal type is 0');
$this->assertEquals($this->testDataObject->MyDecimal1, 0,
'Database default for Decimal type is 0');
}
public function testSpecifiedDefaultValue() {
$this->assertEquals($this->testDataObject->MyDecimal2, 2.5, 'Default value for Decimal type is set to 2.5');
$this->assertEquals($this->testDataObject->MyDecimal2, 2.5,
'Default value for Decimal type is set to 2.5');
}
public function testInvalidSpecifiedDefaultValue() {
$this->assertEquals($this->testDataObject->MyDecimal3, 0, 'Invalid default value for Decimal type is casted to 0');
$this->assertEquals($this->testDataObject->MyDecimal3, 0,
'Invalid default value for Decimal type is casted to 0');
}
public function testSpecifiedDefaultValueInDefaultsArray() {
$this->assertEquals($this->testDataObject->MyDecimal4, 4, 'Default value for Decimal type is set to 4');
$this->assertEquals($this->testDataObject->MyDecimal4, 4,
'Default value for Decimal type is set to 4');
}
}

View File

@ -28,9 +28,12 @@ class HTMLTextTest extends SapphireTest {
'<p>Should strip <b>tags, but leave</b> text</p>' => 'Should strip tags, but leave text',
'<p>Unclosed tags <br>should not phase it</p>' => 'Unclosed tags should not phase it',
'<p>Second paragraph</p><p>should not cause errors or appear in output</p>' => 'Second paragraph',
'<img src="hello" /><p>Second paragraph</p><p>should not cause errors or appear in output</p>' => 'Second paragraph',
' <img src="hello" /><p>Second paragraph</p><p>should not cause errors or appear in output</p>' => 'Second paragraph',
'<p><img src="remove me">example <img src="include me">text words hello<img src="hello"></p>' => 'example text words hello',
'<img src="hello" /><p>Second paragraph</p><p>should not cause errors or appear in output</p>'
=> 'Second paragraph',
' <img src="hello" /><p>Second paragraph</p><p>should not cause errors or appear in output</p>'
=> 'Second paragraph',
'<p><img src="remove me">example <img src="include me">text words hello<img src="hello"></p>'
=> 'example text words hello',
);
foreach($cases as $originalValue => $expectedValue) {
@ -81,7 +84,8 @@ class HTMLTextTest extends SapphireTest {
public function testSummaryInvalidHTML() {
$cases = array(
'It\'s got a <p<> tag, but<p junk true>This doesn\'t <a id="boo">make</b class="wa"> < ><any< sense</p>' => 'This doesn\'t make any',
'It\'s got a <p<> tag, but<p junk true>This doesn\'t <a id="boo">make</b class="wa"> < ><any< sense</p>'
=> 'This doesn\'t make any',
'This doesn\'t <a style="much horray= true>even</b> < ><have< a <i>p tag' => 'This doesn\'t even have'
);
@ -97,7 +101,8 @@ class HTMLTextTest extends SapphireTest {
$cases = array(
'<h1>should ignore</h1><p>First sentence. Second sentence.</p>' => 'First sentence.',
'<h1>should ignore</h1><p>First Mr. sentence. Second sentence.</p>' => 'First Mr. sentence.',
"<h1>should ignore</h1><p>Sentence with {$many}words. Second sentence.</p>" => "Sentence with {$many}words.",
"<h1>should ignore</h1><p>Sentence with {$many}words. Second sentence.</p>"
=> "Sentence with {$many}words.",
);
foreach($cases as $orig => $match) {

View File

@ -37,7 +37,8 @@ class ImageTest extends SapphireTest {
public function testGetTagWithTitle() {
$image = $this->objFromFixture('Image', 'imageWithTitle');
$expected = '<img src="' . Director::baseUrl() . 'assets/ImageTest/test_image.png" alt="This is a image Title" />';
$expected = '<img src="' . Director::baseUrl()
. 'assets/ImageTest/test_image.png" alt="This is a image Title" />';
$actual = $image->getTag();
$this->assertEquals($expected, $actual);
@ -53,7 +54,8 @@ class ImageTest extends SapphireTest {
public function testGetTagWithoutTitleContainingDots() {
$image = $this->objFromFixture('Image', 'imageWithoutTitleContainingDots');
$expected = '<img src="' . Director::baseUrl() . 'assets/ImageTest/test.image.with.dots.png" alt="test.image.with.dots" />';
$expected = '<img src="' . Director::baseUrl()
. 'assets/ImageTest/test.image.with.dots.png" alt="test.image.with.dots" />';
$actual = $image->getTag();
$this->assertEquals($expected, $actual);
@ -71,7 +73,9 @@ class ImageTest extends SapphireTest {
$folderIDs = $this->allFixtureIDs('Folder');
foreach($folderIDs as $folderID) {
$folder = DataObject::get_by_id('Folder', $folderID);
if($folder && file_exists(BASE_PATH."/$folder->Filename")) Filesystem::removeFolder(BASE_PATH."/$folder->Filename");
if($folder && file_exists(BASE_PATH."/$folder->Filename")) {
Filesystem::removeFolder(BASE_PATH."/$folder->Filename");
}
}
parent::tearDown();

View File

@ -12,7 +12,8 @@ class ManyManyListTest extends SapphireTest {
);
public function testCreateList() {
$list = ManyManyList::create('DataObjectTest_Team','DataObjectTest_Team_Players', 'DataObjectTest_TeamID', 'DataObjectTest_PlayerID');
$list = ManyManyList::create('DataObjectTest_Team','DataObjectTest_Team_Players', 'DataObjectTest_TeamID',
'DataObjectTest_PlayerID');
$this->assertEquals(2, $list->count());
}
@ -28,9 +29,11 @@ class ManyManyListTest extends SapphireTest {
$player1->Teams()->add($team1);
$player1->flushCache();
$compareTeams = new ManyManyList('DataObjectTest_Team','DataObjectTest_Team_Players', 'DataObjectTest_TeamID', 'DataObjectTest_PlayerID');
$compareTeams = new ManyManyList('DataObjectTest_Team','DataObjectTest_Team_Players', 'DataObjectTest_TeamID',
'DataObjectTest_PlayerID');
$compareTeams = $compareTeams->forForeignID($player1->ID);
$this->assertEquals($player1->Teams()->column('ID'),$compareTeams->column('ID'),"Adding single record as DataObject to many_many");
$this->assertEquals($player1->Teams()->column('ID'),$compareTeams->column('ID'),
"Adding single record as DataObject to many_many");
}
public function testRemovingSingleDataObjectByReference() {
@ -38,9 +41,11 @@ class ManyManyListTest extends SapphireTest {
$team1 = $this->objFromFixture('DataObjectTest_Team', 'team1');
$player1->Teams()->remove($team1);
$player1->flushCache();
$compareTeams = new ManyManyList('DataObjectTest_Team','DataObjectTest_Team_Players', 'DataObjectTest_TeamID', 'DataObjectTest_PlayerID');
$compareTeams = new ManyManyList('DataObjectTest_Team','DataObjectTest_Team_Players', 'DataObjectTest_TeamID',
'DataObjectTest_PlayerID');
$compareTeams = $compareTeams->forForeignID($player1->ID);
$this->assertEquals($player1->Teams()->column('ID'),$compareTeams->column('ID'),"Removing single record as DataObject from many_many");
$this->assertEquals($player1->Teams()->column('ID'),$compareTeams->column('ID'),
"Removing single record as DataObject from many_many");
}
public function testAddingSingleDataObjectByID() {
@ -48,9 +53,11 @@ class ManyManyListTest extends SapphireTest {
$team1 = $this->objFromFixture('DataObjectTest_Team', 'team1');
$player1->Teams()->add($team1->ID);
$player1->flushCache();
$compareTeams = new ManyManyList('DataObjectTest_Team','DataObjectTest_Team_Players', 'DataObjectTest_TeamID', 'DataObjectTest_PlayerID');
$compareTeams = new ManyManyList('DataObjectTest_Team','DataObjectTest_Team_Players', 'DataObjectTest_TeamID',
'DataObjectTest_PlayerID');
$compareTeams = $compareTeams->forForeignID($player1->ID);
$this->assertEquals($player1->Teams()->column('ID'), $compareTeams->column('ID'), "Adding single record as ID to many_many");
$this->assertEquals($player1->Teams()->column('ID'), $compareTeams->column('ID'),
"Adding single record as ID to many_many");
}
public function testRemoveByID() {
@ -58,9 +65,11 @@ class ManyManyListTest extends SapphireTest {
$team1 = $this->objFromFixture('DataObjectTest_Team', 'team1');
$player1->Teams()->removeByID($team1->ID);
$player1->flushCache();
$compareTeams = new ManyManyList('DataObjectTest_Team','DataObjectTest_Team_Players', 'DataObjectTest_TeamID', 'DataObjectTest_PlayerID');
$compareTeams = new ManyManyList('DataObjectTest_Team','DataObjectTest_Team_Players', 'DataObjectTest_TeamID',
'DataObjectTest_PlayerID');
$compareTeams = $compareTeams->forForeignID($player1->ID);
$this->assertEquals($player1->Teams()->column('ID'), $compareTeams->column('ID'), "Removing single record as ID from many_many");
$this->assertEquals($player1->Teams()->column('ID'), $compareTeams->column('ID'),
"Removing single record as ID from many_many");
}
public function testSetByIdList() {
@ -81,7 +90,8 @@ class ManyManyListTest extends SapphireTest {
$team1 = $this->objFromFixture('DataObjectTest_Team', 'team1');
$team2 = $this->objFromFixture('DataObjectTest_Team', 'team2');
$playersTeam1Team2 = DataObjectTest_Team::get()->relation('Players')->forForeignID(array($team1->ID, $team2->ID));
$playersTeam1Team2 = DataObjectTest_Team::get()->relation('Players')
->forForeignID(array($team1->ID, $team2->ID));
$playersTeam1Team2->add($newPlayer);
$this->assertEquals(
array($team1->ID, $team2->ID),
@ -90,25 +100,31 @@ class ManyManyListTest extends SapphireTest {
}
public function testSubtractOnAManyManyList() {
$allList = ManyManyList::create('DataObjectTest_Player', 'DataObjectTest_Team_Players','DataObjectTest_PlayerID', 'DataObjectTest_TeamID');
$this->assertEquals(3, $allList->count(), 'Precondition; we have all 3 players connected to a team in the list');
$allList = ManyManyList::create('DataObjectTest_Player', 'DataObjectTest_Team_Players',
'DataObjectTest_PlayerID', 'DataObjectTest_TeamID');
$this->assertEquals(3, $allList->count(),
'Precondition; we have all 3 players connected to a team in the list');
$teamOneID = $this->idFromFixture('DataObjectTest_Team', 'team1');
$teamTwoID = $this->idFromFixture('DataObjectTest_Team', 'team2');
// Captain 1 belongs to one team; team1
$captain1 = $this->objFromFixture('DataObjectTest_Player', 'captain1');
$this->assertEquals(array($teamOneID),$captain1->Teams()->column("ID"), 'Precondition; player2 belongs to team1');
$this->assertEquals(array($teamOneID),$captain1->Teams()->column("ID"),
'Precondition; player2 belongs to team1');
// Player 2 belongs to both teams: team1, team2
$player2 = $this->objFromFixture('DataObjectTest_Player', 'player2');
$this->assertEquals(array($teamOneID,$teamTwoID), $player2->Teams()->sort('Title')->column('ID'), 'Precondition; player2 belongs to team1 and team2');
$this->assertEquals(array($teamOneID,$teamTwoID), $player2->Teams()->sort('Title')->column('ID'),
'Precondition; player2 belongs to team1 and team2');
// We want to find the teams for player2 where the captain does not belong to
$teamsWithoutTheCaptain = $player2->Teams()->subtract($captain1->Teams());
// Assertions
$this->assertEquals(1,$teamsWithoutTheCaptain->count(), 'The ManyManyList should onlu contain one team');
$this->assertEquals($teamTwoID, $teamsWithoutTheCaptain->first()->ID, 'The ManyManyList contains the wrong team');
$this->assertEquals(1,$teamsWithoutTheCaptain->count(),
'The ManyManyList should onlu contain one team');
$this->assertEquals($teamTwoID, $teamsWithoutTheCaptain->first()->ID,
'The ManyManyList contains the wrong team');
}
}

View File

@ -76,7 +76,8 @@ class MoneyTest extends SapphireTest {
*/
public function testGettingWrittenDataObject() {
$local = i18n::get_locale();
i18n::set_locale('en_US'); //make sure that the $ amount is not prefixed by US$, as it would be in non-US locale
//make sure that the $ amount is not prefixed by US$, as it would be in non-US locale
i18n::set_locale('en_US');
$obj = new MoneyTest_DataObject();

View File

@ -97,7 +97,8 @@ class PaginatedListTest extends SapphireTest {
$list = new PaginatedList($players);
$list->setPageLength(1);
$list->getIterator();
$this->assertEquals(4, $list->getTotalItems(), 'Getting an iterator should not trim the list to the page length.');
$this->assertEquals(4, $list->getTotalItems(),
'Getting an iterator should not trim the list to the page length.');
}
public function testPages() {

View File

@ -61,11 +61,14 @@ class SQLQueryTest extends SapphireTest {
$query = new SQLQuery();
$query->setSelect(array("Name","Meta"))->setFrom("MyTable");
$query->setWhere("Name = 'Name'")->addWhere("Meta = 'Test'")->addWhere("Beta != 'Gamma'");
$this->assertEquals("SELECT Name, Meta FROM MyTable WHERE (Name = 'Name') AND (Meta = 'Test') AND (Beta != 'Gamma')", $query->sql());
$this->assertEquals(
"SELECT Name, Meta FROM MyTable WHERE (Name = 'Name') AND (Meta = 'Test') AND (Beta != 'Gamma')",
$query->sql());
}
public function testSelectWithLimitClause() {
if(!(DB::getConn() instanceof MySQLDatabase || DB::getConn() instanceof SQLite3Database || DB::getConn() instanceof PostgreSQLDatabase)) {
if(!(DB::getConn() instanceof MySQLDatabase || DB::getConn() instanceof SQLite3Database
|| DB::getConn() instanceof PostgreSQLDatabase)) {
$this->markTestIncomplete();
}
@ -115,18 +118,24 @@ class SQLQueryTest extends SapphireTest {
$query = new SQLQuery();
$query->setFrom("MyTable");
$query->setOrderBy('implode("MyName","Color")');
$this->assertEquals('SELECT *, implode("MyName","Color") AS "_SortColumn0" FROM MyTable ORDER BY "_SortColumn0" ASC', $query->sql());
$this->assertEquals(
'SELECT *, implode("MyName","Color") AS "_SortColumn0" FROM MyTable ORDER BY "_SortColumn0" ASC',
$query->sql());
$query = new SQLQuery();
$query->setFrom("MyTable");
$query->setOrderBy('implode("MyName","Color") DESC');
$this->assertEquals('SELECT *, implode("MyName","Color") AS "_SortColumn0" FROM MyTable ORDER BY "_SortColumn0" DESC', $query->sql());
$this->assertEquals(
'SELECT *, implode("MyName","Color") AS "_SortColumn0" FROM MyTable ORDER BY "_SortColumn0" DESC',
$query->sql());
$query = new SQLQuery();
$query->setFrom("MyTable");
$query->setOrderBy('RAND()');
$this->assertEquals('SELECT *, RAND() AS "_SortColumn0" FROM MyTable ORDER BY "_SortColumn0" ASC', $query->sql());
$this->assertEquals(
'SELECT *, RAND() AS "_SortColumn0" FROM MyTable ORDER BY "_SortColumn0" ASC',
$query->sql());
}
/**
@ -181,7 +190,9 @@ class SQLQueryTest extends SapphireTest {
$query->setOrderBy('implode("MyName","Color") DESC');
$query->reverseOrderBy();
$this->assertEquals('SELECT *, implode("MyName","Color") AS "_SortColumn0" FROM MyTable ORDER BY "_SortColumn0" ASC',$query->sql());
$this->assertEquals(
'SELECT *, implode("MyName","Color") AS "_SortColumn0" FROM MyTable ORDER BY "_SortColumn0" ASC',
$query->sql());
}
public function testFiltersOnID() {

View File

@ -77,7 +77,8 @@ class TextTest extends SapphireTest {
'<p>First sentence.</p>' => 'First sentence.',
'<p>First sentence. Second sentence. Third sentence</p>' => 'First sentence. Second sentence.',
'<p>First sentence. <em>Second sentence</em>. Third sentence</p>' => 'First sentence. Second sentence.',
'<p>First sentence. <em class="dummyClass">Second sentence</em>. Third sentence</p>' => 'First sentence. Second sentence.'
'<p>First sentence. <em class="dummyClass">Second sentence</em>. Third sentence</p>'
=> 'First sentence. Second sentence.'
);
foreach($cases as $originalValue => $expectedValue) {
@ -92,7 +93,8 @@ class TextTest extends SapphireTest {
*/
public function testBigSummary() {
$cases = array(
'This text has multiple sentences. Big Summary uses this to split sentences up.' => 'This text has multiple...',
'This text has multiple sentences. Big Summary uses this to split sentences up.'
=> 'This text has multiple...',
'This text does not have multiple sentences' => 'This text does not...',
'Very short' => 'Very short',
'' => ''
@ -125,7 +127,8 @@ class TextTest extends SapphireTest {
$textObj->setValue($testString2);
$this->assertEquals(
'This is <span class="highlight">some</span> <span class="highlight">test</span> text. <span class="highlight">test</span> <span class="highlight">test</span> what if you have...',
'This is <span class="highlight">some</span> <span class="highlight">test</span> text.'
. ' <span class="highlight">test</span> <span class="highlight">test</span> what if you have...',
$textObj->ContextSummary(50, $testKeywords2)
);

View File

@ -22,7 +22,8 @@ class VersionedTest extends SapphireTest {
$obj->write();
$obj->publish('Stage', 'Live');
$versions = DB::query("SELECT COUNT(*) FROM \"VersionedTest_Subclass_versions\" WHERE \"RecordID\" = '$obj->ID'")->value();
$versions = DB::query("SELECT COUNT(*) FROM \"VersionedTest_Subclass_versions\""
. " WHERE \"RecordID\" = '$obj->ID'")->value();
$this->assertGreaterThan(0, $versions, 'At least 1 version exists in the history of the page');
@ -36,17 +37,20 @@ class VersionedTest extends SapphireTest {
// run the script which should clean that up
$obj->augmentDatabase();
$versions = DB::query("SELECT COUNT(*) FROM \"VersionedTest_Subclass_versions\" WHERE \"RecordID\" = '$obj->ID'")->value();
$versions = DB::query("SELECT COUNT(*) FROM \"VersionedTest_Subclass_versions\""
. " WHERE \"RecordID\" = '$obj->ID'")->value();
$this->assertEquals(0, $versions, 'Orphaned versions on child tables are removed');
// test that it doesn't delete records that we need
$obj->write();
$obj->publish('Stage', 'Live');
$count = DB::query("SELECT COUNT(*) FROM \"VersionedTest_Subclass_versions\" WHERE \"RecordID\" = '$obj->ID'")->value();
$count = DB::query("SELECT COUNT(*) FROM \"VersionedTest_Subclass_versions\""
. " WHERE \"RecordID\" = '$obj->ID'")->value();
$obj->augmentDatabase();
$count2 = DB::query("SELECT COUNT(*) FROM \"VersionedTest_Subclass_versions\" WHERE \"RecordID\" = '$obj->ID'")->value();
$count2 = DB::query("SELECT COUNT(*) FROM \"VersionedTest_Subclass_versions\""
. " WHERE \"RecordID\" = '$obj->ID'")->value();
$this->assertEquals($count, $count2);
}
@ -74,19 +78,22 @@ class VersionedTest extends SapphireTest {
$this->objFromFixture('VersionedTest_DataObject', 'page3')->delete();
// Get all items, ignoring deleted
$remainingPages = DataObject::get("VersionedTest_DataObject", "\"ParentID\" = 0", "\"VersionedTest_DataObject\".\"ID\" ASC");
$remainingPages = DataObject::get("VersionedTest_DataObject", "\"ParentID\" = 0",
"\"VersionedTest_DataObject\".\"ID\" ASC");
// Check that page 3 has gone
$this->assertNotNull($remainingPages);
$this->assertEquals(array("Page 1", "Page 2"), $remainingPages->column('Title'));
// Get all including deleted
$allPages = Versioned::get_including_deleted("VersionedTest_DataObject", "\"ParentID\" = 0", "\"VersionedTest_DataObject\".\"ID\" ASC");
$allPages = Versioned::get_including_deleted("VersionedTest_DataObject", "\"ParentID\" = 0",
"\"VersionedTest_DataObject\".\"ID\" ASC");
// Check that page 3 is still there
$this->assertEquals(array("Page 1", "Page 2", "Page 3"), $allPages->column('Title'));
// Check that this still works if we switch to reading the other stage
Versioned::reading_stage("Live");
$allPages = Versioned::get_including_deleted("VersionedTest_DataObject", "\"ParentID\" = 0", "\"VersionedTest_DataObject\".\"ID\" ASC");
$allPages = Versioned::get_including_deleted("VersionedTest_DataObject", "\"ParentID\" = 0",
"\"VersionedTest_DataObject\".\"ID\" ASC");
$this->assertEquals(array("Page 1", "Page 2", "Page 3"), $allPages->column('Title'));
}
@ -129,7 +136,8 @@ class VersionedTest extends SapphireTest {
$changedVersion = $page1->Version;
$page1->doRollbackTo($origVersion);
$page1 = Versioned::get_one_by_stage('VersionedTest_DataObject', 'Stage', sprintf('"VersionedTest_DataObject"."ID" = %d', $page1->ID));
$page1 = Versioned::get_one_by_stage('VersionedTest_DataObject', 'Stage',
sprintf('"VersionedTest_DataObject"."ID" = %d', $page1->ID));
$this->assertTrue($page1->Version > $changedVersion, 'Create a new higher version number');
$this->assertEquals('orig', $page1->Content, 'Copies the content from the old version');
@ -143,22 +151,28 @@ class VersionedTest extends SapphireTest {
$page1->write();
$page1->publish('Stage', 'Live');
$this->assertEquals(1, DB::query('SELECT COUNT(*) FROM "VersionedTest_DataObject" WHERE "ID" = '.$pageID)->value());
$this->assertEquals(1, DB::query('SELECT COUNT(*) FROM "VersionedTest_DataObject_Live" WHERE "ID" = '.$pageID)->value());
$this->assertEquals(1,
DB::query('SELECT COUNT(*) FROM "VersionedTest_DataObject" WHERE "ID" = '.$pageID)->value());
$this->assertEquals(1,
DB::query('SELECT COUNT(*) FROM "VersionedTest_DataObject_Live" WHERE "ID" = '.$pageID)->value());
$page1->deleteFromStage('Live');
// Confirm that deleteFromStage() doesn't manipulate the original record
$this->assertEquals($pageID, $page1->ID);
$this->assertEquals(1, DB::query('SELECT COUNT(*) FROM "VersionedTest_DataObject" WHERE "ID" = '.$pageID)->value());
$this->assertEquals(0, DB::query('SELECT COUNT(*) FROM "VersionedTest_DataObject_Live" WHERE "ID" = '.$pageID)->value());
$this->assertEquals(1,
DB::query('SELECT COUNT(*) FROM "VersionedTest_DataObject" WHERE "ID" = '.$pageID)->value());
$this->assertEquals(0,
DB::query('SELECT COUNT(*) FROM "VersionedTest_DataObject_Live" WHERE "ID" = '.$pageID)->value());
$page1->delete();
$this->assertEquals(0, $page1->ID);
$this->assertEquals(0, DB::query('SELECT COUNT(*) FROM "VersionedTest_DataObject" WHERE "ID" = '.$pageID)->value());
$this->assertEquals(0, DB::query('SELECT COUNT(*) FROM "VersionedTest_DataObject_Live" WHERE "ID" = '.$pageID)->value());
$this->assertEquals(0,
DB::query('SELECT COUNT(*) FROM "VersionedTest_DataObject" WHERE "ID" = '.$pageID)->value());
$this->assertEquals(0,
DB::query('SELECT COUNT(*) FROM "VersionedTest_DataObject_Live" WHERE "ID" = '.$pageID)->value());
}
public function testWritingNewToStage() {
@ -170,10 +184,12 @@ class VersionedTest extends SapphireTest {
$page->URLSegment = "testWritingNewToStage";
$page->write();
$live = Versioned::get_by_stage('VersionedTest_DataObject', 'Live', "\"VersionedTest_DataObject_Live\".\"ID\"='$page->ID'");
$live = Versioned::get_by_stage('VersionedTest_DataObject', 'Live',
"\"VersionedTest_DataObject_Live\".\"ID\"='$page->ID'");
$this->assertEquals(0, $live->count());
$stage = Versioned::get_by_stage('VersionedTest_DataObject', 'Stage', "\"VersionedTest_DataObject\".\"ID\"='$page->ID'");
$stage = Versioned::get_by_stage('VersionedTest_DataObject', 'Stage',
"\"VersionedTest_DataObject\".\"ID\"='$page->ID'");
$this->assertEquals(1, $stage->count());
$this->assertEquals($stage->First()->Title, 'testWritingNewToStage');
@ -182,8 +198,9 @@ class VersionedTest extends SapphireTest {
/**
* This tests for the situation described in the ticket #5596.
* Writing new Page to live first creates a row in VersionedTest_DataObject table (to get the new ID), then "changes
* it's mind" in Versioned and writes VersionedTest_DataObject_Live. It does not remove the VersionedTest_DataObject record though.
* Writing new Page to live first creates a row in VersionedTest_DataObject table (to get the new ID),
* then "changes it's mind" in Versioned and writes VersionedTest_DataObject_Live. It does not remove
* the VersionedTest_DataObject record though.
*/
public function testWritingNewToLive() {
$origStage = Versioned::current_stage();
@ -194,11 +211,13 @@ class VersionedTest extends SapphireTest {
$page->URLSegment = "testWritingNewToLive";
$page->write();
$live = Versioned::get_by_stage('VersionedTest_DataObject', 'Live', "\"VersionedTest_DataObject_Live\".\"ID\"='$page->ID'");
$live = Versioned::get_by_stage('VersionedTest_DataObject', 'Live',
"\"VersionedTest_DataObject_Live\".\"ID\"='$page->ID'");
$this->assertEquals(1, $live->count());
$this->assertEquals($live->First()->Title, 'testWritingNewToLive');
$stage = Versioned::get_by_stage('VersionedTest_DataObject', 'Stage', "\"VersionedTest_DataObject\".\"ID\"='$page->ID'");
$stage = Versioned::get_by_stage('VersionedTest_DataObject', 'Stage',
"\"VersionedTest_DataObject\".\"ID\"='$page->ID'");
$this->assertEquals(0, $stage->count());
Versioned::reading_stage($origStage);
@ -311,7 +330,8 @@ class VersionedTest extends SapphireTest {
// Test 3 - Today
singleton('VersionedTest_Subclass')->flushCache(true);
Versioned::set_reading_mode('Stage.Stage');
$testPageCurrent = DataObject::get('VersionedTest_Subclass')->filter(array('Title' => 'Archived page'))->first();
$testPageCurrent = DataObject::get('VersionedTest_Subclass')->filter(array('Title' => 'Archived page'))
->first();
$this->assertInstanceOf("VersionedTest_Subclass", $testPageCurrent);
$this->assertEquals("2009", $testPageCurrent->ExtraField);
$this->assertEquals("I'm enjoying 2009", $testPageCurrent->Content);
@ -344,7 +364,8 @@ class VersionedTest extends SapphireTest {
}
$this->assertEquals($versions->Count(), 2, 'All versions returned');
$this->assertEquals($content, array('This is the content from 2005', "It's 2007 already!"), 'Version fields returned');
$this->assertEquals($content, array('This is the content from 2005', "It's 2007 already!"),
'Version fields returned');
$this->assertEquals($extraFields, array('2005', '2007'), 'Version fields returned');
// In 2009 we updated it again
@ -366,7 +387,9 @@ class VersionedTest extends SapphireTest {
}
$this->assertEquals($versions->Count(), 3, 'Additional all versions returned');
$this->assertEquals($content, array('This is the content from 2005', "It's 2007 already!", "I'm enjoying 2009"), 'Additional version fields returned');
$this->assertEquals($content,
array('This is the content from 2005', "It's 2007 already!", "I'm enjoying 2009"),
'Additional version fields returned');
$this->assertEquals($extraFields, array('2005', '2007', '2009'), 'Additional version fields returned');
}
}

View File

@ -10,20 +10,27 @@ class OembedTest extends SapphireTest {
// Test with valid URL
$result = Oembed::get_oembed_from_url('http://www.silverstripe.com/watch12345');
$this->assertTrue($result!=false);
$this->assertEquals($result->getOembedURL(), 'http://www.silverstripe.com/oembed/?format=json&url='.urlencode('http://www.silverstripe.com/watch12345'), 'Triggers on matching URL');
$this->assertEquals($result->getOembedURL(),
'http://www.silverstripe.com/oembed/?format=json&url='.urlencode('http://www.silverstripe.com/watch12345'),
'Triggers on matching URL');
// Test without www.
$result = Oembed::get_oembed_from_url('http://silverstripe.com/watch12345');
$this->assertTrue($result!=false);
$this->assertEquals($result->getOembedURL(), 'http://www.silverstripe.com/oembed/?format=json&url='.urlencode('http://silverstripe.com/watch12345'), 'Triggers on matching URL without www');
$this->assertEquals($result->getOembedURL(),
'http://www.silverstripe.com/oembed/?format=json&url='.urlencode('http://silverstripe.com/watch12345'),
'Triggers on matching URL without www');
// Test if options make their way to the URL
$result = Oembed::get_oembed_from_url('http://www.silverstripe.com/watch12345', false, array('foo'=>'bar'));
$this->assertTrue($result!=false);
$this->assertEquals($result->getOembedURL(), 'http://www.silverstripe.com/oembed/?format=json&url='.urlencode('http://www.silverstripe.com/watch12345').'&foo=bar', 'Includes options');
$this->assertEquals($result->getOembedURL(), 'http://www.silverstripe.com/oembed/?format=json&url='
. urlencode('http://www.silverstripe.com/watch12345').'&foo=bar',
'Includes options');
// Test magic.
$result = Oembed::get_oembed_from_url('http://www.silverstripe.com/watch12345', false, array('height'=>'foo', 'width'=>'bar'));
$result = Oembed::get_oembed_from_url('http://www.silverstripe.com/watch12345', false,
array('height'=>'foo', 'width'=>'bar'));
$this->assertTrue($result!=false);
$urlParts = parse_url($result->getOembedURL());
parse_str($urlParts['query'], $query);

View File

@ -18,14 +18,19 @@ class ShortcodeParserTest extends SapphireTest {
* Tests that valid short codes that have not been registered are not replaced.
*/
public function testNotRegisteredShortcode() {
$this->assertEquals('[not_shortcode]', $this->parser->parse('[not_shortcode]'));
$this->assertEquals('[not_shortcode /]', $this->parser->parse('[not_shortcode /]'));
$this->assertEquals('[not_shortcode,foo="bar"]', $this->parser->parse('[not_shortcode,foo="bar"]'));
$this->assertEquals('[not_shortcode]a[/not_shortcode]', $this->parser->parse('[not_shortcode]a[/not_shortcode]'));
$this->assertEquals('[not_shortcode]',
$this->parser->parse('[not_shortcode]'));
$this->assertEquals('[not_shortcode /]',
$this->parser->parse('[not_shortcode /]'));
$this->assertEquals('[not_shortcode,foo="bar"]',
$this->parser->parse('[not_shortcode,foo="bar"]'));
$this->assertEquals('[not_shortcode]a[/not_shortcode]',
$this->parser->parse('[not_shortcode]a[/not_shortcode]'));
}
public function testSimpleTag() {
$tests = array('[test_shortcode]', '[test_shortcode ]', '[test_shortcode,]', '[test_shortcode/]', '[test_shortcode /]');
$tests = array('[test_shortcode]', '[test_shortcode ]', '[test_shortcode,]', '[test_shortcode/]',
'[test_shortcode /]');
foreach($tests as $test) {
$this->parser->parse($test);
@ -78,7 +83,8 @@ class ShortcodeParserTest extends SapphireTest {
public function testShortcodeEscaping() {
$this->assertEquals('[test_shortcode]', $this->parser->parse('[[test_shortcode]]'));
$this->assertEquals('[test_shortcode]content[/test_shortcode]', $this->parser->parse('[[test_shortcode]content[/test_shortcode]]'));
$this->assertEquals('[test_shortcode]content[/test_shortcode]',
$this->parser->parse('[[test_shortcode]content[/test_shortcode]]'));
}
public function testUnquotedArguments() {

View File

@ -17,7 +17,9 @@ class FulltextSearchableTest extends SapphireTest {
public function tearDown() {
// TODO This shouldn't need all arguments included
if($this->orig['File_searchable']) Object::add_extension('File', 'FulltextSearchable(\'"Filename","Title","Content"\')');
if($this->orig['File_searchable']) {
Object::add_extension('File', 'FulltextSearchable(\'"Filename","Title","Content"\')');
}
parent::tearDown();
}

View File

@ -129,14 +129,16 @@ class SearchFilterApplyRelationTest_HasOneParent extends DataObject implements T
);
}
class SearchFilterApplyRelationTest_HasOneChild extends SearchFilterApplyRelationTest_HasOneParent implements TestOnly {
class SearchFilterApplyRelationTest_HasOneChild extends SearchFilterApplyRelationTest_HasOneParent
implements TestOnly {
// This is to create an seperate Table only.
static $db = array(
"ChildField" => "Varchar"
);
}
class SearchFilterApplyRelationTest_HasOneGrantChild extends SearchFilterApplyRelationTest_HasOneChild implements TestOnly {
class SearchFilterApplyRelationTest_HasOneGrantChild extends SearchFilterApplyRelationTest_HasOneChild
implements TestOnly {
// This is to create an seperate Table only.
static $db = array(
"GrantChildField" => "Varchar"
@ -152,7 +154,8 @@ class SearchFilterApplyRelationTest_HasManyParent extends DataObject implements
);
}
class SearchFilterApplyRelationTest_HasManyChild extends SearchFilterApplyRelationTest_HasManyParent implements TestOnly {
class SearchFilterApplyRelationTest_HasManyChild extends SearchFilterApplyRelationTest_HasManyParent
implements TestOnly {
// This is to create an separate Table only.
static $db = array(
"ChildField" => "Varchar"
@ -171,14 +174,16 @@ class SearchFilterApplyRelationTest_ManyManyParent extends DataObject implements
);
}
class SearchFilterApplyRelationTest_ManyManyChild extends SearchFilterApplyRelationTest_ManyManyParent implements TestOnly {
class SearchFilterApplyRelationTest_ManyManyChild extends SearchFilterApplyRelationTest_ManyManyParent
implements TestOnly {
// This is to create an seperate Table only.
static $db = array(
"ChildField" => "Varchar"
);
}
class SearchFilterApplyRelationTest_ManyManyGrantChild extends SearchFilterApplyRelationTest_ManyManyChild implements TestOnly {
class SearchFilterApplyRelationTest_ManyManyGrantChild extends SearchFilterApplyRelationTest_ManyManyChild
implements TestOnly {
// This is to create an seperate Table only.
static $db = array(
"GrantChildField" => "Varchar"

View File

@ -126,10 +126,14 @@ class GroupTest extends FunctionalTest {
$childGroupID = $this->idFromFixture('Group', 'childgroup');
$group->delete();
$this->assertEquals(0, DataObject::get('Group', "\"ID\" = {$groupID}")->Count(), 'Group is removed');
$this->assertEquals(0, DataObject::get('Permission', "\"GroupID\" = {$groupID}")->Count(), 'Permissions removed along with the group');
$this->assertEquals(0, DataObject::get('Group', "\"ParentID\" = {$groupID}")->Count(), 'Child groups are removed');
$this->assertEquals(0, DataObject::get('Group', "\"ParentID\" = {$childGroupID}")->Count(), 'Grandchild groups are removed');
$this->assertEquals(0, DataObject::get('Group', "\"ID\" = {$groupID}")->Count(),
'Group is removed');
$this->assertEquals(0, DataObject::get('Permission', "\"GroupID\" = {$groupID}")->Count(),
'Permissions removed along with the group');
$this->assertEquals(0, DataObject::get('Group', "\"ParentID\" = {$groupID}")->Count(),
'Child groups are removed');
$this->assertEquals(0, DataObject::get('Group', "\"ParentID\" = {$childGroupID}")->Count(),
'Grandchild groups are removed');
}
}

View File

@ -30,7 +30,8 @@ class MemberAuthenticatorTest extends SapphireTest {
}
public function testNoLegacyPasswordHashMigrationOnIncompatibleAlgorithm() {
Config::inst()->update('PasswordEncryptor', 'encryptors', array('crc32'=>array('PasswordEncryptor_PHPHash'=>'crc32')));
Config::inst()->update('PasswordEncryptor', 'encryptors',
array('crc32'=>array('PasswordEncryptor_PHPHash'=>'crc32')));
$field=Member::get_unique_identifier_field();
$member = new Member();

View File

@ -138,7 +138,8 @@ class MemberTest extends FunctionalTest {
$member->Password = "test3";
$member->write();
$passwords = DataObject::get("MemberPassword", "\"MemberID\" = $member->ID", "\"Created\" DESC, \"ID\" DESC")->getIterator();
$passwords = DataObject::get("MemberPassword", "\"MemberID\" = $member->ID", "\"Created\" DESC, \"ID\" DESC")
->getIterator();
$this->assertNotNull($passwords);
$passwords->rewind();
$this->assertTrue($passwords->current()->checkPassword('test3'), "Password test3 not found in MemberRecord");
@ -151,7 +152,8 @@ class MemberTest extends FunctionalTest {
$passwords->next();
$this->assertInstanceOf('DataObject', $passwords->current());
$this->assertTrue($passwords->current()->checkPassword('1nitialPassword'), "Password 1nitialPassword not found in MemberRecord");
$this->assertTrue($passwords->current()->checkPassword('1nitialPassword'),
"Password 1nitialPassword not found in MemberRecord");
}
/**
@ -165,7 +167,8 @@ class MemberTest extends FunctionalTest {
$valid = $member->changePassword('32asDF##$$%%');
$this->assertTrue($valid->valid());
/*
$this->assertEmailSent("sam@silverstripe.com", null, "/changed password/", '/sam@silverstripe\.com.*32asDF##\$\$%%/');
$this->assertEmailSent("sam@silverstripe.com", null, "/changed password/",
'/sam@silverstripe\.com.*32asDF##\$\$%%/');
*/
}
@ -583,8 +586,10 @@ class MemberTest extends FunctionalTest {
$admin = $this->objFromFixture('Member', 'admin');
$otherAdmin = $this->objFromFixture('Member', 'other-admin');
$this->assertTrue(in_array($admin->getTitle(), $members), $admin->getTitle().' should be in the returned list.');
$this->assertTrue(in_array($otherAdmin->getTitle(), $members), $otherAdmin->getTitle().' should be in the returned list.');
$this->assertTrue(in_array($admin->getTitle(), $members),
$admin->getTitle().' should be in the returned list.');
$this->assertTrue(in_array($otherAdmin->getTitle(), $members),
$otherAdmin->getTitle().' should be in the returned list.');
$this->assertEquals(2, count($members), 'There should be 2 members from the admin group');
}

View File

@ -19,7 +19,8 @@ class PasswordEncryptorTest extends SapphireTest {
}
public function testCreateForCode() {
Config::inst()->update('PasswordEncryptor', 'encryptors', array('test'=>array('PasswordEncryptorTest_TestEncryptor'=>null)));
Config::inst()->update('PasswordEncryptor', 'encryptors',
array('test'=>array('PasswordEncryptorTest_TestEncryptor'=>null)));
$e = PasswordEncryptor::create_for_algorithm('test');
$this->assertInstanceOf('PasswordEncryptorTest_TestEncryptor', $e );
}
@ -32,7 +33,8 @@ class PasswordEncryptorTest extends SapphireTest {
}
public function testRegister() {
Config::inst()->update('PasswordEncryptor', 'encryptors', array('test'=>array('PasswordEncryptorTest_TestEncryptor'=>null)));
Config::inst()->update('PasswordEncryptor', 'encryptors',
array('test'=>array('PasswordEncryptorTest_TestEncryptor'=>null)));
$encryptors = PasswordEncryptor::get_encryptors();
$this->assertContains('test', array_keys($encryptors));
$encryptor = $encryptors['test'];
@ -40,19 +42,22 @@ class PasswordEncryptorTest extends SapphireTest {
}
public function testUnregister() {
Config::inst()->update('PasswordEncryptor', 'encryptors', array('test'=>array('PasswordEncryptorTest_TestEncryptor'=>null)));
Config::inst()->update('PasswordEncryptor', 'encryptors',
array('test'=>array('PasswordEncryptorTest_TestEncryptor'=>null)));
Config::inst()->remove('PasswordEncryptor', 'encryptors', 'test');
$this->assertNotContains('test', array_keys(PasswordEncryptor::get_encryptors()));
}
public function testEncryptorPHPHashWithArguments() {
Config::inst()->update('PasswordEncryptor', 'encryptors', array('test_md5'=>array('PasswordEncryptor_PHPHash'=>'md5')));
Config::inst()->update('PasswordEncryptor', 'encryptors',
array('test_md5'=>array('PasswordEncryptor_PHPHash'=>'md5')));
$e = PasswordEncryptor::create_for_algorithm('test_md5');
$this->assertEquals('md5', $e->getAlgorithm());
}
public function testEncryptorPHPHash() {
Config::inst()->update('PasswordEncryptor', 'encryptors', array('test_sha1'=>array('PasswordEncryptor_PHPHash'=>'sha1')));
Config::inst()->update('PasswordEncryptor', 'encryptors',
array('test_sha1'=>array('PasswordEncryptor_PHPHash'=>'sha1')));
$e = PasswordEncryptor::create_for_algorithm('test_sha1');
$password = 'mypassword';
$salt = 'mysalt';
@ -63,7 +68,8 @@ class PasswordEncryptorTest extends SapphireTest {
}
public function testEncryptorBlowfish() {
Config::inst()->update('PasswordEncryptor', 'encryptors', array('test_blowfish'=>array('PasswordEncryptor_Blowfish'=>'')));
Config::inst()->update('PasswordEncryptor', 'encryptors',
array('test_blowfish'=>array('PasswordEncryptor_Blowfish'=>'')));
$e = PasswordEncryptor::create_for_algorithm('test_blowfish');
$password = 'mypassword';
@ -71,7 +77,8 @@ class PasswordEncryptorTest extends SapphireTest {
$salt = $e->salt($password);
$modSalt = substr($salt, 0, 3) . str_shuffle(substr($salt, 3, strlen($salt)));
$this->assertTrue($e->checkAEncryptionLevel() == 'y' || $e->checkAEncryptionLevel() == 'x' || $e->checkAEncryptionLevel() == 'a');
$this->assertTrue($e->checkAEncryptionLevel() == 'y' || $e->checkAEncryptionLevel() == 'x'
|| $e->checkAEncryptionLevel() == 'a');
$this->assertTrue($e->check($e->encrypt($password, $salt), "mypassword", $salt));
$this->assertFalse($e->check($e->encrypt($password, $salt), "anotherpw", $salt));
$this->assertFalse($e->check($e->encrypt($password, $salt), "mypassword", $modSalt));
@ -107,7 +114,8 @@ class PasswordEncryptorTest extends SapphireTest {
}
public function testEncryptorPHPHashCheck() {
Config::inst()->update('PasswordEncryptor', 'encryptors', array('test_sha1'=>array('PasswordEncryptor_PHPHash'=>'sha1')));
Config::inst()->update('PasswordEncryptor', 'encryptors',
array('test_sha1'=>array('PasswordEncryptor_PHPHash'=>'sha1')));
$e = PasswordEncryptor::create_for_algorithm('test_sha1');
$this->assertTrue($e->check(sha1('mypassword'), 'mypassword'));
$this->assertFalse($e->check(sha1('mypassword'), 'mywrongpassword'));
@ -120,7 +128,8 @@ class PasswordEncryptorTest extends SapphireTest {
* php -r "echo(base_convert(sha1('mypassword'), 16, 36));"
*/
public function testEncryptorLegacyPHPHashCheck() {
Config::inst()->update('PasswordEncryptor', 'encryptors', array('test_sha1legacy'=>array('PasswordEncryptor_LegacyPHPHash'=>'sha1')));
Config::inst()->update('PasswordEncryptor', 'encryptors',
array('test_sha1legacy'=>array('PasswordEncryptor_LegacyPHPHash'=>'sha1')));
$e = PasswordEncryptor::create_for_algorithm('test_sha1legacy');
// precomputed hashes for 'mypassword' from different architectures
$amdHash = 'h1fj0a6m4o6k0sosks88oo08ko4gc4s';

View File

@ -46,7 +46,8 @@ class PermissionCheckboxSetFieldTest extends SapphireTest {
$this->assertEquals($group->Permissions()->Count(), 0, 'The tested group has no permissions');
$this->assertEquals($untouchable->Permissions()->Count(), 1, 'The other group has one permission');
$this->assertEquals($untouchable->Permissions("\"Code\"='ADMIN'")->Count(), 1, 'The other group has ADMIN permission');
$this->assertEquals($untouchable->Permissions("\"Code\"='ADMIN'")->Count(), 1,
'The other group has ADMIN permission');
$this->assertEquals(DataObject::get('Permission')->Count(), $baseCount, 'There are no orphaned permissions');
@ -59,14 +60,20 @@ class PermissionCheckboxSetFieldTest extends SapphireTest {
$field->saveInto($group);
$group->flushCache();
$untouchable->flushCache();
$this->assertEquals($group->Permissions()->Count(), 2, 'The tested group has two permissions permission');
$this->assertEquals($group->Permissions("\"Code\"='ADMIN'")->Count(), 1, 'The tested group has ADMIN permission');
$this->assertEquals($group->Permissions("\"Code\"='NON-ADMIN'")->Count(), 1, 'The tested group has CMS_ACCESS_AssetAdmin permission');
$this->assertEquals($group->Permissions()->Count(), 2,
'The tested group has two permissions permission');
$this->assertEquals($group->Permissions("\"Code\"='ADMIN'")->Count(), 1,
'The tested group has ADMIN permission');
$this->assertEquals($group->Permissions("\"Code\"='NON-ADMIN'")->Count(), 1,
'The tested group has CMS_ACCESS_AssetAdmin permission');
$this->assertEquals($untouchable->Permissions()->Count(), 1, 'The other group has one permission');
$this->assertEquals($untouchable->Permissions("\"Code\"='ADMIN'")->Count(), 1, 'The other group has ADMIN permission');
$this->assertEquals($untouchable->Permissions()->Count(), 1,
'The other group has one permission');
$this->assertEquals($untouchable->Permissions("\"Code\"='ADMIN'")->Count(), 1,
'The other group has ADMIN permission');
$this->assertEquals(DataObject::get('Permission')->Count(), $baseCount+2, 'There are no orphaned permissions');
$this->assertEquals(DataObject::get('Permission')->Count(), $baseCount+2,
'There are no orphaned permissions');
// remove permission
$field->setValue(array(
@ -76,12 +83,17 @@ class PermissionCheckboxSetFieldTest extends SapphireTest {
$field->saveInto($group);
$group->flushCache();
$untouchable->flushCache();
$this->assertEquals($group->Permissions()->Count(), 1, 'The tested group has 1 permission');
$this->assertEquals($group->Permissions("\"Code\"='ADMIN'")->Count(), 1, 'The tested group has ADMIN permission');
$this->assertEquals($group->Permissions()->Count(), 1,
'The tested group has 1 permission');
$this->assertEquals($group->Permissions("\"Code\"='ADMIN'")->Count(), 1,
'The tested group has ADMIN permission');
$this->assertEquals($untouchable->Permissions()->Count(), 1, 'The other group has one permission');
$this->assertEquals($untouchable->Permissions("\"Code\"='ADMIN'")->Count(), 1, 'The other group has ADMIN permission');
$this->assertEquals($untouchable->Permissions()->Count(), 1,
'The other group has one permission');
$this->assertEquals($untouchable->Permissions("\"Code\"='ADMIN'")->Count(), 1,
'The other group has ADMIN permission');
$this->assertEquals(DataObject::get('Permission')->Count(), $baseCount+1, 'There are no orphaned permissions');
$this->assertEquals(DataObject::get('Permission')->Count(), $baseCount+1,
'There are no orphaned permissions');
}
}

View File

@ -11,7 +11,9 @@ class PermissionRoleTest extends FunctionalTest {
$role->delete();
$this->assertEquals(0, DataObject::get('PermissionRole', "\"ID\"={$role->ID}")->count(), 'Role is removed');
$this->assertEquals(0, DataObject::get('PermissionRoleCode',"\"RoleID\"={$role->ID}")->count(), 'Permissions removed along with the role');
$this->assertEquals(0, DataObject::get('PermissionRole', "\"ID\"={$role->ID}")->count(),
'Role is removed');
$this->assertEquals(0, DataObject::get('PermissionRoleCode',"\"RoleID\"={$role->ID}")->count(),
'Permissions removed along with the role');
}
}

View File

@ -132,9 +132,11 @@ class SecurityTest extends FunctionalTest {
$this->session()->inst_set('loggedInAs', null);
// Test internal absolute redirect
$response = $this->doTestLoginForm('noexpiry@silverstripe.com', '1nitialPassword', Director::absoluteBaseURL() . 'testpage');
$response = $this->doTestLoginForm('noexpiry@silverstripe.com', '1nitialPassword',
Director::absoluteBaseURL() . 'testpage');
// for some reason the redirect happens to a relative URL
$this->assertRegExp('/^' . preg_quote(Director::absoluteBaseURL(), '/') . 'testpage/', $response->getHeader('Location'),
$this->assertRegExp('/^' . preg_quote(Director::absoluteBaseURL(), '/') . 'testpage/',
$response->getHeader('Location'),
"Internal absolute BackURLs work when passed through to login form"
);
// Log the user out
@ -142,15 +144,17 @@ class SecurityTest extends FunctionalTest {
// Test external redirect
$response = $this->doTestLoginForm('noexpiry@silverstripe.com', '1nitialPassword', 'http://myspoofedhost.com');
$this->assertNotRegExp('/^' . preg_quote('http://myspoofedhost.com', '/') . '/', (string)$response->getHeader('Location'),
$this->assertNotRegExp('/^' . preg_quote('http://myspoofedhost.com', '/') . '/',
(string)$response->getHeader('Location'),
"Redirection to external links in login form BackURL gets prevented as a measure against spoofing attacks"
);
// Test external redirection on ChangePasswordForm
$this->get('Security/changepassword?BackURL=http://myspoofedhost.com');
$changedResponse = $this->doTestChangepasswordForm('1nitialPassword', 'changedPassword');
$this->assertNotRegExp('/^' . preg_quote('http://myspoofedhost.com', '/') . '/', (string)$changedResponse->getHeader('Location'),
"Redirection to external links in change password form BackURL gets prevented as a measure against spoofing attacks"
$this->assertNotRegExp('/^' . preg_quote('http://myspoofedhost.com', '/') . '/',
(string)$changedResponse->getHeader('Location'),
"Redirection to external links in change password form BackURL gets prevented to stop spoofing attacks"
);
// Log the user out
@ -177,7 +181,8 @@ class SecurityTest extends FunctionalTest {
$expiredResponse = $this->doTestLoginForm('expired@silverstripe.com' , '1nitialPassword');
$this->assertEquals(302, $expiredResponse->getStatusCode());
$this->assertEquals(Director::baseURL() . 'Security/changepassword', $expiredResponse->getHeader('Location'));
$this->assertEquals($this->idFromFixture('Member', 'expiredpassword'), $this->session()->inst_get('loggedInAs'));
$this->assertEquals($this->idFromFixture('Member', 'expiredpassword'),
$this->session()->inst_get('loggedInAs'));
// Make sure it redirects correctly after the password has been changed
$this->mainSession->followRedirection();
@ -313,7 +318,8 @@ class SecurityTest extends FunctionalTest {
$this->assertNull($member1->LockedOutUntil);
$this->assertNull($member2->LockedOutUntil);
// BUT, DOING AN ADDITIONAL LOG-IN WITH EITHER OF THEM WILL LOCK OUT, SINCE THAT IS THE 3RD FAILURE IN THIS SESSION
// BUT, DOING AN ADDITIONAL LOG-IN WITH EITHER OF THEM WILL LOCK OUT, SINCE THAT IS THE 3RD FAILURE IN
// THIS SESSION
$this->doTestLoginForm('sam@silverstripe.com' , 'incorrectpassword');
$member1 = DataObject::get_by_id("Member", $this->idFromFixture('Member', 'test'));

View File

@ -10,7 +10,8 @@ class CSSContentParserTest extends SapphireTest {
$this->assertEquals("//div[@id='UserProfile']//label", $parser->selector2xpath("div#UserProfile label"));
$this->assertEquals("//div", $parser->selector2xpath("div"));
$this->assertEquals("//div[contains(@class,'test')]", $parser->selector2xpath("div.test"));
$this->assertEquals("//*[@id='UserProfile']//div[contains(@class,'test')]//*[contains(@class,'other')]//div[@id='Item']",
$this->assertEquals(
"//*[@id='UserProfile']//div[contains(@class,'test')]//*[contains(@class,'other')]//div[@id='Item']",
$parser->selector2xpath("#UserProfile div.test .other div#Item"));
}

View File

@ -40,9 +40,11 @@ class YamlFixtureTest extends SapphireTest {
}
public function testSQLInsert() {
$object1 = DataObject::get_by_id("YamlFixtureTest_DataObject", $this->idFromFixture("YamlFixtureTest_DataObject", "testobject1"));
$object1 = DataObject::get_by_id("YamlFixtureTest_DataObject",
$this->idFromFixture("YamlFixtureTest_DataObject", "testobject1"));
$this->assertTrue($object1->ManyMany()->Count() == 2, "Should be 2 items in this manymany relationship");
$object2 = DataObject::get_by_id("YamlFixtureTest_DataObject", $this->idFromFixture("YamlFixtureTest_DataObject", "testobject2"));
$object2 = DataObject::get_by_id("YamlFixtureTest_DataObject",
$this->idFromFixture("YamlFixtureTest_DataObject", "testobject2"));
$this->assertTrue($object2->ManyMany()->Count() == 2, "Should be 2 items in this manymany relationship");
}
}

View File

@ -69,7 +69,8 @@ class SSViewerCacheBlockTest extends SapphireTest {
// Make sure a complicated cacheblock parses
$this->_reset();
$this->assertEquals($this->_runtemplate('<% cached \'block\', Foo, Test.Test(4).Test(jumping).Foo %>Yay<% end_cached %>'), 'Yay');
$this->assertEquals($this->_runtemplate(
'<% cached \'block\', Foo, Test.Test(4).Test(jumping).Foo %>Yay<% end_cached %>'), 'Yay');
// ** Conditional Checks **
@ -83,7 +84,8 @@ class SSViewerCacheBlockTest extends SapphireTest {
// Make sure a cacheblock with a complex conditional and arguments parses
$this->_reset();
$this->assertEquals($this->_runtemplate('<% cached Foo, Test.Test(4).Test(jumping).Foo if Test.Test(yank).Foo %>Yay<% end_cached %>'), 'Yay');
$this->assertEquals($this->_runtemplate(
'<% cached Foo, Test.Test(4).Test(jumping).Foo if Test.Test(yank).Foo %>Yay<% end_cached %>'), 'Yay');
}
/**
@ -122,13 +124,17 @@ class SSViewerCacheBlockTest extends SapphireTest {
// Then once cached, once not (and the opposite)
$this->_reset(true);
$this->assertEquals($this->_runtemplate('<% cached if Cache %>$Foo<% end_cached %>', array('Foo' => 1, 'Cache' => true )), '1');
$this->assertEquals($this->_runtemplate('<% cached if Cache %>$Foo<% end_cached %>', array('Foo' => 2, 'Cache' => false)), '2');
$this->assertEquals($this->_runtemplate(
'<% cached if Cache %>$Foo<% end_cached %>', array('Foo' => 1, 'Cache' => true )), '1');
$this->assertEquals($this->_runtemplate(
'<% cached if Cache %>$Foo<% end_cached %>', array('Foo' => 2, 'Cache' => false)), '2');
$this->_reset(true);
$this->assertEquals($this->_runtemplate('<% cached if Cache %>$Foo<% end_cached %>', array('Foo' => 1, 'Cache' => false)), '1');
$this->assertEquals($this->_runtemplate('<% cached if Cache %>$Foo<% end_cached %>', array('Foo' => 2, 'Cache' => true )), '2');
$this->assertEquals($this->_runtemplate(
'<% cached if Cache %>$Foo<% end_cached %>', array('Foo' => 1, 'Cache' => false)), '1');
$this->assertEquals($this->_runtemplate(
'<% cached if Cache %>$Foo<% end_cached %>', array('Foo' => 2, 'Cache' => true )), '2');
}
/**
@ -138,14 +144,18 @@ class SSViewerCacheBlockTest extends SapphireTest {
// First, run twice with caching
$this->_reset(true);
$this->assertEquals($this->_runtemplate('<% cached unless False %>$Foo<% end_cached %>', array('Foo' => 1)), '1');
$this->assertEquals($this->_runtemplate('<% cached unless False %>$Foo<% end_cached %>', array('Foo' => 2)), '1');
$this->assertEquals($this->_runtemplate(
'<% cached unless False %>$Foo<% end_cached %>', array('Foo' => 1)), '1');
$this->assertEquals($this->_runtemplate(
'<% cached unless False %>$Foo<% end_cached %>', array('Foo' => 2)), '1');
// Then twice without caching
$this->_reset(true);
$this->assertEquals($this->_runtemplate('<% cached unless True %>$Foo<% end_cached %>', array('Foo' => 1)), '1');
$this->assertEquals($this->_runtemplate('<% cached unless True %>$Foo<% end_cached %>', array('Foo' => 2)), '2');
$this->assertEquals($this->_runtemplate(
'<% cached unless True %>$Foo<% end_cached %>', array('Foo' => 1)), '1');
$this->assertEquals($this->_runtemplate(
'<% cached unless True %>$Foo<% end_cached %>', array('Foo' => 2)), '2');
}
/**
@ -155,14 +165,18 @@ class SSViewerCacheBlockTest extends SapphireTest {
// First, run twice with caching, to prove we get the same result back normally
$this->_reset(true);
$this->assertEquals($this->_runtemplate('<% cached %> A $Foo B <% end_cached %>', array('Foo' => 1)), ' A 1 B ');
$this->assertEquals($this->_runtemplate('<% cached %> A $Foo B <% end_cached %>', array('Foo' => 2)), ' A 1 B ');
$this->assertEquals($this->_runtemplate(
'<% cached %> A $Foo B <% end_cached %>', array('Foo' => 1)), ' A 1 B ');
$this->assertEquals($this->_runtemplate(
'<% cached %> A $Foo B <% end_cached %>', array('Foo' => 2)), ' A 1 B ');
// Then add uncached to the nested block
$this->_reset(true);
$this->assertEquals($this->_runtemplate('<% cached %> A <% uncached %>$Foo<% end_uncached %> B <% end_cached %>', array('Foo' => 1)), ' A 1 B ');
$this->assertEquals($this->_runtemplate('<% cached %> A <% uncached %>$Foo<% end_uncached %> B <% end_cached %>', array('Foo' => 2)), ' A 2 B ');
$this->assertEquals($this->_runtemplate(
'<% cached %> A <% uncached %>$Foo<% end_uncached %> B <% end_cached %>', array('Foo' => 1)), ' A 1 B ');
$this->assertEquals($this->_runtemplate(
'<% cached %> A <% uncached %>$Foo<% end_uncached %> B <% end_cached %>', array('Foo' => 2)), ' A 2 B ');
}
/**
@ -174,16 +188,20 @@ class SSViewerCacheBlockTest extends SapphireTest {
$template = '<% cached Foo %> $Fooa <% cached Bar %>$Bara<% end_cached %> $Foob <% end_cached %>';
// Do it the first time to load the cache
$this->assertEquals($this->_runtemplate($template, array('Foo' => 1, 'Fooa' => 1, 'Foob' => 3, 'Bar' => 1, 'Bara' => 2)), ' 1 2 3 ');
$this->assertEquals($this->_runtemplate($template,
array('Foo' => 1, 'Fooa' => 1, 'Foob' => 3, 'Bar' => 1, 'Bara' => 2)), ' 1 2 3 ');
// Do it again, the input values are ignored as the cache is hit for both elements
$this->assertEquals($this->_runtemplate($template, array('Foo' => 1, 'Fooa' => 9, 'Foob' => 9, 'Bar' => 1, 'Bara' => 9)), ' 1 2 3 ');
$this->assertEquals($this->_runtemplate($template,
array('Foo' => 1, 'Fooa' => 9, 'Foob' => 9, 'Bar' => 1, 'Bara' => 9)), ' 1 2 3 ');
// Do it again with a new key for Bar, Bara is picked up, Fooa and Foob are not
$this->assertEquals($this->_runtemplate($template, array('Foo' => 1, 'Fooa' => 9, 'Foob' => 9, 'Bar' => 2, 'Bara' => 9)), ' 1 9 3 ');
$this->assertEquals($this->_runtemplate($template,
array('Foo' => 1, 'Fooa' => 9, 'Foob' => 9, 'Bar' => 2, 'Bara' => 9)), ' 1 9 3 ');
// Do it again with a new key for Foo, Fooa and Foob are picked up, Bara are not
$this->assertEquals($this->_runtemplate($template, array('Foo' => 2, 'Fooa' => 9, 'Foob' => 9, 'Bar' => 2, 'Bara' => 1)), ' 9 9 9 ');
$this->assertEquals($this->_runtemplate($template,
array('Foo' => 2, 'Fooa' => 9, 'Foob' => 9, 'Bar' => 2, 'Bara' => 1)), ' 9 9 9 ');
}
public function testNoErrorMessageForControlWithinCached() {
@ -196,12 +214,14 @@ class SSViewerCacheBlockTest extends SapphireTest {
*/
public function testErrorMessageForCachedWithinControlWithinCached() {
$this->_reset(true);
$this->_runtemplate('<% cached %><% control Foo %><% cached %>$Bar<% end_cached %><% end_control %><% end_cached %>');
$this->_runtemplate(
'<% cached %><% control Foo %><% cached %>$Bar<% end_cached %><% end_control %><% end_cached %>');
}
public function testNoErrorMessageForCachedWithinControlWithinUncached() {
$this->_reset(true);
$this->_runtemplate('<% uncached %><% control Foo %><% cached %>$Bar<% end_cached %><% end_control %><% end_uncached %>');
$this->_runtemplate(
'<% uncached %><% control Foo %><% cached %>$Bar<% end_cached %><% end_control %><% end_uncached %>');
}
/**

View File

@ -14,7 +14,8 @@ class SSViewerTest extends SapphireTest {
public function testCurrentTheme() {
//TODO: SiteConfig moved to CMS
SSViewer::set_theme('mytheme');
$this->assertEquals('mytheme', SSViewer::current_theme(), 'Current theme is the default - user has not defined one');
$this->assertEquals('mytheme', SSViewer::current_theme(),
'Current theme is the default - user has not defined one');
}
/**
@ -57,11 +58,12 @@ class SSViewerTest extends SapphireTest {
public function testComments() {
$output = $this->render(<<<SS
This is my template<%-- this is a comment --%>This is some content<%-- this is another comment --%>This is the final content
This is my template<%-- this is a comment --%>This is some content<%-- this is another comment --%>Final content
SS
);
$this->assertEquals("This is my templateThis is some contentThis is the final content", preg_replace("/\n?<!--.*-->\n?/U",'',$output));
$this->assertEquals("This is my templateThis is some contentFinal content",
preg_replace("/\n?<!--.*-->\n?/U",'',$output));
}
public function testBasicText() {
@ -82,7 +84,8 @@ SS
$this->assertEquals('A{$ B', $this->render('A{$ B'), 'No injection as {$ not followed by word character');
$this->assertEquals('{$Test}', $this->render('{\\$Test}'), 'Escapes can be used to avoid injection');
$this->assertEquals('{\\[out:Test]}', $this->render('{\\\\$Test}'), 'Escapes before injections are correctly unescaped');
$this->assertEquals('{\\[out:Test]}', $this->render('{\\\\$Test}'),
'Escapes before injections are correctly unescaped');
}
@ -95,40 +98,60 @@ SS
public function testGlobalVariableCallsWithArguments() {
$this->assertEquals('zz', $this->render('$SSViewerTest_GlobalThatTakesArguments'));
$this->assertEquals('zFooz', $this->render('$SSViewerTest_GlobalThatTakesArguments("Foo")'));
$this->assertEquals('zFoo:Bar:Bazz', $this->render('$SSViewerTest_GlobalThatTakesArguments("Foo", "Bar", "Baz")'));
$this->assertEquals('zreferencez', $this->render('$SSViewerTest_GlobalThatTakesArguments($SSViewerTest_GlobalReferencedByString)'));
$this->assertEquals('zFoo:Bar:Bazz',
$this->render('$SSViewerTest_GlobalThatTakesArguments("Foo", "Bar", "Baz")'));
$this->assertEquals('zreferencez',
$this->render('$SSViewerTest_GlobalThatTakesArguments($SSViewerTest_GlobalReferencedByString)'));
}
public function testGlobalVariablesAreEscaped() {
$this->assertEquals('<div></div>', $this->render('$SSViewerTest_GlobalHTMLFragment'));
$this->assertEquals('&lt;div&gt;&lt;/div&gt;', $this->render('$SSViewerTest_GlobalHTMLEscaped'));
$this->assertEquals('z<div></div>z', $this->render('$SSViewerTest_GlobalThatTakesArguments($SSViewerTest_GlobalHTMLFragment)'));
$this->assertEquals('z&lt;div&gt;&lt;/div&gt;z', $this->render('$SSViewerTest_GlobalThatTakesArguments($SSViewerTest_GlobalHTMLEscaped)'));
$this->assertEquals('z<div></div>z',
$this->render('$SSViewerTest_GlobalThatTakesArguments($SSViewerTest_GlobalHTMLFragment)'));
$this->assertEquals('z&lt;div&gt;&lt;/div&gt;z',
$this->render('$SSViewerTest_GlobalThatTakesArguments($SSViewerTest_GlobalHTMLEscaped)'));
}
public function testCoreGlobalVariableCalls() {
$this->assertEquals(Director::absoluteBaseURL(), $this->render('{$absoluteBaseURL}'), 'Director::absoluteBaseURL can be called from within template');
$this->assertEquals(Director::absoluteBaseURL(), $this->render('{$AbsoluteBaseURL}'), 'Upper-case %AbsoluteBaseURL can be called from within template');
$this->assertEquals(Director::absoluteBaseURL(),
$this->render('{$absoluteBaseURL}'), 'Director::absoluteBaseURL can be called from within template');
$this->assertEquals(Director::absoluteBaseURL(), $this->render('{$AbsoluteBaseURL}'),
'Upper-case %AbsoluteBaseURL can be called from within template');
$this->assertEquals(Director::is_ajax(), $this->render('{$isAjax}'), 'All variations of is_ajax result in the correct call');
$this->assertEquals(Director::is_ajax(), $this->render('{$IsAjax}'), 'All variations of is_ajax result in the correct call');
$this->assertEquals(Director::is_ajax(), $this->render('{$is_ajax}'), 'All variations of is_ajax result in the correct call');
$this->assertEquals(Director::is_ajax(), $this->render('{$Is_ajax}'), 'All variations of is_ajax result in the correct call');
$this->assertEquals(Director::is_ajax(), $this->render('{$isAjax}'),
'All variations of is_ajax result in the correct call');
$this->assertEquals(Director::is_ajax(), $this->render('{$IsAjax}'),
'All variations of is_ajax result in the correct call');
$this->assertEquals(Director::is_ajax(), $this->render('{$is_ajax}'),
'All variations of is_ajax result in the correct call');
$this->assertEquals(Director::is_ajax(), $this->render('{$Is_ajax}'),
'All variations of is_ajax result in the correct call');
$this->assertEquals(i18n::get_locale(), $this->render('{$i18nLocale}'), 'i18n template functions result correct result');
$this->assertEquals(i18n::get_locale(), $this->render('{$get_locale}'), 'i18n template functions result correct result');
$this->assertEquals(i18n::get_locale(), $this->render('{$i18nLocale}'),
'i18n template functions result correct result');
$this->assertEquals(i18n::get_locale(), $this->render('{$get_locale}'),
'i18n template functions result correct result');
$this->assertEquals((string)Member::currentUser(), $this->render('{$CurrentMember}'), 'Member template functions result correct result');
$this->assertEquals((string)Member::currentUser(), $this->render('{$CurrentUser}'), 'Member template functions result correct result');
$this->assertEquals((string)Member::currentUser(), $this->render('{$currentMember}'), 'Member template functions result correct result');
$this->assertEquals((string)Member::currentUser(), $this->render('{$currentUser}'), 'Member template functions result correct result');
$this->assertEquals((string)Member::currentUser(), $this->render('{$CurrentMember}'),
'Member template functions result correct result');
$this->assertEquals((string)Member::currentUser(), $this->render('{$CurrentUser}'),
'Member template functions result correct result');
$this->assertEquals((string)Member::currentUser(), $this->render('{$currentMember}'),
'Member template functions result correct result');
$this->assertEquals((string)Member::currentUser(), $this->render('{$currentUser}'),
'Member template functions result correct result');
$this->assertEquals(SecurityToken::getSecurityID(), $this->render('{$getSecurityID}'), 'SecurityToken template functions result correct result');
$this->assertEquals(SecurityToken::getSecurityID(), $this->render('{$SecurityID}'), 'SecurityToken template functions result correct result');
$this->assertEquals(SecurityToken::getSecurityID(), $this->render('{$getSecurityID}'),
'SecurityToken template functions result correct result');
$this->assertEquals(SecurityToken::getSecurityID(), $this->render('{$SecurityID}'),
'SecurityToken template functions result correct result');
$this->assertEquals(Permission::check("ADMIN"), (bool)$this->render('{$HasPerm(\'ADMIN\')}'), 'Permissions template functions result correct result');
$this->assertEquals(Permission::check("ADMIN"), (bool)$this->render('{$hasPerm(\'ADMIN\')}'), 'Permissions template functions result correct result');
$this->assertEquals(Permission::check("ADMIN"), (bool)$this->render('{$HasPerm(\'ADMIN\')}'),
'Permissions template functions result correct result');
$this->assertEquals(Permission::check("ADMIN"), (bool)$this->render('{$hasPerm(\'ADMIN\')}'),
'Permissions template functions result correct result');
}
public function testLocalFunctionsTakePriorityOverGlobals() {
@ -137,7 +160,8 @@ SS
));
//call method with lots of arguments
$result = $this->render('<% with Page %>$lotsOfArguments11("a","b","c","d","e","f","g","h","i","j","k")<% end_with %>',$data);
$result = $this->render(
'<% with Page %>$lotsOfArguments11("a","b","c","d","e","f","g","h","i","j","k")<% end_with %>',$data);
$this->assertEquals("abcdefghijk",$result, "public function can accept up to 11 arguments");
//call method that does not exist
@ -150,7 +174,8 @@ SS
//call method with same name as a global method (local call should take priority)
$result = $this->render('<% with Page %>$absoluteBaseURL<% end_with %>',$data);
$this->assertEquals("testLocalFunctionPriorityCalled",$result, "Local Object's public function called. Did not return the actual baseURL of the current site");
$this->assertEquals("testLocalFunctionPriorityCalled",$result,
"Local Object's public function called. Did not return the actual baseURL of the current site");
}
public function testCurrentScopeLoopWith() {
@ -174,19 +199,23 @@ SS
))
));
$result = $this->render('<% loop Foo %>$Number<% if Sub %><% with Sub %>$Name<% end_with %><% end_if %><% end_loop %>',$data);
$result = $this->render(
'<% loop Foo %>$Number<% if Sub %><% with Sub %>$Name<% end_with %><% end_if %><% end_loop %>',$data);
$this->assertEquals("SubKid1SubKid2Number6",$result, "Loop works");
$result = $this->render('<% loop Foo %>$Number<% if Sub %><% with Sub %>$Name<% end_with %><% end_if %><% end_loop %>',$data);
$result = $this->render(
'<% loop Foo %>$Number<% if Sub %><% with Sub %>$Name<% end_with %><% end_if %><% end_loop %>',$data);
$this->assertEquals("SubKid1SubKid2Number6",$result, "Loop works");
$result = $this->render('<% with Foo %>$Count<% end_with %>',$data);
$this->assertEquals("4",$result, "4 items in the DataObjectSet");
$result = $this->render('<% with Foo %><% loop Up.Foo %>$Number<% if Sub %><% with Sub %>$Name<% end_with %><% end_if %><% end_loop %><% end_with %>',$data);
$result = $this->render('<% with Foo %><% loop Up.Foo %>$Number<% if Sub %><% with Sub %>$Name<% end_with %>'
. '<% end_if %><% end_loop %><% end_with %>',$data);
$this->assertEquals("SubKid1SubKid2Number6",$result, "Loop in with Up.Foo scope works");
$result = $this->render('<% with Foo %><% loop %>$Number<% if Sub %><% with Sub %>$Name<% end_with %><% end_if %><% end_loop %><% end_with %>',$data);
$result = $this->render('<% with Foo %><% loop %>$Number<% if Sub %><% with Sub %>$Name<% end_with %>'
. '<% end_if %><% end_loop %><% end_with %>',$data);
$this->assertEquals("SubKid1SubKid2Number6",$result, "Loop in current scope works");
}
@ -408,7 +437,8 @@ after')
public function testBaseTagGeneration() {
// XHTML wil have a closed base tag
$tmpl1 = '<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"'
. ' "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head><% base_tag %></head>
<body><p>test</p><body>
@ -421,7 +451,8 @@ after')
<head><% base_tag %></head>
<body><p>test</p><body>
</html>';
$this->assertRegExp('/<head><base href=".*"><!--\[if lte IE 6\]><\/base><!\[endif\]--><\/head>/', $this->render($tmpl2));
$this->assertRegExp('/<head><base href=".*"><!--\[if lte IE 6\]><\/base><!\[endif\]--><\/head>/',
$this->render($tmpl2));
$tmpl3 = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
@ -429,14 +460,16 @@ after')
<head><% base_tag %></head>
<body><p>test</p><body>
</html>';
$this->assertRegExp('/<head><base href=".*"><!--\[if lte IE 6\]><\/base><!\[endif\]--><\/head>/', $this->render($tmpl3));
$this->assertRegExp('/<head><base href=".*"><!--\[if lte IE 6\]><\/base><!\[endif\]--><\/head>/',
$this->render($tmpl3));
// Check that the content negotiator converts to the equally legal formats
$negotiator = new ContentNegotiator();
$response = new SS_HTTPResponse($this->render($tmpl1));
$negotiator->html($response);
$this->assertRegExp('/<head><base href=".*"><!--\[if lte IE 6\]><\/base><!\[endif\]--><\/head>/', $response->getBody());
$this->assertRegExp('/<head><base href=".*"><!--\[if lte IE 6\]><\/base><!\[endif\]--><\/head>/',
$response->getBody());
$response = new SS_HTTPResponse($this->render($tmpl1));
$negotiator->xhtml($response);
@ -465,12 +498,14 @@ after')
);
$this->assertEquals(
$this->render('<% include SSViewerTestIncludeWithArguments Arg1="A", Arg2=$B %>', new ArrayData(array('B' => 'Bar'))),
$this->render('<% include SSViewerTestIncludeWithArguments Arg1="A", Arg2=$B %>',
new ArrayData(array('B' => 'Bar'))),
'<p>A</p><p>Bar</p>'
);
$this->assertEquals(
$this->render('<% include SSViewerTestIncludeWithArguments Arg1="A" %>', new ArrayData(array('Arg1' => 'Foo', 'Arg2' => 'Bar'))),
$this->render('<% include SSViewerTestIncludeWithArguments Arg1="A" %>',
new ArrayData(array('Arg1' => 'Foo', 'Arg2' => 'Bar'))),
'<p>A</p><p>Bar</p>'
);
}
@ -621,12 +656,15 @@ after')
$this->assertEquals("23456789",$result,"Middle numbers rendered in order");
//test MiddleString
$result = $this->render('<% loop Set %><% if MiddleString == "middle" %>$Number$MiddleString<% end_if %><% end_loop %>',$data);
$this->assertEquals("2middle3middle4middle5middle6middle7middle8middle9middle",$result,"Middle numbers rendered in order");
$result = $this->render('<% loop Set %><% if MiddleString == "middle" %>$Number$MiddleString<% end_if %>'
. '<% end_loop %>',$data);
$this->assertEquals("2middle3middle4middle5middle6middle7middle8middle9middle",$result,
"Middle numbers rendered in order");
//test EvenOdd
$result = $this->render('<% loop Set %>$EvenOdd<% end_loop %>',$data);
$this->assertEquals("oddevenoddevenoddevenoddevenoddeven",$result,"Even and Odd is returned in sequence numbers rendered in order");
$this->assertEquals("oddevenoddevenoddevenoddevenoddeven",$result,
"Even and Odd is returned in sequence numbers rendered in order");
//test Pos
$result = $this->render('<% loop Set %>$Pos<% end_loop %>',$data);
@ -658,7 +696,8 @@ after')
//test MultipleOf 9 zero-based
$result = $this->render('<% loop Set %><% if MultipleOf(9,0) %>$Number<% end_if %><% end_loop %>',$data);
$this->assertEquals("110",$result,"Only numbers that are multiples of 9 with zero-based indexing are returned. I.e. the first and last item");
$this->assertEquals("110",$result,
"Only numbers that are multiples of 9 with zero-based indexing are returned. (The first and last item)");
//test MultipleOf 11
$result = $this->render('<% loop Set %><% if MultipleOf(11) %>$Number<% end_if %><% end_loop %>',$data);
@ -701,15 +740,18 @@ after')
// Using $Up in a with block
$this->assertEquals('BazBarQux',
$this->render('<% with Foo.Bar.Baz %>{$Name}<% with $Up %>{$Name}{$Qux.Name}<% end_with %><% end_with %>', $data));
$this->render('<% with Foo.Bar.Baz %>{$Name}<% with $Up %>{$Name}{$Qux.Name}<% end_with %>'
.'<% end_with %>', $data));
// Stepping up & back down the scope tree with with blocks
$this->assertEquals('BazBarQuxBarBaz',
$this->render('<% with Foo.Bar.Baz %>{$Name}<% with $Up %>{$Name}<% with Qux %>{$Name}<% end_with %>{$Name}<% end_with %>{$Name}<% end_with %>', $data));
$this->render('<% with Foo.Bar.Baz %>{$Name}<% with $Up %>{$Name}<% with Qux %>{$Name}<% end_with %>'
. '{$Name}<% end_with %>{$Name}<% end_with %>', $data));
// Using $Up.Up, where first $Up points to a previous scope entered using $Up, thereby skipping up to Foo
$this->assertEquals('Foo',
$this->render('<% with Foo.Bar.Baz %><% with Up %><% with Qux %>{$Up.Up.Name}<% end_with %><% end_with %><% end_with %>', $data));
$this->render('<% with Foo.Bar.Baz %><% with Up %><% with Qux %>{$Up.Up.Name}<% end_with %><% end_with %>'
. '<% end_with %>', $data));
// Using $Up.Up, where first $Up points to an Up used in a local scope lookup, should still skip to Foo
$this->assertEquals('Foo',
@ -827,9 +869,8 @@ after')
// items, checked by using "Last"
$this->assertEqualIgnoringWhitespace(
'1ab23last',
$this->render(
'<% loop $Foo %>$Name<% loop Children %>$Name<% end_loop %><% if Last %>last<% end_if %><% end_loop %>',
$data
$this->render('<% loop $Foo %>$Name<% loop Children %>$Name<% end_loop %><% if Last %>last<% end_if %>'
. '<% end_loop %>', $data
)
);
}
@ -859,7 +900,8 @@ after')
'blackcandy_blog' => 'blackcandy_blog',
'darkshades' => 'darkshades',
'darkshades_blog' => 'darkshades_blog'
), SSViewer::get_themes($testThemeBaseDir, true), 'Our test theme directory contains 2 themes and 2 sub-themes');
), SSViewer::get_themes($testThemeBaseDir, true),
'Our test theme directory contains 2 themes and 2 sub-themes');
// Remove all the test themes we created
Filesystem::removeFolder($testThemeBaseDir);
@ -956,14 +998,21 @@ after')
$data = new ArrayData(array());
$result = $view->process($data);
$expected = '<!-- template ' . FRAMEWORK_PATH . '/tests/templates/SSViewerTestCommentsPartialSource.ss --><div class=\'typography\'></div><!-- end template ' . FRAMEWORK_PATH . '/tests/templates/SSViewerTestCommentsPartialSource.ss -->';
$expected = '<!-- template ' . FRAMEWORK_PATH . '/tests/templates/SSViewerTestCommentsPartialSource.ss -->'
. '<div class=\'typography\'></div><!-- end template ' . FRAMEWORK_PATH
. '/tests/templates/SSViewerTestCommentsPartialSource.ss -->';
$this->assertEquals($result, $expected);
$view = new SSViewer(array('SSViewerTestCommentsWithInclude'));
$data = new ArrayData(array());
$result = $view->process($data);
$expected = '<!-- template ' . FRAMEWORK_PATH . '/tests/templates/SSViewerTestCommentsWithInclude.ss --><div class=\'typography\'><!-- include \'SSViewerTestCommentsInclude\' --><!-- template ' . FRAMEWORK_PATH . '/tests/templates/SSViewerTestCommentsInclude.ss -->Included<!-- end template ' . FRAMEWORK_PATH . '/tests/templates/SSViewerTestCommentsInclude.ss --><!-- end include \'SSViewerTestCommentsInclude\' --></div><!-- end template ' . FRAMEWORK_PATH . '/tests/templates/SSViewerTestCommentsWithInclude.ss -->';
$expected = '<!-- template ' . FRAMEWORK_PATH . '/tests/templates/SSViewerTestCommentsWithInclude.ss -->'
. '<div class=\'typography\'><!-- include \'SSViewerTestCommentsInclude\' --><!-- template '
. FRAMEWORK_PATH . '/tests/templates/SSViewerTestCommentsInclude.ss -->Included<!-- end template '
. FRAMEWORK_PATH . '/tests/templates/SSViewerTestCommentsInclude.ss -->'
. '<!-- end include \'SSViewerTestCommentsInclude\' --></div><!-- end template ' . FRAMEWORK_PATH
. '/tests/templates/SSViewerTestCommentsWithInclude.ss -->';
$this->assertEquals($result, $expected);
SSViewer::set_source_file_comments(false);

View File

@ -25,11 +25,12 @@ class ViewableDataTest extends SapphireTest {
$this->assertTrue($container->obj('alwaysCasted') instanceof ViewableDataTest_RequiresCasting);
$this->assertTrue($caster->obj('alwaysCasted', null, false) instanceof ViewableDataTest_RequiresCasting);
/* @todo - This currently fails, because the default_cast static variable is always taken from the topmost object,
* not the failover object the field actually came from. Should we fix this, or declare current behaviour as correct?
$this->assertTrue($container->obj('noCastingInformation') instanceof ViewableData_Caster);
$this->assertFalse($caster->obj('noCastingInformation', null, false) instanceof ViewableData_Caster);
/* @todo This currently fails, because the default_cast static variable is always taken from the topmost
* object, not the failover object the field actually came from. Should we fix this, or declare current
* behaviour as correct?
*
* $this->assertTrue($container->obj('noCastingInformation') instanceof ViewableData_Caster);
* $this->assertFalse($caster->obj('noCastingInformation', null, false) instanceof ViewableData_Caster);
*/
}

View File

@ -27,7 +27,8 @@ class GenericTemplateGlobalProvider implements TemplateGlobalProvider {
if(isset(self::$modules[$name])) {
return self::$modules[$name];
} else {
throw new InvalidArgumentException(sprintf('%s is not a supported argument. Possible values: %s', $name, implode(', ', self::$modules)));
throw new InvalidArgumentException(sprintf('%s is not a supported argument. Possible values: %s', $name,
implode(', ', self::$modules)));
}
}

View File

@ -215,7 +215,8 @@ class Requirements {
* See {@link Requirements_Backend::includeInHTML()} for more information.
*
* @param string $templateFilePath Absolute path for the *.ss template file
* @param string $content HTML content that has already been parsed from the $templateFilePath through {@link SSViewer}.
* @param string $content HTML content that has already been parsed from the $templateFilePath
* through {@link SSViewer}.
* @return string HTML content thats augumented with the requirements before the closing <head> tag.
*/
public static function includeInHTML($templateFile, $content) {
@ -642,13 +643,15 @@ class Requirements_Backend {
* @todo Calculate $prefix properly
*
* @param string $templateFilePath Absolute path for the *.ss template file
* @param string $content HTML content that has already been parsed from the $templateFilePath through {@link SSViewer}.
* @param string $content HTML content that has already been parsed from the $templateFilePath
* through {@link SSViewer}.
* @return string HTML content thats augumented with the requirements before the closing <head> tag.
*/
public function includeInHTML($templateFile, $content) {
if(isset($_GET['debug_profile'])) Profiler::mark("Requirements::includeInHTML");
if((strpos($content, '</head>') !== false || strpos($content, '</head ') !== false) && ($this->css || $this->javascript || $this->customCSS || $this->customScript || $this->customHeadTags)) {
if((strpos($content, '</head>') !== false || strpos($content, '</head ') !== false)
&& ($this->css||$this->javascript||$this->customCSS||$this->customScript||$this->customHeadTags)) {
$requirements = '';
$jsRequirements = '';
@ -675,7 +678,8 @@ class Requirements_Backend {
foreach(array_diff_key($this->css,$this->blocked) as $file => $params) {
$path = $this->path_for_file($file);
if($path) {
$media = (isset($params['media']) && !empty($params['media'])) ? " media=\"{$params['media']}\"" : "";
$media = (isset($params['media']) && !empty($params['media']))
? " media=\"{$params['media']}\"" : "";
$requirements .= "<link rel=\"stylesheet\" type=\"text/css\"{$media} href=\"$path\" />\n";
}
}
@ -852,9 +856,11 @@ class Requirements_Backend {
*
* @see http://code.google.com/p/jsmin-php/
*
* @todo Should we enforce unique inclusion of files, or leave it to the developer? Can auto-detection cause breaks?
* @todo Should we enforce unique inclusion of files, or leave it to the developer? Can auto-detection cause
* breaks?
*
* @param string $combinedFileName Filename of the combined file (will be stored in {@link Director::baseFolder()} by default)
* @param string $combinedFileName Filename of the combined file (will be stored in {@link Director::baseFolder()}
* by default)
* @param array $files Array of filenames relative to the webroot
*/
public function combine_files($combinedFileName, $files) {
@ -862,7 +868,8 @@ class Requirements_Backend {
foreach($this->combine_files as $_combinedFileName => $_files) {
$duplicates = array_intersect($_files, $files);
if($duplicates && $combinedFileName != $_combinedFileName) {
user_error("Requirements_Backend::combine_files(): Already included files " . implode(',', $duplicates) . " in combined file '{$_combinedFileName}'", E_USER_NOTICE);
user_error("Requirements_Backend::combine_files(): Already included files " . implode(',', $duplicates)
. " in combined file '{$_combinedFileName}'", E_USER_NOTICE);
return false;
}
}
@ -870,7 +877,7 @@ class Requirements_Backend {
if(is_array($file)) {
// Either associative array path=>path type=>type or numeric 0=>path 1=>type
// Otherwise, assume path is the first item
if (isset($file['type']) && ($file['type'] == 'css' || $file['type'] == 'javascript' || $file['type'] == 'js')) {
if (isset($file['type']) && in_array($file['type'], array('css', 'javascript', 'js'))) {
switch ($file['type']) {
case 'css':
$this->css($file['path']);
@ -880,7 +887,7 @@ class Requirements_Backend {
break;
}
$files[$index] = $file['path'];
} elseif (isset($file[1]) && ($file[1] == 'css' || $file[1] == 'javascript' || $file[1] == 'js')) {
} elseif (isset($file[1]) && in_array($file[1], array('css', 'javascript', 'js'))) {
switch ($file[1]) {
case 'css':
$this->css($file[0]);
@ -900,7 +907,8 @@ class Requirements_Backend {
} elseif(substr($file, -3) == 'css') {
$this->css($file);
} else {
user_error("Requirements_Backend::combine_files(): Couldn't guess file type for file '$file', please specify by passing using an array instead.", E_USER_NOTICE);
user_error("Requirements_Backend::combine_files(): Couldn't guess file type for file '$file', "
. "please specify by passing using an array instead.", E_USER_NOTICE);
}
}
}
@ -922,7 +930,8 @@ class Requirements_Backend {
*/
public function delete_combined_files($combinedFileName = null) {
$combinedFiles = ($combinedFileName) ? array($combinedFileName => null) : $this->combine_files;
$combinedFolder = ($this->getCombinedFilesFolder()) ? (Director::baseFolder() . '/' . $this->combinedFilesFolder) : Director::baseFolder();
$combinedFolder = ($this->getCombinedFilesFolder()) ?
(Director::baseFolder() . '/' . $this->combinedFilesFolder) : Director::baseFolder();
foreach($combinedFiles as $combinedFile => $sourceItems) {
$filePath = $combinedFolder . '/' . $combinedFile;
if(file_exists($filePath)) {
@ -954,7 +963,8 @@ class Requirements_Backend {
foreach($this->combine_files as $combinedFile => $sourceItems) {
foreach($sourceItems as $sourceItem) {
if(isset($combinerCheck[$sourceItem]) && $combinerCheck[$sourceItem] != $combinedFile){
user_error("Requirements_Backend::process_combined_files - file '$sourceItem' appears in two combined files:" . " '{$combinerCheck[$sourceItem]}' and '$combinedFile'", E_USER_WARNING);
user_error("Requirements_Backend::process_combined_files - file '$sourceItem' appears in two " .
"combined files:" . " '{$combinerCheck[$sourceItem]}' and '$combinedFile'", E_USER_WARNING);
}
$combinerCheck[$sourceItem] = $combinedFile;
@ -998,12 +1008,13 @@ class Requirements_Backend {
Filesystem::makeFolder(dirname($combinedFilePath));
}
// If the file isn't writeable, don't even bother trying to make the combined file and return (falls back to uncombined)
// Complex test because is_writable fails if the file doesn't exist yet.
// If the file isn't writeable, don't even bother trying to make the combined file and return (falls back
// to uncombined). Complex test because is_writable fails if the file doesn't exist yet.
if((file_exists($combinedFilePath) && !is_writable($combinedFilePath))
|| (!file_exists($combinedFilePath) && !is_writable(dirname($combinedFilePath)))
) {
user_error("Requirements_Backend::process_combined_files(): Couldn't create '$combinedFilePath'", E_USER_WARNING);
user_error("Requirements_Backend::process_combined_files(): Couldn't create '$combinedFilePath'",
E_USER_WARNING);
return false;
}
@ -1048,7 +1059,8 @@ class Requirements_Backend {
// Unsuccessful write - just include the regular JS files, rather than the combined one
if(!$successfulWrite) {
user_error("Requirements_Backend::process_combined_files(): Couldn't create '$combinedFilePath'", E_USER_WARNING);
user_error("Requirements_Backend::process_combined_files(): Couldn't create '$combinedFilePath'",
E_USER_WARNING);
continue;
}
}

View File

@ -17,12 +17,13 @@ else {
}
/**
This is the exception raised when failing to parse a template. Note that we don't currently do any static analysis, so we can't know
if the template will run, just if it's malformed. It also won't catch mistakes that still look valid.
* This is the exception raised when failing to parse a template. Note that we don't currently do any static analysis,
* so we can't know if the template will run, just if it's malformed. It also won't catch mistakes that still look
* valid.
*/
class SSTemplateParseException extends Exception {
public function __construct($message, $parser) {
function __construct($message, $parser) {
$prior = substr($parser->string, 0, $parser->pos);
preg_match_all('/\r\n|\r|\n/', $prior, $matches);
@ -34,33 +35,35 @@ class SSTemplateParseException extends Exception {
}
/**
This is the parser for the SilverStripe template language. It gets called on a string and uses a php-peg parser to match
that string against the language structure, building up the PHP code to execute that structure as it parses
The $result array that is built up as part of the parsing (see thirdparty/php-peg/README.md for more on how parsers
build results) has one special member, 'php', which contains the php equivalent of that part of the template tree.
Some match rules generate alternate php, or other variations, so check the per-match documentation too.
Terms used:
Marked: A string or lookup in the template that has been explictly marked as such - lookups by prepending with "$"
(like $Foo.Bar), strings by wrapping with single or double quotes ('Foo' or "Foo")
Bare: The opposite of marked. An argument that has to has it's type inferred by usage and 2.4 defaults.
Example of using a bare argument for a loop block: <% loop Foo %>
Block: One of two SS template structures. The special characters "<%" and "%>" are used to wrap the opening and
(required or forbidden depending on which block exactly) closing block marks.
Open Block: An SS template block that doesn't wrap any content or have a closing end tag (in fact, a closing end tag is
forbidden)
Closed Block: An SS template block that wraps content, and requires a counterpart <% end_blockname %> tag
Angle Bracket: angle brackets "<" and ">" are used to eat whitespace between template elements
N: eats white space including newlines (using in legacy _t support)
* This is the parser for the SilverStripe template language. It gets called on a string and uses a php-peg parser
* to match that string against the language structure, building up the PHP code to execute that structure as it
* parses
*
* The $result array that is built up as part of the parsing (see thirdparty/php-peg/README.md for more on how
* parsers build results) has one special member, 'php', which contains the php equivalent of that part of the
* template tree.
*
* Some match rules generate alternate php, or other variations, so check the per-match documentation too.
*
* Terms used:
*
* Marked: A string or lookup in the template that has been explictly marked as such - lookups by prepending with
* "$" (like $Foo.Bar), strings by wrapping with single or double quotes ('Foo' or "Foo")
*
* Bare: The opposite of marked. An argument that has to has it's type inferred by usage and 2.4 defaults.
*
* Example of using a bare argument for a loop block: <% loop Foo %>
*
* Block: One of two SS template structures. The special characters "<%" and "%>" are used to wrap the opening and
* (required or forbidden depending on which block exactly) closing block marks.
*
* Open Block: An SS template block that doesn't wrap any content or have a closing end tag (in fact, a closing end
* tag is forbidden)
*
* Closed Block: An SS template block that wraps content, and requires a counterpart <% end_blockname %> tag
*
* Angle Bracket: angle brackets "<" and ">" are used to eat whitespace between template elements
* N: eats white space including newlines (using in legacy _t support)
*/
class SSTemplateParser extends Parser {
@ -73,15 +76,16 @@ class SSTemplateParser extends Parser {
/**
* Override the function that constructs the result arrays to also prepare a 'php' item in the array
*/
public function construct($matchrule, $name, $arguments = null) {
function construct($matchrule, $name, $arguments = null) {
$res = parent::construct($matchrule, $name, $arguments);
if (!isset($res['php'])) $res['php'] = '';
return $res;
}
/* Template: (Comment | Translate | If | Require | CacheBlock | UncachedBlock | OldI18NTag | Include | ClosedBlock | OpenBlock | MalformedBlock | Injection | Text)+ */
/* Template: (Comment | Translate | If | Require | CacheBlock | UncachedBlock | OldI18NTag | Include | ClosedBlock |
OpenBlock | MalformedBlock | Injection | Text)+ */
protected $match_Template_typestack = array('Template');
public function match_Template ($stack = array()) {
function match_Template ($stack = array()) {
$matchrule = "Template"; $result = $this->construct($matchrule, $matchrule, null);
$count = 0;
while (true) {
@ -329,13 +333,13 @@ class SSTemplateParser extends Parser {
public function Template_STR(&$res, $sub) {
function Template_STR(&$res, $sub) {
$res['php'] .= $sub['php'] . PHP_EOL ;
}
/* Word: / [A-Za-z_] [A-Za-z0-9_]* / */
protected $match_Word_typestack = array('Word');
public function match_Word ($stack = array()) {
function match_Word ($stack = array()) {
$matchrule = "Word"; $result = $this->construct($matchrule, $matchrule, null);
if (( $subres = $this->rx( '/ [A-Za-z_] [A-Za-z0-9_]* /' ) ) !== FALSE) {
$result["text"] .= $subres;
@ -347,7 +351,7 @@ class SSTemplateParser extends Parser {
/* Number: / [0-9]+ / */
protected $match_Number_typestack = array('Number');
public function match_Number ($stack = array()) {
function match_Number ($stack = array()) {
$matchrule = "Number"; $result = $this->construct($matchrule, $matchrule, null);
if (( $subres = $this->rx( '/ [0-9]+ /' ) ) !== FALSE) {
$result["text"] .= $subres;
@ -359,7 +363,7 @@ class SSTemplateParser extends Parser {
/* Value: / [A-Za-z0-9_]+ / */
protected $match_Value_typestack = array('Value');
public function match_Value ($stack = array()) {
function match_Value ($stack = array()) {
$matchrule = "Value"; $result = $this->construct($matchrule, $matchrule, null);
if (( $subres = $this->rx( '/ [A-Za-z0-9_]+ /' ) ) !== FALSE) {
$result["text"] .= $subres;
@ -371,7 +375,7 @@ class SSTemplateParser extends Parser {
/* CallArguments: :Argument ( < "," < :Argument )* */
protected $match_CallArguments_typestack = array('CallArguments');
public function match_CallArguments ($stack = array()) {
function match_CallArguments ($stack = array()) {
$matchrule = "CallArguments"; $result = $this->construct($matchrule, $matchrule, null);
$_61 = NULL;
do {
@ -421,18 +425,19 @@ class SSTemplateParser extends Parser {
/**
* Values are bare words in templates, but strings in PHP. We rely on PHP's type conversion to back-convert strings
* to numbers when needed.
* Values are bare words in templates, but strings in PHP. We rely on PHP's type conversion to back-convert
* strings to numbers when needed.
*/
public function CallArguments_Argument(&$res, $sub) {
function CallArguments_Argument(&$res, $sub) {
if (!empty($res['php'])) $res['php'] .= ', ';
$res['php'] .= ($sub['ArgumentMode'] == 'default') ? $sub['string_php'] : str_replace('$$FINAL', 'XML_val', $sub['php']);
$res['php'] .= ($sub['ArgumentMode'] == 'default') ? $sub['string_php'] :
str_replace('$$FINAL', 'XML_val', $sub['php']);
}
/* Call: Method:Word ( "(" < :CallArguments? > ")" )? */
protected $match_Call_typestack = array('Call');
public function match_Call ($stack = array()) {
function match_Call ($stack = array()) {
$matchrule = "Call"; $result = $this->construct($matchrule, $matchrule, null);
$_71 = NULL;
do {
@ -490,7 +495,7 @@ class SSTemplateParser extends Parser {
/* LookupStep: :Call &"." */
protected $match_LookupStep_typestack = array('LookupStep');
public function match_LookupStep ($stack = array()) {
function match_LookupStep ($stack = array()) {
$matchrule = "LookupStep"; $result = $this->construct($matchrule, $matchrule, null);
$_75 = NULL;
do {
@ -523,7 +528,7 @@ class SSTemplateParser extends Parser {
/* LastLookupStep: :Call */
protected $match_LastLookupStep_typestack = array('LastLookupStep');
public function match_LastLookupStep ($stack = array()) {
function match_LastLookupStep ($stack = array()) {
$matchrule = "LastLookupStep"; $result = $this->construct($matchrule, $matchrule, null);
$matcher = 'match_'.'Call'; $key = $matcher; $pos = $this->pos;
$subres = ( $this->packhas( $key, $pos ) ? $this->packread( $key, $pos ) : $this->packwrite( $key, $pos, $this->$matcher(array_merge($stack, array($result))) ) );
@ -537,7 +542,7 @@ class SSTemplateParser extends Parser {
/* Lookup: LookupStep ("." LookupStep)* "." LastLookupStep | LastLookupStep */
protected $match_Lookup_typestack = array('Lookup');
public function match_Lookup ($stack = array()) {
function match_Lookup ($stack = array()) {
$matchrule = "Lookup"; $result = $this->construct($matchrule, $matchrule, null);
$_89 = NULL;
do {
@ -609,7 +614,7 @@ class SSTemplateParser extends Parser {
public function Lookup__construct(&$res) {
function Lookup__construct(&$res) {
$res['php'] = '$scope';
$res['LookupSteps'] = array();
}
@ -619,7 +624,7 @@ class SSTemplateParser extends Parser {
* get the next ViewableData in the sequence, and LastLookupStep calls different methods (XML_val, hasValue, obj)
* depending on the context the lookup is used in.
*/
public function Lookup_AddLookupStep(&$res, $sub, $method) {
function Lookup_AddLookupStep(&$res, $sub, $method) {
$res['LookupSteps'][] = $sub;
$property = $sub['Call']['Method']['text'];
@ -632,18 +637,18 @@ class SSTemplateParser extends Parser {
}
}
public function Lookup_LookupStep(&$res, $sub) {
function Lookup_LookupStep(&$res, $sub) {
$this->Lookup_AddLookupStep($res, $sub, 'obj');
}
public function Lookup_LastLookupStep(&$res, $sub) {
function Lookup_LastLookupStep(&$res, $sub) {
$this->Lookup_AddLookupStep($res, $sub, '$$FINAL');
}
/* Translate: "<%t" < Entity < (Default:QuotedString)? < (!("is" "=") < "is" < Context:QuotedString)? < (InjectionVariables)? > "%>" */
protected $match_Translate_typestack = array('Translate');
public function match_Translate ($stack = array()) {
function match_Translate ($stack = array()) {
$matchrule = "Translate"; $result = $this->construct($matchrule, $matchrule, null);
$_115 = NULL;
do {
@ -752,7 +757,7 @@ class SSTemplateParser extends Parser {
/* InjectionVariables: (< InjectionName:Word "=" Argument)+ */
protected $match_InjectionVariables_typestack = array('InjectionVariables');
public function match_InjectionVariables ($stack = array()) {
function match_InjectionVariables ($stack = array()) {
$matchrule = "InjectionVariables"; $result = $this->construct($matchrule, $matchrule, null);
$count = 0;
while (true) {
@ -795,7 +800,7 @@ class SSTemplateParser extends Parser {
/* Entity: / [A-Za-z_] [\w\.]* / */
protected $match_Entity_typestack = array('Entity');
public function match_Entity ($stack = array()) {
function match_Entity ($stack = array()) {
$matchrule = "Entity"; $result = $this->construct($matchrule, $matchrule, null);
if (( $subres = $this->rx( '/ [A-Za-z_] [\w\.]* /' ) ) !== FALSE) {
$result["text"] .= $subres;
@ -807,43 +812,43 @@ class SSTemplateParser extends Parser {
public function Translate__construct(&$res) {
function Translate__construct(&$res) {
$res['php'] = '$val .= _t(';
}
public function Translate_Entity(&$res, $sub) {
function Translate_Entity(&$res, $sub) {
$res['php'] .= "'$sub[text]'";
}
public function Translate_Default(&$res, $sub) {
function Translate_Default(&$res, $sub) {
$res['php'] .= ",$sub[text]";
}
public function Translate_Context(&$res, $sub) {
function Translate_Context(&$res, $sub) {
$res['php'] .= ",$sub[text]";
}
public function Translate_InjectionVariables(&$res, $sub) {
function Translate_InjectionVariables(&$res, $sub) {
$res['php'] .= ",$sub[php]";
}
public function Translate__finalise(&$res) {
function Translate__finalise(&$res) {
$res['php'] .= ');';
}
public function InjectionVariables__construct(&$res) {
function InjectionVariables__construct(&$res) {
$res['php'] = "array(";
}
public function InjectionVariables_InjectionName(&$res, $sub) {
function InjectionVariables_InjectionName(&$res, $sub) {
$res['php'] .= "'$sub[text]'=>";
}
public function InjectionVariables_Argument(&$res, $sub) {
function InjectionVariables_Argument(&$res, $sub) {
$res['php'] .= str_replace('$$FINAL', 'XML_val', $sub['php']) . ',';
}
public function InjectionVariables__finalise(&$res) {
function InjectionVariables__finalise(&$res) {
if (substr($res['php'], -1) == ',') $res['php'] = substr($res['php'], 0, -1); //remove last comma in the array
$res['php'] .= ')';
}
@ -851,7 +856,7 @@ class SSTemplateParser extends Parser {
/* SimpleInjection: '$' :Lookup */
protected $match_SimpleInjection_typestack = array('SimpleInjection');
public function match_SimpleInjection ($stack = array()) {
function match_SimpleInjection ($stack = array()) {
$matchrule = "SimpleInjection"; $result = $this->construct($matchrule, $matchrule, null);
$_126 = NULL;
do {
@ -876,7 +881,7 @@ class SSTemplateParser extends Parser {
/* BracketInjection: '{$' :Lookup "}" */
protected $match_BracketInjection_typestack = array('BracketInjection');
public function match_BracketInjection ($stack = array()) {
function match_BracketInjection ($stack = array()) {
$matchrule = "BracketInjection"; $result = $this->construct($matchrule, $matchrule, null);
$_131 = NULL;
do {
@ -903,7 +908,7 @@ class SSTemplateParser extends Parser {
/* Injection: BracketInjection | SimpleInjection */
protected $match_Injection_typestack = array('Injection');
public function match_Injection ($stack = array()) {
function match_Injection ($stack = array()) {
$matchrule = "Injection"; $result = $this->construct($matchrule, $matchrule, null);
$_136 = NULL;
do {
@ -934,13 +939,13 @@ class SSTemplateParser extends Parser {
public function Injection_STR(&$res, $sub) {
function Injection_STR(&$res, $sub) {
$res['php'] = '$val .= '. str_replace('$$FINAL', 'XML_val', $sub['Lookup']['php']) . ';';
}
/* DollarMarkedLookup: SimpleInjection */
protected $match_DollarMarkedLookup_typestack = array('DollarMarkedLookup');
public function match_DollarMarkedLookup ($stack = array()) {
function match_DollarMarkedLookup ($stack = array()) {
$matchrule = "DollarMarkedLookup"; $result = $this->construct($matchrule, $matchrule, null);
$matcher = 'match_'.'SimpleInjection'; $key = $matcher; $pos = $this->pos;
$subres = ( $this->packhas( $key, $pos ) ? $this->packread( $key, $pos ) : $this->packwrite( $key, $pos, $this->$matcher(array_merge($stack, array($result))) ) );
@ -953,13 +958,13 @@ class SSTemplateParser extends Parser {
public function DollarMarkedLookup_STR(&$res, $sub) {
function DollarMarkedLookup_STR(&$res, $sub) {
$res['Lookup'] = $sub['Lookup'];
}
/* QuotedString: q:/['"]/ String:/ (\\\\ | \\. | [^$q\\])* / '$q' */
protected $match_QuotedString_typestack = array('QuotedString');
public function match_QuotedString ($stack = array()) {
function match_QuotedString ($stack = array()) {
$matchrule = "QuotedString"; $result = $this->construct($matchrule, $matchrule, null);
$_142 = NULL;
do {
@ -995,7 +1000,7 @@ class SSTemplateParser extends Parser {
/* FreeString: /[^,)%!=|&]+/ */
protected $match_FreeString_typestack = array('FreeString');
public function match_FreeString ($stack = array()) {
function match_FreeString ($stack = array()) {
$matchrule = "FreeString"; $result = $this->construct($matchrule, $matchrule, null);
if (( $subres = $this->rx( '/[^,)%!=|&]+/' ) ) !== FALSE) {
$result["text"] .= $subres;
@ -1011,7 +1016,7 @@ class SSTemplateParser extends Parser {
:Lookup !(< FreeString)|
:FreeString */
protected $match_Argument_typestack = array('Argument');
public function match_Argument ($stack = array()) {
function match_Argument ($stack = array()) {
$matchrule = "Argument"; $result = $this->construct($matchrule, $matchrule, null);
$_162 = NULL;
do {
@ -1122,17 +1127,17 @@ class SSTemplateParser extends Parser {
* if the context indicates a string
*/
public function Argument_DollarMarkedLookup(&$res, $sub) {
function Argument_DollarMarkedLookup(&$res, $sub) {
$res['ArgumentMode'] = 'lookup';
$res['php'] = $sub['Lookup']['php'];
}
public function Argument_QuotedString(&$res, $sub) {
function Argument_QuotedString(&$res, $sub) {
$res['ArgumentMode'] = 'string';
$res['php'] = "'" . str_replace("'", "\\'", $sub['String']['text']) . "'";
}
public function Argument_Lookup(&$res, $sub) {
function Argument_Lookup(&$res, $sub) {
if (count($sub['LookupSteps']) == 1 && !isset($sub['LookupSteps'][0]['Call']['Arguments'])) {
$res['ArgumentMode'] = 'default';
$res['lookup_php'] = $sub['php'];
@ -1144,14 +1149,14 @@ class SSTemplateParser extends Parser {
}
}
public function Argument_FreeString(&$res, $sub) {
function Argument_FreeString(&$res, $sub) {
$res['ArgumentMode'] = 'string';
$res['php'] = "'" . str_replace("'", "\\'", trim($sub['text'])) . "'";
}
/* ComparisonOperator: "==" | "!=" | "=" */
protected $match_ComparisonOperator_typestack = array('ComparisonOperator');
public function match_ComparisonOperator ($stack = array()) {
function match_ComparisonOperator ($stack = array()) {
$matchrule = "ComparisonOperator"; $result = $this->construct($matchrule, $matchrule, null);
$_171 = NULL;
do {
@ -1196,7 +1201,7 @@ class SSTemplateParser extends Parser {
/* Comparison: Argument < ComparisonOperator > Argument */
protected $match_Comparison_typestack = array('Comparison');
public function match_Comparison ($stack = array()) {
function match_Comparison ($stack = array()) {
$matchrule = "Comparison"; $result = $this->construct($matchrule, $matchrule, null);
$_178 = NULL;
do {
@ -1223,7 +1228,7 @@ class SSTemplateParser extends Parser {
public function Comparison_Argument(&$res, $sub) {
function Comparison_Argument(&$res, $sub) {
if ($sub['ArgumentMode'] == 'default') {
if (!empty($res['php'])) $res['php'] .= $sub['string_php'];
else $res['php'] = str_replace('$$FINAL', 'XML_val', $sub['lookup_php']);
@ -1233,13 +1238,13 @@ class SSTemplateParser extends Parser {
}
}
public function Comparison_ComparisonOperator(&$res, $sub) {
function Comparison_ComparisonOperator(&$res, $sub) {
$res['php'] .= ($sub['text'] == '=' ? '==' : $sub['text']);
}
/* PresenceCheck: (Not:'not' <)? Argument */
protected $match_PresenceCheck_typestack = array('PresenceCheck');
public function match_PresenceCheck ($stack = array()) {
function match_PresenceCheck ($stack = array()) {
$matchrule = "PresenceCheck"; $result = $this->construct($matchrule, $matchrule, null);
$_185 = NULL;
do {
@ -1280,11 +1285,11 @@ class SSTemplateParser extends Parser {
public function PresenceCheck_Not(&$res, $sub) {
function PresenceCheck_Not(&$res, $sub) {
$res['php'] = '!';
}
public function PresenceCheck_Argument(&$res, $sub) {
function PresenceCheck_Argument(&$res, $sub) {
if ($sub['ArgumentMode'] == 'string') {
$res['php'] .= '((bool)'.$sub['php'].')';
}
@ -1298,7 +1303,7 @@ class SSTemplateParser extends Parser {
/* IfArgumentPortion: Comparison | PresenceCheck */
protected $match_IfArgumentPortion_typestack = array('IfArgumentPortion');
public function match_IfArgumentPortion ($stack = array()) {
function match_IfArgumentPortion ($stack = array()) {
$matchrule = "IfArgumentPortion"; $result = $this->construct($matchrule, $matchrule, null);
$_190 = NULL;
do {
@ -1329,13 +1334,13 @@ class SSTemplateParser extends Parser {
public function IfArgumentPortion_STR(&$res, $sub) {
function IfArgumentPortion_STR(&$res, $sub) {
$res['php'] = $sub['php'];
}
/* BooleanOperator: "||" | "&&" */
protected $match_BooleanOperator_typestack = array('BooleanOperator');
public function match_BooleanOperator ($stack = array()) {
function match_BooleanOperator ($stack = array()) {
$matchrule = "BooleanOperator"; $result = $this->construct($matchrule, $matchrule, null);
$_195 = NULL;
do {
@ -1363,7 +1368,7 @@ class SSTemplateParser extends Parser {
/* IfArgument: :IfArgumentPortion ( < :BooleanOperator < :IfArgumentPortion )* */
protected $match_IfArgument_typestack = array('IfArgument');
public function match_IfArgument ($stack = array()) {
function match_IfArgument ($stack = array()) {
$matchrule = "IfArgument"; $result = $this->construct($matchrule, $matchrule, null);
$_204 = NULL;
do {
@ -1412,17 +1417,17 @@ class SSTemplateParser extends Parser {
public function IfArgument_IfArgumentPortion(&$res, $sub) {
function IfArgument_IfArgumentPortion(&$res, $sub) {
$res['php'] .= $sub['php'];
}
public function IfArgument_BooleanOperator(&$res, $sub) {
function IfArgument_BooleanOperator(&$res, $sub) {
$res['php'] .= $sub['text'];
}
/* IfPart: '<%' < 'if' [ :IfArgument > '%>' Template:$TemplateMatcher? */
protected $match_IfPart_typestack = array('IfPart');
public function match_IfPart ($stack = array()) {
function match_IfPart ($stack = array()) {
$matchrule = "IfPart"; $result = $this->construct($matchrule, $matchrule, null);
$_214 = NULL;
do {
@ -1465,7 +1470,7 @@ class SSTemplateParser extends Parser {
/* ElseIfPart: '<%' < 'else_if' [ :IfArgument > '%>' Template:$TemplateMatcher */
protected $match_ElseIfPart_typestack = array('ElseIfPart');
public function match_ElseIfPart ($stack = array()) {
function match_ElseIfPart ($stack = array()) {
$matchrule = "ElseIfPart"; $result = $this->construct($matchrule, $matchrule, null);
$_224 = NULL;
do {
@ -1501,7 +1506,7 @@ class SSTemplateParser extends Parser {
/* ElsePart: '<%' < 'else' > '%>' Template:$TemplateMatcher */
protected $match_ElsePart_typestack = array('ElsePart');
public function match_ElsePart ($stack = array()) {
function match_ElsePart ($stack = array()) {
$matchrule = "ElsePart"; $result = $this->construct($matchrule, $matchrule, null);
$_232 = NULL;
do {
@ -1529,7 +1534,7 @@ class SSTemplateParser extends Parser {
/* If: IfPart ElseIfPart* ElsePart? '<%' < 'end_if' > '%>' */
protected $match_If_typestack = array('If');
public function match_If ($stack = array()) {
function match_If ($stack = array()) {
$matchrule = "If"; $result = $this->construct($matchrule, $matchrule, null);
$_242 = NULL;
do {
@ -1579,21 +1584,21 @@ class SSTemplateParser extends Parser {
public function If_IfPart(&$res, $sub) {
function If_IfPart(&$res, $sub) {
$res['php'] =
'if (' . $sub['IfArgument']['php'] . ') { ' . PHP_EOL .
(isset($sub['Template']) ? $sub['Template']['php'] : '') . PHP_EOL .
'}';
}
public function If_ElseIfPart(&$res, $sub) {
function If_ElseIfPart(&$res, $sub) {
$res['php'] .=
'else if (' . $sub['IfArgument']['php'] . ') { ' . PHP_EOL .
$sub['Template']['php'] . PHP_EOL .
'}';
}
public function If_ElsePart(&$res, $sub) {
function If_ElsePart(&$res, $sub) {
$res['php'] .=
'else { ' . PHP_EOL .
$sub['Template']['php'] . PHP_EOL .
@ -1602,7 +1607,7 @@ class SSTemplateParser extends Parser {
/* Require: '<%' < 'require' [ Call:(Method:Word "(" < :CallArguments > ")") > '%>' */
protected $match_Require_typestack = array('Require');
public function match_Require ($stack = array()) {
function match_Require ($stack = array()) {
$matchrule = "Require"; $result = $this->construct($matchrule, $matchrule, null);
$_258 = NULL;
do {
@ -1663,7 +1668,7 @@ class SSTemplateParser extends Parser {
public function Require_Call(&$res, $sub) {
function Require_Call(&$res, $sub) {
$res['php'] = "Requirements::".$sub['Method']['text'].'('.$sub['CallArguments']['php'].');';
}
@ -1676,7 +1681,7 @@ class SSTemplateParser extends Parser {
:Lookup
) */
protected $match_CacheBlockArgument_typestack = array('CacheBlockArgument');
public function match_CacheBlockArgument ($stack = array()) {
function match_CacheBlockArgument ($stack = array()) {
$matchrule = "CacheBlockArgument"; $result = $this->construct($matchrule, $matchrule, null);
$_278 = NULL;
do {
@ -1773,21 +1778,21 @@ class SSTemplateParser extends Parser {
public function CacheBlockArgument_DollarMarkedLookup(&$res, $sub) {
function CacheBlockArgument_DollarMarkedLookup(&$res, $sub) {
$res['php'] = $sub['Lookup']['php'];
}
public function CacheBlockArgument_QuotedString(&$res, $sub) {
function CacheBlockArgument_QuotedString(&$res, $sub) {
$res['php'] = "'" . str_replace("'", "\\'", $sub['String']['text']) . "'";
}
public function CacheBlockArgument_Lookup(&$res, $sub) {
function CacheBlockArgument_Lookup(&$res, $sub) {
$res['php'] = $sub['php'];
}
/* CacheBlockArguments: CacheBlockArgument ( < "," < CacheBlockArgument )* */
protected $match_CacheBlockArguments_typestack = array('CacheBlockArguments');
public function match_CacheBlockArguments ($stack = array()) {
function match_CacheBlockArguments ($stack = array()) {
$matchrule = "CacheBlockArguments"; $result = $this->construct($matchrule, $matchrule, null);
$_287 = NULL;
do {
@ -1831,16 +1836,17 @@ class SSTemplateParser extends Parser {
public function CacheBlockArguments_CacheBlockArgument(&$res, $sub) {
function CacheBlockArguments_CacheBlockArgument(&$res, $sub) {
if (!empty($res['php'])) $res['php'] .= ".'_'.";
else $res['php'] = '';
$res['php'] .= str_replace('$$FINAL', 'XML_val', $sub['php']);
}
/* CacheBlockTemplate: (Comment | Translate | If | Require | OldI18NTag | Include | ClosedBlock | OpenBlock | MalformedBlock | Injection | Text)+ */
/* CacheBlockTemplate: (Comment | Translate | If | Require | OldI18NTag | Include | ClosedBlock |
OpenBlock | MalformedBlock | Injection | Text)+ */
protected $match_CacheBlockTemplate_typestack = array('CacheBlockTemplate','Template');
public function match_CacheBlockTemplate ($stack = array()) {
function match_CacheBlockTemplate ($stack = array()) {
$matchrule = "CacheBlockTemplate"; $result = $this->construct($matchrule, $matchrule, array('TemplateMatcher' => 'CacheRestrictedTemplate'));
$count = 0;
while (true) {
@ -2058,7 +2064,7 @@ class SSTemplateParser extends Parser {
Template:$TemplateMatcher?
'<%' < 'end_' ("uncached"|"cached"|"cacheblock") > '%>' */
protected $match_UncachedBlock_typestack = array('UncachedBlock');
public function match_UncachedBlock ($stack = array()) {
function match_UncachedBlock ($stack = array()) {
$matchrule = "UncachedBlock"; $result = $this->construct($matchrule, $matchrule, null);
$_368 = NULL;
do {
@ -2209,13 +2215,14 @@ class SSTemplateParser extends Parser {
public function UncachedBlock_Template(&$res, $sub){
function UncachedBlock_Template(&$res, $sub){
$res['php'] = $sub['php'];
}
/* CacheRestrictedTemplate: (Comment | Translate | If | Require | CacheBlock | UncachedBlock | OldI18NTag | Include | ClosedBlock | OpenBlock | MalformedBlock | Injection | Text)+ */
/* CacheRestrictedTemplate: (Comment | Translate | If | Require | CacheBlock | UncachedBlock | OldI18NTag | Include | ClosedBlock |
OpenBlock | MalformedBlock | Injection | Text)+ */
protected $match_CacheRestrictedTemplate_typestack = array('CacheRestrictedTemplate','Template');
public function match_CacheRestrictedTemplate ($stack = array()) {
function match_CacheRestrictedTemplate ($stack = array()) {
$matchrule = "CacheRestrictedTemplate"; $result = $this->construct($matchrule, $matchrule, null);
$count = 0;
while (true) {
@ -2463,20 +2470,23 @@ class SSTemplateParser extends Parser {
public function CacheRestrictedTemplate_CacheBlock(&$res, $sub) {
throw new SSTemplateParseException('You cant have cache blocks nested within with, loop or control blocks that are within cache blocks', $this);
function CacheRestrictedTemplate_CacheBlock(&$res, $sub) {
throw new SSTemplateParseException('You cant have cache blocks nested within with, loop or control blocks ' .
'that are within cache blocks', $this);
}
public function CacheRestrictedTemplate_UncachedBlock(&$res, $sub) {
throw new SSTemplateParseException('You cant have uncache blocks nested within with, loop or control blocks that are within cache blocks', $this);
function CacheRestrictedTemplate_UncachedBlock(&$res, $sub) {
throw new SSTemplateParseException('You cant have uncache blocks nested within with, loop or control blocks ' .
'that are within cache blocks', $this);
}
/* CacheBlock:
'<%' < CacheTag:("cached"|"cacheblock") < (CacheBlockArguments)? ( < Conditional:("if"|"unless") > Condition:IfArgument )? > '%>'
'<%' < CacheTag:("cached"|"cacheblock") < (CacheBlockArguments)? ( < Conditional:("if"|"unless") >
Condition:IfArgument )? > '%>'
(CacheBlock | UncachedBlock | CacheBlockTemplate)*
'<%' < 'end_' ("cached"|"uncached"|"cacheblock") > '%>' */
protected $match_CacheBlock_typestack = array('CacheBlock');
public function match_CacheBlock ($stack = array()) {
function match_CacheBlock ($stack = array()) {
$matchrule = "CacheBlock"; $result = $this->construct($matchrule, $matchrule, null);
$_475 = NULL;
do {
@ -2710,31 +2720,33 @@ class SSTemplateParser extends Parser {
public function CacheBlock__construct(&$res){
function CacheBlock__construct(&$res){
$res['subblocks'] = 0;
}
public function CacheBlock_CacheBlockArguments(&$res, $sub){
function CacheBlock_CacheBlockArguments(&$res, $sub){
$res['key'] = !empty($sub['php']) ? $sub['php'] : '';
}
public function CacheBlock_Condition(&$res, $sub){
function CacheBlock_Condition(&$res, $sub){
$res['condition'] = ($res['Conditional']['text'] == 'if' ? '(' : '!(') . $sub['php'] . ') && ';
}
public function CacheBlock_CacheBlock(&$res, $sub){
function CacheBlock_CacheBlock(&$res, $sub){
$res['php'] .= $sub['php'];
}
public function CacheBlock_UncachedBlock(&$res, $sub){
function CacheBlock_UncachedBlock(&$res, $sub){
$res['php'] .= $sub['php'];
}
public function CacheBlock_CacheBlockTemplate(&$res, $sub){
function CacheBlock_CacheBlockTemplate(&$res, $sub){
// Get the block counter
$block = ++$res['subblocks'];
// Build the key for this block from the passed cache key, the block index, and the sha hash of the template itself
$key = "'" . sha1($sub['php']) . (isset($res['key']) && $res['key'] ? "_'.sha1(".$res['key'].")" : "'") . ".'_$block'";
// Build the key for this block from the passed cache key, the block index, and the sha hash of the template
// itself
$key = "'" . sha1($sub['php']) . (isset($res['key']) && $res['key'] ? "_'.sha1(".$res['key'].")" : "'") .
".'_$block'";
// Get any condition
$condition = isset($res['condition']) ? $res['condition'] : '';
@ -2747,7 +2759,7 @@ class SSTemplateParser extends Parser {
/* OldTPart: "_t" N "(" N QuotedString (N "," N CallArguments)? N ")" N (";")? */
protected $match_OldTPart_typestack = array('OldTPart');
public function match_OldTPart ($stack = array()) {
function match_OldTPart ($stack = array()) {
$matchrule = "OldTPart"; $result = $this->construct($matchrule, $matchrule, null);
$_494 = NULL;
do {
@ -2841,7 +2853,7 @@ class SSTemplateParser extends Parser {
/* N: / [\s\n]* / */
protected $match_N_typestack = array('N');
public function match_N ($stack = array()) {
function match_N ($stack = array()) {
$matchrule = "N"; $result = $this->construct($matchrule, $matchrule, null);
if (( $subres = $this->rx( '/ [\s\n]* /' ) ) !== FALSE) {
$result["text"] .= $subres;
@ -2852,11 +2864,11 @@ class SSTemplateParser extends Parser {
public function OldTPart__construct(&$res) {
function OldTPart__construct(&$res) {
$res['php'] = "_t(";
}
public function OldTPart_QuotedString(&$res, $sub) {
function OldTPart_QuotedString(&$res, $sub) {
$entity = $sub['String']['text'];
if (strpos($entity, '.') === false) {
$res['php'] .= "\$scope->XML_val('I18NNamespace').'.$entity'";
@ -2866,17 +2878,17 @@ class SSTemplateParser extends Parser {
}
}
public function OldTPart_CallArguments(&$res, $sub) {
function OldTPart_CallArguments(&$res, $sub) {
$res['php'] .= ',' . $sub['php'];
}
public function OldTPart__finalise(&$res) {
function OldTPart__finalise(&$res) {
$res['php'] .= ')';
}
/* OldTTag: "<%" < OldTPart > "%>" */
protected $match_OldTTag_typestack = array('OldTTag');
public function match_OldTTag ($stack = array()) {
function match_OldTTag ($stack = array()) {
$matchrule = "OldTTag"; $result = $this->construct($matchrule, $matchrule, null);
$_502 = NULL;
do {
@ -2899,13 +2911,13 @@ class SSTemplateParser extends Parser {
public function OldTTag_OldTPart(&$res, $sub) {
function OldTTag_OldTPart(&$res, $sub) {
$res['php'] = $sub['php'];
}
/* OldSprintfTag: "<%" < "sprintf" < "(" < OldTPart < "," < CallArguments > ")" > "%>" */
protected $match_OldSprintfTag_typestack = array('OldSprintfTag');
public function match_OldSprintfTag ($stack = array()) {
function match_OldSprintfTag ($stack = array()) {
$matchrule = "OldSprintfTag"; $result = $this->construct($matchrule, $matchrule, null);
$_519 = NULL;
do {
@ -2954,21 +2966,21 @@ class SSTemplateParser extends Parser {
public function OldSprintfTag__construct(&$res) {
function OldSprintfTag__construct(&$res) {
$res['php'] = "sprintf(";
}
public function OldSprintfTag_OldTPart(&$res, $sub) {
function OldSprintfTag_OldTPart(&$res, $sub) {
$res['php'] .= $sub['php'];
}
public function OldSprintfTag_CallArguments(&$res, $sub) {
function OldSprintfTag_CallArguments(&$res, $sub) {
$res['php'] .= ',' . $sub['php'] . ')';
}
/* OldI18NTag: OldSprintfTag | OldTTag */
protected $match_OldI18NTag_typestack = array('OldI18NTag');
public function match_OldI18NTag ($stack = array()) {
function match_OldI18NTag ($stack = array()) {
$matchrule = "OldI18NTag"; $result = $this->construct($matchrule, $matchrule, null);
$_524 = NULL;
do {
@ -2999,13 +3011,13 @@ class SSTemplateParser extends Parser {
public function OldI18NTag_STR(&$res, $sub) {
function OldI18NTag_STR(&$res, $sub) {
$res['php'] = '$val .= ' . $sub['php'] . ';';
}
/* NamedArgument: Name:Word "=" Value:Argument */
protected $match_NamedArgument_typestack = array('NamedArgument');
public function match_NamedArgument ($stack = array()) {
function match_NamedArgument ($stack = array()) {
$matchrule = "NamedArgument"; $result = $this->construct($matchrule, $matchrule, null);
$_529 = NULL;
do {
@ -3035,17 +3047,18 @@ class SSTemplateParser extends Parser {
public function NamedArgument_Name(&$res, $sub) {
function NamedArgument_Name(&$res, $sub) {
$res['php'] = "'" . $sub['text'] . "' => ";
}
public function NamedArgument_Value(&$res, $sub) {
$res['php'] .= ($sub['ArgumentMode'] == 'default') ? $sub['string_php'] : str_replace('$$FINAL', 'XML_val', $sub['php']);
function NamedArgument_Value(&$res, $sub) {
$res['php'] .= ($sub['ArgumentMode'] == 'default') ? $sub['string_php']
: str_replace('$$FINAL', 'XML_val', $sub['php']);
}
/* Include: "<%" < "include" < Template:Word < (NamedArgument ( < "," < NamedArgument )*)? > "%>" */
protected $match_Include_typestack = array('Include');
public function match_Include ($stack = array()) {
function match_Include ($stack = array()) {
$matchrule = "Include"; $result = $this->construct($matchrule, $matchrule, null);
$_548 = NULL;
do {
@ -3120,23 +3133,24 @@ class SSTemplateParser extends Parser {
public function Include__construct(&$res){
function Include__construct(&$res){
$res['arguments'] = array();
}
public function Include_Template(&$res, $sub){
function Include_Template(&$res, $sub){
$res['template'] = "'" . $sub['text'] . "'";
}
public function Include_NamedArgument(&$res, $sub){
function Include_NamedArgument(&$res, $sub){
$res['arguments'][] = $sub['php'];
}
public function Include__finalise(&$res){
function Include__finalise(&$res){
$template = $res['template'];
$arguments = $res['arguments'];
$res['php'] = '$val .= SSViewer::execute_template('.$template.', $scope->getItem(), array('.implode(',', $arguments)."));\n";
$res['php'] = '$val .= SSViewer::execute_template('.$template.', $scope->getItem(), array(' .
implode(',', $arguments)."));\n";
if($this->includeDebuggingComments) { // Add include filename comments on dev sites
$res['php'] =
@ -3148,7 +3162,7 @@ class SSTemplateParser extends Parser {
/* BlockArguments: :Argument ( < "," < :Argument)* */
protected $match_BlockArguments_typestack = array('BlockArguments');
public function match_BlockArguments ($stack = array()) {
function match_BlockArguments ($stack = array()) {
$matchrule = "BlockArguments"; $result = $this->construct($matchrule, $matchrule, null);
$_557 = NULL;
do {
@ -3197,7 +3211,7 @@ class SSTemplateParser extends Parser {
/* NotBlockTag: "end_" | (("if" | "else_if" | "else" | "require" | "cached" | "uncached" | "cacheblock" | "include")]) */
protected $match_NotBlockTag_typestack = array('NotBlockTag');
public function match_NotBlockTag ($stack = array()) {
function match_NotBlockTag ($stack = array()) {
$matchrule = "NotBlockTag"; $result = $this->construct($matchrule, $matchrule, null);
$_595 = NULL;
do {
@ -3349,9 +3363,10 @@ class SSTemplateParser extends Parser {
}
/* ClosedBlock: '<%' < !NotBlockTag BlockName:Word ( [ :BlockArguments ] )? > Zap:'%>' Template:$TemplateMatcher? '<%' < 'end_' '$BlockName' > '%>' */
/* ClosedBlock: '<%' < !NotBlockTag BlockName:Word ( [ :BlockArguments ] )? > Zap:'%>' Template:$TemplateMatcher?
'<%' < 'end_' '$BlockName' > '%>' */
protected $match_ClosedBlock_typestack = array('ClosedBlock');
public function match_ClosedBlock ($stack = array()) {
function match_ClosedBlock ($stack = array()) {
$matchrule = "ClosedBlock"; $result = $this->construct($matchrule, $matchrule, null);
$_615 = NULL;
do {
@ -3459,11 +3474,11 @@ class SSTemplateParser extends Parser {
* the php result of this block as it's return value, or throw an error if incorrect arguments were passed.
*/
public function ClosedBlock__construct(&$res) {
function ClosedBlock__construct(&$res) {
$res['ArgumentCount'] = 0;
}
public function ClosedBlock_BlockArguments(&$res, $sub) {
function ClosedBlock_BlockArguments(&$res, $sub) {
if (isset($sub['Argument']['ArgumentMode'])) {
$res['Arguments'] = array($sub['Argument']);
$res['ArgumentCount'] = 1;
@ -3474,22 +3489,24 @@ class SSTemplateParser extends Parser {
}
}
public function ClosedBlock__finalise(&$res) {
function ClosedBlock__finalise(&$res) {
$blockname = $res['BlockName']['text'];
$method = 'ClosedBlock_Handle_'.$blockname;
if (method_exists($this, $method)) $res['php'] = $this->$method($res);
else {
throw new SSTemplateParseException('Unknown closed block "'.$blockname.'" encountered. Perhaps you are not supposed to close this block, or have mis-spelled it?', $this);
throw new SSTemplateParseException('Unknown closed block "'.$blockname.'" encountered. Perhaps you are ' .
'not supposed to close this block, or have mis-spelled it?', $this);
}
}
/**
* This is an example of a block handler function. This one handles the loop tag.
*/
public function ClosedBlock_Handle_Loop(&$res) {
function ClosedBlock_Handle_Loop(&$res) {
if ($res['ArgumentCount'] > 1) {
throw new SSTemplateParseException('Either no or too many arguments in control block. Must be one argument only.', $this);
throw new SSTemplateParseException('Either no or too many arguments in control block. Must be one ' .
'argument only.', $this);
}
//loop without arguments loops on the current scope
@ -3500,7 +3517,8 @@ class SSTemplateParser extends Parser {
if ($arg['ArgumentMode'] == 'string') {
throw new SSTemplateParseException('Control block cant take string as argument.', $this);
}
$on = str_replace('$$FINAL', 'obj', ($arg['ArgumentMode'] == 'default') ? $arg['lookup_php'] : $arg['php']);
$on = str_replace('$$FINAL', 'obj',
($arg['ArgumentMode'] == 'default') ? $arg['lookup_php'] : $arg['php']);
}
return
@ -3513,7 +3531,7 @@ class SSTemplateParser extends Parser {
* The deprecated closed block handler for control blocks
* @deprecated
*/
public function ClosedBlock_Handle_Control(&$res) {
function ClosedBlock_Handle_Control(&$res) {
Deprecation::notice('3.1', 'Use <% with %> or <% loop %> instead.');
return $this->ClosedBlock_Handle_Loop($res);
}
@ -3521,9 +3539,10 @@ class SSTemplateParser extends Parser {
/**
* The closed block handler for with blocks
*/
public function ClosedBlock_Handle_With(&$res) {
function ClosedBlock_Handle_With(&$res) {
if ($res['ArgumentCount'] != 1) {
throw new SSTemplateParseException('Either no or too many arguments in with block. Must be one argument only.', $this);
throw new SSTemplateParseException('Either no or too many arguments in with block. Must be one ' .
'argument only.', $this);
}
$arg = $res['Arguments'][0];
@ -3540,7 +3559,7 @@ class SSTemplateParser extends Parser {
/* OpenBlock: '<%' < !NotBlockTag BlockName:Word ( [ :BlockArguments ] )? > '%>' */
protected $match_OpenBlock_typestack = array('OpenBlock');
public function match_OpenBlock ($stack = array()) {
function match_OpenBlock ($stack = array()) {
$matchrule = "OpenBlock"; $result = $this->construct($matchrule, $matchrule, null);
$_628 = NULL;
do {
@ -3602,11 +3621,11 @@ class SSTemplateParser extends Parser {
public function OpenBlock__construct(&$res) {
function OpenBlock__construct(&$res) {
$res['ArgumentCount'] = 0;
}
public function OpenBlock_BlockArguments(&$res, $sub) {
function OpenBlock_BlockArguments(&$res, $sub) {
if (isset($sub['Argument']['ArgumentMode'])) {
$res['Arguments'] = array($sub['Argument']);
$res['ArgumentCount'] = 1;
@ -3617,7 +3636,7 @@ class SSTemplateParser extends Parser {
}
}
public function OpenBlock__finalise(&$res) {
function OpenBlock__finalise(&$res) {
$blockname = $res['BlockName']['text'];
$method = 'OpenBlock_Handle_'.$blockname;
@ -3630,7 +3649,7 @@ class SSTemplateParser extends Parser {
/**
* This is an open block handler, for the <% debug %> utility tag
*/
public function OpenBlock_Handle_Debug(&$res) {
function OpenBlock_Handle_Debug(&$res) {
if ($res['ArgumentCount'] == 0) return '$scope->debug();';
else if ($res['ArgumentCount'] == 1) {
$arg = $res['Arguments'][0];
@ -3648,7 +3667,7 @@ class SSTemplateParser extends Parser {
/**
* This is an open block handler, for the <% base_tag %> tag
*/
public function OpenBlock_Handle_Base_tag(&$res) {
function OpenBlock_Handle_Base_tag(&$res) {
if ($res['ArgumentCount'] != 0) throw new SSTemplateParseException('Base_tag takes no arguments', $this);
return '$val .= SSViewer::get_base_tag($val);';
}
@ -3656,14 +3675,14 @@ class SSTemplateParser extends Parser {
/**
* This is an open block handler, for the <% current_page %> tag
*/
public function OpenBlock_Handle_Current_page(&$res) {
function OpenBlock_Handle_Current_page(&$res) {
if ($res['ArgumentCount'] != 0) throw new SSTemplateParseException('Current_page takes no arguments', $this);
return '$val .= $_SERVER[SCRIPT_URL];';
}
/* MismatchedEndBlock: '<%' < 'end_' :Word > '%>' */
protected $match_MismatchedEndBlock_typestack = array('MismatchedEndBlock');
public function match_MismatchedEndBlock ($stack = array()) {
function match_MismatchedEndBlock ($stack = array()) {
$matchrule = "MismatchedEndBlock"; $result = $this->construct($matchrule, $matchrule, null);
$_636 = NULL;
do {
@ -3690,14 +3709,14 @@ class SSTemplateParser extends Parser {
public function MismatchedEndBlock__finalise(&$res) {
function MismatchedEndBlock__finalise(&$res) {
$blockname = $res['Word']['text'];
throw new SSTemplateParseException('Unexpected close tag end_'.$blockname.' encountered. Perhaps you have mis-nested blocks, or have mis-spelled a tag?', $this);
}
/* MalformedOpenTag: '<%' < !NotBlockTag Tag:Word !( ( [ :BlockArguments ] )? > '%>' ) */
protected $match_MalformedOpenTag_typestack = array('MalformedOpenTag');
public function match_MalformedOpenTag ($stack = array()) {
function match_MalformedOpenTag ($stack = array()) {
$matchrule = "MalformedOpenTag"; $result = $this->construct($matchrule, $matchrule, null);
$_651 = NULL;
do {
@ -3775,14 +3794,14 @@ class SSTemplateParser extends Parser {
public function MalformedOpenTag__finalise(&$res) {
function MalformedOpenTag__finalise(&$res) {
$tag = $res['Tag']['text'];
throw new SSTemplateParseException("Malformed opening block tag $tag. Perhaps you have tried to use operators?", $this);
}
/* MalformedCloseTag: '<%' < Tag:('end_' :Word ) !( > '%>' ) */
protected $match_MalformedCloseTag_typestack = array('MalformedCloseTag');
public function match_MalformedCloseTag ($stack = array()) {
function match_MalformedCloseTag ($stack = array()) {
$matchrule = "MalformedCloseTag"; $result = $this->construct($matchrule, $matchrule, null);
$_663 = NULL;
do {
@ -3839,14 +3858,14 @@ class SSTemplateParser extends Parser {
public function MalformedCloseTag__finalise(&$res) {
function MalformedCloseTag__finalise(&$res) {
$tag = $res['Tag']['text'];
throw new SSTemplateParseException("Malformed closing block tag $tag. Perhaps you have tried to pass an argument to one?", $this);
}
/* MalformedBlock: MalformedOpenTag | MalformedCloseTag */
protected $match_MalformedBlock_typestack = array('MalformedBlock');
public function match_MalformedBlock ($stack = array()) {
function match_MalformedBlock ($stack = array()) {
$matchrule = "MalformedBlock"; $result = $this->construct($matchrule, $matchrule, null);
$_668 = NULL;
do {
@ -3880,7 +3899,7 @@ class SSTemplateParser extends Parser {
/* Comment: "<%--" (!"--%>" /./)+ "--%>" */
protected $match_Comment_typestack = array('Comment');
public function match_Comment ($stack = array()) {
function match_Comment ($stack = array()) {
$matchrule = "Comment"; $result = $this->construct($matchrule, $matchrule, null);
$_676 = NULL;
do {
@ -3931,13 +3950,14 @@ class SSTemplateParser extends Parser {
public function Comment__construct(&$res) {
function Comment__construct(&$res) {
$res['php'] = '';
}
/* TopTemplate: (Comment | Translate | If | Require | CacheBlock | UncachedBlock | OldI18NTag | Include | ClosedBlock | OpenBlock | MalformedBlock | MismatchedEndBlock | Injection | Text)+ */
/* TopTemplate: (Comment | Translate | If | Require | CacheBlock | UncachedBlock | OldI18NTag | Include | ClosedBlock |
OpenBlock | MalformedBlock | MismatchedEndBlock | Injection | Text)+ */
protected $match_TopTemplate_typestack = array('TopTemplate','Template');
public function match_TopTemplate ($stack = array()) {
function match_TopTemplate ($stack = array()) {
$matchrule = "TopTemplate"; $result = $this->construct($matchrule, $matchrule, array('TemplateMatcher' => 'Template'));
$count = 0;
while (true) {
@ -4207,7 +4227,7 @@ class SSTemplateParser extends Parser {
/**
* The TopTemplate also includes the opening stanza to start off the template
*/
public function TopTemplate__construct(&$res) {
function TopTemplate__construct(&$res) {
$res['php'] = "<?php" . PHP_EOL;
}
@ -4220,7 +4240,7 @@ class SSTemplateParser extends Parser {
'{$' !(/[A-Za-z_]/)
)+ */
protected $match_Text_typestack = array('Text');
public function match_Text ($stack = array()) {
function match_Text ($stack = array()) {
$matchrule = "Text"; $result = $this->construct($matchrule, $matchrule, null);
$count = 0;
while (true) {
@ -4421,7 +4441,7 @@ class SSTemplateParser extends Parser {
/**
* We convert text
*/
public function Text__finalise(&$res) {
function Text__finalise(&$res) {
$text = $res['text'];
// Unescape any escaped characters in the text, then put back escapes for any single quotes and backslashes
@ -4452,7 +4472,7 @@ class SSTemplateParser extends Parser {
* @param bool $includeDebuggingComments - True is debugging comments should be included in the output
* @return mixed|string - The php that, when executed (via include or exec) will behave as per the template source
*/
public static function compileString($string, $templateName = "", $includeDebuggingComments=false) {
static function compileString($string, $templateName = "", $includeDebuggingComments=false) {
if (!trim($string)) {
$code = '';
}
@ -4495,7 +4515,7 @@ class SSTemplateParser extends Parser {
* @param $template - A file path that contains template source code
* @return mixed|string - The php that, when executed (via include or exec) will behave as per the template source
*/
public static function compileFile($template) {
static function compileFile($template) {
return self::compileString(file_get_contents($template), $template);
}
}

View File

@ -3,11 +3,14 @@
/*!* !insert_autogen_warning */
/*!* !silent
This is the uncompiled parser for the SilverStripe template language, PHP with special comments that define the parser.
It gets run through the php-peg parser compiler to have those comments turned into code that match parts of the template language,
producing the executable version SSTemplateParser.php
This is the uncompiled parser for the SilverStripe template language, PHP with special comments that define the
parser.
To recompile after changing this file, run this from the 'framework/view' directory via command line (in most cases this is: sapphire/view):
It gets run through the php-peg parser compiler to have those comments turned into code that match parts of the
template language, producing the executable version SSTemplateParser.php
To recompile after changing this file, run this from the 'framework/view' directory via command line (in most cases
this is: sapphire/view):
php ../thirdparty/php-peg/cli.php SSTemplateParser.php.inc > SSTemplateParser.php
@ -35,8 +38,9 @@ else {
}
/**
This is the exception raised when failing to parse a template. Note that we don't currently do any static analysis, so we can't know
if the template will run, just if it's malformed. It also won't catch mistakes that still look valid.
* This is the exception raised when failing to parse a template. Note that we don't currently do any static analysis,
* so we can't know if the template will run, just if it's malformed. It also won't catch mistakes that still look
* valid.
*/
class SSTemplateParseException extends Exception {
@ -52,33 +56,35 @@ class SSTemplateParseException extends Exception {
}
/**
This is the parser for the SilverStripe template language. It gets called on a string and uses a php-peg parser to match
that string against the language structure, building up the PHP code to execute that structure as it parses
The $result array that is built up as part of the parsing (see thirdparty/php-peg/README.md for more on how parsers
build results) has one special member, 'php', which contains the php equivalent of that part of the template tree.
Some match rules generate alternate php, or other variations, so check the per-match documentation too.
Terms used:
Marked: A string or lookup in the template that has been explictly marked as such - lookups by prepending with "$"
(like $Foo.Bar), strings by wrapping with single or double quotes ('Foo' or "Foo")
Bare: The opposite of marked. An argument that has to has it's type inferred by usage and 2.4 defaults.
Example of using a bare argument for a loop block: <% loop Foo %>
Block: One of two SS template structures. The special characters "<%" and "%>" are used to wrap the opening and
(required or forbidden depending on which block exactly) closing block marks.
Open Block: An SS template block that doesn't wrap any content or have a closing end tag (in fact, a closing end tag is
forbidden)
Closed Block: An SS template block that wraps content, and requires a counterpart <% end_blockname %> tag
Angle Bracket: angle brackets "<" and ">" are used to eat whitespace between template elements
N: eats white space including newlines (using in legacy _t support)
* This is the parser for the SilverStripe template language. It gets called on a string and uses a php-peg parser
* to match that string against the language structure, building up the PHP code to execute that structure as it
* parses
*
* The $result array that is built up as part of the parsing (see thirdparty/php-peg/README.md for more on how
* parsers build results) has one special member, 'php', which contains the php equivalent of that part of the
* template tree.
*
* Some match rules generate alternate php, or other variations, so check the per-match documentation too.
*
* Terms used:
*
* Marked: A string or lookup in the template that has been explictly marked as such - lookups by prepending with
* "$" (like $Foo.Bar), strings by wrapping with single or double quotes ('Foo' or "Foo")
*
* Bare: The opposite of marked. An argument that has to has it's type inferred by usage and 2.4 defaults.
*
* Example of using a bare argument for a loop block: <% loop Foo %>
*
* Block: One of two SS template structures. The special characters "<%" and "%>" are used to wrap the opening and
* (required or forbidden depending on which block exactly) closing block marks.
*
* Open Block: An SS template block that doesn't wrap any content or have a closing end tag (in fact, a closing end
* tag is forbidden)
*
* Closed Block: An SS template block that wraps content, and requires a counterpart <% end_blockname %> tag
*
* Angle Bracket: angle brackets "<" and ">" are used to eat whitespace between template elements
* N: eats white space including newlines (using in legacy _t support)
*/
class SSTemplateParser extends Parser {
@ -99,11 +105,12 @@ class SSTemplateParser extends Parser {
/*!* SSTemplateParser
# Template is any structurally-complete portion of template (a full nested level in other words). It's the primary matcher,
# and is used by all enclosing blocks, as well as a base for the top level.
# Template is any structurally-complete portion of template (a full nested level in other words). It's the
# primary matcher, and is used by all enclosing blocks, as well as a base for the top level.
# Any new template elements need to be included in this list, if they are to work.
Template: (Comment | Translate | If | Require | CacheBlock | UncachedBlock | OldI18NTag | Include | ClosedBlock | OpenBlock | MalformedBlock | Injection | Text)+
Template: (Comment | Translate | If | Require | CacheBlock | UncachedBlock | OldI18NTag | Include | ClosedBlock |
OpenBlock | MalformedBlock | Injection | Text)+
*/
function Template_STR(&$res, $sub) {
$res['php'] .= $sub['php'] . PHP_EOL ;
@ -122,13 +129,14 @@ class SSTemplateParser extends Parser {
*/
/**
* Values are bare words in templates, but strings in PHP. We rely on PHP's type conversion to back-convert strings
* to numbers when needed.
* Values are bare words in templates, but strings in PHP. We rely on PHP's type conversion to back-convert
* strings to numbers when needed.
*/
function CallArguments_Argument(&$res, $sub) {
if (!empty($res['php'])) $res['php'] .= ', ';
$res['php'] .= ($sub['ArgumentMode'] == 'default') ? $sub['string_php'] : str_replace('$$FINAL', 'XML_val', $sub['php']);
$res['php'] .= ($sub['ArgumentMode'] == 'default') ? $sub['string_php'] :
str_replace('$$FINAL', 'XML_val', $sub['php']);
}
/*!*
@ -138,9 +146,9 @@ class SSTemplateParser extends Parser {
Call: Method:Word ( "(" < :CallArguments? > ")" )?
# A lookup is a lookup of a value on the current scope object. It's a sequence of calls seperated by "." characters
# This final call in the sequence needs handling specially, as different structures need different sorts of values,
# which require a different final method to be called to get the right return value
# A lookup is a lookup of a value on the current scope object. It's a sequence of calls seperated by "."
# characters. This final call in the sequence needs handling specially, as different structures need different
# sorts of values, which require a different final method to be called to get the right return value
LookupStep: :Call &"."
LastLookupStep: :Call
@ -186,7 +194,8 @@ class SSTemplateParser extends Parser {
# <%t Entity DefaultString is Context name1=string name2=$functionCall
# (This is a new way to call translatable strings. The parser transforms this into a call to the _t() method)
Translate: "<%t" < Entity < (Default:QuotedString)? < (!("is" "=") < "is" < Context:QuotedString)? < (InjectionVariables)? > "%>"
Translate: "<%t" < Entity < (Default:QuotedString)? < (!("is" "=") < "is" < Context:QuotedString)? <
(InjectionVariables)? > "%>"
InjectionVariables: (< InjectionName:Word "=" Argument)+
Entity: / [A-Za-z_] [\w\.]* /
*/
@ -268,14 +277,14 @@ class SSTemplateParser extends Parser {
# In order to support 2.4's base syntax, we also need to detect free strings - strings not surrounded by
# quotes, and containing spaces or punctuation, but supported as a single string. We support almost as flexible
# a string as 2.4 - we don't attempt to determine the closing character by context, but just break on any character
# which, in some context, would indicate the end of a free string, regardless of if we're actually in that context
# or not
# a string as 2.4 - we don't attempt to determine the closing character by context, but just break on any
# character which, in some context, would indicate the end of a free string, regardless of if we're actually in
# that context or not
FreeString: /[^,)%!=|&]+/
# An argument - either a marked value, or a bare value, prefering lookup matching on the bare value over freestring
# matching as long as that would give a successful parse
# An argument - either a marked value, or a bare value, prefering lookup matching on the bare value over
# freestring matching as long as that would give a successful parse
Argument:
:DollarMarkedLookup |
@ -384,7 +393,8 @@ class SSTemplateParser extends Parser {
/*!*
# if and else_if arguments can be combined via these two boolean operators. No precendence overriding is supported
# if and else_if arguments can be combined via these two boolean operators. No precendence overriding is
# supported
BooleanOperator: "||" | "&&"
@ -507,18 +517,21 @@ class SSTemplateParser extends Parser {
CacheRestrictedTemplate extends Template
*/
function CacheRestrictedTemplate_CacheBlock(&$res, $sub) {
throw new SSTemplateParseException('You cant have cache blocks nested within with, loop or control blocks that are within cache blocks', $this);
throw new SSTemplateParseException('You cant have cache blocks nested within with, loop or control blocks ' .
'that are within cache blocks', $this);
}
function CacheRestrictedTemplate_UncachedBlock(&$res, $sub) {
throw new SSTemplateParseException('You cant have uncache blocks nested within with, loop or control blocks that are within cache blocks', $this);
throw new SSTemplateParseException('You cant have uncache blocks nested within with, loop or control blocks ' .
'that are within cache blocks', $this);
}
/*!*
# The partial caching block
CacheBlock:
'<%' < CacheTag:("cached"|"cacheblock") < (CacheBlockArguments)? ( < Conditional:("if"|"unless") > Condition:IfArgument )? > '%>'
'<%' < CacheTag:("cached"|"cacheblock") < (CacheBlockArguments)? ( < Conditional:("if"|"unless") >
Condition:IfArgument )? > '%>'
(CacheBlock | UncachedBlock | CacheBlockTemplate)*
'<%' < 'end_' ("cached"|"uncached"|"cacheblock") > '%>'
@ -546,8 +559,10 @@ class SSTemplateParser extends Parser {
function CacheBlock_CacheBlockTemplate(&$res, $sub){
// Get the block counter
$block = ++$res['subblocks'];
// Build the key for this block from the passed cache key, the block index, and the sha hash of the template itself
$key = "'" . sha1($sub['php']) . (isset($res['key']) && $res['key'] ? "_'.sha1(".$res['key'].")" : "'") . ".'_$block'";
// Build the key for this block from the passed cache key, the block index, and the sha hash of the template
// itself
$key = "'" . sha1($sub['php']) . (isset($res['key']) && $res['key'] ? "_'.sha1(".$res['key'].")" : "'") .
".'_$block'";
// Get any condition
$condition = isset($res['condition']) ? $res['condition'] : '';
@ -625,7 +640,8 @@ class SSTemplateParser extends Parser {
/*!*
# This matches either the old style sprintf(_t()) or _t() tags. As well as including the output portion of the
# php, this rule combines all the old i18n stuff into a single match rule to make it easy to not support these tags later
# php, this rule combines all the old i18n stuff into a single match rule to make it easy to not support these
# tags later
OldI18NTag: OldSprintfTag | OldTTag
@ -646,7 +662,8 @@ class SSTemplateParser extends Parser {
}
function NamedArgument_Value(&$res, $sub) {
$res['php'] .= ($sub['ArgumentMode'] == 'default') ? $sub['string_php'] : str_replace('$$FINAL', 'XML_val', $sub['php']);
$res['php'] .= ($sub['ArgumentMode'] == 'default') ? $sub['string_php']
: str_replace('$$FINAL', 'XML_val', $sub['php']);
}
/*!*
@ -672,7 +689,8 @@ class SSTemplateParser extends Parser {
$template = $res['template'];
$arguments = $res['arguments'];
$res['php'] = '$val .= SSViewer::execute_template('.$template.', $scope->getItem(), array('.implode(',', $arguments)."));\n";
$res['php'] = '$val .= SSViewer::execute_template('.$template.', $scope->getItem(), array(' .
implode(',', $arguments)."));\n";
if($this->includeDebuggingComments) { // Add include filename comments on dev sites
$res['php'] =
@ -699,7 +717,8 @@ class SSTemplateParser extends Parser {
# Match against closed blocks - blocks with an opening and a closing tag that surround some internal portion of
# template
ClosedBlock: '<%' < !NotBlockTag BlockName:Word ( [ :BlockArguments ] )? > Zap:'%>' Template:$TemplateMatcher? '<%' < 'end_' '$BlockName' > '%>'
ClosedBlock: '<%' < !NotBlockTag BlockName:Word ( [ :BlockArguments ] )? > Zap:'%>' Template:$TemplateMatcher?
'<%' < 'end_' '$BlockName' > '%>'
*/
/**
@ -737,7 +756,8 @@ class SSTemplateParser extends Parser {
$method = 'ClosedBlock_Handle_'.$blockname;
if (method_exists($this, $method)) $res['php'] = $this->$method($res);
else {
throw new SSTemplateParseException('Unknown closed block "'.$blockname.'" encountered. Perhaps you are not supposed to close this block, or have mis-spelled it?', $this);
throw new SSTemplateParseException('Unknown closed block "'.$blockname.'" encountered. Perhaps you are ' .
'not supposed to close this block, or have mis-spelled it?', $this);
}
}
@ -746,7 +766,8 @@ class SSTemplateParser extends Parser {
*/
function ClosedBlock_Handle_Loop(&$res) {
if ($res['ArgumentCount'] > 1) {
throw new SSTemplateParseException('Either no or too many arguments in control block. Must be one argument only.', $this);
throw new SSTemplateParseException('Either no or too many arguments in control block. Must be one ' .
'argument only.', $this);
}
//loop without arguments loops on the current scope
@ -757,7 +778,8 @@ class SSTemplateParser extends Parser {
if ($arg['ArgumentMode'] == 'string') {
throw new SSTemplateParseException('Control block cant take string as argument.', $this);
}
$on = str_replace('$$FINAL', 'obj', ($arg['ArgumentMode'] == 'default') ? $arg['lookup_php'] : $arg['php']);
$on = str_replace('$$FINAL', 'obj',
($arg['ArgumentMode'] == 'default') ? $arg['lookup_php'] : $arg['php']);
}
return
@ -780,7 +802,8 @@ class SSTemplateParser extends Parser {
*/
function ClosedBlock_Handle_With(&$res) {
if ($res['ArgumentCount'] != 1) {
throw new SSTemplateParseException('Either no or too many arguments in with block. Must be one argument only.', $this);
throw new SSTemplateParseException('Either no or too many arguments in with block. Must be one ' .
'argument only.', $this);
}
$arg = $res['Arguments'][0];
@ -823,7 +846,8 @@ class SSTemplateParser extends Parser {
$method = 'OpenBlock_Handle_'.$blockname;
if (method_exists($this, $method)) $res['php'] = $this->$method($res);
else {
throw new SSTemplateParseException('Unknown open block "'.$blockname.'" encountered. Perhaps you missed the closing tag or have mis-spelled it?', $this);
throw new SSTemplateParseException('Unknown open block "'.$blockname.'" encountered. Perhaps you missed ' .
' the closing tag or have mis-spelled it?', $this);
}
}
@ -871,7 +895,8 @@ class SSTemplateParser extends Parser {
*/
function MismatchedEndBlock__finalise(&$res) {
$blockname = $res['Word']['text'];
throw new SSTemplateParseException('Unexpected close tag end_'.$blockname.' encountered. Perhaps you have mis-nested blocks, or have mis-spelled a tag?', $this);
throw new SSTemplateParseException('Unexpected close tag end_' . $blockname .
' encountered. Perhaps you have mis-nested blocks, or have mis-spelled a tag?', $this);
}
/*!*
@ -883,7 +908,8 @@ class SSTemplateParser extends Parser {
*/
function MalformedOpenTag__finalise(&$res) {
$tag = $res['Tag']['text'];
throw new SSTemplateParseException("Malformed opening block tag $tag. Perhaps you have tried to use operators?", $this);
throw new SSTemplateParseException("Malformed opening block tag $tag. Perhaps you have tried to use operators?"
, $this);
}
/*!*
@ -895,7 +921,8 @@ class SSTemplateParser extends Parser {
*/
function MalformedCloseTag__finalise(&$res) {
$tag = $res['Tag']['text'];
throw new SSTemplateParseException("Malformed closing block tag $tag. Perhaps you have tried to pass an argument to one?", $this);
throw new SSTemplateParseException("Malformed closing block tag $tag. Perhaps you have tried to pass an " .
"argument to one?", $this);
}
/*!*
@ -960,10 +987,12 @@ class SSTemplateParser extends Parser {
$text = stripslashes($text);
$text = addcslashes($text, '\'\\');
// TODO: This is pretty ugly & gets applied on all files not just html. I wonder if we can make this non-dynamically calculated
// TODO: This is pretty ugly & gets applied on all files not just html. I wonder if we can make this
// non-dynamically calculated
$text = preg_replace(
'/href\s*\=\s*\"\#/',
'href="\' . (SSViewer::$options[\'rewriteHashlinks\'] ? strip_tags( $_SERVER[\'REQUEST_URI\'] ) : "") . \'#',
'href="\' . (SSViewer::$options[\'rewriteHashlinks\'] ? strip_tags( $_SERVER[\'REQUEST_URI\'] ) : "") .
\'#',
$text
);
@ -979,10 +1008,10 @@ class SSTemplateParser extends Parser {
*
* @static
* @throws SSTemplateParseException
* @param $string - The source of the template
* @param string $templateName - The name of the template, normally the filename the template source was loaded from
* @param bool $includeDebuggingComments - True is debugging comments should be included in the output
* @return mixed|string - The php that, when executed (via include or exec) will behave as per the template source
* @param $string The source of the template
* @param string $templateName The name of the template, normally the filename the template source was loaded from
* @param bool $includeDebuggingComments True is debugging comments should be included in the output
* @return mixed|string The php that, when executed (via include or exec) will behave as per the template source
*/
static function compileString($string, $templateName = "", $includeDebuggingComments=false) {
if (!trim($string)) {
@ -993,7 +1022,8 @@ class SSTemplateParser extends Parser {
$parser = new SSTemplateParser($string);
$parser->includeDebuggingComments = $includeDebuggingComments;
// Ignore UTF8 BOM at begining of string. TODO: Confirm this is needed, make sure SSViewer handles UTF (and other encodings) properly
// Ignore UTF8 BOM at begining of string. TODO: Confirm this is needed, make sure SSViewer handles UTF
// (and other encodings) properly
if(substr($string, 0,3) == pack("CCC", 0xef, 0xbb, 0xbf)) $parser->pos = 3;
// Match the source against the parser
@ -1006,12 +1036,14 @@ class SSTemplateParser extends Parser {
// Include top level debugging comments if desired
if($includeDebuggingComments && $templateName && stripos($code, "<?xml") === false) {
// If this template is a full HTML page, then put the comments just inside the HTML tag to prevent any IE glitches
// If this template is a full HTML page, then put the comments just inside the HTML tag to prevent any IE
// glitches
if(stripos($code, "<html") !== false) {
$code = preg_replace('/(<html[^>]*>)/i', "\\1<!-- template $templateName -->", $code);
$code = preg_replace('/(<\/html[^>]*>)/i', "<!-- end template $templateName -->\\1", $code);
} else {
$code = str_replace('<?php' . PHP_EOL, '<?php' . PHP_EOL . '$val .= \'<!-- template ' . $templateName . ' -->\';' . "\n", $code);
$code = str_replace('<?php' . PHP_EOL, '<?php' . PHP_EOL . '$val .= \'<!-- template ' . $templateName .
' -->\';' . "\n", $code);
$code .= "\n" . '$val .= \'<!-- end template ' . $templateName . ' -->\';';
}
}

View File

@ -26,13 +26,23 @@ class SSViewer_Scope {
// And array of item, itemIterator, itemIteratorTotal, pop_index, up_index, current_index
private $itemStack = array();
protected $item; // The current "global" item (the one any lookup starts from)
protected $itemIterator; // If we're looping over the current "global" item, here's the iterator that tracks with item we're up to
protected $itemIteratorTotal; //Total number of items in the iterator
// The current "global" item (the one any lookup starts from)
protected $item;
private $popIndex; // A pointer into the item stack for which item should be scope on the next pop call
private $upIndex = null; // A pointer into the item stack for which item is "up" from this one
private $currentIndex = null; // A pointer into the item stack for which item is this one (or null if not in stack yet)
// If we're looping over the current "global" item, here's the iterator that tracks with item we're up to
protected $itemIterator;
//Total number of items in the iterator
protected $itemIteratorTotal;
// A pointer into the item stack for which item should be scope on the next pop call
private $popIndex;
// A pointer into the item stack for which item is "up" from this one
private $upIndex = null;
// A pointer into the item stack for which item is this one (or null if not in stack yet)
private $currentIndex = null;
private $localIndex;
@ -48,7 +58,8 @@ class SSViewer_Scope {
}
public function resetLocalScope(){
list($this->item, $this->itemIterator, $this->itemIteratorTotal, $this->popIndex, $this->upIndex, $this->currentIndex) = $this->itemStack[$this->localIndex];
list($this->item, $this->itemIterator, $this->itemIteratorTotal, $this->popIndex, $this->upIndex,
$this->currentIndex) = $this->itemStack[$this->localIndex];
array_splice($this->itemStack, $this->localIndex+1);
}
@ -60,13 +71,17 @@ class SSViewer_Scope {
public function obj($name, $arguments = null, $forceReturnedObject = true, $cache = false, $cacheName = null) {
switch ($name) {
case 'Up':
if ($this->upIndex === null) user_error('Up called when we\'re already at the top of the scope', E_USER_ERROR);
if ($this->upIndex === null) {
user_error('Up called when we\'re already at the top of the scope', E_USER_ERROR);
}
list($this->item, $this->itemIterator, $this->itemIteratorTotal, $unused2, $this->upIndex, $this->currentIndex) = $this->itemStack[$this->upIndex];
list($this->item, $this->itemIterator, $this->itemIteratorTotal, $unused2, $this->upIndex,
$this->currentIndex) = $this->itemStack[$this->upIndex];
break;
case 'Top':
list($this->item, $this->itemIterator, $this->itemIteratorTotal, $unused2, $this->upIndex, $this->currentIndex) = $this->itemStack[0];
list($this->item, $this->itemIterator, $this->itemIteratorTotal, $unused2, $this->upIndex,
$this->currentIndex) = $this->itemStack[0];
break;
default:
@ -77,7 +92,8 @@ class SSViewer_Scope {
break;
}
$this->itemStack[] = array($this->item, $this->itemIterator, $this->itemIteratorTotal, null, $this->upIndex, $this->currentIndex);
$this->itemStack[] = array($this->item, $this->itemIterator, $this->itemIteratorTotal, null,
$this->upIndex, $this->currentIndex);
return $this;
}
@ -301,9 +317,16 @@ class SSViewer_DataPresenter extends SSViewer_Scope {
private static $globalProperties = null;
private static $iteratorProperties = null;
/** @var array|null Overlay variables. Take precedence over anything from the current scope */
/**
* Overlay variables. Take precedence over anything from the current scope
* @var array|null
*/
protected $overlay;
/** @var array|null Underlay variables. Concede precedence to overlay variables or anything from the current scope */
/**
* Underlay variables. Concede precedence to overlay variables or anything from the current scope
* @var array|null
*/
protected $underlay;
public function __construct($item, $overlay = null, $underlay = null){
@ -313,14 +336,17 @@ class SSViewer_DataPresenter extends SSViewer_Scope {
if (self::$globalProperties === null) {
self::$globalProperties = array();
// Get all the exposed variables from all classes that implement the TemplateGlobalProvider interface
$this->createCallableArray(self::$globalProperties, "TemplateGlobalProvider", "get_template_global_variables");
$this->createCallableArray(self::$globalProperties, "TemplateGlobalProvider",
"get_template_global_variables");
}
// Build up iterator property providers array only once per request
if (self::$iteratorProperties === null) {
self::$iteratorProperties = array();
// Get all the exposed variables from all classes that implement the TemplateIteratorProvider interface
$this->createCallableArray(self::$iteratorProperties, "TemplateIteratorProvider", "get_template_iterator_variables", true); //call non-statically
// //call non-statically
$this->createCallableArray(self::$iteratorProperties, "TemplateIteratorProvider",
"get_template_iterator_variables", true);
}
$this->overlay = $overlay ? $overlay : array();
@ -338,7 +364,8 @@ class SSViewer_DataPresenter extends SSViewer_Scope {
$exposedVariables = call_user_func(array($implementer, $variableMethod));
foreach($exposedVariables as $varName => $details) {
if (!is_array($details)) $details = array('method' => $details, 'casting' => Config::inst()->get('ViewableData', 'default_cast', Config::FIRST_SET));
if (!is_array($details)) $details = array('method' => $details,
'casting' => Config::inst()->get('ViewableData', 'default_cast', Config::FIRST_SET));
// If just a value (and not a key => value pair), use it for both key and value
if (is_numeric($varName)) $varName = $details['method'];
@ -367,7 +394,8 @@ class SSViewer_DataPresenter extends SSViewer_Scope {
if (array_key_exists($property, $this->overlay)) {
$source = array('value' => $this->overlay[$property]);
}
// Check if the method to-be-called exists on the target object - if so, don't check any further injection locations
// Check if the method to-be-called exists on the target object - if so, don't check any further
// injection locations
else if (isset($on->$property) || method_exists($on, $property)) {
$source = null;
}
@ -379,7 +407,8 @@ class SSViewer_DataPresenter extends SSViewer_Scope {
else if (array_key_exists($property, self::$iteratorProperties)) {
$source = self::$iteratorProperties[$property];
if ($this->itemIterator) {
// Set the current iterator position and total (the object instance is the first item in the callable array)
// Set the current iterator position and total (the object instance is the first item in
// the callable array)
$source['implementer']->iteratorProperties($this->itemIterator->key(), $this->itemIteratorTotal);
} else {
// If we don't actually have an iterator at the moment, act like a list of length 1
@ -397,7 +426,8 @@ class SSViewer_DataPresenter extends SSViewer_Scope {
// Look up the value - either from a callable, or from a directly provided value
if (isset($source['callable'])) $res['value'] = call_user_func_array($source['callable'], $params);
elseif (isset($source['value'])) $res['value'] = $source['value'];
else throw new InvalidArgumentException("Injected property $property does't have a value or callable value source provided");
else throw new InvalidArgumentException("Injected property $property does't have a value or callable " .
"value source provided");
// If we want to provide a casted object, look up what type object to use
if ($cast) {
@ -433,7 +463,8 @@ class SSViewer_DataPresenter extends SSViewer_Scope {
//extract the method name and parameters
$property = $arguments[0]; //the name of the public function being called
if (isset($arguments[1]) && $arguments[1] != null) $params = $arguments[1]; //the public function parameters in an array
//the public function parameters in an array
if (isset($arguments[1]) && $arguments[1] != null) $params = $arguments[1];
else $params = array();
$hasInjected = $res = null;
@ -613,7 +644,8 @@ class SSViewer {
self::flush_template_cache();
} else {
if(!Security::ignore_disallowed_actions()) {
return Security::permissionFailure(null, 'Please log in as an administrator to flush the template cache.');
return Security::permissionFailure(null, 'Please log in as an administrator to flush ' .
'the template cache.');
}
}
}
@ -797,7 +829,8 @@ class SSViewer {
SSViewer::$topLevel[] = $item;
if ($arguments && $arguments instanceof Zend_Cache_Core) {
Deprecation::notice('3.0', 'Use setPartialCacheStore to override the partial cache storage backend, the second argument to process is now an array of variables.');
Deprecation::notice('3.0', 'Use setPartialCacheStore to override the partial cache storage backend, ' .
'the second argument to process is now an array of variables.');
$this->setPartialCacheStore($arguments);
$arguments = null;
}
@ -811,7 +844,8 @@ class SSViewer {
}
if(isset($_GET['debug_profile'])) Profiler::mark("SSViewer::process", " for $template");
$cacheFile = TEMP_FOLDER . "/.cache" . str_replace(array('\\','/',':'), '.', Director::makeRelative(realpath($template)));
$cacheFile = TEMP_FOLDER . "/.cache"
. str_replace(array('\\','/',':'), '.', Director::makeRelative(realpath($template)));
$lastEdited = filemtime($template);
@ -925,7 +959,8 @@ class SSViewer_FromString extends SSViewer {
public function process($item, $arguments = null) {
if ($arguments && $arguments instanceof Zend_Cache_Core) {
Deprecation::notice('3.0', 'Use setPartialCacheStore to override the partial cache storage backend, the second argument to process is now an array of variables.');
Deprecation::notice('3.0', 'Use setPartialCacheStore to override the partial cache storage backend, ' .
'the second argument to process is now an array of variables.');
$this->setPartialCacheStore($arguments);
$arguments = null;
}

View File

@ -1,6 +1,7 @@
<?php
/**
* Interface that is implemented by any classes that want to expose a method that can be called in any scope in a template.
* Interface that is implemented by any classes that want to expose a method that can be called in any
* scope in a template.
*
* Director::AbsoluteBaseURL is an example of this.
*
@ -14,8 +15,8 @@ interface TemplateGlobalProvider {
* this class to get the value for those variables, and the class to use for casting the returned value for use
* in a template
*
* If the method to call is not included for a particular template variable, a method named the same as the template
* variable will be called
* If the method to call is not included for a particular template variable, a method named the same as the
* template variable will be called
*
* If the casting class is not specified for a particular template variable, ViewableData::$default_cast is used
*

View File

@ -1,7 +1,7 @@
<?php
/**
* Interface that is implemented by any classes that want to expose a method that can be called in any scope in a template
* that returns values dependant on the state of the iterator of the current scope.
* Interface that is implemented by any classes that want to expose a method that can be called in any
* scope in a template that returns values dependant on the state of the iterator of the current scope.
*
* SSViewer_BasicIteratorSupport is an example of this. See also @TemplateGlobalProvider
*
@ -11,12 +11,12 @@
interface TemplateIteratorProvider {
/**
* Called by SSViewer to get a list of iterator variables to expose to the template, the instance method to call on
* an instance of this class to get the value for those variables, and the class to use for casting the returned value
* for use in a template
* Called by SSViewer to get a list of iterator variables to expose to the template, the instance method to call
* on an instance of this class to get the value for those variables, and the class to use for casting the returned
* value for use in a template
*
* If the method to call is not included for a particular template variable, a method named the same as the template
* variable will be called
* If the method to call is not included for a particular template variable, a method named the same as the
* template variable will be called
*
* If the casting class is not specified for a particular template variable, ViewableData::$default_cast is used
*