Merge remote-tracking branch 'origin/3'

Conflicts:
	composer.json
	control/Session.php
	docs/en/05_Contributing/01_Code.md
	docs/en/05_Contributing/02_Release_Process.md
	forms/FormField.php
	model/DataQuery.php
	model/Image.php
	model/queries/SQLConditionalExpression.php
	view/SSViewer.php
	view/ViewableData.php
This commit is contained in:
Damian Mooyman 2015-07-31 15:49:35 +12:00
commit 09210efbc0
279 changed files with 7491 additions and 2949 deletions

1
.gitattributes vendored Normal file
View File

@ -0,0 +1 @@
docs/ export-ignore

View File

@ -1,5 +1,12 @@
language: php language: php
sudo: false
addons:
apt:
packages:
- tidy
php: php:
- 5.4 - 5.4
@ -38,9 +45,6 @@ matrix:
env: DB=MYSQL env: DB=MYSQL
- php: hhvm - php: hhvm
env: DB=MYSQL env: DB=MYSQL
before_install:
- sudo apt-get update -qq
- sudo apt-get install -y tidy
before_script: before_script:
- composer self-update || true - composer self-update || true

View File

@ -13,3 +13,8 @@ Name: defaulti18n
i18n: i18n:
module_priority: module_priority:
- other_modules - other_modules
---
Name: textcollector
---
Injector:
i18nTextCollector_Writer: 'i18nTextCollector_Writer_RailsYaml'

View File

@ -131,7 +131,11 @@ class CMSBatchActionHandler extends RequestHandler {
$ids = $this->cleanIDs($csvIDs); $ids = $this->cleanIDs($csvIDs);
// Filter by applicable pages // Filter by applicable pages
$applicableIDs = $actionHandler->applicablePages($ids); if($ids) {
$applicableIDs = $actionHandler->applicablePages($ids);
} else {
$applicableIDs = array();
}
$response = new SS_HTTPResponse(json_encode($applicableIDs)); $response = new SS_HTTPResponse(json_encode($applicableIDs));
$response->addHeader("Content-type", "application/json"); $response->addHeader("Content-type", "application/json");

View File

@ -142,7 +142,7 @@ class CMSMenu extends Object implements IteratorAggregate, i18nEntityProvider {
$cmsClasses = self::get_cms_classes(); $cmsClasses = self::get_cms_classes();
foreach($cmsClasses as $cmsClass) { foreach($cmsClasses as $cmsClass) {
$menuItem = self::menuitem_for_controller($cmsClass); $menuItem = self::menuitem_for_controller($cmsClass);
if($menuItem) $menuItems[$cmsClass] = $menuItem; if($menuItem) $menuItems[Convert::raw2htmlname(str_replace('\\', '-', $cmsClass))] = $menuItem;
} }
} }

View File

@ -76,7 +76,7 @@ class LeftAndMain extends Controller implements PermissionProvider {
* @config * @config
* @var string * @var string
*/ */
private static $help_link = 'http://userhelp.silverstripe.org/framework/en/3.1'; private static $help_link = '//userhelp.silverstripe.org/framework/en/3.2';
/** /**
* @var array * @var array
@ -555,7 +555,7 @@ class LeftAndMain extends Controller implements PermissionProvider {
public static function menu_icon_for_class($class) { public static function menu_icon_for_class($class) {
$icon = Config::inst()->get($class, 'menu_icon', Config::FIRST_SET); $icon = Config::inst()->get($class, 'menu_icon', Config::FIRST_SET);
if (!empty($icon)) { if (!empty($icon)) {
$class = strtolower($class); $class = strtolower(Convert::raw2htmlname(str_replace('\\', '-', $class)));
return ".icon.icon-16.icon-{$class} { background: url('{$icon}'); } "; return ".icon.icon-16.icon-{$class} { background: url('{$icon}'); } ";
} }
return ''; return '';
@ -1397,8 +1397,10 @@ class LeftAndMain extends Controller implements PermissionProvider {
*/ */
public function BatchActionsForm() { public function BatchActionsForm() {
$actions = $this->batchactions()->batchActionList(); $actions = $this->batchactions()->batchActionList();
$actionsMap = array('-1' => _t('LeftAndMain.DropdownBatchActionsDefault', 'Actions')); $actionsMap = array();
foreach($actions as $action) $actionsMap[$action->Link] = $action->Title; foreach($actions as $action) {
$actionsMap[$action->Link] = $action->Title;
}
$form = new Form( $form = new Form(
$this, $this,
@ -1409,7 +1411,9 @@ class LeftAndMain extends Controller implements PermissionProvider {
'Action', 'Action',
false, false,
$actionsMap $actionsMap
)->setAttribute('autocomplete', 'off') )
->setAttribute('autocomplete', 'off')
->setAttribute('data-placeholder', _t('LeftAndMain.DropdownBatchActionsDefault', 'Actions'))
), ),
new FieldList( new FieldList(
// TODO i18n // TODO i18n
@ -1618,15 +1622,17 @@ class LeftAndMain extends Controller implements PermissionProvider {
* @config * @config
* @var String * @var String
*/ */
private static $application_link = 'http://www.silverstripe.org/'; private static $application_link = '//www.silverstripe.org/';
/** /**
* Sets the href for the anchor on the Silverstripe logo in the menu * Sets the href for the anchor on the Silverstripe logo in the menu
* *
* @deprecated since version 4.0
*
* @param String $link * @param String $link
*/ */
public static function set_application_link($link) { public static function set_application_link($link) {
Deprecation::notice('3.2', 'Use the "LeftAndMain.application_link" config setting instead'); Deprecation::notice('4.0', 'Use the "LeftAndMain.application_link" config setting instead');
Config::inst()->update('LeftAndMain', 'application_link', $link); Config::inst()->update('LeftAndMain', 'application_link', $link);
} }
@ -1648,9 +1654,10 @@ class LeftAndMain extends Controller implements PermissionProvider {
/** /**
* @param String $name * @param String $name
* @deprecated since version 4.0
*/ */
public static function setApplicationName($name) { public static function setApplicationName($name) {
Deprecation::notice('3.2', 'Use the "LeftAndMain.application_name" config setting instead'); Deprecation::notice('4.0', 'Use the "LeftAndMain.application_name" config setting instead');
Config::inst()->update('LeftAndMain', 'application_name', $name); Config::inst()->update('LeftAndMain', 'application_name', $name);
} }
@ -1747,21 +1754,24 @@ class LeftAndMain extends Controller implements PermissionProvider {
/** /**
* Register the given javascript file as required in the CMS. * Register the given javascript file as required in the CMS.
* Filenames should be relative to the base, eg, FRAMEWORK_DIR . '/javascript/loader.js' * Filenames should be relative to the base, eg, FRAMEWORK_DIR . '/javascript/loader.js'
*
* @deprecated since version 4.0
*/ */
public static function require_javascript($file) { public static function require_javascript($file) {
Deprecation::notice('3.2', 'Use "LeftAndMain.extra_requirements_javascript" config setting instead'); Deprecation::notice('4.0', 'Use "LeftAndMain.extra_requirements_javascript" config setting instead');
Config::inst()->update('LeftAndMain', 'extra_requirements_javascript', array($file => array())); Config::inst()->update('LeftAndMain', 'extra_requirements_javascript', array($file => array()));
} }
/** /**
* Register the given stylesheet file as required. * Register the given stylesheet file as required.
* @deprecated since version 4.0
* *
* @param $file String Filenames should be relative to the base, eg, THIRDPARTY_DIR . '/tree/tree.css' * @param $file String Filenames should be relative to the base, eg, THIRDPARTY_DIR . '/tree/tree.css'
* @param $media String Comma-separated list of media-types (e.g. "screen,projector") * @param $media String Comma-separated list of media-types (e.g. "screen,projector")
* @see http://www.w3.org/TR/REC-CSS2/media.html * @see http://www.w3.org/TR/REC-CSS2/media.html
*/ */
public static function require_css($file, $media = null) { public static function require_css($file, $media = null) {
Deprecation::notice('3.2', 'Use "LeftAndMain.extra_requirements_css" config setting instead'); Deprecation::notice('4.0', 'Use "LeftAndMain.extra_requirements_css" config setting instead');
Config::inst()->update('LeftAndMain', 'extra_requirements_css', array($file => array('media' => $media))); Config::inst()->update('LeftAndMain', 'extra_requirements_css', array($file => array('media' => $media)));
} }
@ -1770,11 +1780,13 @@ class LeftAndMain extends Controller implements PermissionProvider {
* Themeable stylesheets have globally unique names, just like templates and PHP files. * Themeable stylesheets have globally unique names, just like templates and PHP files.
* Because of this, they can be replaced by similarly named CSS files in the theme directory. * Because of this, they can be replaced by similarly named CSS files in the theme directory.
* *
* @deprecated since version 4.0
*
* @param $name String The identifier of the file. For example, css/MyFile.css would have the identifier "MyFile" * @param $name String The identifier of the file. For example, css/MyFile.css would have the identifier "MyFile"
* @param $media String Comma-separated list of media-types (e.g. "screen,projector") * @param $media String Comma-separated list of media-types (e.g. "screen,projector")
*/ */
public static function require_themed_css($name, $media = null) { public static function require_themed_css($name, $media = null) {
Deprecation::notice('3.2', 'Use "LeftAndMain.extra_requirements_themedCss" config setting instead'); Deprecation::notice('4.0', 'Use "LeftAndMain.extra_requirements_themedCss" config setting instead');
Config::inst()->update('LeftAndMain', 'extra_requirements_themedCss', array($name => array('media' => $media))); Config::inst()->update('LeftAndMain', 'extra_requirements_themedCss', array($name => array('media' => $media)));
} }
@ -1950,8 +1962,9 @@ class LeftAndMain_TreeNode extends ViewableData {
$obj = $this->obj; $obj = $this->obj;
return "<li id=\"record-$obj->ID\" data-id=\"$obj->ID\" data-pagetype=\"$obj->ClassName\" class=\"" return "<li id=\"record-$obj->ID\" data-id=\"$obj->ID\" data-pagetype=\"$obj->ClassName\" class=\""
. $this->getClasses() . "\">" . "<ins class=\"jstree-icon\">&nbsp;</ins>" . $this->getClasses() . "\">" . "<ins class=\"jstree-icon\">&nbsp;</ins>"
. "<a href=\"" . $this->getLink() . "\" title=\"" . _t('LeftAndMain.PAGETYPE','Page type: ') . "<a href=\"" . $this->getLink() . "\" title=\"("
. "$obj->class\" ><ins class=\"jstree-icon\">&nbsp;</ins><span class=\"text\">" . ($obj->TreeTitle) . trim(_t('LeftAndMain.PAGETYPE','Page type'), " :") // account for inconsistencies in translations
. ": " . $obj->i18n_singular_name() . ") $obj->Title\" ><ins class=\"jstree-icon\">&nbsp;</ins><span class=\"text\">" . ($obj->TreeTitle)
. "</span></a>"; . "</span></a>";
} }

View File

@ -465,20 +465,20 @@ abstract class ModelAdmin extends LeftAndMain {
* overwrite the static page_length of the admin panel, * overwrite the static page_length of the admin panel,
* should be called in the project _config file. * should be called in the project _config file.
* *
* @deprecated 3.2 Use "ModelAdmin.page_length" config setting * @deprecated 4.0 Use "ModelAdmin.page_length" config setting
*/ */
public static function set_page_length($length){ public static function set_page_length($length){
Deprecation::notice('3.2', 'Use "ModelAdmin.page_length" config setting'); Deprecation::notice('4.0', 'Use "ModelAdmin.page_length" config setting');
self::config()->page_length = $length; self::config()->page_length = $length;
} }
/** /**
* Return the static page_length of the admin, default as 30 * Return the static page_length of the admin, default as 30
* *
* @deprecated 3.2 Use "ModelAdmin.page_length" config setting * @deprecated 4.0 Use "ModelAdmin.page_length" config setting
*/ */
public static function get_page_length(){ public static function get_page_length(){
Deprecation::notice('3.2', 'Use "ModelAdmin.page_length" config setting'); Deprecation::notice('4.0', 'Use "ModelAdmin.page_length" config setting');
return self::config()->page_length; return self::config()->page_length;
} }

View File

@ -334,41 +334,41 @@ class SecurityAdmin extends LeftAndMain implements PermissionProvider {
* The permissions represented in the $codes will not appearing in the form * The permissions represented in the $codes will not appearing in the form
* containing {@link PermissionCheckboxSetField} so as not to be checked / unchecked. * containing {@link PermissionCheckboxSetField} so as not to be checked / unchecked.
* *
* @deprecated 3.2 Use "Permission.hidden_permissions" config setting instead * @deprecated 4.0 Use "Permission.hidden_permissions" config setting instead
* @param $codes String|Array * @param $codes String|Array
*/ */
public static function add_hidden_permission($codes){ public static function add_hidden_permission($codes){
if(is_string($codes)) $codes = array($codes); if(is_string($codes)) $codes = array($codes);
Deprecation::notice('3.2', 'Use "Permission.hidden_permissions" config setting instead'); Deprecation::notice('4.0', 'Use "Permission.hidden_permissions" config setting instead');
Config::inst()->update('Permission', 'hidden_permissions', $codes); Config::inst()->update('Permission', 'hidden_permissions', $codes);
} }
/** /**
* @deprecated 3.2 Use "Permission.hidden_permissions" config setting instead * @deprecated 4.0 Use "Permission.hidden_permissions" config setting instead
* @param $codes String|Array * @param $codes String|Array
*/ */
public static function remove_hidden_permission($codes){ public static function remove_hidden_permission($codes){
if(is_string($codes)) $codes = array($codes); if(is_string($codes)) $codes = array($codes);
Deprecation::notice('3.2', 'Use "Permission.hidden_permissions" config setting instead'); Deprecation::notice('4.0', 'Use "Permission.hidden_permissions" config setting instead');
Config::inst()->remove('Permission', 'hidden_permissions', $codes); Config::inst()->remove('Permission', 'hidden_permissions', $codes);
} }
/** /**
* @deprecated 3.2 Use "Permission.hidden_permissions" config setting instead * @deprecated 4.0 Use "Permission.hidden_permissions" config setting instead
* @return Array * @return Array
*/ */
public static function get_hidden_permissions(){ public static function get_hidden_permissions(){
Deprecation::notice('3.2', 'Use "Permission.hidden_permissions" config setting instead'); Deprecation::notice('4.0', 'Use "Permission.hidden_permissions" config setting instead');
Config::inst()->get('Permission', 'hidden_permissions', Config::FIRST_SET); Config::inst()->get('Permission', 'hidden_permissions', Config::FIRST_SET);
} }
/** /**
* Clear all permissions previously hidden with {@link add_hidden_permission} * Clear all permissions previously hidden with {@link add_hidden_permission}
* *
* @deprecated 3.2 Use "Permission.hidden_permissions" config setting instead * @deprecated 4.0 Use "Permission.hidden_permissions" config setting instead
*/ */
public static function clear_hidden_permissions(){ public static function clear_hidden_permissions(){
Deprecation::notice('3.2', 'Use "Permission.hidden_permissions" config setting instead'); Deprecation::notice('4.0', 'Use "Permission.hidden_permissions" config setting instead');
Config::inst()->remove('Permission', 'hidden_permissions', Config::anything()); Config::inst()->remove('Permission', 'hidden_permissions', Config::anything());
} }
} }

View File

@ -13,10 +13,16 @@ output_style = :compact
# To enable relative paths to assets via compass helper functions. Uncomment: # To enable relative paths to assets via compass helper functions. Uncomment:
relative_assets = true relative_assets = true
# disable comments in the output. We want admin comments # Disable comments in the output. We want admin comments to be verbose.
# to be verbose
line_comments = false line_comments = false
asset_cache_buster :none # Disable asset cache buster. Using :none doesn't work in some versions of compass.
asset_cache_buster do |http_path, real_path|
nil
end
sourcemap = true
sass_options = { :unix_newlines => true }
Encoding.default_external = "utf-8" Encoding.default_external = "utf-8"

View File

@ -1 +1,3 @@
body.mceContentBody a.ss-broken { background-color: #FF7B71; border: 1px red solid; color: #fff; padding: 1px; text-decoration: underline; } body.mceContentBody a.ss-broken { background-color: #FF7B71; border: 1px red solid; color: #fff; padding: 1px; text-decoration: underline; }
/*# sourceMappingURL=editor.css.map */

7
admin/css/editor.css.map Normal file
View File

@ -0,0 +1,7 @@
{
"version": 3,
"mappings": "AAAA,+BAAgC,GAC/B,gBAAgB,EAAE,OAAO,EACzB,MAAM,EAAE,aAAa,EACrB,KAAK,EAAE,IAAI,EACX,OAAO,EAAE,GAAG,EACZ,eAAe,EAAE,SAAS",
"sources": ["../scss/editor.scss"],
"names": [],
"file": "editor.css"
}

View File

@ -35,6 +35,7 @@
.cms-menu-list li a .icon { filter: none; } .cms-menu-list li a .icon { filter: none; }
/** DEPRECATED: .cms-content-tools will be removed in 4.0 Use .cms-content-filters instead. Fix for model admin filter styling */
.ModelAdmin .cms-content-fields .cms-content-tools .cms-panel-content #Form_ImportForm div.file { margin: 0px; } .ModelAdmin .cms-content-fields .cms-content-tools .cms-panel-content #Form_ImportForm div.file { margin: 0px; }
.ModelAdmin .cms-content-fields .cms-content-tools .cms-panel-content #Form_ImportForm div.file input.file { margin-left: -132px; } .ModelAdmin .cms-content-fields .cms-content-tools .cms-panel-content #Form_ImportForm div.file input.file { margin-left: -132px; }
.ModelAdmin .cms-content-fields .cms-content-tools .cms-panel-content #Form_ImportForm div.checkbox { padding: 0px; } .ModelAdmin .cms-content-fields .cms-content-tools .cms-panel-content #Form_ImportForm div.checkbox { padding: 0px; }
@ -197,6 +198,7 @@ table.ss-gridfield-table tr.ss-gridfield-item.even { background: #F0F4F7; }
.ss-ui-button.ss-gridfield-button-filter { border: none !important; } .ss-ui-button.ss-gridfield-button-filter { border: none !important; }
/** DEPRECATED: .cms-content-tools will be removed in 4.0 Use .cms-content-filters instead. Fix for model admin filter styling */
.ModelAdmin .cms-content-fields .cms-content-tools .cms-panel-content .cms-search-form { overflow: hidden; } .ModelAdmin .cms-content-fields .cms-content-tools .cms-panel-content .cms-search-form { overflow: hidden; }
.ModelAdmin .cms-content-fields .cms-content-tools .cms-panel-content .cms-search-form input { width: 160px; } .ModelAdmin .cms-content-fields .cms-content-tools .cms-panel-content .cms-search-form input { width: 160px; }
@ -225,3 +227,5 @@ table.ss-gridfield-table tr.ss-gridfield-item.even { background: #F0F4F7; }
.ss-uploadfield-item-info .dimensions input { float: left; width: 150px; } .ss-uploadfield-item-info .dimensions input { float: left; width: 150px; }
.ss-uploadfield-item-info .dimensions .fieldgroup-field.last { margin-left: 16px; } .ss-uploadfield-item-info .dimensions .fieldgroup-field.last { margin-left: 16px; }
/*# sourceMappingURL=ie7.css.map */

7
admin/css/ie7.css.map Normal file
View File

@ -0,0 +1,7 @@
{
"version": 3,
"mappings": ";;;;;;;;;AAqBA,kBAAmB,GAClB,gBAAgB,EC2BM,OAAO;AD1B7B,iCAAiB,GAChB,gBAAgB,EAAC,OAAkC;AAEpD,4CAA4B,GAC3B,gBAAgB,EC4BU,OAAO;AD3BjC,2DAAiB,GAChB,gBAAgB,EAAC,OAAuC;AAG1D,6CAA4B,GAE3B,UAAU,EAAC,oEAAkD;;AAK/D,4FAEoC,GACnC,gBAAgB,EAAC,IAAI;;AAItB,wCAAyC,GACxC,UAAU,EAAE,2DAAyE,EACrF,MAAM,EAAC,IAAI;AACX,2DAAqB,GACpB,gBAAgB,EAAE,OAAmB,EACrC,mBAAmB,EAAE,SAAS,EAC9B,MAAM,EAAC,IAAI;AAEZ,oDAAc,GACb,gBAAgB,EAAG,OAAO,EAC1B,mBAAmB,EAAE,SAAS,EAC9B,MAAM,EAAC,IAAI;;AAIb,uCAAwC,GACvC,UAAU,EAAE,yDAAuE,EACnF,MAAM,EAAC,IAAI;AACX,sDAAiB,GAChB,UAAU,EAAE,wDAA6E,EACzF,MAAM,EAAC,IAAI;AAEZ,uDAAkB,GACjB,UAAU,EAAE,2DAAyF,EACrG,MAAM,EAAC,IAAI;;AAOX,mCAAG,GACF,YAAY,EAAE,iBAA+C;AAE9D,mCAAG,GACF,YAAY,EAAE,iBAA+C;AAC7D,wCAAO,GACN,UAAU,EAAE,iBAA+C,EAC3D,aAAa,EAAE,IAAI;AAEpB,yCAAQ,GACP,UAAU,EAAE,iBAA+C,EAC3D,aAAa,EAAC,IAAI;AAIrB,0FAA+B,GAC9B,WAAW,EAAE,iBAA+C;;AAO5D,2DAAO,GACN,MAAM,EAAC,CAAC,EACR,UAAU,EAAC,IAAI;;AAOjB,0DAAqB,GACpB,WAAW,EAAC,CAAC;;AAIf,yBAAyB,GACxB,MAAM,EAAC,IAAI;;;AAaV,+FAAS,GACR,MAAM,EAAC,GAAG;AACV,0GAAW,GACV,WAAW,EAAE,MAAM;AAGrB,mGAAa,GACZ,OAAO,EAAC,GAAG;;AAOZ,iDAA4B,GAC3B,mBAAmB,EAAE,oBAAoB;;;AAM5C,sBAAsB,GACrB,aAAa,EAAE,GAAG;AAClB,8BAAO,GACN,OAAO,EAAE,CAAC,EACV,KAAK,EAAE,IAAO,EACd,IAAI,EAAE,KAAK;AACX,oCAAK,GACJ,QAAQ,EAAC,OAAO,EAChB,aAAa,EAAC,OAAO,EACrB,WAAW,EAAC,MAAM,EAClB,OAAO,EAAC,CAAC;AACT,2CAAQ,GACP,KAAK,EAAC,IAAI,EACV,gBAAgB,EAAE,OAAO;AAE1B,yCAAI,GACH,OAAO,EAAC,MAAM,EACd,OAAO,EAAC,KAAK,EACb,QAAQ,EAAC,OAAO,EAChB,aAAa,EAAC,OAAO,EACrB,WAAW,EAAC,IAAI;AAGlB,4CAAa,GACZ,OAAO,EAAC,IAAI;AAEb,+CAAiB,GAChB,WAAW,EAAE,KAAK;;;AAMpB,4CAAsB,GACrB,OAAO,EAAC,IAAI;;;;;AEpGN,uEAAsB,GAlE5B,mBAAmB,EAAE,GACJ;AAiEX,yFAAsB,GAlE5B,mBAAmB,EAAE,OACJ;AAiEX,iEAAsB,GAlE5B,mBAAmB,EAAE,OACJ;AAiEX,2EAAsB,GAlE5B,mBAAmB,EAAE,OACJ;AAiEX,mFAAsB,GAlE5B,mBAAmB,EAAE,OACJ;AAiEX,yEAAsB,GAlE5B,mBAAmB,EAAE,OACJ;AAiEX,2FAAsB,GAlE5B,mBAAmB,EAAE,QACJ;AAiEX,qGAAsB,GAlE5B,mBAAmB,EAAE,QACJ;AAiEX,iGAAsB,GAlE5B,mBAAmB,EAAE,QACJ;AAiEX,mEAAsB,GAlE5B,mBAAmB,EAAE,QACJ;AAiEX,qFAAsB,GAlE5B,mBAAmB,EAAE,QACJ;AAiEX,mFAAsB,GAlE5B,mBAAmB,EAAE,QACJ;AAiEX,+FAAsB,GAlE5B,mBAAmB,EAAE,QACJ;AAiEX,mFAAsB,GAlE5B,mBAAmB,EAAE,QACJ;AAiEX,qFAAsB,GAlE5B,mBAAmB,EAAE,QACJ;AAiEX,iFAAsB,GAlE5B,mBAAmB,EAAE,QACJ;AAiEX,iFAAsB,GAlE5B,mBAAmB,EAAE,QACJ;AAiEX,qFAAsB,GAlE5B,mBAAmB,EAAE,QACJ;AAiEX,qEAAsB,GAlE5B,mBAAmB,EAAE,QACJ;AAiEX,mFAAsB,GAlE5B,mBAAmB,EAAE,QACJ;AAiEX,qGAAsB,GAlE5B,mBAAmB,EAAE,QACJ;AAiEX,qEAAsB,GAlE5B,mBAAmB,EAAE,QACJ;AAiEX,yEAAsB,GAlE5B,mBAAmB,EAAE,QACJ;AAiEX,2FAAsB,GAlE5B,mBAAmB,EAAE,QACJ;AAiEX,uEAAsB,GAlE5B,mBAAmB,EAAE,QACJ;AAiEX,iFAAsB,GAlE5B,mBAAmB,EAAE,QACJ;AAiEX,mEAAsB,GAlE5B,mBAAmB,EAAE,QACJ;AAiEX,2FAAsB,GAlE5B,mBAAmB,EAAE,QACJ;AAiEX,mFAAsB,GAlE5B,mBAAmB,EAAE,QACJ;AAiEX,mFAAsB,GAlE5B,mBAAmB,EAAE,QACJ;AAiEX,qGAAsB,GAlE5B,mBAAmB,EAAE,QACJ;AAiEX,+EAAsB,GAlE5B,mBAAmB,EAAE,QACJ;AAiEX,iFAAsB,GAlE5B,mBAAmB,EAAE,QACJ;AAiEX,6EAAsB,GAlE5B,mBAAmB,EAAE,QACJ;AAiEX,mFAAsB,GAlE5B,mBAAmB,EAAE,QACJ;AAiEX,qGAAsB,GAlE5B,mBAAmB,EAAE,QACJ;AAiEX,+EAAsB,GAlE5B,mBAAmB,EAAE,QACJ;AAiEX,iGAAsB,GAlE5B,mBAAmB,EAAE,QACJ;AAiEX,qFAAsB,GAlE5B,mBAAmB,EAAE,QACJ;AAiEX,uGAAsB,GAlE5B,mBAAmB,EAAE,QACJ;AAiEX,uEAAsB,GAlE5B,mBAAmB,EAAE,QACJ;AAiEX,yFAAsB,GAlE5B,mBAAmB,EAAE,QACJ;AAiEX,iHAAsB,GAlE5B,mBAAmB,EAAE,QACJ;AAiEX,mIAAsB,GAlE5B,mBAAmB,EAAE,QACJ;AAiEX,yEAAsB,GAlE5B,mBAAmB,EAAE,QACJ;AAiEX,2FAAsB,GAlE5B,mBAAmB,EAAE,QACJ;AAiEX,2EAAsB,GAlE5B,mBAAmB,EAAE,QACJ;AAiEX,6FAAsB,GAlE5B,mBAAmB,EAAE,QACJ;AAiEX,6EAAsB,GAlE5B,mBAAmB,EAAE,QACJ;AAiEX,+FAAsB,GAlE5B,mBAAmB,EAAE,QACJ;;ACiCnB,KAAM,GACL,WAAW,EAAE,OAAO,EACpB,MAAM,EAAE,IAAI,EACZ,OAAO,EAAE,IAAI;AAEb,aAAU,GACT,KAAK,EAAE,IAAI,EACX,MAAM,EAAE,IAAI,EAnCb,UAAU,EAZF,iDAAoC;AAc5C,6BAAkB,GDRjB,mBAAmB,EAAE,QACJ;ACUlB,0BAAe,GDXd,mBAAmB,EAAE,QACJ;ACalB,qCAA0B,GDdzB,mBAAmB,EAAE,QACJ;ACgBlB,wCAA6B,GDjB5B,mBAAmB,EAAE,OACJ;ACmBlB,gCAAqB,GDpBpB,mBAAmB,EAAE,OACJ;ACsBlB,8BAAmB,GDvBlB,mBAAmB,EAAE,QACJ;ACyBlB,+BAAoB,GD1BnB,mBAAmB,EAAE,GACJ;AC4BlB,uBAAY,GD7BX,mBAAmB,EAAE,QACJ;AC6ClB,aAAU,GACT,KAAK,EAAE,IAAI,EACX,MAAM,EAAE,IAAI,EA1Cb,UAAU,EAVF,iDAAoC;AAY5C,6BAAkB,GDRjB,mBAAmB,EAAE,QACJ;ACUlB,0BAAe,GDXd,mBAAmB,EAAE,QACJ;ACalB,qCAA0B,GDdzB,mBAAmB,EAAE,QACJ;ACgBlB,wCAA6B,GDjB5B,mBAAmB,EAAE,OACJ;ACmBlB,gCAAqB,GDpBpB,mBAAmB,EAAE,OACJ;ACsBlB,8BAAmB,GDvBlB,mBAAmB,EAAE,QACJ;ACyBlB,+BAAoB,GD1BnB,mBAAmB,EAAE,GACJ;AC4BlB,uBAAY,GD7BX,mBAAmB,EAAE,OACJ;;AEbnB,IAAK,GACJ,QAAQ,EAAE,MAAM;;AAIjB,oBAAqB,GACpB,cAAc,EAAC,GAAG;;AAIlB,iBAAE,GACD,eAAe,EAAE,IAAI,EAClB,KAAK,EAAE,IAAI,EACX,KAAK,EAAE,IAAI,EACX,MAAM,EAAE,GAAG,EACX,OAAO,EAAE,GAAG;;;;AAUhB,wBAAI,GACH,KAAK,EAAC,IAAI;AAEX,yBAAK,GACJ,KAAK,EAAC,IAAI,EACV,WAAW,EAAC,GAAG;;AAIf,+BAAE,GACD,KAAK,EAAC,KAAK,EACX,QAAQ,EAAC,MAAM,EACf,KAAK,EAAC,IAAI,EACV,OAAO,EAAC,MAAM;;AAKjB,qBAAsB,GACrB,WAAW,EAAE,cAAc;;;;AAO3B,6DAEmB,GAClB,KAAK,EAAE,GAAG;;AAGZ,MAAO,GACN,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,IAAI;;AAGb,yBAA0B,GACzB,OAAO,EAAC,KAAK;;AAKZ,+CAAc,GACb,MAAM,EAAC,gBAAgB;AAGvB,wDAAE,GACD,UAAU,EAAC,KAAK,EAChB,MAAM,EAAC,IAAI,EACX,UAAU,EAAC,KAAK;;AAOpB,iCAAkC,GACjC,KAAK,EAAC,IAAI;;AAKV,wBAAI,GACH,KAAK,EAAC,IAAI;AAEX,yBAAK,GACJ,KAAK,EAAC,IAAI,EACV,WAAW,EAAC,GAAG;;AAIf,+BAAE,GACD,KAAK,EAAC,KAAK,EACX,QAAQ,EAAC,MAAM,EACf,KAAK,EAAC,IAAI,EACV,OAAO,EAAC,MAAM;;AAKjB,oBAAqB,GACpB,cAAc,EAAC,GAAG;;AAIlB,iBAAE,GACD,eAAe,EAAE,IAAI,EAClB,KAAK,EAAE,IAAI,EACX,KAAK,EAAE,IAAI,EACX,MAAM,EAAE,GAAG,EACX,OAAO,EAAE,GAAG;;AAQjB,qBAAsB,GACrB,WAAW,EAAE,cAAc;;;;AAO3B,sCAAS,GAGR,KAAK,EAAE,IAAI;AAEZ,oDAAuB,GAEtB,MAAM,EAAC,IAAI;;AAIb,wCAAyC,GACxC,MAAM,EAAE,SAAS;;AAGlB,uCAAwC,GACvC,MAAM,EAAE,SAAS;;AAMhB,2DAAO,GACN,KAAK,EAAC,IAAI;AACV,mFAA0B,GACzB,OAAO,EAAC,KAAK,EACb,KAAK,EAAC,IAAI;AAEX,mFAA0B,GACzB,OAAO,EAAC,KAAK,EACb,KAAK,EAAC,IAAI;AAGZ,gEAAY,GACX,OAAO,EAAC,KAAK,EACb,KAAK,EAAC,IAAI;;AAOZ,mEAA8B,GAC7B,SAAS,EAAC,KAAK;;AAKjB,uBAAwB,GACvB,QAAQ,EAAC,QAAQ,EACjB,MAAM,EAAC,IAAI,EACX,KAAK,EAAC,IAAI;AACV,6BAAM,GACL,KAAK,EAAC,IAAI,EACV,OAAO,EAAC,GAAG,EACX,QAAQ,EAAC,QAAQ,EACjB,MAAM,EAAC,GAAG;;AAKX,uBAAoB,GACnB,WAAW,EAAC,YAAY,EACxB,cAAc,EAAC,YAAY;;AAK7B,uCAAwC,GACvC,KAAK,EAAC,IAAI;;AAMT,iDAAwB,GACvB,UAAU,EAAE,KAAK;AAElB,kDAAyB,GACxB,UAAU,EAAE,OAAO;;AAQpB,yEAAuB,GACtB,KAAK,EAAC,IAAI;AACV,6FAAoB,GACnB,UAAU,EAAE,2EAA2E,EACvF,OAAO,EAAC,KAAK;;AAOhB,wCAA4B,GAC1B,MAAM,EAAC,eAAe;;;AAcxB,sFAAiB,GAChB,QAAQ,EAAC,MAAM;AACf,4FAAM,GACL,KAAK,EAAC,KAAK;;AAQb,sBAAG,GACF,KAAK,EAAE,IAAI;AACX,oCAAc,GACb,OAAO,EAAE,IAAI;AAIf,4CAAyB,GACxB,QAAQ,EAAE,QAAQ,EAClB,KAAK,EAAE,CAAC;;AAKT,wCAA4B,GAC3B,MAAM,EAAC,eAAe;;AAIvB,qDAA+B,GAC9B,OAAO,EAAC,eAAe;;AAGzB,wCAAwC,GACvC,OAAO,EAAC,KAAK;;AJ3Rb,4BAA6B,GAC5B,QAAQ,EAAC,QAAQ,EACjB,KAAK,EAAE,IAAI;AAEV,kGAAmB,GAClB,IAAI,EAAE,CAAC,EACP,QAAQ,EAAC,QAAQ,EACjB,GAAG,EAAC,IAAI,EACR,KAAK,EAAC,IAAI,EACV,YAAY,EAAE,KAAK,EACnB,KAAK,EAAC,KAAK,EACX,OAAO,EAAC,IAAI;;AIsRhB,oEAAqE,GACpE,KAAK,EAAE,KAAK;AAEX,oGAAgB,GACf,UAAU,EAAE,mFAAmF;AAC/F,0GAAQ,GACP,UAAU,EAAE,kFAAkF;AAGhG,qHAAkC,GACjC,UAAU,EAAE,iFAAiF;AAC7F,2HAAQ,GACP,UAAU,EAAE,gFAAgF;AAI/F,uGAAmC,GAC/B,KAAK,EAAE,KAAK;;;AAMjB,mBAAoB,GACnB,OAAO,EAAE,MAAM;;AAKd,2CAAM,GACL,KAAK,EAAC,IAAI,EACV,KAAK,EAAC,KAAK;AAEZ,4DAAuB,GACtB,WAAW,EAAC,IAAI",
"sources": ["../scss/_ieShared.scss","../scss/themes/_default.scss","../../../../../../../../Library/Ruby/Gems/2.0.0/gems/compass-core-1.0.3/stylesheets/compass/utilities/sprites/_base.scss","../scss/_sprites.scss","../scss/ie7.scss"],
"names": [],
"file": "ie7.css"
}

View File

@ -35,6 +35,7 @@
.cms-menu-list li a .icon { filter: none; } .cms-menu-list li a .icon { filter: none; }
/** DEPRECATED: .cms-content-tools will be removed in 4.0 Use .cms-content-filters instead. Fix for model admin filter styling */
.ModelAdmin .cms-content-fields .cms-content-tools .cms-panel-content #Form_ImportForm div.file { margin: 0px; } .ModelAdmin .cms-content-fields .cms-content-tools .cms-panel-content #Form_ImportForm div.file { margin: 0px; }
.ModelAdmin .cms-content-fields .cms-content-tools .cms-panel-content #Form_ImportForm div.file input.file { margin-left: -132px; } .ModelAdmin .cms-content-fields .cms-content-tools .cms-panel-content #Form_ImportForm div.file input.file { margin-left: -132px; }
.ModelAdmin .cms-content-fields .cms-content-tools .cms-panel-content #Form_ImportForm div.checkbox { padding: 0px; } .ModelAdmin .cms-content-fields .cms-content-tools .cms-panel-content #Form_ImportForm div.checkbox { padding: 0px; }
@ -58,6 +59,7 @@ fieldset.switch-states .switch input.state-name { margin-left: -20px; }
.cms-content-toolbar .cms-tree-view-modes .checkboxAboveTree { margin-right: 1px; } .cms-content-toolbar .cms-tree-view-modes .checkboxAboveTree { margin-right: 1px; }
/** DEPRECATED: .cms-content-tools will be removed in 4.0 Use .cms-content-filters instead. */
.cms .cms-content-tools .cms-panel-content .dropdown select { width: 152px; } .cms .cms-content-tools .cms-panel-content .dropdown select { width: 152px; }
.filter-buttons button { width: 23px !important; height: 23px !important; } .filter-buttons button { width: 23px !important; height: 23px !important; }
@ -71,3 +73,5 @@ fieldset.switch-states .switch input.state-name { margin-left: -20px; }
.tree-holder.filtered-list li > a, .tree-holder.filtered-list li > a:link, .cms-tree.filtered-list li > a, .cms-tree.filtered-list li > a:link { color: #aaa; } .tree-holder.filtered-list li > a, .tree-holder.filtered-list li > a:link, .cms-tree.filtered-list li > a, .cms-tree.filtered-list li > a:link { color: #aaa; }
.tree-holder.filtered-list li.filtered-item > a, .tree-holder.filtered-list li.filtered-item > a:link, .cms-tree.filtered-list li.filtered-item > a, .cms-tree.filtered-list li.filtered-item > a:link { color: #0073C1; } .tree-holder.filtered-list li.filtered-item > a, .tree-holder.filtered-list li.filtered-item > a:link, .cms-tree.filtered-list li.filtered-item > a, .cms-tree.filtered-list li.filtered-item > a:link { color: #0073C1; }
.tree-holder.filtered-list li.disabled > a, .tree-holder.filtered-list li.disabled > a:link, .tree-holder.filtered-list li.edit-disabled > a, .tree-holder.filtered-list li.edit-disabled > a:link, .cms-tree.filtered-list li.disabled > a, .cms-tree.filtered-list li.disabled > a:link, .cms-tree.filtered-list li.edit-disabled > a, .cms-tree.filtered-list li.edit-disabled > a:link { color: #aaa; background: transparent none; cursor: default; } .tree-holder.filtered-list li.disabled > a, .tree-holder.filtered-list li.disabled > a:link, .tree-holder.filtered-list li.edit-disabled > a, .tree-holder.filtered-list li.edit-disabled > a:link, .cms-tree.filtered-list li.disabled > a, .cms-tree.filtered-list li.disabled > a:link, .cms-tree.filtered-list li.edit-disabled > a, .cms-tree.filtered-list li.edit-disabled > a:link { color: #aaa; background: transparent none; cursor: default; }
/*# sourceMappingURL=ie8.css.map */

7
admin/css/ie8.css.map Normal file
View File

@ -0,0 +1,7 @@
{
"version": 3,
"mappings": ";;;;;;;;;AAqBA,kBAAmB,GAClB,gBAAgB,EC2BM,OAAO;AD1B7B,iCAAiB,GAChB,gBAAgB,EAAC,OAAkC;AAEpD,4CAA4B,GAC3B,gBAAgB,EC4BU,OAAO;AD3BjC,2DAAiB,GAChB,gBAAgB,EAAC,OAAuC;AAG1D,6CAA4B,GAE3B,UAAU,EAAC,oEAAkD;;AAK/D,4FAEoC,GACnC,gBAAgB,EAAC,IAAI;;AAItB,wCAAyC,GACxC,UAAU,EAAE,2DAAyE,EACrF,MAAM,EAAC,IAAI;AACX,2DAAqB,GACpB,gBAAgB,EAAE,OAAmB,EACrC,mBAAmB,EAAE,SAAS,EAC9B,MAAM,EAAC,IAAI;AAEZ,oDAAc,GACb,gBAAgB,EAAG,OAAO,EAC1B,mBAAmB,EAAE,SAAS,EAC9B,MAAM,EAAC,IAAI;;AAIb,uCAAwC,GACvC,UAAU,EAAE,yDAAuE,EACnF,MAAM,EAAC,IAAI;AACX,sDAAiB,GAChB,UAAU,EAAE,wDAA6E,EACzF,MAAM,EAAC,IAAI;AAEZ,uDAAkB,GACjB,UAAU,EAAE,2DAAyF,EACrG,MAAM,EAAC,IAAI;;AAOX,mCAAG,GACF,YAAY,EAAE,iBAA+C;AAE9D,mCAAG,GACF,YAAY,EAAE,iBAA+C;AAC7D,wCAAO,GACN,UAAU,EAAE,iBAA+C,EAC3D,aAAa,EAAE,IAAI;AAEpB,yCAAQ,GACP,UAAU,EAAE,iBAA+C,EAC3D,aAAa,EAAC,IAAI;AAIrB,0FAA+B,GAC9B,WAAW,EAAE,iBAA+C;;AAO5D,2DAAO,GACN,MAAM,EAAC,CAAC,EACR,UAAU,EAAC,IAAI;;AAOjB,0DAAqB,GACpB,WAAW,EAAC,CAAC;;AAIf,yBAAyB,GACxB,MAAM,EAAC,IAAI;;;AAaV,+FAAS,GACR,MAAM,EAAC,GAAG;AACV,0GAAW,GACV,WAAW,EAAE,MAAM;AAGrB,mGAAa,GACZ,OAAO,EAAC,GAAG;;AAOZ,iDAA4B,GAC3B,mBAAmB,EAAE,oBAAoB;;;AAM5C,sBAAsB,GACrB,aAAa,EAAE,GAAG;AAClB,8BAAO,GACN,OAAO,EAAE,CAAC,EACV,KAAK,EAAE,IAAO,EACd,IAAI,EAAE,KAAK;AACX,oCAAK,GACJ,QAAQ,EAAC,OAAO,EAChB,aAAa,EAAC,OAAO,EACrB,WAAW,EAAC,MAAM,EAClB,OAAO,EAAC,CAAC;AACT,2CAAQ,GACP,KAAK,EAAC,IAAI,EACV,gBAAgB,EAAE,OAAO;AAE1B,yCAAI,GACH,OAAO,EAAC,MAAM,EACd,OAAO,EAAC,KAAK,EACb,QAAQ,EAAC,OAAO,EAChB,aAAa,EAAC,OAAO,EACrB,WAAW,EAAC,IAAI;AAGlB,4CAAa,GACZ,OAAO,EAAC,IAAI;AAEb,+CAAiB,GAChB,WAAW,EAAE,KAAK;;;AAMpB,4CAAsB,GACrB,OAAO,EAAC,IAAI;;AArLb,uCAA6B,GAC5B,QAAQ,EAAC,QAAQ,EACjB,KAAK,EAAE,IAAI;AAEV,wHAAmB,GAClB,IAAI,EAAE,CAAC,EACP,QAAQ,EAAC,QAAQ,EACjB,GAAG,EAAC,IAAI,EACR,KAAK,EAAC,IAAI,EACV,YAAY,EAAE,KAAK,EACnB,KAAK,EAAC,KAAK,EACX,OAAO,EAAC,IAAI;;AEAd,4DAAmB,GAClB,YAAY,EAAC,GAAG;;;AAalB,2DAAoC,GACnC,KAAK,EAAC,KAAK;;AAKZ,sBAAM,GACL,KAAK,EAAC,eAAe,EACrB,MAAM,EAAC,eAAe;AACtB,gDAA2B,GAC1B,YAAY,EAAC,eAAe;;AAK/B,YAAY,GACX,KAAK,EAAC,IAAI;;;AAMT,4CAAO,GACN,YAAY,EAAE,CAAC;;AAUhB,8IACS,GACR,KAAK,EDpBc,IAAI;ACyBvB,sMACS,GACR,KAAK,EDxBa,OAAO;AC+B1B,0XACS,GACR,KAAK,EDpCa,IAAI,ECqCtB,UAAU,EAAE,gBAAgB,EAC5B,MAAM,EAAE,OAAO",
"sources": ["../scss/_ieShared.scss","../scss/themes/_default.scss","../scss/ie8.scss"],
"names": [],
"file": "ie8.css"
}

View File

@ -111,8 +111,31 @@ Used in side panels and action tabs
.icon.icon-16.icon-help { background-position: 0 -96px; } .icon.icon-16.icon-help { background-position: 0 -96px; }
/** ----------------------------- CMS Components ------------------------------ */ /** ----------------------------- CMS Components ------------------------------ */
@font-face { font-family: 'fontello'; src: url("../font/fontello.eot?77203683"); src: url("../font/fontello.eot?77203683#iefix") format("embedded-opentype"), url("../font/fontello.woff?77203683") format("woff"), url("../font/fontello.ttf?77203683") format("truetype"), url("../font/fontello.svg?77203683#fontello") format("svg"); font-weight: normal; font-style: normal; }
[class^="font-icon-"]:before, [class*=" font-icon-"]:before { color: #66727d; display: inline-block; content: ''; width: 1em; margin-top: 2px; margin-right: .2em; text-align: center; text-decoration: inherit; text-transform: none; font-family: "fontello"; font-style: normal; font-weight: normal; font-variant: normal; speak: none; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; }
.font-icon-search:before { content: '\e800'; }
.font-icon-tree:before { content: '\e801'; }
.font-icon-list2:before { content: '\e802'; }
.font-icon-cog:before { content: '\e803'; }
.font-icon-check:before { content: '\e804'; }
.font-icon-plus-solid:before { content: '\e805'; }
.font-icon-list:before { content: '\e806'; }
.font-icon-plus:before { content: '\e807'; }
.font-icon-search2:before { content: '\e808'; }
.font-icon-pencil:before { content: '\e80b'; }
/** File: typography.scss Contains the basic typography related styles for the admin interface. */ /** File: typography.scss Contains the basic typography related styles for the admin interface. */
body, html { font-size: 12px; line-height: 16px; font-family: Arial, sans-serif; color: #444; } body, html { font-size: 12px; line-height: 16px; font-family: Arial, sans-serif; color: #66727d; }
.cms h2, .cms h3, .cms h4, .cms h5 { font-weight: bold; margin: 16px 0 16px 0; line-height: 16px; } .cms h2, .cms h3, .cms h4, .cms h5 { font-weight: bold; margin: 16px 0 16px 0; line-height: 16px; }
.cms h2 { font-size: 18px; line-height: 24px; } .cms h2 { font-size: 18px; line-height: 24px; }
@ -124,7 +147,7 @@ body, html { font-size: 12px; line-height: 16px; font-family: Arial, sans-serif;
.cms code { font-family: 'Bitstream Vera Sans Mono','Courier', monospace; } .cms code { font-family: 'Bitstream Vera Sans Mono','Courier', monospace; }
/** This file defines CMS-specific customizations to the jQuery UI theme. Every rule should contain ONLY overwritten jQuery UI rules (with 'ui-' prefix). This file should be fairly short, as we're using our own custom jQuery UI theme already. TODO Add theme reference Use _style.scss to add more generic style information, and read the jQuery UI theming API: http://jqueryui.com/docs/Theming/API */ /** This file defines CMS-specific customizations to the jQuery UI theme. Every rule should contain ONLY overwritten jQuery UI rules (with 'ui-' prefix). This file should be fairly short, as we're using our own custom jQuery UI theme already. TODO Add theme reference Use _style.scss to add more generic style information, and read the jQuery UI theming API: http://jqueryui.com/docs/Theming/API */
.ui-widget-content, .ui-widget { color: #444; font-size: 12px; font-family: Arial, sans-serif; border: 0; } .ui-widget-content, .ui-widget { color: #66727d; font-size: 12px; font-family: Arial, sans-serif; border: 0; }
.ui-widget-header { background-color: #b0bec7; padding: 8px 8px 6px 8px; border-bottom: 2px solid #8399a7; background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2RkZTNlNyIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iIzkyYTViMiIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA=='); background-size: 100%; background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #dde3e7), color-stop(100%, #92a5b2)); background-image: -moz-linear-gradient(#dde3e7, #92a5b2); background-image: -webkit-linear-gradient(#dde3e7, #92a5b2); background-image: linear-gradient(#dde3e7, #92a5b2); border-bottom: 3px solid #5c7382; padding: 8px; -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0; } .ui-widget-header { background-color: #b0bec7; padding: 8px 8px 6px 8px; border-bottom: 2px solid #8399a7; background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2RkZTNlNyIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iIzkyYTViMiIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA=='); background-size: 100%; background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #dde3e7), color-stop(100%, #92a5b2)); background-image: -moz-linear-gradient(#dde3e7, #92a5b2); background-image: -webkit-linear-gradient(#dde3e7, #92a5b2); background-image: linear-gradient(#dde3e7, #92a5b2); border-bottom: 3px solid #5c7382; padding: 8px; -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0; }
.ui-widget-header .ui-dialog-title { padding: 6px 10px; text-shadow: #ced7dc 1px 1px 0; } .ui-widget-header .ui-dialog-title { padding: 6px 10px; text-shadow: #ced7dc 1px 1px 0; }
@ -135,13 +158,13 @@ body, html { font-size: 12px; line-height: 16px; font-family: Arial, sans-serif;
.ui-state-hover { cursor: pointer; } .ui-state-hover { cursor: pointer; }
.ui-widget input, .ui-widget select, .ui-widget textarea, .ui-widget button { color: #444; font-size: 12px; font-family: Arial, sans-serif; } .ui-widget input, .ui-widget select, .ui-widget textarea, .ui-widget button { color: #66727d; font-size: 12px; font-family: Arial, sans-serif; }
.ui-accordion .ui-accordion-header { border-color: #c0c0c2; margin-bottom: 0; } .ui-accordion .ui-accordion-header { border-color: #d0d3d5; margin-bottom: 0; }
.ui-accordion .ui-accordion-content { border: 1px solid #c0c0c2; border-top: none; } .ui-accordion .ui-accordion-content { border: 1px solid #d0d3d5; border-top: none; }
.ui-autocomplete { max-height: 240px; overflow-x: hidden; overflow-y: auto; /** sorry about the !important but the specificity of other selectors mandates it over writing out very specific selectors **/ } .ui-autocomplete { max-height: 240px; overflow-x: hidden; overflow-y: auto; /** sorry about the !important but the specificity of other selectors mandates it over writing out very specific selectors **/ }
.ui-autocomplete-loading { background-image: url(../images/throbber.gif) !important; background-position: 97% center !important; background-repeat: no-repeat !important; background-size: auto !important; } .ui-autocomplete .loading { background-image: url(../images/throbber.gif) !important; background-position: 97% center !important; background-repeat: no-repeat !important; background-size: auto !important; }
/** This file defines common styles for form elements used throughout the CMS interface. It is an addition to the base styles defined in framework/css/Form.css. @package framework @subpackage admin */ /** This file defines common styles for form elements used throughout the CMS interface. It is an addition to the base styles defined in framework/css/Form.css. @package framework @subpackage admin */
/** ---------------------------------------------------- Basic form fields ---------------------------------------------------- */ /** ---------------------------------------------------- Basic form fields ---------------------------------------------------- */
@ -151,25 +174,25 @@ form.nostyle label.left { float: none; display: inherit; width: auto; padding: 0
form.nostyle .middleColumn { margin-left: 0; } form.nostyle .middleColumn { margin-left: 0; }
form.nostyle input.text, form.nostyle textarea, form.nostyle select, form.nostyle .TreeDropdownField { width: auto; max-width: auto; } form.nostyle input.text, form.nostyle textarea, form.nostyle select, form.nostyle .TreeDropdownField { width: auto; max-width: auto; }
.field { display: block; border-bottom: 1px solid #d0d3d5; -webkit-box-shadow: 0 1px 0 rgba(245, 245, 245, 0.8); -moz-box-shadow: 0 1px 0 rgba(245, 245, 245, 0.8); -o-box-shadow: 0 1px 0 rgba(245, 245, 245, 0.8); box-shadow: 0 1px 0 rgba(245, 245, 245, 0.8); padding: 0 0 7px 0; margin: 8px 0; *zoom: 1; } .field { display: block; border-bottom: 1px solid #D2D5D8; -webkit-box-shadow: 0 1px 0 rgba(245, 245, 245, 0.8); -moz-box-shadow: 0 1px 0 rgba(245, 245, 245, 0.8); -o-box-shadow: 0 1px 0 rgba(245, 245, 245, 0.8); box-shadow: 0 1px 0 rgba(245, 245, 245, 0.8); padding: 0 0 7px 0; margin: 8px 0; *zoom: 1; }
.field.noborder, .field:last-child { padding-bottom: 0; border-bottom: none; -moz-box-shadow: none; -webkit-box-shadow: none; box-shadow: none; } .field.noborder, .field:last-child { padding-bottom: 0; border-bottom: none; -moz-box-shadow: none; -webkit-box-shadow: none; box-shadow: none; }
.field:after { content: "\0020"; display: block; height: 0; clear: both; overflow: hidden; visibility: hidden; } .field:after { content: "\0020"; display: block; height: 0; clear: both; overflow: hidden; visibility: hidden; }
.field.nolabel .middleColumn { margin-left: 0; } .field.nolabel .middleColumn { margin-left: 0; }
.field.nolabel .description { margin-left: 0; } .field.nolabel .description { margin-left: 0; }
.field.checkbox label.right { margin: 4px 0 0 0; display: inline; font-style: normal; color: #444; clear: none; } .field.checkbox label.right { margin: 4px 0 0 0; display: inline; font-style: normal; color: #66727d; clear: none; }
.field label.left { float: left; display: block; width: 176px; padding: 8px 8px 8px 0; line-height: 16px; font-weight: bold; text-shadow: 1px 1px 0 white; } .field label.left { float: left; display: block; width: 176px; padding: 8px 8px 8px 0; line-height: 16px; font-weight: bold; text-shadow: 1px 1px 0 white; }
.field label.right { cursor: pointer; clear: both; color: #777777; display: block; font-style: italic; margin: 4px 0 0 184px; } .field label.right { cursor: pointer; clear: both; color: #9ba5ae; display: block; font-style: italic; margin: 4px 0 0 184px; }
.field .middleColumn { margin-left: 184px; } .field .middleColumn { margin-left: 184px; }
.field span.readonly { padding-top: 8px; line-height: 16px; display: block; } .field span.readonly { padding-top: 8px; line-height: 16px; display: block; }
.field .fieldgroup .fieldgroup-field.last { /* This is used on page/settings/visibility */ padding-bottom: 8px; /* replicates li item spacing */ } .field .fieldgroup .fieldgroup-field.last { /* This is used on page/settings/visibility */ padding-bottom: 8px; /* replicates li item spacing */ }
.field .description { clear: both; color: #777777; display: block; font-style: italic; line-height: 16px; margin: 4px 0 0 184px; } .field .description { clear: both; color: #9ba5ae; display: block; font-style: italic; line-height: 16px; margin: 4px 0 0 184px; }
.field.checkbox .description, .field.ss-gridfield .description { margin-left: 0; } .field.checkbox .description, .field.ss-gridfield .description { margin-left: 0; }
.field input.text, .field textarea, .field select, .field .TreeDropdownField { margin-left: 10px; width: 100%; max-width: 512px; -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box; } .field input.text, .field textarea, .field select, .field .TreeDropdownField { margin-left: 10px; width: 100%; max-width: 512px; -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box; }
.field input.text.description, .field textarea.description, .field select.description, .field .TreeDropdownField.description { margin: 0; } .field input.text.description, .field textarea.description, .field select.description, .field .TreeDropdownField.description { margin: 0; }
.field input.text .description, .field textarea .description, .field select .description, .field .TreeDropdownField .description { max-width: 512px; } .field input.text .description, .field textarea .description, .field select .description, .field .TreeDropdownField .description { max-width: 512px; }
.field input.text, .field textarea, .field .TreeDropdownField { background: #fff; border: 1px solid #b3b3b3; padding: 7px 7px; line-height: 16px; margin: 0; outline: none; -moz-transition: 0.2s box-shadow ease-in; -webkit-transition: 0.2s box-shadow ease-in; -o-transition: 0.2s box-shadow ease-in; transition: 0.2s box-shadow ease-in; -moz-transition: 0.2s border ease-in; -webkit-transition: 0.2s border ease-in; -o-transition: 0.2s border ease-in; transition: 0.2s border ease-in; -moz-border-radius: 4px; -webkit-border-radius: 4px; border-radius: 4px; background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2VhZWFlYSIvPjxzdG9wIG9mZnNldD0iMTAlIiBzdG9wLWNvbG9yPSIjZmZmZmZmIi8+PC9saW5lYXJHcmFkaWVudD48L2RlZnM+PHJlY3QgeD0iMCIgeT0iMCIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0idXJsKCNncmFkKSIgLz48L3N2Zz4g'); background-size: 100%; background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #eaeaea), color-stop(10%, #ffffff)); background-image: -moz-linear-gradient(#eaeaea, #ffffff 10%); background-image: -webkit-linear-gradient(#eaeaea, #ffffff 10%); background-image: linear-gradient(#eaeaea, #ffffff 10%); } .field input.text, .field textarea, .field .TreeDropdownField { background: #fff; border: 1px solid #b3b3b3; padding: 7px 7px; line-height: 16px; margin: 0; outline: none; -moz-transition: 0.2s box-shadow ease-in; -webkit-transition: 0.2s box-shadow ease-in; -o-transition: 0.2s box-shadow ease-in; transition: 0.2s box-shadow ease-in; -moz-transition: 0.2s border ease-in; -webkit-transition: 0.2s border ease-in; -o-transition: 0.2s border ease-in; transition: 0.2s border ease-in; -moz-border-radius: 4px; -webkit-border-radius: 4px; border-radius: 4px; background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2VhZWFlYSIvPjxzdG9wIG9mZnNldD0iMTAlIiBzdG9wLWNvbG9yPSIjZmZmZmZmIi8+PC9saW5lYXJHcmFkaWVudD48L2RlZnM+PHJlY3QgeD0iMCIgeT0iMCIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0idXJsKCNncmFkKSIgLz48L3N2Zz4g'); background-size: 100%; background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #eaeaea), color-stop(10%, #ffffff)); background-image: -moz-linear-gradient(#eaeaea, #ffffff 10%); background-image: -webkit-linear-gradient(#eaeaea, #ffffff 10%); background-image: linear-gradient(#eaeaea, #ffffff 10%); }
.field input.text:focus, .field textarea:focus, .field .TreeDropdownField:focus { border: 1px solid #9a9a9a; border-top-color: #808080; -moz-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2) inset; -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2) inset; box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2) inset; } .field input.text:focus, .field textarea:focus, .field .TreeDropdownField:focus { border: 1px solid #9a9a9a; border-top-color: #808080; -moz-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2) inset; -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2) inset; box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2) inset; }
.field input[disabled], .field input.disabled, .field textarea[disabled], .field textarea.disabled, .field select[disabled], .field select.disabled { color: #777777; background: #efefef; background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2JjYmNiYyIvPjxzdG9wIG9mZnNldD0iMTAlIiBzdG9wLWNvbG9yPSIjZWZlZmVmIi8+PHN0b3Agb2Zmc2V0PSI5MCUiIHN0b3AtY29sb3I9IiNmZmZmZmYiLz48c3RvcCBvZmZzZXQ9IjEwMCUiIHN0b3AtY29sb3I9IiNiY2JjYmMiLz48L2xpbmVhckdyYWRpZW50PjwvZGVmcz48cmVjdCB4PSIwIiB5PSIwIiB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSJ1cmwoI2dyYWQpIiAvPjwvc3ZnPiA='); background-size: 100%; background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #bcbcbc), color-stop(10%, #efefef), color-stop(90%, #ffffff), color-stop(100%, #bcbcbc)); background-image: -moz-linear-gradient(#bcbcbc, #efefef 10%, #ffffff 90%, #bcbcbc); background-image: -webkit-linear-gradient(#bcbcbc, #efefef 10%, #ffffff 90%, #bcbcbc); background-image: linear-gradient(#bcbcbc, #efefef 10%, #ffffff 90%, #bcbcbc); border: 1px solid #b3b3b3; } .field input[disabled], .field input.disabled, .field textarea[disabled], .field textarea.disabled, .field select[disabled], .field select.disabled { color: #9ba5ae; background: #efefef; background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2JjYmNiYyIvPjxzdG9wIG9mZnNldD0iMTAlIiBzdG9wLWNvbG9yPSIjZWZlZmVmIi8+PHN0b3Agb2Zmc2V0PSI5MCUiIHN0b3AtY29sb3I9IiNmZmZmZmYiLz48c3RvcCBvZmZzZXQ9IjEwMCUiIHN0b3AtY29sb3I9IiNiY2JjYmMiLz48L2xpbmVhckdyYWRpZW50PjwvZGVmcz48cmVjdCB4PSIwIiB5PSIwIiB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSJ1cmwoI2dyYWQpIiAvPjwvc3ZnPiA='); background-size: 100%; background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #bcbcbc), color-stop(10%, #efefef), color-stop(90%, #ffffff), color-stop(100%, #bcbcbc)); background-image: -moz-linear-gradient(#bcbcbc, #efefef 10%, #ffffff 90%, #bcbcbc); background-image: -webkit-linear-gradient(#bcbcbc, #efefef 10%, #ffffff 90%, #bcbcbc); background-image: linear-gradient(#bcbcbc, #efefef 10%, #ffffff 90%, #bcbcbc); border: 1px solid #b3b3b3; }
.field#Action { box-shadow: none; } .field#Action { box-shadow: none; }
.field.cms-description-toggle > .middleColumn { display: inline-block; vertical-align: middle; margin-left: 0; width: 36%; min-width: 300px; } .field.cms-description-toggle > .middleColumn { display: inline-block; vertical-align: middle; margin-left: 0; width: 36%; min-width: 300px; }
.field.cms-description-toggle .right { display: inline-block; vertical-align: middle; height: 15px; margin: 0 0 0 7px; } .field.cms-description-toggle .right { display: inline-block; vertical-align: middle; height: 15px; margin: 0 0 0 7px; }
@ -229,11 +252,11 @@ form.small .field input.text, form.small .field textarea, form.small .field sele
.cms input.loading, .cms button.loading, .cms input.ui-state-default.loading, .cms .ui-widget-content input.ui-state-default.loading, .cms .ui-widget-header input.ui-state-default.loading { color: #525252; border-color: #d5d3d3; cursor: default; } .cms input.loading, .cms button.loading, .cms input.ui-state-default.loading, .cms .ui-widget-content input.ui-state-default.loading, .cms .ui-widget-header input.ui-state-default.loading { color: #525252; border-color: #d5d3d3; cursor: default; }
.cms input.loading .ui-icon, .cms button.loading .ui-icon, .cms input.ui-state-default.loading .ui-icon, .cms .ui-widget-content input.ui-state-default.loading .ui-icon, .cms .ui-widget-header input.ui-state-default.loading .ui-icon { background: transparent url(../../images/network-save.gif) no-repeat 0 0; } .cms input.loading .ui-icon, .cms button.loading .ui-icon, .cms input.ui-state-default.loading .ui-icon, .cms .ui-widget-content input.ui-state-default.loading .ui-icon, .cms .ui-widget-header input.ui-state-default.loading .ui-icon { background: transparent url(../../images/network-save.gif) no-repeat 0 0; }
.cms input.loading.ss-ui-action-constructive .ui-icon, .cms button.loading.ss-ui-action-constructive .ui-icon { background: transparent url(../../images/network-save-constructive.gif) no-repeat 0 0; } .cms input.loading.ss-ui-action-constructive .ui-icon, .cms button.loading.ss-ui-action-constructive .ui-icon { background: transparent url(../../images/network-save-constructive.gif) no-repeat 0 0; }
.cms .ss-ui-button { margin-top: 0px; font-weight: bold; text-decoration: none; line-height: 16px; color: #393939; border: 1px solid #c0c0c2; border-bottom: 1px solid #a6a6a9; cursor: pointer; background-color: #e6e6e6; white-space: nowrap; background: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2ZmZmZmZiIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iI2Q5ZDlkOSIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA=='); background: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #ffffff), color-stop(100%, #d9d9d9)); background: -moz-linear-gradient(#ffffff, #d9d9d9); background: -webkit-linear-gradient(#ffffff, #d9d9d9); background: linear-gradient(#ffffff, #d9d9d9); text-shadow: white 0 1px 1px; /* constructive */ /* destructive */ } .cms .ss-ui-button { margin-top: 0px; font-weight: bold; text-decoration: none; line-height: 16px; color: #393939; border: 1px solid #d0d3d5; border-bottom: 1px solid #b5babd; cursor: pointer; background-color: #e6e6e6; white-space: nowrap; background: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2ZmZmZmZiIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iI2Q5ZDlkOSIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA=='); background: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #ffffff), color-stop(100%, #d9d9d9)); background: -moz-linear-gradient(#ffffff, #d9d9d9); background: -webkit-linear-gradient(#ffffff, #d9d9d9); background: linear-gradient(#ffffff, #d9d9d9); text-shadow: white 0 1px 1px; /* constructive */ /* destructive */ }
.cms .ss-ui-button.ui-state-hover, .cms .ss-ui-button:hover { text-decoration: none; background-color: white; background: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2ZmZmZmZiIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iI2U2ZTZlNiIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA=='); background: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #ffffff), color-stop(100%, #e6e6e6)); background: -moz-linear-gradient(#ffffff, #e6e6e6); background: -webkit-linear-gradient(#ffffff, #e6e6e6); background: linear-gradient(#ffffff, #e6e6e6); -moz-box-shadow: 0 0 5px #b3b3b3; -webkit-box-shadow: 0 0 5px #b3b3b3; box-shadow: 0 0 5px #b3b3b3; } .cms .ss-ui-button.ui-state-hover, .cms .ss-ui-button:hover { text-decoration: none; background-color: white; background: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2ZmZmZmZiIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iI2U2ZTZlNiIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA=='); background: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #ffffff), color-stop(100%, #e6e6e6)); background: -moz-linear-gradient(#ffffff, #e6e6e6); background: -webkit-linear-gradient(#ffffff, #e6e6e6); background: linear-gradient(#ffffff, #e6e6e6); -moz-box-shadow: 0 0 5px #b3b3b3; -webkit-box-shadow: 0 0 5px #b3b3b3; box-shadow: 0 0 5px #b3b3b3; }
.cms .ss-ui-button:active, .cms .ss-ui-button:focus, .cms .ss-ui-button.ui-state-active, .cms .ss-ui-button.ui-state-focus { border: 1px solid #b3b3b3; background-color: white; background: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2ZmZmZmZiIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iI2U2ZTZlNiIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA=='); background: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #ffffff), color-stop(100%, #e6e6e6)); background: -moz-linear-gradient(#ffffff, #e6e6e6); background: -webkit-linear-gradient(#ffffff, #e6e6e6); background: linear-gradient(#ffffff, #e6e6e6); -moz-box-shadow: 0 0 5px #b3b3b3 inset; -webkit-box-shadow: 0 0 5px #b3b3b3 inset; box-shadow: 0 0 5px #b3b3b3 inset; } .cms .ss-ui-button:active, .cms .ss-ui-button:focus, .cms .ss-ui-button.ui-state-active, .cms .ss-ui-button.ui-state-focus { border: 1px solid #b3b3b3; background-color: white; background: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2ZmZmZmZiIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iI2U2ZTZlNiIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA=='); background: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #ffffff), color-stop(100%, #e6e6e6)); background: -moz-linear-gradient(#ffffff, #e6e6e6); background: -webkit-linear-gradient(#ffffff, #e6e6e6); background: linear-gradient(#ffffff, #e6e6e6); -moz-box-shadow: 0 0 5px #b3b3b3 inset; -webkit-box-shadow: 0 0 5px #b3b3b3 inset; box-shadow: 0 0 5px #b3b3b3 inset; }
.cms .ss-ui-button.ss-ui-action-minor span { padding-left: 0; padding-right: 0; } .cms .ss-ui-button.ss-ui-action-minor span { padding-left: 0; padding-right: 0; }
.cms .ss-ui-button.ss-ui-action-constructive { text-shadow: none; font-weight: bold; color: white; border-color: #1F9433; border-bottom-color: #166a24; background-color: #1F9433; background: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iIzk0YmU0MiIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iIzFmOTQzMyIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA=='); background: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #94be42), color-stop(100%, #1f9433)); background: -moz-linear-gradient(#94be42, #1f9433); background: -webkit-linear-gradient(#94be42, #1f9433); background: linear-gradient(#94be42, #1f9433); text-shadow: #1c872f 0 -1px -1px; } .cms .ss-ui-button.ss-ui-action-constructive { text-shadow: none; font-weight: bold; color: white; border-color: #1F9433; border-bottom-color: #166a24; background-color: #1F9433; background: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iIzkzYmU0MiIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iIzFmOTQzMyIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA=='); background: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #93be42), color-stop(100%, #1f9433)); background: -moz-linear-gradient(#93be42, #1f9433); background: -webkit-linear-gradient(#93be42, #1f9433); background: linear-gradient(#93be42, #1f9433); text-shadow: #1c872f 0 -1px -1px; }
.cms .ss-ui-button.ss-ui-action-constructive.ui-state-hover, .cms .ss-ui-button.ss-ui-action-constructive:hover { border-color: #166a24; background-color: #1F9433; background: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2E0Y2EzYSIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iIzIzYTkzYSIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA=='); background: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #a4ca3a), color-stop(100%, #23a93a)); background: -moz-linear-gradient(#a4ca3a, #23a93a); background: -webkit-linear-gradient(#a4ca3a, #23a93a); background: linear-gradient(#a4ca3a, #23a93a); } .cms .ss-ui-button.ss-ui-action-constructive.ui-state-hover, .cms .ss-ui-button.ss-ui-action-constructive:hover { border-color: #166a24; background-color: #1F9433; background: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2E0Y2EzYSIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iIzIzYTkzYSIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA=='); background: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #a4ca3a), color-stop(100%, #23a93a)); background: -moz-linear-gradient(#a4ca3a, #23a93a); background: -webkit-linear-gradient(#a4ca3a, #23a93a); background: linear-gradient(#a4ca3a, #23a93a); }
.cms .ss-ui-button.ss-ui-action-constructive:active, .cms .ss-ui-button.ss-ui-action-constructive:focus, .cms .ss-ui-button.ss-ui-action-constructive.ui-state-active, .cms .ss-ui-button.ss-ui-action-constructive.ui-state-focus { background-color: #1d8c30; -moz-box-shadow: inset 0 1px 3px #17181a, 0 1px 0 rgba(255, 255, 255, 0.6); -webkit-box-shadow: inset 0 1px 3px #17181a, 0 1px 0 rgba(255, 255, 255, 0.6); box-shadow: inset 0 1px 3px #17181a, 0 1px 0 rgba(255, 255, 255, 0.6); } .cms .ss-ui-button.ss-ui-action-constructive:active, .cms .ss-ui-button.ss-ui-action-constructive:focus, .cms .ss-ui-button.ss-ui-action-constructive.ui-state-active, .cms .ss-ui-button.ss-ui-action-constructive.ui-state-focus { background-color: #1d8c30; -moz-box-shadow: inset 0 1px 3px #17181a, 0 1px 0 rgba(255, 255, 255, 0.6); -webkit-box-shadow: inset 0 1px 3px #17181a, 0 1px 0 rgba(255, 255, 255, 0.6); box-shadow: inset 0 1px 3px #17181a, 0 1px 0 rgba(255, 255, 255, 0.6); }
.cms .ss-ui-button.ss-ui-action-destructive { color: #f00; background-color: #e6e6e6; } .cms .ss-ui-button.ss-ui-action-destructive { color: #f00; background-color: #e6e6e6; }
@ -353,6 +376,32 @@ fieldset.switch-states.size_5 input:checked:nth-of-type(5) ~ .slide-button { lef
@-webkit-keyframes bugfix { from { position: relative; } @-webkit-keyframes bugfix { from { position: relative; }
to { position: relative; } } to { position: relative; } }
.cms-content-filters fieldset { margin-left: -16px; margin-right: -16px; }
.cms-content-filters .fieldgroup { width: 50%; display: inline-block; max-width: 440px; padding-right: 16px; padding-left: 16px; margin-bottom: 16px; box-sizing: border-box; margin-right: -2px; vertical-align: top; }
.cms-content-filters .fieldgroup .first label, .cms-content-filters .fieldgroup .first h1, .cms-content-filters .fieldgroup .first h2, .cms-content-filters .fieldgroup .first h3, .cms-content-filters .fieldgroup .first h4, .cms-content-filters .fieldgroup .first h5 { display: block; width: 176px; padding: 8px 8px 6px 0; line-height: 16px; font-weight: bold; margin: 0; font-size: 100%; }
.cms-content-filters .fieldgroup .field { width: 100%; padding-right: 0; padding-left: 0; }
.cms-content-filters .fieldgroup .fieldgroup-field { position: relative; margin-right: 0; width: 48%; display: inline-block; padding: 0; }
.cms-content-filters .fieldgroup .fieldgroup-field .description { margin-top: 24px; }
.cms-content-filters .fieldgroup .fieldgroup-field label { position: absolute; top: 28px; font-style: italic; color: #777; font-weight: normal; }
.cms-content-filters .fieldgroup .fieldgroup-field.first { width: 100%; float: left; }
.cms-content-filters .fieldgroup .fieldgroup-field.last { padding-right: 0; float: right; }
.cms-content-filters .fieldgroup .fieldgroup { margin: 0; padding: 0; }
.cms-content-filters .field { border: none; box-shadow: none; width: 50%; max-width: 440px; display: inline-block; margin: 0 0 8px 0; padding-right: 16px; padding-left: 16px; padding-bottom: 0; box-sizing: border-box; margin-right: -2px; vertical-align: top; }
.cms-content-filters .field label.left { text-shadow: none; padding-bottom: 6px; }
.cms-content-filters .field.dropdown { float: none; display: inline-block; }
.cms-content-filters .field .chzn-container { width: 100% !important; max-width: 100%; }
.cms-content-filters .field input.text { max-width: 100%; }
.cms-content-filters .field.checkbox { display: block; }
.cms-content-filters .importSpec { margin-bottom: 8px; padding-left: 16px; }
.cms-content-filters .description { margin-left: 0; }
.cms-content-filters .middleColumn { width: 100%; margin-left: 0; max-width: 100%; }
.cms-content-filters .Actions { margin: 8px 0 16px; }
@media screen and (max-width: 767px) { .cms-content-filters fieldset .field, .cms-content-filters fieldset .fieldgroup { width: 100%; max-width: 100%; } }
.cms-panel .cms-content-filters .field, .cms-panel .cms-content-filters .fieldgroup { width: 100%; margin-bottom: 16px; }
.cms-panel .cms-content-filters .fieldgroup-field h4 { padding-top: 0; }
.cms-panel .cms-content-filters .fieldgroup-field label { position: static; }
.cms-panel .cms-content-filters .Actions { margin-bottom: 0; }
/** /**
* This file defines most styles of the CMS: Colors, fonts, backgrounds, * This file defines most styles of the CMS: Colors, fonts, backgrounds,
* alignments, dimensions. * alignments, dimensions.
@ -383,12 +432,12 @@ body.cms { overflow: hidden; }
.cms-preview, .cms-menu, .cms-content, .cms-content-header, .cms-content-tools, .cms-content-fields, .cms-edit-form, .cms-preview, .cms-preview iframe, .cms-preview-controls { display: inline-block; vertical-align: middle; *vertical-align: auto; *zoom: 1; *display: inline; } .cms-preview, .cms-menu, .cms-content, .cms-content-header, .cms-content-tools, .cms-content-fields, .cms-edit-form, .cms-preview, .cms-preview iframe, .cms-preview-controls { display: inline-block; vertical-align: middle; *vertical-align: auto; *zoom: 1; *display: inline; }
.cms-content-header { padding-left: 16px; z-index: 60; min-height: 40px; background-image: url(../images/textures/cms_content_header.png); background-repeat: repeat; background-position: left bottom; background-color: #D4D6D8; } .cms-content-header { padding-left: 16px; z-index: 60; min-height: 52px; background-image: url(../images/textures/cms_content_header.png); background-repeat: repeat; background-position: left bottom; background-color: #E6EAED; }
.cms-content-header a { color: #0073C1; } .cms-content-header a { color: #0073C1; }
.cms-content-header .backlink span.btn-icon-back { height: 16px; } .cms-content-header .backlink span.btn-icon-back { height: 16px; }
.cms-content-header h2 { font-size: 14px; font-weight: bold; margin: 0; margin-bottom: 8px; } .cms-content-header h2 { font-size: 14px; font-weight: bold; margin: 0; margin-bottom: 8px; }
.cms-content-header h2 * { vertical-align: middle; } .cms-content-header h2 * { vertical-align: middle; }
.cms-content-header .cms-content-header-info { float: left; padding-top: 6px; } .cms-content-header .cms-content-header-info { float: left; padding-top: 7px; }
.cms-content-header .cms-content-header-info * { display: inline-block; } .cms-content-header .cms-content-header-info * { display: inline-block; }
.cms-content-header .cms-content-header-info .section-icon { opacity: 0.2; margin-right: 4px; background-repeat: no-repeat; } .cms-content-header .cms-content-header-info .section-icon { opacity: 0.2; margin-right: 4px; background-repeat: no-repeat; }
.cms-content-header .ss-ui-button { line-height: 24px; } .cms-content-header .ss-ui-button { line-height: 24px; }
@ -398,51 +447,112 @@ body.cms { overflow: hidden; }
.cms-container .column-hidden { display: none; } .cms-container .column-hidden { display: none; }
.cms-content-header-top { display: inline-block; vertical-align: middle; *vertical-align: auto; *zoom: 1; *display: inline; width: 100%; }
.has-panel .cms-content-header.north { padding-left: 16px; }
.has-panel .cms-content-header.north.collapsed .cms-content-header-info { width: 24px; text-align: right; padding-left: 12px; padding-right: 8px; }
.has-panel .cms-content-header.north.collapsed .view-controls, .has-panel .cms-content-header.north.collapsed .section-label { display: none; }
.has-panel .cms-content-header.north.collapsed .cms-content-header-nav { margin-left: 31px; }
.has-panel .cms-content-header-info { position: absolute; top: 0; left: 0; bottom: 1px; width: 272px; margin-left: -4px; padding-bottom: 8px; padding-left: 16px; padding-right: 16px; border-right: 1px solid #C1C7CC; }
.has-panel .cms-content-header-nav { margin-left: 280px; }
.has-panel .section-heading { margin-top: 8px; padding-left: 4px; }
.has-panel .section-icon { vertical-align: middle; }
.has-panel .section-label { vertical-align: middle; font-size: 1.2em; font-weight: normal; }
.has-panel .breadcrumbs-wrapper { float: left; padding-top: 7px; padding-left: 20px; }
.has-panel .cms-content-header-tabs { margin-top: 8px; }
.has-panel .view-controls { float: right; margin-top: 1px; }
.has-panel .font-icon-search { margin-top: 1px; }
.has-panel .font-icon-search::before { font-size: 1.3em; }
.has-panel .cms-content-tools .cms-panel-content { padding-top: 0; overflow-x: hidden; }
#page-title-heading { line-height: 1.2em; }
/** ------------------------------------------------------------------ CMS Breadcrumbs ----------------------------------------------------------------- */
.breadcrumbs-wrapper .crumb, .breadcrumbs-wrapper .sep { font-size: .8em; line-height: 1.2em; font-weight: normal; }
.breadcrumbs-wrapper .crumb.last { display: block; padding: 8px 0; font-size: 1.2em; }
.breadcrumbs-wrapper .sep + .crumb.last { padding-top: 0; padding-bottom: 0; }
/** ------------------------------------------------------------------ Filters available in the top bar. This is a togglable element that displays a form used for filtering content. ----------------------------------------------------------------- */
.cms-content-filters { display: none; width: 100%; margin: 0 0 0 -16px; padding: 16px; background-color: #dddfe1; }
.cms-content-filters .cms-search-form { margin-bottom: 0; }
.cms-tabset-nav-primary { display: inline-block; vertical-align: middle; *vertical-align: auto; *zoom: 1; *display: inline; vertical-align: middle; }
/** ------------------------------------------------------------------ Buttons that use font icons. There are !important rules here because we need to override some Tab styling. It's tidier to have some !important rules here than have the Tab styles littered with load of context specific rules for icon-buttons. Icon buttons styles should always take presedence over Tab styles. Tabs should be refactored to use weaker selectors. ----------------------------------------------------------------- */
a.icon-button, .ui-tabs .ui-tabs-nav li a.icon-button, button.ss-ui-button.icon-button { vertical-align: middle; margin-right: 2px; padding: .4em; font-size: 1.2em; letter-spacing: -3px; text-indent: 0; text-shadow: none; line-height: 1em; color: #66727d; background-color: transparent; background-image: none; border: 0; }
a.icon-button:hover, .ui-tabs .ui-tabs-nav li a.icon-button:hover, a.icon-button:active, .ui-tabs .ui-tabs-nav li a.icon-button:active, a.icon-button:focus, .ui-tabs .ui-tabs-nav li a.icon-button:focus, button.ss-ui-button.icon-button:hover, button.ss-ui-button.icon-button:active, button.ss-ui-button.icon-button:focus { border: 0; box-shadow: none; background-image: none; text-decoration: none; }
a.icon-button:hover, .ui-tabs .ui-tabs-nav li a.icon-button:hover, button.ss-ui-button.icon-button:hover { background-color: #d4dbe1; }
a.icon-button.active, .ui-tabs .ui-tabs-nav li a.active.icon-button, a.icon-button:active, .ui-tabs .ui-tabs-nav li a.icon-button:active, button.ss-ui-button.icon-button.active, button.ss-ui-button.icon-button:active { background-color: #d4dbe1; box-shadow: inset 0 0 3px rgba(191, 194, 196, 0.9); }
a.icon-button .ui-button-text, .ui-tabs .ui-tabs-nav li a.icon-button .ui-button-text, button.ss-ui-button.icon-button .ui-button-text { display: none; }
.ui-tabs.ui-tabs-nav li.cms-tabset-icon.ui-corner-top.ui-state-active a.icon-button.cms-panel-link, .ui-tabs.ui-tabs-nav li.cms-tabset-icon.ui-corner-top.ui-state-default a.icon-button.cms-panel-link, .ui-tabs.ui-tabs-nav li.cms-tabset-icon.ui-corner-top.ui-state-active button.ss-ui-button.icon-button.cms-panel-link, .ui-tabs.ui-tabs-nav li.cms-tabset-icon.ui-corner-top.ui-state-default button.ss-ui-button.icon-button.cms-panel-link { padding: 6px 8px; line-height: 1em; background-color: transparent; background-image: none; border: 0; }
.ui-tabs.ui-tabs-nav li.cms-tabset-icon.ui-corner-top.ui-state-active a.icon-button.cms-panel-link:before, .ui-tabs.ui-tabs-nav li.cms-tabset-icon.ui-corner-top.ui-state-default a.icon-button.cms-panel-link:before, .ui-tabs.ui-tabs-nav li.cms-tabset-icon.ui-corner-top.ui-state-active button.ss-ui-button.icon-button.cms-panel-link:before, .ui-tabs.ui-tabs-nav li.cms-tabset-icon.ui-corner-top.ui-state-default button.ss-ui-button.icon-button.cms-panel-link:before { margin-top: 2px; }
.ModelAdmin a.icon-button, .ModelAdmin .ui-tabs .ui-tabs-nav li a.icon-button, .ui-tabs .ui-tabs-nav li .ModelAdmin a.icon-button, .ModelAdmin button.ss-ui-button.icon-button { margin-top: -11px; }
.icon-button-group { display: inline-block; vertical-align: middle; *vertical-align: auto; *zoom: 1; *display: inline; margin-top: 2px; vertical-align: middle; border: 1px solid #CDCCD0; -moz-border-radius: 4px; -webkit-border-radius: 4px; border-radius: 4px; }
.icon-button-group a.icon-button, .icon-button-group .ui-tabs .ui-tabs-nav li a.icon-button, .ui-tabs .ui-tabs-nav li .icon-button-group a.icon-button, .icon-button-group button.ss-ui-button.icon-button { margin-right: 0; line-height: 13px; -moz-border-radius: 0 0 0 0; -webkit-border-radius: 0; border-radius: 0 0 0 0; }
.icon-button-group a.icon-button:first-child, .icon-button-group .ui-tabs .ui-tabs-nav li a.icon-button:first-child, .ui-tabs .ui-tabs-nav li .icon-button-group a.icon-button:first-child, .icon-button-group button.ss-ui-button.icon-button:first-child { -moz-border-radius: 3px 0 0 3px; -webkit-border-radius: 3px; border-radius: 3px 0 0 3px; }
.icon-button-group a.icon-button:last-child, .icon-button-group .ui-tabs .ui-tabs-nav li a.icon-button:last-child, .ui-tabs .ui-tabs-nav li .icon-button-group a.icon-button:last-child, .icon-button-group button.ss-ui-button.icon-button:last-child { -moz-border-radius: 0 3px 3px 0; -webkit-border-radius: 0; border-radius: 0 3px 3px 0; }
.icon-button-group a.icon-button:hover, .icon-button-group .ui-tabs .ui-tabs-nav li a.icon-button:hover, .ui-tabs .ui-tabs-nav li .icon-button-group a.icon-button:hover, .icon-button-group button.ss-ui-button.icon-button:hover { background: #ECEFF1; padding-bottom: 5px; }
.icon-button-group a.icon-button.active:hover, .icon-button-group button.ss-ui-button.icon-button.active:hover { background: #d4dbe1; }
.icon-button-group a.icon-button + a.icon-button, .icon-button-group .ui-tabs .ui-tabs-nav li a.icon-button + a.icon-button, .icon-button-group a.icon-button + button.ss-ui-button.icon-button, .icon-button-group .ui-tabs .ui-tabs-nav li a.icon-button + button.ss-ui-button.icon-button, .icon-button-group button.ss-ui-button.icon-button + a.icon-button, .icon-button-group .ui-tabs .ui-tabs-nav li button.ss-ui-button.icon-button + a.icon-button, .icon-button-group button.ss-ui-button.icon-button + button.ss-ui-button.icon-button { border-left: 1px solid #CDCCD0; }
.icon-button-group .ui-tabs.ui-tabs-nav { border-left: 0 !important; margin-top: -2px !important; padding-right: 0 !important; overflow: hidden; }
.icon-button-group .ui-tabs.ui-tabs-nav .cms-tabset-icon.ui-state-default { background-color: transparent; background-image: none; border-left: 0; border-right: 0; -moz-box-shadow: none; -webkit-box-shadow: none; box-shadow: none; }
.icon-button-group .ui-tabs.ui-tabs-nav .cms-tabset-icon.ui-state-default + .cms-tabset-icon.ui-state-default { border-left: 1px solid #CDCCD0; }
.icon-button-group .ui-tabs.ui-tabs-nav:hover { background: #ECEFF1; }
.icon-button-group .ui-tabs.ui-tabs-nav.active:hover { background: #d4dbe1; }
.icon-button-group .ui-tabs.ui-tabs-nav .cms-tabset-icon.ui-state-active { background-color: #d4dbe1; box-shadow: inset 0 0 3px rgba(191, 194, 196, 0.9); }
.cms-content-header-tabs .icon-button-group { overflow: hidden; }
/** -------------------------------------------- Tabs -------------------------------------------- */ /** -------------------------------------------- Tabs -------------------------------------------- */
.ui-tabs { padding: 0; background: none; } .ui-tabs { padding: 0; background: none; }
.ui-tabs .ui-tabs { position: static; } .ui-tabs .ui-tabs { position: static; }
.ui-tabs .ui-tabs-panel { padding: 24px; background: transparent; border: 0; } .ui-tabs .ui-tabs-panel { padding: 12px 16px; background: transparent; border: 0; }
.ui-tabs .ui-tabs-panel.cms-edit-form { padding: 0; } .ui-tabs .ui-tabs-panel.cms-edit-form { padding: 0; }
.ui-tabs .ui-tabs-panel.cms-panel-padded { padding: 16px 16px; }
.ui-tabs .ui-widget-header { border: 0; background: none; } .ui-tabs .ui-widget-header { border: 0; background: none; }
.ui-tabs .ui-tabs-nav { float: right; margin: 16px 0 -1px 0; padding: 0 12px 0 0; border-bottom: none; } .ui-tabs .ui-tabs-nav { float: right; margin: 16px 0 -1px 0; padding: 0 16px 0 0; border-bottom: 0; }
.ui-tabs .ui-tabs-nav ~ .ui-tabs-panel { border-top: 1px solid #c0c0c2; clear: both; } .ui-tabs .ui-tabs-nav ~ .ui-tabs-panel { clear: both; }
.ui-tabs .ui-tabs-nav li { top: 0; float: left; border-bottom: 0 !important; } .ui-tabs .ui-tabs-nav li { top: 0; float: left; margin-top: 0; }
.ui-tabs .ui-tabs-nav li a { display: inline-block; vertical-align: middle; *vertical-align: auto; *zoom: 1; *display: inline; float: none; font-weight: bold; color: #444; line-height: 32px; padding: 0 16px 0; } .ui-tabs .ui-tabs-nav li a { display: inline-block; vertical-align: middle; *vertical-align: auto; *zoom: 1; *display: inline; float: none; font-weight: normal; color: #66727d; line-height: 32px; padding: 0 12px 0; }
.ui-tabs .ui-tabs-nav li:last-child { margin-right: 0; } .ui-tabs .ui-tabs-nav li:last-child { margin-right: 0; }
.ui-tabs .ui-tabs-nav li.ui-tabs-active { margin-bottom: 0; } .ui-tabs .ui-tabs-nav .ui-state-default { border: 0; background: transparent; }
.ui-tabs .ui-tabs-nav .ui-state-default { border: 1px solid #c0c0c2; background: #ced7dc; } .ui-tabs .ui-tabs-nav .ui-state-default a { line-height: 28px; padding-top: 12px; padding-bottom: 8px; color: #7f8c97; }
.ui-tabs .ui-tabs-nav .ui-state-default a { color: #5e5e5e; text-shadow: #e6e6e6 0 1px 0; } .ui-tabs .ui-tabs-nav .ui-state-default a:hover { color: #66727d; }
.ui-tabs .ui-tabs-nav .ui-state-active { padding-bottom: 1px; border: 1px solid #c0c0c2; background-color: #e6eaed; } .ui-tabs .ui-tabs-nav .ui-state-active { padding-bottom: 1px; background-color: transparent; cursor: text; }
.ui-tabs .ui-tabs-nav .ui-state-active a { color: #444; } .ui-tabs .ui-tabs-nav .ui-state-active a { border-bottom: 4px solid #66727d; padding-left: 0; padding-right: 0; margin: 0 12px 0; color: #66727d; }
.ui-tabs .ui-tabs-nav.ui-state-active { border-color: #808080; } .ui-tabs .ui-tabs-nav.ui-state-active { border-color: #808080; }
.ui-tabs .ui-tabs-nav li.cms-tabset-icon { text-indent: -9999em; } .ui-tabs .ui-tabs-nav li.cms-tabset-icon.ui-corner-top { text-indent: -9999em; }
.ui-tabs .ui-tabs-nav li.cms-tabset-icon a { display: block; padding-left: 40px; padding-right: 0; } .ui-tabs .ui-tabs-nav li.cms-tabset-icon.ui-corner-top a { display: block; padding-left: 40px; padding-right: 0; margin: 0; }
.ui-tabs .ui-tabs-nav li.cms-tabset-icon.list a { background: url('../images/sprites-64x64-s45180e3c4f.png') 0 -304px no-repeat; } .ui-tabs .ui-tabs-nav li.cms-tabset-icon.ui-corner-top.list a { background: url('../images/sprites-64x64-s45180e3c4f.png') 0 -304px no-repeat; }
.ui-tabs .ui-tabs-nav li.cms-tabset-icon.tree a { background: url('../images/sprites-64x64-s45180e3c4f.png') 0 -504px no-repeat; } .ui-tabs .ui-tabs-nav li.cms-tabset-icon.ui-corner-top.tree a { background: url('../images/sprites-64x64-s45180e3c4f.png') 0 -504px no-repeat; }
.ui-tabs .ui-tabs-nav li.cms-tabset-icon.gallery a { background: url('../images/sprites-64x64-s45180e3c4f.png') 0 -204px no-repeat; } .ui-tabs .ui-tabs-nav li.cms-tabset-icon.ui-corner-top.gallery a { background: url('../images/sprites-64x64-s45180e3c4f.png') 0 -204px no-repeat; }
.ui-tabs .ui-tabs-nav li.cms-tabset-icon.edit a { background: url('../images/sprites-64x64-s45180e3c4f.png') 0 -104px no-repeat; } .ui-tabs .ui-tabs-nav li.cms-tabset-icon.ui-corner-top.edit a { background: url('../images/sprites-64x64-s45180e3c4f.png') 0 -104px no-repeat; }
.ui-tabs .ui-tabs-nav li.cms-tabset-icon.search a { background: url('../images/sprites-64x64-s45180e3c4f.png') 0 -404px no-repeat; } .ui-tabs .ui-tabs-nav li.cms-tabset-icon.ui-corner-top.search a { background: url('../images/sprites-64x64-s45180e3c4f.png') 0 -404px no-repeat; }
.ui-tabs .ui-tabs-nav li.cms-tabset-icon.list.ui-state-active a { background: url('../images/sprites-64x64-s45180e3c4f.png') 0 -254px no-repeat; } .ui-tabs .ui-tabs-nav li.cms-tabset-icon.ui-corner-top.list.ui-state-active a { background: url('../images/sprites-64x64-s45180e3c4f.png') 0 -254px no-repeat; }
.ui-tabs .ui-tabs-nav li.cms-tabset-icon.tree.ui-state-active a { background: url('../images/sprites-64x64-s45180e3c4f.png') 0 -454px no-repeat; } .ui-tabs .ui-tabs-nav li.cms-tabset-icon.ui-corner-top.tree.ui-state-active a { background: url('../images/sprites-64x64-s45180e3c4f.png') 0 -454px no-repeat; }
.ui-tabs .ui-tabs-nav li.cms-tabset-icon.gallery.ui-state-active a { background: url('../images/sprites-64x64-s45180e3c4f.png') 0 -154px no-repeat; } .ui-tabs .ui-tabs-nav li.cms-tabset-icon.ui-corner-top.gallery.ui-state-active a { background: url('../images/sprites-64x64-s45180e3c4f.png') 0 -154px no-repeat; }
.ui-tabs .ui-tabs-nav li.cms-tabset-icon.edit.ui-state-active a { background: url('../images/sprites-64x64-s45180e3c4f.png') 0 -54px no-repeat; } .ui-tabs .ui-tabs-nav li.cms-tabset-icon.ui-corner-top.edit.ui-state-active a { background: url('../images/sprites-64x64-s45180e3c4f.png') 0 -54px no-repeat; }
.ui-tabs .ui-tabs-nav li.cms-tabset-icon.search.ui-state-active a { background: url('../images/sprites-64x64-s45180e3c4f.png') 0 -354px no-repeat; } .ui-tabs .ui-tabs-nav li.cms-tabset-icon.ui-corner-top.search.ui-state-active a { background: url('../images/sprites-64x64-s45180e3c4f.png') 0 -354px no-repeat; }
.ui-tabs .cms-panel-padded .ui-tabs-panel { padding: 0; } .ui-tabs .cms-panel-padded .ui-tabs-panel { padding: 0; }
.ui-tabs .cms-panel-padded .ui-tabs-panel .ui-tabs-panel { padding: 8px 0 0 0; } .ui-tabs .cms-panel-padded .ui-tabs-panel .ui-tabs-panel { padding: 8px 0 0 0; }
.ui-tabs .cms-panel-padded .Actions { padding: 0; } .ui-tabs .cms-panel-padded .Actions { padding: 0; }
.ui-tabs.ss-tabset-tabshidden .ui-tabs-panel { border-top: none; } .ui-tabs.ss-tabset-tabshidden .ui-tabs-panel { border-top: none; }
/** Primary styles which sit on top of screen, with different tab colors. TODO Only use one "primary" selector and fix HTMLEditorField TabSet addExtraClass() */ /** Primary styles which sit on top of screen, with different tab colors. TODO Only use one "primary" selector and fix HTMLEditorField TabSet addExtraClass() */
.ui-tabs.cms-tabset-primary .ui-tabs-nav, .ui-tabs .ui-tabs-nav.cms-tabset-nav-primary, .ui-tabs .cms-content-header-tabs .ui-tabs-nav { margin-top: 0; border-left: 1px solid #b3b3b3; float: none; } .ui-tabs.cms-tabset-primary .ui-tabs-nav, .ui-tabs .ui-tabs-nav.cms-tabset-nav-primary, .ui-tabs .cms-content-header-tabs .ui-tabs-nav { margin-top: 0; float: none; }
.ui-tabs.cms-tabset-primary .ui-tabs-nav li, .ui-tabs .ui-tabs-nav.cms-tabset-nav-primary li, .ui-tabs .cms-content-header-tabs .ui-tabs-nav li { margin-right: 0; margin-top: 0; } .ui-tabs.cms-tabset-primary .ui-tabs-nav li, .ui-tabs .ui-tabs-nav.cms-tabset-nav-primary li, .ui-tabs .cms-content-header-tabs .ui-tabs-nav li { margin-right: 0; margin-top: 0; }
.ui-tabs.cms-tabset-primary .ui-tabs-nav li a, .ui-tabs .ui-tabs-nav.cms-tabset-nav-primary li a, .ui-tabs .cms-content-header-tabs .ui-tabs-nav li a { margin: 0; line-height: 39px; } .ui-tabs.cms-tabset-primary .ui-tabs-nav li a, .ui-tabs .ui-tabs-nav.cms-tabset-nav-primary li a, .ui-tabs .cms-content-header-tabs .ui-tabs-nav li a { margin: 0; line-height: 39px; padding-top: 0; padding-bottom: 0; }
.ui-tabs.cms-tabset-primary .ui-tabs-nav .ui-corner-all, .ui-tabs.cms-tabset-primary .ui-tabs-nav .ui-corner-top, .ui-tabs.cms-tabset-primary .ui-tabs-nav .ui-corner-right, .ui-tabs.cms-tabset-primary .ui-tabs-nav .ui-corner-tr, .ui-tabs.cms-tabset-primary .ui-tabs-nav .ui-corner-tl, .ui-tabs .ui-tabs-nav.cms-tabset-nav-primary .ui-corner-all, .ui-tabs .ui-tabs-nav.cms-tabset-nav-primary .ui-corner-top, .ui-tabs .ui-tabs-nav.cms-tabset-nav-primary .ui-corner-right, .ui-tabs .ui-tabs-nav.cms-tabset-nav-primary .ui-corner-tr, .ui-tabs .ui-tabs-nav.cms-tabset-nav-primary .ui-corner-tl, .ui-tabs .cms-content-header-tabs .ui-tabs-nav .ui-corner-all, .ui-tabs .cms-content-header-tabs .ui-tabs-nav .ui-corner-top, .ui-tabs .cms-content-header-tabs .ui-tabs-nav .ui-corner-right, .ui-tabs .cms-content-header-tabs .ui-tabs-nav .ui-corner-tr, .ui-tabs .cms-content-header-tabs .ui-tabs-nav .ui-corner-tl { border-radius: 0; } .ui-tabs.cms-tabset-primary .ui-tabs-nav .ui-corner-all, .ui-tabs.cms-tabset-primary .ui-tabs-nav .ui-corner-top, .ui-tabs.cms-tabset-primary .ui-tabs-nav .ui-corner-right, .ui-tabs.cms-tabset-primary .ui-tabs-nav .ui-corner-tr, .ui-tabs.cms-tabset-primary .ui-tabs-nav .ui-corner-tl, .ui-tabs .ui-tabs-nav.cms-tabset-nav-primary .ui-corner-all, .ui-tabs .ui-tabs-nav.cms-tabset-nav-primary .ui-corner-top, .ui-tabs .ui-tabs-nav.cms-tabset-nav-primary .ui-corner-right, .ui-tabs .ui-tabs-nav.cms-tabset-nav-primary .ui-corner-tr, .ui-tabs .ui-tabs-nav.cms-tabset-nav-primary .ui-corner-tl, .ui-tabs .cms-content-header-tabs .ui-tabs-nav .ui-corner-all, .ui-tabs .cms-content-header-tabs .ui-tabs-nav .ui-corner-top, .ui-tabs .cms-content-header-tabs .ui-tabs-nav .ui-corner-right, .ui-tabs .cms-content-header-tabs .ui-tabs-nav .ui-corner-tr, .ui-tabs .cms-content-header-tabs .ui-tabs-nav .ui-corner-tl { border-radius: 0; }
.ui-tabs.cms-tabset-primary .ui-tabs-nav .ui-state-default, .ui-tabs .ui-tabs-nav.cms-tabset-nav-primary .ui-state-default, .ui-tabs .cms-content-header-tabs .ui-tabs-nav .ui-state-default { -moz-box-shadow: rgba(201, 205, 206, 0.8) 0 0 2px; -webkit-box-shadow: rgba(201, 205, 206, 0.8) 0 0 2px; box-shadow: rgba(201, 205, 206, 0.8) 0 0 2px; background-color: #b0bec7; background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2Q0ZGJlMCIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iI2IwYmVjNyIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA=='); background-size: 100%; background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #d4dbe0), color-stop(100%, #b0bec7)); background-image: -moz-linear-gradient(#d4dbe0, #b0bec7); background-image: -webkit-linear-gradient(#d4dbe0, #b0bec7); background-image: linear-gradient(#d4dbe0, #b0bec7); border-top: none; border-right-color: #8399a7; border-left-color: #ced7dc; } .ui-tabs.cms-tabset-primary .ui-tabs-nav .ui-state-default, .ui-tabs .ui-tabs-nav.cms-tabset-nav-primary .ui-state-default, .ui-tabs .cms-content-header-tabs .ui-tabs-nav .ui-state-default { background: none; border-top: none; border: none; }
.ui-tabs.cms-tabset-primary .ui-tabs-nav .ui-state-active, .ui-tabs .ui-tabs-nav.cms-tabset-nav-primary .ui-state-active, .ui-tabs .cms-content-header-tabs .ui-tabs-nav .ui-state-active { -moz-box-shadow: none; -webkit-box-shadow: none; box-shadow: none; background: #e6eaed; border-top: none; border-right-color: #b3b3b3; border-left-color: #ECEFF1; z-index: 2; } .ui-tabs.cms-tabset-primary .ui-tabs-nav .ui-state-active, .ui-tabs .ui-tabs-nav.cms-tabset-nav-primary .ui-state-active, .ui-tabs .cms-content-header-tabs .ui-tabs-nav .ui-state-active { -moz-box-shadow: none; -webkit-box-shadow: none; box-shadow: none; background: transparent; border-top: none; border: none; z-index: 2; }
.ui-tabs.cms-tabset-primary .ui-tabs-nav .ui-state-active a, .ui-tabs .ui-tabs-nav.cms-tabset-nav-primary .ui-state-active a, .ui-tabs .cms-content-header-tabs .ui-tabs-nav .ui-state-active a { border-bottom: none; } .ui-tabs.cms-tabset-primary .ui-tabs-nav .ui-state-active a, .ui-tabs .ui-tabs-nav.cms-tabset-nav-primary .ui-state-active a, .ui-tabs .cms-content-header-tabs .ui-tabs-nav .ui-state-active a { border-bottom: 4px solid #66727d; padding: 0; margin: 0 12px 0; }
.cms-content-header-tabs { float: right; } .cms-content-header-tabs { float: right; margin-top: 8px; }
.cms-content-header-tabs .icon-button-group { margin-right: 16px; }
.cms-content-header-tabs .font-icon-search::before { font-size: 1.3em; }
.cms-content-fields .ui-tabs-nav { float: none; padding: 0; border-bottom: 1px solid #d0d3d5; margin: 0 16px 0; }
.cms-content-fields .ui-tabs-nav li { margin-bottom: -1px; }
.cms-content-fields .ui-tabs-nav li.first a { margin-left: 0; padding-left: 0; }
/** ------------------------------------------------------- Loading Interface ------------------------------------------------------- */ /** ------------------------------------------------------- Loading Interface ------------------------------------------------------- */
.cms-content-loading-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; z-index: 9998; } .cms-content-loading-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; z-index: 9998; }
@ -457,7 +567,7 @@ body.cms { overflow: hidden; }
.ss-loading-screen .loading-animation { display: none; position: absolute; left: 50%; margin-left: -21.5px; top: 80%; } .ss-loading-screen .loading-animation { display: none; position: absolute; left: 50%; margin-left: -21.5px; top: 80%; }
/** -------------------------------------------- Actions -------------------------------------------- */ /** -------------------------------------------- Actions -------------------------------------------- */
.cms-content-actions, .cms-preview-controls { margin: 0; padding: 12px 12px; z-index: 999; border-top: 1px solid #cacacc; -moz-box-shadow: 1px 0 0 #ECEFF1, rgba(248, 248, 248, 0.9) 0 1px 0px inset, rgba(201, 205, 206, 0.8) 0 0 1px; -webkit-box-shadow: 1px 0 0 #ECEFF1, rgba(248, 248, 248, 0.9) 0 1px 0px inset, rgba(201, 205, 206, 0.8) 0 0 1px; box-shadow: 1px 0 0 #ECEFF1, rgba(248, 248, 248, 0.9) 0 1px 0px inset, rgba(201, 205, 206, 0.8) 0 0 1px; height: 28px; background-color: #ECEFF1; } .cms-content-actions, .cms-preview-controls { margin: 0; padding: 12px 16px; z-index: 999; border-top: 1px solid #D2D5D8; -moz-box-shadow: 1px 0 0 #ECEFF1, rgba(248, 248, 248, 0.9) 0 1px 0px inset, rgba(201, 205, 206, 0.8) 0 0 1px; -webkit-box-shadow: 1px 0 0 #ECEFF1, rgba(248, 248, 248, 0.9) 0 1px 0px inset, rgba(201, 205, 206, 0.8) 0 0 1px; box-shadow: 1px 0 0 #ECEFF1, rgba(248, 248, 248, 0.9) 0 1px 0px inset, rgba(201, 205, 206, 0.8) 0 0 1px; height: 28px; background-color: #ECEFF1; }
/** -------------------------------------------- Messages -------------------------------------------- */ /** -------------------------------------------- Messages -------------------------------------------- */
.message { display: block; clear: both; margin: 0 0 8px; padding: 10px 12px; font-weight: normal; border: 1px #ccc solid; background: #fff; background: rgba(255, 255, 255, 0.5); text-shadow: none; -moz-border-radius: 3px 3px 3px 3px; -webkit-border-radius: 3px; border-radius: 3px 3px 3px 3px; } .message { display: block; clear: both; margin: 0 0 8px; padding: 10px 12px; font-weight: normal; border: 1px #ccc solid; background: #fff; background: rgba(255, 255, 255, 0.5); text-shadow: none; -moz-border-radius: 3px 3px 3px 3px; -webkit-border-radius: 3px; border-radius: 3px 3px 3px 3px; }
@ -506,24 +616,23 @@ body.cms { overflow: hidden; }
#PageType ul li .description { font-style: italic; display: inline; clear: none; margin: 0; } #PageType ul li .description { font-style: italic; display: inline; clear: none; margin: 0; }
/** -------------------------------------------- Content toolbar -------------------------------------------- */ /** -------------------------------------------- Content toolbar -------------------------------------------- */
.cms-content-toolbar { min-height: 29px; display: block; margin: 0 0 12px 0; padding-bottom: 0; border-bottom: 1px solid #d0d3d5; -webkit-box-shadow: 0 1px 0 rgba(248, 248, 248, 0.9); -moz-box-shadow: 0 1px 0 rgba(248, 248, 248, 0.9); -o-box-shadow: 0 1px 0 rgba(248, 248, 248, 0.9); box-shadow: 0 1px 0 rgba(248, 248, 248, 0.9); *zoom: 1; /* smaller treedropdown */ } .cms-content-toolbar { min-height: 29px; display: block; margin: 12px 0 0; padding-bottom: 0; *zoom: 1; border-bottom: 0; box-shadow: none; }
.cms-content-toolbar:after { content: "\0020"; display: block; height: 0; clear: both; overflow: hidden; visibility: hidden; } .cms-content-toolbar:after { content: "\0020"; display: block; height: 0; clear: both; overflow: hidden; visibility: hidden; }
.cms-content-toolbar .cms-tree-view-modes { float: right; padding-top: 5px; } .cms-content-toolbar .cms-tree-view-modes { float: right; padding-top: 5px; }
.cms-content-toolbar .cms-tree-view-modes * { display: inline-block; } .cms-content-toolbar .cms-tree-view-modes * { display: inline-block; }
.cms-content-toolbar .cms-tree-view-modes * label { color: #0073C1; } .cms-content-toolbar .cms-tree-view-modes * label { color: #0073C1; }
.cms-content-toolbar .chzn-container-single .chzn-single { height: 26px; line-height: 26px; padding-left: 25px; color: #576468; background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2U2ZTZlNiIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iI2NkY2RjZCIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA=='); background-size: 100%; background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #e6e6e6), color-stop(100%, #cdcdcd)); background-image: -moz-linear-gradient(#e6e6e6, #cdcdcd); background-image: -webkit-linear-gradient(#e6e6e6, #cdcdcd); background-image: linear-gradient(#e6e6e6, #cdcdcd); font-size: 13px; font-weight: bold; text-shadow: #e6e6e6 0 -1px 1px; box-shadow: none; } .cms-content-toolbar .ss-ui-button { margin-bottom: 12px; }
.cms-content-toolbar .chzn-container-single .chzn-single:hover { -moz-box-shadow: 0 0 5px #b3b3b3; -webkit-box-shadow: 0 0 5px #b3b3b3; box-shadow: 0 0 5px #b3b3b3; background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2ViZWJlYiIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iI2QyZDJkMiIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA=='); background-size: 100%; background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #ebebeb), color-stop(100%, #d2d2d2)); background-image: -moz-linear-gradient(#ebebeb, #d2d2d2); background-image: -webkit-linear-gradient(#ebebeb, #d2d2d2); background-image: linear-gradient(#ebebeb, #d2d2d2); } .cms-content-toolbar .cms-actions-buttons-row { clear: both; }
.cms-content-toolbar .chzn-container-single .chzn-single:active { -moz-box-shadow: inset 0 1px 3px #4d4d4d; -webkit-box-shadow: inset 0 1px 3px #4d4d4d; box-shadow: inset 0 1px 3px #4d4d4d; } .cms-content-toolbar .cms-actions-buttons-row .ss-ui-button, .cms-content-toolbar .cms-actions-buttons-row .ss-ui-button:hover, .cms-content-toolbar .cms-actions-buttons-row .ss-ui-button:active, .cms-content-toolbar .cms-actions-buttons-row .ss-ui-button:focus { color: #66727d; border: 0; background: none; box-shadow: none; text-shadow: none; text-decoration: none; font-weight: normal; }
.cms-content-toolbar .chzn-container-single .chzn-single span { padding-top: 1px; } .cms-content-toolbar .cms-actions-buttons-row .ss-ui-button.active, .cms-content-toolbar .cms-actions-buttons-row .ss-ui-button:active, .cms-content-toolbar .cms-actions-buttons-row .ss-ui-button:hover { background-color: #dee3e8; }
.cms-content-toolbar .chzn-container-single .chzn-single div { border-left: none; width: 100%; } .cms-content-toolbar .cms-actions-tools-row { clear: both; }
.cms-content-toolbar .chzn-container-single .chzn-single div b { background: url(../images/sprites-32x32/menu-arrow-deselected-down.png) no-repeat 9px 11px; float: right; width: 24px; } .cms-content-toolbar .tool-action { display: none; }
.cms-content-toolbar .ss-ui-button { margin-bottom: 8px; }
/* -------------------------------------------------------- Content Tools is the sidebar on the left of the main content panel */ /** DEPRECATED: .cms-content-tools will be removed in 4.0 Use .cms-content-filters instead. Content Tools is the sidebar on the left of the main content panel */
.cms-content-tools { background: #ECEFF1; width: 288px; overflow-y: auto; overflow-x: hidden; z-index: 70; border-right: 1px solid #C0C0C2; -moz-box-shadow: rgba(248, 248, 248, 0.9) -1px 0 0 inset, 0 0 1px rgba(201, 205, 206, 0.8); -webkit-box-shadow: rgba(248, 248, 248, 0.9) -1px 0 0 inset, 0 0 1px rgba(201, 205, 206, 0.8); box-shadow: rgba(248, 248, 248, 0.9) -1px 0 0 inset, 0 0 1px rgba(201, 205, 206, 0.8); float: left; position: relative; } .cms-content-tools { background: #ECEFF1; width: 288px; overflow-y: auto; overflow-x: hidden; z-index: 70; border-right: 1px solid #C1C7CC; -moz-box-shadow: rgba(248, 248, 248, 0.9) -1px 0 0 inset, 0 0 1px rgba(201, 205, 206, 0.8); -webkit-box-shadow: rgba(248, 248, 248, 0.9) -1px 0 0 inset, 0 0 1px rgba(201, 205, 206, 0.8); box-shadow: rgba(248, 248, 248, 0.9) -1px 0 0 inset, 0 0 1px rgba(201, 205, 206, 0.8); float: left; position: relative; }
.cms-content-tools.filter { padding: 0 !important; } .cms-content-tools.filter { padding: 0 !important; }
.cms-content-tools .cms-panel-header { clear: both; margin: 10px 0 7px; padding-bottom: 2px; line-height: 24px; border-bottom: 1px solid #d0d3d5; -webkit-box-shadow: 0 1px 0 rgba(248, 248, 248, 0.9); -moz-box-shadow: 0 1px 0 rgba(248, 248, 248, 0.9); -o-box-shadow: 0 1px 0 rgba(248, 248, 248, 0.9); box-shadow: 0 1px 0 rgba(248, 248, 248, 0.9); } .cms-content-tools .cms-panel-header { clear: both; margin: 10px 0 7px; padding-bottom: 2px; line-height: 24px; border-bottom: 1px solid #D2D5D8; -webkit-box-shadow: 0 1px 0 rgba(248, 248, 248, 0.9); -moz-box-shadow: 0 1px 0 rgba(248, 248, 248, 0.9); -o-box-shadow: 0 1px 0 rgba(248, 248, 248, 0.9); box-shadow: 0 1px 0 rgba(248, 248, 248, 0.9); }
.cms-content-tools .cms-panel-content { width: 272px; padding: 8.8px 8px 0; overflow: auto; height: 100%; } .cms-content-tools .cms-panel-content { width: 272px; padding: 0 12px 0 16px; overflow: auto; height: 100%; }
.cms-content-tools .cms-panel-content .Actions .ss-ui-action-constructive { margin-right: 5px; } .cms-content-tools .cms-panel-content .Actions .ss-ui-action-constructive { margin-right: 5px; }
.cms-content-tools .cms-content-header { background-color: #748d9d; background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2IwYmVjNyIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iIzc0OGQ5ZCIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA=='); background-size: 100%; background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #b0bec7), color-stop(100%, #748d9d)); background-image: -moz-linear-gradient(#b0bec7, #748d9d); background-image: -webkit-linear-gradient(#b0bec7, #748d9d); background-image: linear-gradient(#b0bec7, #748d9d); } .cms-content-tools .cms-content-header { background-color: #748d9d; background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2IwYmVjNyIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iIzc0OGQ5ZCIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA=='); background-size: 100%; background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #b0bec7), color-stop(100%, #748d9d)); background-image: -moz-linear-gradient(#b0bec7, #748d9d); background-image: -webkit-linear-gradient(#b0bec7, #748d9d); background-image: linear-gradient(#b0bec7, #748d9d); }
.cms-content-tools .cms-content-header h2 { text-shadow: #5c7382 -1px -1px 0; width: 176px; color: white; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; -o-text-overflow: ellipsis; } .cms-content-tools .cms-content-header h2 { text-shadow: #5c7382 -1px -1px 0; width: 176px; color: white; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; -o-text-overflow: ellipsis; }
@ -532,7 +641,7 @@ body.cms { overflow: hidden; }
.cms-content-tools h4 { font-size: 12px; margin: 5px 0; } .cms-content-tools h4 { font-size: 12px; margin: 5px 0; }
.cms-content-tools .ui-widget-content { background: none; } .cms-content-tools .ui-widget-content { background: none; }
.cms-content-tools .field { /* Fields are more compressed in the sidebar compared to the main content editing window so the below alters the internal spacing of the fields so we can move that spacing to between the form fields rather than padding */ } .cms-content-tools .field { /* Fields are more compressed in the sidebar compared to the main content editing window so the below alters the internal spacing of the fields so we can move that spacing to between the form fields rather than padding */ }
.cms-content-tools .field label { float: none; width: auto; font-size: 11px; padding: 0 8px 4px 0; } .cms-content-tools .field label { float: none; width: auto; font-size: 12px; padding: 0 8px 4px 0; }
.cms-content-tools .field .middleColumn { margin: 0; } .cms-content-tools .field .middleColumn { margin: 0; }
.cms-content-tools .field input.text, .cms-content-tools .field select, .cms-content-tools .field textarea { padding: 5px; font-size: 11px; } .cms-content-tools .field input.text, .cms-content-tools .field select, .cms-content-tools .field textarea { padding: 5px; font-size: 11px; }
.cms-content-tools .field.checkbox { padding: 0 0 8px; } .cms-content-tools .field.checkbox { padding: 0 0 8px; }
@ -551,29 +660,27 @@ body.cms { overflow: hidden; }
/** ------------------------------------------------------------------ /** ------------------------------------------------------------------
* CMS notice, used for filter messages, but generic enough to use elsewhere * CMS notice, used for filter messages, but generic enough to use elsewhere
* ----------------------------------------------------------------- */ * ----------------------------------------------------------------- */
.cms-notice { display: block; margin: 0 0 8px; padding: 10px 12px; font-weight: normal; border: 1px #d0d3d5 solid; background: #fff; background: rgba(255, 255, 255, 0.5); text-shadow: none; -moz-border-radius: 3px; -webkit-border-radius: 3px; border-radius: 3px; } .cms-notice { display: block; margin: 0 0 8px; padding: 10px 12px; font-weight: normal; border: 1px #D2D5D8 solid; background: #fff; background: rgba(255, 255, 255, 0.5); text-shadow: none; -moz-border-radius: 3px; -webkit-border-radius: 3px; border-radius: 3px; }
/** CMS Batch actions */ /** CMS Batch actions */
.cms-content-batchactions-button { display: inline-block; vertical-align: middle; *vertical-align: auto; *zoom: 1; *display: inline; padding: 4px 6px; vertical-align: middle; background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2ZmZmZmZiIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iI2Q5ZDlkOSIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA=='); background-size: 100%; background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #ffffff), color-stop(100%, #d9d9d9)); background-image: -moz-linear-gradient(top, #ffffff, #d9d9d9); background-image: -webkit-linear-gradient(top, #ffffff, #d9d9d9); background-image: linear-gradient(to bottom, #ffffff, #d9d9d9); border: 1px solid #aaa; -moz-border-radius: 4px; -webkit-border-radius: 4px; border-radius: 4px; }
.cms-content-batchactions { float: left; position: relative; display: block; } .cms-content-batchactions { float: left; position: relative; display: block; }
.cms-content-batchactions .view-mode-batchactions-wrapper { height: 18px; float: left; padding: 4px 6px; border: 1px solid #aaa; margin-bottom: 8px; margin-right: -1px; background-color: #D9D9D9; background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2ZmZmZmZiIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iI2Q5ZDlkOSIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA=='); background-size: 100%; background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #ffffff), color-stop(100%, #d9d9d9)); background-image: -moz-linear-gradient(top, #ffffff, #d9d9d9); background-image: -webkit-linear-gradient(top, #ffffff, #d9d9d9); background-image: linear-gradient(to bottom, #ffffff, #d9d9d9); border-top-left-radius: 4px; border-bottom-left-radius: 4px; } .cms-content-batchactions .view-mode-batchactions-wrapper { height: 18px; float: left; padding: 4px 6px; border: 1px solid #aaa; margin-bottom: 8px; margin-right: -1px; background-color: #D9D9D9; background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2ZmZmZmZiIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iI2Q5ZDlkOSIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA=='); background-size: 100%; background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #ffffff), color-stop(100%, #d9d9d9)); background-image: -moz-linear-gradient(top, #ffffff, #d9d9d9); background-image: -webkit-linear-gradient(top, #ffffff, #d9d9d9); background-image: linear-gradient(to bottom, #ffffff, #d9d9d9); border-top-left-radius: 4px; border-bottom-left-radius: 4px; }
.cms-content-batchactions .view-mode-batchactions-wrapper input { vertical-align: middle; } .cms-content-batchactions .view-mode-batchactions-wrapper input { vertical-align: middle; }
.cms-content-batchactions .view-mode-batchactions-wrapper label { vertical-align: middle; display: none; } .cms-content-batchactions .view-mode-batchactions-wrapper .view-mode-batchactions-label { vertical-align: middle; display: none; }
.cms-content-batchactions .view-mode-batchactions-wrapper fieldset, .cms-content-batchactions .view-mode-batchactions-wrapper .Actions { display: inline-block; } .cms-content-batchactions .checkbox { margin-top: 2px; vertical-align: middle; }
.cms-content-batchactions .view-mode-batchactions-wrapper #view-mode-batchactions { margin-top: 2px; }
.cms-content-batchactions.inactive .view-mode-batchactions-wrapper { border-radius: 4px; }
.cms-content-batchactions.inactive .view-mode-batchactions-wrapper label { display: inline; }
.cms-content-batchactions form > * { display: block; float: left; }
.cms-content-batchactions form.cms-batch-actions { float: left; }
.cms-content-batchactions.inactive form { display: none; }
.cms-content-batchactions .chzn-container-single { display: block; }
.cms-content-batchactions .chzn-container-single .chzn-single { border-radius: 0; background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2ZmZmZmZiIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iI2Q5ZDlkOSIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA=='); background-size: 100%; background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #ffffff), color-stop(100%, #d9d9d9)); background-image: -moz-linear-gradient(top, #ffffff, #d9d9d9); background-image: -webkit-linear-gradient(top, #ffffff, #d9d9d9); background-image: linear-gradient(to bottom, #ffffff, #d9d9d9); }
.cms-content-batchactions .chzn-container-single .chzn-single span { padding-top: 0; }
.cms-content-batchactions .cms-batch-actions .dropdown { margin: 0; }
.cms-content-batchactions .cms-batch-actions .dropdown .chzn-single { padding-left: 8px; /* use default size without icon */ }
.cms-content-batchactions .cms-batch-actions .Actions .ss-ui-button { padding-top: 4px; padding-bottom: 4px; height: 28px; margin-left: -1px; border-top-left-radius: 0; border-bottom-left-radius: 0; }
.cms-content-batchactions .cms-batch-actions .Actions .ss-ui-button.ui-state-disabled { opacity: 1; color: #ccc; }
#Form_BatchActionsForm select { width: 200px; } .cms-content-batchactions-dropdown { display: inline-block; vertical-align: middle; *vertical-align: auto; *zoom: 1; *display: inline; }
.cms-content-tools .cms-content-batchactions-dropdown { width: 100%; }
.cms-content-batchactions-dropdown fieldset { display: inline-block; vertical-align: middle; *vertical-align: auto; *zoom: 1; *display: inline; width: 200px; }
.cms-content-batchactions-dropdown fieldset .view-mode-batchactions-label { display: inline; }
.cms-content-tools .cms-content-batchactions-dropdown fieldset { width: 82%; }
.cms-content-batchactions-dropdown .dropdown { width: 100%; height: 32px; }
.cms-content-batchactions-dropdown .dropdown .chzn-single { border-top-right-radius: 0; border-bottom-right-radius: 0; box-shadow: none; }
.cms-content-batchactions-dropdown .Actions { display: inline-block; vertical-align: middle; *vertical-align: auto; *zoom: 1; *display: inline; padding: 0; margin-left: -4px; }
.cms-content-tools .cms-content-batchactions-dropdown .Actions { width: 16%; }
.cms-content-batchactions-dropdown .action { width: 100%; height: 32px; margin-bottom: 0; border-top-left-radius: 0; border-bottom-left-radius: 0; }
/** -------------------------------------------- Preview -------------------------------------------- */ /** -------------------------------------------- Preview -------------------------------------------- */
.cms-switch-view a { padding-right: 1em; } .cms-switch-view a { padding-right: 1em; }
@ -600,7 +707,7 @@ form.member-profile-form #Permissions .optionset li { float: none; width: auto;
.memberdatetimeoptionset .toggle { font-size: 11px; } .memberdatetimeoptionset .toggle { font-size: 11px; }
.cms .cms-content { border-right: 1px solid #BBB; -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0; background: #ECEFF1; width: 800px; z-index: 40; } .cms .cms-content { border-right: 1px solid #BBB; -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0; background: #ECEFF1; width: 800px; z-index: 40; }
.cms .cms-content-fields { overflow-y: auto; overflow-x: auto; background: #e6eaed; width: 100%; } .cms .cms-content-fields { overflow-y: auto; overflow-x: auto; background: #ECEFF1; width: 100%; }
.cms .cms-content-fields #Root_Main .confirmedpassword { border-bottom: none; box-shadow: none; } .cms .cms-content-fields #Root_Main .confirmedpassword { border-bottom: none; box-shadow: none; }
.cms .cms-content-fields #Root_Main .customFormat { max-width: 80px; } .cms .cms-content-fields #Root_Main .customFormat { max-width: 80px; }
.cms .cms-content-fields #Root_Main .cms-help-toggle { text-indent: -9999em; display: inline-block; width: 20px; background: url(../images/question.png) no-repeat 0px 0px; } .cms .cms-content-fields #Root_Main .cms-help-toggle { text-indent: -9999em; display: inline-block; width: 20px; background: url(../images/question.png) no-repeat 0px 0px; }
@ -614,13 +721,13 @@ form.member-profile-form #Permissions .optionset li { float: none; width: auto;
#ViewerGroups select, #EditorGroups select, #CreateTopLevelGroups select { width: 512px; } #ViewerGroups select, #EditorGroups select, #CreateTopLevelGroups select { width: 512px; }
/** -------------------------------------------- Panels -------------------------------------------- */ /** -------------------------------------------- Panels -------------------------------------------- */
.cms-panel { overflow: hidden; } .cms-panel { overflow: hidden; /** DEPRECATED: .cms-content-tools will be removed in 4.0 Use .cms-content-filters instead. */ }
.cms-panel .cms-panel-toggle { -moz-box-shadow: 0 0 1px rgba(248, 248, 248, 0.9); -webkit-box-shadow: 0 0 1px rgba(248, 248, 248, 0.9); box-shadow: 0 0 1px rgba(248, 248, 248, 0.9); } .cms-panel .cms-panel-toggle { -moz-box-shadow: 0 0 1px rgba(248, 248, 248, 0.9); -webkit-box-shadow: 0 0 1px rgba(248, 248, 248, 0.9); box-shadow: 0 0 1px rgba(248, 248, 248, 0.9); }
.cms-panel .cms-panel-toggle.south { border-top: 1px solid #9eafba; -moz-box-shadow: #bcc8cf 0 1px 0px inset; -webkit-box-shadow: #bcc8cf 0 1px 0px inset; box-shadow: #bcc8cf 0 1px 0px inset; position: absolute; bottom: 0; width: 100%; } .cms-panel .cms-panel-toggle.south { border-top: 1px solid #a9afb4; -moz-box-shadow: #bcc8cf 0 1px 0px inset; -webkit-box-shadow: #bcc8cf 0 1px 0px inset; box-shadow: #bcc8cf 0 1px 0px inset; position: absolute; bottom: 0; width: 100%; }
.cms-panel .cms-panel-toggle a { display: block; text-align: right; padding: 4px 0; width: 100%; text-decoration: none; } .cms-panel .cms-panel-toggle a { display: block; text-align: right; padding: 4px 0; width: 100%; text-decoration: none; }
.cms-panel .cms-panel-toggle a span { display: inline-block; margin: 0 5px; color: #555d60; font-size: 16px; } .cms-panel .cms-panel-toggle a span { display: inline-block; margin: 0 5px; color: #555d60; font-size: 16px; }
.cms-panel .cms-panel-toggle a.toggle-expand { width: 40px; display: none; } .cms-panel .cms-panel-toggle a.toggle-expand { width: 40px; display: none; }
.cms-panel.cms-content-tools .cms-panel-toggle.south { border-top: 1px solid #cfd6db; -moz-box-shadow: rgba(248, 248, 248, 0.9) 0 1px 0px inset; -webkit-box-shadow: rgba(248, 248, 248, 0.9) 0 1px 0px inset; box-shadow: rgba(248, 248, 248, 0.9) 0 1px 0px inset; } .cms-panel.cms-content-tools .cms-panel-toggle.south { border-top: 1px solid #D2D5D8; -moz-box-shadow: rgba(248, 248, 248, 0.9) 0 1px 0px inset; -webkit-box-shadow: rgba(248, 248, 248, 0.9) 0 1px 0px inset; box-shadow: rgba(248, 248, 248, 0.9) 0 1px 0px inset; }
.cms-panel.collapsed { cursor: pointer; } .cms-panel.collapsed { cursor: pointer; }
.cms-panel.collapsed .cms-panel-header *, .cms-panel.collapsed .cms-panel-content, .cms-panel.collapsed .cms-panel-toggle a.toggle-collapse { display: none; } .cms-panel.collapsed .cms-panel-header *, .cms-panel.collapsed .cms-panel-content, .cms-panel.collapsed .cms-panel-toggle a.toggle-collapse { display: none; }
.cms-panel.collapsed .cms-panel-toggle a.toggle-expand { display: block; } .cms-panel.collapsed .cms-panel-toggle a.toggle-expand { display: block; }
@ -635,7 +742,10 @@ form.member-profile-form #Permissions .optionset li { float: none; width: auto;
.cms-panel .collapsed-flyout { display: block !important; left: 41px; margin-top: -40px; position: fixed; width: 191px; } .cms-panel .collapsed-flyout { display: block !important; left: 41px; margin-top: -40px; position: fixed; width: 191px; }
.cms-panel .collapsed-flyout li a span { display: block !important; } .cms-panel .collapsed-flyout li a span { display: block !important; }
.cms .cms-panel-padded { padding: 16px 16px; margin: 0; } .cms .cms-panel-padded { padding: 0 16px 12px; }
.cms .cms-panel-padded.ReportAdmin > fieldset { padding-top: 12px; }
.CMSPageAddController .cms-panel-padded { padding: 16px 16px; }
/** ------------------------------------------------------------------ /** ------------------------------------------------------------------
* Dialog * Dialog
@ -657,7 +767,7 @@ form.member-profile-form #Permissions .optionset li { float: none; width: auto;
.ui-dialog .cms-dialog-content { background: url("../images/textures/bg_cms_main_content.png") repeat left top #F0F3F4; padding-bottom: 8px; padding-top: 0px; } .ui-dialog .cms-dialog-content { background: url("../images/textures/bg_cms_main_content.png") repeat left top #F0F3F4; padding-bottom: 8px; padding-top: 0px; }
.ui-dialog .cms-dialog-content .Actions { overflow: auto; margin: 8px 0; padding-bottom: 8px; float: right; } .ui-dialog .cms-dialog-content .Actions { overflow: auto; margin: 8px 0; padding-bottom: 8px; float: right; }
.ui-dialog .cms-dialog-content .ui-tabs { position: static; } .ui-dialog .cms-dialog-content .ui-tabs { position: static; }
.ui-dialog .cms-dialog-content .ui-tabs .ui-tabs-nav { position: absolute; top: 0; right: 40px; } .ui-dialog .cms-dialog-content .ui-tabs .ui-tabs-nav { position: absolute; top: 9px; right: 40px; }
.ui-dialog .cms-dialog-content .ui-tabs .ui-tabs-panel { border: 0; } .ui-dialog .cms-dialog-content .ui-tabs .ui-tabs-panel { border: 0; }
.ui-dialog .cms-dialog-content .clear { clear: both; } .ui-dialog .cms-dialog-content .clear { clear: both; }
.ui-dialog.loading { background-image: url(../images/spinner.gif); background-position: 50% 50%; background-repeat: no-repeat; } .ui-dialog.loading { background-image: url(../images/spinner.gif); background-position: 50% 50%; background-repeat: no-repeat; }
@ -677,8 +787,8 @@ body.cms-dialog { overflow: auto; background: url("../images/textures/bg_cms_mai
.htmleditorfield-dialog .htmleditorfield-from-web button.add-url:hover, .htmleditorfield-dialog .htmleditorfield-from-web button.add-url:active { border: none; -moz-box-shadow: none; -webkit-box-shadow: none; box-shadow: none; opacity: 1; } .htmleditorfield-dialog .htmleditorfield-from-web button.add-url:hover, .htmleditorfield-dialog .htmleditorfield-from-web button.add-url:active { border: none; -moz-box-shadow: none; -webkit-box-shadow: none; box-shadow: none; opacity: 1; }
.htmleditorfield-dialog .htmleditorfield-from-web button.add-url.ui-state-disabled, .htmleditorfield-dialog .htmleditorfield-from-web button.add-url.ui-state-disabled:hover, .htmleditorfield-dialog .htmleditorfield-from-web button.add-url.ui-state-disabled:active { opacity: 0.35; filter: Alpha(Opacity=35); } .htmleditorfield-dialog .htmleditorfield-from-web button.add-url.ui-state-disabled, .htmleditorfield-dialog .htmleditorfield-from-web button.add-url.ui-state-disabled:hover, .htmleditorfield-dialog .htmleditorfield-from-web button.add-url.ui-state-disabled:active { opacity: 0.35; filter: Alpha(Opacity=35); }
.htmleditorfield-dialog .htmleditorfield-from-web .loading button.add-url .ui-icon { background-image: url(../images/throbber.gif); background-position: 50% 50%; background-repeat: no-repeat; } .htmleditorfield-dialog .htmleditorfield-from-web .loading button.add-url .ui-icon { background-image: url(../images/throbber.gif); background-position: 50% 50%; background-repeat: no-repeat; }
.htmleditorfield-dialog .cms-content-header { padding: 0; width: 100%; height: 40px; } .htmleditorfield-dialog .cms-content-header { padding: 0; width: 100%; height: 53px; }
.htmleditorfield-dialog .cms-content-header h3 { padding: 3px 8px; margin: 10px; } .htmleditorfield-dialog .cms-content-header h3 { padding: 12px 16px; margin: 0; line-height: 28px; }
.htmleditorfield-dialog .ss-insert-media, .htmleditorfield-dialog .Actions, .htmleditorfield-dialog .ss-insert-link { padding: 8px 16px; } .htmleditorfield-dialog .ss-insert-media, .htmleditorfield-dialog .Actions, .htmleditorfield-dialog .ss-insert-link { padding: 8px 16px; }
.htmleditorfield-dialog .ss-insert-media .ui-tabs-panel, .htmleditorfield-dialog .Actions .ui-tabs-panel, .htmleditorfield-dialog .ss-insert-link .ui-tabs-panel { padding: 0; } .htmleditorfield-dialog .ss-insert-media .ui-tabs-panel, .htmleditorfield-dialog .Actions .ui-tabs-panel, .htmleditorfield-dialog .ss-insert-link .ui-tabs-panel { padding: 0; }
.htmleditorfield-dialog .details .file-url { display: block; width: 300px; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; -o-text-overflow: ellipsis; } .htmleditorfield-dialog .details .file-url { display: block; width: 300px; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; -o-text-overflow: ellipsis; }
@ -733,8 +843,6 @@ form.small .cms-file-info-data .field .middleColumn { margin-left: 120px; }
.members_grid p button#action_export span.btn-icon-download-csv { height: 17px; } .members_grid p button#action_export span.btn-icon-download-csv { height: 17px; }
/** Import forms */ /** Import forms */
body.SecurityAdmin { background-color: #e6eaed; }
form.import-form ul { list-style: disc; } form.import-form ul { list-style: disc; }
form.import-form ul li { margin-left: 20px; } form.import-form ul li { margin-left: 20px; }
form.import-form p { margin-bottom: 5px; } form.import-form p { margin-bottom: 5px; }
@ -774,7 +882,8 @@ form.import-form label.left { width: 250px; }
.cms .jstree a, .TreeDropdownField .treedropdownfield-panel .jstree a { display: inline-block; line-height: 16px; height: 16px; color: black; white-space: nowrap; text-decoration: none; padding: 1px 2px; margin: 0; border: 1px solid #fff; } .cms .jstree a, .TreeDropdownField .treedropdownfield-panel .jstree a { display: inline-block; line-height: 16px; height: 16px; color: black; white-space: nowrap; text-decoration: none; padding: 1px 2px; margin: 0; border: 1px solid #fff; }
.cms .jstree a:focus, .cms .jstree a:active, .cms .jstree a:hover, .TreeDropdownField .treedropdownfield-panel .jstree a:focus, .TreeDropdownField .treedropdownfield-panel .jstree a:active, .TreeDropdownField .treedropdownfield-panel .jstree a:hover { text-decoration: none; cursor: pointer; text-shadow: 1px 1px 1px white; } .cms .jstree a:focus, .cms .jstree a:active, .cms .jstree a:hover, .TreeDropdownField .treedropdownfield-panel .jstree a:focus, .TreeDropdownField .treedropdownfield-panel .jstree a:active, .TreeDropdownField .treedropdownfield-panel .jstree a:hover { text-decoration: none; cursor: pointer; text-shadow: 1px 1px 1px white; }
.cms .jstree a ins, .TreeDropdownField .treedropdownfield-panel .jstree a ins { height: 16px; width: 12px; } .cms .jstree a ins, .TreeDropdownField .treedropdownfield-panel .jstree a ins { height: 16px; width: 12px; }
.cms .jstree a ins.jstree-checkbox, .TreeDropdownField .treedropdownfield-panel .jstree a ins.jstree-checkbox { height: 19px; width: 16px; } .cms .jstree a ins.jstree-checkbox, .TreeDropdownField .treedropdownfield-panel .jstree a ins.jstree-checkbox { width: 16px; position: relative; }
.cms .jstree a ins.jstree-checkbox:before, .TreeDropdownField .treedropdownfield-panel .jstree a ins.jstree-checkbox:before { content: ''; display: block; position: absolute; z-index: 1; left: -3px; top: -3px; height: 22px; width: 25px; }
.cms .jstree .jstree-real-checkbox, .TreeDropdownField .treedropdownfield-panel .jstree .jstree-real-checkbox { display: none; } .cms .jstree .jstree-real-checkbox, .TreeDropdownField .treedropdownfield-panel .jstree .jstree-real-checkbox { display: none; }
.cms .jstree .jstree-wholerow-real, .TreeDropdownField .treedropdownfield-panel .jstree .jstree-wholerow-real { position: relative; z-index: 1; } .cms .jstree .jstree-wholerow-real, .TreeDropdownField .treedropdownfield-panel .jstree .jstree-wholerow-real { position: relative; z-index: 1; }
.cms .jstree .jstree-wholerow-real li, .TreeDropdownField .treedropdownfield-panel .jstree .jstree-wholerow-real li { cursor: pointer; } .cms .jstree .jstree-wholerow-real li, .TreeDropdownField .treedropdownfield-panel .jstree .jstree-wholerow-real li { cursor: pointer; }
@ -835,7 +944,7 @@ form.import-form label.left { width: 250px; }
.tree-holder.jstree li.Root strong, .cms-tree.jstree li.Root strong { font-weight: bold; padding-left: 1px; } .tree-holder.jstree li.Root strong, .cms-tree.jstree li.Root strong { font-weight: bold; padding-left: 1px; }
.tree-holder.jstree li.Root > a .jstree-icon, .cms-tree.jstree li.Root > a .jstree-icon { background-position: -56px -36px; } .tree-holder.jstree li.Root > a .jstree-icon, .cms-tree.jstree li.Root > a .jstree-icon { background-position: -56px -36px; }
.tree-holder.jstree li.status-deletedonlive > a .text, .tree-holder.jstree li.status-deletedonlive > a:link .text, .tree-holder.jstree li.status-archived > a .text, .tree-holder.jstree li.status-archived > a:link .text, .cms-tree.jstree li.status-deletedonlive > a .text, .cms-tree.jstree li.status-deletedonlive > a:link .text, .cms-tree.jstree li.status-archived > a .text, .cms-tree.jstree li.status-archived > a:link .text { text-decoration: line-through; } .tree-holder.jstree li.status-deletedonlive > a .text, .tree-holder.jstree li.status-deletedonlive > a:link .text, .tree-holder.jstree li.status-archived > a .text, .tree-holder.jstree li.status-archived > a:link .text, .cms-tree.jstree li.status-deletedonlive > a .text, .cms-tree.jstree li.status-deletedonlive > a:link .text, .cms-tree.jstree li.status-archived > a .text, .cms-tree.jstree li.status-archived > a:link .text { text-decoration: line-through; }
.tree-holder.jstree li.jstree-checked > a, .tree-holder.jstree li.jstree-checked > a:link, .cms-tree.jstree li.jstree-checked > a, .cms-tree.jstree li.jstree-checked > a:link { background-color: #efe999; } .tree-holder.jstree li.jstree-checked > a, .tree-holder.jstree li.jstree-checked > a:link, .cms-tree.jstree li.jstree-checked > a, .cms-tree.jstree li.jstree-checked > a:link { background-color: #fffcdc; }
.tree-holder.jstree li.disabled > a, .tree-holder.jstree li.disabled > a:link, .tree-holder.jstree li.edit-disabled > a, .tree-holder.jstree li.edit-disabled > a:link, .cms-tree.jstree li.disabled > a, .cms-tree.jstree li.disabled > a:link, .cms-tree.jstree li.edit-disabled > a, .cms-tree.jstree li.edit-disabled > a:link { color: #aaa; background-color: transparent; cursor: default; } .tree-holder.jstree li.disabled > a, .tree-holder.jstree li.disabled > a:link, .tree-holder.jstree li.edit-disabled > a, .tree-holder.jstree li.edit-disabled > a:link, .cms-tree.jstree li.disabled > a, .cms-tree.jstree li.disabled > a:link, .cms-tree.jstree li.edit-disabled > a, .cms-tree.jstree li.edit-disabled > a:link { color: #aaa; background-color: transparent; cursor: default; }
.tree-holder.jstree li.disabled > a > .jstree-checkbox, .tree-holder.jstree li.disabled > a:link > .jstree-checkbox, .tree-holder.jstree li.edit-disabled > a > .jstree-checkbox, .tree-holder.jstree li.edit-disabled > a:link > .jstree-checkbox, .cms-tree.jstree li.disabled > a > .jstree-checkbox, .cms-tree.jstree li.disabled > a:link > .jstree-checkbox, .cms-tree.jstree li.edit-disabled > a > .jstree-checkbox, .cms-tree.jstree li.edit-disabled > a:link > .jstree-checkbox { background-position: -57px -54px; } .tree-holder.jstree li.disabled > a > .jstree-checkbox, .tree-holder.jstree li.disabled > a:link > .jstree-checkbox, .tree-holder.jstree li.edit-disabled > a > .jstree-checkbox, .tree-holder.jstree li.edit-disabled > a:link > .jstree-checkbox, .cms-tree.jstree li.disabled > a > .jstree-checkbox, .cms-tree.jstree li.disabled > a:link > .jstree-checkbox, .cms-tree.jstree li.edit-disabled > a > .jstree-checkbox, .cms-tree.jstree li.edit-disabled > a:link > .jstree-checkbox { background-position: -57px -54px; }
.tree-holder.jstree li.readonly, .cms-tree.jstree li.readonly { color: #aaa; padding-left: 18px; } .tree-holder.jstree li.readonly, .cms-tree.jstree li.readonly { color: #aaa; padding-left: 18px; }
@ -858,9 +967,17 @@ form.import-form label.left { width: 250px; }
.jstree-default a .jstree-icon, .jstree-default-rtl a .jstree-icon, .jstree-classic a .jstree-icon, .jstree-apple a .jstree-icon { background-position: -60px -19px; } .jstree-default a .jstree-icon, .jstree-default-rtl a .jstree-icon, .jstree-classic a .jstree-icon, .jstree-apple a .jstree-icon { background-position: -60px -19px; }
/* ensure status is visible in sidebar */ <<<<<<< HEAD
/** DEPRECATED: .cms-content-tools will be removed in 4.0 Use .cms-content-filters instead. Ensure status is visible in sidebar */
.cms-content-tools .cms-tree.jstree li { min-width: 159px; } .cms-content-tools .cms-tree.jstree li { min-width: 159px; }
.cms-content-tools .cms-tree.jstree a { overflow: hidden; display: block; position: relative; } .cms-content-tools .cms-tree.jstree a { overflow: hidden; display: block; position: relative; }
=======
.jstree-apple a { border-radius: 3px; }
/* ensure status is visible in sidebar */
.cms-content-tools .cms-tree.jstree li { min-width: 187px; }
.cms-content-tools .cms-tree.jstree a { overflow: hidden; text-overflow: ellipsis; display: block; position: relative; }
>>>>>>> origin/3.2
.cms-content-tools .cms-tree.jstree span.badge { position: absolute; top: 0; right: 0; padding: 7px 9px 6px 5px; margin: 0; max-width: 40%; -moz-transition: max-width 0.75s linear; -o-transition: max-width 0.75s linear; -webkit-transition: max-width 0.75s linear; transition: max-width 0.75s linear; } .cms-content-tools .cms-tree.jstree span.badge { position: absolute; top: 0; right: 0; padding: 7px 9px 6px 5px; margin: 0; max-width: 40%; -moz-transition: max-width 0.75s linear; -o-transition: max-width 0.75s linear; -webkit-transition: max-width 0.75s linear; transition: max-width 0.75s linear; }
.cms-content-tools .cms-tree.jstree span.badge:hover { max-width: 150px; } .cms-content-tools .cms-tree.jstree span.badge:hover { max-width: 150px; }
@ -870,45 +987,59 @@ li.class-RedirectorPage > a .jstree-pageicon { background-position: 0 -16px; }
li.class-VirtualPage > a .jstree-pageicon { background-position: 0 -32px; } li.class-VirtualPage > a .jstree-pageicon { background-position: 0 -32px; }
li.class-ErrorPage > a .jstree-pageicon { background-position: 0 -112px; } li.class-ErrorPage > a .jstree-pageicon { background-position: 0 -112px; }
/* tree status icons and labels */ /* Tree status labels and dots */
.cms-tree.jstree .status-modified > a .jstree-pageicon:before, .cms-tree.jstree .status-addedtodraft > a .jstree-pageicon:before, .cms-tree.jstree .status-deletedonlive > a .jstree-pageicon:before, .cms-tree.jstree .status-archived > a .jstree-pageicon:before, .cms-tree.jstree .status-removedfromdraft > a .jstree-pageicon:before, .cms-tree.jstree .status-workflow-approval > a .jstree-pageicon:before { content: ""; display: block; width: 5px; height: 5px; position: absolute; bottom: 0; right: 0; background: #fce2d0; border: 1px solid #ff9344; border-radius: 100px; box-shadow: 0px 0px 0px 1px #fff; } .jstree-apple .jstree-clicked, .jstree-apple .jstree-hovered { background: #ebfbff; }
.jstree .status-modified > .jstree-hovered, .jstree .status-modified > .jstree-clicked, .cms-tree.jstree span.badge.status-modified, .cms-tree.jstree .status-modified > a .jstree-pageicon:before { background-color: #FFF4ED; border-color: #EE6214; } .cms-tree.jstree .status-addedtodraft > a .jstree-pageicon:before, .cms-tree.jstree .status-modified > a .jstree-pageicon:before, .cms-tree.jstree .status-archived > a .jstree-pageicon:before, .cms-tree.jstree .status-deletedonlive > a .jstree-pageicon:before, .cms-tree.jstree .status-removedfromdraft > a .jstree-pageicon:before, .cms-tree.jstree .status-workflow-approval > a .jstree-pageicon:before { content: ""; display: block; width: 6px; height: 6px; position: absolute; bottom: 0; right: 0; background: #fce2d0; border: 1px solid #fff; border-radius: 100px; }
#cms-content-tools-CMSMain .cms-tree.jstree span.badge.status-modified { box-shadow: 0px 0px 6px 2px #FFF4ED; } .jstree .status-addedtodraft > .jstree-hovered, .jstree .status-addedtodraft > .jstree-clicked, .cms-tree.jstree span.badge.status-addedtodraft { background-color: #fff7f2; border-color: #F46B00; }
.cms-tree.jstree span.badge.status-modified { color: #EE6214; } .cms-tree.jstree span.badge.status-addedtodraft { color: #F46B00; }
.jstree .status-addedtodraft > .jstree-hovered, .jstree .status-addedtodraft > .jstree-clicked, .cms-tree.jstree span.badge.status-addedtodraft, .cms-tree.jstree .status-addedtodraft > a .jstree-pageicon:before { background-color: #FFF4ED; border-color: #EE6214; } .cms-tree.jstree .status-addedtodraft > a .jstree-pageicon:before { background-color: #ff7f22; -moz-box-shadow: 0px 1px 1px rgba(0, 0, 0, 0.3), inset 0 0 0 1px #F46B00; -webkit-box-shadow: 0px 1px 1px rgba(0, 0, 0, 0.3), inset 0 0 0 1px #F46B00; box-shadow: 0px 1px 1px rgba(0, 0, 0, 0.3), inset 0 0 0 1px #F46B00; }
#cms-content-tools-CMSMain .cms-tree.jstree span.badge.status-addedtodraft { box-shadow: 0px 0px 6px 2px #FFF4ED; } #cms-content-tools-CMSMain .cms-tree.jstree span.badge.status-addedtodraft { -moz-box-shadow: 0px 0px 6px 2px #fff7f2; -webkit-box-shadow: 0px 0px 6px 2px #fff7f2; box-shadow: 0px 0px 6px 2px #fff7f2; }
.cms-tree.jstree span.badge.status-addedtodraft { color: #EE6214; } .jstree .status-modified > .jstree-hovered, .jstree .status-modified > .jstree-clicked, .cms-tree.jstree span.badge.status-modified { background-color: #fff7f2; border-color: #F46B00; }
.jstree .status-deletedonlive > .jstree-hovered, .jstree .status-deletedonlive > .jstree-clicked, .cms-tree.jstree span.badge.status-deletedonlive, .cms-tree.jstree .status-deletedonlive > a .jstree-pageicon:before { background-color: #F5F5F5; border-color: #5F7688; } .cms-tree.jstree span.badge.status-modified { color: #F46B00; }
#cms-content-tools-CMSMain .cms-tree.jstree span.badge.status-deletedonlive { box-shadow: 0px 0px 6px 2px #F5F5F5; } .cms-tree.jstree .status-modified > a .jstree-pageicon:before { background-color: #fff2e8; -moz-box-shadow: 0px 1px 1px rgba(0, 0, 0, 0.3), inset 0 0 0 1px #F46B00; -webkit-box-shadow: 0px 1px 1px rgba(0, 0, 0, 0.3), inset 0 0 0 1px #F46B00; box-shadow: 0px 1px 1px rgba(0, 0, 0, 0.3), inset 0 0 0 1px #F46B00; }
.cms-tree.jstree span.badge.status-deletedonlive { color: #5F7688; } #cms-content-tools-CMSMain .cms-tree.jstree span.badge.status-modified { -moz-box-shadow: 0px 0px 6px 2px #fff7f2; -webkit-box-shadow: 0px 0px 6px 2px #fff7f2; box-shadow: 0px 0px 6px 2px #fff7f2; }
.jstree .status-archived > .jstree-hovered, .jstree .status-archived > .jstree-clicked, .cms-tree.jstree span.badge.status-archived, .cms-tree.jstree .status-archived > a .jstree-pageicon:before { background-color: #F5F5F5; border-color: #5F7688; } .jstree .status-archived > .jstree-hovered, .jstree .status-archived > .jstree-clicked, .cms-tree.jstree span.badge.status-archived { background-color: #f7f7f7; border-color: #455b6c; }
#cms-content-tools-CMSMain .cms-tree.jstree span.badge.status-archived { box-shadow: 0px 0px 6px 2px #F5F5F5; } .cms-tree.jstree span.badge.status-archived { color: #455b6c; }
.cms-tree.jstree span.badge.status-archived { color: #5F7688; } .cms-tree.jstree .status-archived > a .jstree-pageicon:before { background-color: #5F7688; -moz-box-shadow: 0px 1px 1px rgba(0, 0, 0, 0.3), inset 0 0 0 1px #455b6c; -webkit-box-shadow: 0px 1px 1px rgba(0, 0, 0, 0.3), inset 0 0 0 1px #455b6c; box-shadow: 0px 1px 1px rgba(0, 0, 0, 0.3), inset 0 0 0 1px #455b6c; }
.jstree .status-removedfromdraft > .jstree-hovered, .jstree .status-removedfromdraft > .jstree-clicked, .cms-tree.jstree span.badge.status-removedfromdraft, .cms-tree.jstree .status-removedfromdraft > a .jstree-pageicon:before { background-color: #F5F5F5; border-color: #5F7688; } #cms-content-tools-CMSMain .cms-tree.jstree span.badge.status-archived { -moz-box-shadow: 0px 0px 6px 2px #f7f7f7; -webkit-box-shadow: 0px 0px 6px 2px #f7f7f7; box-shadow: 0px 0px 6px 2px #f7f7f7; }
#cms-content-tools-CMSMain .cms-tree.jstree span.badge.status-removedfromdraft { box-shadow: 0px 0px 6px 2px #F5F5F5; } .jstree .status-deletedonlive > .jstree-hovered, .jstree .status-deletedonlive > .jstree-clicked, .cms-tree.jstree span.badge.status-deletedonlive { background-color: #f7f7f7; border-color: #455b6c; }
.cms-tree.jstree span.badge.status-removedfromdraft { color: #5F7688; } .cms-tree.jstree span.badge.status-deletedonlive { color: #455b6c; }
.jstree .status-workflow-approval > .jstree-hovered, .jstree .status-workflow-approval > .jstree-clicked, .cms-tree.jstree span.badge.status-workflow-approval, .cms-tree.jstree .status-workflow-approval > a .jstree-pageicon:before { background-color: #E8FAFF; border-color: #0070B4; } .cms-tree.jstree .status-deletedonlive > a .jstree-pageicon:before { background-color: #f7f7f7; -moz-box-shadow: 0px 1px 1px rgba(0, 0, 0, 0.3), inset 0 0 0 1px #455b6c; -webkit-box-shadow: 0px 1px 1px rgba(0, 0, 0, 0.3), inset 0 0 0 1px #455b6c; box-shadow: 0px 1px 1px rgba(0, 0, 0, 0.3), inset 0 0 0 1px #455b6c; }
#cms-content-tools-CMSMain .cms-tree.jstree span.badge.status-workflow-approval { box-shadow: 0px 0px 6px 2px #E8FAFF; } #cms-content-tools-CMSMain .cms-tree.jstree span.badge.status-deletedonlive { -moz-box-shadow: 0px 0px 6px 2px #f7f7f7; -webkit-box-shadow: 0px 0px 6px 2px #f7f7f7; box-shadow: 0px 0px 6px 2px #f7f7f7; }
.jstree .status-removedfromdraft > .jstree-hovered, .jstree .status-removedfromdraft > .jstree-clicked, .cms-tree.jstree span.badge.status-removedfromdraft { background-color: #f7f7f7; border-color: #455b6c; }
.cms-tree.jstree span.badge.status-removedfromdraft { color: #455b6c; }
.cms-tree.jstree .status-removedfromdraft > a .jstree-pageicon:before { background-color: #f7f7f7; -moz-box-shadow: 0px 1px 1px rgba(0, 0, 0, 0.3), inset 0 0 0 1px #455b6c; -webkit-box-shadow: 0px 1px 1px rgba(0, 0, 0, 0.3), inset 0 0 0 1px #455b6c; box-shadow: 0px 1px 1px rgba(0, 0, 0, 0.3), inset 0 0 0 1px #455b6c; }
#cms-content-tools-CMSMain .cms-tree.jstree span.badge.status-removedfromdraft { -moz-box-shadow: 0px 0px 6px 2px #f7f7f7; -webkit-box-shadow: 0px 0px 6px 2px #f7f7f7; box-shadow: 0px 0px 6px 2px #f7f7f7; }
.jstree .status-workflow-approval > .jstree-hovered, .jstree .status-workflow-approval > .jstree-clicked, .cms-tree.jstree span.badge.status-workflow-approval { background-color: #E8FAFF; border-color: #0070B4; }
.cms-tree.jstree span.badge.status-workflow-approval { color: #0070B4; } .cms-tree.jstree span.badge.status-workflow-approval { color: #0070B4; }
.cms-tree.jstree .status-workflow-approval > a .jstree-pageicon:before { background-color: #0070B4; -moz-box-shadow: 0px 1px 1px rgba(0, 0, 0, 0.3), inset 0 0 0 1px #0070B4; -webkit-box-shadow: 0px 1px 1px rgba(0, 0, 0, 0.3), inset 0 0 0 1px #0070B4; box-shadow: 0px 1px 1px rgba(0, 0, 0, 0.3), inset 0 0 0 1px #0070B4; }
#cms-content-tools-CMSMain .cms-tree.jstree span.badge.status-workflow-approval { -moz-box-shadow: 0px 0px 6px 2px #E8FAFF; -webkit-box-shadow: 0px 0px 6px 2px #E8FAFF; box-shadow: 0px 0px 6px 2px #E8FAFF; }
.cms-tree { visibility: hidden; } .cms-tree { visibility: hidden; }
.cms-tree.multiple li > a > .jstree-icon { display: none; } .cms-tree.multiple li > a > .jstree-icon { display: none; }
.cms-tree.multiple li > a > .jstree-icon.jstree-checkbox { display: inline-block; } .cms-tree.multiple li > a > .jstree-icon.jstree-checkbox { display: inline-block; }
@ -922,15 +1053,16 @@ li.class-ErrorPage > a .jstree-pageicon { background-position: 0 -112px; }
.cms-logo-header span { color: white; display: block; padding-left: 26px; } .cms-logo-header span { color: white; display: block; padding-left: 26px; }
.cms-logo-header span a { color: #3EBAE0; display: inline; } .cms-logo-header span a { color: #3EBAE0; display: inline; }
.cms-logo { border-bottom: 1px solid #1a2a45; overflow: hidden; padding: 10px 0 9px; /* should come to 40px with border bottom and line-height */ position: relative; vertical-align: middle; font-size: 12px; min-height: 20px; } .cms-logo { border-bottom: 1px solid #1a2a45; overflow: hidden; padding: 12px 0 11px; /* should come to 52px with border bottom and line-height */ position: relative; vertical-align: middle; font-size: 12px; min-height: 28px; }
.collapsed .cms-logo { padding: 0; } .collapsed .cms-logo { padding: 0; }
.cms-logo .version { display: none; } .cms-logo .version { display: none; }
.cms-logo a { position: absolute; top: 8px; bottom: 8px; display: block; width: 24px; background: url("../images/logo_small.png") no-repeat left center; text-indent: -9999em; padding: 0 1px; left: 0; } .cms-logo a { position: absolute; top: 8px; bottom: 8px; display: block; width: 24px; background: url("../images/logo_small.png") no-repeat left center; text-indent: -9999em; padding: 0 1px; left: 0; }
.cms-logo span { font-weight: bold; font-size: 12px; line-height: 16px; padding: 2px 0; margin-left: 30px; } .cms-logo span { font-weight: bold; font-size: 12px; line-height: 16px; padding: 6px 0; margin-left: 30px; }
.cms-login-status { border-top: 1px solid #19435c; padding: 8px 0 9.6px; line-height: 16px; font-size: 11px; } .cms-login-status { border-top: 1px solid #19435c; padding: 12px 0; line-height: 16px; font-size: 11px; min-height: 28px; }
.cms-login-status .logout-link { display: inline-block; height: 16px; width: 16px; float: left; margin: 0 8px 0 5px; background: url('../images/sprites-32x32-s47450c5f5b.png') 0 -772px no-repeat; text-indent: -9999em; opacity: 0.9; } .cms-login-status .logout-link { display: inline-block; height: 28px; width: 16px; float: left; margin: 0 8px 0 5px; background: url('../images/sprites-32x32-s47450c5f5b.png') 0 -772px no-repeat; background-position: 0 -766px; text-indent: -9999em; opacity: 0.9; }
.cms-login-status .logout-link:hover, .cms-login-status .logout-link:focus { opacity: 1; } .cms-login-status .logout-link:hover, .cms-login-status .logout-link:focus { opacity: 1; }
.cms-login-status span { padding-top: 6px; }
.cms-menu { z-index: 80; background: #b0bec7; width: 160px; -moz-box-shadow: rgba(0, 0, 0, 0.9) 0 0 3px; -webkit-box-shadow: rgba(0, 0, 0, 0.9) 0 0 3px; box-shadow: rgba(0, 0, 0, 0.9) 0 0 3px; } .cms-menu { z-index: 80; background: #b0bec7; width: 160px; -moz-box-shadow: rgba(0, 0, 0, 0.9) 0 0 3px; -webkit-box-shadow: rgba(0, 0, 0, 0.9) 0 0 3px; box-shadow: rgba(0, 0, 0, 0.9) 0 0 3px; }
.cms-menu a { text-decoration: none; } .cms-menu a { text-decoration: none; }
@ -942,9 +1074,9 @@ li.class-ErrorPage > a .jstree-pageicon { background-position: 0 -112px; }
.cms-menu.collapsed .cms-menu-list li { width: 100%; float: left; } .cms-menu.collapsed .cms-menu-list li { width: 100%; float: left; }
.cms-menu.collapsed .cms-menu-list li span.text { display: none; } .cms-menu.collapsed .cms-menu-list li span.text { display: none; }
.cms-menu.collapsed .cms-menu-list li ul { display: none; } .cms-menu.collapsed .cms-menu-list li ul { display: none; }
.cms-menu.collapsed .cms-login-status { height: 16px; } .cms-menu.collapsed .cms-login-status { height: 28px; }
.cms-menu.collapsed .cms-login-status span { display: none; } .cms-menu.collapsed .cms-login-status span { display: none; }
.cms-menu.collapsed .cms-logo { height: 20px; padding: 10px 0 9px; } .cms-menu.collapsed .cms-logo { height: 28px; padding: 12px 0 11px; }
.cms-menu.collapsed.cms-panel .cms-panel-content { display: block; } .cms-menu.collapsed.cms-panel .cms-panel-content { display: block; }
.cms-menu.collapsed .ss-ui-button.sticky-toggle { width: 50%; } .cms-menu.collapsed .ss-ui-button.sticky-toggle { width: 50%; }
.cms-menu .cms-panel-toggle a, .cms-menu .cms-panel-toggle a.toggle-expand { float: right; width: 20px; } .cms-menu .cms-panel-toggle a, .cms-menu .cms-panel-toggle a.toggle-expand { float: right; width: 20px; }
@ -1024,8 +1156,8 @@ li.class-ErrorPage > a .jstree-pageicon { background-position: 0 -112px; }
.cms-content-controls .preview-selector .chzn-container.chzn-with-rise .chzn-drop ul li.restricted:before { opacity: 0.2; } .cms-content-controls .preview-selector .chzn-container.chzn-with-rise .chzn-drop ul li.restricted:before { opacity: 0.2; }
.cms-content-controls .preview-selector .chzn-container.chzn-with-rise .chzn-drop ul li span { display: block; color: #6c6c6c; font-size: 0.85em; line-height: 1.1em; padding-left: 23px; } .cms-content-controls .preview-selector .chzn-container.chzn-with-rise .chzn-drop ul li span { display: block; color: #6c6c6c; font-size: 0.85em; line-height: 1.1em; padding-left: 23px; }
.cms-content-controls .preview-selector .chzn-container.chzn-with-rise .chzn-drop ul li .icon-view { margin-right: 4px; } .cms-content-controls .preview-selector .chzn-container.chzn-with-rise .chzn-drop ul li .icon-view { margin-right: 4px; }
.cms-content-controls .preview-selector .chzn-drop ul.chzn-results li.result-selected { background: #e6eaed; color: #444; } .cms-content-controls .preview-selector .chzn-drop ul.chzn-results li.result-selected { background: #e6eaed; color: #66727d; }
.cms-content-controls .preview-selector .chzn-drop ul.chzn-results li.result-selected.highlighted, .cms-content-controls .preview-selector .chzn-drop ul.chzn-results li.result-selected:hover, .cms-content-controls .preview-selector .chzn-drop ul.chzn-results li.result-selected:focus { background: #e6eaed; color: #444; } .cms-content-controls .preview-selector .chzn-drop ul.chzn-results li.result-selected.highlighted, .cms-content-controls .preview-selector .chzn-drop ul.chzn-results li.result-selected:hover, .cms-content-controls .preview-selector .chzn-drop ul.chzn-results li.result-selected:focus { background: #e6eaed; color: #66727d; }
.cms-content-controls .cms-preview-states { float: right; } .cms-content-controls .cms-preview-states { float: right; }
.cms-content-controls .cms-preview-states select { max-width: 150px; } .cms-content-controls .cms-preview-states select { max-width: 150px; }
.cms-content-controls .cms-preview-states.dropdown { max-width: 150px; } .cms-content-controls .cms-preview-states.dropdown { max-width: 150px; }
@ -1087,7 +1219,7 @@ visible. Added and removed with js in TabSet.js */ /***************************
.cms .ss-ui-action-tabset.multi ul.ui-tabs-nav li.ui-state-active a:active, .cms .ss-ui-action-tabset.multi ul.ui-tabs-nav li.ui-state-active a span:active { outline: none; box-shadow: none; -webkit-box-shadow: none; } .cms .ss-ui-action-tabset.multi ul.ui-tabs-nav li.ui-state-active a:active, .cms .ss-ui-action-tabset.multi ul.ui-tabs-nav li.ui-state-active a span:active { outline: none; box-shadow: none; -webkit-box-shadow: none; }
.cms .ss-ui-action-tabset.multi ul.ui-tabs-nav li.first { -moz-border-radius-topleft: 3px; -webkit-border-top-left-radius: 3px; border-top-left-radius: 3px; -moz-border-radius-bottomleft: 3px; -webkit-border-bottom-left-radius: 3px; border-bottom-left-radius: 3px; border-left: none; } .cms .ss-ui-action-tabset.multi ul.ui-tabs-nav li.first { -moz-border-radius-topleft: 3px; -webkit-border-top-left-radius: 3px; border-top-left-radius: 3px; -moz-border-radius-bottomleft: 3px; -webkit-border-bottom-left-radius: 3px; border-bottom-left-radius: 3px; border-left: none; }
.cms .ss-ui-action-tabset.multi ul.ui-tabs-nav li.last { -moz-border-radius-topright: 3px; -webkit-border-top-right-radius: 3px; border-top-right-radius: 3px; -moz-border-radius-bottomright: 3px; -webkit-border-bottom-right-radius: 3px; border-bottom-right-radius: 3px; border-right: none; } .cms .ss-ui-action-tabset.multi ul.ui-tabs-nav li.last { -moz-border-radius-topright: 3px; -webkit-border-top-right-radius: 3px; border-top-right-radius: 3px; -moz-border-radius-bottomright: 3px; -webkit-border-bottom-right-radius: 3px; border-bottom-right-radius: 3px; border-right: none; }
.cms .ss-ui-action-tabset.multi ul.ui-tabs-nav li a.tab-nav-link { color: #444; display: inline-block; font-weight: bold; line-height: 16px; padding: 5px 10px; } .cms .ss-ui-action-tabset.multi ul.ui-tabs-nav li a.tab-nav-link { color: #66727d; display: inline-block; font-weight: bold; line-height: 16px; padding: 5px 10px; }
.cms .ss-ui-action-tabset.multi ul.ui-tabs-nav li a.tab-nav-link .ui-no-icon { display: inline-block; float: left; height: 16px; padding: 0 2px; width: 16px; } .cms .ss-ui-action-tabset.multi ul.ui-tabs-nav li a.tab-nav-link .ui-no-icon { display: inline-block; float: left; height: 16px; padding: 0 2px; width: 16px; }
.cms .ss-ui-action-tabset.multi ul.ui-tabs-nav li a.tab-nav-link .title { display: inline-block; line-height: 18px; } .cms .ss-ui-action-tabset.multi ul.ui-tabs-nav li a.tab-nav-link .title { display: inline-block; line-height: 18px; }
.cms .ss-ui-action-tabset.multi ul.ui-tabs-nav li a.tab-nav-link.view-mode-batchactions-wrapper .title { margin-left: 22px; } .cms .ss-ui-action-tabset.multi ul.ui-tabs-nav li a.tab-nav-link.view-mode-batchactions-wrapper .title { margin-left: 22px; }
@ -1098,8 +1230,8 @@ visible. Added and removed with js in TabSet.js */ /***************************
.cms .ss-ui-action-tabset.multi .ss-ui-action-tab.ui-tabs-panel .ui-widget-content { background: none; } .cms .ss-ui-action-tabset.multi .ss-ui-action-tab.ui-tabs-panel .ui-widget-content { background: none; }
.cms .ss-ui-action-tabset.multi .ss-ui-action-tab.ui-tabs-panel .field { /* Fields are more compressed in some areas compared to the main content editing window so the below alters the internal spacing of the fields so we can move that spacing to between the form fields rather than padding */ border-bottom: none; -moz-box-shadow: none; -webkit-box-shadow: none; box-shadow: none; } .cms .ss-ui-action-tabset.multi .ss-ui-action-tab.ui-tabs-panel .field { /* Fields are more compressed in some areas compared to the main content editing window so the below alters the internal spacing of the fields so we can move that spacing to between the form fields rather than padding */ border-bottom: none; -moz-box-shadow: none; -webkit-box-shadow: none; box-shadow: none; }
.cms .ss-ui-action-tabset.multi .ss-ui-action-tab.ui-tabs-panel .field label { float: none; width: auto; font-size: 12px; padding: 0 8px 4px 0; } .cms .ss-ui-action-tabset.multi .ss-ui-action-tab.ui-tabs-panel .field label { float: none; width: auto; font-size: 12px; padding: 0 8px 4px 0; }
.cms .ss-ui-action-tabset.multi .ss-ui-action-tab.ui-tabs-panel .field label.extra-details { overflow: hidden; margin-top: 10px; display: block; color: #9d9d9d; font-style: italic; font-weight: normal; font-size: 1em; float: left; text-shadow: none; } .cms .ss-ui-action-tabset.multi .ss-ui-action-tab.ui-tabs-panel .field label.extra-details { overflow: hidden; margin-top: 10px; display: block; color: #c5cbd0; font-style: italic; font-weight: normal; font-size: 1em; float: left; text-shadow: none; }
.cms .ss-ui-action-tabset.multi .ss-ui-action-tab.ui-tabs-panel .field label.extra-details.fill:before { color: #fff; content: '?'; font-size: 12px; -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box; padding-left: 3px; padding-right: 3px; display: block; float: left; text-shadow: none; -moz-border-radius: 50px; -webkit-border-radius: 50px; border-radius: 50px; background-color: #b7b7b7; width: 15px; height: 15px; margin-right: 5px; margin-bottom: 5px; } .cms .ss-ui-action-tabset.multi .ss-ui-action-tab.ui-tabs-panel .field label.extra-details.fill:before { color: #fff; content: '?'; font-size: 12px; -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box; padding-left: 3px; padding-right: 3px; display: block; float: left; text-shadow: none; -moz-border-radius: 50px; -webkit-border-radius: 50px; border-radius: 50px; background-color: #e2e4e7; width: 15px; height: 15px; margin-right: 5px; margin-bottom: 5px; }
.cms .ss-ui-action-tabset.multi .ss-ui-action-tab.ui-tabs-panel .field .middleColumn { margin: 0; } .cms .ss-ui-action-tabset.multi .ss-ui-action-tab.ui-tabs-panel .field .middleColumn { margin: 0; }
.cms .ss-ui-action-tabset.multi .ss-ui-action-tab.ui-tabs-panel .field input.text, .cms .ss-ui-action-tabset.multi .ss-ui-action-tab.ui-tabs-panel .field select, .cms .ss-ui-action-tabset.multi .ss-ui-action-tab.ui-tabs-panel .field textarea { padding: 5px; font-size: 11px; } .cms .ss-ui-action-tabset.multi .ss-ui-action-tab.ui-tabs-panel .field input.text, .cms .ss-ui-action-tabset.multi .ss-ui-action-tab.ui-tabs-panel .field select, .cms .ss-ui-action-tabset.multi .ss-ui-action-tab.ui-tabs-panel .field textarea { padding: 5px; font-size: 11px; }
.cms .ss-ui-action-tabset.multi .ss-ui-action-tab.ui-tabs-panel .field.checkbox { padding: 0 8px 0; } .cms .ss-ui-action-tabset.multi .ss-ui-action-tab.ui-tabs-panel .field.checkbox { padding: 0 8px 0; }
@ -1141,7 +1273,7 @@ visible. Added and removed with js in TabSet.js */ /***************************
.cms .ss-ui-action-tabset.action-menus.ss-tabset ul.ui-tabs-nav { margin: 0; float: left; /* needed for ie but doesnt effect other browsers */ } .cms .ss-ui-action-tabset.action-menus.ss-tabset ul.ui-tabs-nav { margin: 0; float: left; /* needed for ie but doesnt effect other browsers */ }
.cms .ss-ui-action-tabset.action-menus.ss-tabset ul.ui-tabs-nav li { background: none; border: none; border-bottom: none !important; display: inline; padding: 0; /* Make arrow point in up when nav open */ } .cms .ss-ui-action-tabset.action-menus.ss-tabset ul.ui-tabs-nav li { background: none; border: none; border-bottom: none !important; display: inline; padding: 0; /* Make arrow point in up when nav open */ }
.cms .ss-ui-action-tabset.action-menus.ss-tabset ul.ui-tabs-nav li:hover, .cms .ss-ui-action-tabset.action-menus.ss-tabset ul.ui-tabs-nav li:active { -moz-box-shadow: none; -webkit-box-shadow: none; box-shadow: none; outline: none; } .cms .ss-ui-action-tabset.action-menus.ss-tabset ul.ui-tabs-nav li:hover, .cms .ss-ui-action-tabset.action-menus.ss-tabset ul.ui-tabs-nav li:active { -moz-box-shadow: none; -webkit-box-shadow: none; box-shadow: none; outline: none; }
.cms .ss-ui-action-tabset.action-menus.ss-tabset ul.ui-tabs-nav li a { text-shadow: #fff 0 1px 1px; color: #0073C1; font-size: 13px; font-weight: normal; line-height: 24px; padding: 0 25px 0 10px; /* Arrow */ } .cms .ss-ui-action-tabset.action-menus.ss-tabset ul.ui-tabs-nav li a { text-shadow: #fff 0 1px 1px; color: #0073C1; font-size: 13px; font-weight: normal; line-height: 24px; padding: 0 25px 0 10px; border-bottom: 0; margin: 0; /* Arrow */ }
.cms .ss-ui-action-tabset.action-menus.ss-tabset ul.ui-tabs-nav li a:hover, .cms .ss-ui-action-tabset.action-menus.ss-tabset ul.ui-tabs-nav li a:active { -moz-box-shadow: none; -webkit-box-shadow: none; box-shadow: none; outline: none; } .cms .ss-ui-action-tabset.action-menus.ss-tabset ul.ui-tabs-nav li a:hover, .cms .ss-ui-action-tabset.action-menus.ss-tabset ul.ui-tabs-nav li a:active { -moz-box-shadow: none; -webkit-box-shadow: none; box-shadow: none; outline: none; }
.cms .ss-ui-action-tabset.action-menus.ss-tabset ul.ui-tabs-nav li a:hover { text-shadow: #fff 0 10px 10px; color: #005b98; } .cms .ss-ui-action-tabset.action-menus.ss-tabset ul.ui-tabs-nav li a:hover { text-shadow: #fff 0 10px 10px; color: #005b98; }
.cms .ss-ui-action-tabset.action-menus.ss-tabset ul.ui-tabs-nav li a:after { background: url('../images/sprites-32x32-s47450c5f5b.png') 0 -26px no-repeat; border-bottom: 0; content: ""; display: inline-block; height: 16px; margin-left: 6px; width: 16px; } .cms .ss-ui-action-tabset.action-menus.ss-tabset ul.ui-tabs-nav li a:after { background: url('../images/sprites-32x32-s47450c5f5b.png') 0 -26px no-repeat; border-bottom: 0; content: ""; display: inline-block; height: 16px; margin-left: 6px; width: 16px; }
@ -1155,8 +1287,8 @@ visible. Added and removed with js in TabSet.js */ /***************************
.cms .ss-ui-action-tabset.action-menus.ss-tabset .ui-tabs-panel .ui-widget-content { background: none; } .cms .ss-ui-action-tabset.action-menus.ss-tabset .ui-tabs-panel .ui-widget-content { background: none; }
.cms .ss-ui-action-tabset.action-menus.ss-tabset .ui-tabs-panel .field { /* Fields are more compressed in some areas compared to the main content editing window so the below alters the internal spacing of the fields so we can move that spacing to between the form fields rather than padding */ border-bottom: none; -moz-box-shadow: none; -webkit-box-shadow: none; box-shadow: none; } .cms .ss-ui-action-tabset.action-menus.ss-tabset .ui-tabs-panel .field { /* Fields are more compressed in some areas compared to the main content editing window so the below alters the internal spacing of the fields so we can move that spacing to between the form fields rather than padding */ border-bottom: none; -moz-box-shadow: none; -webkit-box-shadow: none; box-shadow: none; }
.cms .ss-ui-action-tabset.action-menus.ss-tabset .ui-tabs-panel .field label { float: none; width: auto; font-size: 12px; padding: 0 8px 4px 0; } .cms .ss-ui-action-tabset.action-menus.ss-tabset .ui-tabs-panel .field label { float: none; width: auto; font-size: 12px; padding: 0 8px 4px 0; }
.cms .ss-ui-action-tabset.action-menus.ss-tabset .ui-tabs-panel .field label.extra-details { overflow: hidden; margin-top: 10px; display: block; color: #9d9d9d; font-style: italic; font-weight: normal; font-size: 1em; float: left; text-shadow: none; } .cms .ss-ui-action-tabset.action-menus.ss-tabset .ui-tabs-panel .field label.extra-details { overflow: hidden; margin-top: 10px; display: block; color: #c5cbd0; font-style: italic; font-weight: normal; font-size: 1em; float: left; text-shadow: none; }
.cms .ss-ui-action-tabset.action-menus.ss-tabset .ui-tabs-panel .field label.extra-details.fill:before { color: #fff; content: '?'; font-size: 12px; -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box; padding-left: 3px; padding-right: 3px; display: block; float: left; text-shadow: none; -moz-border-radius: 50px; -webkit-border-radius: 50px; border-radius: 50px; background-color: #b7b7b7; width: 15px; height: 15px; margin-right: 5px; margin-bottom: 5px; } .cms .ss-ui-action-tabset.action-menus.ss-tabset .ui-tabs-panel .field label.extra-details.fill:before { color: #fff; content: '?'; font-size: 12px; -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box; padding-left: 3px; padding-right: 3px; display: block; float: left; text-shadow: none; -moz-border-radius: 50px; -webkit-border-radius: 50px; border-radius: 50px; background-color: #e2e4e7; width: 15px; height: 15px; margin-right: 5px; margin-bottom: 5px; }
.cms .ss-ui-action-tabset.action-menus.ss-tabset .ui-tabs-panel .field .middleColumn { margin: 0; } .cms .ss-ui-action-tabset.action-menus.ss-tabset .ui-tabs-panel .field .middleColumn { margin: 0; }
.cms .ss-ui-action-tabset.action-menus.ss-tabset .ui-tabs-panel .field input.text, .cms .ss-ui-action-tabset.action-menus.ss-tabset .ui-tabs-panel .field select, .cms .ss-ui-action-tabset.action-menus.ss-tabset .ui-tabs-panel .field textarea { padding: 5px; font-size: 11px; } .cms .ss-ui-action-tabset.action-menus.ss-tabset .ui-tabs-panel .field input.text, .cms .ss-ui-action-tabset.action-menus.ss-tabset .ui-tabs-panel .field select, .cms .ss-ui-action-tabset.action-menus.ss-tabset .ui-tabs-panel .field textarea { padding: 5px; font-size: 11px; }
.cms .ss-ui-action-tabset.action-menus.ss-tabset .ui-tabs-panel .field.checkbox { padding: 0 8px 0; } .cms .ss-ui-action-tabset.action-menus.ss-tabset .ui-tabs-panel .field.checkbox { padding: 0 8px 0; }
@ -1176,17 +1308,18 @@ visible. Added and removed with js in TabSet.js */ /***************************
.cms .ss-ui-action-tabset.action-menus.ss-tabset .ui-tabs-panel .treedropdown .treedropdownfield-toggle-panel-link, .cms .ss-ui-action-tabset.action-menus.ss-tabset .ui-tabs-panel .SelectionGroup li.selected div.field .treedropdownfield-toggle-panel-link { background: none; border-left: none; padding: 5px 3px; } .cms .ss-ui-action-tabset.action-menus.ss-tabset .ui-tabs-panel .treedropdown .treedropdownfield-toggle-panel-link, .cms .ss-ui-action-tabset.action-menus.ss-tabset .ui-tabs-panel .SelectionGroup li.selected div.field .treedropdownfield-toggle-panel-link { background: none; border-left: none; padding: 5px 3px; }
.cms .ss-ui-action-tabset.action-menus.ss-tabset .ui-tabs-panel .treedropdown .treedropdownfield-toggle-panel-link .ui-icon, .cms .ss-ui-action-tabset.action-menus.ss-tabset .ui-tabs-panel .SelectionGroup li.selected div.field .treedropdownfield-toggle-panel-link .ui-icon { float: right; opacity: 0.7; } .cms .ss-ui-action-tabset.action-menus.ss-tabset .ui-tabs-panel .treedropdown .treedropdownfield-toggle-panel-link .ui-icon, .cms .ss-ui-action-tabset.action-menus.ss-tabset .ui-tabs-panel .SelectionGroup li.selected div.field .treedropdownfield-toggle-panel-link .ui-icon { float: right; opacity: 0.7; }
.cms .ss-ui-action-tabset.action-menus.ss-tabset .ui-tabs-panel .cms-add-form ul.SelectionGroup { padding-left: 0; padding-right: 0; overflow: visible; border-bottom: none; } .cms .ss-ui-action-tabset.action-menus.ss-tabset .ui-tabs-panel .cms-add-form ul.SelectionGroup { padding-left: 0; padding-right: 0; overflow: visible; border-bottom: none; }
.cms .ss-ui-action-tabset.action-menus.ss-tabset .ui-tabs-panel .cms-sitetree-information { border-bottom: 1px solid #e6e7e8; margin-bottom: 8px; padding: 0 20px 0 0; margin-right: 10px; margin-left: 10px; } .cms .ss-ui-action-tabset.action-menus.ss-tabset .ui-tabs-panel .cms-sitetree-information { border-bottom: 1px solid #e8e9eb; margin-bottom: 8px; padding: 0 20px 0 0; margin-right: 10px; margin-left: 10px; }
.cms .ss-ui-action-tabset.action-menus.ss-tabset .ui-tabs-panel .cms-sitetree-information p.meta-info { color: #999; font-size: 11px; line-height: 16px; margin-bottom: 8px; white-space: nowrap; } .cms .ss-ui-action-tabset.action-menus.ss-tabset .ui-tabs-panel .cms-sitetree-information p.meta-info { color: #999; font-size: 11px; line-height: 16px; margin-bottom: 8px; white-space: nowrap; }
.cms .ss-ui-action-tabset.action-menus.ss-tabset .ui-tabs-panel button.ss-ui-button { width: 100%; } .cms .ss-ui-action-tabset.action-menus.ss-tabset .ui-tabs-panel button.ss-ui-button { width: 100%; }
.cms .ss-ui-action-tabset.action-menus.ss-tabset .ui-tabs-panel button.ss-ui-button:hover, .cms .ss-ui-action-tabset.action-menus.ss-tabset .ui-tabs-panel button.ss-ui-button:focus, .cms .ss-ui-action-tabset.action-menus.ss-tabset .ui-tabs-panel button.ss-ui-button:active { -moz-box-shadow: none; -webkit-box-shadow: none; box-shadow: none; background-color: #e0e5e8; outline: none; } .cms .ss-ui-action-tabset.action-menus.ss-tabset .ui-tabs-panel button.ss-ui-button:hover, .cms .ss-ui-action-tabset.action-menus.ss-tabset .ui-tabs-panel button.ss-ui-button:focus, .cms .ss-ui-action-tabset.action-menus.ss-tabset .ui-tabs-panel button.ss-ui-button:active { -moz-box-shadow: none; -webkit-box-shadow: none; box-shadow: none; background-color: #e0e5e8; outline: none; }
.cms .ss-ui-action-tabset.action-menus.ss-tabset .last .ui-tabs-panel.ss-ui-action-tab { left: auto; right: -1px; } .cms .ss-ui-action-tabset.action-menus.ss-tabset .last .ui-tabs-panel.ss-ui-action-tab { left: auto; right: -1px; }
.cms .cms-content-actions .Actions { overflow: visible; } .cms .cms-content-actions .Actions { overflow: visible; }
.ModelAdmin .cms-content-fields { overflow: hidden; } .ModelAdmin .cms-content-fields { /** DEPRECATED: .cms-content-tools will be removed in 4.0 Use .cms-content-filters instead. Hide certain elements when shown in "sidebar mode" */ }
.ModelAdmin .cms-content-fields .cms-edit-form { overflow-y: auto; overflow-x: hidden; } .ModelAdmin .cms-content-fields .cms-edit-form { overflow-y: auto; overflow-x: hidden; }
.ModelAdmin .cms-content-fields .cms-content-tools .cms-panel-content .cms-search-form .resetformaction { margin-right: 0px; } .ModelAdmin .cms-content-fields .cms-content-tools .cms-panel-content .cms-search-form .resetformaction { margin-right: 0px; }
.ModelAdmin .cms-content-fields .cms-content-tools .cms-panel-content #Form_ImportForm { overflow: hidden; } .ModelAdmin .cms-content-fields .cms-content-tools .cms-panel-content #Form_ImportForm { overflow: hidden; }
.ModelAdmin .cms-content-fields .cms-content-filters { padding-top: 16px; }
.permissioncheckboxset h5, .permissioncheckboxsetfield_readonly h5 { margin: 0; } .permissioncheckboxset h5, .permissioncheckboxsetfield_readonly h5 { margin: 0; }
.permissioncheckboxset .optionset, .permissioncheckboxsetfield_readonly .optionset { overflow: auto; } .permissioncheckboxset .optionset, .permissioncheckboxsetfield_readonly .optionset { overflow: auto; }
@ -1225,7 +1358,7 @@ green tick icon as a background this is created using compass generated classes
@media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2 / 1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { /* Loading spinner */ @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2 / 1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { /* Loading spinner */
.cms-content-loading-spinner { background-image: url(../images/spinner@2x.gif); background-size: 43px 43px; } .cms-content-loading-spinner { background-image: url(../images/spinner@2x.gif); background-size: 43px 43px; }
.ui-dialog .ui-dialog-content.loading { background-image: url(../images/spinner@2x.gif); background-size: 43px 43px; } .ui-dialog .ui-dialog-content.loading { background-image: url(../images/spinner@2x.gif); background-size: 43px 43px; }
.ui-dialog.loading { background-image: url(../images/spinner@2x.gif); background-size: 43px 22px; } .ui-dialog.loading { background-image: url(../images/spinner@2x.gif); background-size: 43px 43px; }
/* Default CMS logo */ /* Default CMS logo */
.cms-logo a { background-image: url("../images/logo_small@2x.png"); background-size: 22px 22px; } .cms-logo a { background-image: url("../images/logo_small@2x.png"); background-size: 22px 22px; }
/* Logout button */ /* Logout button */
@ -1277,3 +1410,5 @@ green tick icon as a background this is created using compass generated classes
.icon.icon-16.icon-reportadmin { background-position: 0 -160px; } .icon.icon-16.icon-reportadmin { background-position: 0 -160px; }
.icon.icon-16.icon-commentadmin { background-position: 0 0; } .icon.icon-16.icon-commentadmin { background-position: 0 0; }
.icon.icon-16.icon-help { background-position: 0 -96px; } } .icon.icon-16.icon-help { background-position: 0 -96px; } }
/*# sourceMappingURL=screen.css.map */

7
admin/css/screen.css.map Normal file

File diff suppressed because one or more lines are too long

12
admin/font/LICENSE.txt Normal file
View File

@ -0,0 +1,12 @@
Font license info
## Entypo
Copyright (C) 2012 by Daniel Bruce
Author: Daniel Bruce
License: SIL (http://scripts.sil.org/OFL)
Homepage: http://www.entypo.com

BIN
admin/font/fontello.eot Normal file

Binary file not shown.

21
admin/font/fontello.svg Normal file
View File

@ -0,0 +1,21 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg">
<metadata>Copyright (C) 2015 by original authors @ fontello.com</metadata>
<defs>
<font id="fontello" horiz-adv-x="1000" >
<font-face font-family="fontello" font-weight="400" font-stretch="normal" units-per-em="1000" ascent="850" descent="-150" />
<missing-glyph horiz-adv-x="1000" />
<glyph glyph-name="search" unicode="&#xe800;" d="m772 78q30-34 6-62l-46-46q-36-32-68 0l-190 190q-74-42-156-42-128 0-223 95t-95 223 90 219 218 91 224-95 96-223q0-88-46-162z m-678 358q0-88 68-156t156-68 151 63 63 153q0 88-68 155t-156 67-151-63-63-151z" horiz-adv-x="789" />
<glyph glyph-name="tree" unicode="&#xe801;" d="m700 633v84c0 33 28 55 56 55h133c33 0 55-28 55-55v-228c0-33-27-56-55-56h-128c-33 0-55 28-55 56v89h-184c0-17 0-422 0-422 0-17 11-28 28-28h150v83c0 33 28 56 56 56h133c33 0 55-28 55-56v-228c0-33-27-55-55-55h-128c-33 0-55 28-55 55v84h-156c-50 0-89 39-89 83v428h-161v-84c0-33-28-55-56-55h-133c-33 0-55 28-55 55v223c0 33 27 55 55 55h128c33 0 55-28 55-55v-84l406 0z" horiz-adv-x="1000" />
<glyph glyph-name="list2" unicode="&#xe802;" d="m150 444c-28 0-50-11-67-27-16-17-27-39-27-67 0-28 11-50 27-67 17-16 39-27 67-27s50 11 67 27c22 17 27 39 27 67 0 28-11 50-27 67-17 16-39 27-67 27z m0 256c-28 0-50-11-67-28-16-16-27-44-27-66 0-28 11-50 27-67 17-22 39-28 67-28s50 6 67 28c16 17 27 39 27 67 0 27-11 50-27 66-17 17-39 28-67 28z m0-511c-28 0-50-11-67-28-16-17-27-39-27-67 0-27 11-50 27-66 17-17 39-28 67-28s50 11 67 28c16 16 27 39 27 66 0 28-11 50-27 67-17 22-39 28-67 28z m789 472c-6 6-6 6-11 6h-600c-6 0-6 0-11-6-6 0-6-5-6-11v-94c0-6 0-6 6-12 5-5 5-5 11-5h605c6 0 6 0 11 5 0 6 0 6 0 12v94c0 6 0 11-5 11z m-11-250h-600c-6 0-6 0-11-5-6 0-6-6-6-6v-94c0-6 0-6 6-12 5-5 5-5 11-5h605c6 0 6 0 11 5 0 0 0 6 0 6v100c0 6 0 6-5 11 0 0-6 0-11 0z m0-250h-600c-6 0-6 0-11-5-6-6-6-6-6-12v-94c0-6 0-6 6-11 5-6 5-6 11-6h605c6 0 6 0 11 6 6 5 6 5 6 11v94c0 6 0 6-6 12-5 0-11 5-16 5z" horiz-adv-x="1000" />
<glyph glyph-name="cog" unicode="&#xe803;" d="m760 350q0-72 80-122-12-40-34-82-70 18-136-44-54-58-34-136-40-20-84-36-46 82-132 82t-132-82q-44 16-84 36 20 80-34 136-54 54-136 34-14 26-34 82 82 52 82 132 0 72-82 124 20 56 34 82 74-18 136 44 54 56 34 136 42 22 84 34 46-80 132-80t132 80q42-12 84-34-20-78 34-136 66-62 136-44 22-42 34-82-80-50-80-124z m-340-182q76 0 129 53t53 129-53 130-129 54-129-54-53-130 53-129 129-53z" horiz-adv-x="840" />
<glyph glyph-name="check" unicode="&#xe804;" d="m786 331v-177q0-67-47-114t-114-47h-464q-67 0-114 47t-47 114v464q0 66 47 113t114 48h464q35 0 65-14 9-4 10-13 2-10-5-16l-27-28q-6-5-13-5-2 0-5 1-13 3-25 3h-464q-37 0-63-26t-27-63v-464q0-37 27-63t63-27h464q37 0 63 27t26 63v141q0 8 5 13l36 35q6 6 13 6 3 0 7-2 11-4 11-16z m129 273l-455-454q-13-14-31-14t-32 14l-240 240q-14 13-14 31t14 32l61 62q14 13 32 13t32-13l147-147 361 361q13 13 31 13t32-13l62-61q13-14 13-32t-13-32z" horiz-adv-x="928.6" />
<glyph glyph-name="plus-solid" unicode="&#xe805;" d="m817 667c-89 83-200 127-317 127s-228-44-317-127c-83-89-127-200-127-317 0-117 44-228 127-317 89-83 200-127 317-127 117 0 228 44 317 127 83 89 127 200 127 317s-44 228-127 317z m-95-373h-166v-166h-112v166h-166v112h166v166h112v-166h166v-112z" horiz-adv-x="1000" />
<glyph glyph-name="list" unicode="&#xe806;" d="m286 154v-108q0-22-16-37t-38-16h-178q-23 0-38 16t-16 37v108q0 22 16 38t38 15h178q22 0 38-15t16-38z m0 285v-107q0-22-16-38t-38-15h-178q-23 0-38 15t-16 38v107q0 23 16 38t38 16h178q22 0 38-16t16-38z m714-285v-108q0-22-16-37t-38-16h-535q-23 0-38 16t-16 37v108q0 22 16 38t38 15h535q23 0 38-15t16-38z m-714 571v-107q0-22-16-38t-38-16h-178q-23 0-38 16t-16 38v107q0 22 16 38t38 16h178q22 0 38-16t16-38z m714-286v-107q0-22-16-38t-38-15h-535q-23 0-38 15t-16 38v107q0 23 16 38t38 16h535q23 0 38-16t16-38z m0 286v-107q0-22-16-38t-38-16h-535q-23 0-38 16t-16 38v107q0 22 16 38t38 16h535q23 0 38-16t16-38z" horiz-adv-x="1000" />
<glyph glyph-name="plus" unicode="&#xe807;" d="m817 667c-89 83-200 127-317 127s-228-44-317-127c-83-89-127-200-127-317 0-117 44-228 127-317 89-83 200-127 317-127 117 0 228 44 317 127 83 89 127 200 127 317s-44 228-127 317z m-73-561c-66-67-150-100-244-100-94 0-178 33-244 100-67 66-100 150-100 244s33 178 100 244c66 67 150 100 244 100s178-33 244-100c67-66 100-150 100-244s-33-178-100-244z m-27 194h-167v-167h-100v167h-167v100h167v167h100v-167h167v-100z" horiz-adv-x="1000" />
<glyph glyph-name="search2" unicode="&#xe808;" d="m922-72c-16-17-33-22-50-22-16 0-39 5-50 22l-211 211c0 0-5 5-5 5-56-33-123-55-195-55-200 0-355 161-355 355 0 195 155 350 350 350 194 0 350-155 350-350 0-72-23-138-56-188 0 0 6-6 6-6l211-211c33-33 33-83 5-111z m-272 516c0 134-111 239-244 239s-239-105-239-239c0-133 105-238 239-238s244 105 244 238z" horiz-adv-x="1000" />
<glyph glyph-name="pencil" unicode="&#xe80b;" d="m0-119l60 281 627 628q21 21 51 21t50-20l121-122q21-21 21-50t-21-50l-628-628z m68 69l194 44 24 25-149 149-24-24z" horiz-adv-x="930.2" />
</font>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 4.9 KiB

BIN
admin/font/fontello.ttf Normal file

Binary file not shown.

BIN
admin/font/fontello.woff Normal file

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 110 B

After

Width:  |  Height:  |  Size: 14 KiB

View File

@ -40,7 +40,8 @@
}, },
/** /**
* Register default bulk confirmation dialogs * @func registerDefault
* @desc Register default bulk confirmation dialogs
*/ */
registerDefault: function() { registerDefault: function() {
// Publish selected pages action // Publish selected pages action
@ -115,60 +116,15 @@
}); });
}, },
/**
* Constructor: onmatch
*/
onadd: function() { onadd: function() {
this._updateStateFromViewMode();
this.registerDefault(); this.registerDefault();
this._super(); this._super();
}, },
'from .cms-content-batchactions :input[name=view-mode-batchactions]': {
onclick: function(e){
var checkbox = $(e.target), dropdown = this.find(':input[name=Action]'), tree = this.getTree();
if(checkbox.is(':checked')) {
tree.addClass('multiple');
tree.removeClass('draggable');
this.serializeFromTree();
} else {
tree.removeClass('multiple');
tree.addClass('draggable');
}
this._updateStateFromViewMode();
}
},
/** /**
* Updates the select box state according to the current view mode. * @func register
*/ * @param {string} type
_updateStateFromViewMode: function() { * @param {function} callback
var viewMode = $('.cms-content-batchactions :input[name=view-mode-batchactions]');
var batchactions = $('.cms-content-batchactions');
var dropdown = this.find(':input[name=Action]');
// Batch actions only make sense when multiselect is enabled.
if(viewMode.is(':checked')) {
dropdown.trigger("liszt:updated");
batchactions.removeClass('inactive');
}
else {
dropdown.trigger("liszt:updated");
// Used timeout to make sure when it shows up you won't see
// the native dropdown
setTimeout(function() { batchactions.addClass('inactive'); }, 100);
}
},
/**
* Function: register
*
* Parameters:
*
* (String) type - ...
* (Function) callback - ...
*/ */
register: function(type, callback) { register: function(type, callback) {
this.trigger('register', {type: type, callback: callback}); this.trigger('register', {type: type, callback: callback});
@ -178,13 +134,9 @@
}, },
/** /**
* Function: unregister * @func unregister
* * @param {string} type
* Remove an existing action. * @desc Remove an existing action.
*
* Parameters:
*
* {String} type
*/ */
unregister: function(type) { unregister: function(type) {
this.trigger('unregister', {type: type}); this.trigger('unregister', {type: type});
@ -195,27 +147,9 @@
}, },
/** /**
* Function: _isActive * @func refreshSelected
* * @param {object} rootNode
* Determines if we should allow and track tree selections. * @desc Ajax callbacks determine which pages is selectable in a certain batch action.
*
* Todo:
* Too much coupling with tabset
*
* Returns:
* (boolean)
*/
_isActive: function() {
return $('.cms-content-batchactions').is(':visible');
},
/**
* Function: refreshSelected
*
* Ajax callbacks determine which pages is selectable in a certain batch action.
*
* Parameters:
* {Object} rootNode
*/ */
refreshSelected : function(rootNode) { refreshSelected : function(rootNode) {
var self = this, var self = this,
@ -232,7 +166,7 @@
} }
// If no action is selected, enable all nodes // If no action is selected, enable all nodes
if(selectedAction == -1) { if(!selectedAction || selectedAction == -1) {
$(rootNode).find('li').each(function() { $(rootNode).find('li').each(function() {
$(this).setEnabled(true); $(this).setEnabled(true);
}); });
@ -267,10 +201,8 @@
}, },
/** /**
* Function: serializeFromTree * @func serializeFromTree
* * @return {boolean}
* Returns:
* (boolean)
*/ */
serializeFromTree: function() { serializeFromTree: function() {
var tree = this.getTree(), ids = tree.getSelectedIDs(); var tree = this.getTree(), ids = tree.getSelectedIDs();
@ -282,20 +214,16 @@
}, },
/** /**
* Function: setIDS * @func setIDS
* * @param {array} ids
* Parameters:
* {Array} ids
*/ */
setIDs: function(ids) { setIDs: function(ids) {
this.find(':input[name=csvIDs]').val(ids ? ids.join(',') : null); this.find(':input[name=csvIDs]').val(ids ? ids.join(',') : null);
}, },
/** /**
* Function: getIDS * @func getIDS
* * @return {array}
* Returns:
* {Array}
*/ */
getIDs: function() { getIDs: function() {
// Map empty value to empty array // Map empty value to empty array
@ -305,12 +233,6 @@
: []; : [];
}, },
/**
* Function: onsubmit
*
* Parameters:
* (Event) e
*/
onsubmit: function(e) { onsubmit: function(e) {
var self = this, ids = this.getIDs(), tree = this.getTree(), actions = this.getActions(); var self = this, ids = this.getIDs(), tree = this.getTree(), actions = this.getActions();
@ -350,8 +272,9 @@
complete: function(xmlhttp, status) { complete: function(xmlhttp, status) {
button.removeClass('loading'); button.removeClass('loading');
// Deselect all nodes // Refresh the tree.
tree.jstree('uncheck_all'); // Makes sure all nodes have the correct CSS classes applied.
tree.jstree('refresh', -1);
self.setIDs([]); self.setIDs([]);
// Reset action // Reset action
@ -396,27 +319,44 @@
}); });
$('.cms-content-batchactions-button').entwine({
onmatch: function () {
this._super();
this.updateTree();
},
onunmatch: function () {
this._super();
},
onclick: function (e) {
this.updateTree();
},
updateTree: function () {
var tree = $('.cms-tree'),
form = $('#Form_BatchActionsForm');
this._super();
if(this.data('active')) {
tree.addClass('multiple');
tree.removeClass('draggable');
form.serializeFromTree();
$('#Form_BatchActionsForm').refreshSelected();
} else {
tree.removeClass('multiple');
tree.addClass('draggable');
}
}
});
/** /**
* Class: #Form_BatchActionsForm :select[name=Action] * Class: #Form_BatchActionsForm :select[name=Action]
*/ */
$('#Form_BatchActionsForm select[name=Action]').entwine({ $('#Form_BatchActionsForm select[name=Action]').entwine({
onmatch: function() {
this.trigger('change');
this._super();
},
onunmatch: function() {
this._super();
},
/**
* Function: onchange
*
* Parameters:
* (Event) e
*/
onchange: function(e) { onchange: function(e) {
var form = $(e.target.form), btn = form.find(':submit'); var form = $(e.target.form),
if($(e.target).val() == -1) { btn = form.find(':submit'),
selected = $(e.target).val();
if(!selected || selected == -1) {
btn.attr('disabled', 'disabled').button('refresh'); btn.attr('disabled', 'disabled').button('refresh');
} else { } else {
btn.removeAttr('disabled').button('refresh'); btn.removeAttr('disabled').button('refresh');

View File

@ -176,7 +176,7 @@
updateMenuFromResponse: function(xhr) { updateMenuFromResponse: function(xhr) {
var controller = xhr.getResponseHeader('X-Controller'); var controller = xhr.getResponseHeader('X-Controller');
if(controller) { if(controller) {
var item = this.find('li#Menu-' + controller); var item = this.find('li#Menu-' + controller.replace(/\\/g, '-').replace(/[^a-zA-Z0-9\-_:.]+/, ''));
if(!item.hasClass('current')) item.select(); if(!item.hasClass('current')) item.select();
} }
this.updateItems(); this.updateItems();

View File

@ -250,7 +250,7 @@
*/ */
createNode: function(html, data, callback) { createNode: function(html, data, callback) {
var self = this, var self = this,
parentNode = data.ParentID ? self.getNodeByID(data.ParentID) : false, parentNode = data.ParentID !== void 0 ? self.getNodeByID(data.ParentID) : false, // Explicitly check for undefined as 0 is a valid ParentID
newNode = $(html); newNode = $(html);
// Extract the state for the new node from the properties taken from the provided HTML template. // Extract the state for the new node from the properties taken from the provided HTML template.
@ -487,26 +487,5 @@
return this.data('id'); return this.data('id');
} }
}); });
$('.cms-content-batchactions input[name=view-mode-batchactions]').entwine({
onmatch: function() {
// set active by default
this.redraw();
this._super();
},
onunmatch: function() {
this._super();
},
onclick: function(e) {
this.redraw();
},
redraw: function(type) {
if(window.debug) console.log('redraw', this.attr('class'), this.get(0));
$('.cms-tree')
.toggleClass('draggable', !this.is(':checked'))
.toggleClass('multiple', this.is(':checked'));
}
});
}); });
}(jQuery)); }(jQuery));

View File

@ -1290,7 +1290,7 @@ jQuery.noConflict();
form.find(".dropdown select").prop('selectedIndex', 0).trigger("liszt:updated"); // Reset chosen.js form.find(".dropdown select").prop('selectedIndex', 0).trigger("liszt:updated"); // Reset chosen.js
form.submit(); form.submit();
} }
}) });
/** /**
* Allows to lazy load a panel, by leaving it empty * Allows to lazy load a panel, by leaving it empty
@ -1401,6 +1401,46 @@ jQuery.noConflict();
}); });
} }
}); });
/**
* CMS content filters
*/
$('#filters-button').entwine({
onmatch: function () {
this._super();
this.data('collapsed', true); // The current collapsed state of the element.
this.data('animating', false); // True if the element is currently animating.
},
onunmatch: function () {
this._super();
},
showHide: function () {
var self = this,
$filters = $('.cms-content-filters').first(),
collapsed = this.data('collapsed');
// Prevent the user from spamming the UI with animation requests.
if (this.data('animating')) {
return;
}
this.toggleClass('active');
this.data('animating', true);
// Slide the element down / up based on it's current collapsed state.
$filters[collapsed ? 'slideDown' : 'slideUp']({
complete: function () {
// Update the element's state.
self.data('collapsed', !collapsed);
self.data('animating', false);
}
});
},
onclick: function () {
this.showHide();
}
});
}); });
}(jQuery)); }(jQuery));

View File

@ -1,4 +1,10 @@
{ {
"CMSMAIN.SELECTONEPAGE": "Please select at least one page",
"CMSMAIN.BATCH_UNPUBLISH_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to unpublish",
"CMSMAIN.BATCH_PUBLISH_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to publish?",
"CMSMAIN.BATCH_DELETE_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to delete?",
"CMSMAIN.BATCH_ARCHIVE_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to archive?\n\nThese pages will be removed from both the draft and published sites without discarding the history.",
"CMSMAIN.BATCH_DELETELIVE_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to delete these pages from live?",
"LeftAndMain.CONFIRMUNSAVED": "Určitě chcete opustit navigaci z této stránky?\n\nUPOZORNĚNÍ: Vaše změny nebyly uloženy.\n\nStlačte OK pro pokračovat, nebo Cancel, zůstanete na této stránce.", "LeftAndMain.CONFIRMUNSAVED": "Určitě chcete opustit navigaci z této stránky?\n\nUPOZORNĚNÍ: Vaše změny nebyly uloženy.\n\nStlačte OK pro pokračovat, nebo Cancel, zůstanete na této stránce.",
"LeftAndMain.CONFIRMUNSAVEDSHORT": "UPOZORNĚNÍ: Vaše změny nebyly uloženy.", "LeftAndMain.CONFIRMUNSAVEDSHORT": "UPOZORNĚNÍ: Vaše změny nebyly uloženy.",
"SecurityAdmin.BATCHACTIONSDELETECONFIRM": "Skutečně chcete smazat %s skupiny?", "SecurityAdmin.BATCHACTIONSDELETECONFIRM": "Skutečně chcete smazat %s skupiny?",

View File

@ -1,4 +1,10 @@
{ {
"CMSMAIN.SELECTONEPAGE": "Please select at least one page",
"CMSMAIN.BATCH_UNPUBLISH_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to unpublish",
"CMSMAIN.BATCH_PUBLISH_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to publish?",
"CMSMAIN.BATCH_DELETE_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to delete?",
"CMSMAIN.BATCH_ARCHIVE_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to archive?\n\nThese pages will be removed from both the draft and published sites without discarding the history.",
"CMSMAIN.BATCH_DELETELIVE_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to delete these pages from live?",
"LeftAndMain.CONFIRMUNSAVED": "Sind Sie sicher, dass Sie die Seite verlassen möchten?\n\nWARNUNG: Ihre Änderungen werden nicht gespeichert.\n\nDrücken Sie \"OK\" um fortzufahren, oder \"Abbrechen\" um auf dieser Seite zu bleiben.", "LeftAndMain.CONFIRMUNSAVED": "Sind Sie sicher, dass Sie die Seite verlassen möchten?\n\nWARNUNG: Ihre Änderungen werden nicht gespeichert.\n\nDrücken Sie \"OK\" um fortzufahren, oder \"Abbrechen\" um auf dieser Seite zu bleiben.",
"LeftAndMain.CONFIRMUNSAVEDSHORT": "WARNUNG: Ihre Änderungen wurden nicht gespeichert.", "LeftAndMain.CONFIRMUNSAVEDSHORT": "WARNUNG: Ihre Änderungen wurden nicht gespeichert.",
"SecurityAdmin.BATCHACTIONSDELETECONFIRM": "Möchten Sie wirklich %s Gruppen löschen?", "SecurityAdmin.BATCHACTIONSDELETECONFIRM": "Möchten Sie wirklich %s Gruppen löschen?",

View File

@ -1,4 +1,10 @@
{ {
"CMSMAIN.SELECTONEPAGE": "Please select at least one page",
"CMSMAIN.BATCH_UNPUBLISH_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to unpublish",
"CMSMAIN.BATCH_PUBLISH_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to publish?",
"CMSMAIN.BATCH_DELETE_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to delete?",
"CMSMAIN.BATCH_ARCHIVE_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to archive?\n\nThese pages will be removed from both the draft and published sites without discarding the history.",
"CMSMAIN.BATCH_DELETELIVE_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to delete these pages from live?",
"LeftAndMain.CONFIRMUNSAVED": "Ĉu vi vere volas navigi for de ĉi tiu paĝo?\n\nAVERTO: Viaj ŝanĝoj ne estas konservitaj.\n\nPremu je Akcepti por daŭrigi, aŭ Nuligi por resti ĉe la aktuala paĝo.", "LeftAndMain.CONFIRMUNSAVED": "Ĉu vi vere volas navigi for de ĉi tiu paĝo?\n\nAVERTO: Viaj ŝanĝoj ne estas konservitaj.\n\nPremu je Akcepti por daŭrigi, aŭ Nuligi por resti ĉe la aktuala paĝo.",
"LeftAndMain.CONFIRMUNSAVEDSHORT": "AVERTO: Viaj ŝanĝoj ne estas konservitaj.", "LeftAndMain.CONFIRMUNSAVEDSHORT": "AVERTO: Viaj ŝanĝoj ne estas konservitaj.",
"SecurityAdmin.BATCHACTIONSDELETECONFIRM": "Ĉu vi vere volas forigi %s grupojn?", "SecurityAdmin.BATCHACTIONSDELETECONFIRM": "Ĉu vi vere volas forigi %s grupojn?",

View File

@ -1,4 +1,10 @@
{ {
"CMSMAIN.SELECTONEPAGE": "Please select at least one page",
"CMSMAIN.BATCH_UNPUBLISH_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to unpublish",
"CMSMAIN.BATCH_PUBLISH_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to publish?",
"CMSMAIN.BATCH_DELETE_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to delete?",
"CMSMAIN.BATCH_ARCHIVE_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to archive?\n\nThese pages will be removed from both the draft and published sites without discarding the history.",
"CMSMAIN.BATCH_DELETELIVE_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to delete these pages from live?",
"LeftAndMain.CONFIRMUNSAVED": "¿Estás seguro que quieres navegar fuera de esta página?⏎\n⏎\nADVERTENCIA: Tus cambios no han sido guardados.⏎\n⏎\nPresionar OK para continuar o Cancelar para continuar en la página actual", "LeftAndMain.CONFIRMUNSAVED": "¿Estás seguro que quieres navegar fuera de esta página?⏎\n⏎\nADVERTENCIA: Tus cambios no han sido guardados.⏎\n⏎\nPresionar OK para continuar o Cancelar para continuar en la página actual",
"LeftAndMain.CONFIRMUNSAVEDSHORT": "ADVERTENCIA: Tus cambios no han sido guardados.", "LeftAndMain.CONFIRMUNSAVEDSHORT": "ADVERTENCIA: Tus cambios no han sido guardados.",
"SecurityAdmin.BATCHACTIONSDELETECONFIRM": "¿Realmente quieres eliminar el grupo %s?", "SecurityAdmin.BATCHACTIONSDELETECONFIRM": "¿Realmente quieres eliminar el grupo %s?",

View File

@ -1,4 +1,10 @@
{ {
"CMSMAIN.SELECTONEPAGE": "Please select at least one page",
"CMSMAIN.BATCH_UNPUBLISH_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to unpublish",
"CMSMAIN.BATCH_PUBLISH_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to publish?",
"CMSMAIN.BATCH_DELETE_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to delete?",
"CMSMAIN.BATCH_ARCHIVE_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to archive?\n\nThese pages will be removed from both the draft and published sites without discarding the history.",
"CMSMAIN.BATCH_DELETELIVE_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to delete these pages from live?",
"LeftAndMain.CONFIRMUNSAVED": "Haluatko varmasti poistua tältä sivulta?\n\nVAROITUS: Muutoksiasi ei ole tallennettu.\n\nPaina OK jatkaaksesi, tai Peruuta pysyäksesi nykyisellä sivulla.", "LeftAndMain.CONFIRMUNSAVED": "Haluatko varmasti poistua tältä sivulta?\n\nVAROITUS: Muutoksiasi ei ole tallennettu.\n\nPaina OK jatkaaksesi, tai Peruuta pysyäksesi nykyisellä sivulla.",
"LeftAndMain.CONFIRMUNSAVEDSHORT": "VAROITUS: Muutoksiasi ei ole tallennettu.", "LeftAndMain.CONFIRMUNSAVEDSHORT": "VAROITUS: Muutoksiasi ei ole tallennettu.",
"SecurityAdmin.BATCHACTIONSDELETECONFIRM": "Haluatko varmasti poistaa %s ryhmät?", "SecurityAdmin.BATCHACTIONSDELETECONFIRM": "Haluatko varmasti poistaa %s ryhmät?",

View File

@ -1,4 +1,10 @@
{ {
"CMSMAIN.SELECTONEPAGE": "Please select at least one page",
"CMSMAIN.BATCH_UNPUBLISH_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to unpublish",
"CMSMAIN.BATCH_PUBLISH_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to publish?",
"CMSMAIN.BATCH_DELETE_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to delete?",
"CMSMAIN.BATCH_ARCHIVE_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to archive?\n\nThese pages will be removed from both the draft and published sites without discarding the history.",
"CMSMAIN.BATCH_DELETELIVE_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to delete these pages from live?",
"LeftAndMain.CONFIRMUNSAVED": "Etes-vous sûr de vouloir quitter cette page ?\n\nATTENTION: Vos changements n'ont pas été sauvegardés.\n\nCliquez sur OK pour continuer, ou sur Annuler pour rester sur la page actuelle.", "LeftAndMain.CONFIRMUNSAVED": "Etes-vous sûr de vouloir quitter cette page ?\n\nATTENTION: Vos changements n'ont pas été sauvegardés.\n\nCliquez sur OK pour continuer, ou sur Annuler pour rester sur la page actuelle.",
"LeftAndMain.CONFIRMUNSAVEDSHORT": "WARNING: Your changes have not been saved.", "LeftAndMain.CONFIRMUNSAVEDSHORT": "WARNING: Your changes have not been saved.",
"SecurityAdmin.BATCHACTIONSDELETECONFIRM": "Do you really want to delete %s groups?", "SecurityAdmin.BATCHACTIONSDELETECONFIRM": "Do you really want to delete %s groups?",

View File

@ -1,4 +1,10 @@
{ {
"CMSMAIN.SELECTONEPAGE": "Please select at least one page",
"CMSMAIN.BATCH_UNPUBLISH_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to unpublish",
"CMSMAIN.BATCH_PUBLISH_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to publish?",
"CMSMAIN.BATCH_DELETE_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to delete?",
"CMSMAIN.BATCH_ARCHIVE_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to archive?\n\nThese pages will be removed from both the draft and published sites without discarding the history.",
"CMSMAIN.BATCH_DELETELIVE_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to delete these pages from live?",
"LeftAndMain.CONFIRMUNSAVED": "Anda ingin tinggalkan laman ini?\n\nPERINGATAN: Perubahan tidak akan disimpan.\n\nTekan OK untuk lanjut, atau Batal untuk tetap di laman ini.", "LeftAndMain.CONFIRMUNSAVED": "Anda ingin tinggalkan laman ini?\n\nPERINGATAN: Perubahan tidak akan disimpan.\n\nTekan OK untuk lanjut, atau Batal untuk tetap di laman ini.",
"LeftAndMain.CONFIRMUNSAVEDSHORT": "PERINGATAN: Perubahan tidak akan disimpan.", "LeftAndMain.CONFIRMUNSAVEDSHORT": "PERINGATAN: Perubahan tidak akan disimpan.",
"SecurityAdmin.BATCHACTIONSDELETECONFIRM": "Anda ingin menghapus kelompok %s?", "SecurityAdmin.BATCHACTIONSDELETECONFIRM": "Anda ingin menghapus kelompok %s?",

View File

@ -1,4 +1,10 @@
{ {
"CMSMAIN.SELECTONEPAGE": "Please select at least one page",
"CMSMAIN.BATCH_UNPUBLISH_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to unpublish",
"CMSMAIN.BATCH_PUBLISH_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to publish?",
"CMSMAIN.BATCH_DELETE_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to delete?",
"CMSMAIN.BATCH_ARCHIVE_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to archive?\n\nThese pages will be removed from both the draft and published sites without discarding the history.",
"CMSMAIN.BATCH_DELETELIVE_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to delete these pages from live?",
"LeftAndMain.CONFIRMUNSAVED": "Anda ingin tinggalkan laman ini?\n\nPERINGATAN: Perubahan tidak akan disimpan.\n\nTekan OK untuk lanjut, atau Batal untuk tetap di laman ini.", "LeftAndMain.CONFIRMUNSAVED": "Anda ingin tinggalkan laman ini?\n\nPERINGATAN: Perubahan tidak akan disimpan.\n\nTekan OK untuk lanjut, atau Batal untuk tetap di laman ini.",
"LeftAndMain.CONFIRMUNSAVEDSHORT": "PERINGATAN: Perubahan tidak akan disimpan.", "LeftAndMain.CONFIRMUNSAVEDSHORT": "PERINGATAN: Perubahan tidak akan disimpan.",
"SecurityAdmin.BATCHACTIONSDELETECONFIRM": "Anda ingin menghapus kelompok %s?", "SecurityAdmin.BATCHACTIONSDELETECONFIRM": "Anda ingin menghapus kelompok %s?",

View File

@ -1,4 +1,10 @@
{ {
"CMSMAIN.SELECTONEPAGE": "Please select at least one page",
"CMSMAIN.BATCH_UNPUBLISH_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to unpublish",
"CMSMAIN.BATCH_PUBLISH_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to publish?",
"CMSMAIN.BATCH_DELETE_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to delete?",
"CMSMAIN.BATCH_ARCHIVE_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to archive?\n\nThese pages will be removed from both the draft and published sites without discarding the history.",
"CMSMAIN.BATCH_DELETELIVE_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to delete these pages from live?",
"LeftAndMain.CONFIRMUNSAVED": "Siete sicuri di voler uscire da questa pagina?\n\nATTENZIONE: I vostri cambiamenti non sono stati salvati.\n\nCliccare OK per continuare, o su Annulla per rimanere sulla pagina corrente.", "LeftAndMain.CONFIRMUNSAVED": "Siete sicuri di voler uscire da questa pagina?\n\nATTENZIONE: I vostri cambiamenti non sono stati salvati.\n\nCliccare OK per continuare, o su Annulla per rimanere sulla pagina corrente.",
"LeftAndMain.CONFIRMUNSAVEDSHORT": "WARNING: Your changes have not been saved.", "LeftAndMain.CONFIRMUNSAVEDSHORT": "WARNING: Your changes have not been saved.",
"SecurityAdmin.BATCHACTIONSDELETECONFIRM": "Do you really want to delete %s groups?", "SecurityAdmin.BATCHACTIONSDELETECONFIRM": "Do you really want to delete %s groups?",

View File

@ -1,4 +1,10 @@
{ {
"CMSMAIN.SELECTONEPAGE": "Please select at least one page",
"CMSMAIN.BATCH_UNPUBLISH_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to unpublish",
"CMSMAIN.BATCH_PUBLISH_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to publish?",
"CMSMAIN.BATCH_DELETE_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to delete?",
"CMSMAIN.BATCH_ARCHIVE_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to archive?\n\nThese pages will be removed from both the draft and published sites without discarding the history.",
"CMSMAIN.BATCH_DELETELIVE_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to delete these pages from live?",
"LeftAndMain.CONFIRMUNSAVED": "このページから移動しても良いですか?\n\n警告: あなたの変更は保存されていません.\n\n続行するにはOKを押してくださいキャンセルをクリックするとこのページにとどまります", "LeftAndMain.CONFIRMUNSAVED": "このページから移動しても良いですか?\n\n警告: あなたの変更は保存されていません.\n\n続行するにはOKを押してくださいキャンセルをクリックするとこのページにとどまります",
"LeftAndMain.CONFIRMUNSAVEDSHORT": "警告: あなたの変更は保存されていません.", "LeftAndMain.CONFIRMUNSAVEDSHORT": "警告: あなたの変更は保存されていません.",
"SecurityAdmin.BATCHACTIONSDELETECONFIRM": "%sグループを本当に削除しても良いですか?", "SecurityAdmin.BATCHACTIONSDELETECONFIRM": "%sグループを本当に削除しても良いですか?",

View File

@ -1,4 +1,10 @@
{ {
"CMSMAIN.SELECTONEPAGE": "Please select at least one page",
"CMSMAIN.BATCH_UNPUBLISH_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to unpublish",
"CMSMAIN.BATCH_PUBLISH_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to publish?",
"CMSMAIN.BATCH_DELETE_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to delete?",
"CMSMAIN.BATCH_ARCHIVE_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to archive?\n\nThese pages will be removed from both the draft and published sites without discarding the history.",
"CMSMAIN.BATCH_DELETELIVE_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to delete these pages from live?",
"LeftAndMain.CONFIRMUNSAVED": "Ar tikrai norite išeiti iš šio puslapio?\n\nDĖMESIO: Jūsų pakeitimai neišsaugoti.\n\nNorėdami tęsti, spauskite OK, jeigu norite likti, spauskite Cancel.", "LeftAndMain.CONFIRMUNSAVED": "Ar tikrai norite išeiti iš šio puslapio?\n\nDĖMESIO: Jūsų pakeitimai neišsaugoti.\n\nNorėdami tęsti, spauskite OK, jeigu norite likti, spauskite Cancel.",
"LeftAndMain.CONFIRMUNSAVEDSHORT": "DĖMESIO: Jūsų pakeitimai neišsaugoti.", "LeftAndMain.CONFIRMUNSAVEDSHORT": "DĖMESIO: Jūsų pakeitimai neišsaugoti.",
"SecurityAdmin.BATCHACTIONSDELETECONFIRM": "Ar tikrai norite ištrinti %s grupes?", "SecurityAdmin.BATCHACTIONSDELETECONFIRM": "Ar tikrai norite ištrinti %s grupes?",

View File

@ -1,4 +1,10 @@
{ {
"CMSMAIN.SELECTONEPAGE": "Please select at least one page",
"CMSMAIN.BATCH_UNPUBLISH_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to unpublish",
"CMSMAIN.BATCH_PUBLISH_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to publish?",
"CMSMAIN.BATCH_DELETE_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to delete?",
"CMSMAIN.BATCH_ARCHIVE_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to archive?\n\nThese pages will be removed from both the draft and published sites without discarding the history.",
"CMSMAIN.BATCH_DELETELIVE_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to delete these pages from live?",
"LeftAndMain.CONFIRMUNSAVED": "Kei te hiahia whakatere atu i tēnei whārangi?\n\nWHAKATŪPATO: Kāore anō ō huringa kia tiakina.\n\nPēhi AE kia haere tonu, Whakakore rānei kia noho i te whārangi onāianei.", "LeftAndMain.CONFIRMUNSAVED": "Kei te hiahia whakatere atu i tēnei whārangi?\n\nWHAKATŪPATO: Kāore anō ō huringa kia tiakina.\n\nPēhi AE kia haere tonu, Whakakore rānei kia noho i te whārangi onāianei.",
"LeftAndMain.CONFIRMUNSAVEDSHORT": "WHAKATŪPATO: Kāore anō ō huringa kia tiakina.", "LeftAndMain.CONFIRMUNSAVEDSHORT": "WHAKATŪPATO: Kāore anō ō huringa kia tiakina.",
"SecurityAdmin.BATCHACTIONSDELETECONFIRM": "Kei te tino hiahia muku i te %s rōpū?", "SecurityAdmin.BATCHACTIONSDELETECONFIRM": "Kei te tino hiahia muku i te %s rōpū?",

View File

@ -1,4 +1,10 @@
{ {
"CMSMAIN.SELECTONEPAGE": "Please select at least one page",
"CMSMAIN.BATCH_UNPUBLISH_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to unpublish",
"CMSMAIN.BATCH_PUBLISH_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to publish?",
"CMSMAIN.BATCH_DELETE_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to delete?",
"CMSMAIN.BATCH_ARCHIVE_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to archive?\n\nThese pages will be removed from both the draft and published sites without discarding the history.",
"CMSMAIN.BATCH_DELETELIVE_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to delete these pages from live?",
"LeftAndMain.CONFIRMUNSAVED": "Er du sikker på at du vil forlate denne siden?\n\nADVARSEL: Endringene din har ikke blitt lagret.\n\nTrykk OK for å fortsette eller Avbryt for å holde deg på samme side.", "LeftAndMain.CONFIRMUNSAVED": "Er du sikker på at du vil forlate denne siden?\n\nADVARSEL: Endringene din har ikke blitt lagret.\n\nTrykk OK for å fortsette eller Avbryt for å holde deg på samme side.",
"LeftAndMain.CONFIRMUNSAVEDSHORT": "ADVARSEL: Endringene dine har ikke blitt lagret.", "LeftAndMain.CONFIRMUNSAVEDSHORT": "ADVARSEL: Endringene dine har ikke blitt lagret.",
"SecurityAdmin.BATCHACTIONSDELETECONFIRM": "Vil du virkelig slette %s grupper?", "SecurityAdmin.BATCHACTIONSDELETECONFIRM": "Vil du virkelig slette %s grupper?",

View File

@ -1,4 +1,10 @@
{ {
"CMSMAIN.SELECTONEPAGE": "Please select at least one page",
"CMSMAIN.BATCH_UNPUBLISH_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to unpublish",
"CMSMAIN.BATCH_PUBLISH_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to publish?",
"CMSMAIN.BATCH_DELETE_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to delete?",
"CMSMAIN.BATCH_ARCHIVE_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to archive?\n\nThese pages will be removed from both the draft and published sites without discarding the history.",
"CMSMAIN.BATCH_DELETELIVE_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to delete these pages from live?",
"LeftAndMain.CONFIRMUNSAVED": "Weet u zeker dat u deze pagina wilt verlaten?\nWAARSCHUWING: Uw veranderingen zijn niet opgeslagen.\n\nKies OK om te verlaten, of Cancel om op de huidige pagina te blijven.", "LeftAndMain.CONFIRMUNSAVED": "Weet u zeker dat u deze pagina wilt verlaten?\nWAARSCHUWING: Uw veranderingen zijn niet opgeslagen.\n\nKies OK om te verlaten, of Cancel om op de huidige pagina te blijven.",
"LeftAndMain.CONFIRMUNSAVEDSHORT": "WAARSCHUWING: Uw veranderingen zijn niet opgeslagen", "LeftAndMain.CONFIRMUNSAVEDSHORT": "WAARSCHUWING: Uw veranderingen zijn niet opgeslagen",
"SecurityAdmin.BATCHACTIONSDELETECONFIRM": "Weet u zeker dat u deze groep %s wilt verwijderen?", "SecurityAdmin.BATCHACTIONSDELETECONFIRM": "Weet u zeker dat u deze groep %s wilt verwijderen?",

View File

@ -1,4 +1,10 @@
{ {
"CMSMAIN.SELECTONEPAGE": "Please select at least one page",
"CMSMAIN.BATCH_UNPUBLISH_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to unpublish",
"CMSMAIN.BATCH_PUBLISH_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to publish?",
"CMSMAIN.BATCH_DELETE_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to delete?",
"CMSMAIN.BATCH_ARCHIVE_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to archive?\n\nThese pages will be removed from both the draft and published sites without discarding the history.",
"CMSMAIN.BATCH_DELETELIVE_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to delete these pages from live?",
"LeftAndMain.CONFIRMUNSAVED": "Czy na pewno chcesz kontynuować nawigację poza tą stronę?\n\nUWAGA: Twoje zmiany nie zostały zapisane.\n\nWciśnij OK aby kontynuować, wciśnij Anuluj aby pozostać na tej stronie.", "LeftAndMain.CONFIRMUNSAVED": "Czy na pewno chcesz kontynuować nawigację poza tą stronę?\n\nUWAGA: Twoje zmiany nie zostały zapisane.\n\nWciśnij OK aby kontynuować, wciśnij Anuluj aby pozostać na tej stronie.",
"LeftAndMain.CONFIRMUNSAVEDSHORT": "UWAGA: Twoje zmiany nie zostały zapisane.", "LeftAndMain.CONFIRMUNSAVEDSHORT": "UWAGA: Twoje zmiany nie zostały zapisane.",
"SecurityAdmin.BATCHACTIONSDELETECONFIRM": "Czy na pewno chcesz usunąć %s grup?", "SecurityAdmin.BATCHACTIONSDELETECONFIRM": "Czy na pewno chcesz usunąć %s grup?",

View File

@ -1,4 +1,10 @@
{ {
"CMSMAIN.SELECTONEPAGE": "Please select at least one page",
"CMSMAIN.BATCH_UNPUBLISH_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to unpublish",
"CMSMAIN.BATCH_PUBLISH_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to publish?",
"CMSMAIN.BATCH_DELETE_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to delete?",
"CMSMAIN.BATCH_ARCHIVE_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to archive?\n\nThese pages will be removed from both the draft and published sites without discarding the history.",
"CMSMAIN.BATCH_DELETELIVE_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to delete these pages from live?",
"LeftAndMain.CONFIRMUNSAVED": "Sunteți sigur că doriți să părăsiți pagina?\n\nAVERTISMENT: Modificările nu au fost salvate.\n\nApăsați OK pentru a continua, sau Anulați pentru a rămâne pe pagina curentă.", "LeftAndMain.CONFIRMUNSAVED": "Sunteți sigur că doriți să părăsiți pagina?\n\nAVERTISMENT: Modificările nu au fost salvate.\n\nApăsați OK pentru a continua, sau Anulați pentru a rămâne pe pagina curentă.",
"LeftAndMain.CONFIRMUNSAVEDSHORT": "AVERTISMENT: Modificările nu au fost salvate.", "LeftAndMain.CONFIRMUNSAVEDSHORT": "AVERTISMENT: Modificările nu au fost salvate.",
"SecurityAdmin.BATCHACTIONSDELETECONFIRM": "Sigur doriți să ștergeți grupurile %s?", "SecurityAdmin.BATCHACTIONSDELETECONFIRM": "Sigur doriți să ștergeți grupurile %s?",

View File

@ -1,4 +1,10 @@
{ {
"CMSMAIN.SELECTONEPAGE": "Please select at least one page",
"CMSMAIN.BATCH_UNPUBLISH_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to unpublish",
"CMSMAIN.BATCH_PUBLISH_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to publish?",
"CMSMAIN.BATCH_DELETE_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to delete?",
"CMSMAIN.BATCH_ARCHIVE_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to archive?\n\nThese pages will be removed from both the draft and published sites without discarding the history.",
"CMSMAIN.BATCH_DELETELIVE_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to delete these pages from live?",
"LeftAndMain.CONFIRMUNSAVED": "Вы действительно хотите покинуть эту страницу?\n\nВНИМАНИЕ: Ваши изменения не были сохранены.\n\nНажмите ОК, чтобы продолжить или Отмена, чтобы остаться на текущей странице.", "LeftAndMain.CONFIRMUNSAVED": "Вы действительно хотите покинуть эту страницу?\n\nВНИМАНИЕ: Ваши изменения не были сохранены.\n\nНажмите ОК, чтобы продолжить или Отмена, чтобы остаться на текущей странице.",
"LeftAndMain.CONFIRMUNSAVEDSHORT": "ВНИМАНИЕ: Ваши изменения не были сохранены", "LeftAndMain.CONFIRMUNSAVEDSHORT": "ВНИМАНИЕ: Ваши изменения не были сохранены",
"SecurityAdmin.BATCHACTIONSDELETECONFIRM": "Вы действительно хотите удалить %s групп?", "SecurityAdmin.BATCHACTIONSDELETECONFIRM": "Вы действительно хотите удалить %s групп?",

View File

@ -1,4 +1,10 @@
{ {
"CMSMAIN.SELECTONEPAGE": "Please select at least one page",
"CMSMAIN.BATCH_UNPUBLISH_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to unpublish",
"CMSMAIN.BATCH_PUBLISH_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to publish?",
"CMSMAIN.BATCH_DELETE_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to delete?",
"CMSMAIN.BATCH_ARCHIVE_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to archive?\n\nThese pages will be removed from both the draft and published sites without discarding the history.",
"CMSMAIN.BATCH_DELETELIVE_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to delete these pages from live?",
"LeftAndMain.CONFIRMUNSAVED": "Určite chcete opustiť navigáciu z tejto stránky?\n\nUPOZORNENIE: Vaše zmeny neboli uložené.\n\nStlačte OK pre pokračovať, alebo Cancel, ostanete na teto stránke.", "LeftAndMain.CONFIRMUNSAVED": "Určite chcete opustiť navigáciu z tejto stránky?\n\nUPOZORNENIE: Vaše zmeny neboli uložené.\n\nStlačte OK pre pokračovať, alebo Cancel, ostanete na teto stránke.",
"LeftAndMain.CONFIRMUNSAVEDSHORT": "UPOZORNENIE: Vaše zmeny neboli uložené.", "LeftAndMain.CONFIRMUNSAVEDSHORT": "UPOZORNENIE: Vaše zmeny neboli uložené.",
"SecurityAdmin.BATCHACTIONSDELETECONFIRM": "Skutočne chcete zmazať % skupiny?", "SecurityAdmin.BATCHACTIONSDELETECONFIRM": "Skutočne chcete zmazať % skupiny?",

View File

@ -1,4 +1,10 @@
{ {
"CMSMAIN.SELECTONEPAGE": "Please select at least one page",
"CMSMAIN.BATCH_UNPUBLISH_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to unpublish",
"CMSMAIN.BATCH_PUBLISH_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to publish?",
"CMSMAIN.BATCH_DELETE_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to delete?",
"CMSMAIN.BATCH_ARCHIVE_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to archive?\n\nThese pages will be removed from both the draft and published sites without discarding the history.",
"CMSMAIN.BATCH_DELETELIVE_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to delete these pages from live?",
"LeftAndMain.CONFIRMUNSAVED": "Res želite zapusitit stran?\n\nOPOZORILO: spremembe niso bile shranjene\n\nKliknite OK za nadaljevanje ali Prekliči, da ostanete na trenutni strani.", "LeftAndMain.CONFIRMUNSAVED": "Res želite zapusitit stran?\n\nOPOZORILO: spremembe niso bile shranjene\n\nKliknite OK za nadaljevanje ali Prekliči, da ostanete na trenutni strani.",
"LeftAndMain.CONFIRMUNSAVEDSHORT": "OPOZORILO: spremembe niso bile shranjene.", "LeftAndMain.CONFIRMUNSAVEDSHORT": "OPOZORILO: spremembe niso bile shranjene.",
"SecurityAdmin.BATCHACTIONSDELETECONFIRM": "Izbrišem %s skupin?", "SecurityAdmin.BATCHACTIONSDELETECONFIRM": "Izbrišem %s skupin?",

View File

@ -1,4 +1,10 @@
{ {
"CMSMAIN.SELECTONEPAGE": "Please select at least one page",
"CMSMAIN.BATCH_UNPUBLISH_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to unpublish",
"CMSMAIN.BATCH_PUBLISH_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to publish?",
"CMSMAIN.BATCH_DELETE_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to delete?",
"CMSMAIN.BATCH_ARCHIVE_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to archive?\n\nThese pages will be removed from both the draft and published sites without discarding the history.",
"CMSMAIN.BATCH_DELETELIVE_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to delete these pages from live?",
"LeftAndMain.CONFIRMUNSAVED": "Да ли сте сигурни да желите да одете са ове странице?\n\nУПОЗОРЕЊЕ: Ваше измене још нису сачуване.\n\nПритисните У реду за наставак или Одустани да би сте остали на овој страници.", "LeftAndMain.CONFIRMUNSAVED": "Да ли сте сигурни да желите да одете са ове странице?\n\nУПОЗОРЕЊЕ: Ваше измене још нису сачуване.\n\nПритисните У реду за наставак или Одустани да би сте остали на овој страници.",
"LeftAndMain.CONFIRMUNSAVEDSHORT": "УПОЗОРЕЊЕ: Ваше измене нису сачуване.", "LeftAndMain.CONFIRMUNSAVEDSHORT": "УПОЗОРЕЊЕ: Ваше измене нису сачуване.",
"SecurityAdmin.BATCHACTIONSDELETECONFIRM": "Да ли заиста желите да се избришете %s групе?", "SecurityAdmin.BATCHACTIONSDELETECONFIRM": "Да ли заиста желите да се избришете %s групе?",

View File

@ -1,4 +1,10 @@
{ {
"CMSMAIN.SELECTONEPAGE": "Please select at least one page",
"CMSMAIN.BATCH_UNPUBLISH_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to unpublish",
"CMSMAIN.BATCH_PUBLISH_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to publish?",
"CMSMAIN.BATCH_DELETE_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to delete?",
"CMSMAIN.BATCH_ARCHIVE_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to archive?\n\nThese pages will be removed from both the draft and published sites without discarding the history.",
"CMSMAIN.BATCH_DELETELIVE_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to delete these pages from live?",
"LeftAndMain.CONFIRMUNSAVED": "Да ли сте сигурни да желите да одете са ове странице?\n\nУПОЗОРЕЊЕ: Ваше измене још нису сачуване.\n\nПритисните У реду за наставак или Одустани да би сте остали на овој страници.", "LeftAndMain.CONFIRMUNSAVED": "Да ли сте сигурни да желите да одете са ове странице?\n\nУПОЗОРЕЊЕ: Ваше измене још нису сачуване.\n\nПритисните У реду за наставак или Одустани да би сте остали на овој страници.",
"LeftAndMain.CONFIRMUNSAVEDSHORT": "УПОЗОРЕЊЕ: Ваше измене нису сачуване.", "LeftAndMain.CONFIRMUNSAVEDSHORT": "УПОЗОРЕЊЕ: Ваше измене нису сачуване.",
"SecurityAdmin.BATCHACTIONSDELETECONFIRM": "Да ли заиста желите да се избришете %s групе?", "SecurityAdmin.BATCHACTIONSDELETECONFIRM": "Да ли заиста желите да се избришете %s групе?",

View File

@ -1,4 +1,10 @@
{ {
"CMSMAIN.SELECTONEPAGE": "Please select at least one page",
"CMSMAIN.BATCH_UNPUBLISH_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to unpublish",
"CMSMAIN.BATCH_PUBLISH_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to publish?",
"CMSMAIN.BATCH_DELETE_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to delete?",
"CMSMAIN.BATCH_ARCHIVE_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to archive?\n\nThese pages will be removed from both the draft and published sites without discarding the history.",
"CMSMAIN.BATCH_DELETELIVE_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to delete these pages from live?",
"LeftAndMain.CONFIRMUNSAVED": "Är du säker på att du vill lämna denna sida?\n\nVARNING: Dina ändringar har inte sparats.\n\nTryck OK för att lämna sidan eller Avbryt för att stanna på aktuell sida.", "LeftAndMain.CONFIRMUNSAVED": "Är du säker på att du vill lämna denna sida?\n\nVARNING: Dina ändringar har inte sparats.\n\nTryck OK för att lämna sidan eller Avbryt för att stanna på aktuell sida.",
"LeftAndMain.CONFIRMUNSAVEDSHORT": "WARNING: Your changes have not been saved.", "LeftAndMain.CONFIRMUNSAVEDSHORT": "WARNING: Your changes have not been saved.",
"SecurityAdmin.BATCHACTIONSDELETECONFIRM": "Vill du verkligen radera %s grupper?", "SecurityAdmin.BATCHACTIONSDELETECONFIRM": "Vill du verkligen radera %s grupper?",

View File

@ -1,4 +1,10 @@
{ {
"CMSMAIN.SELECTONEPAGE": "Please select at least one page",
"CMSMAIN.BATCH_UNPUBLISH_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to unpublish",
"CMSMAIN.BATCH_PUBLISH_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to publish?",
"CMSMAIN.BATCH_DELETE_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to delete?",
"CMSMAIN.BATCH_ARCHIVE_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to archive?\n\nThese pages will be removed from both the draft and published sites without discarding the history.",
"CMSMAIN.BATCH_DELETELIVE_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to delete these pages from live?",
"LeftAndMain.CONFIRMUNSAVED": "您确定要离开此页面?\n警告您所做的更改尚未保存。\n请按“确定”继续或“取消”留在当前页面。\n", "LeftAndMain.CONFIRMUNSAVED": "您确定要离开此页面?\n警告您所做的更改尚未保存。\n请按“确定”继续或“取消”留在当前页面。\n",
"LeftAndMain.CONFIRMUNSAVEDSHORT": "警告:您所做的更改尚未保存。", "LeftAndMain.CONFIRMUNSAVEDSHORT": "警告:您所做的更改尚未保存。",
"SecurityAdmin.BATCHACTIONSDELETECONFIRM": "您真的要删除 %s 小组吗?", "SecurityAdmin.BATCHACTIONSDELETECONFIRM": "您真的要删除 %s 小组吗?",

View File

@ -1,9 +1,16 @@
.ModelAdmin .cms-content-fields { .ModelAdmin .cms-content-fields {
overflow:hidden; //removes scrolling from filter panel
.cms-edit-form { .cms-edit-form {
overflow-y:auto; //adds scrolling only to the datagrid overflow-y:auto; //adds scrolling only to the datagrid
overflow-x:hidden; overflow-x:hidden;
} }
/**
* DEPRECATED:
* .cms-content-tools will be removed in 4.0
* Use .cms-content-filters instead.
*
* Hide certain elements when shown in "sidebar mode"
*/
.cms-content-tools .cms-panel-content { .cms-content-tools .cms-panel-content {
.cms-search-form { .cms-search-form {
.resetformaction { .resetformaction {
@ -14,4 +21,8 @@
overflow:hidden; overflow:hidden;
} }
} }
.cms-content-filters {
padding-top: 16px;
}
} }

View File

@ -125,7 +125,7 @@ $border: 1px solid darken(#D9D9D9, 15%);
font-size: 12px; font-size: 12px;
} }
#Form_AddForm_PageType_Holder ul { #Form_AddForm_PageType ul {
padding: 0; padding: 0;
li{ li{
@ -235,6 +235,9 @@ $border: 1px solid darken(#D9D9D9, 15%);
font-weight: normal; font-weight: normal;
line-height: 24px; line-height: 24px;
padding:0 25px 0 10px; padding:0 25px 0 10px;
border-bottom: 0;
margin: 0;
&:hover, &:active{ &:hover, &:active{
@include box-shadow(none); @include box-shadow(none);
outline:none; outline:none;

43
admin/scss/_fonts.scss Normal file
View File

@ -0,0 +1,43 @@
@font-face {
font-family: 'fontello';
src: url('../font/fontello.eot?77203683');
src: url('../font/fontello.eot?77203683#iefix') format('embedded-opentype'),
url('../font/fontello.woff?77203683') format('woff'),
url('../font/fontello.ttf?77203683') format('truetype'),
url('../font/fontello.svg?77203683#fontello') format('svg');
font-weight: normal;
font-style: normal;
}
[class^="font-icon-"],
[class*=" font-icon-"] {
&:before {
color: #66727d;
display: inline-block;
content: '';
width: 1em;
margin-top: 2px;
margin-right: .2em;
text-align: center;
text-decoration: inherit;
text-transform: none;
font-family: "fontello";
font-style: normal;
font-weight: normal;
font-variant: normal;
speak: none;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
}
.font-icon-search:before { content: '\e800'; }
.font-icon-tree:before { content: '\e801'; }
.font-icon-list2:before { content: '\e802'; }
.font-icon-cog:before { content: '\e803'; }
.font-icon-check:before { content: '\e804'; }
.font-icon-plus-solid:before { content: '\e805'; }
.font-icon-list:before { content: '\e806'; }
.font-icon-plus:before { content: '\e807'; }
.font-icon-search2:before { content: '\e808'; }
.font-icon-pencil:before { content: '\e80b'; }

View File

@ -911,3 +911,167 @@ fieldset.switch-states{
} }
//old web-kit browser fix //old web-kit browser fix
@-webkit-keyframes bugfix { from { position: relative; } to { position: relative; } } @-webkit-keyframes bugfix { from { position: relative; } to { position: relative; } }
//Styling for filter/search dropdown
.cms-content-filters {
fieldset {
margin-left: -16px;
margin-right: -16px;
}
.fieldgroup {
width: 50%;
display: inline-block;
max-width: 440px;
padding-right: 16px;
padding-left: 16px;
margin-bottom: 16px;
box-sizing: border-box;
margin-right: -2px;
vertical-align: top;
.first {
label, h1, h2, h3, h4, h5 {
display: block;
width: 176px;
padding: 8px 8px 6px 0;
line-height: 16px;
font-weight: bold;
margin: 0;
font-size: 100%;
}
}
.field {
width: 100%;
padding-right: 0;
padding-left: 0;
}
.fieldgroup-field {
position: relative;
margin-right: 0;
width: 48%;
display: inline-block;
padding: 0;
.description {
margin-top: 24px;
}
label {
position: absolute;
top: 28px;
font-style: italic;
color: #777;
font-weight: normal;
}
&.first {
width: 100%;
float: left;
}
&.last {
padding-right: 0;
float: right;
}
}
.fieldgroup {
margin: 0;
padding: 0;
}
}
.field {
border: none;
box-shadow: none;
width: 50%;
max-width: 440px;
display: inline-block;
margin: 0 0 8px 0;
padding-right: 16px;
padding-left: 16px;
padding-bottom: 0;
box-sizing: border-box;
margin-right: -2px;
vertical-align: top;
label.left {
text-shadow: none;
padding-bottom: 6px;
}
&.dropdown {
float: none;
display: inline-block;
}
.chzn-container {
width: 100% !important;
max-width: 100%;
}
input.text {
max-width: 100%;
}
&.checkbox {
display: block;
}
}
.importSpec {
margin-bottom: 8px;
padding-left: 16px;
}
.description {
margin-left: 0;
}
.middleColumn {
width: 100%;
margin-left: 0;
max-width: 100%;
}
.Actions {
margin: 8px 0 16px;
}
@media screen and (max-width:767px) {
fieldset {
.field,
.fieldgroup {
width: 100%;
max-width: 100%;
}
}
}
// Context specific rules for when the filters are in a panel
.cms-panel & {
.field,
.fieldgroup {
width: 100%;
margin-bottom: 16px;
}
.fieldgroup-field {
h4 {
padding-top: 0;
}
label {
position: static;
}
}
.Actions {
margin-bottom: 0;
}
}
}

View File

@ -117,7 +117,13 @@
} }
//fix for model admin filter styling /**
* DEPRECATED:
* .cms-content-tools will be removed in 4.0
* Use .cms-content-filters instead.
*
* Fix for model admin filter styling
*/
.ModelAdmin .cms-content-fields .cms-content-tools .cms-panel-content { .ModelAdmin .cms-content-fields .cms-content-tools .cms-panel-content {
#Form_ImportForm { #Form_ImportForm {
div.file { div.file {

View File

@ -33,13 +33,13 @@
.cms-logo { .cms-logo {
border-bottom: 1px solid lighten($color-dark-bg,4%); border-bottom: 1px solid lighten($color-dark-bg,4%);
overflow: hidden; overflow: hidden;
padding: $grid-y+2 0 $grid-y+1; /* should come to 40px with border bottom and line-height */ padding: $grid-y*1.5 0 $grid-y*1.5-1; /* should come to 52px with border bottom and line-height */
position: relative; position: relative;
vertical-align: middle; vertical-align: middle;
font-size: $font-base-size; font-size: $font-base-size;
min-height: 20px; min-height: 28px;
.collapsed &{ .collapsed & {
padding:0; padding:0;
} }
.version { .version {
@ -60,30 +60,37 @@
font-weight: bold; font-weight: bold;
font-size: $font-base-size; font-size: $font-base-size;
line-height: 16px; line-height: 16px;
padding: 2px 0; padding: 6px 0;
margin-left: 30px; margin-left: 30px;
} }
} }
.cms-login-status { .cms-login-status {
border-top: 1px solid $color-dark-separator; border-top: 1px solid $color-dark-separator;
padding: $grid-y*1 0 $grid-y*1.2; padding: $grid-y*1.5 0;
line-height: 16px; line-height: 16px;
font-size: $font-base-size - 1; font-size: $font-base-size - 1;
min-height: 28px;
.logout-link { .logout-link {
display: inline-block; display: inline-block;
height: 16px; height: 28px;
width: 16px; width: 16px;
float: left; float: left;
margin: 0 8px 0 5px; margin: 0 8px 0 5px;
background: sprite($sprites32, logout) no-repeat; background: sprite($sprites32, logout) no-repeat;
background-position: 0 -766px;
text-indent: -9999em; text-indent: -9999em;
opacity:0.9; opacity:0.9;
&:hover, &:focus{ &:hover, &:focus{
opacity:1; opacity:1;
} }
} }
span {
padding-top: 6px;
}
} }
.cms-menu { .cms-menu {
@ -134,15 +141,15 @@
} }
.cms-login-status { .cms-login-status {
height:16px; height: 28px;
span { span {
display:none; display: none;
} }
} }
.cms-logo { .cms-logo {
height:20px; height: 28px;
padding: 10px 0 9px; padding: 12px 0 11px;
} }
&.cms-panel .cms-panel-content { &.cms-panel .cms-panel-content {

View File

@ -71,7 +71,7 @@
&.loading { &.loading {
background-image: url(../images/spinner@2x.gif); background-image: url(../images/spinner@2x.gif);
background-size: 43px 22px; background-size: 43px 43px;
} }
} }

View File

@ -79,6 +79,11 @@ body.cms {
.cms-menu, .cms-menu,
.cms-content, .cms-content,
.cms-content-header, .cms-content-header,
/**
* DEPRECATED:
* .cms-content-tools will be removed in 4.0
* Use .cms-content-filters instead.
*/
.cms-content-tools, .cms-content-tools,
.cms-content-fields, .cms-content-fields,
.cms-edit-form, .cms-edit-form,
@ -92,7 +97,7 @@ body.cms {
.cms-content-header { .cms-content-header {
padding-left: $grid-x * 2; padding-left: $grid-x * 2;
z-index: 60; z-index: 60;
min-height: 40px; min-height: 52px;
background: { background: {
image: url(../images/textures/cms_content_header.png); image: url(../images/textures/cms_content_header.png);
repeat: repeat; repeat: repeat;
@ -123,7 +128,7 @@ body.cms {
.cms-content-header-info { .cms-content-header-info {
float:left; float:left;
padding-top: 6px; padding-top: $grid-y - 1;
& * { & * {
display: inline-block; // align back button and breadcrumbs display: inline-block; // align back button and breadcrumbs
@ -157,6 +162,289 @@ body.cms {
} }
} }
.cms-content-header-top {
@include inline-block;
width: 100%;
}
// We have a faux three column layout when displaying Page content in the CMS.
.has-panel {
.cms-content-header.north {
padding-left: $grid-x*2;
&.collapsed {
.cms-content-header-info {
width: 24px;
text-align: right;
padding-left: 12px;
padding-right: 8px;
}
.view-controls,
.section-label {
display: none;
}
.cms-content-header-nav {
margin-left: 31px;
}
}
}
.cms-content-header-info {
position: absolute;
top: 0;
left: 0;
bottom: 1px;
width: $grid-x * 34;
margin-left: -$grid-x*.5;
padding-bottom: $grid-y;
padding-left: $grid-x * 2;
padding-right: $grid-x*2;
border-right: 1px solid $color-separator;
}
.cms-content-header-nav {
margin-left: 280px;
}
.section-heading {
margin-top: 8px;
padding-left: 4px;
}
.section-icon {
vertical-align: middle;
}
.section-label {
vertical-align: middle;
font-size: 1.2em;
font-weight: normal;
}
.breadcrumbs-wrapper {
float: left;
padding-top: $grid-y - 1;
padding-left: $grid-x*2.5;
}
.cms-content-header-tabs {
margin-top: $grid-y;
}
.view-controls {
float: right;
margin-top: 1px;
}
.font-icon-search {
margin-top: 1px;
&::before {
font-size: 1.3em;
}
}
.cms-content-tools {
.cms-panel-content {
padding-top: 0;
overflow-x: hidden;
}
}
}
#page-title-heading {
line-height: 1.2em;
}
/** ------------------------------------------------------------------
* CMS Breadcrumbs
* ----------------------------------------------------------------- */
.breadcrumbs-wrapper {
.crumb,
.sep {
font-size: .8em;
line-height: 1.2em;
font-weight: normal;
}
.crumb {
&.last {
display: block;
padding: 8px 0;
font-size: 1.2em;
}
}
.sep {
+ .crumb.last {
padding-top: 0;
padding-bottom: 0;
}
}
}
/** ------------------------------------------------------------------
* Filters available in the top bar.
* This is a togglable element that displays a form
* used for filtering content.
* ----------------------------------------------------------------- */
.cms-content-filters {
display: none;
width: 100%;
margin: 0 0 0 -16px;
padding: 16px;
background-color: #dddfe1;
.cms-search-form {
margin-bottom: 0;
}
}
.cms-tabset-nav-primary {
@include inline-block;
vertical-align: middle;
}
/** ------------------------------------------------------------------
* Buttons that use font icons.
* There are !important rules here because we need to override some Tab styling.
* It's tidier to have some !important rules here than have the Tab styles
* littered with load of context specific rules for icon-buttons.
* Icon buttons styles should always take presedence over Tab styles.
* Tabs should be refactored to use weaker selectors.
* ----------------------------------------------------------------- */
a.icon-button,
button.ss-ui-button.icon-button {
vertical-align: middle;
margin-right: 2px;
padding: .4em;
font-size: 1.2em;
letter-spacing: -3px;
text-indent: 0;
text-shadow: none;
line-height: 1em;
color: $color-text;
background-color: transparent;
background-image: none;
border: 0;
&:hover,
&:active,
&:focus {
border: 0;
box-shadow: none;
background-image: none;
text-decoration: none;
}
&:hover {
background-color: #d4dbe1;
}
&.active,
&:active {
background-color: #d4dbe1;
box-shadow: inset 0 0 3px rgba(191, 194, 196, .9);
}
.ui-button-text {
display: none;
}
// Context specific overrides for Tabs.
.ui-tabs.ui-tabs-nav li.cms-tabset-icon.ui-corner-top.ui-state-active &.cms-panel-link,
.ui-tabs.ui-tabs-nav li.cms-tabset-icon.ui-corner-top.ui-state-default &.cms-panel-link {
padding: 6px 8px;
line-height: 1em;
background-color: transparent;
background-image: none;
border: 0;
&:before {
margin-top: 2px;
}
}
.ModelAdmin & {
margin-top: -11px;
}
}
.icon-button-group {
@include inline-block;
margin-top: 2px;
vertical-align: middle;
border: 1px solid #CDCCD0;
@include border-radius(4px);
a.icon-button,
button.ss-ui-button.icon-button {
margin-right: 0;
line-height: 13px;
@include border-radius(0 0 0 0);
&:first-child {
@include border-radius(3px 0 0 3px);
}
&:last-child {
@include border-radius(0 3px 3px 0);
}
&:hover {
background: $tab-panel-texture-color;
padding-bottom: 5px;
}
&.active:hover {
background: #d4dbe1;
}
+ a.icon-button,
+ button.ss-ui-button.icon-button {
border-left: 1px solid #CDCCD0;
}
}
// Context specific overrides for Tabs.
.ui-tabs.ui-tabs-nav {
border-left: 0 !important;
margin-top: -2px !important;
padding-right: 0 !important;
overflow: hidden;
.cms-tabset-icon.ui-state-default {
background-color: transparent;
background-image: none;
border-left: 0;
border-right: 0;
@include box-shadow(none);
+ .cms-tabset-icon.ui-state-default {
border-left: 1px solid #CDCCD0;
}
}
&:hover {
background: $tab-panel-texture-color;
}
&.active:hover {
background: #d4dbe1;
}
.cms-tabset-icon.ui-state-active {
background-color: #d4dbe1;
box-shadow: inset 0 0 3px rgba(191, 194, 196, .9);
}
}
.cms-content-header-tabs & {
overflow: hidden;
}
}
/** -------------------------------------------- /** --------------------------------------------
* Tabs * Tabs
* -------------------------------------------- */ * -------------------------------------------- */
@ -170,16 +458,13 @@ body.cms {
} }
.ui-tabs-panel { .ui-tabs-panel {
padding: $grid-x*3; padding: $grid-y*1.5 $grid-x*2;
background: transparent; // default it's white background: transparent; // default it's white
border: 0; // suppress default borders border: 0; // suppress default borders
&.cms-edit-form { &.cms-edit-form {
padding: 0; padding: 0;
} }
&.cms-panel-padded {
padding: $grid-y*2 $grid-x*2;
}
} }
.ui-widget-header { .ui-widget-header {
@ -187,57 +472,66 @@ body.cms {
background: none; background: none;
} }
.ui-tabs-nav { .ui-tabs-nav {
float: right; float: right;
margin: $grid-x*2 0 -1px 0; margin: $grid-x*2 0 -1px 0;
padding: 0 $grid-x*1.5 0 0; padding: 0 $grid-x*2 0 0;
border-bottom: none; border-bottom: 0;
~ .ui-tabs-panel { ~ .ui-tabs-panel {
border-top:1px solid $color-button-generic-border; clear: both;
clear: both; }
}
li { li {
top: 0; top: 0;
float: left; float: left;
border-bottom: 0 !important; margin-top: 0;
a {
@include inline-block;
float: none;
font-weight: bold;
color: $color-text;
line-height: $grid-y * 4;
padding: 0 $grid-x*2 0;
}
&:last-child {
// correctly right-align last tab
margin-right: 0;
}
&.ui-tabs-active {
margin-bottom: 0;
}
}
.ui-state-default {
border:1px solid $color-button-generic-border;
background: darken($color-widget-bg, 10%);
a { a {
@include inline-block;
float: none;
font-weight: normal;
color: $color-text;
line-height: $grid-y * 4;
padding: 0 $grid-x*1.5 0;
&.icon-button {
@extend a.icon-button;
}
}
&:last-child {
// correctly right-align last tab
margin-right: 0;
}
}
.ui-state-default {
border: 0;
background: transparent;
a {
line-height: 28px;
padding-top: 12px;
padding-bottom: 8px;
color: lighten($color-text, 10%); color: lighten($color-text, 10%);
text-shadow: lighten($color-tab, 5%) 0 1px 0;
&:hover {
color: $color-text;
}
} }
} }
.ui-state-active { .ui-state-active {
padding-bottom: 1px; padding-bottom: 1px;
border: 1px solid $color-button-generic-border; background-color: transparent;
background-color: darken($tab-panel-texture-color, 2%); cursor: text;
a { a {
border-bottom: 4px solid #66727d;
padding-left: 0;
padding-right: 0;
margin: 0 12px 0;
color: $color-text; color: $color-text;
} }
} }
@ -246,13 +540,14 @@ body.cms {
border-color: $color-medium-separator; border-color: $color-medium-separator;
} }
li.cms-tabset-icon { li.cms-tabset-icon.ui-corner-top {
text-indent:-9999em; text-indent:-9999em;
a { a {
display: block; display: block;
padding-left: 40px; // icon width padding-left: 40px; // icon width
padding-right: 0; padding-right: 0;
margin: 0;
} }
&.list a {background: sprite($sprites64, tab-list) no-repeat;} &.list a {background: sprite($sprites64, tab-list) no-repeat;}
@ -276,7 +571,8 @@ body.cms {
.ui-tabs-panel { .ui-tabs-panel {
padding: $grid-x 0 0 0; padding: $grid-x 0 0 0;
} }
} }
.Actions { .Actions {
padding: 0; // Avoid double padding with parent padding: 0; // Avoid double padding with parent
} }
@ -284,7 +580,7 @@ body.cms {
&.ss-tabset-tabshidden .ui-tabs-panel { &.ss-tabset-tabshidden .ui-tabs-panel {
border-top: none; border-top: none;
} }
} }
/** /**
@ -295,7 +591,6 @@ body.cms {
.ui-tabs .ui-tabs-nav.cms-tabset-nav-primary, .ui-tabs .ui-tabs-nav.cms-tabset-nav-primary,
.ui-tabs .cms-content-header-tabs .ui-tabs-nav { .ui-tabs .cms-content-header-tabs .ui-tabs-nav {
margin-top: 0; margin-top: 0;
border-left: 1px solid darken($color-tab, 15%);
float: none; // parent container is already right floated float: none; // parent container is already right floated
li { li {
@ -305,6 +600,8 @@ body.cms {
margin: 0; margin: 0;
// overlap divider line below // overlap divider line below
line-height: 40px - 1px; line-height: 40px - 1px;
padding-top: 0;
padding-bottom: 0;
} }
} }
@ -313,38 +610,56 @@ body.cms {
} }
.ui-state-default { .ui-state-default {
@include box-shadow($color-shadow-light 0 0 2px); background: none;
background-color: $color-base; border-top: none;
@include background-image( border: none;
linear-gradient(lighten($color-base, 12%), $color-base) }
);
border-top:none;
border: {
right-color: darken($color-base, 15%);
left-color: lighten($color-base, 10%);
}
}
.ui-state-active { .ui-state-active {
@include box-shadow(none); @include box-shadow(none);
background:darken($tab-panel-texture-color,2%); background: transparent;
border-top: none; border-top: none;
border: { border: none;
right-color: darken($color-tab, 15%); // same color as divider between header and body, needed for IE
left-color: $tab-panel-texture-color;
}
z-index:2; z-index:2;
a { a {
border-bottom: none; border-bottom: 4px solid #66727d;
} padding: 0;
margin: 0 12px 0;
} }
} }
}
.cms-content-header-tabs { .cms-content-header-tabs {
float: right; float: right;
margin-top: 8px;
.icon-button-group {
margin-right: 16px;
}
.font-icon-search {
&::before {
font-size: 1.3em;
} }
}
}
.cms-content-fields .ui-tabs-nav {
float: none;
padding: 0;
border-bottom: 1px solid $color-button-generic-border;
margin: 0 16px 0;
li {
margin-bottom: -1px;
&.first a {
margin-left: 0;
padding-left: 0;
}
}
}
/** ------------------------------------------------------- /** -------------------------------------------------------
* Loading Interface * Loading Interface
@ -428,9 +743,9 @@ body.cms {
.cms-content-actions, .cms-preview-controls { .cms-content-actions, .cms-preview-controls {
margin: 0; margin: 0;
padding: $grid-y*1.5 $grid-y*1.5; padding: $grid-y*1.5 $grid-x*2;
z-index: 999; z-index: 999;
border-top: 1px solid lighten($color-separator, 4%); border-top: 1px solid $color-light-separator;
@include box-shadow( @include box-shadow(
1px 0 0 $tab-panel-texture-color, 1px 0 0 $tab-panel-texture-color,
$box-shadow-shine 0 1px 0px inset, $box-shadow-shine 0 1px 0px inset,
@ -642,11 +957,11 @@ body.cms {
.cms-content-toolbar { .cms-content-toolbar {
min-height: 29px; min-height: 29px;
display: block; display: block;
margin: 0 0 $grid-y*1.5 0; margin: $grid-y*1.5 0 0;
padding-bottom: 0; padding-bottom: 0;
@include doubleborder(bottom, $color-light-separator, $box-shadow-shine);
@include legacy-pie-clearfix(); @include legacy-pie-clearfix();
border-bottom: 0;
box-shadow: none;
.cms-tree-view-modes { .cms-tree-view-modes {
float:right; float:right;
@ -659,55 +974,49 @@ body.cms {
} }
} }
/* smaller treedropdown */ .ss-ui-button {
.chzn-container-single .chzn-single { margin-bottom: $grid-y*1.5;
height: 26px; }
line-height: 26px;
padding-left:25px; .cms-actions-buttons-row {
color: darken($color-dark-grey, 15%); clear: both;
@include background-image(
linear-gradient($color-button-generic, darken($color-button-generic, 10%)) .ss-ui-button,
); .ss-ui-button:hover,
font: { .ss-ui-button:active,
size:13px; .ss-ui-button:focus {
weight:bold; color: $color-text;
} border: 0;
text-shadow:darken($color-text-shadow, 10%) 0 -1px 1px; background: none;
box-shadow:none; box-shadow: none;
&:hover { text-shadow: none;
@include box-shadow(0 0 5px darken($color-button-generic, 20%)); text-decoration: none;
@include background-image( font-weight: normal;
linear-gradient(lighten($color-button-generic, 2%), darken($color-button-generic, 8%))
);
} }
&:active { .ss-ui-button {
@include box-shadow(inset 0 1px 3px darken($color-button-generic, 60%)); &.active,
} &:active,
&:hover {
span { background-color: #dee3e8;
padding-top:1px; }
}
div {
border-left:none;
width:100%;
}
div b {
background: url(../images/sprites-32x32/menu-arrow-deselected-down.png) no-repeat 9px 11px;
float:right;
width:24px;
} }
} }
.ss-ui-button { .cms-actions-tools-row {
margin-bottom: $grid-y; clear: both;
}
.tool-action {
display: none;
} }
} }
/**
/* -------------------------------------------------------- * DEPRECATED:
* .cms-content-tools will be removed in 4.0
* Use .cms-content-filters instead.
*
* Content Tools is the sidebar on the left of the main content * Content Tools is the sidebar on the left of the main content
* panel * panel
*/ */
@ -737,7 +1046,7 @@ body.cms {
.cms-panel-content { .cms-panel-content {
width: $grid-x * 34; width: $grid-x * 34;
padding: $grid-x*1.1 $grid-x 0; // smaller left/right padding to use space efficiently padding: 0 $grid-x*1.5 0 $grid-x*2;
overflow: auto; overflow: auto;
height:100%; height:100%;
@ -787,7 +1096,7 @@ body.cms {
label { label {
float: none; float: none;
width: auto; width: auto;
font-size: 11px; font-size: $font-base-size;
padding: 0 $grid-x 4px 0; padding: 0 $grid-x 4px 0;
} }
@ -885,6 +1194,14 @@ body.cms {
/** /**
* CMS Batch actions * CMS Batch actions
*/ */
.cms-content-batchactions-button {
@include inline-block;
padding: 4px 6px;
vertical-align: middle;
@include background-image(linear-gradient(top, #fff, #D9D9D9));
border: 1px solid #aaa;
@include border-radius(4px);
}
.cms-content-batchactions { .cms-content-batchactions {
float: left; float: left;
@ -907,79 +1224,69 @@ body.cms {
vertical-align: middle; vertical-align: middle;
} }
label { .view-mode-batchactions-label {
vertical-align: middle; vertical-align: middle;
display: none; display: none;
} }
fieldset, .Actions {
display: inline-block;
}
#view-mode-batchactions {
margin-top: 2px;
}
} }
&.inactive .view-mode-batchactions-wrapper { .checkbox {
border-radius: 4px; margin-top: 2px;
vertical-align: middle;
label {
display: inline;
}
}
form > * {
display: block;
float: left;
}
form.cms-batch-actions {
float: left;
}
&.inactive form {
display: none;
}
.chzn-container-single {
display: block;
.chzn-single {
border-radius: 0;
@include background-image(linear-gradient(top, #fff, #D9D9D9));
span {
padding-top: 0;
}
}
}
.cms-batch-actions {
.dropdown {
margin: 0;
.chzn-single {
padding-left: 8px; /* use default size without icon */
}
}
.Actions .ss-ui-button {
padding-top: 4px;
padding-bottom: 4px;
height: 28px;
margin-left: -1px;
border-top-left-radius: 0;
border-bottom-left-radius: 0;
&.ui-state-disabled {
opacity: 1;
color: #ccc;
}
}
} }
} }
.cms-content-batchactions-dropdown {
@include inline-block;
#Form_BatchActionsForm select { // Context specific rules for when batch actions are in the SiteTree panel.
width: 200px; .cms-content-tools & {
width: 100%;
}
fieldset { // The dropdown element wrapper
@include inline-block;
width: 200px;
.view-mode-batchactions-label {
display: inline;
}
// Context specific rules for when batch actions are in the SiteTree panel.
.cms-content-tools & {
width: 82%;
}
}
.dropdown { // The 'select' element
width: 100%;
height: 32px;
.chzn-single {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
box-shadow: none;
}
}
.Actions {
@include inline-block;
// Context specific rules for when batch actions are in the SiteTree panel.
.cms-content-tools & {
width: 16%;
}
padding: 0;
margin-left: -4px;
}
.action {
width: 100%;
height: 32px;
margin-bottom: 0;
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
} }
/** -------------------------------------------- /** --------------------------------------------
@ -1101,8 +1408,8 @@ form.member-profile-form {
// can trigger longer pages and the extra scroll bar doesn't fire our sizing bar // can trigger longer pages and the extra scroll bar doesn't fire our sizing bar
overflow-y: auto; overflow-y: auto;
overflow-x: auto; overflow-x: auto;
background: darken($tab-panel-texture-color, 2%); background: $tab-panel-texture-color;
width:100%; width: 100%;
#Root_Main { #Root_Main {
.confirmedpassword { .confirmedpassword {
border-bottom:none; border-bottom:none;
@ -1163,7 +1470,7 @@ form.member-profile-form {
.cms-panel-toggle { .cms-panel-toggle {
@include box-shadow(0 0 1px $box-shadow-shine); @include box-shadow(0 0 1px $box-shadow-shine);
&.south { &.south {
border-top: 1px solid darken($color-base, 6%); border-top: 1px solid darken($color-light-separator, 15%);
@include box-shadow(lighten($color-base, 4%) 0 1px 0px inset); @include box-shadow(lighten($color-base, 4%) 0 1px 0px inset);
position: absolute; position: absolute;
bottom: 0; bottom: 0;
@ -1188,9 +1495,15 @@ form.member-profile-form {
} }
} }
} }
/**
* DEPRECATED:
* .cms-content-tools will be removed in 4.0
* Use .cms-content-filters instead.
*/
&.cms-content-tools .cms-panel-toggle { &.cms-content-tools .cms-panel-toggle {
&.south { &.south {
border-top: 1px solid darken($tab-panel-texture-color,10%); border-top: 1px solid $color-light-separator;
@include box-shadow($box-shadow-shine 0 1px 0px inset); @include box-shadow($box-shadow-shine 0 1px 0px inset);
} }
} }
@ -1279,11 +1592,21 @@ form.member-profile-form {
.cms { .cms {
.cms-panel-padded { .cms-panel-padded {
padding: $grid-y*2 $grid-x*2; padding: 0 $grid-x*2 $grid-y*1.5;
margin:0;
&.ReportAdmin {
> fieldset {
padding-top: 12px;
}
}
} }
} }
.CMSPageAddController {
.cms-panel-padded {
padding: $grid-y*2 $grid-x*2;
}
}
/** ------------------------------------------------------------------ /** ------------------------------------------------------------------
* Dialog * Dialog
* *
@ -1366,7 +1689,7 @@ form.member-profile-form {
// Note: Does NOT apply to member profile form (unnecessary, since it doesn't have a dialog title) // Note: Does NOT apply to member profile form (unnecessary, since it doesn't have a dialog title)
.ui-tabs-nav { .ui-tabs-nav {
position:absolute; position:absolute;
top:0; top: 9px;
// Tabs nav is included in scollbar. By absolutely positioning it off the edge, // Tabs nav is included in scollbar. By absolutely positioning it off the edge,
// we avoid tabs shifting position when scrollbar toggles // we avoid tabs shifting position when scrollbar toggles
right: 40px; right: 40px;
@ -1471,10 +1794,12 @@ body.cms-dialog {
.cms-content-header{ .cms-content-header{
padding:0; padding:0;
width:100%; width:100%;
height: 40px; height: 53px;
h3{ h3{
padding: 3px 8px; padding: $grid-y*1.5 $grid-x*2;
margin: 10px; margin: 0;
line-height: 28px;
} }
} }
@ -1731,10 +2056,6 @@ form.small {
* Import forms * Import forms
*/ */
body.SecurityAdmin {
background-color: darken($tab-panel-texture-color,2%); //adds background to import members/groups iframe
}
form.import-form { form.import-form {
ul { ul {
list-style: disc; list-style: disc;

View File

@ -64,8 +64,19 @@
height: 16px; height: 16px;
width: 12px; width: 12px;
&.jstree-checkbox { &.jstree-checkbox {
height: 19px; //Larger to help avoid accidental page loads when trying to click checkboxes
width: 16px; width: 16px;
position: relative;
//Larger to help avoid accidental page loads when trying to click checkboxes
&:before {
content: '';
display: block;
position: absolute;
z-index: 1;
left: -3px;
top: -3px;
height: 22px;
width: 25px;
}
} }
} }
} }
@ -592,14 +603,24 @@
.jstree-apple a .jstree-icon { .jstree-apple a .jstree-icon {
background-position:-60px -19px; background-position:-60px -19px;
} }
.jstree-apple a {
border-radius: 3px;
}
/* ensure status is visible in sidebar */ /**
* DEPRECATED:
* .cms-content-tools will be removed in 4.0
* Use .cms-content-filters instead.
*
* Ensure status is visible in sidebar
*/
.cms-content-tools .cms-tree.jstree { .cms-content-tools .cms-tree.jstree {
li { li {
min-width: 159px; min-width: 187px;
} }
a { a {
overflow: hidden; overflow: hidden;
text-overflow: ellipsis;
display: block; display: block;
position: relative; position: relative;
} }
@ -635,44 +656,52 @@ a .jstree-pageicon {
} }
} }
/* tree status icons and labels */ /* Tree status labels and dots */
.jstree-apple .jstree-clicked,
.jstree-apple .jstree-hovered {
background: #ebfbff;
}
%tree-status-icon-before { %tree-status-icon-before {
content:""; content:"";
display: block; display: block;
width:5px; width:6px;
height: 5px; height: 6px;
position: absolute; position: absolute;
bottom: 0; bottom: 0;
right: 0; right: 0;
background: #fce2d0; background: #fce2d0;
border: 1px solid #ff9344; border: 1px solid #fff;
border-radius: 100px; border-radius: 100px;
box-shadow: 0px 0px 0px 1px #fff;
} }
@mixin tree-status-icon($label, $color, $bgColor) { @mixin tree-status-icon($label, $dotColor, $textColor, $bgColor) {
.cms-tree.jstree .status-#{$label} > a .jstree-pageicon:before { .cms-tree.jstree .status-#{$label} > a .jstree-pageicon:before {
@extend %tree-status-icon-before; @extend %tree-status-icon-before;
} }
// Labels
.jstree .status-#{$label} > .jstree-hovered, .jstree .status-#{$label} > .jstree-hovered,
.jstree .status-#{$label} > .jstree-clicked, .jstree .status-#{$label} > .jstree-clicked,
.cms-tree.jstree span.badge.status-#{$label}, .cms-tree.jstree span.badge.status-#{$label} {
.cms-tree.jstree .status-#{$label} > a .jstree-pageicon:before {
background-color:$bgColor; background-color:$bgColor;
border-color:$color; border-color:$textColor;
}
#cms-content-tools-CMSMain .cms-tree.jstree span.badge.status-#{$label} {
box-shadow: 0px 0px 6px 2px $bgColor;
} }
.cms-tree.jstree span.badge.status-#{$label} { .cms-tree.jstree span.badge.status-#{$label} {
color: $color; color: $textColor;
}
// Dots
.cms-tree.jstree .status-#{$label} > a .jstree-pageicon:before {
background-color:$dotColor;
@include box-shadow(0px 1px 1px rgba(0, 0, 0, 0.3), inset 0 0 0 1px $textColor);
}
#cms-content-tools-CMSMain .cms-tree.jstree span.badge.status-#{$label} {
@include box-shadow(0px 0px 6px 2px $bgColor);
} }
} }
@include tree-status-icon('modified', #EE6214, #FFF4ED); @include tree-status-icon('addedtodraft', #ff7f22, #F46B00, #fff7f2);
@include tree-status-icon('addedtodraft', #EE6214, #FFF4ED); @include tree-status-icon('modified', #fff2e8, #F46B00, #fff7f2);
@include tree-status-icon('deletedonlive', #5F7688, #F5F5F5); @include tree-status-icon('archived', #5F7688, #455b6c, #f7f7f7);
@include tree-status-icon('archived', #5F7688, #F5F5F5); @include tree-status-icon('deletedonlive', #f7f7f7, #455b6c, #f7f7f7);
@include tree-status-icon('removedfromdraft', #5F7688, #F5F5F5); @include tree-status-icon('removedfromdraft', #f7f7f7, #455b6c, #f7f7f7);
@include tree-status-icon('workflow-approval', #0070B4, #E8FAFF); @include tree-status-icon('workflow-approval', #0070B4, #0070B4, #E8FAFF);
.cms-tree { .cms-tree {
visibility: hidden; // enabled by JS to avoid layout glitches visibility: hidden; // enabled by JS to avoid layout glitches

View File

@ -91,7 +91,7 @@
overflow-y: auto; overflow-y: auto;
/** sorry about the !important but the specificity of other selectors mandates it over writing out very specific selectors **/ /** sorry about the !important but the specificity of other selectors mandates it over writing out very specific selectors **/
&-loading { & .loading {
background-image: url(../images/throbber.gif) !important; background-image: url(../images/throbber.gif) !important;
background-position: 97% center !important; background-position: 97% center !important;
background-repeat: no-repeat !important; background-repeat: no-repeat !important;

View File

@ -240,7 +240,13 @@ table.ss-gridfield-table {
//fix for model admin filter styling /**
* DEPRECATED:
* .cms-content-tools will be removed in 4.0
* Use .cms-content-filters instead.
*
* Fix for model admin filter styling
*/
.ModelAdmin .cms-content-fields .cms-content-tools .cms-panel-content { .ModelAdmin .cms-content-fields .cms-content-tools .cms-panel-content {
.cms-search-form { .cms-search-form {
overflow:hidden; overflow:hidden;

View File

@ -17,6 +17,11 @@
} }
} }
/**
* DEPRECATED:
* .cms-content-tools will be removed in 4.0
* Use .cms-content-filters instead.
*/
.cms .cms-content-tools { .cms .cms-content-tools {
//fix for width of dropdowns in filter panel //fix for width of dropdowns in filter panel

View File

@ -48,6 +48,7 @@ $experimental-support-for-svg: true;
/** ----------------------------- /** -----------------------------
* CMS Components * CMS Components
* ------------------------------ */ * ------------------------------ */
@import "fonts.scss";
@import "typography.scss"; @import "typography.scss";
@import "uitheme.scss"; @import "uitheme.scss";
@import "forms.scss"; @import "forms.scss";

View File

@ -11,12 +11,12 @@ $color-base: #b0bec7 !default;
$color-widget-bg: lighten($color-base, 20%) !default; $color-widget-bg: lighten($color-base, 20%) !default;
/* Keep as solid colours transparent borders wont work in ie */ /* Keep as solid colours transparent borders wont work in ie */
$color-darker-bg: #D4D6D8 !default; $color-darker-bg: #E6EAED !default;
$color-dark-bg: #142136 !default; $color-dark-bg: #142136 !default;
$color-dark-separator: #19435c !default; $color-dark-separator: #19435c !default;
$color-medium-separator: #808080 !default; $color-medium-separator: #808080 !default;
$color-separator: #C0C0C2 !default; $color-separator: #C1C7CC !default; // Vertical dividers
$color-light-separator: #d0d3d5 !default; $color-light-separator: #D2D5D8 !default; // Horiontal dividers
$color-tab: #d9d9d9 !default; $color-tab: #d9d9d9 !default;
$color-dark-grey: #7B8C91 !default; $color-dark-grey: #7B8C91 !default;
@ -37,7 +37,7 @@ $color-menu-background: #c6d7df !default;
$color-menu-border: #8c99a1 !default; $color-menu-border: #8c99a1 !default;
$color-panel-background: #c6d7df !default; $color-panel-background: #c6d7df !default;
$color-text: #444 !default; $color-text: #66727d !default;
$color-text-light: white !default; $color-text-light: white !default;
$color-text-light-link: white !default; $color-text-light-link: white !default;
$color-text-disabled: #aaa !default; $color-text-disabled: #aaa !default;
@ -48,7 +48,7 @@ $color-text-shadow: white !default;
$color-button-generic: #e6e6e6 !default; $color-button-generic: #e6e6e6 !default;
$color-button-generic-border: #c0c0c2 !default; $color-button-generic-border: #d0d3d5 !default;
$color-button-highlight: #e6e6e6 !default; $color-button-highlight: #e6e6e6 !default;
$color-button-highlight-border: #708284 !default; $color-button-highlight-border: #708284 !default;
@ -67,7 +67,7 @@ $color-good: #72c34b !default; // green
/*$color-optional: #a1d2eb !default; */ // orange /*$color-optional: #a1d2eb !default; */ // orange
$color-cms-batchactions-menu-background: #f5f5f5 !default; $color-cms-batchactions-menu-background: #f5f5f5 !default;
$color-cms-batchactions-menu-selected-background: #efe999 !default; $color-cms-batchactions-menu-selected-background: #fffcdc !default;
/** ----------------------------------------------- /** -----------------------------------------------
* Textures * Textures

View File

@ -1,13 +1,5 @@
<div class="breadcrumbs-wrapper" data-pjax-fragment="Breadcrumbs"> <div class="breadcrumbs-wrapper" data-pjax-fragment="Breadcrumbs">
<h2 id="page-title-heading"> <h2 id="page-title-heading">
<% if $ToplevelController %>
<span class="section-icon icon icon-16 icon-{$ToplevelController.MenuCurrentItem.Code.LowerCase}"></span>
<% else_if $Controller %>
<span class="section-icon icon icon-16 icon-{$Controller.MenuCurrentItem.Code.LowerCase}"></span>
<% else %>
<span class="section-icon icon icon-16 icon-{$MenuCurrentItem.Code.LowerCase}"></span>
<% end_if %>
<% loop $Breadcrumbs %> <% loop $Breadcrumbs %>
<% if $Last %> <% if $Last %>
<span class="cms-panel-link crumb last">$Title.XML</span> <span class="cms-panel-link crumb last">$Title.XML</span>

View File

@ -1,7 +1,7 @@
<% if $Backlink %> <% if $Backlink %>
<div class="cms_backlink"> <div class="cms_backlink">
<a class="backlink ss-ui-button cms-panel-link" data-icon="back" href="$Backlink"> <a class="backlink ss-ui-button cms-panel-link" data-icon="back" href="$Backlink">
<% _t('BackLink_Button_ss.Back', 'Back') %> <%t BackLink_Button_ss.Back 'Back' %>
</a> </a>
</div> </div>
<% end_if %> <% end_if %>

View File

@ -1,6 +1,6 @@
<div class="ss-loading-screen"> <div class="ss-loading-screen">
<div class="loading-logo"> <div class="loading-logo">
<img class="loading-animation" src="$ModulePath(frameworkadmin)/images/spinner.gif" alt="<% _t('CMSLoadingScreen_ss.LOADING','Loading...') %>" /> <img class="loading-animation" src="$ModulePath(frameworkadmin)/images/spinner.gif" alt="<%t CMSLoadingScreen_ss.LOADING 'Loading...' %>" />
<noscript><p class="nojs-warning"><span class="message notice"><% _t('CMSLoadingScreen_ss.REQUIREJS','The CMS requires that you have JavaScript enabled.') %></span></p></noscript> <noscript><p class="nojs-warning"><span class="message notice"><%t CMSLoadingScreen_ss.REQUIREJS 'The CMS requires that you have JavaScript enabled.' %></span></p></noscript>
</div> </div>
</div> </div>

View File

@ -1,5 +1,5 @@
<div class="cms-navigator"> <div class="cms-navigator">
<a href="#" class="ss-ui-button cms-preview-toggle-link" data-icon="preview"> <a href="#" class="ss-ui-button cms-preview-toggle-link" data-icon="preview">
&laquo; <% _t('SilverStripeNavigator.Edit', 'Edit') %> &laquo; <%t SilverStripeNavigator.Edit 'Edit' %>
</a> </a>
</div> </div>

View File

@ -3,7 +3,6 @@
<% end_if %> <% end_if %>
<div class="cms-content-header north"> <div class="cms-content-header north">
<div class="cms-content-header-info"> <div class="cms-content-header-info">
<% include BackLink_Button %>
<% with $Controller %> <% with $Controller %>
<% include CMSBreadcrumbs %> <% include CMSBreadcrumbs %>
<% end_with %> <% end_with %>
@ -51,7 +50,7 @@
<% end_loop %> <% end_loop %>
<% if $Controller.LinkPreview %> <% if $Controller.LinkPreview %>
<a href="$Controller.LinkPreview" class="cms-preview-toggle-link ss-ui-button" data-icon="preview"> <a href="$Controller.LinkPreview" class="cms-preview-toggle-link ss-ui-button" data-icon="preview">
<% _t('LeftAndMain.PreviewButton', 'Preview') %> &raquo; <%t LeftAndMain.PreviewButton 'Preview' %> &raquo;
</a> </a>
<% end_if %> <% end_if %>
</div> </div>

View File

@ -8,10 +8,10 @@
</div> </div>
<div class="cms-login-status"> <div class="cms-login-status">
<a href="$LogoutURL" class="logout-link" title="<% _t('LeftAndMain_Menu_ss.LOGOUT','Log out') %>"><% _t('LeftAndMain_Menu_ss.LOGOUT','Log out') %></a> <a href="$LogoutURL" class="logout-link" title="<%t LeftAndMain_Menu_ss.LOGOUT 'Log out' %>"><%t LeftAndMain_Menu_ss.LOGOUT 'Log out' %></a>
<% with $CurrentMember %> <% with $CurrentMember %>
<span> <span>
<% _t('LeftAndMain_Menu_ss.Hello','Hi') %> <%t LeftAndMain_Menu_ss.Hello 'Hi' %>
<a href="{$AbsoluteBaseURL}admin/myprofile" class="profile-link"> <a href="{$AbsoluteBaseURL}admin/myprofile" class="profile-link">
<% if $FirstName && $Surname %>$FirstName $Surname<% else_if $FirstName %>$FirstName<% else %>$Email<% end_if %> <% if $FirstName && $Surname %>$FirstName $Surname<% else_if $FirstName %>$FirstName<% else %>$Email<% end_if %>
</a> </a>

View File

@ -3,18 +3,18 @@
<% include LeftAndMain_ViewModeSelector SelectID="preview-mode-dropdown-in-preview" %> <% include LeftAndMain_ViewModeSelector SelectID="preview-mode-dropdown-in-preview" %>
<span id="preview-size-dropdown" class="preview-size-selector preview-selector field dropdown"> <span id="preview-size-dropdown" class="preview-size-selector preview-selector field dropdown">
<select title="<% _t('SilverStripeNavigator.ViewDeviceWidth', 'Select a preview width') %>" id="preview-size-dropdown-select" class="preview-dropdown dropdown nolabel" autocomplete="off" name="Action"> <select title="<%t SilverStripeNavigator.ViewDeviceWidth 'Select a preview width' %>" id="preview-size-dropdown-select" class="preview-dropdown dropdown nolabel" autocomplete="off" name="Action">
<option data-icon="icon-auto" data-description="<% _t('SilverStripeNavigator.Responsive', 'Responsive') %>" class="icon-auto icon-view first" value="auto"> <option data-icon="icon-auto" data-description="<%t SilverStripeNavigator.Responsive 'Responsive' %>" class="icon-auto icon-view first" value="auto">
<% _t('SilverStripeNavigator.Auto', 'Auto') %> <%t SilverStripeNavigator.Auto 'Auto' %>
</option> </option>
<option data-icon="icon-desktop" data-description="1024px <% _t('SilverStripeNavigator.Width', 'width') %>" class="icon-desktop icon-view" value="desktop"> <option data-icon="icon-desktop" data-description="1024px <%t SilverStripeNavigator.Width 'width' %>" class="icon-desktop icon-view" value="desktop">
<% _t('SilverStripeNavigator.Desktop', 'Desktop') %> <%t SilverStripeNavigator.Desktop 'Desktop' %>
</option> </option>
<option data-icon="icon-tablet" data-description="800px <% _t('SilverStripeNavigator.Width', 'width') %>" class="icon-tablet icon-view" value="tablet"> <option data-icon="icon-tablet" data-description="800px <%t SilverStripeNavigator.Width 'width' %>" class="icon-tablet icon-view" value="tablet">
<% _t('SilverStripeNavigator.Tablet', 'Tablet') %> <%t SilverStripeNavigator.Tablet 'Tablet' %>
</option> </option>
<option data-icon="icon-mobile" data-description="400px <% _t('SilverStripeNavigator.Width', 'width') %>" class="icon-mobile icon-view last" value="mobile"> <option data-icon="icon-mobile" data-description="400px <%t SilverStripeNavigator.Width 'width' %>" class="icon-mobile icon-view last" value="mobile">
<% _t('SilverStripeNavigator.Mobile', 'Mobile') %> <%t SilverStripeNavigator.Mobile 'Mobile' %>
</option> </option>
</select> </select>
</span> </span>
@ -32,7 +32,7 @@
</fieldset> </fieldset>
<% else %> <% else %>
<span id="preview-state-dropdown" class="cms-preview-states field dropdown"> <span id="preview-state-dropdown" class="cms-preview-states field dropdown">
<select title="<% _t('SilverStripeNavigator.PreviewState', 'Preview State') %>" id="preview-states" class="preview-state dropdown nolabel" autocomplete="off" name="preview-state"> <select title="<%t SilverStripeNavigator.PreviewState 'Preview State' %>" id="preview-states" class="preview-state dropdown nolabel" autocomplete="off" name="preview-state">
<% loop $Items %> <% loop $Items %>
<option name="$Name" data-name="$Name" data-link="$Link" class="state-name $FirstLast" value="$Link" <% if $isActive %>selected<% end_if %>> <option name="$Name" data-name="$Name" data-link="$Link" class="state-name $FirstLast" value="$Link" <% if $isActive %>selected<% end_if %>>
$Title $Title

View File

@ -2,30 +2,37 @@
<div class="cms-content-header north"> <div class="cms-content-header north">
<div class="cms-content-header-info"> <div class="cms-content-header-info">
<h2> <div class="breadcrumbs-wrapper">
<% include CMSSectionIcon %> <h2 id="page-title-heading">
<% if $SectionTitle %> <span class="cms-panel-link crumb last">
$SectionTitle <% if $SectionTitle %>
<% else %> $SectionTitle
<% _t('ModelAdmin.Title', 'Data Models') %> <% else %>
<% end_if %> <%t ModelAdmin.Title 'Data Models' %>
</h2> <% end_if %>
</span>
</h2>
</div>
</div> </div>
<div class="cms-content-header-tabs cms-tabset-nav-primary ss-ui-tabs-nav"> <div class="cms-content-header-tabs cms-tabset-nav-primary ss-ui-tabs-nav">
<ul> <button id="filters-button" class="icon-button font-icon-search" title="<%t CMSPagesController_Tools_ss.FILTER 'Filter' %>"></button>
<% loop $ManagedModelTabs %> <ul class="cms-tabset-nav-primary">
<% loop $ManagedModelTabs %>
<li class="tab-$ClassName $LinkOrCurrent<% if $LinkOrCurrent == 'current' %> ui-tabs-active<% end_if %>"> <li class="tab-$ClassName $LinkOrCurrent<% if $LinkOrCurrent == 'current' %> ui-tabs-active<% end_if %>">
<a href="$Link" class="cms-panel-link" title="Form_EditForm">$Title</a> <a href="$Link" class="cms-panel-link" title="Form_EditForm">$Title</a>
</li> </li>
<% end_loop %> <% end_loop %>
</ul> </ul>
</div> </div>
</div> </div>
<div class="cms-content-fields center ui-widget-content" data-layout-type="border"> <div class="cms-content-fields center ui-widget-content cms-panel-padded" data-layout-type="border">
$Tools $Tools
$EditForm
<div class="cms-content-view">
$EditForm
</div>
</div> </div>
</div> </div>

View File

@ -1,8 +1,8 @@
<div class="importSpec" id="SpecFor{$ModelName}"> <div class="importSpec" id="SpecFor{$ModelName}">
<a href="#SpecDetailsFor{$ModelName}" class="detailsLink"><% sprintf(_t('ModelAdmin_ImportSpec_ss.IMPORTSPECLINK', 'Show Specification for %s'),$ModelName) %></a> <a href="#SpecDetailsFor{$ModelName}" class="detailsLink"><%t ModelAdmin_ImportSpec_ss.IMPORTSPECLINK 'Show Specification for %s' s=$ModelName %></a>
<div class="details" id="SpecDetailsFor{$ModelName}"> <div class="details" id="SpecDetailsFor{$ModelName}">
<h4><% sprintf(_t('ModelAdmin_ImportSpec_ss.IMPORTSPECTITLE', 'Specification for %s'),$ModelName) %></h4> <h4><%t ModelAdmin_ImportSpec_ss.IMPORTSPECTITLE 'Specification for %s' s=$ModelName %></h4>
<h5><% _t('ModelAdmin_ImportSpec_ss.IMPORTSPECFIELDS', 'Database columns') %></h5> <h5><%t ModelAdmin_ImportSpec_ss.IMPORTSPECFIELDS 'Database columns' %></h5>
<% loop $Fields %> <% loop $Fields %>
<dl> <dl>
<dt><em>$Name</em></dt> <dt><em>$Name</em></dt>
@ -10,7 +10,7 @@
</dl> </dl>
<% end_loop %> <% end_loop %>
<h5><% _t('ModelAdmin_ImportSpec_ss.IMPORTSPECRELATIONS', 'Relations') %></h5> <h5><%t ModelAdmin_ImportSpec_ss.IMPORTSPECRELATIONS 'Relations' %></h5>
<% loop $Relations %> <% loop $Relations %>
<dl> <dl>
<dt><em>$Name</em></dt> <dt><em>$Name</em></dt>

View File

@ -1,16 +1,11 @@
<div class="cms-content-tools west cms-panel cms-panel-layout" id="cms-content-tools-ModelAdmin" data-expandOnClick="true" data-layout-type="border"> <div id="cms-content-tools-ModelAdmin" class="cms-content-filters">
<div class="cms-panel-content center"> <% if $SearchForm %>
<% if $SearchForm %> <h3 class="cms-panel-header"><%t ModelAdmin_Tools_ss.FILTER 'Filter' %></h3>
<h3 class="cms-panel-header"><% _t('ModelAdmin_Tools_ss.FILTER', 'Filter') %></h3> $SearchForm
$SearchForm <% end_if %>
<% end_if %>
<% if $ImportForm %> <% if $ImportForm %>
<h3 class="cms-panel-header"><% _t('ModelAdmin_Tools_ss.IMPORT', 'Import') %></h3> <h3 class="cms-panel-header"><%t ModelAdmin_Tools_ss.IMPORT 'Import' %></h3>
$ImportForm $ImportForm
<% end_if %> <% end_if %>
</div>
<div class="cms-panel-content-collapsed">
<h3 class="cms-panel-header"><% _t('ModelAdmin_Tools_ss.FILTER', 'Filter') %></h3>
</div>
</div> </div>

View File

@ -16,7 +16,7 @@
$Content $Content
<div class="cms-preview east" data-layout-type="border"> <div class="cms-preview east" data-layout-type="border">
<div class="preview-note"><span><!-- --></span><% _t('CMSPageHistoryController_versions_ss.PREVIEW','Website preview') %></div> <div class="preview-note"><span><!-- --></span><%t CMSPageHistoryController_versions_ss.PREVIEW 'Website preview' %></div>
<div class="preview-scroll center"> <div class="preview-scroll center">
<div class="preview-device-outer"> <div class="preview-device-outer">
<div class="preview-device-inner"> <div class="preview-device-inner">

View File

@ -1,7 +1,7 @@
<h3><% _t('ModelSidebar_ss.SEARCHLISTINGS','Search') %></h3> <h3><%t ModelSidebar_ss.SEARCHLISTINGS 'Search' %></h3>
$SearchForm $SearchForm
<% if $ImportForm %> <% if $ImportForm %>
<h3><% _t('ModelSidebar_ss.IMPORT_TAB_HEADER', 'Import') %></h3> <h3><%t ModelSidebar_ss.IMPORT_TAB_HEADER 'Import' %></h3>
$ImportForm $ImportForm
<% end_if %> <% end_if %>

View File

@ -135,7 +135,7 @@ class LeftAndMainTest extends FunctionalTest {
$link = $menuItem->Link; $link = $menuItem->Link;
// don't test external links // don't test external links
if(preg_match('/^https?:\/\//',$link)) continue; if(preg_match('/^(https?:)?\/\//',$link)) continue;
$response = $this->get($link); $response = $this->get($link);

View File

@ -47,22 +47,22 @@ class RestfulService extends ViewableData implements Flushable {
* set a curl option that will be applied to all requests as default * set a curl option that will be applied to all requests as default
* {@see http://php.net/manual/en/function.curl-setopt.php#refsect1-function.curl-setopt-parameters} * {@see http://php.net/manual/en/function.curl-setopt.php#refsect1-function.curl-setopt-parameters}
* *
* @deprecated 3.2 Use the "RestfulService.default_curl_options" config setting instead * @deprecated 4.0 Use the "RestfulService.default_curl_options" config setting instead
* @param int $option The cURL opt Constant * @param int $option The cURL opt Constant
* @param mixed $value The cURL opt value * @param mixed $value The cURL opt value
*/ */
public static function set_default_curl_option($option, $value) { public static function set_default_curl_option($option, $value) {
Deprecation::notice('3.2', 'Use the "RestfulService.default_curl_options" config setting instead'); Deprecation::notice('4.0', 'Use the "RestfulService.default_curl_options" config setting instead');
Config::inst()->update('RestfulService', 'default_curl_options', array($option => $value)); Config::inst()->update('RestfulService', 'default_curl_options', array($option => $value));
} }
/** /**
* set many defauly curl options at once * set many defauly curl options at once
* *
* @deprecated 3.2 Use the "RestfulService.default_curl_options" config setting instead * @deprecated 4.0 Use the "RestfulService.default_curl_options" config setting instead
*/ */
public static function set_default_curl_options($optionArray) { public static function set_default_curl_options($optionArray) {
Deprecation::notice('3.2', 'Use the "RestfulService.default_curl_options" config setting instead'); Deprecation::notice('4.0', 'Use the "RestfulService.default_curl_options" config setting instead');
Config::inst()->update('RestfulService', 'default_curl_options', $optionArray); Config::inst()->update('RestfulService', 'default_curl_options', $optionArray);
} }
@ -74,12 +74,12 @@ class RestfulService extends ViewableData implements Flushable {
* @param string $user The proxy auth user name * @param string $user The proxy auth user name
* @param string $password The proxy auth password * @param string $password The proxy auth password
* @param boolean $socks Set true to use socks5 proxy instead of http * @param boolean $socks Set true to use socks5 proxy instead of http
* @deprecated 3.2 Use the "RestfulService.default_curl_options" config setting instead, * @deprecated 4.0 Use the "RestfulService.default_curl_options" config setting instead,
* with direct reference to the CURL_* options * with direct reference to the CURL_* options
*/ */
public static function set_default_proxy($proxy, $port = 80, $user = "", $password = "", $socks = false) { public static function set_default_proxy($proxy, $port = 80, $user = "", $password = "", $socks = false) {
Deprecation::notice( Deprecation::notice(
'3.1', '4.0',
'Use the "RestfulService.default_curl_options" config setting instead, ' 'Use the "RestfulService.default_curl_options" config setting instead, '
. 'with direct reference to the CURL_* options' . 'with direct reference to the CURL_* options'
); );
@ -144,8 +144,11 @@ class RestfulService extends ViewableData implements Flushable {
$this->customHeaders[] = $header; $this->customHeaders[] = $header;
} }
/**
* @deprecated since version 4.0
*/
protected function constructURL(){ protected function constructURL(){
Deprecation::notice('3.2', 'constructURL is deprecated, please use `getAbsoluteRequestURL` instead'); Deprecation::notice('4.0', 'constructURL is deprecated, please use `getAbsoluteRequestURL` instead');
return Controller::join_links($this->baseURL, '?' . $this->queryString); return Controller::join_links($this->baseURL, '?' . $this->queryString);
} }
@ -616,9 +619,10 @@ class RestfulService_Response extends SS_HTTPResponse {
/** /**
* @param string * @param string
* @deprecated since version 4.0
*/ */
public function setCachedBody($content) { public function setCachedBody($content) {
Deprecation::notice('3.2', 'Setting the response body is now deprecated, set the cached request instead'); Deprecation::notice('4.0', 'Setting the response body is now deprecated, set the cached request instead');
if (!$this->cachedResponse) { if (!$this->cachedResponse) {
$this->cachedResponse = new RestfulService_Response($content); $this->cachedResponse = new RestfulService_Response($content);
} }

View File

@ -17,10 +17,10 @@
], ],
"require": { "require": {
"php": ">=5.3.3", "php": ">=5.3.3",
"composer/installers": "*" "composer/installers": "~1.0"
}, },
"require-dev": { "require-dev": {
"phpunit/PHPUnit": "~3.7@stable" "phpunit/PHPUnit": "~3.7"
}, },
"extra": { "extra": {
"branch-alias": { "branch-alias": {
@ -29,6 +29,5 @@
}, },
"autoload": { "autoload": {
"classmap": ["tests/behat/features/bootstrap"] "classmap": ["tests/behat/features/bootstrap"]
}, }
"minimum-stability": "dev"
} }

View File

@ -13,10 +13,16 @@ output_style = :compact
# To enable relative paths to assets via compass helper functions. Uncomment: # To enable relative paths to assets via compass helper functions. Uncomment:
relative_assets = true relative_assets = true
# disable comments in the output. We want admin comments # Disable comments in the output. We want admin comments to be verbose.
# to be verbose
line_comments = false line_comments = false
asset_cache_buster :none # Disable asset cache buster. Using :none doesn't work in some versions of compass.
asset_cache_buster do |http_path, real_path|
nil
end
sourcemap = true
sass_options = { :unix_newlines => true }
Encoding.default_external = "utf-8" Encoding.default_external = "utf-8"

View File

@ -56,10 +56,10 @@ class ContentNegotiator extends Object {
* Set the character set encoding for this page. By default it's utf-8, but you could change it to, say, * 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. * windows-1252, to improve interoperability with extended characters being imported from windows excel.
* *
* @deprecated 3.2 Use the "ContentNegotiator.encoding" config setting instead * @deprecated 4.0 Use the "ContentNegotiator.encoding" config setting instead
*/ */
public static function set_encoding($encoding) { public static function set_encoding($encoding) {
Deprecation::notice('3.2', 'Use the "ContentNegotiator.encoding" config setting instead'); Deprecation::notice('4.0', 'Use the "ContentNegotiator.encoding" config setting instead');
Config::inst()->update('ContentNegotiator', 'encoding', $encoding); Config::inst()->update('ContentNegotiator', 'encoding', $encoding);
} }
@ -67,30 +67,30 @@ class ContentNegotiator extends Object {
* Return the character encoding set bhy ContentNegotiator::set_encoding(). It's recommended that all classes * 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. * that need to specify the character set make use of this function.
* *
* @deprecated 3.2 Use the "ContentNegotiator.encoding" config setting instead * @deprecated 4.0 Use the "ContentNegotiator.encoding" config setting instead
*/ */
public static function get_encoding() { public static function get_encoding() {
Deprecation::notice('3.2', 'Use the "ContentNegotiator.encoding" config setting instead'); Deprecation::notice('4.0', 'Use the "ContentNegotiator.encoding" config setting instead');
return Config::inst()->get('ContentNegotiator', 'encoding'); return Config::inst()->get('ContentNegotiator', 'encoding');
} }
/** /**
* Enable content negotiation for all templates, not just those with the xml header. * Enable content negotiation for all templates, not just those with the xml header.
* *
* @deprecated 3.2 Use the "ContentNegotiator.enabled" config setting instead * @deprecated 4.0 Use the "ContentNegotiator.enabled" config setting instead
*/ */
public static function enable() { public static function enable() {
Deprecation::notice('3.2', 'Use the "ContentNegotiator.enabled" config setting instead'); Deprecation::notice('4.0', 'Use the "ContentNegotiator.enabled" config setting instead');
Config::inst()->update('ContentNegotiator', 'enabled', true); Config::inst()->update('ContentNegotiator', 'enabled', true);
} }
/** /**
* Disable content negotiation for all templates, not just those with the xml header. * Disable content negotiation for all templates, not just those with the xml header.
* *
* @deprecated 3.2 Use the "ContentNegotiator.enabled" config setting instead * @deprecated 4.0 Use the "ContentNegotiator.enabled" config setting instead
*/ */
public static function disable() { public static function disable() {
Deprecation::notice('3.2', 'Use the "ContentNegotiator.enabled" config setting instead'); Deprecation::notice('4.0', 'Use the "ContentNegotiator.enabled" config setting instead');
Config::inst()->update('ContentNegotiator', 'enabled', false); Config::inst()->update('ContentNegotiator', 'enabled', false);
} }

View File

@ -84,13 +84,13 @@ class Director implements TemplateGlobalProvider {
* *
* The director is responsible for turning URLs into Controller objects. * The director is responsible for turning URLs into Controller objects.
* *
* @deprecated 3.2 Use the "Director.rules" config setting instead * @deprecated 4.0 Use the "Director.rules" config setting instead
* @param $priority The priority of the rules; higher values will get your rule checked first. We recommend * @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 100 for your site's rules. The built-in rules are priority 10, standard modules are
* priority 50. * priority 50.
*/ */
public static function addRules($priority, $rules) { public static function addRules($priority, $rules) {
Deprecation::notice('3.2', 'Use the "Director.rules" config setting instead'); Deprecation::notice('4.0', 'Use the "Director.rules" config setting instead');
Config::inst()->update('Director', 'rules', $rules); Config::inst()->update('Director', 'rules', $rules);
} }
@ -610,10 +610,10 @@ class Director implements TemplateGlobalProvider {
* Sets the root URL for the website. * Sets the root URL for the website.
* If the site isn't accessible from the URL you provide, weird things will happen. * If the site isn't accessible from the URL you provide, weird things will happen.
* *
* @deprecated 3.2 Use the "Director.alternate_base_url" config setting instead * @deprecated 4.0 Use the "Director.alternate_base_url" config setting instead
*/ */
public static function setBaseURL($baseURL) { public static function setBaseURL($baseURL) {
Deprecation::notice('3.2', 'Use the "Director.alternate_base_url" config setting instead'); Deprecation::notice('4.0', 'Use the "Director.alternate_base_url" config setting instead');
Config::inst()->update('Director', 'alternate_base_url', $baseURL); Config::inst()->update('Director', 'alternate_base_url', $baseURL);
} }
@ -630,10 +630,10 @@ class Director implements TemplateGlobalProvider {
* Sets the root folder for the website. * Sets the root folder for the website.
* If the site isn't accessible from the folder you provide, weird things will happen. * If the site isn't accessible from the folder you provide, weird things will happen.
* *
* @deprecated 3.2 Use the "Director.alternate_base_folder" config setting instead * @deprecated 4.0 Use the "Director.alternate_base_folder" config setting instead
*/ */
public static function setBaseFolder($baseFolder) { public static function setBaseFolder($baseFolder) {
Deprecation::notice('3.2', 'Use the "Director.alternate_base_folder" config setting instead'); Deprecation::notice('4.0', 'Use the "Director.alternate_base_folder" config setting instead');
Config::inst()->update('Director', 'alternate_base_folder', $baseFolder); Config::inst()->update('Director', 'alternate_base_folder', $baseFolder);
} }
@ -1023,7 +1023,7 @@ class Director implements TemplateGlobalProvider {
* Once the environment type is set, it can be checked with {@link Director::isDev()}, {@link Director::isTest()}, * Once the environment type is set, it can be checked with {@link Director::isDev()}, {@link Director::isTest()},
* and {@link Director::isLive()}. * and {@link Director::isLive()}.
* *
* @deprecated 3.2 Use the "Director.environment_type" config setting instead * @deprecated 4.0 Use the "Director.environment_type" config setting instead
* @param $et string The environment type: dev, test, or live. * @param $et string The environment type: dev, test, or live.
*/ */
public static function set_environment_type($et) { public static function set_environment_type($et) {
@ -1031,7 +1031,7 @@ class Director implements TemplateGlobalProvider {
user_error("Director::set_environment_type passed '$et'. It should be passed dev, test, or live", user_error("Director::set_environment_type passed '$et'. It should be passed dev, test, or live",
E_USER_WARNING); E_USER_WARNING);
} else { } else {
Deprecation::notice('3.2', 'Use the "Director.environment_type" config setting instead'); Deprecation::notice('4.0', 'Use the "Director.environment_type" config setting instead');
Config::inst()->update('Director', 'environment_type', $et); Config::inst()->update('Director', 'environment_type', $et);
} }
} }

View File

@ -103,7 +103,7 @@ class HTTP {
*/ */
public static function urlRewriter($content, $code) { public static function urlRewriter($content, $code) {
if(!is_callable($code)) { if(!is_callable($code)) {
Deprecation::notice(3.1, 'HTTP::urlRewriter expects a callable as the second parameter'); Deprecation::notice('4.0', 'HTTP::urlRewriter expects a callable as the second parameter');
} }
// Replace attributes // Replace attributes
@ -308,7 +308,7 @@ class HTTP {
/** /**
* Add the appropriate caching headers to the response, including If-Modified-Since / 304 handling. * 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 * @param SS_HTTPResponse $body The SS_HTTPResponse object to augment. Omitted the argument or passing a string is
* deprecated; in these cases, the headers are output directly. * deprecated; in these cases, the headers are output directly.
*/ */
public static function add_cache_headers($body = null) { public static function add_cache_headers($body = null) {
@ -328,21 +328,17 @@ class HTTP {
// us trying. // us trying.
if(headers_sent() && !$body) return; if(headers_sent() && !$body) return;
// Popuplate $responseHeaders with all the headers that we want to build // Populate $responseHeaders with all the headers that we want to build
$responseHeaders = array(); $responseHeaders = array();
$config = Config::inst(); $config = Config::inst();
$cacheControlHeaders = Config::inst()->get('HTTP', 'cache_control'); $cacheControlHeaders = Config::inst()->get('HTTP', 'cache_control');
// currently using a config setting to cancel this, seems to be so taht the CMS caches ajax requests // currently using a config setting to cancel this, seems to be so that the CMS caches ajax requests
if(function_exists('apache_request_headers') && $config->get(get_called_class(), 'cache_ajax_requests')) { if(function_exists('apache_request_headers') && $config->get(get_called_class(), 'cache_ajax_requests')) {
$requestHeaders = apache_request_headers(); $requestHeaders = array_change_key_case(apache_request_headers(), CASE_LOWER);
if(isset($requestHeaders['X-Requested-With']) && $requestHeaders['X-Requested-With']=='XMLHttpRequest') {
$cacheAge = 0;
}
// bdc: now we must check for DUMB IE6:
if(isset($requestHeaders['x-requested-with']) && $requestHeaders['x-requested-with']=='XMLHttpRequest') { if(isset($requestHeaders['x-requested-with']) && $requestHeaders['x-requested-with']=='XMLHttpRequest') {
$cacheAge = 0; $cacheAge = 0;
} }
@ -383,13 +379,16 @@ class HTTP {
foreach($cacheControlHeaders as $header => $value) { foreach($cacheControlHeaders as $header => $value) {
if(is_null($value)) { if(is_null($value)) {
unset($cacheControlHeaders[$header]); unset($cacheControlHeaders[$header]);
} elseif(is_bool($value) || $value === "true") { } elseif((is_bool($value) && $value) || $value === "true") {
$cacheControlHeaders[$header] = $header; $cacheControlHeaders[$header] = $header;
} else { } else {
$cacheControlHeaders[$header] = $header."=".$value; $cacheControlHeaders[$header] = $header."=".$value;
} }
} }
$responseHeaders['Cache-Control'] = implode(', ', $cacheControlHeaders);
unset($cacheControlHeaders, $header, $value);
if(self::$modification_date && $cacheAge > 0) { if(self::$modification_date && $cacheAge > 0) {
$responseHeaders["Last-Modified"] = self::gmt_date(self::$modification_date); $responseHeaders["Last-Modified"] = self::gmt_date(self::$modification_date);

View File

@ -162,24 +162,24 @@ class Session {
* To make cookies visible on all subdomains then the domain * To make cookies visible on all subdomains then the domain
* must be prefixed with a dot like '.php.net'. * must be prefixed with a dot like '.php.net'.
* *
* @deprecated 3.2 Use the "Session.cookie_domain" config setting instead * @deprecated 4.0 Use the "Session.cookie_domain" config setting instead
* *
* @param string $domain The domain to set * @param string $domain The domain to set
*/ */
public static function set_cookie_domain($domain) { public static function set_cookie_domain($domain) {
Deprecation::notice('3.2', 'Use the "Session.cookie_domain" config setting instead'); Deprecation::notice('4.0', 'Use the "Session.cookie_domain" config setting instead');
Config::inst()->update('Session', 'cookie_domain', $domain); Config::inst()->update('Session', 'cookie_domain', $domain);
} }
/** /**
* Get the cookie domain. * Get the cookie domain.
* *
* @deprecated 3.2 Use the "Session.cookie_domain" config setting instead * @deprecated 4.0 Use the "Session.cookie_domain" config setting instead
* *
* @return string * @return string
*/ */
public static function get_cookie_domain() { public static function get_cookie_domain() {
Deprecation::notice('3.2', 'Use the "Session.cookie_domain" config setting instead'); Deprecation::notice('4.0', 'Use the "Session.cookie_domain" config setting instead');
return Config::inst()->get('Session', 'cookie_domain'); return Config::inst()->get('Session', 'cookie_domain');
} }
@ -187,24 +187,24 @@ class Session {
* Path to set on the domain where the session cookie will work. * Path to set on the domain where the session cookie will work.
* Use a single slash ('/') for all paths on the domain. * Use a single slash ('/') for all paths on the domain.
* *
* @deprecated 3.2 Use the "Session.cookie_path" config setting instead * @deprecated 4.0 Use the "Session.cookie_path" config setting instead
* *
* @param string $path The path to set * @param string $path The path to set
*/ */
public static function set_cookie_path($path) { public static function set_cookie_path($path) {
Deprecation::notice('3.2', 'Use the "Session.cookie_path" config setting instead'); Deprecation::notice('4.0', 'Use the "Session.cookie_path" config setting instead');
Config::inst()->update('Session', 'cookie_path', $path); Config::inst()->update('Session', 'cookie_path', $path);
} }
/** /**
* Get the path on the domain where the session cookie will work. * Get the path on the domain where the session cookie will work.
* *
* @deprecated 3.2 Use the "Session.cookie_path" config setting instead * @deprecated 4.0 Use the "Session.cookie_path" config setting instead
* *
* @return string * @return string
*/ */
public static function get_cookie_path() { public static function get_cookie_path() {
Deprecation::notice('3.2', 'Use the "Session.cookie_path" config setting instead'); Deprecation::notice('4.0', 'Use the "Session.cookie_path" config setting instead');
if(Config::inst()->get('Session', 'cookie_path')) { if(Config::inst()->get('Session', 'cookie_path')) {
return Config::inst()->get('Session', 'cookie_path'); return Config::inst()->get('Session', 'cookie_path');
} else { } else {
@ -215,45 +215,46 @@ class Session {
/** /**
* Secure cookie, tells the browser to only send it over SSL. * Secure cookie, tells the browser to only send it over SSL.
* *
* @deprecated 3.2 Use the "Session.cookie_secure" config setting instead * @deprecated 4.0 Use the "Session.cookie_secure" config setting instead
* *
* @param boolean $secure * @param boolean $secure
*/ */
public static function set_cookie_secure($secure) { public static function set_cookie_secure($secure) {
Deprecation::notice('3.2', 'Use the "Session.cookie_secure" config setting instead'); Deprecation::notice('4.0', 'Use the "Session.cookie_secure" config setting instead');
Config::inst()->update('Session', 'cookie_secure', (bool)$secure); Config::inst()->update('Session', 'cookie_secure', (bool)$secure);
} }
/** /**
* Get if the cookie is secure * Get if the cookie is secure
* *
* @deprecated 3.2 Use the "Session.cookie_secure" config setting instead * @deprecated 4.0 Use the "Session.cookie_secure" config setting instead
* *
* @return boolean * @return boolean
*/ */
public static function get_cookie_secure() { public static function get_cookie_secure() {
Deprecation::notice('3.2', 'Use the "Session.cookie_secure" config setting instead'); Deprecation::notice('4.0', 'Use the "Session.cookie_secure" config setting instead');
return Config::inst()->get('Session', 'cookie_secure'); return Config::inst()->get('Session', 'cookie_secure');
} }
/** /**
* Set the session store path * Set the session store path
* *
* @deprecated 3.2 Use the "Session.session_store_path" config setting instead * @deprecated 4.0 Use the "Session.session_store_path" config setting instead
* *
* @param string $path Filesystem path to the session store * @param string $path Filesystem path to the session store
*/ */
public static function set_session_store_path($path) { public static function set_session_store_path($path) {
Deprecation::notice('3.2', 'Use the "Session.session_store_path" config setting instead'); Deprecation::notice('4.0', 'Use the "Session.session_store_path" config setting instead');
Config::inst()->update('Session', 'session_store_path', $path); Config::inst()->update('Session', 'session_store_path', $path);
} }
/** /**
* Get the session store path * Get the session store path
* @return string * @return string
* @deprecated since version 4.0
*/ */
public static function get_session_store_path() { public static function get_session_store_path() {
Deprecation::notice('3.2', 'Use the "Session.session_store_path" config setting instead'); Deprecation::notice('4.0', 'Use the "Session.session_store_path" config setting instead');
return Config::inst()->get('Session', 'session_store_path'); return Config::inst()->get('Session', 'session_store_path');
} }
@ -270,12 +271,12 @@ class Session {
* *
* Session::set_timeout is used to set the timeout value for any users whose address is not in the given IP range. * Session::set_timeout is used to set the timeout value for any users whose address is not in the given IP range.
* *
* @deprecated 3.2 Use the "Session.timeout_ips" config setting instead * @deprecated 4.0 Use the "Session.timeout_ips" config setting instead
* *
* @param array $session_ips Array of IPv4 rules. * @param array $session_ips Array of IPv4 rules.
*/ */
public static function set_timeout_ips($ips) { public static function set_timeout_ips($ips) {
Deprecation::notice('3.2', 'Use the "Session.timeout_ips" config setting instead'); Deprecation::notice('4.0', 'Use the "Session.timeout_ips" config setting instead');
Config::inst()->update('Session', 'timeout_ips', $ips); Config::inst()->update('Session', 'timeout_ips', $ips);
} }
@ -605,20 +606,20 @@ class Session {
/** /**
* Set the timeout of a Session value * Set the timeout of a Session value
* *
* @deprecated 3.2 Use the "Session.timeout" config setting instead * @deprecated 4.0 Use the "Session.timeout" config setting instead
* *
* @param int $timeout Time until a session expires in seconds. Defaults to expire when browser is closed. * @param int $timeout Time until a session expires in seconds. Defaults to expire when browser is closed.
*/ */
public static function set_timeout($timeout) { public static function set_timeout($timeout) {
Deprecation::notice('3.2', 'Use the "Session.timeout" config setting instead'); Deprecation::notice('4.0', 'Use the "Session.timeout" config setting instead');
Config::inst()->update('Session', 'timeout', (int) $timeout); Config::inst()->update('Session', 'timeout', (int) $timeout);
} }
/** /**
* @deprecated 3.2 Use the "Session.timeout" config setting instead * @deprecated 4.0 Use the "Session.timeout" config setting instead
*/ */
public static function get_timeout() { public static function get_timeout() {
Deprecation::notice('3.2', 'Use the "Session.timeout" config setting instead'); Deprecation::notice('4.0', 'Use the "Session.timeout" config setting instead');
return Config::inst()->get('Session', 'timeout'); return Config::inst()->get('Session', 'timeout');
} }
} }

View File

@ -272,7 +272,16 @@ class Injector {
* @return Injector Reference to restored active Injector instance * @return Injector Reference to restored active Injector instance
*/ */
public static function unnest() { public static function unnest() {
return self::set_inst(self::$instance->nestedFrom); if (self::inst()->nestedFrom) {
self::set_inst(self::inst()->nestedFrom);
}
else {
user_error(
"Unable to unnest root Injector, please make sure you don't have mis-matched nest/unnest",
E_USER_WARNING
);
}
return self::inst();
} }
/** /**
@ -787,10 +796,10 @@ class Injector {
/** /**
* Register a service with an explicit name * Register a service with an explicit name
* *
* @deprecated since 3.1.1 * @deprecated since 4.0
*/ */
public function registerNamedService($name, $service) { public function registerNamedService($name, $service) {
Deprecation::notice('3.1.1', 'registerNamedService is deprecated, use registerService instead'); Deprecation::notice('4.0', 'registerNamedService is deprecated, use registerService instead');
return $this->registerService($service, $name); return $this->registerService($service, $name);
} }

View File

@ -19,13 +19,14 @@ class SilverStripeServiceConfigurationLocator extends ServiceConfigurationLocato
protected $configs = array(); protected $configs = array();
public function locateConfigFor($name) { public function locateConfigFor($name) {
// Check direct or cached result // Check direct or cached result
$config = $this->configFor($name); $config = $this->configFor($name);
if($config !== null) return $config; if($config !== null) return $config;
// do parent lookup if it's a class // do parent lookup if it's a class
if (class_exists($name)) { if (class_exists($name)) {
$parents = array_reverse(array_keys(ClassInfo::ancestry($name))); $parents = array_reverse(array_values(ClassInfo::ancestry($name)));
array_shift($parents); array_shift($parents);
foreach ($parents as $parent) { foreach ($parents as $parent) {

View File

@ -61,6 +61,7 @@ class ClassInfo {
* @return array List of subclasses * @return array List of subclasses
*/ */
public static function getValidSubClasses($class = 'SiteTree', $includeUnbacked = false) { public static function getValidSubClasses($class = 'SiteTree', $includeUnbacked = false) {
$class = self::class_name($class);
$classes = DB::get_schema()->enumValuesForField($class, 'ClassName'); $classes = DB::get_schema()->enumValuesForField($class, 'ClassName');
if (!$includeUnbacked) $classes = array_filter($classes, array('ClassInfo', 'exists')); if (!$includeUnbacked) $classes = array_filter($classes, array('ClassInfo', 'exists'));
return $classes; return $classes;
@ -77,9 +78,7 @@ class ClassInfo {
public static function dataClassesFor($class) { public static function dataClassesFor($class) {
$result = array(); $result = array();
if (is_object($class)) { $class = self::class_name($class);
$class = get_class($class);
}
$classes = array_merge( $classes = array_merge(
self::ancestry($class), self::ancestry($class),
@ -101,7 +100,7 @@ class ClassInfo {
* @return string * @return string
*/ */
public static function baseDataClass($class) { public static function baseDataClass($class) {
if (is_object($class)) $class = get_class($class); $class = self::class_name($class);
if (!is_subclass_of($class, 'DataObject')) { if (!is_subclass_of($class, 'DataObject')) {
throw new InvalidArgumentException("$class is not a subclass of DataObject"); throw new InvalidArgumentException("$class is not a subclass of DataObject");
@ -125,7 +124,7 @@ class ClassInfo {
* <code> * <code>
* ClassInfo::subclassesFor('BaseClass'); * ClassInfo::subclassesFor('BaseClass');
* array( * array(
* 0 => 'BaseClass', * 'BaseClass' => 'BaseClass',
* 'ChildClass' => 'ChildClass', * 'ChildClass' => 'ChildClass',
* 'GrandChildClass' => 'GrandChildClass' * 'GrandChildClass' => 'GrandChildClass'
* ) * )
@ -135,8 +134,10 @@ class ClassInfo {
* @return array Names of all subclasses as an associative array. * @return array Names of all subclasses as an associative array.
*/ */
public static function subclassesFor($class) { public static function subclassesFor($class) {
//normalise class case
$className = self::class_name($class);
$descendants = SS_ClassLoader::instance()->getManifest()->getDescendantsOf($class); $descendants = SS_ClassLoader::instance()->getManifest()->getDescendantsOf($class);
$result = array($class => $class); $result = array($className => $className);
if ($descendants) { if ($descendants) {
return $result + ArrayLib::valuekey($descendants); return $result + ArrayLib::valuekey($descendants);
@ -145,6 +146,23 @@ class ClassInfo {
} }
} }
/**
* Convert a class name in any case and return it as it was defined in PHP
*
* eg: self::class_name('dataobJEct'); //returns 'DataObject'
*
* @param string|object $nameOrObject The classname or object you want to normalise
*
* @return string The normalised class name
*/
public static function class_name($nameOrObject) {
if (is_object($nameOrObject)) {
return get_class($nameOrObject);
}
$reflection = new ReflectionClass($nameOrObject);
return $reflection->getName();
}
/** /**
* Returns the passed class name along with all its parent class names in an * Returns the passed class name along with all its parent class names in an
* array, sorted with the root class first. * array, sorted with the root class first.
@ -154,9 +172,11 @@ class ClassInfo {
* @return array * @return array
*/ */
public static function ancestry($class, $tablesOnly = false) { public static function ancestry($class, $tablesOnly = false) {
if (!is_string($class)) $class = get_class($class); $class = self::class_name($class);
$cacheKey = $class . '_' . (string)$tablesOnly; $lClass = strtolower($class);
$cacheKey = $lClass . '_' . (string)$tablesOnly;
$parent = $class; $parent = $class;
if(!isset(self::$_cache_ancestry[$cacheKey])) { if(!isset(self::$_cache_ancestry[$cacheKey])) {
$ancestry = array(); $ancestry = array();
@ -183,7 +203,7 @@ class ClassInfo {
* Returns true if the given class implements the given interface * Returns true if the given class implements the given interface
*/ */
public static function classImplements($className, $interfaceName) { public static function classImplements($className, $interfaceName) {
return in_array($className, SS_ClassLoader::instance()->getManifest()->getImplementorsOf($interfaceName)); return in_array($className, self::implementorsOf($interfaceName));
} }
/** /**
@ -232,22 +252,26 @@ class ClassInfo {
private static $method_from_cache = array(); private static $method_from_cache = array();
public static function has_method_from($class, $method, $compclass) { public static function has_method_from($class, $method, $compclass) {
if (!isset(self::$method_from_cache[$class])) self::$method_from_cache[$class] = array(); $lClass = strtolower($class);
$lMethod = strtolower($method);
$lCompclass = strtolower($compclass);
if (!isset(self::$method_from_cache[$lClass])) self::$method_from_cache[$lClass] = array();
if (!array_key_exists($method, self::$method_from_cache[$class])) { if (!array_key_exists($lMethod, self::$method_from_cache[$lClass])) {
self::$method_from_cache[$class][$method] = false; self::$method_from_cache[$lClass][$lMethod] = false;
$classRef = new ReflectionClass($class); $classRef = new ReflectionClass($class);
if ($classRef->hasMethod($method)) { if ($classRef->hasMethod($method)) {
$methodRef = $classRef->getMethod($method); $methodRef = $classRef->getMethod($method);
self::$method_from_cache[$class][$method] = $methodRef->getDeclaringClass()->getName(); self::$method_from_cache[$lClass][$lMethod] = $methodRef->getDeclaringClass()->getName();
} }
} }
return self::$method_from_cache[$class][$method] == $compclass; return strtolower(self::$method_from_cache[$lClass][$lMethod]) == $lCompclass;
} }
/** /**
* Returns the table name in the class hierarchy which contains a given * Returns the table name in the class hierarchy which contains a given
* field column for a {@link DataObject}. If the field does not exist, this * field column for a {@link DataObject}. If the field does not exist, this
@ -259,11 +283,14 @@ class ClassInfo {
* @return string * @return string
*/ */
public static function table_for_object_field($candidateClass, $fieldName) { public static function table_for_object_field($candidateClass, $fieldName) {
if(!$candidateClass || !$fieldName) { if(!$candidateClass || !$fieldName || !is_subclass_of($candidateClass, 'DataObject')) {
return null; return null;
} }
$exists = class_exists($candidateClass); //normalise class name
$candidateClass = self::class_name($candidateClass);
$exists = self::exists($candidateClass);
while($candidateClass && $candidateClass != 'DataObject' && $exists) { while($candidateClass && $candidateClass != 'DataObject' && $exists) {
if(DataObject::has_own_table($candidateClass)) { if(DataObject::has_own_table($candidateClass)) {
@ -275,7 +302,7 @@ class ClassInfo {
} }
$candidateClass = get_parent_class($candidateClass); $candidateClass = get_parent_class($candidateClass);
$exists = class_exists($candidateClass); $exists = $candidateClass && self::exists($candidateClass);
} }
if(!$candidateClass || !$exists) { if(!$candidateClass || !$exists) {

View File

@ -237,7 +237,16 @@ class Config {
* @return Config Reference to new active Config instance * @return Config Reference to new active Config instance
*/ */
public static function unnest() { public static function unnest() {
return self::set_instance(self::$instance->nestedFrom); if (self::inst()->nestedFrom) {
self::set_instance(self::inst()->nestedFrom);
}
else {
user_error(
"Unable to unnest root Config, please make sure you don't have mis-matched nest/unnest",
E_USER_WARNING
);
}
return self::inst();
} }
/** /**
@ -681,7 +690,7 @@ class Config {
/** /**
* @package framework * @package framework
* @subpackage core * @subpackage core
* @deprecated 3.2 * @deprecated 4.0
*/ */
class Config_LRU { class Config_LRU {
const SIZE = 1000; const SIZE = 1000;
@ -693,7 +702,7 @@ class Config_LRU {
protected $c = 0; protected $c = 0;
public function __construct() { public function __construct() {
Deprecation::notice('3.2', 'Please use Config_MemCache instead', Deprecation::SCOPE_CLASS); Deprecation::notice('4.0', 'Please use Config_MemCache instead', Deprecation::SCOPE_CLASS);
if (version_compare(PHP_VERSION, '5.3.7', '<')) { if (version_compare(PHP_VERSION, '5.3.7', '<')) {
// SplFixedArray causes seg faults before PHP 5.3.7 // SplFixedArray causes seg faults before PHP 5.3.7
$this->cache = array(); $this->cache = array();

View File

@ -111,6 +111,7 @@ if(!defined('TRUSTED_PROXY')) {
*/ */
if(!isset($_SERVER['HTTP_HOST'])) { if(!isset($_SERVER['HTTP_HOST'])) {
// HTTP_HOST, REQUEST_PORT, SCRIPT_NAME, and PHP_SELF // HTTP_HOST, REQUEST_PORT, SCRIPT_NAME, and PHP_SELF
global $_FILE_TO_URL_MAPPING;
if(isset($_FILE_TO_URL_MAPPING)) { if(isset($_FILE_TO_URL_MAPPING)) {
$fullPath = $testPath = realpath($_SERVER['SCRIPT_FILENAME']); $fullPath = $testPath = realpath($_SERVER['SCRIPT_FILENAME']);
while($testPath && $testPath != '/' && !preg_match('/^[A-Z]:\\\\$/', $testPath)) { while($testPath && $testPath != '/' && !preg_match('/^[A-Z]:\\\\$/', $testPath)) {
@ -162,9 +163,10 @@ if(!isset($_SERVER['HTTP_HOST'])) {
} }
} }
if (defined('SS_ALLOWED_HOSTS')) { // Filter by configured allowed hosts
if (defined('SS_ALLOWED_HOSTS') && php_sapi_name() !== "cli") {
$all_allowed_hosts = explode(',', SS_ALLOWED_HOSTS); $all_allowed_hosts = explode(',', SS_ALLOWED_HOSTS);
if (!in_array($_SERVER['HTTP_HOST'], $all_allowed_hosts)) { if (!isset($_SERVER['HTTP_HOST']) || !in_array($_SERVER['HTTP_HOST'], $all_allowed_hosts)) {
header('HTTP/1.1 400 Invalid Host', true, 400); header('HTTP/1.1 400 Invalid Host', true, 400);
die(); die();
} }

View File

@ -27,6 +27,9 @@ error_reporting(E_ALL | E_STRICT);
*/ */
require_once dirname(__FILE__).'/Constants.php'; require_once dirname(__FILE__).'/Constants.php';
global $_increase_time_limit_max;
$_increase_time_limit_max = -1;
/** /**
* Priorities definition. These constants are used in calls to _t() as an optional argument * Priorities definition. These constants are used in calls to _t() as an optional argument
*/ */
@ -255,7 +258,7 @@ function translate_memstring($memString) {
*/ */
function increase_time_limit_to($timeLimit = null) { function increase_time_limit_to($timeLimit = null) {
$max = get_increase_time_limit_max(); $max = get_increase_time_limit_max();
if($max != -1 && $timeLimit > $max) return false; if($max != -1 && $max != null && $timeLimit > $max) return false;
if(!ini_get('safe_mode')) { if(!ini_get('safe_mode')) {
if(!$timeLimit) { if(!$timeLimit) {
@ -274,8 +277,6 @@ function increase_time_limit_to($timeLimit = null) {
} }
} }
$_increase_time_limit_max = -1;
/** /**
* Set the maximum allowed value for {@link increase_timeLimit_to()}; * Set the maximum allowed value for {@link increase_timeLimit_to()};
* *

View File

@ -584,7 +584,7 @@ abstract class Object {
$sources[] = $extensionClass; $sources[] = $extensionClass;
if(!ClassInfo::has_method_from($extensionClass, 'add_to_class', 'Extension')) { if(!ClassInfo::has_method_from($extensionClass, 'add_to_class', 'Extension')) {
Deprecation::notice('3.2.0', Deprecation::notice('4.0',
"add_to_class deprecated on $extensionClass. Use get_extra_config instead"); "add_to_class deprecated on $extensionClass. Use get_extra_config instead");
} }
@ -995,6 +995,8 @@ abstract class Object {
* @return mixed the cached data * @return mixed the cached data
*/ */
public function cacheToFile($method, $lifetime = 3600, $ID = false, $arguments = array()) { public function cacheToFile($method, $lifetime = 3600, $ID = false, $arguments = array()) {
Deprecation::notice('4.0', 'Caching methods on Object have been deprecated. Use the SS_Cache API instead.');
if(!$this->hasMethod($method)) { if(!$this->hasMethod($method)) {
throw new InvalidArgumentException("Object->cacheToFile(): the method $method does not exist to cache"); throw new InvalidArgumentException("Object->cacheToFile(): the method $method does not exist to cache");
} }
@ -1022,6 +1024,8 @@ abstract class Object {
* Clears the cache for the given cacheToFile call * Clears the cache for the given cacheToFile call
*/ */
public function clearCache($method, $ID = false, $arguments = array()) { public function clearCache($method, $ID = false, $arguments = array()) {
Deprecation::notice('4.0', 'Caching methods on Object have been deprecated. Use the SS_Cache API instead.');
$cacheName = $this->class . '_' . $method; $cacheName = $this->class . '_' . $method;
if(!is_array($arguments)) $arguments = array($arguments); if(!is_array($arguments)) $arguments = array($arguments);
if($ID) $cacheName .= '_' . $ID; if($ID) $cacheName .= '_' . $ID;
@ -1039,6 +1043,8 @@ abstract class Object {
* @return mixed * @return mixed
*/ */
protected function loadCache($cache, $lifetime = 3600) { protected function loadCache($cache, $lifetime = 3600) {
Deprecation::notice('4.0', 'Caching methods on Object have been deprecated. Use the SS_Cache API instead.');
$path = TEMP_FOLDER . '/' . $this->sanitiseCachename($cache); $path = TEMP_FOLDER . '/' . $this->sanitiseCachename($cache);
if(!isset($_REQUEST['flush']) && file_exists($path) && (filemtime($path) + $lifetime) > time()) { if(!isset($_REQUEST['flush']) && file_exists($path) && (filemtime($path) + $lifetime) > time()) {
@ -1055,6 +1061,7 @@ abstract class Object {
* @param mixed $data data to save (must be serializable) * @param mixed $data data to save (must be serializable)
*/ */
protected function saveCache($cache, $data) { protected function saveCache($cache, $data) {
Deprecation::notice('4.0', 'Caching methods on Object have been deprecated. Use the SS_Cache API instead.');
file_put_contents(TEMP_FOLDER . '/' . $this->sanitiseCachename($cache), serialize($data)); file_put_contents(TEMP_FOLDER . '/' . $this->sanitiseCachename($cache), serialize($data));
} }
@ -1065,6 +1072,7 @@ abstract class Object {
* @return string the name with all special cahracters replaced with underscores * @return string the name with all special cahracters replaced with underscores
*/ */
protected function sanitiseCachename($name) { protected function sanitiseCachename($name) {
Deprecation::notice('4.0', 'Caching methods on Object have been deprecated. Use the SS_Cache API instead.');
return str_replace(array('~', '.', '/', '!', ' ', "\n", "\r", "\t", '\\', ':', '"', '\'', ';'), '_', $name); return str_replace(array('~', '.', '/', '!', ' ', "\n", "\r", "\t", '\\', ':', '"', '\'', ';'), '_', $name);
} }

View File

@ -106,6 +106,33 @@ class SS_ClassManifest {
)); ));
} }
/**
* Create a {@link TokenisedRegularExpression} that extracts the namespaces imported with the 'use' keyword
*
* This searches symbols for a `use` followed by 1 or more namespaces which are optionally aliased using the `as`
* keyword. The relevant matching tokens are added one-by-one into an array (using `save_to` param).
*
* eg: use Namespace\ClassName as Alias, OtherNamespace\ClassName;
*
* @return TokenisedRegularExpression
*/
public static function get_imported_namespace_parser() {
return new TokenisedRegularExpression(array(
0 => T_USE,
1 => T_WHITESPACE,
2 => array(T_NS_SEPARATOR, 'save_to' => 'importString[]', 'optional' => true),
3 => array(T_STRING, 'save_to' => 'importString[]', 'can_jump_to' => array(2, 8)),
4 => array(T_WHITESPACE, 'save_to' => 'importString[]'),
5 => array(T_AS, 'save_to' => 'importString[]'),
6 => array(T_WHITESPACE, 'save_to' => 'importString[]'),
7 => array(T_STRING, 'save_to' => 'importString[]'),
8 => array(T_WHITESPACE, 'optional' => true),
9 => array(',', 'save_to' => 'importString[]', 'optional' => true, 'can_jump_to' => 2),
10 => array(T_WHITESPACE, 'optional' => true, 'can_jump_to' => 2),
11 => ';',
));
}
/** /**
* Constructs and initialises a new class manifest, either loading the data * Constructs and initialises a new class manifest, either loading the data
* from the cache or re-scanning for classes. * from the cache or re-scanning for classes.
@ -333,6 +360,124 @@ class SS_ClassManifest {
} }
} }
/**
* Find a the full namespaced declaration of a class (or interface) from a list of candidate imports
*
* This is typically used to determine the full class name in classes that have imported namesapced symbols (having
* used the `use` keyword)
*
* NB: remember the '\\' is an escaped backslash and is interpreted as a single \
*
* @param string $class The class (or interface) name to find in the candidate imports
* @param string $namespace The namespace that was declared for the classes definition (if there was one)
* @param array $imports The list of imported symbols (Classes or Interfaces) to test against
*
* @return string The fully namespaced class name
*/
protected function findClassOrInterfaceFromCandidateImports($class, $namespace = '', $imports = array()) {
//normalise the namespace
$namespace = rtrim($namespace, '\\');
//by default we'll use the $class as our candidate
$candidateClass = $class;
if (!$class) {
return $candidateClass;
}
//if the class starts with a \ then it is explicitly in the global namespace and we don't need to do
// anything else
if (substr($class, 0, 1) == '\\') {
$candidateClass = substr($class, 1);
return $candidateClass;
}
//if there's a namespace, starting assumption is the class is defined in that namespace
if ($namespace) {
$candidateClass = $namespace . '\\' . $class;
}
if (empty($imports)) {
return $candidateClass;
}
//normalised class name (PHP is case insensitive for symbols/namespaces
$lClass = strtolower($class);
//go through all the imports and see if the class exists within one of them
foreach ($imports as $alias => $import) {
//normalise import
$import = trim($import, '\\');
//if there is no string key, then there was no declared alias - we'll use the main declaration
if (is_int($alias)) {
$alias = strtolower($import);
} else {
$alias = strtolower($alias);
}
//exact match? Then it's a class in the global namespace that was imported OR it's an alias of
// another namespace
// or if it ends with the \ClassName then it's the class we are looking for
if ($lClass == $alias
|| substr_compare(
$alias,
'\\' . $lClass,
strlen($alias) - strlen($lClass) - 1,
// -1 because the $lClass length is 1 longer due to \
strlen($alias)
) === 0
) {
$candidateClass = $import;
break;
}
}
return $candidateClass;
}
/**
* Return an array of array($alias => $import) from tokenizer's tokens of a PHP file
*
* NB: If there is no alias we don't set a key to the array
*
* @param array $tokens The parsed tokens from tokenizer's parsing of a PHP file
*
* @return array The array of imports as (optional) $alias => $import
*/
protected function getImportsFromTokens($tokens) {
//parse out the imports
$imports = self::get_imported_namespace_parser()->findAll($tokens);
//if there are any imports, clean them up
// imports come to us as array('importString' => array([array of matching tokens]))
// we need to join this nested array into a string and split out the alias and the import
if (!empty($imports)) {
$cleanImports = array();
foreach ($imports as $import) {
if (!empty($import['importString'])) {
//join the array up into a string
$importString = implode('', $import['importString']);
//split at , to get each import declaration
$importSet = explode(',', $importString);
foreach ($importSet as $importDeclaration) {
//split at ' as ' (any case) to see if we are aliasing the namespace
$importDeclaration = preg_split('/\s+as\s+/i', $importDeclaration);
//shift off the fully namespaced import
$qualifiedImport = array_shift($importDeclaration);
//if there are still items in the array, it's the alias
if (!empty($importDeclaration)) {
$cleanImports[array_shift($importDeclaration)] = $qualifiedImport;
}
else {
$cleanImports[] = $qualifiedImport;
}
}
}
}
$imports = $cleanImports;
}
return $imports;
}
public function handleFile($basename, $pathname, $depth) { public function handleFile($basename, $pathname, $depth) {
if ($basename == self::CONF_FILE) { if ($basename == self::CONF_FILE) {
$this->configs[] = $pathname; $this->configs[] = $pathname;
@ -342,6 +487,7 @@ class SS_ClassManifest {
$classes = null; $classes = null;
$interfaces = null; $interfaces = null;
$namespace = null; $namespace = null;
$imports = null;
// The results of individual file parses are cached, since only a few // The results of individual file parses are cached, since only a few
// files will have changed and TokenisedRegularExpression is quite // files will have changed and TokenisedRegularExpression is quite
@ -352,14 +498,17 @@ class SS_ClassManifest {
if ($data = $this->cache->load($key)) { if ($data = $this->cache->load($key)) {
$valid = ( $valid = (
isset($data['classes']) && isset($data['interfaces']) && isset($data['namespace']) isset($data['classes']) && is_array($data['classes'])
&& is_array($data['classes']) && is_array($data['interfaces']) && is_string($data['namespace']) && isset($data['interfaces']) && is_array($data['interfaces'])
&& isset($data['namespace']) && is_string($data['namespace'])
&& isset($data['imports']) && is_array($data['imports'])
); );
if ($valid) { if ($valid) {
$classes = $data['classes']; $classes = $data['classes'];
$interfaces = $data['interfaces']; $interfaces = $data['interfaces'];
$namespace = $data['namespace']; $namespace = $data['namespace'];
$imports = $data['imports'];
} }
} }
@ -367,28 +516,52 @@ class SS_ClassManifest {
$tokens = token_get_all($file); $tokens = token_get_all($file);
$classes = self::get_namespaced_class_parser()->findAll($tokens); $classes = self::get_namespaced_class_parser()->findAll($tokens);
$namespace = self::get_namespace_parser()->findAll($tokens); $namespace = self::get_namespace_parser()->findAll($tokens);
if($namespace) { if($namespace) {
$namespace = implode('', $namespace[0]['namespaceName']) . '\\'; $namespace = implode('', $namespace[0]['namespaceName']);
} else { } else {
$namespace = ''; $namespace = '';
} }
$imports = $this->getImportsFromTokens($tokens);
$interfaces = self::get_interface_parser()->findAll($tokens); $interfaces = self::get_interface_parser()->findAll($tokens);
$cache = array('classes' => $classes, 'interfaces' => $interfaces, 'namespace' => $namespace); $cache = array(
'classes' => $classes,
'interfaces' => $interfaces,
'namespace' => $namespace,
'imports' => $imports
);
$this->cache->save($cache, $key); $this->cache->save($cache, $key);
} }
foreach ($classes as $class) { foreach ($classes as $class) {
$name = $namespace . $class['className']; $name = $class['className'];
$extends = isset($class['extends']) ? implode('', $class['extends']) : null; if ($namespace) {
$namespace = rtrim($namespace, '\\');
$name = $namespace . '\\' . $name;
}
$extends = isset($class['extends']) ? implode('', $class['extends']) : null;
$implements = isset($class['interfaces']) ? $class['interfaces'] : null; $implements = isset($class['interfaces']) ? $class['interfaces'] : null;
if($extends && $extends[0] != '\\') { if ($extends) {
$extends = $namespace . $extends; $extends = $this->findClassOrInterfaceFromCandidateImports($extends, $namespace, $imports);
} elseif($extends) { }
$extends = substr($extends, 1);
if (!empty($implements)) {
//join all the tokens
$implements = implode('', $implements);
//split at comma
$implements = explode(',', $implements);
//normalise interfaces
foreach ($implements as &$interface) {
$interface = $this->findClassOrInterfaceFromCandidateImports($interface, $namespace, $imports);
}
//release the var name
unset($interface);
} }
$lowercaseName = strtolower($name); $lowercaseName = strtolower($name);
@ -414,32 +587,24 @@ class SS_ClassManifest {
} }
if ($implements) { if ($implements) {
$interface = $namespace; foreach ($implements as $interface) {
for($i = 0; $i < count($implements); ++$i) { $interface = strtolower($interface);
if($implements[$i] == ',') {
$interface = $namespace;
continue;
}
if($implements[$i] == '\\' && $interface == $namespace) {
$interface = '';
} else {
$interface .= $implements[$i];
}
if($i == count($implements)-1 || $implements[$i+1] == ',') {
$interface = strtolower($interface);
if (!isset($this->implementors[$interface])) { if (!isset($this->implementors[$interface])) {
$this->implementors[$interface] = array($name); $this->implementors[$interface] = array($name);
} else { } else {
$this->implementors[$interface][] = $name; $this->implementors[$interface][] = $name;
}
} }
} }
} }
} }
$interfaceBase = '';
if ($namespace) {
$interfaceBase = $namespace . '\\';
}
foreach ($interfaces as $interface) { foreach ($interfaces as $interface) {
$this->interfaces[strtolower($namespace . $interface['interfaceName'])] = $pathname; $this->interfaces[strtolower($interfaceBase . $interface['interfaceName'])] = $pathname;
} }
} }

View File

@ -79,7 +79,7 @@ class SS_ConfigStaticManifest {
$static = $this->statics[$class][$name]; $static = $this->statics[$class][$name];
if ($static['access'] != T_PRIVATE) { if ($static['access'] != T_PRIVATE) {
Deprecation::notice('3.2.0', "Config static $class::\$$name must be marked as private", Deprecation::notice('4.0', "Config static $class::\$$name must be marked as private",
Deprecation::SCOPE_GLOBAL); Deprecation::SCOPE_GLOBAL);
// Don't warn more than once per static // Don't warn more than once per static
$this->statics[$class][$name]['access'] = T_PRIVATE; $this->statics[$class][$name]['access'] = T_PRIVATE;

View File

@ -16,7 +16,7 @@ Used in side panels and action tabs
.backlink { padding-left: 12px; } .backlink { padding-left: 12px; }
body.cms.ss-uploadfield-edit-iframe, .composite.ss-assetuploadfield .details fieldset { overflow: auto; background: #E2E2E2; } body.cms.ss-uploadfield-edit-iframe, .composite.ss-assetuploadfield .details fieldset { overflow: auto; background: #E2E2E2; }
body.cms.ss-uploadfield-edit-iframe span.readonly, .composite.ss-assetuploadfield .details fieldset span.readonly { font-style: italic; color: #777777; text-shadow: 0px 1px 0px #fff; } body.cms.ss-uploadfield-edit-iframe span.readonly, .composite.ss-assetuploadfield .details fieldset span.readonly { font-style: italic; color: #9ba5ae; text-shadow: 0px 1px 0px #fff; }
body.cms.ss-uploadfield-edit-iframe .fieldholder-small label, .composite.ss-assetuploadfield .details fieldset .fieldholder-small label { margin-left: 0; } body.cms.ss-uploadfield-edit-iframe .fieldholder-small label, .composite.ss-assetuploadfield .details fieldset .fieldholder-small label { margin-left: 0; }
.composite.ss-assetuploadfield .details fieldset { padding: 16px; } .composite.ss-assetuploadfield .details fieldset { padding: 16px; }
@ -30,7 +30,7 @@ body.cms.ss-uploadfield-edit-iframe .fieldholder-small label, .composite.ss-asse
.ss-assetuploadfield .fileOverview .uploadStatus .state { float: left; font-size: 16px; font-weight: bold; line-height: 1.1em; } .ss-assetuploadfield .fileOverview .uploadStatus .state { float: left; font-size: 16px; font-weight: bold; line-height: 1.1em; }
.ss-assetuploadfield .fileOverview .uploadStatus .details { opacity: 0.9; float: right; } .ss-assetuploadfield .fileOverview .uploadStatus .details { opacity: 0.9; float: right; }
.ss-assetuploadfield .ss-uploadfield-item-actions.edit-all { clear: both; position: relative; z-index: 9; } .ss-assetuploadfield .ss-uploadfield-item-actions.edit-all { clear: both; position: relative; z-index: 9; }
.ss-assetuploadfield .ss-uploadfield-item-actions.edit-all .ss-uploadfield-item-edit-all { z-index: 8; position: absolute; top: -33px; padding: 0; background: none; border: 0; right: 0; -moz-box-shadow: none; -webkit-box-shadow: none; box-shadow: none; color: #444; } .ss-assetuploadfield .ss-uploadfield-item-actions.edit-all .ss-uploadfield-item-edit-all { z-index: 8; position: absolute; top: -33px; padding: 0; background: none; border: 0; right: 0; -moz-box-shadow: none; -webkit-box-shadow: none; box-shadow: none; color: #66727d; }
.ss-assetuploadfield .ss-uploadfield-item-actions.edit-all .ss-uploadfield-item-edit-all:hover { color: #1e7cba; } .ss-assetuploadfield .ss-uploadfield-item-actions.edit-all .ss-uploadfield-item-edit-all:hover { color: #1e7cba; }
.ss-assetuploadfield .ss-uploadfield-files { margin: 0; padding: 0; clear: both; } .ss-assetuploadfield .ss-uploadfield-files { margin: 0; padding: 0; clear: both; }
.ss-assetuploadfield .ss-uploadfield-files .ss-uploadfield-item { border: 1px solid #b3b3b3; -moz-border-radius: 5px; -webkit-border-radius: 5px; border-radius: 5px; -moz-background-clip: padding; -o-background-clip: padding-box; -webkit-background-clip: padding; background-clip: padding-box; margin: 0 0 5px; padding: 0; overflow: hidden; position: relative; } .ss-assetuploadfield .ss-uploadfield-files .ss-uploadfield-item { border: 1px solid #b3b3b3; -moz-border-radius: 5px; -webkit-border-radius: 5px; border-radius: 5px; -moz-background-clip: padding; -o-background-clip: padding-box; -webkit-background-clip: padding; background-clip: padding-box; margin: 0 0 5px; padding: 0; overflow: hidden; position: relative; }
@ -41,8 +41,8 @@ body.cms.ss-uploadfield-edit-iframe .fieldholder-small label, .composite.ss-asse
.ss-assetuploadfield .ss-uploadfield-files .ui-state-error .ss-uploadfield-item-info { background-color: #c11f1d; padding-right: 130px; background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2MxMWYxZCIvPjxzdG9wIG9mZnNldD0iNCUiIHN0b3AtY29sb3I9IiNiZjFkMWIiLz48c3RvcCBvZmZzZXQ9IjglIiBzdG9wLWNvbG9yPSIjYjcxYjFjIi8+PHN0b3Agb2Zmc2V0PSIxNSUiIHN0b3AtY29sb3I9IiNiNjFlMWQiLz48c3RvcCBvZmZzZXQ9IjI3JSIgc3RvcC1jb2xvcj0iI2IxMWQxZCIvPjxzdG9wIG9mZnNldD0iMzElIiBzdG9wLWNvbG9yPSIjYWIxZDFjIi8+PHN0b3Agb2Zmc2V0PSI0MiUiIHN0b3AtY29sb3I9IiNhNTFiMWIiLz48c3RvcCBvZmZzZXQ9IjQ2JSIgc3RvcC1jb2xvcj0iIzlmMWIxOSIvPjxzdG9wIG9mZnNldD0iNTAlIiBzdG9wLWNvbG9yPSIjOWYxYjE5Ii8+PHN0b3Agb2Zmc2V0PSI1NCUiIHN0b3AtY29sb3I9IiM5OTFjMWEiLz48c3RvcCBvZmZzZXQ9IjU4JSIgc3RvcC1jb2xvcj0iIzk3MWExOCIvPjxzdG9wIG9mZnNldD0iNjIlIiBzdG9wLWNvbG9yPSIjOTExYjFiIi8+PHN0b3Agb2Zmc2V0PSI2NSUiIHN0b3AtY29sb3I9IiM5MTFiMWIiLz48c3RvcCBvZmZzZXQ9Ijg4JSIgc3RvcC1jb2xvcj0iIzdlMTgxNiIvPjxzdG9wIG9mZnNldD0iOTIlIiBzdG9wLWNvbG9yPSIjNzcxOTE5Ii8+PHN0b3Agb2Zmc2V0PSIxMDAlIiBzdG9wLWNvbG9yPSIjNzMxODE3Ii8+PC9saW5lYXJHcmFkaWVudD48L2RlZnM+PHJlY3QgeD0iMCIgeT0iMCIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0idXJsKCNncmFkKSIgLz48L3N2Zz4g'); background-size: 100%; background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #c11f1d), color-stop(4%, #bf1d1b), color-stop(8%, #b71b1c), color-stop(15%, #b61e1d), color-stop(27%, #b11d1d), color-stop(31%, #ab1d1c), color-stop(42%, #a51b1b), color-stop(46%, #9f1b19), color-stop(50%, #9f1b19), color-stop(54%, #991c1a), color-stop(58%, #971a18), color-stop(62%, #911b1b), color-stop(65%, #911b1b), color-stop(88%, #7e1816), color-stop(92%, #771919), color-stop(100%, #731817)); background-image: -moz-linear-gradient(top, #c11f1d 0%, #bf1d1b 4%, #b71b1c 8%, #b61e1d 15%, #b11d1d 27%, #ab1d1c 31%, #a51b1b 42%, #9f1b19 46%, #9f1b19 50%, #991c1a 54%, #971a18 58%, #911b1b 62%, #911b1b 65%, #7e1816 88%, #771919 92%, #731817 100%); background-image: -webkit-linear-gradient(top, #c11f1d 0%, #bf1d1b 4%, #b71b1c 8%, #b61e1d 15%, #b11d1d 27%, #ab1d1c 31%, #a51b1b 42%, #9f1b19 46%, #9f1b19 50%, #991c1a 54%, #971a18 58%, #911b1b 62%, #911b1b 65%, #7e1816 88%, #771919 92%, #731817 100%); background-image: linear-gradient(to bottom, #c11f1d 0%, #bf1d1b 4%, #b71b1c 8%, #b61e1d 15%, #b11d1d 27%, #ab1d1c 31%, #a51b1b 42%, #9f1b19 46%, #9f1b19 50%, #991c1a 54%, #971a18 58%, #911b1b 62%, #911b1b 65%, #7e1816 88%, #771919 92%, #731817 100%); } .ss-assetuploadfield .ss-uploadfield-files .ui-state-error .ss-uploadfield-item-info { background-color: #c11f1d; padding-right: 130px; background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2MxMWYxZCIvPjxzdG9wIG9mZnNldD0iNCUiIHN0b3AtY29sb3I9IiNiZjFkMWIiLz48c3RvcCBvZmZzZXQ9IjglIiBzdG9wLWNvbG9yPSIjYjcxYjFjIi8+PHN0b3Agb2Zmc2V0PSIxNSUiIHN0b3AtY29sb3I9IiNiNjFlMWQiLz48c3RvcCBvZmZzZXQ9IjI3JSIgc3RvcC1jb2xvcj0iI2IxMWQxZCIvPjxzdG9wIG9mZnNldD0iMzElIiBzdG9wLWNvbG9yPSIjYWIxZDFjIi8+PHN0b3Agb2Zmc2V0PSI0MiUiIHN0b3AtY29sb3I9IiNhNTFiMWIiLz48c3RvcCBvZmZzZXQ9IjQ2JSIgc3RvcC1jb2xvcj0iIzlmMWIxOSIvPjxzdG9wIG9mZnNldD0iNTAlIiBzdG9wLWNvbG9yPSIjOWYxYjE5Ii8+PHN0b3Agb2Zmc2V0PSI1NCUiIHN0b3AtY29sb3I9IiM5OTFjMWEiLz48c3RvcCBvZmZzZXQ9IjU4JSIgc3RvcC1jb2xvcj0iIzk3MWExOCIvPjxzdG9wIG9mZnNldD0iNjIlIiBzdG9wLWNvbG9yPSIjOTExYjFiIi8+PHN0b3Agb2Zmc2V0PSI2NSUiIHN0b3AtY29sb3I9IiM5MTFiMWIiLz48c3RvcCBvZmZzZXQ9Ijg4JSIgc3RvcC1jb2xvcj0iIzdlMTgxNiIvPjxzdG9wIG9mZnNldD0iOTIlIiBzdG9wLWNvbG9yPSIjNzcxOTE5Ii8+PHN0b3Agb2Zmc2V0PSIxMDAlIiBzdG9wLWNvbG9yPSIjNzMxODE3Ii8+PC9saW5lYXJHcmFkaWVudD48L2RlZnM+PHJlY3QgeD0iMCIgeT0iMCIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0idXJsKCNncmFkKSIgLz48L3N2Zz4g'); background-size: 100%; background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #c11f1d), color-stop(4%, #bf1d1b), color-stop(8%, #b71b1c), color-stop(15%, #b61e1d), color-stop(27%, #b11d1d), color-stop(31%, #ab1d1c), color-stop(42%, #a51b1b), color-stop(46%, #9f1b19), color-stop(50%, #9f1b19), color-stop(54%, #991c1a), color-stop(58%, #971a18), color-stop(62%, #911b1b), color-stop(65%, #911b1b), color-stop(88%, #7e1816), color-stop(92%, #771919), color-stop(100%, #731817)); background-image: -moz-linear-gradient(top, #c11f1d 0%, #bf1d1b 4%, #b71b1c 8%, #b61e1d 15%, #b11d1d 27%, #ab1d1c 31%, #a51b1b 42%, #9f1b19 46%, #9f1b19 50%, #991c1a 54%, #971a18 58%, #911b1b 62%, #911b1b 65%, #7e1816 88%, #771919 92%, #731817 100%); background-image: -webkit-linear-gradient(top, #c11f1d 0%, #bf1d1b 4%, #b71b1c 8%, #b61e1d 15%, #b11d1d 27%, #ab1d1c 31%, #a51b1b 42%, #9f1b19 46%, #9f1b19 50%, #991c1a 54%, #971a18 58%, #911b1b 62%, #911b1b 65%, #7e1816 88%, #771919 92%, #731817 100%); background-image: linear-gradient(to bottom, #c11f1d 0%, #bf1d1b 4%, #b71b1c 8%, #b61e1d 15%, #b11d1d 27%, #ab1d1c 31%, #a51b1b 42%, #9f1b19 46%, #9f1b19 50%, #991c1a 54%, #971a18 58%, #911b1b 62%, #911b1b 65%, #7e1816 88%, #771919 92%, #731817 100%); }
.ss-assetuploadfield .ss-uploadfield-files .ui-state-error .ss-uploadfield-item-info .ss-uploadfield-item-name { width: 100%; cursor: default; background: #bcb9b9; background: rgba(201, 198, 198, 0.9); } .ss-assetuploadfield .ss-uploadfield-files .ui-state-error .ss-uploadfield-item-info .ss-uploadfield-item-name { width: 100%; cursor: default; background: #bcb9b9; background: rgba(201, 198, 198, 0.9); }
.ss-assetuploadfield .ss-uploadfield-files .ui-state-error .ss-uploadfield-item-info .ss-uploadfield-item-name .name { text-shadow: 0px 1px 0px rgba(255, 255, 255, 0.7); } .ss-assetuploadfield .ss-uploadfield-files .ui-state-error .ss-uploadfield-item-info .ss-uploadfield-item-name .name { text-shadow: 0px 1px 0px rgba(255, 255, 255, 0.7); }
.ss-assetuploadfield .ss-uploadfield-files .ui-state-warning .ss-uploadfield-item-info { background-color: #E9D104; background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2U1ZDMzYiIvPjxzdG9wIG9mZnNldD0iOCUiIHN0b3AtY29sb3I9IiNlMmNlMjQiLz48c3RvcCBvZmZzZXQ9IjUwJSIgc3RvcC1jb2xvcj0iI2QxYmUxYyIvPjxzdG9wIG9mZnNldD0iNTQlIiBzdG9wLWNvbG9yPSIjZDFiYzFiIi8+PHN0b3Agb2Zmc2V0PSI5NiUiIHN0b3AtY29sb3I9IiNkMDlhMWEiLz48c3RvcCBvZmZzZXQ9IjEwMCUiIHN0b3AtY29sb3I9IiNjZTg3MTkiLz48L2xpbmVhckdyYWRpZW50PjwvZGVmcz48cmVjdCB4PSIwIiB5PSIwIiB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSJ1cmwoI2dyYWQpIiAvPjwvc3ZnPiA='); background-size: 100%; background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #e5d33b), color-stop(8%, #e2ce24), color-stop(50%, #d1be1c), color-stop(54%, #d1bc1b), color-stop(96%, #d09a1a), color-stop(100%, #ce8719)); background-image: -moz-linear-gradient(top, #e5d33b 0%, #e2ce24 8%, #d1be1c 50%, #d1bc1b 54%, #d09a1a 96%, #ce8719 100%); background-image: -webkit-linear-gradient(top, #e5d33b 0%, #e2ce24 8%, #d1be1c 50%, #d1bc1b 54%, #d09a1a 96%, #ce8719 100%); background-image: linear-gradient(to bottom, #e5d33b 0%, #e2ce24 8%, #d1be1c 50%, #d1bc1b 54%, #d09a1a 96%, #ce8719 100%); } .ss-assetuploadfield .ss-uploadfield-files .ui-state-warning .ss-uploadfield-item-info { background-color: #E9D104; background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2U1ZDMzYiIvPjxzdG9wIG9mZnNldD0iOCUiIHN0b3AtY29sb3I9IiNlMmNlMjQiLz48c3RvcCBvZmZzZXQ9IjUwJSIgc3RvcC1jb2xvcj0iI2QxYmUxYyIvPjxzdG9wIG9mZnNldD0iNTQlIiBzdG9wLWNvbG9yPSIjZDFiZDFjIi8+PHN0b3Agb2Zmc2V0PSI5NiUiIHN0b3AtY29sb3I9IiNkMDlhMWEiLz48c3RvcCBvZmZzZXQ9IjEwMCUiIHN0b3AtY29sb3I9IiNjZjg3MWEiLz48L2xpbmVhckdyYWRpZW50PjwvZGVmcz48cmVjdCB4PSIwIiB5PSIwIiB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSJ1cmwoI2dyYWQpIiAvPjwvc3ZnPiA='); background-size: 100%; background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #e5d33b), color-stop(8%, #e2ce24), color-stop(50%, #d1be1c), color-stop(54%, #d1bd1c), color-stop(96%, #d09a1a), color-stop(100%, #cf871a)); background-image: -moz-linear-gradient(top, #e5d33b 0%, #e2ce24 8%, #d1be1c 50%, #d1bd1c 54%, #d09a1a 96%, #cf871a 100%); background-image: -webkit-linear-gradient(top, #e5d33b 0%, #e2ce24 8%, #d1be1c 50%, #d1bd1c 54%, #d09a1a 96%, #cf871a 100%); background-image: linear-gradient(to bottom, #e5d33b 0%, #e2ce24 8%, #d1be1c 50%, #d1bd1c 54%, #d09a1a 96%, #cf871a 100%); }
.ss-assetuploadfield .ss-uploadfield-files .ss-uploadfield-item-name { position: relative; z-index: 1; margin: 3px 0 3px 50px; width: 50%; color: #5e5e5e; background: #eeeded; background: rgba(255, 255, 255, 0.8); -moz-border-radius: 3px; -webkit-border-radius: 3px; border-radius: 3px; line-height: 24px; height: 22px; padding: 0 5px; text-align: left; cursor: pointer; display: table; table-layout: fixed; } .ss-assetuploadfield .ss-uploadfield-files .ss-uploadfield-item-name { position: relative; z-index: 1; margin: 3px 0 3px 50px; width: 50%; color: #7f8c97; background: #eeeded; background: rgba(255, 255, 255, 0.8); -moz-border-radius: 3px; -webkit-border-radius: 3px; border-radius: 3px; line-height: 24px; height: 22px; padding: 0 5px; text-align: left; cursor: pointer; display: table; table-layout: fixed; }
.ss-assetuploadfield .ss-uploadfield-files .ss-uploadfield-item-name .name { text-shadow: 0px 1px 0px rgba(255, 255, 255, 0.5); display: inline; float: left; max-width: 50%; font-weight: normal; padding: 0 5px 0 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; -o-text-overflow: ellipsis; } .ss-assetuploadfield .ss-uploadfield-files .ss-uploadfield-item-name .name { text-shadow: 0px 1px 0px rgba(255, 255, 255, 0.5); display: inline; float: left; max-width: 50%; font-weight: normal; padding: 0 5px 0 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; -o-text-overflow: ellipsis; }
.ss-assetuploadfield .ss-uploadfield-files .ss-uploadfield-item-name .ss-uploadfield-item-status { position: relative; float: right; padding: 0 0 0 5px; max-width: 30%; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; -o-text-overflow: ellipsis; text-shadow: 0px 1px 0px rgba(255, 255, 255, 0.5); } .ss-assetuploadfield .ss-uploadfield-files .ss-uploadfield-item-name .ss-uploadfield-item-status { position: relative; float: right; padding: 0 0 0 5px; max-width: 30%; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; -o-text-overflow: ellipsis; text-shadow: 0px 1px 0px rgba(255, 255, 255, 0.5); }
.ss-assetuploadfield .ss-uploadfield-files .ss-uploadfield-item-name .ss-uploadfield-item-status.ui-state-error-text { text-shadow: 0px 1px 0px rgba(255, 255, 255, 0.6); color: #cc0000; } .ss-assetuploadfield .ss-uploadfield-files .ss-uploadfield-item-name .ss-uploadfield-item-status.ui-state-error-text { text-shadow: 0px 1px 0px rgba(255, 255, 255, 0.6); color: #cc0000; }
@ -78,7 +78,7 @@ body.cms.ss-uploadfield-edit-iframe .fieldholder-small label, .composite.ss-asse
.ss-insert-media .ss-assetuploadfield .ss-uploadfield-addfile .ss-uploadfield-item-uploador { font-size: 18px; margin-top: -5px; } .ss-insert-media .ss-assetuploadfield .ss-uploadfield-addfile .ss-uploadfield-item-uploador { font-size: 18px; margin-top: -5px; }
.ss-assetuploadfield .ss-uploadfield-addfile .ss-uploadfield-dropzone { margin-top: 9px; padding: 8px 0; -moz-border-radius: 13px; -webkit-border-radius: 13px; border-radius: 13px; -moz-box-shadow: rgba(128, 128, 128, 0.4) 0 0 4px 0 inset, 0 1px 0 #FAFAFA; -webkit-box-shadow: rgba(128, 128, 128, 0.4) 0 0 4px 0 inset, 0 1px 0 #FAFAFA; box-shadow: rgba(128, 128, 128, 0.4) 0 0 4px 0 inset, 0 1px 0 #FAFAFA; border: 2px dashed #808080; background: #d4dbe0; display: none; height: 54px; min-width: 280px; float: left; text-align: center; } .ss-assetuploadfield .ss-uploadfield-addfile .ss-uploadfield-dropzone { margin-top: 9px; padding: 8px 0; -moz-border-radius: 13px; -webkit-border-radius: 13px; border-radius: 13px; -moz-box-shadow: rgba(128, 128, 128, 0.4) 0 0 4px 0 inset, 0 1px 0 #FAFAFA; -webkit-box-shadow: rgba(128, 128, 128, 0.4) 0 0 4px 0 inset, 0 1px 0 #FAFAFA; box-shadow: rgba(128, 128, 128, 0.4) 0 0 4px 0 inset, 0 1px 0 #FAFAFA; border: 2px dashed #808080; background: #d4dbe0; display: none; height: 54px; min-width: 280px; float: left; text-align: center; }
.ss-assetuploadfield .ss-uploadfield-addfile .ss-uploadfield-dropzone.active.hover { -moz-box-shadow: rgba(255, 255, 255, 0.6) 0 0 3px 2px inset; -webkit-box-shadow: rgba(255, 255, 255, 0.6) 0 0 3px 2px inset; box-shadow: rgba(255, 255, 255, 0.6) 0 0 3px 2px inset; } .ss-assetuploadfield .ss-uploadfield-addfile .ss-uploadfield-dropzone.active.hover { -moz-box-shadow: rgba(255, 255, 255, 0.6) 0 0 3px 2px inset; -webkit-box-shadow: rgba(255, 255, 255, 0.6) 0 0 3px 2px inset; box-shadow: rgba(255, 255, 255, 0.6) 0 0 3px 2px inset; }
.ss-assetuploadfield .ss-uploadfield-addfile .ss-uploadfield-dropzone div { color: #5e5e5e; text-shadow: 0px 1px 0px #fff; background: url("../images/upload.png") 0 10px no-repeat; z-index: 1; padding: 6px 48px 0; line-height: 25px; font-size: 20px; font-weight: bold; display: inline-block; } .ss-assetuploadfield .ss-uploadfield-addfile .ss-uploadfield-dropzone div { color: #7f8c97; text-shadow: 0px 1px 0px #fff; background: url("../images/upload.png") 0 10px no-repeat; z-index: 1; padding: 6px 48px 0; line-height: 25px; font-size: 20px; font-weight: bold; display: inline-block; }
.ss-assetuploadfield .ss-uploadfield-addfile .ss-uploadfield-dropzone div span { display: block; font-size: 12px; z-index: -1; margin-top: -3px; } .ss-assetuploadfield .ss-uploadfield-addfile .ss-uploadfield-dropzone div span { display: block; font-size: 12px; z-index: -1; margin-top: -3px; }
.ss-insert-media .ss-assetuploadfield .ss-uploadfield-addfile .ss-uploadfield-dropzone { height: 54px; min-width: 250px; overflow: hidden; padding: 0; margin-top: 2px; } .ss-insert-media .ss-assetuploadfield .ss-uploadfield-addfile .ss-uploadfield-dropzone { height: 54px; min-width: 250px; overflow: hidden; padding: 0; margin-top: 2px; }
.ss-insert-media .ss-assetuploadfield .ss-uploadfield-addfile .ss-uploadfield-dropzone div { background-position: 0 11px; padding-top: 21px; margin-left: 33px; } .ss-insert-media .ss-assetuploadfield .ss-uploadfield-addfile .ss-uploadfield-dropzone div { background-position: 0 11px; padding-top: 21px; margin-left: 33px; }
@ -87,3 +87,5 @@ body.cms.ss-uploadfield-edit-iframe .fieldholder-small label, .composite.ss-asse
/** ---------------------------------------------------- "@2x" media query ---------------------------------------------------- */ /** ---------------------------------------------------- "@2x" media query ---------------------------------------------------- */
@media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2 / 1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { /* Loading spinner */ @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2 / 1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { /* Loading spinner */
.ss-assetuploadfield .ss-uploadfield-files .ss-uploadfield-item-editform.loading { background-image: url(../admin/images/spinner@2x.gif); background-size: 43px 43px; } } .ss-assetuploadfield .ss-uploadfield-files .ss-uploadfield-item-editform.loading { background-image: url(../admin/images/spinner@2x.gif); background-size: 43px 43px; } }
/*# sourceMappingURL=AssetUploadField.css.map */

View File

@ -0,0 +1,7 @@
{
"version": 3,
"mappings": ";;;;;;;;;;;;AASA,uCAAwC,GACvC,WAAW,EAAC,GAAG,EACf,KAAK,EAAC,IAAI,EACV,SAAS,EAAC,KAAK,EACf,OAAO,EAAC,KAAK;AAEb,+CAAQ,GACP,UAAU,EAAE,MAAM,EAClB,SAAS,ECgEM,IAAI;;AD5DrB,SAAU,GACT,YAAY,EAAE,IAAI;;AAGnB,qFAAoC,GACnC,QAAQ,EAAE,IAAI,EACd,UAAU,EAAE,OAAO;AAEnB,iHAAc,GACb,UAAU,EAAC,MAAM,EACjB,KAAK,EAAC,OAAyB,EAC/B,WAAW,EAAE,gBAAgB;AAI7B,uIAAK,GACJ,WAAW,EAAC,CAAC;;AAKhB,gDAAiD,GAEhD,OAAO,EAAE,IAAS;;AAGnB,oBAAqB,GACpB,aAAa,EAAE,CAAC,EE4Sf,eAAwC,ECnT/B,IAAkD,EDmT3D,kBAAwC,ECnT/B,IAAkD,EDmT3D,UAAwC,ECnT/B,IAAkD;AHU5D,uBAAG,GACF,aAAa,EAAE,kCAA6B,EEwS5C,eAAwC,ECnT/B,gCAAkD,EDmT3D,kBAAwC,ECnT/B,gCAAkD,EDmT3D,UAAwC,ECnT/B,gCAAkD,EHa3D,MAAM,EAAE,OAAO,EACf,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE,IAAI,EACX,QAAQ,EAAC,QAAQ;AAElB,2BAAO,GACN,aAAa,EAAE,CAAC,EAChB,UAAU,EAAC,IAAI;AAEhB,kCAAa,GACZ,KAAK,EAAC,IAAI,EACV,UAAU,EAAC,IAAI,EACf,QAAQ,EAAC,QAAQ;AI/BlB,sDAAO,GACN,OAAO,EAAE,GAAG,EACZ,OAAO,EAAE,KAAK,EACd,MAAM,EAAE,CAAC,EACT,KAAK,EAAE,IAAI,EACX,UAAU,EAAE,MAAM;AAEnB,8DAAe,GAAE,IAAI,EAAC,CAAC;AJ4BrB,uDAAM,GACL,KAAK,EAAC,IAAI,EACV,SAAS,EAAE,IAAI,EACf,WAAW,EAAC,IAAI,EAChB,WAAW,EAAC,KAAK;AAElB,yDAAQ,GACP,OAAO,EAAC,GAAG,EACX,KAAK,EAAC,KAAK;AAId,0DAAqC,GACpC,KAAK,EAAC,IAAI,EACV,QAAQ,EAAC,QAAQ,EACjB,OAAO,EAAC,CAAC;AACT,wFAA8B,GAC7B,OAAO,EAAC,CAAC,EACT,QAAQ,EAAC,QAAQ,EACjB,GAAG,EAAC,KAAK,EACT,OAAO,EAAC,CAAC,EACT,UAAU,EAAE,IAAI,EAChB,MAAM,EAAE,CAAC,EACT,KAAK,EAAC,CAAC,EE+PR,eAAwC,ECnT/B,IAAkD,EDmT3D,kBAAwC,ECnT/B,IAAkD,EDmT3D,UAAwC,ECnT/B,IAAkD,EHsD1D,KAAK,ECxDK,OAAO;ADyDjB,8FAAO,GACN,KAAK,EAAE,OAAO;AAMjB,0CAAsB,GACrB,MAAM,EAAE,CAAC,EACT,OAAO,EAAE,CAAC,EACV,KAAK,EAAC,IAAI;AAEV,+DAAqB,GACpB,MAAM,EAAE,iBAA+C,EE+OxD,kBAAwC,EF9OhB,GAAG,EE8O3B,qBAAwC,EG9Sb,GAAuB,EH8SlD,aAAwC,EF9OhB,GAAG,EE8O3B,oBAAwC,EIzUvB,OAA8C,EJyU/D,kBAAwC,EI1U3B,WAAuC,EJ0UpD,uBAAwC,EIzUvB,OAA8C,EJyU/D,eAAwC,EI1U3B,WAAuC,EN8FnD,MAAM,EAAE,OAAO,EACf,OAAO,EAAE,CAAC,EACV,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,QAAQ;AAClB,qEAAM,GACL,QAAQ,EAAE,QAAQ,EAClB,OAAO,EAAE,GAAG,EACZ,QAAQ,EAAE,MAAM,EAChB,gBAAgB,EAAE,OAAO,EACzB,MAAM,EAAE,iBAAiB;AAG3B,uEAA6B,GAC5B,QAAQ,EAAE,QAAQ,EAClB,MAAM,EAAE,IAAI,EACZ,KAAK,EAAE,IAAI,EACX,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,CAAC;AACV,mFAAW,GACV,OAAO,EAAC,KAAK,EACb,MAAM,EAAC,IAAI,EACX,KAAK,EAAC,IAAI,EACV,UAAU,EAAC,qDAAqD;AAGlE,oEAA0B,GACzB,QAAQ,EAAE,QAAQ,EAClB,WAAW,EAAE,IAAI,EACjB,QAAQ,EAAE,MAAM,EAChB,gBAAgB,EAAE,OAAO,EOrEtB,gBAAY,EAAE,6uBAAgC,EA2B9C,eAAe,EAAE,IAAI,EA3BrB,gBAAY,EAAE,qMAAgC,EAA9C,gBAAY,EAAE,sGAAgC,EAA9C,gBAAY,EAAE,yGAAgC,EAE9C,gBAAY,EAAE,uGAAO;APsEzB,oFAA0C,GACzC,gBAAgB,EAAE,OAAO,EACzB,aAAa,EAAC,KAAK,EO1EhB,gBAAY,EAAE,6wCAAgC,EA2B9C,eAAe,EAAE,IAAI,EA3BrB,gBAAY,EAAE,wcAAgC,EAA9C,gBAAY,EAAE,uOAAgC,EAA9C,gBAAY,EAAE,0OAAgC,EAE9C,gBAAY,EAAE,wOAAO;AP2ExB,8GAA0B,GACzB,KAAK,EAAC,IAAI,EACV,MAAM,EAAC,OAAO,EACd,UAAU,EAAE,OAAkC,EAC9C,UAAU,EAAE,wBAA6C;AAEzD,oHAAM,GACL,WAAW,EAAE,oCAA2B;AAI3C,sFAA4C,GAC3C,gBAAgB,EClGH,OAAO,EMSjB,gBAAY,EAAE,6uBAAgC,EA2B9C,eAAe,EAAE,IAAI,EA3BrB,gBAAY,EAAE,qMAAgC,EAA9C,gBAAY,EAAE,sGAAgC,EAA9C,gBAAY,EAAE,yGAAgC,EAE9C,gBAAY,EAAE,uGAAO;APoGzB,oEAA0B,GACzB,QAAQ,EAAE,QAAQ,EAClB,OAAO,EAAE,CAAC,EACV,MAAM,EAAE,cAAc,EACtB,KAAK,EAAE,GAAG,EACV,KAAK,EAAC,OAAyB,EAC/B,UAAU,ECxHW,OAAO,EDyH5B,UAAU,EAAE,wBAAe,EEuK5B,kBAAwC,EFtKhB,GAAG,EEsK3B,qBAAwC,EG9Sb,GAAuB,EH8SlD,aAAwC,EFtKhB,GAAG,EAC1B,WAAW,EAAE,IAAI,EACjB,MAAM,EAAE,IAAI,EACZ,OAAO,EAAE,KAAK,EACd,UAAU,EAAE,IAAI,EAChB,MAAM,EAAC,OAAO,EAEd,OAAO,EAAC,KAAK,EACb,YAAY,EAAC,KAAK;AAElB,0EAAM,GACL,WAAW,EAAE,oCAA2B,EACxC,OAAO,EAAC,MAAM,EACd,KAAK,EAAC,IAAI,EACV,SAAS,EAAC,GAAG,EACb,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,SAAS,EIvLrB,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,MAAM,EAInB,aAAa,EAAE,QAAQ,EACvB,gBAAgB,EAAE,QAAQ;AJoLxB,gGAA4B,GAC3B,QAAQ,EAAC,QAAQ,EACjB,KAAK,EAAE,KAAK,EACZ,OAAO,EAAE,SAAS,EAClB,SAAS,EAAC,GAAG,EI9LhB,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,MAAM,EAInB,aAAa,EAAE,QAAQ,EACvB,gBAAgB,EAAE,QAAQ,EJ0LvB,WAAW,EAAE,oCAA2B;AAExC,oHAAsB,GACrB,WAAW,EAAE,oCAA2B,EACxC,KAAK,EAAE,OAAsC;AAE9C,sHAAwB,GACvB,KAAK,EAAE,OAA2B;AAEnC,sHAAwB,GACvB,KAAK,EClKkB,OAAO;ADsKjC,uEAA6B,GAC5B,QAAQ,EAAE,QAAQ,EAClB,GAAG,EAAE,CAAC,EACN,KAAK,EAAE,CAAC,EACR,IAAI,EAAE,CAAC,EACP,OAAO,EAAE,CAAC,EACV,KAAK,EAAE,IAAI,EAEX,SAAS,EAAC,IAAI;AQlNhB,qFAAc,GACb,UAAU,EAAE,IAAI,EAChB,MAAM,EAAE,CAAC,ENuUT,eAAwC,ECnT/B,IAAkD,EDmT3D,kBAAwC,ECnT/B,IAAkD,EDmT3D,UAAwC,ECnT/B,IAAkD,EMK3D,WAAW,EANG,IAAwB,EDhBtC,KAAK,EPgBY,KAAK,EOftB,KAAK,EAAE,KAAK;AAEZ,gHAA6B,GAE5B,OAAO,EAAE,IAAI;AAEd,6OAAsE,GN6TtE,kBAAwC,EM5ThB,CAAC,EN4TzB,qBAAwC,EG9Sb,CAAuB,EH8SlD,aAAwC,EM5ThB,CAAC,EACxB,WAAW,EAAC,kCAAyB,EACrC,UAAU,EAAC,GAAG,EACd,MAAM,EAAE,OAAO,EACf,OAAO,EAAC,GAAG;AACX,yPAAO,GACN,OAAO,EAAC,CAAC;AAEV,+PAAS,GACP,OAAO,EAAE,KAAK,EACd,MAAM,EAAE,CAAC,EACT,QAAQ,EAAC,QAAQ,EACjB,GAAG,EAAC,GAAG;AAaX,8GAA2B,GAC1B,OAAO,EAAC,GAAG,EACX,WAAW,EAAE,GAAG,EAChB,cAAc,EAAE,CAAC,EACjB,MAAM,EAAC,IAAI,EN+RX,kBAAwC,EM9RjB,CAAC,EN8RxB,qBAAwC,EG9Sb,CAAuB,EH8SlD,aAAwC,EM9RjB,CAAC;AACxB,6HAAgB,GACf,UAAU,EAAC,IAAI,EACf,OAAO,EAAC,CAAC;AACT,iJAAmB,GAClB,OAAO,EAAC,CAAC;AAGX,kIAAmB,GAClB,OAAO,EAAC,GAAG,EACX,WAAW,EAAC,GAAG,EACf,OAAO,EAAE,YAAY,EACrB,KAAK,EAAE,GAAG,EACV,MAAM,EAAE,IAAI,EACZ,MAAM,EAAE,OAAO;AACf,uJAAqB,GACpB,UAAU,EAAC,GAAG,EACd,OAAO,EAAE,YAAY,EACrB,KAAK,EAAE,GAAG,EACV,MAAM,EAAE,GAAG,EACX,cAAc,EAAE,MAAM;AACtB,8JAAS,GACR,UAAU,EAAC,CAAC;AAMhB,8FAAS,GACR,OAAO,EAAE,IAAI;AR6Ib,wEAA8B,GAC7B,KAAK,EAAE,IAAI;AAEX,4EAAI,GEiHL,kBAAwC,EFhHf,GAAG,EEgH5B,qBAAwC,EG9Sb,GAAuB,EH8SlD,aAAwC,EFhHf,GAAG,EAC1B,MAAM,EAAE,IAAI,EACZ,OAAO,EAAE,CAAC,EACV,MAAM,EAAE,CAAC,EACT,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,IAAI;AAEZ,yGAAiC,GAChC,gBAAgB,EAAE,OAAO,EO5KvB,gBAAY,EAAE,6uBAAgC,EA2B9C,eAAe,EAAE,IAAI,EA3BrB,gBAAY,EAAE,sMAAgC,EAA9C,gBAAY,EAAE,uGAAgC,EAA9C,gBAAY,EAAE,0GAAgC,EAE9C,gBAAY,EAAE,wGAAO;AP6KxB,8GAAsC,GACrC,KAAK,EAAE,CAAC,EACR,UAAU,EAAE,8DAA8D;AAI5E,wEAA8B,wFAE7B,MAAM,EAAE,CAAC,EACT,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,IAAI;AAEX,gFAAU,GACT,KAAK,EAAE,IAAI,EACX,MAAM,EAAE,IAAI,EACZ,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,kDAAkD;AAE9D,uFAAO,6FAEN,WAAW,EAAE,CAAC,EAAE,UAAU,EAAE,IAAI,EAChC,MAAM,EAAE,IAAI;AAId,+EAAO,GACN,KAAK,EAAE,IAAI,EACX,OAAO,EAAE,IAAS,EAClB,UAAU,EAAE,OAAO;AAOrB,sEAA0B,GACzB,KAAK,EAAE,IAAI,EACX,MAAM,EAAE,QAAQ;AAChB,uFAAkB,GACjB,MAAM,EAAE,eAAe;AAExB,4EAAK,GACJ,SAAS,EAAE,IAAI,EACf,WAAW,EAAE,IAAI,EACjB,OAAO,EAAE,QAAQ,EACjB,YAAY,EAAC,GAAG;AAGlB,yEAA6B,4BAE5B,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,KAAK;AACd,sGAA6B,GAC5B,UAAU,EAAE,uDAAuD,EACnE,KAAK,EAAC,IAAI,EACV,MAAM,EAAC,IAAI,EACX,UAAU,EAAC,KAAK;AAGlB,0EAA8B,GAC7B,KAAK,EAAE,IAAI,EACX,WAAW,EAAE,IAAI,EACjB,SAAS,EAAE,IAAI,EACf,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,IAAI,EACjB,UAAU,EAAC,IAAI,EACf,OAAO,EAAE,IAAI;AACb,2FAAkB,GACjB,SAAS,EAAE,IAAI,EACf,UAAU,EAAE,IAAI;AAGlB,qEAAyB,GACxB,UAAU,EAAC,GAAG,EACd,OAAO,EAAE,KAAK,EE2Bf,kBAAwC,EF1BhB,IAAI,EE0B5B,qBAAwC,EG9Sb,IAAuB,EH8SlD,aAAwC,EF1BhB,IAAI,EE0B5B,eAAwC,ECnT/B,yDAAkD,EDmT3D,kBAAwC,ECnT/B,yDAAkD,EDmT3D,UAAwC,ECnT/B,yDAAkD,EH2R1D,MAAM,EAAE,kBAAkC,EAC1C,UAAU,EAAE,OAAwB,EACpC,OAAO,EAAE,IAAI,EACb,MAAM,EAAE,IAAI,EACZ,SAAS,EAAE,KAAK,EAChB,KAAK,EAAE,IAAI,EACX,UAAU,EAAE,MAAM;AAGjB,kFAAO,GEeT,eAAwC,ECnT/B,0CAAkD,EDmT3D,kBAAwC,ECnT/B,0CAAkD,EDmT3D,UAAwC,ECnT/B,0CAAkD;AHwS1D,yEAAI,GACH,KAAK,EAAC,OAAyB,EAC/B,WAAW,EAAE,gBAAgB,EAC7B,UAAU,EAAE,4CAA4C,EACxD,OAAO,EAAC,CAAC,EACT,OAAO,EAAE,UAAU,EACnB,WAAW,EAAE,IAAI,EACjB,SAAS,EAAE,IAAI,EACf,WAAW,EAAE,IAAI,EACjB,OAAO,EAAE,YAAY;AACrB,8EAAK,GACJ,OAAO,EAAE,KAAK,EACd,SAAS,EAAE,IAAI,EACf,OAAO,EAAC,EAAE,EACV,UAAU,EAAC,IAAI;AAGjB,sFAAkB,GACjB,MAAM,EAAE,IAAI,EACZ,SAAS,EAAE,KAAK,EAChB,QAAQ,EAAC,MAAM,EACf,OAAO,EAAC,CAAC,EACT,UAAU,EAAC,GAAG;AACd,0FAAG,GACF,mBAAmB,EAAC,MAAM,EAC1B,WAAW,EAAC,IAAI,EAChB,WAAW,EAAE,IAAI;AACjB,+FAAI,GACH,MAAM,EAAC,IAAI,EACX,SAAS,EAAC,IAAI,EACd,WAAW,EAAE,IAAI;;;AAYvB,gSAMyD;EAMrD,gFAAU,GACT,gBAAgB,EAAE,mCAAmC,EACrD,eAAe,EAAE,SAAS",
"sources": ["../scss/AssetUploadField.scss","../admin/scss/themes/_default.scss","../../../../usr/local/share/gems/gems/compass-core-1.0.3/stylesheets/compass/_support.scss","../../../../usr/local/share/gems/gems/compass-core-1.0.3/stylesheets/compass/css3/_box-shadow.scss","../admin/scss/_mixins.scss","../../../../usr/local/share/gems/gems/compass-core-1.0.3/stylesheets/compass/css3/_border-radius.scss","../../../../usr/local/share/gems/gems/compass-core-1.0.3/stylesheets/compass/css3/_background-clip.scss","../../../../usr/local/share/gems/gems/compass-core-1.0.3/stylesheets/compass/css3/_images.scss","../scss/_elementMixins.scss","../../../../usr/local/share/gems/gems/compass-core-1.0.3/stylesheets/compass/css3/_text-shadow.scss"],
"names": [],
"file": "AssetUploadField.css"
}

View File

@ -1,3 +1,5 @@
#right form .CheckboxSetField ul { margin: 0; padding: 0; } #right form .CheckboxSetField ul { margin: 0; padding: 0; }
#right form .CheckboxSetField ul li { list-style: none !important; margin: 0; padding: 0; } #right form .CheckboxSetField ul li { list-style: none !important; margin: 0; padding: 0; }
#right form .CheckboxSetField input { width: auto; } #right form .CheckboxSetField input { width: auto; }
/*# sourceMappingURL=CheckboxSetField.css.map */

View File

@ -0,0 +1,7 @@
{
"version": 3,
"mappings": "AACC,gCAAE,GACD,MAAM,EAAC,CAAC,EACR,OAAO,EAAC,CAAC;AAEV,mCAAK,GACJ,UAAU,EAAC,eAAe,EAC1B,MAAM,EAAC,CAAC,EACR,OAAO,EAAC,CAAC;AAEV,mCAAK,GACJ,KAAK,EAAC,IAAI",
"sources": ["../scss/CheckboxSetField.scss"],
"names": [],
"file": "CheckboxSetField.css"
}

View File

@ -1 +1,3 @@
.confirmedpassword .showOnClickContainer { /* used to hide a container by-default when loading it so there is no "flash" as the content loads */ display: none; } .confirmedpassword .showOnClickContainer { /* used to hide a container by-default when loading it so there is no "flash" as the content loads */ display: none; }
/*# sourceMappingURL=ConfirmedPasswordField.css.map */

View File

@ -0,0 +1,7 @@
{
"version": 3,
"mappings": "AAAA,wCAAyC,yGAExC,OAAO,EAAC,IAAI",
"sources": ["../scss/ConfirmedPasswordField.scss"],
"names": [],
"file": "ConfirmedPasswordField.css"
}

View File

@ -1,3 +1,5 @@
.datetime .middleColumn .middleColumn { margin: 0; padding: 0; clear: none; float: left; } .datetime .middleColumn .middleColumn { margin: 0; padding: 0; clear: none; float: left; }
.datetime .middleColumn .field { margin: 0; border-bottom: none; box-shadow: none; } .datetime .middleColumn .field { margin: 0; border-bottom: none; box-shadow: none; }
/*# sourceMappingURL=DatetimeField.css.map */

View File

@ -0,0 +1,7 @@
{
"version": 3,
"mappings": "AAAA,qCAAsC,GACrC,MAAM,EAAE,CAAC,EACT,OAAO,EAAE,CAAC,EACV,KAAK,EAAE,IAAI,EACX,KAAK,EAAE,IAAI;;AAEZ,8BAA+B,GAC9B,MAAM,EAAE,CAAC,EACT,aAAa,EAAE,IAAI,EACnB,UAAU,EAAE,IAAI",
"sources": ["../scss/DatetimeField.scss"],
"names": [],
"file": "DatetimeField.css"
}

View File

@ -20,3 +20,5 @@ form .message.required, form .message.validation { display: block; margin-top: 5
form .message.validation { color: #FF4040; width: 240px; border-color: #FF4040; } form .message.validation { color: #FF4040; width: 240px; border-color: #FF4040; }
.typography .ss-tabset ul { margin: 0; } .typography .ss-tabset ul { margin: 0; }
/*# sourceMappingURL=Form.css.map */

7
css/Form.css.map Normal file
View File

@ -0,0 +1,7 @@
{
"version": 3,
"mappings": "AAAA,IAAK;AAEJ,MAAE,GACD,SAAS,EAAE,IAAI;AAGhB,aAAS,GACR,MAAM,EAAE,CAAC,EACT,OAAO,EAAE,CAAC,EACV,YAAY,EAAE,IAAI;AAGnB,WAAO,GACN,KAAK,EAAE,IAAI,EACX,OAAO,EAAE,KAAK,EACd,MAAM,EAAE,UAAU,EAClB,cAAc,EAAE,MAAM;AAGvB,eAAW,GACV,MAAM,EAAE,WAAW;AAGpB,mBAAe,GACd,WAAW,EAAE,CAAC;AAGf,eAAW,GACV,KAAK,EAAE,IAAI,EACX,KAAK,EAAE,IAAI,EACX,WAAW,EAAE,KAAK;AAGnB,oBAAgB,GACf,KAAK,EAAE,IAAI;AAGZ,aAAQ,GACP,KAAK,EAAG,KAAK;AAGd,6CAA+B,GAC9B,MAAM,EAAE,cAAc,EACtB,UAAU,EAAE,IAAI,EAChB,OAAO,EAAE,KAAK,EACd,KAAK,EAAE,GAAG;AAGX,yBAAqB,GACpB,MAAM,EAAE,eAAe,EACvB,gBAAgB,EAAE,OAAO,EACzB,OAAO,EAAE,KAAK,EACd,KAAK,EAAE,GAAG,EACV,OAAO,EAAE,GAAG,EACZ,MAAM,EAAC,KAAK;AAGb,sBAAkB,GACjB,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,GAAG,EAChB,cAAc,EAAE,MAAM;AAGvB,qBAAiB,GAChB,OAAO,EAAE,MAAM;AAIhB,uBAAmB,GAClB,UAAU,EAAE,IAAI,EAChB,OAAO,EAAE,CAAC,EACV,MAAM,EAAE,CAAC,EACT,KAAK,EAAE,OAAO,EACd,eAAe,EAAE,SAAS;AAM3B,aAAS,GACR,MAAM,EAAE,KAAK,EACb,OAAO,EAAE,KAAK,EACd,WAAW,EAAE,IAAI,EACjB,MAAM,EAAE,eAAe,EACvB,gBAAgB,EAAE,OAAO,EACzB,YAAY,EAAE,OAAO;AAErB,oBAAS,GACR,gBAAgB,EAAE,OAAO,EACzB,YAAY,EAAE,OAAO;AAGtB,qBAAU,GACT,gBAAgB,EAAE,OAAO,EACzB,YAAY,EAAE,OAAO;AAGtB,iBAAM,GACL,gBAAgB,EAAE,OAAO,EACzB,YAAY,EAAE,OAAO;AAGtB,gDAAyB,GACxB,OAAO,EAAC,KAAK,EACb,UAAU,EAAC,GAAG,EACd,KAAK,EAAC,OAAO,EACb,KAAK,EAAC,KAAK,EACX,YAAY,EAAE,OAAO;AAGtB,wBAAa,GACZ,KAAK,EAAC,OAAO,EACb,KAAK,EAAC,KAAK,EACX,YAAY,EAAE,OAAO;;AAKxB,yBAA0B,GACzB,MAAM,EAAE,CAAC",
"sources": ["../scss/Form.scss"],
"names": [],
"file": "Form.css"
}

View File

@ -134,3 +134,5 @@ Used in side panels and action tabs
.cms table.ss-gridfield-table tr.last td { border-bottom: 0 none; } .cms table.ss-gridfield-table tr.last td { border-bottom: 0 none; }
.cms table.ss-gridfield-table td:first-child { border-left: 1px solid rgba(0, 0, 0, 0.1); } .cms table.ss-gridfield-table td:first-child { border-left: 1px solid rgba(0, 0, 0, 0.1); }
.cms table.ss-gridfield-table td:last-child { border-right: 1px solid rgba(0, 0, 0, 0.1); } .cms table.ss-gridfield-table td:last-child { border-right: 1px solid rgba(0, 0, 0, 0.1); }
/*# sourceMappingURL=GridField.css.map */

7
css/GridField.css.map Normal file
View File

@ -0,0 +1,7 @@
{
"version": 3,
"mappings": ";;;;;;;;;;;;;AAwCE,wBAAQ,GACP,aAAa,EAAE,IAAY;AAC3B,8CAAuB,GACtB,aAAa,EAAE,CAAC;AAChB,sDAAQ,GACR,aAAa,EAXL,IAAI;AAcb,sDAA+B,GAC9B,aAAa,EAAE,CAAC;AAChB,8DAAQ,GACR,aAAa,EAjBL,IAAI;AAoBb,qDAA8B,GAC7B,aAAa,EAAE,CAAC;AAChB,6DAAQ,GACR,UAAU,EAvBF,IAAI;AA6Bb,uGAAgC,GAC/B,UAAU,EAAE,kBAAkB;AAG/B,sCAAG,GACF,MAAM,EAAE,OAAO;AAIjB,4DAA0C,GACxC,OAAO,EAAC,IAAI;AAIb,oEAA2B,GAC1B,MAAM,EAAC,IAAI;AAGZ,yBAAO,GACN,KAAK,EAAC,KAAK;AACZ,6BAAM,GACL,KAAK,EAAE,KAAK,EACZ,WAAW,EAAC,GAAY;AAGzB,oDAA2B,GAC1B,SAAS,EAAE,KAAK,EAChB,OAAO,EAAE,aAAa,EACtB,KAAK,ECnDU,KAAK,ECMtB,WAAW,EANG,6BAAwB,EFqDpC,WAAW,EAAE,MAAM;AAGrB,wBAAM,GACL,KAAK,EAAC,IAAI;AACV,4BAAM,GACL,YAAY,EAAC,GAAY,EACzB,KAAK,EAAE,IAAI,EACX,SAAS,EAAE,MAAc;AAI3B,0CAAwB,GACvB,SAAS,EAAE,MAAc;AAK1B,gCAAc,GACb,WAAW,EAAE,OAAO,EAKpB,aAAa,EAAE,GAAG;AAJlB,mDAAkB,GACjB,UAAU,EAAE,+DAA+D,EAC3E,OAAO,EAAC,KAAK;AAKd,mDAAK,GACJ,KAAK,EAAE,IAAI,EGvGb,OAAO,EAAE,YAAY,EAEnB,cAAc,EHsGQ,GAAG,EGjGvB,eAAe,EAbmD,IAAI,EAexE,KAAK,EAAE,CAAC,EACR,QAAQ,EAAE,MAAM;AHgGjB,oEAAsB,GACrB,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAC1B,aAAa,EA5FL,IAAI,EA6FZ,uBAAuB,EAAE,CAAC,EAC1B,0BAA0B,EAAE,CAAC;AAE9B,kFAAoC,GACnC,MAAM,EAAE,IAAI,EACZ,WAAW,EAAE,CAAC,EACd,sBAAsB,EAAE,CAAC,EACzB,yBAAyB,EAAE,CAAC,EAC5B,WAAW,EAAE,IAAI;AAGnB,0EAAqC,GACpC,aAAa,EAAE,CAAC,EAChB,SAAS,EC3DK,IAAI,EE/DnB,OAAO,EAAE,YAAY,EAEnB,cAAc,EAXO,MAAM,EAgBzB,eAAe,EAbmD,IAAI,EAexE,KAAK,EAAE,CAAC,EACR,QAAQ,EAAE,MAAM;AHoHnB,6BAAyB,GACxB,OAAO,EAAE,KAAK,EIxHf,eAAe,EAAE,IAAI,EACrB,kBAAkB,EAAE,IAAI,EACrB,UAAU,EAAE,IAAI,EJwHlB,OAAO,EAAE,CAAC,EACV,eAAe,EAAE,QAAQ,EACzB,aAAa,EAAE,MAAM,EACrB,KAAK,EAAE,IAAI;AAEX,mCAAM,GACL,KAAK,EAAE,OAAwB,EAC/B,UAAU,EAAE,WAAW;AAEtB,gEAAY,GACX,SAAS,EAAC,KAAa;AACvB,kFAAkB,GACjB,OAAO,EAAC,CAAC;AAKX,iEAAe,GKwLjB,0BAAwC,EL5TvB,GAAG,EK4TpB,8BAAwC,EL5TvB,GAAG,EK4TpB,sBAAwC,EL5TvB,GAAG;AAuIlB,gEAAc,GKqLhB,2BAAwC,EL5TvB,GAAG,EK4TpB,+BAAwC,EL5TvB,GAAG,EK4TpB,uBAAwC,EL5TvB,GAAG;AA6IpB,mCAAM,GACL,UAAU,EAAE,IAAI;AAChB,sCAAG,GAGF,MAAM,EAAE,OAAO;AAEhB,sCAAG,GACF,KAAK,EAAE,IAAI,EACX,SAAS,EAAE,KAAK,EAChB,SAAS,EAAC,UAAU;AAIpB,kDAAc,GACb,KAAK,EAAE,GAAG,EACV,OAAO,EAAC,KAAc,EACtB,UAAU,EAAE,KAAK,EACjB,WAAW,EAAE,MAAM;AAEpB,2DAAuB,GACtB,KAAK,EA/JE,IAAI,EAgKX,YAAY,EAAC,IAAI,EACjB,WAAW,EAAC,OAAO,EACnB,OAAO,EAAC,CAAC;AACT,+EAAoB,GACnB,UAAU,EAAE,2EAA2E,EACvF,OAAO,EAAC,KAAK;AAKd,iEAAU,GACT,KAAK,ECjKW,OAAO;ADmKxB,kEAAW,GACV,KAAK,EAAE,IAAI,EACX,cAAc,EAAE,SAAS,EACzB,OAAO,EAAE,YAAY,EACrB,OAAO,EAAE,OAAO,EAChB,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,GAAG,EAChB,WAAW,EAAE,IAAI,EACjB,YAAY,EAAE,GAAG,EACjB,UAAU,EAAG,IAAI,EKmIrB,kBAAwC,EAAE,SAAM,EAAhD,qBAAwC,EC7SU,OAA+D,ED6SjH,aAAwC,EAAE,SAAM;AL/H7C,kFAA2B,GAC1B,KAAK,EAAG,OAAO,EACf,MAAM,EAAE,iBAAiB,EACzB,gBAAgB,EAAE,OAAO;AAG1B,sFAA+B,GAC9B,KAAK,EAAG,OAAO,EACf,MAAM,EAAE,iBAAiB,EACzB,gBAAgB,EAAE,OAAO;AAG1B,uFAAgC,GAC/B,KAAK,EAAG,OAAO,EACf,MAAM,EAAE,iBAAiB,EACzB,gBAAgB,EAAE,OAAO;AAG1B,0FAAmC,GAClC,KAAK,EAAG,OAAO,EACf,MAAM,EAAE,iBAAiB,EACzB,gBAAgB,EAAE,OAAO;AAG1B,2FAAoC,GACnC,KAAK,EAAG,OAAO,EACf,MAAM,EAAE,iBAAiB,EACzB,gBAAgB,EAAE,OAAO;AAI3B,6CAAO,GACN,MAAM,EAAE,IAAI,EACZ,UAAU,EAAE,IAAI,EAChB,MAAM,EAAE,SAAS,EACjB,OAAO,EAAE,KAAK,EACd,KAAK,EAAE,IAAI,EACX,WAAW,EAAE,IAAI;AACjB,4DAAiB,GAChB,UAAU,EAAC,IAAI,EI3OpB,eAAe,EAAE,IAAI,EACrB,kBAAkB,EAAE,IAAI,EACrB,UAAU,EAAE,IAAI;AJ4Of,6DAAkB,GACjB,MAAM,EAAC,IAAI,EI/OhB,eAAe,EAAE,IAAI,EACrB,kBAAkB,EAAE,IAAI,EACrB,UAAU,EAAE,IAAI;AJgPf,qEAA0B,GACzB,KAAK,EAAE,IAAI,EACX,MAAM,EAAE,CAAC;AACT,2FAAsB,GACrB,IAAI,EAAE,GAAG;AAIZ,sGAAyB,GACxB,OAAO,EAAC,YAAY,EACpB,KAAK,EAAC,IAAI,EACV,MAAM,EAAC,IAAI,EACX,WAAW,EAAC,MAAM,EAClB,QAAQ,EAAE,MAAM,EAChB,cAAc,EAAE,MAAM;AAEvB,kDAAY,GACX,UAAU,EAAE,2DAA2D;AAExE,kDAAY,GACX,UAAU,EAAE,oEAAoE;AAKnF,mCAAM,GACL,KAAK,EAAE,OAAwB;AAE9B,yCAAG,GACF,UAAU,EC/RF,OAAO,EDgSf,OAAO,EAAE,IAAI,EACb,aAAa,EAAE,4BAAwB;AAOxC,yCAAG,GACF,QAAQ,EAAE,QAAQ,EAClB,UAAU,EA1RW,OAAwB,EA2R7C,aAAa,EAAE,iBAA6C,EAC5D,OAAO,EAAE,GAAG,EACZ,UAAU,EAAE,IAAI,EO9Of,gBAAY,EAAE,qhBAAgC,EA2B9C,eAAe,EAAE,IAAI,EA3BrB,gBAAY,EAAE,8FAAgC,EAA9C,gBAAY,EAAE,sCAAgC,EAA9C,gBAAY,EAAE,yCAAgC,EAE9C,gBAAY,EAAE,iCAAO,EL5BzB,WAAW,EANG,6BAAwB;AFiRpC,4CAAE,GACA,OAAO,EAAE,GAAG,EACZ,SAAS,EAAE,MAAc,EACzB,KAAK,EAAC,IAAI,EACV,MAAM,EAAE,SAAS,EACjB,OAAO,EAAC,YAAY,EACpB,KAAK,EAAC,IAAI;AAIb,gDAAkB,GACjB,UAAU,EA1SQ,OAAsC;AA2SxD,mDAAE,GACD,OAAO,EAAE,CAAC,EACV,WAAW,EAAE,MAAM;AACnB,iEAAc,GACb,WAAW,EAAE,MAAM;AAItB,sCAAQ,GACP,UAAU,EAAE,OAAO;AAEpB,4CAAc,GACb,UAAU,EAAE,WAAW;AAEvB,kDAAQ,GACP,UAAU,EAAE,OAAO;AAGrB,kDAAoB,GACnB,UAAU,EA5TI,OAAO;AA8TrB,oEAAoB,GACnB,aAAa,EAAE,IAAI;AAEpB,wDAAQ,GACP,UAAU,EAAE,OAAO;AAGrB,qCAAO,GACN,UAAU,EAtUI,OAAO;AAwUrB,2CAAQ,GACP,UAAU,EAAE,OAAO;AAIrB,mCAAG,GACF,WAAW,EAAE,IAAI,EACjB,SAAS,EAzUD,IAAI,EA0UZ,KAAK,EAAE,IAAI,EACX,OAAO,EAAE,GAAG,EACZ,YAAY,EAAE,4BAA2B;AAGxC,4GAAgC,GAC/B,KAAK,EAAE,IAAI,EACX,QAAQ,EAAC,QAAQ;AAElB,kDAAa,GACZ,SAAS,EAAE,KAAe,EAC1B,aAAa,EAAC,CAAC;AACf,iEAAgB,GACf,SAAS,EAAC,IAAI,EACd,UAAU,EAAE,IAAI,EAChB,MAAM,EAAE,IAAI;AACZ,qEAAG,GACF,KAAK,EAAC,IAAI,EACV,OAAO,EAAC,MAAM;AAKlB,wCAAM,GACL,WAAW,EAAC,MAAM,EAClB,UAAU,EAAE,iBAAgC,EAC5C,WAAW,EAAE,iBAAgC,EAC7C,KAAK,EAAC,IAAI,EACV,UAAU,EA/WW,OAAwB,EAgX7C,aAAa,EAAE,4BAA2B;AAC1C,6CAAI,GE5VP,WAAW,EANG,6BAAwB,EFoWlC,YAAY,EAAE,GAAY,EAC1B,aAAa,EAAE,GAAY,EI9XhC,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,MAAM,EAInB,aAAa,EAAE,QAAQ,EACvB,gBAAgB,EAAE,QAAQ,EJ0XrB,YAAY,EAAE,GAAG;AAGlB,6DAAuB,GACtB,YAAY,EAAC,IAAI;AAGnB,qFAAiB,GAChB,OAAO,EAAE,CAAC,EACV,MAAM,EAAE,OAAO;AAEhB,yCAAQ,GACP,QAAQ,EAAC,QAAQ,EACjB,UAAU,EAAC,OAA6B,EACxC,UAAU,EAAE,kBAAe,EAC3B,OAAO,EAAE,GAAG,EACZ,UAAU,EErVJ,kBAAmD;AFuVzD,+CAAM,GACL,MAAM,EAAC,IAAI;AAGZ,6DAAoB,GACnB,OAAO,EAAE,IAAI,EACb,WAAW,EAAE,CAAC,EI9YnB,eAAe,EAAE,IAAI,EACrB,kBAAkB,EAAE,IAAI,EACrB,UAAU,EAAE,IAAI,EJ8Yd,QAAQ,EAAE,QAAQ,EAClB,mBAAmB,EAAE,CAAC,EK9E1B,kBAAwC,EAAE,SAAM,EAAhD,qBAAwC,EC7SU,OAA+D,ED6SjH,aAAwC,EAAE,SAAM;ALkF7C,gDAAO,GACN,MAAM,EAAE,CAAC;AAGX,yCAAQ,GKtFV,0BAAwC,EL5TvB,GAAG,EK4TpB,8BAAwC,EL5TvB,GAAG,EK4TpB,sBAAwC,EL5TvB,GAAG;AAqZlB,wCAAO,GKzFT,2BAAwC,EL5TvB,GAAG,EK4TpB,+BAAwC,EL5TvB,GAAG,EK4TpB,uBAAwC,EL5TvB,GAAG;AA4ZjB,6EAAqC,GACpC,KAAK,EAAE,eAAe;AAEvB,gDAAQ,GACP,KAAK,EAAE,eAAe;AAEvB,kEAA0B,GACzB,KAAK,EAAE,eAAe,EI1a3B,eAAe,EAAE,IAAI,EACrB,kBAAkB,EAAE,IAAI,EACrB,UAAU,EAAE,IAAI;AJ2af,4DAAoB,GACnB,UAAU,EAAE,yDAAyD,EACrE,MAAM,EAAC,IAAI,EACX,KAAK,EAAC,IAAI,EACV,UAAU,EAAE,IAAI,EAChB,OAAO,EAAE,aAAa,EE7Z1B,WAAW,EANG,6BAAwB,EFqalC,KAAK,EAAE,IAAI,EKjHf,kBAAwC,ELkHb,CAAC,EKlH5B,qBAAwC,EC9Sb,CAAuB,ED8SlD,aAAwC,ELkHb,CAAC;AACxB,kEAAQ,GACP,mBAAmB,EAAE,WAAW;AAEjC,qFAA2B,GAC1B,mBAAmB,EAAE,WAAW;AAEjC,oFAA0B,GACzB,mBAAmB,EAAE,YAAY;AAKlC,kFAA4B,GAE3B,gBAAgB,EAAC,OAA+B,EQpdtD,MAAM,EAAC,IAAI,EACX,OAAO,EAAC,KAAK,EACb,WAAW,EAAC,OAAO,EACnB,KAAK,EAAC,IAAI,EACV,MAAM,EAAC,IAAI,ED2DN,UAAY,EAAE,glBAAgC,EAA9C,UAAY,EAAE,yJAAgC,EAA9C,UAAY,EAAE,iGAAgC,EAA9C,UAAY,EAAE,oGAAgC,EAE9C,UAAY,EAAE,4FAAO,EP4ZpB,KAAK,EAAE,IAAI,EACX,UAAU,EAAE,iBAAyC;AAErD,0XAAuD,GOjaxD,UAAY,EAAE,glBAAgC,EAA9C,UAAY,EAAE,yJAAgC,EAA9C,UAAY,EAAE,iGAAgC,EAA9C,UAAY,EAAE,oGAAgC,EAE9C,UAAY,EAAE,4FAAO;APwapB,0FAAS,GACR,WAAW,EAjdP,IAAI,EAkdR,MAAM,EAAC,IAAI,EO1ab,UAAY,EAAE,yDAAO,EP4anB,aAAa,EAAE,IAAI,EACnB,MAAM,EAAE,KAAc;AAEtB,+FAAI,GACH,OAAO,EAAE,GAAG,EACZ,QAAQ,EAAC,QAAQ,EACjB,KAAK,EAAC,IAAI,EACV,IAAI,EAAC,IAAI,EACT,GAAG,EAAC,GAAG,EACP,UAAU,EAAE,8DAA8D;AAE3E,gGAAO,GOvbT,UAAY,EAAE,2DAAO,EHjD1B,eAAe,EAAE,IAAI,EACrB,kBAAkB,EAAE,IAAI,EACrB,UAAU,EAAE,IAAI;AJyeX,qGAAI,GACH,OAAO,EAAC,GAAG;AAMf,iFAA2B,GOjc3B,UAAY,EAAE,yDAAO,ECjE1B,MAAM,EAAC,IAAI,EACX,OAAO,EAAC,KAAK,EACb,WAAW,EAAC,OAAO,EACnB,KAAK,EAAC,IAAI,EACV,MAAM,EAAC,IAAI,ERkgBL,KAAK,EAAC,IAAI,EACV,OAAO,EAAC,GAAG,EACX,YAAY,EAAC,IAAI;AACjB,sXAAuD,GACtD,OAAO,EAAC,CAAC,EO3cX,UAAY,EAAE,goBAAgC,EAA9C,UAAY,EAAE,2LAAgC,EAA9C,UAAY,EAAE,mIAAgC,EAA9C,UAAY,EAAE,sIAAgC,EAE9C,UAAY,EAAE,8HAAO;APodrB,iFAA2B,GQrhBhC,MAAM,EAAC,IAAI,EACX,OAAO,EAAC,KAAK,EACb,WAAW,EAAC,OAAO,EACnB,KAAK,EAAC,IAAI,EACV,MAAM,EAAC,IAAI,ERmhBL,QAAQ,EAAC,QAAQ,EACjB,GAAG,EAAE,KAAK,EACV,KAAK,EAAE,IAAI,EACX,KAAK,EAAC,IAAI,EACV,MAAM,EAAC,IAAI,EACX,OAAO,EAAC,IAAI;AAEZ,0FAAU,GACT,OAAO,EAAC,KAAK,EAEb,UAAU,EAAE,yDAAyD,EACrE,OAAO,EAAC,GAAG;AACX,gGAAO,GACN,OAAO,EAAC,GAAG;AAEZ,iGAAS,GACR,OAAO,EAAC,CAAC;AAQb,2DAAoB,GACnB,MAAM,EAAC,IAAI,EACX,OAAO,EAAE,GAAG,EAKZ,MAAM,EAAE,iBAAiB;AAGxB,sFAAY,GACX,UAAU,EAAC,MAAM,EACjB,KAAK,EAAE,OAA6B;AAFrC,4EAAY,GACX,UAAU,EAAC,MAAM,EACjB,KAAK,EAAE,OAA6B;AAFrC,iFAAY,GACX,UAAU,EAAC,MAAM,EACjB,KAAK,EAAE,OAA6B;AAFrC,uEAAY,GACX,UAAU,EAAC,MAAM,EACjB,KAAK,EAAE,OAA6B;AAItC,iEAAQ,GI/iBb,eAAe,EAAE,IAAI,EACrB,kBAAkB,EAAE,IAAI,EACrB,UAAU,EAAE,IAAI;AJojBhB,qDAAkB,GACjB,OAAO,EAAC,KAAK,EACb,OAAO,EAAE,OAAO;AAIlB,mCAAG,GACF,YAAY,EAAE,4BAA2B,EACzC,OAAO,EAAE,OAAyB,EAClC,KAAK,EA3jBQ,IAAI;AA4jBjB,8CAAa,GK7Pf,6BAAwC,EL5TvB,GAAG,EK4TpB,iCAAwC,EL5TvB,GAAG,EK4TpB,yBAAwC,EL5TvB,GAAG,EK4TpB,8BAAwC,EL5TvB,GAAG,EK4TpB,kCAAwC,EL5TvB,GAAG,EK4TpB,0BAAwC,EL5TvB,GAAG,EOwChB,gBAAY,EAAE,qhBAAgC,EA2B9C,eAAe,EAAE,IAAI,EA3BrB,gBAAY,EAAE,8FAAgC,EAA9C,gBAAY,EAAE,sCAAgC,EAA9C,gBAAY,EAAE,yCAAgC,EAE9C,gBAAY,EAAE,iCAAO,EPkhBtB,OAAO,EAAE,QAAuB;AAEhC,uEAAyB,GACrB,UAAU,EAAE,MAAM,EACrB,WAAW,EAAE,GAAG,EACV,KAAK,ECzjBE,KAAK;AD2jBnB,mEAAqB,GACpB,WAAW,EAAC,GAAG,EACf,QAAQ,EAAC,QAAQ,EACjB,IAAI,EAAC,GAAG,EACR,WAAW,EAAC,MAAM,EAclB,OAAO,EAAC,CAAC;AAbT,2FAAwB,GACvB,KAAK,ECjkBO,KAAK,EDkkBjB,UAAU,EAAE,MAAM,EE5jBvB,WAAW,EANG,6BAAwB;AFokBjC,iGAAM,GACL,KAAK,EAAC,IAAI,EACV,MAAM,EAAC,IAAI,EACX,aAAa,EAAC,IAAI,EAClB,OAAO,EAAC,GAAG,EACX,MAAM,EAAE,iBAA8C,EACtD,aAAa,EAAE,iBAA+C;AAIhE,0EAAM,GI7lBX,eAAe,EAAE,IAAI,EACrB,kBAAkB,EAAE,IAAI,EACrB,UAAU,EAAE,IAAI,EJ6lBb,MAAM,EAAC,IAAI,EACX,KAAK,EAAC,IAAI,EACV,MAAM,EAAC,MAAM,EACb,OAAO,EAAC,MAAM,EACd,KAAK,EAAC,IAAI;AACV,+EAAK,GACJ,WAAW,EAAC,OAAO;AAEpB,oGAA4B,GOtjB7B,UAAY,EAAE,8DAAO;APyjBpB,gGAAwB,GOzjBzB,UAAY,EAAE,8DAAO;AP4jBpB,iGAAyB,GO5jB1B,UAAY,EAAE,4DAAO;AP+jBpB,gGAAwB,GO/jBzB,UAAY,EAAE,8DAAO;APkkBpB,+FAAsB,GACrB,OAAO,EAAC,EAAE;AAIb,yEAA2B,GAC1B,KAAK,EAAC,KAAK,EACX,OAAO,EAAC,KAAK,EACb,KAAK,EC5mBQ,KAAK,ECMtB,WAAW,EANG,6BAAwB;AFknBrC,wCAAU,GACT,aAAa,EAAE,MAAM;AAGvB,4CAAc,GACb,WAAW,EAAE,4BAA2B;AAEzC,2CAAa,GACZ,YAAY,EAAE,4BAA2B",
"sources": ["../scss/GridField.scss","../admin/scss/themes/_default.scss","../../../../usr/local/share/gems/gems/compass-core-1.0.3/stylesheets/compass/css3/_text-shadow.scss","../../../../usr/local/share/gems/gems/compass-core-1.0.3/stylesheets/compass/css3/_inline-block.scss","../admin/scss/_mixins.scss","../../../../usr/local/share/gems/gems/compass-core-1.0.3/stylesheets/compass/_support.scss","../../../../usr/local/share/gems/gems/compass-core-1.0.3/stylesheets/compass/css3/_border-radius.scss","../../../../usr/local/share/gems/gems/compass-core-1.0.3/stylesheets/compass/css3/_images.scss","../scss/_elementMixins.scss"],
"names": [],
"file": "GridField.css"
}

View File

@ -1,3 +1,5 @@
.ss-gridfield-pagination { text-align: center; padding-bottom: 10px; } .ss-gridfield-pagination { text-align: center; padding-bottom: 10px; }
.ss-gridfield-pagination-button.loading { background: url(../images/network-save.gif) no-repeat 0% 50%; padding-left: 20px; } .ss-gridfield-pagination-button.loading { background: url(../images/network-save.gif) no-repeat 0% 50%; padding-left: 20px; }
/*# sourceMappingURL=GridFieldPaginator.css.map */

View File

@ -0,0 +1,7 @@
{
"version": 3,
"mappings": "AAAA,wBAAyB,GACxB,UAAU,EAAE,MAAM,EAClB,cAAc,EAAE,IAAI;;AAErB,uCAAuC,GACtC,UAAU,EAAE,gDAAgD,EAAE,YAAY,EAAE,IAAI",
"sources": ["../scss/GridFieldPaginator.scss"],
"names": [],
"file": "GridFieldPaginator.css"
}

View File

@ -5,3 +5,5 @@ table th { border-bottom: 2px solid #333; padding: 5px 10px; font-weight: bold;
table th:first-child { padding-left: 0px; } table th:first-child { padding-left: 0px; }
table td { border-top: 1px solid #aaa; border-bottom: 1px solid #aaa; text-align: left; padding: 5px 10px; } table td { border-top: 1px solid #aaa; border-bottom: 1px solid #aaa; text-align: left; padding: 5px 10px; }
table td:first-child { padding-left: 0px; } table td:first-child { padding-left: 0px; }
/*# sourceMappingURL=GridField_print.css.map */

View File

@ -0,0 +1,7 @@
{
"version": 3,
"mappings": "AAAA,EAAG,GACF,WAAW,EAAE,iBAAiB;;AAE/B,KAAM,GACL,eAAe,EAAE,QAAQ,EACzB,WAAW,EAAE,iBAAiB,EAC9B,KAAK,EAAE,IAAI,EACX,SAAS,EAAE,IAAI;AAIf,QAAE,GACD,aAAa,EAAE,cAAc,EAC7B,OAAO,EAAE,QAAQ,EACjB,WAAW,EAAE,IAAI,EACjB,UAAU,EAAE,IAAI;AAChB,oBAAa,GACZ,YAAY,EAAE,GAAG;AAInB,QAAE,GACD,UAAU,EAAE,cAAc,EAC1B,aAAa,EAAE,cAAc,EAC7B,UAAU,EAAE,IAAI,EAChB,OAAO,EAAE,QAAQ;AACjB,oBAAa,GACZ,YAAY,EAAE,GAAG",
"sources": ["../scss/GridField_print.scss"],
"names": [],
"file": "GridField_print.css"
}

View File

@ -7,3 +7,5 @@ p#Remember label { display: inline-block; margin: 0; }
#MemberLoginForm_LoginForm .Actions { padding-left: 12em; } #MemberLoginForm_LoginForm .Actions { padding-left: 12em; }
#ForgotPassword { margin-top: 1em; } #ForgotPassword { margin-top: 1em; }
/*# sourceMappingURL=Security_login.css.map */

View File

@ -0,0 +1,7 @@
{
"version": 3,
"mappings": "AAAA,SAAU,GACT,MAAM,EAAE,6BAA6B;;AAErC,gBAAiB,GAChB,OAAO,EAAE,YAAY,EACrB,MAAM,EAAE,CAAC;;AAEV,eAAgB,GACf,KAAK,EAAE,IAAI,EACX,MAAM,EAAE,SAAS;;AAEnB,mCAAoC,GACnC,YAAY,EAAE,IAAI;;AAEnB,eAAgB,GACf,UAAU,EAAE,GAAG",
"sources": ["../scss/Security_login.scss"],
"names": [],
"file": "Security_login.css"
}

View File

@ -9,3 +9,5 @@
.mainblock.allassets ul { padding: 0 0 0 10px; height: 100px; overflow-y: scroll; } .mainblock.allassets ul { padding: 0 0 0 10px; height: 100px; overflow-y: scroll; }
.mainblock.allassets ul li { width: 150px; } .mainblock.allassets ul li { width: 150px; }
/*# sourceMappingURL=SelectionGroup.css.map */

View File

@ -0,0 +1,7 @@
{
"version": 3,
"mappings": "AAAA,eAAgB,GACf,OAAO,EAAE,GAAG,EACZ,KAAK,EAAG,IAAI;AAEZ,kBAAG,GACF,eAAe,EAAE,IAAI,EACrB,KAAK,EAAG,IAAI,EACZ,KAAK,EAAE,IAAI,EACX,KAAK,EAAG,IAAI;AAEb,iCAAkB,GACjB,KAAK,EAAE,IAAI,EACX,KAAK,EAAG,IAAI;AAEb,4CAA6B,GAC5B,OAAO,EAAE,IAAI,EACb,SAAS,EAAE,GAAG;AAEf,yEAA0C,GACzC,OAAO,EAAE,KAAK;AAEf,qDAAsC,GACrC,WAAW,EAAE,IAAI,EACjB,OAAO,EAAE,KAAK,EACd,aAAa,EAAE,GAAG;AAEnB,0CAA2B,GAC1B,WAAW,EAAE,IAAI;;AAInB,uBAAwB,GACrB,OAAO,EAAE,UAAU,EACnB,MAAM,EAAC,KAAK,EACZ,UAAU,EAAC,MAAM;;AAEpB,0BAA2B,GACxB,KAAK,EAAE,KAAK",
"sources": ["../scss/SelectionGroup.scss"],
"names": [],
"file": "SelectionGroup.css"
}

View File

@ -5,3 +5,5 @@ div.report span.submitted { font-style: italic; }
div.report table * { font-size: 1em; } div.report table * { font-size: 1em; }
div.report table td.field { font-weight: bold; padding-right: 10px; padding-left: 0px; } div.report table td.field { font-weight: bold; padding-right: 10px; padding-left: 0px; }
/*# sourceMappingURL=SubmittedFormReportField.css.map */

View File

@ -0,0 +1,7 @@
{
"version": 3,
"mappings": "AAAA,gBAAiB,GAChB,WAAW,EAAE,IAAI,EACjB,aAAa,EAAE,IAAI;;AAGpB,yBAA0B,GACzB,UAAU,EAAE,MAAM;;AAGnB,kBAAmB,GAClB,SAAS,EAAE,GAAG;;AAGf,yBAA0B,GACzB,WAAW,EAAE,IAAI,EACjB,aAAa,EAAE,IAAI,EACnB,YAAY,EAAE,GAAG",
"sources": ["../scss/SubmittedFormReportField.scss"],
"names": [],
"file": "SubmittedFormReportField.css"
}

View File

@ -19,3 +19,5 @@ div.TreeDropdownField .treedropdownfield-toggle-panel-link { border: none; margi
div.TreeDropdownField .treedropdownfield-toggle-panel-link.treedropdownfield-open-tree { background: transparent; border: none; } div.TreeDropdownField .treedropdownfield-toggle-panel-link.treedropdownfield-open-tree { background: transparent; border: none; }
div.TreeDropdownField .treedropdownfield-toggle-panel-link a { text-decoration: none; display: block; border: 0; margin: 0; opacity: 0.5; } div.TreeDropdownField .treedropdownfield-toggle-panel-link a { text-decoration: none; display: block; border: 0; margin: 0; opacity: 0.5; }
div.TreeDropdownField a.jstree-loading .jstree-pageicon { background: #fff url("../images/network-save.gif") center center no-repeat; } div.TreeDropdownField a.jstree-loading .jstree-pageicon { background: #fff url("../images/network-save.gif") center center no-repeat; }
/*# sourceMappingURL=TreeDropdownField.css.map */

View File

@ -0,0 +1,7 @@
{
"version": 3,
"mappings": ";;;AAGA,qBAAsB,GACrB,KAAK,EAAE,KAAK,EACZ,UAAU,EAAE,IAAI,EAChB,MAAM,EAAE,cAAc,EACtB,MAAM,EAAE,OAAO,EACf,QAAQ,EAAE,OAAO,EACjB,QAAQ,EAAC,QAAQ;AAEjB,2BAAM,GACL,MAAM,EAAE,IAAI,EACZ,UAAU,EAAE,IAAI,EAChB,OAAO,EAAE,CAAC,EACV,MAAM,EAAE,CAAC;AAGV,+FAAyB,GACxB,KAAK,EAAE,IAAI,EACX,OAAO,EAAE,GAAG,EACZ,KAAK,EAAE,GAAG,EACV,WAAW,EAAE,IAAI,EACjB,QAAQ,EAAC,MAAM,EACf,OAAO,EAAE,IAAI,EACb,OAAO,EAAC,CAAC,ECVV,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,MAAM,EAInB,aAAa,EAAE,QAAQ,EACvB,gBAAgB,EAAE,QAAQ;ADQ1B,+CAAyB,GAMxB,UAAU,EAAC,+EAAkC,EEqCzC,UAAY,EAAE,kmBAAgC,EAA9C,UAAY,EAAE,8KAAgC,EAA9C,UAAY,EAAE,mIAAgC,EAA9C,UAAY,EAAE,sIAAgC,EAE9C,UAAY,EAAE,oIAAO,ECkRzB,eAAwC,EC1U5B,UAAmB,ED0U/B,kBAAwC,EC1U5B,UAAmB,ED0U/B,UAAwC,EC1U5B,UAAmB,EJuB/B,QAAQ,EAAC,QAAQ,EACjB,OAAO,EAAC,IAAI,EACZ,MAAM,EAAE,cAAc,EACtB,OAAO,EAAC,YAAY,EACpB,WAAW,EAAE,UAAU,EACvB,SAAS,EAAE,GAAG,EACd,MAAM,EAAC,IAAI,EACX,OAAO,EAAE,CAAC,EACV,OAAO,EAAE,gBAAgB,EACzB,KAAK,EAAC,GAAG;AAGV,iEAA6C,GAC5C,UAAU,EAAE,IAA4E,EACxF,mBAAmB,EAAE,QAAQ;AAG9B,8CAAyB,GACxB,KAAK,EAAE,IAAI,EACX,QAAQ,EAAE,QAAQ,EAClB,OAAO,EAAE,IAAI,EACb,MAAM,EAAE,OAAO,EACf,MAAM,EAAE,cAAc,EACtB,UAAU,EAAE,IAAI,EAChB,MAAM,EAAE,YAAY,4CACpB,gBAAgB,EAAE,IAAI,EACtB,OAAO,EAAE,EAAE,EACX,kBAAkB,EAAE,6BAAyB,EAC7C,eAAe,EAAK,6BAAyB,EAC7C,aAAa,EAAO,6BAAyB,EAC7C,UAAU,EAAU,6BAAyB;AAE7C,sDAAU,GACT,UAAU,EAAE,IAAoC,EAChD,UAAU,EAAE,wDAAwD;AAGrE,2DAAY,GACX,QAAQ,EAAC,QAAQ,EACjB,OAAO,EAAC,CAAC;AACT,gEAAI,GACH,QAAQ,EAAC,QAAQ,EACjB,UAAU,EAAC,KAAK,EAChB,UAAU,EAAE,IAAI;AAIlB,iDAAE,GACD,UAAU,EAAC,MAAM,EACjB,KAAK,EAAC,IAAI,EACV,KAAK,EAAC,IAAI;AACV,8DAAY,GACX,WAAW,EAAC,GAAG;AAEhB,oEAAkB,GACjB,mBAAmB,EAAC,OAAO;AAI7B,sDAAQ,GACP,MAAM,EAAE,CAAC;AACT,wDAAE,GACD,SAAS,EAAE,IAAI;AAKlB,0DAAqC,GACpC,MAAM,EAAE,IAAI,EACZ,MAAM,EAAE,CAAC,EACT,OAAO,EAAE,CAAC,EACV,OAAO,EAAE,OAAO,EAChB,QAAQ,EAAE,MAAM,EAChB,qBAAqB,EAAE,WAAW,EAClC,kBAAkB,EAAE,WAAW,EAC/B,aAAa,EAAE,WAAW;AAE1B,sFAA8B,GAC7B,UAAU,EAAE,WAAW,EACvB,MAAM,EAAE,IAAI;AAGb,4DAAE,GACD,eAAe,EAAE,IAAI,EACrB,OAAO,EAAE,KAAK,EACd,MAAM,EAAE,CAAC,EACT,MAAM,EAAE,CAAC,EACT,OAAO,EAAE,GAAG;AAId,uDAAkC,GAEjC,UAAU,EAAE,8DAA8D",
"sources": ["../scss/TreeDropdownField.scss","../admin/scss/_mixins.scss","../../../../lib/gems/1.9.1/gems/compass-core-1.0.3/stylesheets/compass/css3/_images.scss","../../../../lib/gems/1.9.1/gems/compass-core-1.0.3/stylesheets/compass/_support.scss","../../../../lib/gems/1.9.1/gems/compass-core-1.0.3/stylesheets/compass/css3/_box-sizing.scss"],
"names": [],
"file": "TreeDropdownField.css"
}

View File

@ -35,3 +35,5 @@ CODE.expected { color: green; background: none; font-weight: normal; }
CODE.actual { color: red; background: none; font-weight: normal; } CODE.actual { color: red; background: none; font-weight: normal; }
.typeinfo { color: gray; } .typeinfo { color: gray; }
/*# sourceMappingURL=UnitTesting.css.map */

7
css/UnitTesting.css.map Normal file
View File

@ -0,0 +1,7 @@
{
"version": 3,
"mappings": "AACA,IAAK,GACJ,IAAI,EAAC,kCAAkC,EACvC,KAAK,EAAC,OAAO;;AAEd,wBAAyB,GACrB,SAAS,EAAE,GAAG;;AAElB,mBAAmB,GAClB,WAAW,EAAE,IAAI,EACjB,UAAU,EAAC,IAAI,EACf,UAAU,EAAC,OAAO;;AAEnB,gBAAgB,GACf,UAAU,EAAC,OAAO;;AAGnB,CAAE,GACD,WAAW,EAAC,KAAK,EACjB,UAAU,EAAC,KAAK,EAAE,aAAa,EAAC,KAAK;;AAEtC,EAAG,GACF,MAAM,EAAE,WAAW,EAAE,IAAI,EAAE,8BAAY;;AAExC,EAAG,GACF,UAAU,EAAE,GAAG,EAAE,aAAa,EAAE,KAAK,EAAE,IAAI,EAAE,mCAAiB;;AAE/D,EAAG,GACF,aAAa,EAAE,KAAK,EAAE,IAAI,EAAE,mCAAiB;;AAE9C,EAAG,GACF,aAAa,EAAE,KAAK,EAAE,IAAI,EAAE,mCAAiB;;AAE9C,EAAG,GACF,aAAa,EAAE,KAAK,EAAE,IAAI,EAAE,mCAAiB;;AAE9C,EAAG,GACF,aAAa,EAAE,KAAK,EAAE,IAAI,EAAE,mCAAiB;;AAE9C,MAAO,GACN,WAAW,EAAC,IAAI,EAAE,KAAK,EAAC,GAAG;;AAE5B,qBAAsB,GACrB,UAAU,EAAC,OAAO,EAAE,WAAW,EAAC,IAAI,EAAE,KAAK,EAAC,KAAK;;AAElD,QAAS,GACR,UAAU,EAAC,OAAO,EAAE,WAAW,EAAC,IAAI,EAAE,KAAK,EAAC,KAAK;;AAElD,gBAAiB,GAChB,UAAU,EAAC,OAAO,EAAE,WAAW,EAAC,IAAI,EAAE,KAAK,EAAC,KAAK;;AAElD,WAAY,GACV,UAAU,EAAC,KAAK;;AAGlB,aAAc,GACb,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM;;AAEpD,WAAY,GACX,KAAK,EAAE,GAAG,EAAE,UAAU,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM;;AAElD,SAAU,GACT,KAAK,EAAE,IAAI",
"sources": ["../scss/UnitTesting.scss"],
"names": [],
"file": "UnitTesting.css"
}

View File

@ -12,14 +12,14 @@ Used in side panels and action tabs
*/ */
.ss-uploadfield .clear { clear: both; } .ss-uploadfield .clear { clear: both; }
.ss-uploadfield .description { margin-left: 0; } .ss-uploadfield .description { margin-left: 0; }
.ss-uploadfield .middleColumn { min-width: 510px; max-width: 600px; width: 100%; margin-left: 0; clear: both; padding: 0; background: #fff; border: 1px solid #b3b3b3; -moz-border-radius: 4px; -webkit-border-radius: 4px; border-radius: 4px; background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2VmZWZlZiIvPjxzdG9wIG9mZnNldD0iMTAlIiBzdG9wLWNvbG9yPSIjZmZmZmZmIi8+PHN0b3Agb2Zmc2V0PSI5MCUiIHN0b3AtY29sb3I9IiNmZmZmZmYiLz48c3RvcCBvZmZzZXQ9IjEwMCUiIHN0b3AtY29sb3I9IiNlZmVmZWYiLz48L2xpbmVhckdyYWRpZW50PjwvZGVmcz48cmVjdCB4PSIwIiB5PSIwIiB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSJ1cmwoI2dyYWQpIiAvPjwvc3ZnPiA='); background-size: 100%; background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #efefef), color-stop(10%, #ffffff), color-stop(90%, #ffffff), color-stop(100%, #efefef)); background-image: -moz-linear-gradient(#efefef, #ffffff 10%, #ffffff 90%, #efefef); background-image: -webkit-linear-gradient(#efefef, #ffffff 10%, #ffffff 90%, #efefef); background-image: linear-gradient(#efefef, #ffffff 10%, #ffffff 90%, #efefef); } .ss-uploadfield .middleColumn { min-width: 510px; max-width: 696px; width: 100%; margin-left: 0; clear: both; padding: 0; background: #fff; border: 1px solid #b3b3b3; -moz-border-radius: 4px; -webkit-border-radius: 4px; border-radius: 4px; background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2VmZWZlZiIvPjxzdG9wIG9mZnNldD0iMTAlIiBzdG9wLWNvbG9yPSIjZmZmZmZmIi8+PHN0b3Agb2Zmc2V0PSI5MCUiIHN0b3AtY29sb3I9IiNmZmZmZmYiLz48c3RvcCBvZmZzZXQ9IjEwMCUiIHN0b3AtY29sb3I9IiNlZmVmZWYiLz48L2xpbmVhckdyYWRpZW50PjwvZGVmcz48cmVjdCB4PSIwIiB5PSIwIiB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSJ1cmwoI2dyYWQpIiAvPjwvc3ZnPiA='); background-size: 100%; background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #efefef), color-stop(10%, #ffffff), color-stop(90%, #ffffff), color-stop(100%, #efefef)); background-image: -moz-linear-gradient(#efefef, #ffffff 10%, #ffffff 90%, #efefef); background-image: -webkit-linear-gradient(#efefef, #ffffff 10%, #ffffff 90%, #efefef); background-image: linear-gradient(#efefef, #ffffff 10%, #ffffff 90%, #efefef); }
.ss-uploadfield .ss-uploadfield-item { margin: 0; padding: 15px; overflow: auto; } .ss-uploadfield .ss-uploadfield-item { margin: 0; padding: 15px; overflow: auto; }
.ss-uploadfield .ss-uploadfield-item .ss-uploadfield-item-preview { height: 60px; line-height: 60px; width: 80px; text-align: center; font-weight: bold; float: left; overflow: hidden; } .ss-uploadfield .ss-uploadfield-item .ss-uploadfield-item-preview { height: 60px; line-height: 60px; width: 80px; text-align: center; font-weight: bold; float: left; overflow: hidden; }
.ss-uploadfield .ss-uploadfield-item .ss-uploadfield-item-preview.ss-uploadfield-dropzone { -moz-box-shadow: #808080 0 0 4px 0 inset; -webkit-box-shadow: #808080 0 0 4px 0 inset; box-shadow: #808080 0 0 4px 0 inset; border: 2px dashed #808080; background: #d0d3d5; display: none; margin-right: 15px; } .ss-uploadfield .ss-uploadfield-item .ss-uploadfield-item-preview.ss-uploadfield-dropzone { -moz-box-shadow: #808080 0 0 4px 0 inset; -webkit-box-shadow: #808080 0 0 4px 0 inset; box-shadow: #808080 0 0 4px 0 inset; border: 2px dashed #808080; background: #D2D5D8; display: none; margin-right: 15px; }
.ss-uploadfield .ss-uploadfield-item .ss-uploadfield-item-info { margin-left: 95px; } .ss-uploadfield .ss-uploadfield-item .ss-uploadfield-item-info { margin-left: 95px; }
.ss-uploadfield .ss-uploadfield-item .ss-uploadfield-item-info .ss-uploadfield-item-name { display: block; line-height: 13px; height: 26px; margin: 0; text-align: left; } .ss-uploadfield .ss-uploadfield-item .ss-uploadfield-item-info .ss-uploadfield-item-name { display: block; line-height: 13px; height: 26px; margin: 0; text-align: left; }
.ss-uploadfield .ss-uploadfield-item .ss-uploadfield-item-info .ss-uploadfield-item-name .name { max-width: 240px; font-weight: bold; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; -o-text-overflow: ellipsis; display: inline; float: left; } .ss-uploadfield .ss-uploadfield-item .ss-uploadfield-item-info .ss-uploadfield-item-name .name { max-width: 240px; font-weight: bold; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; -o-text-overflow: ellipsis; display: inline; float: left; }
.ss-uploadfield .ss-uploadfield-item .ss-uploadfield-item-info .ss-uploadfield-item-name .size { color: #848484; padding: 0 0 0 5px; display: inline; float: left; } .ss-uploadfield .ss-uploadfield-item .ss-uploadfield-item-info .ss-uploadfield-item-name .size { color: #a9b2b9; padding: 0 0 0 5px; display: inline; float: left; }
.ss-uploadfield .ss-uploadfield-item .ss-uploadfield-item-info .ss-uploadfield-item-name .ss-uploadfield-item-status { float: right; padding: 0 0 0 5px; text-align: right; max-width: 75%; } .ss-uploadfield .ss-uploadfield-item .ss-uploadfield-item-info .ss-uploadfield-item-name .ss-uploadfield-item-status { float: right; padding: 0 0 0 5px; text-align: right; max-width: 75%; }
.ss-uploadfield .ss-uploadfield-item .ss-uploadfield-item-info .ss-uploadfield-item-name .ss-uploadfield-item-status.ui-state-error-text { color: #f00; font-weight: bold; width: 150px; } .ss-uploadfield .ss-uploadfield-item .ss-uploadfield-item-info .ss-uploadfield-item-name .ss-uploadfield-item-status.ui-state-error-text { color: #f00; font-weight: bold; width: 150px; }
.ss-uploadfield .ss-uploadfield-item .ss-uploadfield-item-info .ss-uploadfield-item-name .ss-uploadfield-item-status.ui-state-warning-text { color: #b7a403; } .ss-uploadfield .ss-uploadfield-item .ss-uploadfield-item-info .ss-uploadfield-item-name .ss-uploadfield-item-status.ui-state-warning-text { color: #b7a403; }
@ -34,7 +34,7 @@ Used in side panels and action tabs
.ss-uploadfield .ss-ui-button { display: block; float: left; margin: 0 10px 6px 0; } .ss-uploadfield .ss-ui-button { display: block; float: left; margin: 0 10px 6px 0; }
.ss-uploadfield .ss-ui-button.ss-uploadfield-fromcomputer { position: relative; overflow: hidden; } .ss-uploadfield .ss-ui-button.ss-uploadfield-fromcomputer { position: relative; overflow: hidden; }
.ss-uploadfield .ss-uploadfield-files { margin: 0; padding: 0; overflow: auto; position: relative; } .ss-uploadfield .ss-uploadfield-files { margin: 0; padding: 0; overflow: auto; position: relative; }
.ss-uploadfield .ss-uploadfield-files .ss-uploadfield-item, .ss-uploadfield .ss-uploadfield-files .ss-uploadfield-item.ui-state-error { border: 0; border-bottom: 1px solid #b3b3b3; background: none; color: #444; } .ss-uploadfield .ss-uploadfield-files .ss-uploadfield-item, .ss-uploadfield .ss-uploadfield-files .ss-uploadfield-item.ui-state-error { border: 0; border-bottom: 1px solid #b3b3b3; background: none; color: #66727d; }
.ss-uploadfield .ss-uploadfield-files .ss-uploadfield-item:last-child, .ss-uploadfield .ss-uploadfield-files .ss-uploadfield-item.ui-state-error:last-child { border-bottom: 0; } .ss-uploadfield .ss-uploadfield-files .ss-uploadfield-item:last-child, .ss-uploadfield .ss-uploadfield-files .ss-uploadfield-item.ui-state-error:last-child { border-bottom: 0; }
.ss-uploadfield .ss-uploadfield-files .ss-uploadfield-item-actions { min-height: 28px; overflow: hidden; margin: 6px 0 -6px 0; position: relative; } .ss-uploadfield .ss-uploadfield-files .ss-uploadfield-item-actions { min-height: 28px; overflow: hidden; margin: 6px 0 -6px 0; position: relative; }
.ss-uploadfield .ss-uploadfield-files .ss-uploadfield-item-progress { position: absolute; left: 0; right: 42px; width: auto; margin: 11px 0 0; height: 15px; } .ss-uploadfield .ss-uploadfield-files .ss-uploadfield-item-progress { position: absolute; left: 0; right: 42px; width: auto; margin: 11px 0 0; height: 15px; }
@ -49,7 +49,7 @@ Used in side panels and action tabs
.ss-uploadfield .ss-uploadfield-files .ss-uploadfield-item-editform { /* don't use display none, for it will break jQuery('iframe').contents().height() */ height: 0; overflow: hidden; clear: both; } .ss-uploadfield .ss-uploadfield-files .ss-uploadfield-item-editform { /* don't use display none, for it will break jQuery('iframe').contents().height() */ height: 0; overflow: hidden; clear: both; }
.ss-uploadfield .ss-uploadfield-files .ss-uploadfield-item-editform.loading { width: 100%; height: 22px; margin: 15px 0 0; background: url(../admin/images/spinner.gif) no-repeat 50% 0; } .ss-uploadfield .ss-uploadfield-files .ss-uploadfield-item-editform.loading { width: 100%; height: 22px; margin: 15px 0 0; background: url(../admin/images/spinner.gif) no-repeat 50% 0; }
.ss-uploadfield .ss-uploadfield-files .ss-uploadfield-item-editform.loading iframe { /* Old IE needs this or it'll give the iframe a white background, covering the spinner */ padding-top: 0; margin-top: 22px; border: none; } .ss-uploadfield .ss-uploadfield-files .ss-uploadfield-item-editform.loading iframe { /* Old IE needs this or it'll give the iframe a white background, covering the spinner */ padding-top: 0; margin-top: 22px; border: none; }
.ss-uploadfield .ss-uploadfield-files .ss-uploadfield-item-editform iframe { margin-top: 8px; padding-top: 8px; border-top: 1px solid #d0d3d5; width: 100%; } .ss-uploadfield .ss-uploadfield-files .ss-uploadfield-item-editform iframe { margin-top: 8px; padding-top: 8px; border-top: 1px solid #D2D5D8; width: 100%; }
.ss-uploadfield .ss-uploadfield-addfile.borderTop { border-top: 1px solid #b3b3b3; } .ss-uploadfield .ss-uploadfield-addfile.borderTop { border-top: 1px solid #b3b3b3; }
.ss-upload .clear { clear: both; } .ss-upload .clear { clear: both; }
@ -59,3 +59,5 @@ Used in side panels and action tabs
/** ---------------------------------------------------- "@2x" media query ---------------------------------------------------- */ /** ---------------------------------------------------- "@2x" media query ---------------------------------------------------- */
@media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2 / 1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { .ss-uploadfield .ss-uploadfield-files .ss-uploadfield-item-editform.loading { background-image: url(../admin/images/spinner@2x.gif); background-size: 43px 43px; } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2 / 1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx) { .ss-uploadfield .ss-uploadfield-files .ss-uploadfield-item-editform.loading { background-image: url(../admin/images/spinner@2x.gif); background-size: 43px 43px; }
.ss-upload .loader { background-image: url(../admin/images/spinner@2x.gif); background-size: 43px 43px; } } .ss-upload .loader { background-image: url(../admin/images/spinner@2x.gif); background-size: 43px 43px; } }
/*# sourceMappingURL=UploadField.css.map */

7
css/UploadField.css.map Normal file
View File

@ -0,0 +1,7 @@
{
"version": 3,
"mappings": ";;;;;;;;;;;;AAQC,sBAAO,GACN,KAAK,EAAE,IAAI;AAGZ,4BAAa,GACZ,WAAW,EAAE,CAAC;AAGf,6BAAc,GAGb,SAAS,EAAE,KAAK,EAChB,SAAS,EAAE,KAAY,EACvB,KAAK,EAAC,IAAI,EACV,WAAW,EAAC,CAAC,EACb,KAAK,EAAC,IAAI,EACV,OAAO,EAAE,CAAC,EACV,UAAU,EAAE,IAAI,EAChB,MAAM,EAAE,iBAA+C,ECkUvD,kBAAwC,EDjUjB,GAAG,ECiU1B,qBAAwC,EC9Sb,GAAuB,ED8SlD,aAAwC,EDjUjB,GAAG,EG6CtB,gBAAY,EAAE,ioBAAgC,EA2B9C,eAAe,EAAE,IAAI,EA3BrB,gBAAY,EAAE,kJAAgC,EAA9C,gBAAY,EAAE,gEAAgC,EAA9C,gBAAY,EAAE,mEAAgC,EAE9C,gBAAY,EAAE,2DAAO;AH5C1B,oCAAqB,GACpB,MAAM,EAAE,CAAC,EACT,OAAO,EAAE,IAAI,EACb,QAAQ,EAAE,IAAI;AAEd,iEAA6B,GAC5B,MAAM,EAAE,IAAI,EACZ,WAAW,EAAE,IAAI,EACjB,KAAK,EAAE,IAAI,EACX,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,IAAI,EACjB,KAAK,EAAE,IAAI,EACX,QAAQ,EAAE,MAAM;AAChB,yFAA0B,GCiT3B,eAAwC,EGnT/B,uBAAkD,EHmT3D,kBAAwC,EGnT/B,uBAAkD,EHmT3D,UAAwC,EGnT/B,uBAAkD,EJIzD,MAAM,EAAE,kBAAkC,EAC1C,UAAU,EK5BU,OAAO,EL6B3B,OAAO,EAAE,IAAI,EACb,YAAY,EAAE,IAAI;AAGpB,8DAA0B,GACzB,WAAW,EAAE,IAAI;AAEjB,wFAA0B,GACzB,OAAO,EAAE,KAAK,EACd,WAAW,EAAE,IAAI,EACjB,MAAM,EAAE,IAAI,EACZ,MAAM,EAAE,CAAC,EACT,UAAU,EAAE,IAAI;AAChB,8FAAM,GACL,SAAS,EAAE,KAAK,EAChB,WAAW,EAAE,IAAI,EM/CrB,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,MAAM,EAInB,aAAa,EAAE,QAAQ,EACvB,gBAAgB,EAAE,QAAQ,EN2CtB,OAAO,EAAC,MAAM,EACd,KAAK,EAAC,IAAI;AAEX,8FAAM,GACL,KAAK,EAAE,OAAyB,EAChC,OAAO,EAAE,SAAS,EAClB,OAAO,EAAC,MAAM,EACd,KAAK,EAAC,IAAI;AAEX,oHAA4B,GAC3B,KAAK,EAAE,KAAK,EACZ,OAAO,EAAE,SAAS,EAClB,UAAU,EAAC,KAAK,EAChB,SAAS,EAAE,GAAG;AAEd,wIAAsB,GACrB,KAAK,EKtBgB,IAAI,ELuBzB,WAAW,EAAE,IAAI,EACjB,KAAK,EAAC,KAAK;AAGZ,0IAAwB,GACvB,KAAK,EAAE,OAA2B;AAGnC,0IAAwB,GACvB,KAAK,EKnCiB,OAAO;AL4ChC,gFAA6B,GAC5B,KAAK,EAAE,IAAI,EACX,MAAM,EAAE,IAAI,EACZ,YAAY,EAAE,IAAI;AAGnB,6EAA0B,GACzB,WAAW,EAAE,CAAC;AAEd,uGAA0B,GACzB,KAAK,EAAE,IAAI,EACX,KAAK,EAAE,GAAG,EACV,MAAM,EAAE,IAAI;AAEZ,6GACA,GACC,KAAK,EAAE,IAAI,EACX,KAAK,EAAE,IAAI,EACX,aAAa,EAAE,GAAG;AAGnB,mIAA4B,GAC3B,KAAK,EAAE,IAAI,EACX,KAAK,EAAE,IAAI,EACX,OAAO,EAAE,CAAC,EACV,UAAU,EAAE,IAAI;AAIlB,0GAA6B,GAC5B,KAAK,EAAE,KAAK,EACZ,KAAK,EAAE,EAAE,EACT,UAAU,EAAE,CAAC,EACb,MAAM,EAAE,CAAC;AAET,sIAA4B,GAC3B,QAAQ,EAAE,QAAQ,EAClB,GAAG,EAAE,IAAI;AAMd,6BAAc,GACb,OAAO,EAAE,KAAK,EACd,KAAK,EAAE,IAAI,EACX,MAAM,EAAE,YAAY;AAEpB,yDAA8B,GAC7B,QAAQ,EAAE,QAAQ,EAClB,QAAQ,EAAE,MAAM;AAGlB,qCAAsB,GACrB,MAAM,EAAE,CAAC,EACT,OAAO,EAAE,CAAC,EACV,QAAQ,EAAE,IAAI,EACd,QAAQ,EAAE,QAAQ;AAElB,qIACoC,GACnC,MAAM,EAAE,CAAC,EACT,aAAa,EAAE,iBAA+C,EAC9D,UAAU,EAAE,IAAI,EAChB,KAAK,EK5HK,OAAO;AL8HjB,2JAAa,GACZ,aAAa,EAAE,CAAC;AAGlB,kEAA6B,GAC5B,UAAU,EAAE,IAAI,EAChB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,YAAY,EACpB,QAAQ,EAAE,QAAQ;AAEnB,mEAA8B,GAC7B,QAAQ,EAAE,QAAQ,EAClB,IAAI,EAAE,CAAC,EACP,KAAK,EAAE,IAAI,EACX,KAAK,EAAE,IAAI,EACX,MAAM,EAAE,QAAQ,EAChB,MAAM,EAAE,IAAI;AAEZ,uEAAI,GCqKL,kBAAwC,EDpKf,IAAI,ECoK7B,qBAAwC,EC9Sb,IAAuB,ED8SlD,aAAwC,EDpKf,IAAI,EAC3B,MAAM,EAAE,IAAI,EACZ,OAAO,EAAE,CAAC,EACV,MAAM,EAAE,CAAC,EACT,QAAQ,EAAE,MAAM;AAGlB,sEAAiC,GAChC,MAAM,EAAE,iBAAiC,EACzC,gBAAgB,EAAE,OAAO,EGzHtB,gBAAY,EAAE,6uBAAgC,EA2B9C,eAAe,EAAE,IAAI,EA3BrB,gBAAY,EAAE,sMAAgC,EAA9C,gBAAY,EAAE,uGAAgC,EAA9C,gBAAY,EAAE,0GAAgC,EAE9C,gBAAY,EAAE,wGAAO;AH0HzB,2EAAsC,GACrC,MAAM,EAAE,CAAC,EACT,KAAK,EAAE,EAAE,EACT,UAAU,EAAE,gEAAgE;AAE7E,mIAC2B,GAC1B,QAAQ,EAAE,QAAQ,EAClB,GAAG,EAAE,IAAI,EACT,KAAK,EAAE,CAAC;AAER,iJAAO,GACN,OAAO,EAAE,KAAK,EACd,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,OAAO,EACpB,OAAO,EAAE,CAAC,EACV,MAAM,EAAE,CAAC,EACT,MAAM,EAAE,CAAC,EACT,KAAK,EAAE,IAAI,EACX,MAAM,EAAE,IAAI,EACZ,MAAM,EAAE,OAAO,ECoIjB,eAAwC,EGnT/B,eAAkD,EHmT3D,kBAAwC,EGnT/B,eAAkD,EHmT3D,UAAwC,EGnT/B,eAAkD,EJiLzD,QAAQ,EAAE,QAAQ;AAGlB,2JAAK,GACJ,QAAQ,EAAE,QAAQ,EAClB,IAAI,EAAE,CAAC,EACP,GAAG,EAAE,CAAC,EACN,MAAM,EAAE,CAAC;AAET,yLAAiB,GAChB,OAAO,EAAE,IAAI;AAKjB,gEAA2B,GAC1B,KAAK,EAAE,IAAI;AAKZ,mEAA8B,wFAE7B,MAAM,EAAE,CAAC,EACT,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,IAAI;AAEX,2EAAU,GACT,KAAK,EAAE,IAAI,EACX,MAAM,EAAE,IAAI,EACZ,MAAM,EAAE,QAAQ,EAChB,UAAU,EAAE,gDAAgD;AAE5D,kFAAO,6FAEN,WAAW,EAAE,CAAC,EAAE,UAAU,EAAE,IAAI,EAChC,MAAM,EAAE,IAAI;AAId,0EAAO,GACN,UAAU,EKzKL,GAAG,EL0KR,WAAW,EK1KN,GAAG,EL2KR,UAAU,EAAE,iBAAgC,EAC5C,KAAK,EAAE,IAAI;AAKb,iDAAY,GACX,UAAU,EAAE,iBAA+C;;AAM7D,iBAAO,GACN,KAAK,EAAE,IAAI;AAGX,6CAAM,kHAEL,QAAQ,EAAE,QAAQ,EAClB,GAAG,EAAE,CAAC,EACN,KAAK,EAAE,CAAC,EACR,MAAM,EAAE,CAAC,EACT,OAAO,EAAE,CAAC,EACV,MAAM,EAAE,gBAAgB,EACxB,SAAS,EAAE,6BAA6B,EACxC,SAAS,EAAE,IAAI,EACf,SAAS,EAAE,GAAG,EACd,MAAM,EAAE,OAAO,EACf,MAAM,EAAE,IAAI,EACZ,WAAW,EAAE,IAAI;AAGnB,kBAAQ,GACP,MAAM,EAAE,IAAI,EACZ,UAAU,EAAE,8DAA8D;;;AAQ5E,gSAMyD,GAKrD,2EAAU,GACT,gBAAgB,EAAE,mCAAmC,EACrD,eAAe,EAAE,SAAS;EAO7B,kBAAQ,GACP,gBAAgB,EAAE,mCAAmC,EACrD,eAAe,EAAE,SAAS",
"sources": ["../scss/UploadField.scss","../../../../usr/local/share/gems/gems/compass-core-1.0.3/stylesheets/compass/_support.scss","../../../../usr/local/share/gems/gems/compass-core-1.0.3/stylesheets/compass/css3/_border-radius.scss","../../../../usr/local/share/gems/gems/compass-core-1.0.3/stylesheets/compass/css3/_images.scss","../../../../usr/local/share/gems/gems/compass-core-1.0.3/stylesheets/compass/css3/_box-shadow.scss","../admin/scss/themes/_default.scss","../admin/scss/_mixins.scss"],
"names": [],
"file": "UploadField.css"
}

View File

@ -33,3 +33,5 @@ fieldset { border: 1px solid #b2b2b2; margin-bottom: 18px; padding: 17px; }
.pass { margin-top: 18px; padding: 2px 20px 2px 40px; color: #006600; background: #E2F9E3; border: 1px solid #8DD38D; border-radius: 4px; } .pass { margin-top: 18px; padding: 2px 20px 2px 40px; color: #006600; background: #E2F9E3; border: 1px solid #8DD38D; border-radius: 4px; }
.fail { margin-top: 18px; padding: 2px 20px 2px 40px; color: #C80700; background: #FFE9E9; border: 1px solid #C80700; border-radius: 4px; } .fail { margin-top: 18px; padding: 2px 20px 2px 40px; color: #C80700; background: #FFE9E9; border: 1px solid #C80700; border-radius: 4px; }
/*# sourceMappingURL=debug.css.map */

7
css/debug.css.map Normal file
View File

@ -0,0 +1,7 @@
{
"version": 3,
"mappings": "AAEA,IAAK,GACH,UAAU,EAAE,eAAe,EAC3B,MAAM,EAAC,CAAC,EACR,UAAU,EAAE,MAAM,EAClB,OAAO,EAAC,CAAC,EACT,WAAW,EAAE,0BAA0B;;AAGzC,KAAM,GACL,MAAM,EAAC,SAAS,EAChB,OAAO,EAAE,IAAI,EACb,gBAAgB,EAAE,OAAO,EACzB,QAAQ,EAAE,QAAQ,EAClB,WAAW,EAAE,IAAI,EACjB,KAAK,EAAE,IAAI,ECwDN,gBAAY,EAAE,ioBAAgC,EA2B9C,eAAe,EAAE,IAAI,EA3BrB,gBAAY,EAAE,kJAAgC,EAA9C,gBAAY,EAAE,gEAAgC,EAA9C,gBAAY,EAAE,mEAAgC,EAE9C,gBAAY,EAAE,2DAAO,EDnD1B,OAAO,EAAE,IAAI;AACb,QAAG,GACF,MAAM,EAAE,SAAS,EACjB,OAAO,EAAE,UAAU,EACnB,KAAK,EAAE,IAAI,EACX,SAAS,EAAE,IAAI,EACf,WAAW,EAAE,aAAyB,EACtC,WAAW,EAAE,IAAI,EAEjB,UAAU,EAAE,uDAAuD;AAGpE,QAAG,GACF,KAAK,EAAE,OAAO,EACd,SAAS,EAAE,IAAI,EACf,WAAW,EAAE,IAAI,EACjB,WAAW,EAAE,MAAM;AAEpB,OAAE,GACD,MAAM,EAAE,CAAC,EACT,SAAS,EAAE,IAAI,EACf,KAAK,EAAE,IAAI;AAGZ,OAAE,GACD,KAAK,EAAE,IAAI,EACX,WAAW,EAAE,IAAI,EACjB,eAAe,EAAE,IAAI;AAErB,6BACS,GACR,KAAK,EAAE,IAAI,EACX,eAAe,EAAE,SAAS;;AAO7B,OAAQ,GACP,MAAM,EAAE,CAAC,EACT,aAAa,EAAE,iBAAiB,EAChC,MAAM,EAAE,IAAI,EACZ,gBAAgB,EAAE,OAAO,EACzB,OAAO,EAAE,aAAa;;AAGvB,wBAES,GACR,OAAO,EAAC,QAAQ,EAEhB,UAAU,EAAE,eAAe,EAE3B,QAAQ,EAAE,QAAQ,EAClB,OAAO,EAAE,IAAI;AACb,iCAAG,GACF,SAAS,EAAC,IAAI,EACd,MAAM,EAAC,KAAK;;AAId,CAAE,GACD,KAAK,EAAE,IAAI;AAEX,OAAQ,GACP,KAAK,EAAE,IAAI;AAGZ,QAAS,GACR,KAAK,EAAE,IAAI;;AAIb,CAAE,GACD,aAAa,EAAE,GAAG;;AAGnB,GAAI,GACH,aAAa,EAAE,IAAI,EACnB,gBAAgB,EAAE,OAAO,EACzB,MAAM,EAAE,cAAc,EACtB,MAAM,EAAE,6BAAyB,EACjC,KAAK,EAAE,IAAI,EACX,OAAO,EAAE,IAAI,EACb,QAAQ,EAAE,IAAI,EEgPb,kBAAwC,EF9OlB,GAAG,EE8OzB,qBAAwC,EC9Sb,GAAuB,ED8SlD,aAAwC,EF9OlB,GAAG,EE8OzB,eAAwC,EEnT/B,mCAAkD,EFmT3D,kBAAwC,EEnT/B,mCAAkD,EFmT3D,UAAwC,EEnT/B,mCAAkD;AJwE3D,QAAK,GACL,KAAK,EAAC,IAAI;AAEX,UAAO,GACN,KAAK,EAAC,IAAI;;AAIZ,EAAG,GACF,MAAM,EAAE,UAAU;;AAGnB,EAAG,GACF,MAAM,EAAE,SAAS,EACjB,KAAK,EAAE,IAAI,EACX,SAAS,EAAE,IAAI,EACf,WAAW,EAAE,IAAI;;AAGlB,EAAG,GACF,MAAM,EAAE,UAAU,EAClB,OAAO,EAAE,UAAU;;AAGpB,QAAS,GACR,MAAM,EAAE,iBAAiB,EACzB,aAAa,EAAE,IAAI,EACnB,OAAO,EAAE,IAAI;;AAGd,KAAM,GACL,UAAU,EAAC,IAAI,EAAE,OAAO,EAAC,iBAAiB,EAAE,KAAK,EAAC,OAAO,EAAE,UAAU,EAAC,OAAO,EAAE,MAAM,EAAE,iBAAiB,EACxG,aAAa,EAAC,GAAG;;AAGlB,KAAM,GACL,UAAU,EAAC,IAAI,EAAE,OAAO,EAAC,iBAAiB,EAAE,KAAK,EAAC,OAAO,EAAE,UAAU,EAAC,OAAO,EAC7E,MAAM,EAAC,iBAAiB,EAAE,aAAa,EAAC,GAAG",
"sources": ["../scss/debug.scss","../../../../lib/gems/1.9.1/gems/compass-core-1.0.3/stylesheets/compass/css3/_images.scss","../../../../lib/gems/1.9.1/gems/compass-core-1.0.3/stylesheets/compass/_support.scss","../../../../lib/gems/1.9.1/gems/compass-core-1.0.3/stylesheets/compass/css3/_border-radius.scss","../../../../lib/gems/1.9.1/gems/compass-core-1.0.3/stylesheets/compass/css3/_box-shadow.scss"],
"names": [],
"file": "debug.css"
}

View File

@ -524,7 +524,8 @@ function exceptionHandler($exception) {
$file = $exception->getFile(); $file = $exception->getFile();
$line = $exception->getLine(); $line = $exception->getLine();
$context = $exception->getTrace(); $context = $exception->getTrace();
return Debug::fatalHandler($errno, $message, $file, $line, $context); Debug::fatalHandler($errno, $message, $file, $line, $context);
exit(1);
} }
/** /**
@ -558,6 +559,7 @@ function errorHandler($errno, $errstr, $errfile, $errline) {
case E_CORE_ERROR: case E_CORE_ERROR:
case E_USER_ERROR: case E_USER_ERROR:
default: default:
return Debug::fatalHandler($errno, $errstr, $errfile, $errline, debug_backtrace()); Debug::fatalHandler($errno, $errstr, $errfile, $errline, debug_backtrace());
exit(1);
} }
} }

View File

@ -42,6 +42,19 @@ class Deprecation {
*/ */
protected static $version; protected static $version;
/**
* Override whether deprecation is enabled. If null, then fallback to
* SS_DEPRECATION_ENABLED, and then true if not defined.
*
* Deprecation is only available on dev.
*
* Must be configured outside of the config API, as deprecation API
* must be available before this to avoid infinite loops.
*
* @var boolean|null
*/
protected static $enabled = null;
/** /**
* *
* @var array * @var array
@ -117,19 +130,46 @@ class Deprecation {
} }
} }
/**
* Determine if deprecation notices should be displayed
*
* @return bool
*/
public static function get_enabled() {
// Deprecation is only available on dev
if(!Director::isDev()) {
return false;
}
if(isset(self::$enabled)) {
return self::$enabled;
}
if(defined('SS_DEPRECATION_ENABLED')) {
return SS_DEPRECATION_ENABLED;
}
return true;
}
/**
* Toggle on or off deprecation notices. Will be ignored in live.
*
* @param bool $enabled
*/
public static function set_enabled($enabled) {
self::$enabled = $enabled;
}
/** /**
* Raise a notice indicating the method is deprecated if the version passed as the second argument is greater * Raise a notice indicating the method is deprecated if the version passed as the second argument is greater
* than or equal to the check version set via ::notification_version * than or equal to the check version set via ::notification_version
* *
* @static * @param string $atVersion The version at which this notice should start being raised
* @param $string - The notice to raise * @param string $string The notice to raise
* @param $atVersion - The version at which this notice should start being raised * @param bool $scope Notice relates to the method or class context its called in.
* @param Boolean $scope - Notice relates to the method or class context its called in.
* @return void
*/ */
public static function notice($atVersion, $string = '', $scope = Deprecation::SCOPE_METHOD) { public static function notice($atVersion, $string = '', $scope = Deprecation::SCOPE_METHOD) {
// Never raise deprecation notices in a live environment if(!static::get_enabled()) {
if(Director::isLive(true)) return; return;
}
$checkVersion = self::$version; $checkVersion = self::$version;
// Getting a backtrace is slow, so we only do it if we need it // Getting a backtrace is slow, so we only do it if we need it
@ -179,25 +219,27 @@ class Deprecation {
/** /**
* Method for when testing. Dump all the current version settings to a variable for later passing to restore * Method for when testing. Dump all the current version settings to a variable for later passing to restore
* @return array - opaque array that should only be used to pass to ::restore_version_settings *
* @return array Opaque array that should only be used to pass to {@see Deprecation::restore_settings()}
*/ */
public static function dump_settings() { public static function dump_settings() {
return array( return array(
'level' => self::$notice_level, 'level' => self::$notice_level,
'version' => self::$version, 'version' => self::$version,
'moduleVersions' => self::$module_version_overrides 'moduleVersions' => self::$module_version_overrides,
'enabled' => self::$enabled,
); );
} }
/** /**
* Method for when testing. Restore all the current version settings from a variable * Method for when testing. Restore all the current version settings from a variable
* @static *
* @param $settings array - An array as returned by ::dump_version_settings * @param $settings array An array as returned by {@see Deprecation::dump_settings()}
* @return void
*/ */
public static function restore_settings($settings) { public static function restore_settings($settings) {
self::$notice_level = $settings['level']; self::$notice_level = $settings['level'];
self::$version = $settings['version']; self::$version = $settings['version'];
self::$module_version_overrides = $settings['moduleVersions']; self::$module_version_overrides = $settings['moduleVersions'];
self::$enabled = $settings['enabled'];
} }
} }

View File

@ -31,18 +31,18 @@ class SS_LogEmailWriter extends Zend_Log_Writer_Abstract {
} }
/** /**
* @deprecated 3.2 Use the "SS_LogEmailWriter.send_from" config setting instead * @deprecated 4.0 Use the "SS_LogEmailWriter.send_from" config setting instead
*/ */
public static function set_send_from($address) { public static function set_send_from($address) {
Deprecation::notice('3.2', 'Use the "SS_LogEmailWriter.send_from" config setting instead'); Deprecation::notice('4.0', 'Use the "SS_LogEmailWriter.send_from" config setting instead');
Config::inst()->update('SS_LogEmailWriter', 'send_from', $address); Config::inst()->update('SS_LogEmailWriter', 'send_from', $address);
} }
/** /**
* @deprecated 3.2 Use the "SS_LogEmailWriter.send_from" config setting instead * @deprecated 4.0 Use the "SS_LogEmailWriter.send_from" config setting instead
*/ */
public static function get_send_from() { public static function get_send_from() {
Deprecation::notice('3.2', 'Use the "SS_LogEmailWriter.send_from" config setting instead'); Deprecation::notice('4.0', 'Use the "SS_LogEmailWriter.send_from" config setting instead');
return Config::inst()->get('SS_LogEmailWriter', 'send_from'); return Config::inst()->get('SS_LogEmailWriter', 'send_from');
} }

View File

@ -16,7 +16,7 @@
* protected $description = "Description"; // description of what it does * protected $description = "Description"; // description of what it does
* *
* public function run($request) { * public function run($request) {
* if ($request->param('Direction') == 'down') { * if ($request->getVar('Direction') == 'down') {
* $this->down(); * $this->down();
* } else { * } else {
* $this->up(); * $this->up();

View File

@ -43,7 +43,11 @@ class SapphireTest extends PHPUnit_Framework_TestCase {
*/ */
protected $usesDatabase = null; protected $usesDatabase = null;
/**
* @deprecated since version 4.0
*/
protected $originalMailer; protected $originalMailer;
protected $originalMemberPasswordValidator; protected $originalMemberPasswordValidator;
protected $originalRequirements; protected $originalRequirements;
protected $originalIsRunningTest; protected $originalIsRunningTest;
@ -157,9 +161,20 @@ class SapphireTest extends PHPUnit_Framework_TestCase {
return static::$fixture_file; return static::$fixture_file;
} }
/**
* @var array $fixtures Array of {@link YamlFixture} instances
* @deprecated 3.1 Use $fixtureFactory instad
*/
protected $fixtures = array();
protected $model; protected $model;
public function setUp() { public function setUp() {
//nest config and injector for each test so they are effectively sandboxed per test
Config::nest();
Injector::nest();
// We cannot run the tests on this abstract class. // We cannot run the tests on this abstract class.
if(get_class($this) == "SapphireTest") $this->skipTest = true; if(get_class($this) == "SapphireTest") $this->skipTest = true;
@ -210,7 +225,7 @@ class SapphireTest extends PHPUnit_Framework_TestCase {
// Set up email // Set up email
$this->originalMailer = Email::mailer(); $this->originalMailer = Email::mailer();
$this->mailer = new TestMailer(); $this->mailer = new TestMailer();
Email::set_mailer($this->mailer); Injector::inst()->registerService($this->mailer, 'Mailer');
Config::inst()->remove('Email', 'send_all_emails_to'); Config::inst()->remove('Email', 'send_all_emails_to');
// Todo: this could be a special test model // Todo: this could be a special test model
@ -284,6 +299,10 @@ class SapphireTest extends PHPUnit_Framework_TestCase {
* for tearing down the state again. * for tearing down the state again.
*/ */
public function setUpOnce() { public function setUpOnce() {
//nest config and injector for each suite so they are effectively sandboxed
Config::nest();
Injector::nest();
$isAltered = false; $isAltered = false;
if(!Director::isDev()) { if(!Director::isDev()) {
@ -332,24 +351,12 @@ class SapphireTest extends PHPUnit_Framework_TestCase {
* tearDown method that's called once per test class rather once per test method. * tearDown method that's called once per test class rather once per test method.
*/ */
public function tearDownOnce() { public function tearDownOnce() {
// If we have made changes to the extensions present, then migrate the database schema. //unnest injector / config now that the test suite is over
if($this->extensionsToReapply || $this->extensionsToRemove) { // this will reset all the extensions on the object too (see setUpOnce)
// Remove extensions added for testing Injector::unnest();
foreach($this->extensionsToRemove as $class => $extensions) { Config::unnest();
foreach($extensions as $extension) {
$class::remove_extension($extension);
}
}
// Reapply ones removed if(!empty($this->extensionsToReapply) || !empty($this->extensionsToRemove) || !empty($this->extraDataObjects)) {
foreach($this->extensionsToReapply as $class => $extensions) {
foreach($extensions as $extension) {
$class::add_extension($extension);
}
}
}
if($this->extensionsToReapply || $this->extensionsToRemove || $this->extraDataObjects) {
$this->resetDBSchema(); $this->resetDBSchema();
} }
} }
@ -468,10 +475,7 @@ class SapphireTest extends PHPUnit_Framework_TestCase {
ini_set('memory_limit', ($this->originalMemoryLimit) ? $this->originalMemoryLimit : -1); ini_set('memory_limit', ($this->originalMemoryLimit) ? $this->originalMemoryLimit : -1);
// Restore email configuration // Restore email configuration
if($this->originalMailer) { $this->originalMailer = null;
Email::set_mailer($this->originalMailer);
$this->originalMailer = null;
}
$this->mailer = null; $this->mailer = null;
// Restore password validation // Restore password validation
@ -499,6 +503,9 @@ class SapphireTest extends PHPUnit_Framework_TestCase {
$controller->response->setStatusCode(200); $controller->response->setStatusCode(200);
$controller->response->removeHeader('Location'); $controller->response->removeHeader('Location');
} }
//unnest injector / config now that tests are over
Injector::unnest();
Config::unnest();
} }
public static function assertContains( public static function assertContains(
@ -763,7 +770,6 @@ class SapphireTest extends PHPUnit_Framework_TestCase {
$this->assertNotContains($needleSQL, $haystackSQL, $message, $ignoreCase, $checkForObjectIdentity); $this->assertNotContains($needleSQL, $haystackSQL, $message, $ignoreCase, $checkForObjectIdentity);
} }
/** /**
* Helper function for the DOS matchers * Helper function for the DOS matchers
*/ */

View File

@ -415,6 +415,13 @@ class InstallRequirements {
"Is the mysite/_config.php file writeable?", "Is the mysite/_config.php file writeable?",
null null
)); ));
$this->requireWriteable('mysite/_config/config.yml', array(
"File permissions",
"Is the mysite/_config/config.yml file writeable?",
null
));
if(!$this->checkModuleExists('cms')) { if(!$this->checkModuleExists('cms')) {
$this->requireWriteable('mysite/code/RootURLController.php', array( $this->requireWriteable('mysite/code/RootURLController.php', array(
"File permissions", "File permissions",

View File

@ -8,7 +8,7 @@ Our web-based [PHP installer](installation/) can check if you meet the requireme
## Web server software requirements ## Web server software requirements
* PHP 5.3.2+ * PHP 5.3.3+
* We recommend using a PHP accelerator or opcode cache, such as [xcache](http://xcache.lighttpd.net/) or [WinCache](http://www.iis.net/download/wincacheforphp). * We recommend using a PHP accelerator or opcode cache, such as [xcache](http://xcache.lighttpd.net/) or [WinCache](http://www.iis.net/download/wincacheforphp).
* Allocate at least 48MB of memory to each PHP process. (SilverStripe can be resource hungry for some intensive operations.) * Allocate at least 48MB of memory to each PHP process. (SilverStripe can be resource hungry for some intensive operations.)
* Required modules: dom, gd2, fileinfo, hash, iconv, mbstring, mysqli (or other database driver), session, simplexml, tokenizer, xml. * Required modules: dom, gd2, fileinfo, hash, iconv, mbstring, mysqli (or other database driver), session, simplexml, tokenizer, xml.

View File

@ -116,6 +116,7 @@ This is my `_ss_environment.php` file. I have it placed in `/var`, as each of th
| `SS_DATABASE_TIMEZONE`| Set the database timezone to something other than the system timezone. | `SS_DATABASE_TIMEZONE`| Set the database timezone to something other than the system timezone.
| `SS_DATABASE_NAME` | Set the database name. Assumes the `$database` global variable in your config is missing or empty. | | `SS_DATABASE_NAME` | Set the database name. Assumes the `$database` global variable in your config is missing or empty. |
| `SS_DATABASE_CHOOSE_NAME`| Boolean/Int. If defined, 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.| | `SS_DATABASE_CHOOSE_NAME`| Boolean/Int. If defined, 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.|
| `SS_DEPRECATION_ENABLED` | Enable deprecation notices for this environment.|
| `SS_ENVIRONMENT_TYPE`| The environment type: dev, test or live.| | `SS_ENVIRONMENT_TYPE`| The environment type: dev, test or live.|
| `SS_DEFAULT_ADMIN_USERNAME`| The username of the default admin. This is a user with administrative privileges.| | `SS_DEFAULT_ADMIN_USERNAME`| The username of the default admin. This is a user with administrative privileges.|
| `SS_DEFAULT_ADMIN_PASSWORD`| The password of the default admin. This will not be stored in the database.| | `SS_DEFAULT_ADMIN_PASSWORD`| The password of the default admin. This will not be stored in the database.|

View File

@ -9,8 +9,8 @@ lets you install and upgrade SilverStripe and its modules. Although installing
Other ways to get SilverStripe: Other ways to get SilverStripe:
* If you just want to get the code as quickly as possible, you can [download SilverStripe from our website](http://silverstripe.org/download). * If you just want to get the code as quickly as possible, you can [download SilverStripe from our website](http://www.silverstripe.org/software/download/).
* If you already have an installed version of SilverStripe, and you haven't used Composer to get it, please see our [upgrading](upgrading) guide. Note that [Composer](composer) provides its own tools for upgrading. * If you already have an installed version of SilverStripe, and you haven't used Composer to get it, please see our [upgrading](/upgrading) guide. Note that [Composer](composer) provides its own tools for upgrading.
## Setting up a server ## Setting up a server
@ -18,25 +18,25 @@ Other ways to get SilverStripe:
To run SilverStripe on Linux/Unix, set up one of the following web servers: To run SilverStripe on Linux/Unix, set up one of the following web servers:
* [Install using Apache](webserver) - our preferred platform * [Install using Apache](installation) - our preferred platform
* [Install using Lighttpd](lighttpd) - fast, but a bit tricker to get going * [Install using Lighttpd](installation/how_to/configure_lighttpd) - fast, but a bit trickier to get going
* [Install using Nginx](nginx) - Super fast at serving static files. Great for large traffic sites. * [Install using Nginx](installation/how_to/configure_nginx) - Super fast at serving static files. Great for large traffic sites.
* [Install using nginx and HHVM](nginx-hhvm) - nginx and [HHVM](http://hhvm.com/) as a faster alternative to PHP * [Install using nginx and HHVM](installation/how_to/setup_nginx_and_hhvm) - nginx and [HHVM](http://hhvm.com/) as a faster alternative to PHP.
### Windows ### Windows
The most straightforward way to get SilverStripe running on Windows is with the [Microsoft Web Platform installer](windows-pi). You can skip the "getting the code" step. The most straightforward way to get SilverStripe running on Windows is with the [Microsoft Web Platform installer](installation/other_installation_options/windows_platform_installer). You can skip the "getting the code" step.
For more flexibility, you can set up either of the following web servers, and use Composer to get the code: For more flexibility, you can set up either of the following web servers, and use Composer to get the code:
* [Install using IIS](windows-manual-iis) * [Install using IIS](installation/other_installation_options/windows_iis7)
* [Install using Apache/WAMP](windows-wamp) * [Install using Apache/WAMP](installation/windows)
### Mac OS X ### Mac OS X
Mac OS X comes with a built-in webserver, but there are a number of other options: Mac OS X comes with a built-in webserver, but there are a number of other options:
* [Install using MAMP](mac-osx) * [Install using MAMP](installation/mac_osx)
* [Install using Homebrew](installation/other_installation_options/mac_osx_homebrew) * [Install using Homebrew](installation/other_installation_options/mac_osx_homebrew)
### Virtual Machines through Vagrant ### Virtual Machines through Vagrant
@ -61,10 +61,9 @@ ready for download or installation on a cloud platform.
## Troubleshooting ## Troubleshooting
If you run into trouble, see [common-problems](common-problems) or post to the If you run into trouble, see [common-problems](installation/common_problems) or post to the
[SilverStripe forums](http://silverstripe.com/silverstripe-forum/). [SilverStripe forums](http://silverstripe.org/community/forums/).
## Related ## Related
* [Module installation](../topics/modules) * [Module installation](/developer_guides/extending/modules)
* [Suggested web hosts](http://doc.silverstripe.org/old/suggested-web-hosts)

View File

@ -27,6 +27,7 @@ These include video screencasts, written tutorials and code examples to get you
* [Lesson 15: Building a Search Form](http://www.silverstripe.org/learn/lessons/building-a-search-form) * [Lesson 15: Building a Search Form](http://www.silverstripe.org/learn/lessons/building-a-search-form)
* [Lesson 16: Lists and Pagination](http://www.silverstripe.org/learn/lessons/lists-and-pagination) * [Lesson 16: Lists and Pagination](http://www.silverstripe.org/learn/lessons/lists-and-pagination)
* [Lesson 17: Ajax Behaviour and Viewable Data](http://www.silverstripe.org/learn/lessons/ajax-behaviour-and-viewabledata) * [Lesson 17: Ajax Behaviour and Viewable Data](http://www.silverstripe.org/learn/lessons/ajax-behaviour-and-viewabledata)
* [Lesson 18: Dealing with Arbitrary Template Data](http://www.silverstripe.org/learn/lessons/dealing-with-arbitrary-template-data)
## Help: If you get stuck ## Help: If you get stuck

View File

@ -80,7 +80,7 @@ does, such as `ArrayData` or `ArrayList`.
'Name' => 'John', 'Name' => 'John',
'Role' => 'Head Coach', 'Role' => 'Head Coach',
'Experience' => $experience 'Experience' => $experience
))->renderWith("AjaxTemplate"); )))->renderWith("AjaxTemplate");
} else { } else {
return $this->httpError(404); return $this->httpError(404);
} }

View File

@ -56,7 +56,7 @@ First we need to define a callback for the shortcode.
'MyShortCodeMethod' => 'HTMLText' 'MyShortCodeMethod' => 'HTMLText'
); );
public function MyShortCodeMethod($arguments, $content = null, $parser = null, $tagName) { public static function MyShortCodeMethod($arguments, $content = null, $parser = null, $tagName) {
return "<em>" . $tagName . "</em> " . $content . "; " . count($arguments) . " arguments."; return "<em>" . $tagName . "</em> " . $content . "; " . count($arguments) . " arguments.";
} }
} }

View File

@ -182,18 +182,10 @@ end of each test.
$page->publish('Stage', 'Live'); $page->publish('Stage', 'Live');
} }
// reset configuration for the test. // set custom configuration for the test.
Config::nest();
Config::inst()->update('Foo', 'bar', 'Hello!'); Config::inst()->update('Foo', 'bar', 'Hello!');
} }
public function tearDown() {
// restores the config variables
Config::unnest();
parent::tearDown();
}
public function testMyMethod() { public function testMyMethod() {
// .. // ..
} }
@ -224,6 +216,32 @@ individual test case.
} }
} }
### Config and Injector Nesting
A powerful feature of both [`Config`](/developer_guides/configuration/configuration/) and [`Injector`](/developer_guides/extending/injector/) is the ability to "nest" them so that you can make changes that can easily be discarded without having to manage previous values.
The testing suite makes use of this to "sandbox" each of the unit tests as well as each suite to prevent leakage between tests.
If you need to make changes to `Config` (or `Injector) for each test (or the whole suite) you can safely update `Config` (or `Injector`) settings in the `setUp` or `tearDown` functions.
It's important to remember that the `parent::setUp();` functions will need to be called first to ensure the nesting feature works as expected.
:::php
function setUpOnce() {
parent::setUpOnce();
//this will remain for the whole suite and be removed for any other tests
Config::inst()->update('ClassName', 'var_name', 'var_value');
}
function testFeatureDoesAsExpected() {
//this will be reset to 'var_value' at the end of this test function
Config::inst()->update('ClassName', 'var_name', 'new_var_value');
}
function testAnotherFeatureDoesAsExpected() {
Config::inst()->get('ClassName', 'var_name'); // this will be 'var_value'
}
## Generating a Coverage Report ## Generating a Coverage Report
PHPUnit can generate a code coverage report ([docs](http://www.phpunit.de/manual/current/en/code-coverage-analysis.html)) PHPUnit can generate a code coverage report ([docs](http://www.phpunit.de/manual/current/en/code-coverage-analysis.html))

View File

@ -117,14 +117,16 @@ To use this backend, you need a memcached daemon and the memcache PECL extension
'primary_memcached', 'primary_memcached',
'Memcached', 'Memcached',
array( array(
'host' => 'localhost', 'servers' => array(
'port' => 11211, 'host' => 'localhost',
'persistent' => true, 'port' => 11211,
'weight' => 1, 'persistent' => true,
'timeout' => 5, 'weight' => 1,
'retry_interval' => 15, 'timeout' => 5,
'status' => true, 'retry_interval' => 15,
'failure_callback' => '' 'status' => true,
'failure_callback' => ''
)
) )
); );
SS_Cache::pick_backend('primary_memcached', 'any', 10); SS_Cache::pick_backend('primary_memcached', 'any', 10);

View File

@ -39,6 +39,43 @@ records and cannot easily be adapted to include custom `DataObject` instances. T
default site search, have a look at those extensions and modify as required. default site search, have a look at those extensions and modify as required.
</div> </div>
### Fulltext Filter
SilverStripe provides a `[api:FulltextFiler]` which you can use to perform custom fulltext searches on
`[api:DataList]`'s.
Example DataObject:
:::php
class SearchableDataObject extends DataObject {
private static $db = array(
"Title" => "Varchar(255)",
"Content" => "HTMLText",
);
private static $indexes = array(
'SearchFields' => array(
'type' => 'fulltext',
'name' => 'SearchFields',
'value' => '"Title", "Content"',
)
);
private static $create_table_options = array(
'MySQLDatabase' => 'ENGINE=MyISAM'
);
}
Performing the search:
:::php
SearchableDataObject::get()->filter('SearchFields:fulltext', 'search term');
If your search index is a single field size, then you may also specify the search filter by the name of the
field instead of the index.
## API Documentation ## API Documentation
* [api:FulltextSearchable] * [api:FulltextSearchable]

View File

@ -1,6 +1,6 @@
# How to customize the CMS Menu # How to customize the CMS Menu
## Adding a administration panel ## Adding an administration panel
Every time you add a new extension of the `[api:LeftAndMain]` class to the CMS, Every time you add a new extension of the `[api:LeftAndMain]` class to the CMS,
SilverStripe will automatically create a new `[api:CMSMenuItem]` for it SilverStripe will automatically create a new `[api:CMSMenuItem]` for it

View File

@ -40,7 +40,7 @@ Never update a website on the live server without trying it on a development cop
How easy will it be to update my project? It's a fair question, and sometimes a difficult one to answer. How easy will it be to update my project? It's a fair question, and sometimes a difficult one to answer.
* "Micro" releases (x.y.z) are explicitly backwards compatible, "minor" and "major" releases can deprecate features and change APIs (see our [/misc/release-process](release process) for details) * "Micro" releases (x.y.z) are explicitly backwards compatible, "minor" and "major" releases can deprecate features and change APIs (see our [/misc/release-process](../contributing/release_process) for details)
* If you've made custom branches of SilverStripe core, or any thirdparty module, it's going to be harder to upgrade. * If you've made custom branches of SilverStripe core, or any thirdparty module, it's going to be harder to upgrade.
* The more custom features you have, the harder it will be to upgrade. You will have to re-test all of those features, and adapt to API changes in core. * The more custom features you have, the harder it will be to upgrade. You will have to re-test all of those features, and adapt to API changes in core.
* Customizations of a well defined type - such as custom page types or custom blog widgets - are going to be easier to upgrade than customisations that modify deep system internals like rewriting SQL queries. * Customizations of a well defined type - such as custom page types or custom blog widgets - are going to be easier to upgrade than customisations that modify deep system internals like rewriting SQL queries.

View File

@ -55,7 +55,7 @@
* Implementation of a parameterised query framework eliminating the need to manually escape variables for * Implementation of a parameterised query framework eliminating the need to manually escape variables for
use in SQL queries. This has been integrated into nearly every level of the database ORM. use in SQL queries. This has been integrated into nearly every level of the database ORM.
* Refactor of database connectivity classes into separate components linked together through dependency injection * Refactor of database connectivity classes into separate components linked together through dependency injection
* Refactor of `SQLQuery` into separate objects for each query type: `SQLQuery`, `SQLDelete`, `SQLUpdate` and `SQLInsert` * Refactor of `SQLQuery` into separate objects for each query type: `SQLSelect`, `SQLDelete`, `SQLUpdate` and `SQLInsert`
* PDO is now a standard connector, and is available for all database interfaces * PDO is now a standard connector, and is available for all database interfaces
* `DataObject::doValidate()` method visibility added to access `DataObject::validate` externally * `DataObject::doValidate()` method visibility added to access `DataObject::validate` externally
* `NumericField` now uses HTML5 "number" type instead of "text" * `NumericField` now uses HTML5 "number" type instead of "text"
@ -73,6 +73,8 @@
* Security: The multiple authenticator login page should now be styled manually - i.e. without the default jQuery * Security: The multiple authenticator login page should now be styled manually - i.e. without the default jQuery
UI layout. A new template, Security_MultiAuthenticatorLogin.ss is available. UI layout. A new template, Security_MultiAuthenticatorLogin.ss is available.
* Security: This controller's templates can be customised by overriding the `getTemplatesFor` function. * Security: This controller's templates can be customised by overriding the `getTemplatesFor` function.
* `Deprecation::set_enabled()` or `SS_DEPRECATION_ENABLED` can now be used to
enable or disable deprecation notices. Deprecation notices are no longer displayed on test.
* API: Form and FormField ID attributes rewritten. * API: Form and FormField ID attributes rewritten.
* `SearchForm::getSearchQuery` no longer pre-escapes search keywords and must * `SearchForm::getSearchQuery` no longer pre-escapes search keywords and must
be cast in your template be cast in your template
@ -455,6 +457,26 @@ After:
$query->execute(); $query->execute();
When working with SQLQuery passed into user code, it is advisable to strictly
cast it into either a SQLSelect or SQLDelete. This can be done by using the new
`SQLQuery::toAppropriateExpression()` method, which will automatically convert
to the correct type based on whether the SQLQuery is set to delete or not.
If a SQLQuery is not converted, then the result of `getWhere` will not be parameterised.
This is because user code written for 3.1 expects this list to be a flat array
of strings. This format is inherently unsafe, and should be avoided where possible.
:::php
<?php
public function augmentSQL(SQLQuery &$query) {
$query->getWhere(); // Will be flattened (unsafe 3.1 compatible format)
$expression = $query->toAppropriateExpression(); // Either SQLSelect or SQLDelete
$expression->getWhere(); // Will be parameterised (preferred 3.2 compatible format)
}
Alternatively: Alternatively:
@ -463,7 +485,8 @@ Alternatively:
$query = SQLQuery::create() $query = SQLQuery::create()
->setFrom('"SiteTree"') ->setFrom('"SiteTree"')
->setWhere(array('"SiteTree"."ShowInMenus"' => 0)) ->setWhere(array('"SiteTree"."ShowInMenus"' => 0))
->toDelete(); ->setDelete(true)
->toAppropriateExpression();
$query->execute(); $query->execute();
@ -616,12 +639,14 @@ E.g.
DB::prepared_query('DELETE FROM "MyObject" WHERE ParentID = ? OR IsValid = ?', $parameters); DB::prepared_query('DELETE FROM "MyObject" WHERE ParentID = ? OR IsValid = ?', $parameters);
#### 4. Interaction with `SQLQuery::getWhere()` method #### 4. Interaction with `SQLSelect::getWhere()` method
As all where conditions are now parameterised, the format of the results returned by `SQLQuery::getWhere()` The `SQLSelect` class supercedes the old `SQLQuery` object for performing select queries. Although
will not always equate with that in FrameWork 3.1. Once this would be a list of strings, what will both implement the `getWhere()` method, the results returned by `SQLSelect::getWhere()` will be
now be returned is a list of conditions, each of which is an associative array mapping the parameterised while `SQLQuery::getWhere()` will be a flattened array of strings.
condition string to a list of parameters provided.
`SQLSelect::getWhere()` returns a list of conditions, each of which is an
associative array mapping the condition string to a list of parameters provided.
Before: Before:
@ -630,6 +655,7 @@ Before:
<?php <?php
// Increment value of a single condition // Increment value of a single condition
$query = new SQLQuery(/*...*/);
$conditions = $query->getWhere(); $conditions = $query->getWhere();
$new = array(); $new = array();
foreach($conditions as $condition) { foreach($conditions as $condition) {
@ -646,6 +672,7 @@ After:
:::php :::php
// Increment value of a single condition // Increment value of a single condition
$query = new SQLSelect(/*...*/);
$conditions = $query->getWhere(); $conditions = $query->getWhere();
$new = array(); $new = array();
foreach($conditions as $condition) { foreach($conditions as $condition) {
@ -665,7 +692,7 @@ replace this method call with the new `getWhereParameterised($parameters)` metho
applicable. applicable.
This method returns a manipulated form of the where conditions stored by the query, so This method returns a manipulated form of the where conditions stored by the query, so
that it matches the list of strings consistent with the old 3.1 `SQLQuery::getWhere()` behaviour. that it matches the list of strings similar to the old 3.1 `SQLQuery::getWhere()` behaviour.
Additionally, the list of parameters is safely extracted, flattened, and can be passed out Additionally, the list of parameters is safely extracted, flattened, and can be passed out
through the `$parameters` argument which is passed by reference. through the `$parameters` argument which is passed by reference.

View File

@ -0,0 +1,923 @@
# 3.2.0 beta1
## Contents
* [Major Changes](#major-changes)
* [Removed API](#deprecated-classesmethods-removed)
* [New API](#new-and-changed-api)
* [Bugfixes](#bugfixes)
* [Upgrading Notes](#upgrading-notes)
* [Changelog](#changelog)
## Major changes
* Minimum PHP version raised to 5.3.3
* Introduction of new parameterised ORM
* Default support for PDO
* Moved SS_Report and ReportAdmin out to a separate module. If you're using
composer or downloading a release, this module should be included for you.
Otherwise, you'll need to include the module yourself
(https://github.com/silverstripe-labs/silverstripe-reports)
* Moved SiteConfig also out to its own module. This will be included by
default if you include the CMS module.
(https://github.com/silverstripe/silverstripe-siteconfig)
* Implementation of new "Archive" concept for page removal, which supercedes
"delete from draft". Where deletion removed pages only from draft, archiving
removes from both draft and live simultaneously.
* Most of the `Image` manipulation methods have been renamed
## Deprecated classes/methods removed
* `ToggleField` was deprecated in 3.1, and has been removed. Use custom Javascript with `ReadonlyField` instead.
* `ExactMatchMultiFilter` was deprecated in 3.1, and has been removed. Use `ExactMatchFilter` instead.
* `NegationFilter` was deprecated in 3.1, and has been removed. Use `ExactMatchFilter:not` instead.
* `StartsWithMultiFilter` was deprecated in 3.1, and has been removed. Use `StartsWithFilter` instead.
* `ScheduledTask` and subclasses like `DailyTask` were deprecated in 3.1, and have been removed.
Use custom code instead, or a module like silverstripe-crontask: https://github.com/silverstripe-labs/silverstripe-crontask
* `Cookie::forceExpiry()` was removed. Use `Cookie::force_expiry()` instead
* `Object` statics removal: `get_static()`, `set_static()`, `uninherited_static()`, `combined_static()`,
`addStaticVars()` and `add_static_var()` removed. Use the Config methods instead.
* `GD` methods removed: `setGD()`, `getGD()`, `hasGD()`. Use `setImageResource()`, `getImageResource()`, and `hasImageResource()` instead
* `DataExtension::get_extra_config()` removed, no longer supports `extraStatics` or `extraDBFields`. Define your
statics on the class directly.
* `DataList::getRange()` removed. Use `limit()` instead.
* `SQLMap` removed. Call `map()` on a `DataList` or use `SS_Map` directly instead.
* `Profiler` removed. Use xhprof or xdebug for profiling instead.
* `Aggregate` removed. Call aggregate methods on a `DataList` instead e.g. `Member::get()->max('LastEdited')`
* `MySQLDatabase::set_connection_charset()` removed. Use `MySQLDatabase.connection_charset` config setting instead
* `SQLConditionalExpression/SQLQuery` `select()`, `limit()`, `orderby()`, `groupby()`, `having()`, `from()`, `leftjoin()`, `innerjoin()`, `where()` and `whereAny()` removed.
Use `set*()` and `add*()` methods instead.
* Template `<% control $MyList %>` syntax removed. Use `<% loop $MyList %>` instead.
* Removed `Member.LastVisited` and `Member.NumVisits` properties, see
[Howto: Track Member Logins](/developer_guides/extending/how_tos/track_member_logins) to restore functionality as custom code
## New and changed API
* Implementation of a parameterised query framework eliminating the need to manually escape variables for
use in SQL queries. This has been integrated into nearly every level of the database ORM.
* Refactor of database connectivity classes into separate components linked together through dependency injection
* Refactor of `SQLQuery` into separate objects for each query type: `SQLSelect`, `SQLDelete`, `SQLUpdate` and `SQLInsert`
* PDO is now a standard connector, and is available for all database interfaces
* `DataObject::doValidate()` method visibility added to access `DataObject::validate` externally
* `NumericField` now uses HTML5 "number" type instead of "text"
* `UploadField` "Select from files" shows files in all folders by default
* `UploadField` won't display an overwrite warning unless `Upload::replaceFile` is true
* `HtmlEditorField` no longer substitutes `<blockquote />` for indented text
* `ClassInfo::dataClassesFor` now returns classes which should have tables, regardless of whether those
tables actually exist.
* `SS_Filterable`, `SS_Limitable` and `SS_Sortable` now explicitly extend `SS_List`
* `Convert::html2raw` no longer wraps text by default and can decode single quotes.
* `Mailer` no longer calls `xml2raw` on all email subject line, and now must be passed in via plain text.
* `ErrorControlChain` now supports reload on exceptions
* `FormField::validate` now requires an instance of `Validator`
* API: Removed URL routing by controller name
* Security: The multiple authenticator login page should now be styled manually - i.e. without the default jQuery
UI layout. A new template, Security_MultiAuthenticatorLogin.ss is available.
* Security: This controller's templates can be customised by overriding the `getTemplatesFor` function.
* API: Form and FormField ID attributes rewritten.
* `SearchForm::getSearchQuery` no longer pre-escapes search keywords and must
be cast in your template
* Helper function `DB::placeholders` can be used to generate a comma separated list of placeholders
useful for creating "WHERE ... IN (?,...)" SQL fragments
* Implemented Convert::symbol2sql to safely encode database and table names and identifiers.
E.g. `Convert::symbol2sql('table.column') => '"table"."column"';`
* `Convert::raw2sql` may now quote the escaped value, as well as safely escape it, according to the current
database adaptor's preference.
* `DB` class has been updated and many static methods have been renamed to conform to coding convention.
* Renamed API:
* `affectedRows` -> `affected_rows`
* `checkAndRepairTable` -> `check_and_repair_table`
* `createDatabase` -> `create_database`
* `createField` -> `create_field`
* `createTable` -> `create_table`
* `dontRequireField` -> `dont_require_field`
* `dontRequireTable` -> `dont_require_table`
* `fieldList` -> `field_list`
* `getConn` -> `get_conn`
* `getGeneratedID` -> `get_generated_id`
* `isActive` -> `is_active`
* `requireField` -> `require_field`
* `requireIndex` -> `require_index`
* `requireTable` -> `require_table`
* `setConn` -> `set_conn`
* `tableList` -> `table_list`
* Deprecated API:
* `getConnect` (Was placeholder for PDO connection string building code, but is made
redundant after the PDOConnector being fully abstracted)
* New API:
* `build_sql` - Hook into new SQL generation code
* `get_connector` (Nothing to do with getConnect)
* `get_schema`
* `placeholders`
* `prepared_query`
* `SS_Database` class has been updated and many functions have been deprecated, or refactored into
the various other database classes. Most of the database management classes remain in the database
controller, due to individual databases (changing, creating of, etc) varying quite a lot from
API to API, but schema updates within a database itself is managed by an attached DBSchemaManager
* Refactored into DBSchemaManager:
* `createTable`
* `alterTable`
* `renameTable`
* `createField`
* `renameField`
* `fieldList`
* `tableList`
* `hasTable`
* `enumValuesForField`
* `beginSchemaUpdate` and `endSchemaUpdate` -> Use `schemaUpdate` with a callback
* `cancelSchemaUpdate`
* `isSchemaUpdating`
* `doesSchemaNeedUpdating`
* `transCreateTable`
* `transAlterTable`
* `transCreateField`
* `transCreateField`
* `transCreateIndex`
* `transAlterField`
* `transAlterIndex`
* `requireTable`
* `dontRequireTable`
* `requireIndex`
* `hasField`
* `requireField`
* `dontRequireField`
* Refactored into DBQueryBuilder
* `sqlQueryToString`
* Deprecated:
* `getConnect` - Was intended for use with PDO, but was never implemented, and is now
redundant, now that there is a stand-alone `PDOConnector`
* `prepStringForDB` - Use `quoteString` instead
* `dropDatabase` - Use `dropSelectedDatabase`
* `createDatabase` - Use `selectDatabase` with the second parameter set to true instead
* `allDatabaseNames` - Use `databaseList` instead
* `currentDatabase` - Use `getSelectedDatabase` instead
* `addslashes` - Use `escapeString` instead
* `LogErrorEmailFormatter` now better displays SQL queries in errors by respecting line breaks
* Installer has been majorly upgraded to handle the new database configuration options
and additional PDO functionality.
* Created `SS_DatabaseException` to emit database errors. Query information such as SQL
and any relevant parameters may be used by error handling user code that catches
this exception.
* The `SQLConditionGroup` interface has been created to represent dynamically
evaluated SQL conditions. This may be used to wrap a class that generates
a custom SQL clause(s) to be evaluated at the time of execution.
* `DataObject` constants CHANGE_NONE, CHANGE_STRICT, and CHANGE_VALUE have been created
to provide more verbosity to field modification detection. This replaces the use of
various magic numbers with the same meaning.
* create_table_options now uses constants as API specific filters rather than strings.
This is in order to promote better referencing of elements across the codebase.
See `FulltextSearchable->enable` for example.
* `$FromEnd` iterator variable now available in templates.
* Support for multiple HtmlEditorConfigs on the same page.
* Object::singleton() method for better type-friendly singleton generation
* New `Image` methods `CropWidth` and `CropHeight` added
* 'Max' versions of `Image` methods introduced to prevent up-sampling
* Update Image method names in PHP code and templates
* `SetRatioSize` -> `Fit`
* `CroppedImage` -> `Fill`
* `PaddedImage` -> `Pad`
* `SetSize` -> `Pad`
* `SetWidth` -> `ScaleWidth`
* `SetHeight` -> `ScaleHeight`
## Bugfixes
* Reduced database regeneration chances on subsequent rebuilds after the initial dev/build
* Elimination of various SQL injection vulnerability points
* `DataObject::writeComponents()` now called correctly during `DataObject::write()`
* Fixed missing theme declaration in installer
* Fixed incorrect use of non-existing exception classes (e.g. `HTTPResponse_exception`)
* `GridState` fixed to distinguish between check for missing values, and creation of
nested state values, in order to prevent non-empty values being returned for
missing keys. This was breaking `DataObject::get_by_id` by passing in an object
for the ID.
* Fixed order of `File` fulltext searchable fields to use same order as actual fields.
This is required to prevent unnecessary rebuild of MS SQL databases when fulltext
searching is enabled.
* In the past E_RECOVERABLE_ERROR would be ignored, and now correctly appear as warnings.
## Upgrading Notes
### UploadField "Select from files" shows files in all folders by default
In order to list files in a single folder by default (previous default behaviour),
use `setDisplayFolderName()` with a folder path relative to `assets/`:
:::php
UploadField::create('MyField')->setDisplayFolderName('Uploads');
### UploadField won't display an overwrite warning unless Upload:replaceFile is true
The configuration setting `UploadField:overwriteWarning` is dependent on `Upload:replaceFile`
which is set to false by default.
To display a warning before overwriting a file:
Via config:
::yaml
Upload:
# Replace an existing file rather than renaming the new one.
replaceFile: true
UploadField:
# Warning before overwriting existing file (only relevant when Upload: replaceFile is true)
overwriteWarning: true
Or per instance:
::php
$uploadField->getUpload()->setReplaceFile(true);
$uploadField->setOverwriteWarning(true);
### File.allowed_extensions restrictions
Certain file types such as swf, html, htm, xhtml and xml have been removed from the list
of allowable file uploads. If your application requires the ability to upload these,
you will need to append these to the `File.allowed_extensions` config as necessary.
Also if uploading other file types, it's necessary to ensure that `File.allowed_extensions`
includes that extension, as extensions passed to `[api:UploadField]` will be filtered against
this list.
### Removed format detection in i18n::$date_format and i18n::$time_format
Localized dates cause inconsistencies in client-side vs. server-side formatting
and validation, particularly in abbreviated month names. The default date
format has been changed to "yyyy-MM-dd" (e.g. 2014-12-31).
New users will continue to have the option for a localized date
format in their profile (based on their chosen locale).
If you have existing users with `Member.DateFormat` set to a format
including "MMM" or "MMMM", consider deleting those formats to fall back to
the global (and more stable) default.
### Cookies set via Cookie::set() are now HTTP only by default
Cookies set through `Cookie::set()` now default to "HTTP only". This means that scripting
languages like JavaScript won't be able to read them.
To set it back to be non-HTTP only, you need to set the `$httpOnly` argument to false when calling
`Cookie::set()`.
### API: Removed URL routing by controller name
The auto-routing of controller class names to URL endpoints
has been removed (rule: `'$Controller//$Action/$ID/$OtherID': '*'`).
This increases clarity in routing since it makes URL entpoints explicit,
and thereby simplifies system and security reviews.
Please access any custom controllers exclusively through self-defined
[routes](/reference/director). For controllers extending `Page_Controller`,
simply use the provided page URLs.
:::php
class MyController extends Controller {
static $allowed_actions = array('myaction');
public function myaction($request) {
// ...
}
}
Create a new file `mysite/_config/routes.yml`
(read more about the [config format](/topics/configuration)).
Your controller is now available on `http://yourdomain.com/my-controller-endpoint`,
after refreshing the configuration cache through `?flush=all`.
:::yaml
---
Name: my-routes
After: framework/routes#coreroutes
---
Director:
rules:
'my-controller-endpoint//$Action' : 'MyController'
The auto-routing is still in place for unit tests,
since its a frequently used feature there. Although we advise against it,
you can reinstate the old behaviour through a director rule:
:::yaml
---
Name: my-routes
After: framework/routes#coreroutes
---
Director:
rules:
'$Controller//$Action/$ID/$OtherID': '*'
### API: Default Form and FormField ID attributes rewritten.
Previously the automatic generation of ID attributes throughout the Form API
could generate invalid ID values such as Password[ConfirmedPassword] as well
as duplicate ID values between forms on the same page. For example, if you
created a field called `Email` on more than one form on the page, the resulting
HTML would have multiple instances of `#Email`. ID should be a unique
identifier for a single element within the document.
This rewrite has several angles, each of which is described below. If you rely
on ID values in your CSS files, Javascript code or application unit tests *you
will need to update your code*.
#### Conversion of invalid form ID values
ID attributes on Form and Form Fields will now follow the
[HTML specification](http://www.w3.org/TR/REC-html40/types.html#type-cdata).
Generating ID attributes is now handled by the new `FormTemplateHelper` class.
Please test each of your existing site forms to ensure that they work
correctly in particular, javascript and css styles which rely on specific ID
values.
#### Invalid ID attributes stripped
ID attributes will now be run through `Convert::raw2htmlid`. Invalid characters
are replaced with a single underscore character. Duplicate, leading and trailing
underscores are removed. Custom ID attributes (set through `setHTMLID`) will not
be altered.
Before:
:::html
<form id="MyForm[Form]"
<div id="MyForm[Form][ID]">
Now:
:::html
<form id="MyForm_Form">
<div id="MyForm_Form_ID">
#### Namespaced FormField ID's
Form Field ID values will now be namespaced with the parent form ID.
Before:
:::html
<div id="Email">
Now:
:::html
<div id="MyForm_Email">
#### FormField wrapper containers suffixed with `_Holder`
Previously both the container div and FormField tag shared the same ID in
certain cases. Now, the wrapper div in the default `FormField` template will be
suffixed with `_Holder`.
Before:
:::html
<div id="Email">
<input id="Email" />
After:
:::html
<div id="MyForm_Email_Holder"
<input id="MyForm_Email" />
#### Reverting to the old specification
If upgrading existing forms is not feasible, developers can opt out of the new
specifications by using the `FormTemplateHelper_Pre32` class rules instead of
the default ones.
`mysite/config/_config.yml`:
:::yaml
Injector:
FormTemplateHelper:
class: FormTemplateHelper_Pre32
### Update code that uses SQLQuery
SQLQuery has been changed. Previously this class was used for both selecting and deleting, but
deletion is now handled by the new SQLDelete class.
Additionally, 3.2 now provides SQLUpdate and SQLInsert to generate parameterised query friendly
data updates.
SQLQuery, SQLDelete and SQLUpdate all inherit from SQLConditionalExpression, which
implements toSelect, toDelete, and toUpdate to generate basic transformations
between query types.
In the past SQLQuery->setDelete(true) would be used to turn a select into a delete,
although now a new SQLDelete object should be created from the original SQLQuery.
Before:
:::php
<?php
$query = new SQLQuery('*');
$query->setFrom('"SiteTree"');
$query->setWhere('"SiteTree"."ShowInMenus" = 0');
$query->setDelete(true);
$query->execute();
After:
:::php
<?php
$query = SQLDelete::create()
->setFrom('"SiteTree"')
->setWhere(array('"SiteTree"."ShowInMenus"' => 0));
$query->execute();
When working with SQLQuery passed into user code, it is advisable to strictly
cast it into either a SQLSelect or SQLDelete. This can be done by using the new
`SQLQuery::toAppropriateExpression()` method, which will automatically convert
to the correct type based on whether the SQLQuery is set to delete or not.
If a SQLQuery is not converted, then the result of `getWhere` will not be parameterised.
This is because user code written for 3.1 expects this list to be a flat array
of strings. This format is inherently unsafe, and should be avoided where possible.
:::php
<?php
public function augmentSQL(SQLQuery &$query) {
$query->getWhere(); // Will be flattened (unsafe 3.1 compatible format)
$expression = $query->toAppropriateExpression(); // Either SQLSelect or SQLDelete
$expression->getWhere(); // Will be parameterised (preferred 3.2 compatible format)
}
Alternatively:
:::php
<?php
$query = SQLQuery::create()
->setFrom('"SiteTree"')
->setWhere(array('"SiteTree"."ShowInMenus"' => 0))
->setDelete(true)
->toAppropriateExpression();
$query->execute();
### Update code that interacts with SQL strings to use parameters
The Silverstripe ORM (object relation model) has moved from using escaped SQL strings
to query the database, to a combination of parameterised SQL expressions alongside
a related list of parameter values. As a result of this, it is necessary to assume
that any `SQLQuery` object may, and will usually, have un-injected parameters.
All database queries performed through `DataList`, `DataQuery` and `SQLQuery` will continue
to work, as will those through `DataObject::get()` (which returns a filterable `DataList`).
However, any conditional expression that includes values escaped with `Convert::raw2sql()`
should use the new standard syntax. This new querying standard method enforces a much
higher level of security than was previously available, and all code using manual
escaping should be upgraded.
See [the security topic](/topics/security#parameterised-queries) for details on why this is necessary, or
[the databamodel topic](/topics/datamodel#raw-sql-options-for-advanced-users) for more information.
As a result of this upgrade there are now very few cases where `Convert::raw2sql` needs to be used.
Examples of areas where queries should be upgraded are below:
#### 1. Querying the database directly through DB, including non-SELECT queries
Before:
:::php
<?php
// Note: No deprecation notices will be caused here
DB::query("UPDATE \"SiteTree\" SET \"Title\" LIKE '%" . Convert::raw2sql($myTitle) . "%' WHERE \"ID\" = 1");
$myPages = DB::query(sprintf('SELECT "ID" FROM "MyObject" WHERE "Title" = \'%s\'', Convert::raw2sql($parentTitle)));
After:
:::php
<?php
DB::prepared_query(
'UPDATE "SiteTree" SET "Title" LIKE ? WHERE "ID" = ?',
array("%{$myTitle}%", 1)
);
$myPages = DB::prepared_query(
'SELECT "ID" FROM "MyObject" WHERE "Title" = ?',
array($parentTitle)
);
#### 2. Querying the database through `DataList`, `DataQuery`, `SQLQuery`, and `DataObject`
Before:
:::php
<?php
$items = DataObject::get_one('MyObject', '"Details" = \''.Convert::raw2sql($details).'\'');
$things = MyObject::get()->where('"Name" = \''.Convert::raw2sql($name).'\'');
$list = DataList::create('Banner')->where(array(
'"ParentID" IS NOT NULL',
'"Title" = \'' . Convert::raw2sql($title) . '\''
);
After:
:::php
<?php
$items = DataObject::get_one('MyObject', array('"MyObject"."Details"' => $details));
$things = MyObject::get()->where(array('"MyObject"."Name" = ?' => $name));
$list = DataList::create('Banner')->where(array(
'"ParentID" IS NOT NULL',
'"Title" = ?', $title
);
#### 3. Interaction with `DataList::sql()`, `DataQuery::sql()`, `SQLQuery::sql()`, or `SQLQuery::getJoins()` methods
The place where legacy code would almost certainly fail is any code that calls
DataList::sql`, `DataQuery::sql`, `SQLQuery::sql` or `SQLQuery::getJoins()`, as the api requires that user
code passes in an argument here to retrieve SQL parameters by value.
User code that assumes parameterless queries will likely fail, and need to be
updated to handle this case properly.
Before:
:::php
<?php
// Generate query
$argument = 'whatever';
$query = SQLQuery::create()
->setFrom('"SiteTree"')
->setWhere(array("\"SiteTree\".\"Title\" LIKE '" . Convert::raw2sql($argument) . "'"));
// Inspect elements of the query
$sql = $query->sql();
$sql = preg_replace('/LIKE \'(.+)\'/', 'LIKE \'%${1}%\'', $sql); // Adds %% around the argument
// Pass new query to database connector
DB::query($sql);
After:
:::php
<?php
// Generate query
$argument = 'whatever';
$query = SQLQuery::create()
->setFrom('"SiteTree"')
->setWhere(array('"SiteTree"."Title" LIKE ?' => $argument));
// Inspect elements of the query
$sql = $query->sql($parameters);
foreach($parameters as $key => $value) {
// Adds %% around arguments
$parameters[$key] = "%{$value}%";
}
// Pass new query to database connector
// Note that DB::query($sql) would fail, as it would contain ? with missing parameters
DB::prepared_query($sql, $parameters);
Also note that the parameters may not be a single level array, as certain values
may be forced to be cast as a certain type (where supported by the current API).
E.g.
:::php
<?php
$parameters = array(
10,
array('value' => 0, 'type' => 'boolean') // May also contain other database API specific options
)
DB::prepared_query('DELETE FROM "MyObject" WHERE ParentID = ? OR IsValid = ?', $parameters);
#### 4. Interaction with `SQLSelect::getWhere()` method
The `SQLSelect` class supercedes the old `SQLQuery` object for performing select queries. Although
both implement the `getWhere()` method, the results returned by `SQLSelect::getWhere()` will be
parameterised while `SQLQuery::getWhere()` will be a flattened array of strings.
`SQLSelect::getWhere()` returns a list of conditions, each of which is an
associative array mapping the condition string to a list of parameters provided.
Before:
:::php
<?php
// Increment value of a single condition
$query = new SQLQuery(/*...*/);
$conditions = $query->getWhere();
$new = array();
foreach($conditions as $condition) {
if(preg_match('/\"Count\" = (?<count>\d+)/', $condition, $matches)) {
$condition = '"Count" = '.($matches['count'] + 1);
}
$new[] = $condition;
}
$query->setWhere($new);
After:
:::php
// Increment value of a single condition
$query = new SQLSelect(/*...*/);
$conditions = $query->getWhere();
$new = array();
foreach($conditions as $condition) {
// $condition will be a single length array
foreach($condition as $predicate => $parameters) {
if('"Count" = ?' === $predicate) {
$parameters[0] = $parameters[0] + 1;
}
$new[] = array($predicate => $parameters);
}
}
$query->setWhere($new);
In cases where user code will manipulate the results of this value, it may be useful to
replace this method call with the new `getWhereParameterised($parameters)` method, where
applicable.
This method returns a manipulated form of the where conditions stored by the query, so
that it matches the list of strings similar to the old 3.1 `SQLQuery::getWhere()` behaviour.
Additionally, the list of parameters is safely extracted, flattened, and can be passed out
through the `$parameters` argument which is passed by reference.
Before:
:::php
public function filtersOnColumn($query, $column) {
$regexp = '/^(.*\.)?("|`)?' . $column . ' ("|`)?\s?=/';
foreach($this->getWhere() as $predicate) {
if(preg_match($regexp, $predicate)) return true;
}
return false;
}
After:
:::php
public function filtersOnColumn($query, $column) {
$regexp = '/^(.*\.)?("|`)?' . $column . ' ("|`)?\s?=/';
foreach($this->getWhereParameterised($parameters) as $predicate) {
if(preg_match($regexp, $predicate)) return true;
}
return false;
}
#### 5. Update code that interacts with the DB schema
Updating database schema is now done by `updateSchema` with a callback, rather than relying
on user code to call `beginSchemaUpdate` and `endSchemaUpdate` around the call.
Since the schema management object is separate from the database controller you
interact with it via `DB::get_schema` instead of `DB::get_conn` (previously named
`DB::getConn`)
Before:
:::php
<?php
$conn = DB::getConn();
$conn->beginSchemaUpdate();
foreach($dataClasses as $dataClass) {
singleton($dataClass)->requireTable();
}
$conn->endSchemaUpdate();
After:
:::php
<?php
$schema = DB::get_schema();
$schema->schemaUpdate(function() use($dataClasses){
foreach($dataClasses as $dataClass) {
singleton($dataClass)->requireTable();
}
});
Also should be noted is that many functions have been renamed to conform better with
coding conventions. E.g. `DB::requireTable` is now `DB::require_table`
### Revert to legacy CMS page actions
By default "delete from live" and "delete" actions are deprecated in favour of "unpublish" and "archive".
"unpublish" is an existing action which is functionally equivalent to "delete from live", and "archive" is a new
feature which performs both unpublish and deletion simultaneously.
To restore "delete from live" add the following config to your site's config.yml.
:::yml
CMSMain:
enabled_legacy_actions:
- CMSBatchAction_DeleteFromLive
In order to remove the new "archive" action and restore the old "delete" action you can use the following config
:::yml
CMSMain:
enabled_legacy_actions:
- CMSBatchAction_Delete
## Changelog
### API Changes
* 2015-06-16 [f3e1472](https://github.com/silverstripe/silverstripe-cms/commit/f3e1472) Revert DataObject::validate to 3.1 method signature (protected) (Damian Mooyman)
* 2015-06-16 [58cc3da](https://github.com/silverstripe/sapphire/commit/58cc3da) Revert DataObject::validate to 3.1 method signature (protected) (Damian Mooyman)
* 2015-06-13 [e766658](https://github.com/silverstripe/sapphire/commit/e766658) Allow HTTP Cache Headers to be customized (Jeremy Shipman)
* 2015-06-12 [8389260](https://github.com/silverstripe/sapphire/commit/8389260) New and renamed image functions (Jonathon Menz)
* 2015-06-09 [a8ace75](https://github.com/silverstripe/sapphire/commit/a8ace75) Support for multiple HTMLEditorConfig per page (Damian Mooyman)
* 2015-05-15 [b169823](https://github.com/silverstripe/silverstripe-cms/commit/b169823) Deprecate delete in favour of archive Remove "delete from live" duplicate action in favour of existing "unpublish" which is more consistent with current terminology Add pop-up verification to destructive actions Fix bug preventing side-by-side preview of archived pages Fix bug in reporting publishing of error pages Restoring a page without an available parent will restore to root (Damian Mooyman)
* 2015-05-15 [a72bd16](https://github.com/silverstripe/sapphire/commit/a72bd16) Deprecate delete in favour of archive Remove "delete from live" duplicate action in favour of existing "unpublish" which is more consistent with current terminology Add pop-up verification to destructive actions Fix bug in reporting publishing of error pages Restoring a page also restores parents (Damian Mooyman)
* 2015-04-30 [c5e0c8f](https://github.com/silverstripe/silverstripe-cms/commit/c5e0c8f) Enable tree filter highlighting Decoupling of CMS / Framework (Damian Mooyman)
* 2015-04-30 [8863797](https://github.com/silverstripe/sapphire/commit/8863797) Enable tree filter highlighting Decoupling of CMS / Framework (Damian Mooyman)
* 2015-04-29 [e8d6f15](https://github.com/silverstripe/sapphire/commit/e8d6f15) Use mysql buffered statements Avoids the usage of any MySQL Native Driver specific API (Damian Mooyman)
* 2015-04-09 [e91606e](https://github.com/silverstripe/sapphire/commit/e91606e) Introduce $FromEnd variable for iterators (Damian Mooyman)
* 2015-03-31 [95c162e](https://github.com/silverstripe/sapphire/commit/95c162e) Security better respects BackURL on login BUG Restore missing authentication message not appearing in the login form $Content area (regression from #1807) (Damian Mooyman)
* 2015-03-05 [9367fd2](https://github.com/silverstripe/sapphire/commit/9367fd2) enable PaginatedList to be disabled by setting page length to 0 (Damian Mooyman)
* 2015-01-14 [5d4c2c4](https://github.com/silverstripe/sapphire/commit/5d4c2c4) Adding default_classes to FormField (Daniel Hensby)
* 2015-01-14 [6d00027](https://github.com/silverstripe/sapphire/commit/6d00027) Adding default_classes to Form (Daniel Hensby)
* 2014-09-25 [e478009](https://github.com/silverstripe/sapphire/commit/e478009) Mailer can be configured to use different encoding mechanisms, and added support for unicode quoted-string encoding API Mailer bounce email can now be configured API Mailer no longer calls Convert::xml2raw on all email subjects API Deprecate dead Mailer code and refactored duplicate or mis-documented code. (Damian Mooyman)
* 2014-09-25 [29e3347](https://github.com/silverstripe/sapphire/commit/29e3347) Convert::html2raw no longer wraps text automatically BUG Convert::html2raw now correctly decodes single quotes (Damian Mooyman)
* 2014-09-24 [5631553](https://github.com/silverstripe/sapphire/commit/5631553) Cookies set via Cookie::set() are now HTTP only by default (Sean Harvey)
* 2014-09-15 [062ad8e](https://github.com/silverstripe/sapphire/commit/062ad8e) Allow parameterised joins / subselects (Damian Mooyman)
* 2014-08-15 [2ba1c46](https://github.com/silverstripe/silverstripe-cms/commit/2ba1c46) broken link hihglighting to write to database. (Mateusz Uzdowski)
* 2014-08-14 [784e292](https://github.com/silverstripe/sapphire/commit/784e292) Add a getter for customisedObject property. (Mateusz Uzdowski)
* 2014-08-09 [18d6c53](https://github.com/silverstripe/silverstripe-cms/commit/18d6c53) Extract siteconfig out to an external module. (Will Rossiter)
* 2014-08-04 [1759d5d](https://github.com/silverstripe/sapphire/commit/1759d5d) Use "number" HTML5 type for NumericField by default (Sean Harvey)
* 2014-07-30 [26a0e91](https://github.com/silverstripe/sapphire/commit/26a0e91) SS_Filterable, SS_Limitable and SS_Sortable now explicitly extend SS_List (Damian Mooyman)
* 2014-04-23 [d16db2d](https://github.com/silverstripe/sapphire/commit/d16db2d) tinymce editor no longer transforms paragraphs with margin-left into blockquotes This is legacy behaviour which does not often reflect the expected behaviour of the current editor. indent and outdent can (in some situations) prefer to use margin instead of padding. sapphiremce_cleanup faultily assumes that such indented text should be block quoted, and replaces this with a block quote element. This is not necessary, since the blockquote element can be placed explicitly by the user when necessary. (Damian Mooyman)
* 2014-04-16 [5f7ebd3](https://github.com/silverstripe/sapphire/commit/5f7ebd3) UploadField: move replaceFile to the front end config (Devlin)
* 2014-04-11 [5b55361](https://github.com/silverstripe/sapphire/commit/5b55361) DateTime.Ago better infers significance of date units. BUG Fixes missing i18n translation in Date::TimeDiffIn BUG Fixes Date::TimeDiffIn not respecting mocked SS_Datetime::now This provides less vague date periods. I.e. "36 days" has a lot more relevance that "1 month" Reduced duplication of time period calculation code (ref: CWPBUG-141) (Damian Mooyman)
* 2014-04-09 [2e73dcb](https://github.com/silverstripe/sapphire/commit/2e73dcb) Remove swf,html,htm,xhtml,xml as default allowed upload able file types (Damian Mooyman)
* 2014-04-04 [bf4e9eb](https://github.com/silverstripe/sapphire/commit/bf4e9eb) Singleton method allowing type inference This pattern improves over the current usage of singleton by allowing type inference. This also better supports refactor, code usage detection, and auto-completion of classes. (Damian Mooyman)
* 2014-02-12 [6906c9b](https://github.com/silverstripe/sapphire/commit/6906c9b) Removed auto-detection for i18n date/time formats (Ingo Schommer)
* 2014-01-17 [973b967](https://github.com/silverstripe/sapphire/commit/973b967) Adding chaining to i18nTextCollector::write() (Daniel Hensby)
* 2014-01-02 [791ee71](https://github.com/silverstripe/sapphire/commit/791ee71) Prevent large images from repeatedly crashing PHP on resize (Loz Calver)
* 2013-12-23 [5fff5af](https://github.com/silverstripe/sapphire/commit/5fff5af) moved useTestTheme to base Sapphire test class so that it can be used elsewhere (eg CMS test) (micmania1)
* 2013-12-19 [6fc9db6](https://github.com/silverstripe/sapphire/commit/6fc9db6) DataObject::validate() visibility changed to public (issue #1659) (Sean Harvey)
* 2013-11-26 [b88a095](https://github.com/silverstripe/sapphire/commit/b88a095) Support string descriptors for unique indexes in Versioned (Fred Condo)
* 2013-10-18 [fee54c7](https://github.com/silverstripe/sapphire/commit/fee54c7) Change DropdownField::getSource() to not return the emptyString value. (Nathan J. Brauer)
* 2013-10-18 [1c983bc](https://github.com/silverstripe/sapphire/commit/1c983bc) LookupField::Field now returns an HTMLText instance. (Will Rossiter)
* 2013-10-17 [52f6581](https://github.com/silverstripe/sapphire/commit/52f6581) Better declaration of DataObject field change levels. Use of const named identifiers to represent each change level rather than numbers. (Damian Mooyman)
* 2013-10-11 [b6b3cd9](https://github.com/silverstripe/sapphire/commit/b6b3cd9) GridState_Data values can have default values specified during retrieval. Fixes issues with GridStata_Data being returned from various states when value types are necessary. Pruning of dead code from GridFieldAddExistingAutocompleter Documentation for GridState (Damian Mooyman)
* 2013-10-09 [b367dd6](https://github.com/silverstripe/sapphire/commit/b367dd6) Removed Member.LastVisited and Member.NumVisits (Ingo Schommer)
* 2013-09-27 [c7f656c](https://github.com/silverstripe/sapphire/commit/c7f656c) Removed "PastMember" cookie and template getter (Ingo Schommer)
* 2013-08-08 [4385264](https://github.com/silverstripe/sapphire/commit/4385264) Make GridFieldConfig objects decoratable (unclecheese)
* 2013-07-10 [7c60c73](https://github.com/silverstripe/sapphire/commit/7c60c73) Polymorphic has_one behaviour (Damian Mooyman)
* 2013-07-01 [47147eb](https://github.com/silverstripe/sapphire/commit/47147eb) delete simplepie from framework thirdparty (carlos barberis)
* 2013-06-21 [a395c53](https://github.com/silverstripe/silverstripe-cms/commit/a395c53) Move of codebase to parameterised query database abstraction layer API Renamed DB static methods to properly conform to naming convention (lowercase, underscored) API Replaced deprecated method (Damian Mooyman)
* 2013-06-21 [d8e9af8](https://github.com/silverstripe/sapphire/commit/d8e9af8) New Database abstraction layer. Ticket #7429 Database abstraction broken up into controller, connector, query builder, and schema manager, each independently configurable via YAML / Injector Creation of new DBQueryGenerator for database specific generation of SQL Support for parameterised queries, move of code base to use these over escaped conditions Refactor of SQLQuery into separate query classes for each of INSERT UPDATE DELETE and SELECT Support for PDO Installation process upgraded to use new ORM SS_DatabaseException created to handle database errors, maintaining details of raw sql and parameter details for user code designed interested in that data. Renamed DB static methods to conform correctly to naming conventions (e.g. DB::getConn -&gt; DB::get_conn) 3.2 upgrade docs Performance Optimisation and simplification of code to use more concise API API Ability for database adapters to register extensions to ConfigureFromEnv.php (Damian Mooyman)
* 2013-05-31 [0c4ec47](https://github.com/silverstripe/sapphire/commit/0c4ec47) Using $HolderID for form field container templates (Ingo Schommer)
* 2013-05-26 [ca87b8b](https://github.com/silverstripe/sapphire/commit/ca87b8b) Form Field ID attribute should follow HTML specification (Will Rossiter)
* 2013-05-22 [cb1f95e](https://github.com/silverstripe/sapphire/commit/cb1f95e) Remove AjaxUniqueTextField, since its operation is very limited (#1947) (Ingo Schommer)
* 2013-01-29 [957469d](https://github.com/silverstripe/sapphire/commit/957469d) Removed auto-routing of controller name (Ingo Schommer)
* 2013-01-17 [56346a5](https://github.com/silverstripe/silverstripe-cms/commit/56346a5) moved reports API to separate module (Will Rossiter)
### Features and Enhancements
* 2015-06-03 [a9d22f1](https://github.com/silverstripe/sapphire/commit/a9d22f1) Files can be uploaded directly in the 'Insert Link' form (scott1702)
* 2015-05-29 [44b1ff1](https://github.com/silverstripe/sapphire/commit/44b1ff1) Configurable file version prefix (Jonathon Menz)
* 2015-05-11 [ce5a8f2](https://github.com/silverstripe/sapphire/commit/ce5a8f2) Cookie names with dots are now handled more gracefully (Daniel Hensby)
* 2015-03-31 [ae8dbe3](https://github.com/silverstripe/sapphire/commit/ae8dbe3) - Added maximum upload file size by type (Turnerj)
* 2015-03-24 [16f0e7b](https://github.com/silverstripe/sapphire/commit/16f0e7b) ViewableData_Debugger implements __toString (Daniel Hensby)
* 2015-03-03 [835ee69](https://github.com/silverstripe/sapphire/commit/835ee69) Only validate DataObject model definitions during a build (Loz Calver)
* 2015-02-24 [8ee9130](https://github.com/silverstripe/sapphire/commit/8ee9130) CMS site tree status icons (Jonathon Menz)
* 2015-02-08 [5f31983](https://github.com/silverstripe/sapphire/commit/5f31983) updateAttributes hook in FormField (Ingo Schommer)
* 2015-01-23 [3f1805b](https://github.com/silverstripe/sapphire/commit/3f1805b) Support multiple many_manys between the same classes (closes #1377) (Josh)
* 2014-12-15 [6ad8f7c](https://github.com/silverstripe/sapphire/commit/6ad8f7c) Subject line for email links in HtmlEditorField (Loz Calver)
* 2014-11-12 [41ea83b](https://github.com/silverstripe/sapphire/commit/41ea83b) add validation to form field subclasses (Stevie Mayhew)
* 2014-10-17 [dc7bc46](https://github.com/silverstripe/sapphire/commit/dc7bc46) Text - Limit characters to closest word (Anton Smith)
* 2014-10-03 [23fc498](https://github.com/silverstripe/sapphire/commit/23fc498) Allow 'null' limit for database queries (closes #3487) (Loz Calver)
* 2014-05-04 [3b9056f](https://github.com/silverstripe/sapphire/commit/3b9056f) Cookie_Backend for managing cookie state (Daniel Hensby)
* 2013-10-17 [e8287cd](https://github.com/silverstripe/sapphire/commit/e8287cd) Hook for `Member::registerFailedLogin` (Thomas Speak)
* 2013-08-23 [7d7c754](https://github.com/silverstripe/silverstripe-cms/commit/7d7c754) Track broken anchors (Russell Michell)
* 2013-06-05 [60333f6](https://github.com/silverstripe/sapphire/commit/60333f6) UploadField lists all files, shows path info (Ingo Schommer)
* 2013-06-03 [2a91d27](https://github.com/silverstripe/sapphire/commit/2a91d27) use Injector pattern to create ValidationResult in validate (Will Morgan)
* 2013-05-26 [736bde8](https://github.com/silverstripe/sapphire/commit/736bde8) Add Convert::raw2htmlid() (Will Rossiter)
* 2013-03-26 [64349fe](https://github.com/silverstripe/sapphire/commit/64349fe) Allow setting of ASSETS_DIR in _ss_environment.php (Loz Calver)
### Bugfixes
* 2015-06-16 [6169bf2](https://github.com/silverstripe/sapphire/commit/6169bf2) No longer caching has_one after ID change (Daniel Hensby)
* 2015-06-16 [ce3b5a5](https://github.com/silverstripe/sapphire/commit/ce3b5a5) Fix major segfault on PDOConnector after any DDL BUG Fix issue in PDOQuery::first() Refactor previewWrite and benchmarkQuery into SS_Database (Damian Mooyman)
* 2015-06-11 [6be0488](https://github.com/silverstripe/sapphire/commit/6be0488) TreeDropdownField doesnt change label on unselect (Daniel Hensby)
* 2015-06-09 [24a268a](https://github.com/silverstripe/sapphire/commit/24a268a) Image test cleanup (Jonathon Menz)
* 2015-06-09 [07c21e2](https://github.com/silverstripe/sapphire/commit/07c21e2) Fix deletion of orphaned versioned records when a parent _versions table has been deleted (Damian Mooyman)
* 2015-06-08 [acf19b7](https://github.com/silverstripe/sapphire/commit/acf19b7) Fix false values for many_many_ExtraFields not being saved Fixes #4067 (Damian Mooyman)
* 2015-06-05 [a819bcf](https://github.com/silverstripe/silverstripe-cms/commit/a819bcf) explicitly call get functions for site tree checks (Stevie Mayhew)
* 2015-05-29 [0319f78](https://github.com/silverstripe/sapphire/commit/0319f78) Incorrect env setting in 3.1.13 (Damian Mooyman)
* 2015-05-22 [68d8df4](https://github.com/silverstripe/sapphire/commit/68d8df4) DropdownField didn't consider disabled items (Loz Calver)
* 2015-05-22 [e0710ae](https://github.com/silverstripe/sapphire/commit/e0710ae) Fix DirectorTest failing when run with sake (Damian Mooyman)
* 2015-05-20 [94f6a13](https://github.com/silverstripe/sapphire/commit/94f6a13) Fixed setting LastEdited for DataObject with class ancestry (Gregory Smirnov)
* 2015-05-20 [869e69a](https://github.com/silverstripe/sapphire/commit/869e69a) Clicking icon in site tree link fails (Jonathon Menz)
* 2015-05-20 [f9bdf61](https://github.com/silverstripe/sapphire/commit/f9bdf61) Fixed handling of numbers in certain locales (Gregory Smirnov)
* 2015-05-19 [dbe2ad4](https://github.com/silverstripe/silverstripe-cms/commit/dbe2ad4) Folder expansion icons (Jonathon Menz)
* 2015-05-19 [a56d08b](https://github.com/silverstripe/sapphire/commit/a56d08b) TreeDropdownField Folder expansion (Jonathon Menz)
* 2015-05-16 [c6bcfea](https://github.com/silverstripe/sapphire/commit/c6bcfea) FieldList::changeFieldOrder() leftovers discarded (Jonathon Menz)
* 2015-05-11 [9e8a5c9](https://github.com/silverstripe/sapphire/commit/9e8a5c9) remove validation type constraint from form fields for 3.2 release (Stevie Mayhew)
* 2015-02-14 [bee642a](https://github.com/silverstripe/sapphire/commit/bee642a) make class loader classExists check interface_exists as per docs (Daniel Hensby)
* 2015-02-08 [6212b4b](https://github.com/silverstripe/sapphire/commit/6212b4b) Versioned not ignoring obsolete fields (Benjamin R. White)
* 2015-01-31 [e724d6f](https://github.com/silverstripe/sapphire/commit/e724d6f) notice level error when value is not set on CreditCardField (Will Rossiter)
* 2015-01-07 [cee7adc](https://github.com/silverstripe/sapphire/commit/cee7adc) . Placeholder isn't completely translated (Elvinas L)
* 2014-12-15 [c358ac6](https://github.com/silverstripe/sapphire/commit/c358ac6) How to folder on forms (Cam Findlay)
* 2014-12-09 [bdb3b7f](https://github.com/silverstripe/sapphire/commit/bdb3b7f) Feedback to name the fields section to "field types" to make it clearer what the section is about. (Cam Findlay)
* 2014-12-09 [aba9667](https://github.com/silverstripe/sapphire/commit/aba9667) use GFMD code blocks to fix code formatting consistency. (Cam Findlay)
* 2014-11-03 [51337ac](https://github.com/silverstripe/sapphire/commit/51337ac) Image backend ignoring config. (Michael Strong)
* 2014-10-26 [ec0c259](https://github.com/silverstripe/sapphire/commit/ec0c259) Reinstate tab and form focus states (fixes CMS #732 and #817) (Naomi Guyer)
* 2014-10-25 [28be51c](https://github.com/silverstripe/sapphire/commit/28be51c) Config state leaking between unit tests (Loz Calver)
* 2014-09-26 [db0cad4](https://github.com/silverstripe/sapphire/commit/db0cad4) ErrorControlChain now supports exception handling (Damian Mooyman)
* 2014-09-20 [bbc1cb8](https://github.com/silverstripe/sapphire/commit/bbc1cb8) #3458 iframe transport multi file upload FIX #3343, FIX #3148 (Thierry François)
* 2014-09-02 [c140459](https://github.com/silverstripe/sapphire/commit/c140459) Fix versioned Versioned is not writing Version to _version tables for subclasses of Version dataobjects which have their own DB fields - Fix disjoint of ID / RecordID (which should be the same) - Fix calculation of new record version - Fix use of empty vs !isset to check for existing version (Damian Mooyman)
* 2014-09-01 [3644110](https://github.com/silverstripe/sapphire/commit/3644110) Ensure that columns are unique within a gridfield (Will Rossiter)
* 2014-08-01 [b0239f4](https://github.com/silverstripe/sapphire/commit/b0239f4) Fix PDOConnector issues Travis support for PDO ATTR_EMULATE_PREPARES = false breaks some test cases Enable username sans password Remove unnecessary semicolons delimiting queries (Damian Mooyman)
* 2014-07-25 [81c0a34](https://github.com/silverstripe/sapphire/commit/81c0a34) Remove caching of statements due to risk of instability This would cause segfaults in rare situations where statements are reused (Damian Mooyman)
* 2014-07-15 [0433ba1](https://github.com/silverstripe/sapphire/commit/0433ba1) Revert some changes to ManyManyList BUG Fix incompatibility in Member_GroupList Fix regressions in merges from 3.1 BUG Fix Security failing on test classes BUG Fix postgresql compatibility Clarify sql encoding of table names (Damian Mooyman)
* 2014-05-22 [3213630](https://github.com/silverstripe/sapphire/commit/3213630) fix listview not working with IE9 (Igor)
* 2014-05-09 [8335de4](https://github.com/silverstripe/sapphire/commit/8335de4) remove redundant DB name switch in TestRunner (Will Morgan)
* 2014-05-02 [9cbfd14](https://github.com/silverstripe/sapphire/commit/9cbfd14) TemplateManifest prevent cache collision (Will Morgan)
* 2014-04-30 [5dd0583](https://github.com/silverstripe/silverstripe-cms/commit/5dd0583) Fix encoding of SearchForm::getSearchQuery This made it awkward for user code to extract the query value for use in other applications; This would otherwise have to be xml decoded again. Casting has been promoted to the templating level via DBField::create_field and a `SearchForm.casting` config setting. (Damian Mooyman)
* 2014-04-08 [438fe02](https://github.com/silverstripe/sapphire/commit/438fe02) change action variable source to getViewer (Will Morgan)
* 2014-03-28 [cf5d524](https://github.com/silverstripe/sapphire/commit/cf5d524) Fix regressions from #2206 in hasValue and dbObject (Damian Mooyman)
* 2014-03-25 [4b87b2e](https://github.com/silverstripe/silverstripe-cms/commit/4b87b2e) Fix ContentControllerTest (Damian Mooyman)
* 2014-02-28 [ab52b67](https://github.com/silverstripe/sapphire/commit/ab52b67) Log out current member when forgotten password (Daniel Hensby)
* 2014-02-20 [f6b72a2](https://github.com/silverstripe/sapphire/commit/f6b72a2) Fixed regression in ContentController template selection. (Sam Minnee)
* 2014-02-14 [d0a4fc2](https://github.com/silverstripe/silverstripe-cms/commit/d0a4fc2) Fix failover to index template in ContentController::getViewer() (Sam Minnee)
* 2014-02-04 [cd213ab](https://github.com/silverstripe/sapphire/commit/cd213ab) Fixed handing of false values in GridState_Data API Added ability to unset values (Damian Mooyman)
* 2014-01-31 [6df276c](https://github.com/silverstripe/sapphire/commit/6df276c) GridState_Data doesn't hold falsey values (Daniel Hensby)
* 2013-10-30 [4102cc6](https://github.com/silverstripe/sapphire/commit/4102cc6) Issues with CMSForm not consistently respecting new form naming scheme. Fixes for failing CMSFormTest cases (Damian Mooyman)
* 2013-10-23 [8534982](https://github.com/silverstripe/sapphire/commit/8534982) Debug error handler breaks error_get_last (Damian Mooyman)
* 2013-10-19 [ab10c2e](https://github.com/silverstripe/sapphire/commit/ab10c2e) An enum field in the search panel model admin misses an option to not filter on that field (Nico Haase)
* 2013-10-17 [d22ca62](https://github.com/silverstripe/sapphire/commit/d22ca62) FailedLoginCount reset (Thomas Speak)
* 2013-10-02 [fb5bb64](https://github.com/silverstripe/sapphire/commit/fb5bb64) Fixed cross-platform issues with test cases and file utilities (Damian Mooyman)
* 2013-05-30 [c7468ca](https://github.com/silverstripe/sapphire/commit/c7468ca) Generate Form::FormName() through (Will Rossiter)
* 2013-05-26 [831a507](https://github.com/silverstripe/sapphire/commit/831a507) Update references to ID values from 79c9433 (Will Rossiter)
* 2013-05-17 [3728907](https://github.com/silverstripe/sapphire/commit/3728907) allow children to be accessed via template (Will Morgan)
* 2013-01-23 [60c4d99](https://github.com/silverstripe/sapphire/commit/60c4d99) PHPUnit latest not working with composer installed builds (Hamish Friedlander)
* 2012-12-13 [31255fc](https://github.com/silverstripe/sapphire/commit/31255fc) Set visibility on login form methods to public. (Justin Martin)
* 2012-12-12 [379b561](https://github.com/silverstripe/sapphire/commit/379b561) RSSFeed now sets the Content-Type on the current HTTPResponse (Simon Welsh)

View File

@ -1,7 +1,7 @@
title: Changelogs title: Changelogs
introduction: Key information on new features and improvements in each version. introduction: Key information on new features and improvements in each version.
Keep up to date with new releases subscribe to the [SilverStripe Release Announcements](https://groups.google.com/group/silverstripe-announce) group, Keep up to date with new releases by subscribing to the [SilverStripe Release Announcements](https://groups.google.com/group/silverstripe-announce) group,
or read our [blog posts about releases](http://silverstripe.org/blog/tag/release). or read our [blog posts about releases](http://silverstripe.org/blog/tag/release).
We also keep an overview of [security-related releases](http://silverstripe.org/security-releases/). We also keep an overview of [security-related releases](http://silverstripe.org/security-releases/).

View File

@ -62,4 +62,4 @@ read our guide on [how to write secure code](/developer_guides/security/secure_c
* [silverstripe.org/forums](http://www.silverstripe.org/community/forums/): Forums on silverstripe.org * [silverstripe.org/forums](http://www.silverstripe.org/community/forums/): Forums on silverstripe.org
* [silverstripe-dev](http://groups.google.com/group/silverstripe-dev/): Core development mailinglist * [silverstripe-dev](http://groups.google.com/group/silverstripe-dev/): Core development mailinglist
* [silverstripe-documentation](http://groups.google.com/group/silverstripe-translators/): Translation team mailing list * [silverstripe-documentation](http://groups.google.com/group/silverstripe-documentation/): Documentation mailing list

View File

@ -1,6 +1,9 @@
title: Contributing Code
summary: Fix bugs and add new features to help make SilverStripe better.
# Contributing Code - Submiting Bugfixes and Enhancements # Contributing Code - Submiting Bugfixes and Enhancements
SilverStripe will never be finished, and we need your help to keep making it better. If you're a developer a great way to get involved is to contribute patches to our modules and core codebase, fixing bugs or adding feautres. SilverStripe will never be finished, and we need your help to keep making it better. If you're a developer a great way to get involved is to contribute patches to our modules and core codebase, fixing bugs or adding features.
The SilverStripe core modules (`framework` and `cms`), as well as some of the more popular modules are in The SilverStripe core modules (`framework` and `cms`), as well as some of the more popular modules are in
git version control. SilverStripe hosts its modules on [github.com/silverstripe](http://github.com/silverstripe) and [github.com/silverstripe-labs](http://github.com/silverstripe-labs). After [installing git](http://help.github.com/git-installation-redirect) and creating a [free github.com account](https://github.com/signup/free), you can "fork" a module, git version control. SilverStripe hosts its modules on [github.com/silverstripe](http://github.com/silverstripe) and [github.com/silverstripe-labs](http://github.com/silverstripe-labs). After [installing git](http://help.github.com/git-installation-redirect) and creating a [free github.com account](https://github.com/signup/free), you can "fork" a module,
@ -16,7 +19,9 @@ We ask for this so that the ownership in the license is clear and unambiguous, a
## Step-by-step: From forking to sending the pull request ## Step-by-step: From forking to sending the pull request
_**NOTE:** The commands on this page assume that you are branching from `4.0`, at the time of writing this is the pre-release branch._ <div class="notice" markdown='1'>
**Note:** Please adjust the commands below to the version of SilverStripe that you're targeting.
</div>
1. Install the project through composer. The process is described in detail in "[Installation through Composer](../getting_started/composer#contributing)". 1. Install the project through composer. The process is described in detail in "[Installation through Composer](../getting_started/composer#contributing)".
@ -49,18 +54,18 @@ _**NOTE:** The commands on this page assume that you are branching from `4.0`, a
# [make sure all your changes are committed as necessary in branch] # [make sure all your changes are committed as necessary in branch]
git fetch upstream git fetch upstream
git rebase upstream/3.1 git rebase upstream/3.2
6. When development is complete, [squash all commit related to a single issue into a single commit](code#squash-all-commits-related-to-a-single-issue-into-a-single-commit). 6. When development is complete, [squash all commit related to a single issue into a single commit](code#squash-all-commits-related-to-a-single-issue-into-a-single-commit).
git fetch upstream git fetch upstream
git rebase -i upstream/3.1 git rebase -i upstream/3.2
7. Push release candidate branch to GitHub 7. Push release candidate branch to GitHub
git push origin ###-description git push origin ###-description
8. Issue pull request on GitHub. Visit your forked respoistory on GitHub.com and click the "Create Pull Request" button nex tot the new branch. 8. Issue pull request on GitHub. Visit your forked repository on GitHub.com and click the "Create Pull Request" button next to the new branch.
The core team is then responsible for reviewing patches and deciding if they will make it into core. If The core team is then responsible for reviewing patches and deciding if they will make it into core. If
there are any problems they will follow up with you, so please ensure they have a way to contact you! there are any problems they will follow up with you, so please ensure they have a way to contact you!
@ -78,11 +83,11 @@ A core committer will also "label" your PR using the labels defined in GitHub, t
The current GitHub labels are grouped into 5 sections: The current GitHub labels are grouped into 5 sections:
1. Changes - These are designed to signal what kind of change they are and how they fit into the [Semantic Versioning](http://semver.org/) schema 1. *Changes* - These are designed to signal what kind of change they are and how they fit into the [Semantic Versioning](http://semver.org/) schema
2. Impact - What impact does this bug/issue/fix have, does it break a feature completely, is it just a side effect or is it trivial and not a bit problem (but a bit annoying) 2. *Impact* - What impact does this bug/issue/fix have, does it break a feature completely, is it just a side effect or is it trivial and not a bit problem (but a bit annoying)
3. Effort - How much effort is required to fix this issue? 3. *Effort* - How much effort is required to fix this issue?
4. Type - What aspect of the system the PR/issue covers 4. *Type* - What aspect of the system the PR/issue covers
5. Feedback - Are we waiting on feedback, if so who from? Typically used for issues that are likely to take a while to have feedback given 5. *Feedback* - Are we waiting on feedback, if so who from? Typically used for issues that are likely to take a while to have feedback given
| Label | Purpose | | Label | Purpose |
| ----- | ------- | | ----- | ------- |
@ -101,9 +106,9 @@ The current GitHub labels are grouped into 5 sections:
| feedback-required/core-team | Core team members need to give an in-depth consideration | | feedback-required/core-team | Core team members need to give an in-depth consideration |
| feedback-required/author | This issue is awaiting feedback from the original author of the PR | | feedback-required/author | This issue is awaiting feedback from the original author of the PR |
### Workflow Diagram ### ### Workflow Diagram
![Workflow diagram](http://www.silverstripe.org/assets/doc-silverstripe-org/collaboration-on-github.png) [![Workflow diagram](http://www.silverstripe.org/assets/doc-silverstripe-org/collaboration-on-github.png)](http://www.silverstripe.org/assets/doc-silverstripe-org/collaboration-on-github.png)
### Quickfire Do's and Don't's ### Quickfire Do's and Don't's
@ -111,18 +116,18 @@ If you aren't familiar with git and GitHub, try reading the ["GitHub bootcamp do
We also found the [free online git book](http://git-scm.com/book/) and the [git crash course](http://gitref.org/) useful. We also found the [free online git book](http://git-scm.com/book/) and the [git crash course](http://gitref.org/) useful.
If you're familiar with it, here's the short version of what you need to know. Once you fork and download the code: If you're familiar with it, here's the short version of what you need to know. Once you fork and download the code:
* **Don't develop on the master branch.** Always create a development branch specific to "the issue" you're working on (on our [GitHub repository's issues](https://github.com/silverstripe/silverstripe-framework/issues)). Name it by issue number and description. For example, if you're working on Issue #100, a `DataObject::get_one()` bugfix, your development branch should be called 100-dataobject-get-one. If you decide to work on another issue mid-stream, create a new branch for that issue--don't work on both in one branch. * **Don't develop on the master branch.** Always create a development branch specific to "the issue" you're working on (on our [GitHub repository's issues](https://github.com/silverstripe/silverstripe-framework/issues)). Name it by issue number and description. For example, if you're working on Issue #100, a `DataObject::get_one()` bugfix, your development branch should be called 100-dataobject-get-one. If you decide to work on another issue mid-stream, create a new branch for that issue--don't work on both in one branch.
* **Do not merge the upstream master** with your development branch; *rebase* your branch on top of the upstream master. * **Do not merge the upstream master** with your development branch; *rebase* your branch on top of the upstream master.
* **A single development branch should represent changes related to a single issue.** If you decide to work on another issue, create another branch. * **A single development branch should represent changes related to a single issue.** If you decide to work on another issue, create another branch.
* **Squash your commits, so that each commit addresses a single issue.** After you rebase your work on top of the upstream master, you can squash multiple commits into one. Say, for instance, you've got three commits in related to Issue #100. Squash all three into one with the message "Issue #100 Description of the issue here." We won't accept pull requests for multiple commits related to a single issue; it's up to you to squash and clean your commit tree. (Remember, if you squash commits you've already pushed to GitHub, you won't be able to push that same branch again. Create a new local branch, squash, and push the new squashed branch.) * **Squash your commits, so that each commit addresses a single issue.** After you rebase your work on top of the upstream master, you can squash multiple commits into one. Say, for instance, you've got three commits in related to Issue #100. Squash all three into one with the message "Description of the issue here (fixes #100)" We won't accept pull requests for multiple commits related to a single issue; it's up to you to squash and clean your commit tree. (Remember, if you squash commits you've already pushed to GitHub, you won't be able to push that same branch again. Create a new local branch, squash, and push the new squashed branch.)
* **Choose the correct branch**: Assume the current release is 3.0.3, and 3.1.0 is in beta state. * **Choose the correct branch**: Assume the current release is 3.0.3, and 3.1.0 is in beta state.
Most pull requests should go against the `3.1.x-dev` *pre-release branch*, only critical bugfixes Most pull requests should go against the `3.1.x-dev` *pre-release branch*, only critical bugfixes
against the `3.0.x-dev` *release branch*. If you're changing an API or introducing a major feature, against the `3.0.x-dev` *release branch*. If you're changing an API or introducing a major feature,
the pull request should go against `master` (read more about our [release process](release_process)). Branches are periodically merged "upwards" (3.0 into 3.1, 3.1 into master). the pull request should go against `master` (read more about our [release process](release_process)). Branches are periodically merged "upwards" (3.0 into 3.1, 3.1 into master).
### Editing files directly on GitHub.com ### Editing files directly on GitHub.com
@ -132,21 +137,20 @@ After you have edited the file, GitHub will offer to create a pull request for y
## Check List ## Check List
* Adhere to our [coding conventions](/getting_started/coding_conventions) * Adhere to our [coding conventions](/getting_started/coding_conventions)
* If your patch is extensive, discuss it first on the [silverstripe-dev google group](https://groups.google.com/group/silverstripe-dev) (ideally before doing any serious coding) * If your patch is extensive, discuss it first on the [silverstripe-dev google group](https://groups.google.com/group/silverstripe-dev) (ideally before doing any serious coding)
* When working on existing tickets, provide status updates through ticket comments * When working on existing tickets, provide status updates through ticket comments
* Check your patches against the "master" branch, as well as the latest release branch * Check your patches against the "master" branch, as well as the latest release branch
* Write [unit tests](../developer_guides/testing/unit_testing) * Write [unit tests](../developer_guides/testing/unit_testing)
* Write [Behat integration tests](https://github.com/silverstripe-labs/silverstripe-behat-extension) for any interface changes * Write [Behat integration tests](https://github.com/silverstripe-labs/silverstripe-behat-extension) for any interface changes
* Describe specifics on how to test the effects of the patch * Describe specifics on how to test the effects of the patch
* It's better to submit multiple patches with separate bits of functionality than a big patch containing lots of * It's better to submit multiple patches with separate bits of functionality than a big patch containing lots of changes
changes * Only submit a pull request for work you expect to be ready to merge. Work in progress is best discussed in an issue, or on your own repository fork.
* Only submit a pull request for work you expect to be ready to merge. Work in progress is best discussed in an issue, or on your own repository fork. * Document your code inline through [PHPDoc](http://en.wikipedia.org/wiki/PHPDoc) syntax. See our
* Document your code inline through [PHPDoc](http://en.wikipedia.org/wiki/PHPDoc) syntax. See our
[API documentation](http://api.silverstripe.org/3.1/) for good examples. [API documentation](http://api.silverstripe.org/3.1/) for good examples.
* Check and update documentation on [doc.silverstripe.org](http://doc.silverstripe.org). Check for any references to functionality deprecated or extended through your patch. Documentation changes should be included in the patch. * Check and update documentation on [docs.silverstripe.org](http://docs.silverstripe.org). Check for any references to functionality deprecated or extended through your patch. Documentation changes should be included in the patch.
* If you get stuck, please post to the [forum](http://silverstripe.org/forum) or for deeper core problems, to the [core mailinglist](https://groups.google.com/forum/#!forum/silverstripe-dev) * If you get stuck, please post to the [forum](http://silverstripe.org/forum) or for deeper core problems, to the [core mailinglist](https://groups.google.com/forum/#!forum/silverstripe-dev)
* When working with the CMS, please read the ["CMS Architecture Guide"](cms_architecture) first * When working with the CMS, please read the ["CMS Architecture Guide"](/developer_guides/customising_the_admin_interface/cms_architecture/) first
## Commit Messages ## Commit Messages
@ -158,10 +162,9 @@ This ensures commits are easy to browse, and look nice on github.com
As we automatically generate [changelogs](http://doc.silverstripe.org/sapphire/en/trunk/changelogs/) from them, we need a way to categorize and filter. As we automatically generate [changelogs](http://doc.silverstripe.org/sapphire/en/trunk/changelogs/) from them, we need a way to categorize and filter.
Please prefix **noteworthy** commit messages with one of the following tags: Please prefix **noteworthy** commit messages with one of the following tags:
* `NEW`: New feature or major enhancement (both for users and developers) * `NEW` New feature or major enhancement (both for users and developers)
* `API`: Addition of a new API, or modification/removal/deprecation of an existing API. * `API` Addition of a new API, or modification/removal/deprecation of an existing API. Includes any change developers should be aware of when upgrading.
Includes any change developers should be aware of when upgrading. * `BUG` Bugfix or minor enhancement on something developers or users are likely to encounter.
* `BUG`: Bugfix or minor enhancement on something developers or users are likely to encounter.
All other commits should not be tagged if they are so trivial that most developers All other commits should not be tagged if they are so trivial that most developers
can ignore them during upgrades or when reviewing changes to the codebase. can ignore them during upgrades or when reviewing changes to the codebase.
@ -194,16 +197,14 @@ Example: Good commit message
### Branch for new issue and develop on issue branch ### Branch for new issue and develop on issue branch
Before you start working on a new feature or bugfix, create a new branch dedicated to that one change named by issue number and description. If you're working on Issue #100, a retweet bugfix, create a new branch with the issue number and description, like this: Before you start working on a new feature or bugfix, create a new branch dedicated to that one change named by issue number and description. If you're working on Issue #100, a `DataObject::get_one()` bugfix, create a new branch with the issue number and description, like this:
$ git status $ git checkout -b 100-dataobject-get-one
$ git branch 100-dataobject-get-one
$ git checkout 100-dataobject-get-one
Edit and test the files on your development environment. When you've got something the way you want and established that it works, commit the changes to your branch on your local git repo. Edit and test the files on your development environment. When you've got something the way you want and established that it works, commit the changes to your branch on your local git repo.
$ git add <filename> $ git add <filename>
$ git commit -m 'Issue #100: Some kind of descriptive message' $ git commit -m 'Some kind of descriptive message (fixes #100)'
You'll need to use git add for each file that you created or modified. There are ways to add multiple files, but I highly recommend a more deliberate approach unless you know what you're doing. You'll need to use git add for each file that you created or modified. There are ways to add multiple files, but I highly recommend a more deliberate approach unless you know what you're doing.
@ -219,13 +220,13 @@ To keep your development branch up to date, rebase your changes on top of the cu
If you've set up an upstream branch as detailed above, and a development branch called `100-dataobject-get-one`, you can update `upstream` and rebase your branch from it like so: If you've set up an upstream branch as detailed above, and a development branch called `100-dataobject-get-one`, you can update `upstream` and rebase your branch from it like so:
# [make sure all your changes are committed as necessary in branch] # make sure all your changes are committed as necessary in branch
$ git fetch upstream $ git fetch upstream
$ git rebase upstream/master $ git rebase upstream/master
Note that the example doesn't keep your own master branch up to date. If you wanted to that, you might take the following approach instead: Note that the example doesn't keep your own master branch up to date. If you wanted to that, you might take the following approach instead:
# [make sure all your changes are committed as necessary in branch] # make sure all your changes are committed as necessary in branch
$ git fetch upstream $ git fetch upstream
$ git checkout master $ git checkout master
$ git rebase upstream/master $ git rebase upstream/master
@ -247,11 +248,15 @@ To squash four commits into one, do the following:
$ git rebase -i upstream/master $ git rebase -i upstream/master
In the text editor that comes up, replace the words "pick" with "squash" next to the commits you want to squash into the commit before it. Save and close the editor, and git will combine the "squash"'ed commits with the one before it. Git will then give you the opportunity to change your commit message to something like, "BUGFIX Issue #100: Fixed DataObject::get_one() parameter order" In the text editor that comes up, replace the words "pick" with "squash" or just "s" next to the commits you want to squash into the commit before it.
Save and close the editor, and git will combine the "squash"'ed commits with the one before it.
Git will then give you the opportunity to change your commit message to something like, `BUG DataObject::get_one() parameter order (fixes #100)`.
If you want to discard the commit messages from the commits you're squashing and just use the message from your "pick" commit(s) you can use "fixup" or "f" instead of "squash" to bypass the message editing and make the process a bit quicker.
Important: If you've already pushed commits to GitHub, and then squash them locally, you will have to force-push to your GitHub again. Add the `-f` argument to your git push command: Important: If you've already pushed commits to GitHub, and then squash them locally, you will have to force-push to your GitHub again. Add the `-f` argument to your git push command:
$ git push -f origin 100-dataobject-get-one $ git push -f origin 100-dataobject-get-one
Helpful hint: You can always edit your last commit message by using: Helpful hint: You can always edit your last commit message by using:
@ -273,21 +278,23 @@ Sometimes, you might correct an issue which was reported in a different repo. In
$ git commit -m 'Issue silverstripe/silverstripe-cms#100: Some kind of descriptive message' $ git commit -m 'Issue silverstripe/silverstripe-cms#100: Some kind of descriptive message'
Sometimes, you might correct an issue which was reported in a different repo. In these cases, don't simply refer to the issue number as GitHub will infer that as correcting an issue in the current repo. See [Commit Messages](code#commit-messages) above for the correct way to reference these issues.
## What is git rebase? ## What is git rebase?
Using `git rebase` helps create clean commit trees and makes keeping your code up-to-date with the current state of the upstream master easy. Here's how it works. Using `git rebase` helps create clean commit trees and makes keeping your code up-to-date with the current state of the upstream master easy. Here's how it works.
Let's say you're working on Issue #212 a new plugin in your own branch and you start with something like this: Let's say you're working on Issue #212 a new plugin in your own branch and you start with something like this:
1---2---3 #212-my-new-plugin 1---2---3 #212-my-new-plugin
/ /
A---B #master A---B #master
You keep coding for a few days and then pull the latest upstream stuff and you end up like this: You keep coding for a few days and then pull the latest upstream stuff and you end up like this:
1---2---3 #212-my-new-plugin 1---2---3 #212-my-new-plugin
/ /
A---B--C--D--E--F #master A---B--C--D--E--F #master
So all these new things (C,D,..F) have happened since you started. Normally you would just keep going (let's say you're not finished with the plugin yet) and then deal with a merge later on, which becomes a commit, which get moved upstream and ends up grafted on the tree forever. So all these new things (C,D,..F) have happened since you started. Normally you would just keep going (let's say you're not finished with the plugin yet) and then deal with a merge later on, which becomes a commit, which get moved upstream and ends up grafted on the tree forever.
@ -297,9 +304,9 @@ git rebase master 212-my-new-plugin
git will rewrite your commits like this: git will rewrite your commits like this:
1---2---3 #212-my-new-plugin 1---2---3 #212-my-new-plugin
/ /
A---B--C--D--E--F #master A---B--C--D--E--F #master
It's as if you had just started your branch. One immediate advantage you get is that you can test your branch now to see if C, D, E, or F had any impact on your code (you don't need to wait until you're finished with your plugin and merge to find this out). And, since you can keep doing this over and over again as you develop your plugin, at the end your merge will just be a fast-forward (in other words no merge at all). It's as if you had just started your branch. One immediate advantage you get is that you can test your branch now to see if C, D, E, or F had any impact on your code (you don't need to wait until you're finished with your plugin and merge to find this out). And, since you can keep doing this over and over again as you develop your plugin, at the end your merge will just be a fast-forward (in other words no merge at all).

View File

@ -2,7 +2,7 @@ summary: Describes the process followed for "core" releases.
# Release Process # Release Process
Describes the process followed for "core" releases (mainly the `framework` and `cms` modules). This page describes the process followed for "core" releases (mainly the `framework` and `cms` modules).
## Release Maintainer ## Release Maintainer
@ -19,13 +19,13 @@ Release dates are usually not published prior to the release, but you can get a
reviewing the release milestone on github.com. Releases will be reviewing the release milestone on github.com. Releases will be
announced on the [release announcements mailing list](http://groups.google.com/group/silverstripe-announce). announced on the [release announcements mailing list](http://groups.google.com/group/silverstripe-announce).
Releases of the *cms* and *framework* modules are coupled at the moment, they follow the same numbering scheme. Releases of the *cms* and *framework* modules are coupled at the moment, and they follow the same numbering scheme.
## Release Numbering ## Release Numbering
SilverStripe follows [Semantic Versioning](http://semver.org). SilverStripe follows [Semantic Versioning](http://semver.org).
Note: Until November 2014, the project didn't adhere to Semantic Versioning. Instead. a "minor release" in semver terminology Note: Until November 2014, the project didn't adhere to Semantic Versioning. Instead, a "minor release" in semver terminology
was treated as a "major release" in SilverStripe. For example, the *3.1.0* release contained API breaking changes, and was treated as a "major release" in SilverStripe. For example, the *3.1.0* release contained API breaking changes, and
the *3.1.1* release contained new features rather than just bugfixes. the *3.1.1* release contained new features rather than just bugfixes.
@ -43,7 +43,7 @@ patch release
## Deprecation ## Deprecation
Needs of developers (both on core framework and custom projects) can outgrow the capabilities Needs of developers (both on core framework and custom projects) can outgrow the capabilities
of a certain API. Existing APIs might turn out to be hard to understand, maintain, test or stabilize. of a certain API. Existing APIs might turn out to be hard to understand, maintain, test or stabilise.
In these cases, it is best practice to "refactor" these APIs into something more useful. In these cases, it is best practice to "refactor" these APIs into something more useful.
SilverStripe acknowledges that developers have built a lot of code on top of existing APIs, SilverStripe acknowledges that developers have built a lot of code on top of existing APIs,
so we strive for giving ample warning on any upcoming changes through a "deprecation cycle". so we strive for giving ample warning on any upcoming changes through a "deprecation cycle".
@ -53,17 +53,15 @@ How to deprecate an API:
* Add a `@deprecated` item to the docblock tag, with a `{@link <class>}` item pointing to the new API to use. * Add a `@deprecated` item to the docblock tag, with a `{@link <class>}` item pointing to the new API to use.
* Update the deprecated code to throw a `[api:Deprecation::notice()]` error. * Update the deprecated code to throw a `[api:Deprecation::notice()]` error.
* Both the docblock and error message should contain the **target version** where the functionality is removed. * Both the docblock and error message should contain the **target version** where the functionality is removed.
So if you're committing the change to a *3.1* minor release, the target version will be *4.0*. So, if you're committing the change to a *3.1* minor release, the target version will be *4.0*.
* Deprecations should not be committed to patch releases * Deprecations should not be committed to patch releases
* Deprecations should just be committed to pre-release branches, ideally before they enter the "beta" phase. * Deprecations should only be committed to pre-release branches, ideally before they enter the "beta" phase.
If deprecations are introduced after this point, their target version needs to be increased by one. If deprecations are introduced after this point, their target version needs to be increased by one.
* Make sure that the old deprecated function works by calling the new function - don't have duplicated code! * Make sure that the old deprecated function works by calling the new function - don't have duplicated code!
* The commit message should contain an `API` prefix (see ["commit message format"](code#commit-messages)) * The commit message should contain an `API` prefix (see ["commit message format"](code#commit-messages))
* Document the change in the [changelog](/changelogs) for the next release * Document the change in the [changelog](/changelogs) for the next release
* Deprecated APIs can be removed after developers had a chance to react to the changes. As a rule of thumb, leave the * Deprecated APIs can be removed only after developers have had sufficient time to react to the changes. Hence, deprecated APIs should be removed in MAJOR releases only. Between MAJOR releases, leave the code in place with a deprecation warning.
code with the deprecation warning in for at least three micro releases. Only remove code in a minor or major release. * Exceptions to the deprecation cycle are APIs that have been moved into their own module, and continue to work with the new minor release. These changes can be performed in a single minor release without a deprecation period.
* Exceptions to the deprecation cycle are APIs that have been moved into their own module, and continue to work with the
new minor release. These changes can be performed in a single minor release without a deprecation period.
Here's an example for replacing `Director::isDev()` with a (theoretical) `Env::is_dev()`: Here's an example for replacing `Director::isDev()` with a (theoretical) `Env::is_dev()`:
@ -77,8 +75,27 @@ Here's an example for replacing `Director::isDev()` with a (theoretical) `Env::i
return Env::is_dev(); return Env::is_dev();
} }
This change could be committed to a minor release like *3.2.0*, and stays deprecated in all following minor releases This change could be committed to a minor release like *3.2.0*, and remains deprecated in all subsequent minor releases
(e.g. *3.3.0*, *3.4.0*), until a new major release (e.g. *4.0.0*) where it gets removed from the codebase. (e.g. *3.3.0*, *3.4.0*), until a new major release (e.g. *4.0.0*), at which point it gets removed from the codebase.
Deprecation notices are enabled by default on dev environment, but can be
turned off via either _ss_environment.php or in your _config.php. Deprecation
notices are always disabled on both live and test.
`mysite/_config.php`
:::php
Deprecation::set_enabled(false);
`_ss_environment.php`
:::php
define('SS_DEPRECATION_ENABLED', false);
## Security Releases ## Security Releases
@ -104,7 +121,7 @@ previous major release (if applicable).
[new release](http://silverstripe.org/security-releases/) publically. [new release](http://silverstripe.org/security-releases/) publically.
You can help us determine the problem and speed up responses by providing us with more information on how to reproduce You can help us determine the problem and speed up responses by providing us with more information on how to reproduce
the issue: SilverStripe version (incl. any installed modules), PHP/webserver version and configuration, anonymized the issue: SilverStripe version (incl. any installed modules), PHP/webserver version and configuration, anonymised
webserver access logs (if a hack is suspected), any other services and web packages running on the same server. webserver access logs (if a hack is suspected), any other services and web packages running on the same server.
### Severity rating ### Severity rating
@ -114,7 +131,7 @@ each vulnerability. The rating indicates how important an update is:
| Severity | Description | | Severity | Description |
|---------------|-------------| |---------------|-------------|
| **Critical** | Critical releases require immediate actions. Such vulnerabilities allow attackers to take control of your site and you should upgrade on the day of release. *Example: Directory traversal, privilege escalation* | | **Critical** | Critical releases require immediate action. Such vulnerabilities allow attackers to take control of your site and you should upgrade on the day of release. *Example: Directory traversal, privilege escalation* |
| **Important** | Important releases should be evaluated immediately. These issues allow an attacker to compromise a site's data and should be fixed within days. *Example: SQL injection.* | | **Important** | Important releases should be evaluated immediately. These issues allow an attacker to compromise a site's data and should be fixed within days. *Example: SQL injection.* |
| **Moderate** | Releases of moderate severity should be applied as soon as possible. They allow the unauthorized editing or creation of content. *Examples: Cross Site Scripting (XSS) in template helpers.* | | **Moderate** | Releases of moderate severity should be applied as soon as possible. They allow the unauthorized editing or creation of content. *Examples: Cross Site Scripting (XSS) in template helpers.* |
| **Low** | Low risk releases fix information disclosure and read-only privilege escalation vulnerabilities. These updates should also be applied as soon as possible, but with an impact-dependent priority. *Example: Exposure of the core version number, Cross Site Scripting (XSS) limited to the admin interface.* | | **Low** | Low risk releases fix information disclosure and read-only privilege escalation vulnerabilities. These updates should also be applied as soon as possible, but with an impact-dependent priority. *Example: Exposure of the core version number, Cross Site Scripting (XSS) limited to the admin interface.* |

View File

@ -3,31 +3,30 @@ summary: Writing guide for contributing to SilverStripe developer and CMS user h
# Contributing documentation # Contributing documentation
Documentation for a software project is a continued and collaborative effort. We encourage everybody to contribute in any way they can, from simply fixing spelling mistakes, to writing recipes, to reviewing existing documentation and translating it to another language. Documentation for a software project is a continuing, collaborative effort. We encourage everybody to contribute in any way they can, from simply fixing spelling mistakes, to writing recipes, reviewing existing documentation and translation to other languages.
Modifying documentation requires basic [PHPDoc](http://en.wikipedia.org/wiki/PHPDoc) and Modifying documentation requires basic knowledge of [PHPDoc](http://en.wikipedia.org/wiki/PHPDoc) and
[Markdown](http://daringfireball.net/projects/markdown/) knowledge, and a GitHub user account. [Markdown](http://daringfireball.net/projects/markdown/) as well as a GitHub user account.
## Editing online ## Editing online
The easiest way of editing any documentation is by clicking the "Edit this page" link at the bottom of the The easiest way of editing any documentation is by clicking the "Edit this page" button at the bottom of the
page you want to edit. Alternatively, locate the appropriate .md file in the page you want to edit. Alternatively, locate the appropriate .md file in the
[github.com/silverstripe/silverstripe-framework](https://github.com/silverstripe/silverstripe-framework/tree/master/docs/) repository and press the "edit" button. **You will need a free GitHub account to do this**. [github.com/silverstripe/silverstripe-framework](https://github.com/silverstripe/silverstripe-framework/tree/master/docs/) repository and press the "edit" button. **You will need a free GitHub account to do this**.
* After editing the documentation, describe your changes in the "commit summary" and "extended description" fields below then press "Commit Changes". * After editing the documentation, describe your changes in the "commit summary" and "extended description" fields below then press "Commit Changes".
* After that you will see a form to submit a Pull Request: "[pull requests](http://help.github.com/pull-requests/)". You should be able to adjust the version your * After committing you changes, you will see a form to submit a Pull Request: "[pull requests](http://help.github.com/pull-requests/)". You should be able to adjust the version to which your documentation changes apply before submitting the form. Any changes submitted in a pull request will be sent to the core committers for approval.
* documentation changes are for and then submit the form. Your changes will be sent to the core committers for approval.
<div class="warning" markdown='1'> <div class="warning" markdown='1'>
You should make your changes in the lowest branch they apply to. For instance, if you fix a spelling issue that you found in the 3.1 documentation, submit your fix to that branch in Github and it'll be copied to the master (3.2) version of the documentation automatically. *Don't submit multiple pull requests*. You should make your changes in the lowest branch they apply to. For instance, if you fix a spelling issue that you found in the 3.2 documentation, submit your fix to that branch in Github and it'll be copied to the master (4.0) version of the documentation automatically. *Don't submit multiple pull requests*.
</div> </div>
## Editing on your computer ## Editing on your computer
If you prefer to edit content on your local machine, you can "[fork](http://help.github.com/forking/)" the If you prefer to edit content on your local machine, you can "[fork](http://help.github.com/forking/)" the
[github.com/silverstripe/silverstripe-framework](http://github.com/silverstripe/silverstripe-framework) and [github.com/silverstripe/silverstripe-framework](http://github.com/silverstripe/silverstripe-framework) and
[github.com/silverstripe/silverstripe-cms](http://github.com/silverstripe/silverstripe-cms) repositories, and send us "[pull requests](http://help.github.com/pull-requests/)" to incorporate your changes. If you have previously downloaded SilverStripe or a module, chances are that you already have these repositories on your machine. [github.com/silverstripe/silverstripe-cms](http://github.com/silverstripe/silverstripe-cms) repositories, make changes locally, and then send us a "[pull request](http://help.github.com/pull-requests/)" to incorporate your changes. If you have previously downloaded SilverStripe or a module, chances are that you already have these repositories on your machine.
The documentation is kept alongside the source code in the `docs/` subfolder of any SilverStripe module, framework or CMS folder. The documentation is kept alongside the source code in the `docs/` subfolder of any SilverStripe module, framework or CMS folder.
@ -37,8 +36,8 @@ If you submit a new feature or an API change, we strongly recommend that your pa
## Repositories ## Repositories
* End-user: [userhelp.silverstripe.org](http://github.com/silverstripe/userhelp.silverstripe.org) * End-user help: [userhelp.silverstripe.org](http://github.com/silverstripe/userhelp.silverstripe.org)
* Developer guides: [doc.silverstripe.org](http://github.com/silverstripe/doc.silverstripe.org) * Developer guides: [docs.silverstripe.org](http://github.com/silverstripe/docs.silverstripe.org)
* Developer API documentation: [api.silverstripe.org](http://github.com/silverstripe/api.silverstripe.org) * Developer API documentation: [api.silverstripe.org](http://github.com/silverstripe/api.silverstripe.org)
## Source control ## Source control
@ -52,12 +51,12 @@ for documenting open source software.
## Structure ## Structure
* Keep documentation lines to 120 characters. * Keep documentation lines shorter than 120 characters.
* Don't duplicate: Search for existing places to put your documentation. Do you really require a new page, or just a new paragraph of text somewhere? * Don't duplicate: Search for existing places to put your documentation. Do you really require a new page, or just a new paragraph of text somewhere?
* Use PHPDoc in source code: Leave low level technical documentation to code comments within PHP, in [PHPDoc](http://en.wikipedia.org/wiki/PHPDoc) format. * Use PHPDoc in source code: Leave low level technical documentation to code comments within PHP, in [PHPDoc](http://en.wikipedia.org/wiki/PHPDoc) format.
* API and developer guides are two forms of source code documentation that complement each other. * API and developer guides are two forms of source code documentation that complement each other.
* API documentation should provide context, ie, the "bigger picture", by referring to developer guides inside your PHPDoc. * API documentation should provide context, ie, the "bigger picture", by referring to developer guides inside your PHPDoc.
* Make your documentation easy to find: Documentation lives by interlinking content so please make sure your contribution doesn't become an inaccessible island. At the very least, put a link to your should on the index page in the same folder. A link to your page can also appear * Make your documentation easy to find: Documentation is useful only when it is interlinked so please make sure your contribution doesn't become an inaccessible island. At the very least, put a link to your index page in the same folder. A link to your page can also appear
as "related content" on other resource (e.g. `/tutorials/site_search` might link to `/developer_guides/forms/introduction`). as "related content" on other resource (e.g. `/tutorials/site_search` might link to `/developer_guides/forms/introduction`).
## Writing style ## Writing style
@ -65,7 +64,7 @@ as "related content" on other resource (e.g. `/tutorials/site_search` might link
* Write in second person plural form: Use "we" instead of "I". It gives the text an instructive and collaborative style. * Write in second person plural form: Use "we" instead of "I". It gives the text an instructive and collaborative style.
* It's okay to address the reader: For example "First you'll install a webserver" is good style. * It's okay to address the reader: For example "First you'll install a webserver" is good style.
* Write in an active and direct voice. * Write in an active and direct voice.
* Mark up correctly: Use preformatted text. Emphasis and bold make technical writing more easily "scannable". * Mark up correctly: The use of preformatted text, emphasis and bold make technical writing easier to scan.
* Avoid FAQs: FAQs are not a replacement for coherent, well explained documentation. If you've done a good job * Avoid FAQs: FAQs are not a replacement for coherent, well explained documentation. If you've done a good job
documenting, there shouldn't be any "frequently asked questions" left. documenting, there shouldn't be any "frequently asked questions" left.
* "SilverStripe" should always appear without a space with both "S"s capitalised. * "SilverStripe" should always appear without a space with both "S"s capitalised.
@ -74,7 +73,7 @@ documenting, there shouldn't be any "frequently asked questions" left.
* We use sentence case for titles so only capitalise the first letter of the first word of a title. The only exceptions to this are when using brand names (e.g. SilverStripe), acronyms (e.g. PHP) and class names (e.g. ModelAdmin). * We use sentence case for titles so only capitalise the first letter of the first word of a title. The only exceptions to this are when using brand names (e.g. SilverStripe), acronyms (e.g. PHP) and class names (e.g. ModelAdmin).
* Use gender neutral language throughout the document, unless referencing a specific person. Use them, they, their, instead of he and she, his or her. * Use gender neutral language throughout the document, unless referencing a specific person. Use them, they, their, instead of he and she, his or her.
* URLs: if the end of your sentence is a URL then you don't need to use a full stop. * URLs: if the end of your sentence is a URL then you don't need to use a full stop.
* Bullet points: Sentence case your bullet points. If a bullet point is a full sentence then end with a full stop. If it is a short point or a list, full stops are not required. * Bullet points: Sentence case your bullet points. If a bullet point is a full sentence then end with a full stop. If it is a sentence fragment or a comma separated list, full stops are not required.
## Highlighted blocks ## Highlighted blocks
@ -85,7 +84,7 @@ sparingly.
"Tip box": A tip box is great for adding, deepening or accenting information in the main text. They can be used for background knowledge, or to provide links to further information (ie, a "see also" link). "Tip box": A tip box is great for adding, deepening or accenting information in the main text. They can be used for background knowledge, or to provide links to further information (ie, a "see also" link).
</div> </div>
Code: Code for a Tip box:
<div class="hint" markdown='1'> <div class="hint" markdown='1'>
... ...
@ -95,7 +94,7 @@ Code:
"Notification box": A notification box is good for technical notifications relating to the main text. For example, notifying users about a deprecated feature. "Notification box": A notification box is good for technical notifications relating to the main text. For example, notifying users about a deprecated feature.
</div> </div>
Code: Code for a Notification box:
<div class="notice" markdown='1'> <div class="notice" markdown='1'>
... ...
@ -105,7 +104,7 @@ Code:
"Warning box": A warning box is useful for highlighting a severe bug or a technical issue requiring a user's attention. For example, suppose a rare edge case sometimes leads to a variable being overwritten incorrectly. A warning box can be used to alert the user to this case so they can write their own code to handle it. "Warning box": A warning box is useful for highlighting a severe bug or a technical issue requiring a user's attention. For example, suppose a rare edge case sometimes leads to a variable being overwritten incorrectly. A warning box can be used to alert the user to this case so they can write their own code to handle it.
</div> </div>
Code: Code for a Warning box:
<div class="warning" markdown='1'> <div class="warning" markdown='1'>
... ...
@ -118,7 +117,7 @@ on placing HTML blocks inside Markdown.
Documentation is kept alongside the source code, typically in a module subdirectory like `framework/docs/en/`. Each language has its own subfolder, which can duplicate parts of or the entire body of documentation. German documentation would, for example, live in `framework/docs/de/`. The Documentation is kept alongside the source code, typically in a module subdirectory like `framework/docs/en/`. Each language has its own subfolder, which can duplicate parts of or the entire body of documentation. German documentation would, for example, live in `framework/docs/de/`. The
[docsviewer](https://github.com/silverstripe/silverstripe-docsviewer) module that drives [docsviewer](https://github.com/silverstripe/silverstripe-docsviewer) module that drives
[doc.silverstripe.org](http://doc.silverstripe.org) automatically resolves these subfolders into a language dropdown. [docs.silverstripe.org](http://docs.silverstripe.org) automatically resolves these subfolders into a language dropdown.
## Further reading ## Further reading

View File

@ -1,12 +1,12 @@
title: Implement Internationalization title: Implement Internationalisation
summary: Implement SilverStripe's internationalization system in your own modules. summary: Implement SilverStripe's internationalisation system in your own modules.
# Implementing Internationalization # Implementing Internationalisation
To find out about how to assist with translating SilverStripe from a users point of view, see the To find out about how to assist with translating SilverStripe from a user's point of view, see the
[Contributing Translations page](/contributing/translations). [Contributing Translations page](/contributing/translations).
## Set up your own module for localization ## Set up your own module for localisation
### Collecting translatable text ### Collecting translatable text
@ -33,7 +33,7 @@ source_lang = en
type = YML type = YML
``` ```
If you don't have existing translations, your project is ready to go - simply point translators to the URL, have them If you don't have existing translations to import, your project is ready to go - simply point translators to the URL, have them
sign up, and they can create languages and translations as required. sign up, and they can create languages and translations as required.
### Import existing translations ### Import existing translations
@ -57,7 +57,7 @@ assumes you're adhering to the following guidelines:
- Run the `i18nTextCollectorTask` with the `merge=true` option to avoid deleting unused entities - Run the `i18nTextCollectorTask` with the `merge=true` option to avoid deleting unused entities
(which might still be relevant in older release branches) (which might still be relevant in older release branches)
### Converting your language files from 2.4 PHP format ### Converting your language files from 2.4 PHP format to YML
The conversion from PHP format to YML is taken care of by a module called The conversion from PHP format to YML is taken care of by a module called
[i18n_yml_converter](https://github.com/chillu/i18n_yml_converter). [i18n_yml_converter](https://github.com/chillu/i18n_yml_converter).
@ -116,7 +116,7 @@ First of all, you need to create those source files in JSON, and store them in `
source_lang = en source_lang = en
type = KEYVALUEJSON type = KEYVALUEJSON
Now you can upload the source files via a normal `tx push`. Once translations come in, you need to convert the source Then you can upload the source files via a normal `tx push`. Once translations come in, you need to convert the source
files back into the JS files SilverStripe can actually read. This requires an installation of our files back into the JS files SilverStripe can actually read. This requires an installation of our
[buildtools](https://github.com/silverstripe/silverstripe-buildtools). [buildtools](https://github.com/silverstripe/silverstripe-buildtools).

View File

@ -1,34 +0,0 @@
# Core Committers
The core committers team is reviewed approximately annually, new members are added based on quality contributions to SilverStipe code and outstanding community participation.
## Core committer team
* [Damian Mooyman](https://github.com/tractorcow/)
* [Daniel Hensby](https://github.com/dhensby)
* [Hamish Friedlander](https://github.com/hafriedlander)
* [Ingo Schommer](https://github.com/chillu)
* [Loz Calver](https://github.com/kinglozzer)
* [Mateusz Uzdowski](https://github.com/mateusz/)
* [Sam Minnée](https://github.com/sminnee)
* [Sean Harvey](https://github.com/halkyon/)
* [Stig Lindqvist](https://github.com/stojg)
* [Will Morgan](https://github.com/willmorgan)
* [Will Rossiter](https://github.com/wilr/)
## House rules for the core committer team
The "core committers" consist of everybody with write permissions to our codebase.
With great power comes great responsibility, so we have agreed on certain expectations:
* Be friendly, encouraging and constructive towards other community members
* Frequently review pull requests and new issues (in particular, respond quickly to @mentions)
* Treat issues according to our [issue guidelines](issues_and_bugs)
* Don't commit directly to core, raise pull requests instead (except trivial fixes)
* Only merge code you have tested and fully understand. If in doubt, ask for a second opinion.
* Ensure contributions have appropriate [test coverage](/topics/testing), are documented, and pass our [coding conventions](/getting_started/coding-conventions)
* Keep the codebase "releasable" at all times (check our [release process](release_process))
* API changes and non-trivial features should not be merged into release branches.
* API changes on master should not be merged until they have the buy-in of at least two core committers (or better, through the [core mailing list](https://groups.google.com/forum/#!forum/silverstripe-dev))
* Be inclusive. Ensure a wide range of SilverStripe developers can obtain an understanding of your code and docs, and you're not the only one who can maintain it.
* Avoid `git push --force`, and be careful with your git remotes (no accidental pushes)
* Use your own forks to create feature branches

View File

@ -10,39 +10,39 @@ Honour the code of conduct whenever you participate formally and informally in o
* Experiment with your code and share the results. * Experiment with your code and share the results.
* Ask questions if you get stuck. * Ask questions if you get stuck.
* If you can answer questions be responsive and help guide others towards solutions * If you can answer questions, be responsive and help guide others towards solutions.
* Share code and knowledge that might help others. * Share code and knowledge that might help others.
* The community is first and foremost about people sharing their knowledge to build something great together. * The community is first and foremost about people sharing their knowledge to build something great together.
## Be empathetic, friendly and considerate. ## Be empathetic, friendly and considerate.
* Welcome and encourage participation from everyone that wishes to join our community. * Welcome and encourage participation from everyone that wishes to join and contribute to our community.
* Remember growing the community socially is as important as code related matters. * Remember, growing the community socially is just as important as code related matters.
* Be respectful and honour diversity in our community. Sexist, racist, and other exclusionary jokes can offend those around you and are not appropriate. * Be respectful and honour diversity in our community. Sexist, racist, and other exclusionary jokes can offend those around you and are not appropriate.
* Keep your actions lawful, non-discriminatory, and unthreatening towards others. * Keep your actions lawful, non-discriminatory, and unthreatening towards others.
## Respect the readers time. ## Respect the readers time.
* Be concise and read over things before you post. * Be concise and read over things before you post.
* Mind your words, you write something once that will be read by many - being abusive, mean spirited and swearing at others does nothing to help get points across and drive constructive contributions to open source. * Mind your words; you write a single time but what you write will be read by many. Being abusive, mean spirited and swearing at others do nothing to help get your points across nor do they drive constructive collaboration and hence stimulate contributions to open source.
* Be clear when responding who you are representing, are you speaking on behalf of another business or acting in your capacity as an individual community member? Either is fine and encouraged and you should aim to make it clear to the reader. * Be clear about who you are representing when responding; are you speaking on behalf of another business or acting in your capacity as an individual community member? Either is fine and both are encouraged. Just make it clear to the reader.
## Respect different levels of participation. ## Respect different levels of participation.
* Welcome newcomers, spend a little time helping orientate them in our community. * Welcome newcomers. Spend some time helping them to get orientated in our community.
* All contributors and core committers, module maintainers and knowledge sharers are participating in the community in a voluntary nature. Have respect for them and their time when making requests. You may need to exercise some patience. * All contributors and core committers, module maintainers and knowledge sharers are participating in the community in a voluntary nature. Have respect for them and their time when making requests. You may need to exercise some patience.
* Whenever possible reference your comments to others with example code or links to documentation. This helps people learn and become more experienced community members and developers. * Whenever possible, reference your comments to others with example code or links to documentation. This helps people learn and become more experienced community members and developers.
* If you manage to solve your own problem, tell others how you solved it. This will help people in the future that find they have the same problem as you. * If you manage to solve your own problem, tell others how you solved it. This will help people in the future who have to solve similar problems.
* If you are reducing your input and potentially stepping away from the community please remember others might rely on your contributions. Be prepared to ensure these are kept open and available, spend some time handing over to another community member or core contributor to help continuity of SilverStripe open source. * If you are reducing your input, and potentially stepping away from the community, please remember that others might be relying on your contributions. Be prepared to ensure your contributions remain open and available to the community. Spend some time handing your contributions over to another community member or core contributor to help with the continuity of SilverStripe open source development.
## Resolve conflicts directly ## Resolve conflicts directly
* Conflict may eventuate from time to time, we should view it as a constructive process. Understanding others positions without descending into ad hominem attacks is valuable. * Conflict may eventuate from time to time. We should all try to view it as a constructive process. Understanding others' positions, without descending into ad hominem attacks, is valuable and may lead to better solutions to problems.
* Involve a 3rd party or if necessary inform a core committer if conflicts continue to escalate. * Involve a 3rd party. If necessary, inform a core committer if a conflict continues to escalate.
* Breaches of the code of conduct by community members may first result in a reminder about the code of conduct (we realise we're all human and sometimes we have bad days). * Breaches of the code of conduct by a community member may result in a first reminder about the code of conduct (we realise we're all human and sometimes we have bad days).
* Repeated and deliberate breaching after this will be referred to the core committers team and may result in members being asked to leave the community channels. * Repeated and deliberate breachings of the code of conduct following this first reminder will be referred on to the team of core committers and may result in the member being asked to leave the community channels.
* While we have a policy of not removing postings and comments from our digital channels, there may be times in which items are removed if deemed blatantly discriminatory towards individuals, groups or cultures. * While we have a policy of not removing postings and comments from our digital channels, there may be times in which items are removed if deemed blatantly discriminatory towards individuals, groups or cultures.
* Call others on their behaviour within the community and remind them of this code where necessary. * If a member behaves inappropriately, politely bring this to their attention and gently remind them of the code of conduct when necessary.
* Refer to [these helpful guides on conflict resolution](http://www.crnhq.org/pages.php?pID=10) to aid you. * Refer to [these helpful guides on conflict resolution](http://www.crnhq.org/pages.php?pID=10) to aid you when dealing with a conflict.
## A living document ## A living document
* This is a living document, as core committers we don't have all the answers and we attempt to make the community an innovative and valuable space for everyone that wishes to participate. * This is a living document. As core committers, we know we don't have all the answers so we want to make the community an innovative and valuable space for everyone that wishes to contribute.
* If you would like to improve the wording, add additional items, or simply fix typos, each contribution to the code of conduct will be considered on it's merit and agreed upon unanimously by the core committers. Any changes will be included in future revisions of the SilverStripe code of conduct. * If you would like to improve the wording, add additional items, or simply fix typos, each contribution to the code of conduct will be considered on it's merit and agreed upon unanimously by the core committers. Any changes will be included in future revisions of the SilverStripe code of conduct.

View File

@ -62,10 +62,10 @@ class Email extends ViewableData {
protected $bcc; protected $bcc;
/** /**
* @deprecated since version 3.3 * @deprecated since version 4.0
*/ */
public static function set_mailer(Mailer $mailer) { public static function set_mailer(Mailer $mailer) {
Deprecation::notice('3.3.0', 'Use Injector to override the Mailer service'); Deprecation::notice('4.0', 'Use Injector to override the Mailer service');
Injector::inst()->registerService($mailer, 'Mailer'); Injector::inst()->registerService($mailer, 'Mailer');
} }
@ -162,7 +162,7 @@ class Email extends ViewableData {
if($bcc != null) $this->bcc = $bcc; if($bcc != null) $this->bcc = $bcc;
if($bounceHandlerURL != null) { if($bounceHandlerURL != null) {
Deprecation::notice('3.1', 'Use "emailbouncehandler" module'); Deprecation::notice('4.0', 'Use "emailbouncehandler" module');
} }
parent::__construct(); parent::__construct();
@ -177,8 +177,11 @@ class Email extends ViewableData {
return $this; return $this;
} }
/**
* @deprecated since version 4.0
*/
public function setBounceHandlerURL($bounceHandlerURL) { public function setBounceHandlerURL($bounceHandlerURL) {
Deprecation::notice('3.1', 'Use "emailbouncehandler" module'); Deprecation::notice('4.0', 'Use "emailbouncehandler" module');
} }
public function attachFile($filename, $attachedFilename = null, $mimetype = null) { public function attachFile($filename, $attachedFilename = null, $mimetype = null) {
@ -538,20 +541,20 @@ class Email extends ViewableData {
* *
* Used by {@link Email->send()}, {@link Email->sendPlain()}, {@link Debug->friendlyError()}. * Used by {@link Email->send()}, {@link Email->sendPlain()}, {@link Debug->friendlyError()}.
* *
* @deprecated 3.2 Use the "Email.admin_email" config setting instead * @deprecated 4.0 Use the "Email.admin_email" config setting instead
* @param string $newEmail * @param string $newEmail
*/ */
public static function setAdminEmail($newEmail) { public static function setAdminEmail($newEmail) {
Deprecation::notice('3.2', 'Use the "Email.admin_email" config setting instead'); Deprecation::notice('4.0', 'Use the "Email.admin_email" config setting instead');
Config::inst()->update('Email', 'admin_email', $newEmail); Config::inst()->update('Email', 'admin_email', $newEmail);
} }
/** /**
* @deprecated 3.2 Use the "Email.admin_email" config setting instead * @deprecated 4.0 Use the "Email.admin_email" config setting instead
* @return string * @return string
*/ */
public static function getAdminEmail() { public static function getAdminEmail() {
Deprecation::notice('3.2', 'Use the "Email.admin_email" config setting instead'); Deprecation::notice('4.0', 'Use the "Email.admin_email" config setting instead');
return Config::inst()->get('Email', 'admin_email'); return Config::inst()->get('Email', 'admin_email');
} }
@ -562,10 +565,10 @@ class Email extends ViewableData {
* *
* if(!Director::isLive()) Email::send_all_emails_to("someone@example.com") * if(!Director::isLive()) Email::send_all_emails_to("someone@example.com")
* *
* @deprecated 3.2 Use the "Email.send_all_emails_to" config setting instead * @deprecated 4.0 Use the "Email.send_all_emails_to" config setting instead
*/ */
public static function send_all_emails_to($emailAddress) { public static function send_all_emails_to($emailAddress) {
Deprecation::notice('3.2', 'Use the "Email.send_all_emails_to" config setting instead'); Deprecation::notice('4.0', 'Use the "Email.send_all_emails_to" config setting instead');
Config::inst()->update('Email', 'send_all_emails_to', $emailAddress); Config::inst()->update('Email', 'send_all_emails_to', $emailAddress);
} }
@ -580,10 +583,10 @@ class Email extends ViewableData {
* *
* if(Director::isLive()) Email::cc_all_emails_to("supportperson@example.com") * if(Director::isLive()) Email::cc_all_emails_to("supportperson@example.com")
* *
* @deprecated 3.2 Use the "Email.cc_all_emails_to" config setting instead * @deprecated 4.0 Use the "Email.cc_all_emails_to" config setting instead
*/ */
public static function cc_all_emails_to($emailAddress) { public static function cc_all_emails_to($emailAddress) {
Deprecation::notice('3.2', 'Use the "Email.cc_all_emails_to" config setting instead'); Deprecation::notice('4.0', 'Use the "Email.cc_all_emails_to" config setting instead');
Config::inst()->update('Email', 'cc_all_emails_to', $emailAddress); Config::inst()->update('Email', 'cc_all_emails_to', $emailAddress);
} }
@ -598,10 +601,10 @@ class Email extends ViewableData {
* *
* if(Director::isLive()) Email::cc_all_emails_to("supportperson@example.com") * if(Director::isLive()) Email::cc_all_emails_to("supportperson@example.com")
* *
* @deprecated 3.2 Use the "Email.bcc_all_emails_to" config setting instead * @deprecated 4.0 Use the "Email.bcc_all_emails_to" config setting instead
*/ */
public static function bcc_all_emails_to($emailAddress) { public static function bcc_all_emails_to($emailAddress) {
Deprecation::notice('3.2', 'Use the "Email.bcc_all_emails_to" config setting instead'); Deprecation::notice('4.0', 'Use the "Email.bcc_all_emails_to" config setting instead');
Config::inst()->update('Email', 'bcc_all_emails_to', $emailAddress); Config::inst()->update('Email', 'bcc_all_emails_to', $emailAddress);
} }

View File

@ -477,17 +477,17 @@ class Mailer extends Object {
} }
/** /**
* @deprecated since version 3.2.0 * @deprecated since version 4.0
*/ */
public function wrapImagesInline($htmlContent) { public function wrapImagesInline($htmlContent) {
Deprecation::notice('3.2.0', 'wrapImagesInline is deprecated'); Deprecation::notice('4.0', 'wrapImagesInline is deprecated');
} }
/** /**
* @deprecated since version 3.2.0 * @deprecated since version 4.0
*/ */
public function wrapImagesInline_rewriter($url) { public function wrapImagesInline_rewriter($url) {
Deprecation::notice('3.2.0', 'wrapImagesInline_rewriter is deprecated'); Deprecation::notice('4.0', 'wrapImagesInline_rewriter is deprecated');
} }
} }

View File

@ -140,10 +140,10 @@ class File extends DataObject {
private static $app_categories = array( private static $app_categories = array(
'audio' => array( 'audio' => array(
"aif" ,"au" ,"mid" ,"midi" ,"mp3" ,"ra" ,"ram" ,"rm","mp3" ,"wav" ,"m4a" ,"snd" ,"aifc" ,"aiff" ,"wma", "aif" ,"au" ,"mid" ,"midi" ,"mp3" ,"ra" ,"ram" ,"rm","mp3" ,"wav" ,"m4a" ,"snd" ,"aifc" ,"aiff" ,"wma",
"apl", "avr" ,"cda" ,"mp4" ,"ogg" "apl", "avr" ,"cda" ,"ogg"
), ),
'mov' => array( 'mov' => array(
"mpeg" ,"mpg" ,"m1v" ,"mp2" ,"mpa" ,"mpe" ,"ifo" ,"vob","avi" ,"wmv" ,"asf" ,"m2v" ,"qt", "ogv", "webm" "mpeg" ,"mpg" ,"mp4" ,"m1v" ,"mp2" ,"mpa" ,"mpe" ,"ifo" ,"vob","avi" ,"wmv" ,"asf" ,"m2v" ,"qt", "ogv", "webm"
), ),
'zip' => array( 'zip' => array(
"arc" ,"rar" ,"tar" ,"gz" ,"tgz" ,"bz2" ,"dmg" ,"jar","ace" ,"arj" ,"bz" ,"cab" "arc" ,"rar" ,"tar" ,"gz" ,"tgz" ,"bz2" ,"dmg" ,"jar","ace" ,"arj" ,"bz" ,"cab"
@ -221,6 +221,15 @@ class File extends DataObject {
} }
} }
/**
* A file only exists if the file_exists() and is in the DB as a record
*
* @return bool
*/
public function exists() {
return parent::exists() && file_exists($this->getFullPath());
}
/** /**
* Find a File object by the given filename. * Find a File object by the given filename.
* *
@ -293,7 +302,7 @@ class File extends DataObject {
// ensure that the record is synced with the filesystem before deleting // ensure that the record is synced with the filesystem before deleting
$this->updateFilesystem(); $this->updateFilesystem();
if($this->Filename && $this->Name && file_exists($this->getFullPath()) && !is_dir($this->getFullPath())) { if($this->exists() && !is_dir($this->getFullPath())) {
unlink($this->getFullPath()); unlink($this->getFullPath());
} }
} }
@ -637,7 +646,7 @@ class File extends DataObject {
// If it's changed, check for duplicates // If it's changed, check for duplicates
if($oldName && $oldName != $name) { if($oldName && $oldName != $name) {
$base = pathinfo($name, PATHINFO_BASENAME); $base = pathinfo($name, PATHINFO_FILENAME);
$ext = self::get_file_extension($name); $ext = self::get_file_extension($name);
$suffix = 1; $suffix = 1;
@ -649,7 +658,7 @@ class File extends DataObject {
))->first() ))->first()
) { ) {
$suffix++; $suffix++;
$name = "$base-$suffix$ext"; $name = "$base-$suffix.$ext";
} }
} }
@ -836,7 +845,7 @@ class File extends DataObject {
'htm' => _t('File.HtmlType', 'HTML file') 'htm' => _t('File.HtmlType', 'HTML file')
); );
$ext = $this->getExtension(); $ext = strtolower($this->getExtension());
return isset($types[$ext]) ? $types[$ext] : 'unknown'; return isset($types[$ext]) ? $types[$ext] : 'unknown';
} }

View File

@ -25,11 +25,11 @@ class GDBackend extends Object implements Image_Backend {
/** /**
* Set the default image quality. * Set the default image quality.
* *
* @deprecated 3.2 Use the "GDBackend.default_quality" config setting instead * @deprecated 4.0 Use the "GDBackend.default_quality" config setting instead
* @param quality int A number from 0 to 100, 100 being the best quality. * @param quality int A number from 0 to 100, 100 being the best quality.
*/ */
public static function set_default_quality($quality) { public static function set_default_quality($quality) {
Deprecation::notice('3.2', 'Use the "GDBackend.default_quality" config setting instead'); Deprecation::notice('4.0', 'Use the "GDBackend.default_quality" config setting instead');
if(is_numeric($quality) && (int) $quality >= 0 && (int) $quality <= 100) { if(is_numeric($quality) && (int) $quality >= 0 && (int) $quality <= 100) {
config::inst()->update('GDBackend', 'default_quality', (int) $quality); config::inst()->update('GDBackend', 'default_quality', (int) $quality);
} }
@ -569,10 +569,10 @@ class GDBackend extends Object implements Image_Backend {
class GD extends GDBackend { class GD extends GDBackend {
/** /**
* @deprecated 3.2 Use the "GDBackend.default_quality" config setting instead * @deprecated 4.0 Use the "GDBackend.default_quality" config setting instead
*/ */
public static function set_default_quality($quality) { public static function set_default_quality($quality) {
Deprecation::notice('3.2', 'Use the "GDBackend.default_quality" config setting instead'); Deprecation::notice('4.0', 'Use the "GDBackend.default_quality" config setting instead');
GDBackend::set_default_quality($quality); GDBackend::set_default_quality($quality);
} }
} }

View File

@ -43,12 +43,12 @@ class ImagickBackend extends Imagick implements Image_Backend {
/** /**
* set_default_quality * set_default_quality
* *
* @deprecated 3.2 Use the "ImagickBackend.default_quality" config setting instead * @deprecated 4.0 Use the "ImagickBackend.default_quality" config setting instead
* @param int $quality * @param int $quality
* @return void * @return void
*/ */
public static function set_default_quality($quality) { public static function set_default_quality($quality) {
Deprecation::notice('3.2', 'Use the "ImagickBackend.default_quality" config setting instead'); Deprecation::notice('4.0', 'Use the "ImagickBackend.default_quality" config setting instead');
if(is_numeric($quality) && (int) $quality >= 0 && (int) $quality <= 100) { if(is_numeric($quality) && (int) $quality >= 0 && (int) $quality <= 100) {
Config::inst()->update('ImagickBackend', 'default_quality', (int) $quality); Config::inst()->update('ImagickBackend', 'default_quality', (int) $quality);
} }

View File

@ -96,8 +96,12 @@ class CheckboxSetField extends OptionsetField {
} }
} }
} elseif($values && is_string($values)) { } elseif($values && is_string($values)) {
$items = explode(',', $values); if(!empty($values)) {
$items = str_replace('{comma}', ',', $items); $items = explode(',', $values);
$items = str_replace('{comma}', ',', $items);
} else {
$items = array();
}
} }
} }
} else { } else {
@ -109,8 +113,12 @@ class CheckboxSetField extends OptionsetField {
$items = array(); $items = array();
} }
else { else {
$items = explode(',', $values); if(!empty($values)) {
$items = str_replace('{comma}', ',', $items); $items = explode(',', $values);
$items = str_replace('{comma}', ',', $items);
} else {
$items = array();
}
} }
} }
} }

View File

@ -120,7 +120,7 @@ class ConfirmedPasswordField extends FormField {
/** /**
* @param array $properties * @param array $properties
* *
* @return string * @return HTMLText
*/ */
public function Field($properties = array()) { public function Field($properties = array()) {
Requirements::javascript(FRAMEWORK_DIR . '/thirdparty/jquery/jquery.js'); Requirements::javascript(FRAMEWORK_DIR . '/thirdparty/jquery/jquery.js');

View File

@ -31,6 +31,8 @@ class DatalessField extends FormField {
/** /**
* Returns the field's representation in the form. * Returns the field's representation in the form.
* For dataless fields, this defaults to $Field. * For dataless fields, this defaults to $Field.
*
* @return HTMLText
*/ */
public function FieldHolder($properties = array()) { public function FieldHolder($properties = array()) {
return $this->Field($properties); return $this->Field($properties);

View File

@ -323,13 +323,13 @@ class DateField extends TextField {
} }
/** /**
* @deprecated 3.2 Use the "DateField.default_config" config setting instead * @deprecated 4.0 Use the "DateField.default_config" config setting instead
* @param String $k * @param String $k
* @param mixed $v * @param mixed $v
* @return boolean * @return boolean
*/ */
public static function set_default_config($k, $v) { public static function set_default_config($k, $v) {
Deprecation::notice('3.2', 'Use the "DateField.default_config" config setting instead'); Deprecation::notice('4.0', 'Use the "DateField.default_config" config setting instead');
return Config::inst()->update('DateField', 'default_config', array($k => $v)); return Config::inst()->update('DateField', 'default_config', array($k => $v));
} }

View File

@ -63,7 +63,7 @@ class DatetimeField extends FormField {
->addExtraClass('fieldgroup-field'); ->addExtraClass('fieldgroup-field');
$this->timeField = TimeField::create($name . '[time]', false) $this->timeField = TimeField::create($name . '[time]', false)
->addExtraClass('fieldgroup-field'); ->addExtraClass('fieldgroup-field');
$this->timezoneField = new HiddenField($this->getName() . '[timezone]'); $this->timezoneField = new HiddenField($name . '[timezone]');
parent::__construct($name, $title, $value); parent::__construct($name, $title, $value);
} }
@ -88,7 +88,10 @@ class DatetimeField extends FormField {
return $this; return $this;
} }
/**
* @param array $properties
* @return HTMLText
*/
public function FieldHolder($properties = array()) { public function FieldHolder($properties = array()) {
$config = array( $config = array(
'datetimeorder' => $this->getConfig('datetimeorder'), 'datetimeorder' => $this->getConfig('datetimeorder'),
@ -100,14 +103,19 @@ class DatetimeField extends FormField {
return parent::FieldHolder($properties); return parent::FieldHolder($properties);
} }
/**
* @param array $properties
* @return HTMLText
*/
public function Field($properties = array()) { public function Field($properties = array()) {
Requirements::css(FRAMEWORK_DIR . '/css/DatetimeField.css'); Requirements::css(FRAMEWORK_DIR . '/css/DatetimeField.css');
$tzField = ($this->getConfig('usertimezone')) ? $this->timezoneField->FieldHolder() : ''; $tzField = ($this->getConfig('usertimezone')) ? $this->timezoneField->FieldHolder() : '';
return $this->dateField->FieldHolder() . return DBField::create_field('HTMLText', $this->dateField->FieldHolder() .
$this->timeField->FieldHolder() . $this->timeField->FieldHolder() .
$tzField . $tzField .
'<div class="clear"><!-- --></div>'; '<div class="clear"><!-- --></div>'
);
} }
/** /**

View File

@ -128,6 +128,10 @@ class DropdownField extends FormField {
parent::__construct($name, ($title===null) ? $name : $title, $value, $form); parent::__construct($name, ($title===null) ? $name : $title, $value, $form);
} }
/**
* @param array $properties
* @return HTMLText
*/
public function Field($properties = array()) { public function Field($properties = array()) {
$source = $this->getSource(); $source = $this->getSource();
$options = array(); $options = array();

View File

@ -83,6 +83,10 @@ class FileField extends FormField {
parent::__construct($name, $title, $value); parent::__construct($name, $title, $value);
} }
/**
* @param array $properties
* @return HTMLText
*/
public function Field($properties = array()) { public function Field($properties = array()) {
$properties = array_merge($properties, array( $properties = array_merge($properties, array(
'MaxFileSize' => $this->getValidator()->getAllowedMaxFileSize() 'MaxFileSize' => $this->getValidator()->getAllowedMaxFileSize()

View File

@ -74,6 +74,10 @@ class FormAction extends FormField {
return $this; return $this;
} }
/**
* @param array $properties
* @return HTMLText
*/
public function Field($properties = array()) { public function Field($properties = array()) {
$properties = array_merge( $properties = array_merge(
$properties, $properties,
@ -87,6 +91,10 @@ class FormAction extends FormField {
return parent::Field($properties); return parent::Field($properties);
} }
/**
* @param array $properties
* @return HTMLText
*/
public function FieldHolder($properties = array()) { public function FieldHolder($properties = array()) {
return $this->Field($properties); return $this->Field($properties);
} }
@ -100,7 +108,7 @@ class FormAction extends FormField {
// Remove this method override in 4.0 // Remove this method override in 4.0
$decoded = Convert::xml2raw($title); $decoded = Convert::xml2raw($title);
if($decoded !== $title) { if($title && $decoded !== $title) {
Deprecation::notice( Deprecation::notice(
'4.0', '4.0',
'The FormAction title field should not be html encoded. Use buttonContent to set custom html instead' 'The FormAction title field should not be html encoded. Use buttonContent to set custom html instead'

View File

@ -3,20 +3,22 @@
/** /**
* Represents a field in a form. * Represents a field in a form.
* *
* A FieldList contains a number of FormField objects which make up the whole * A FieldList contains a number of FormField objects which make up the whole of a form.
* of a form. In addition to single fields, FormField objects can be
* "composite", for example, the {@link TabSet} field. Composite fields let us
* define complex forms without having to resort to custom HTML.
* *
* <b>Subclassing</b> * In addition to single fields, FormField objects can be "composite", for example, the
* {@link TabSet} field. Composite fields let us define complex forms without having to resort to
* custom HTML.
* *
* Define a {@link dataValue()} method that returns a value suitable for * To subclass:
* inserting into a single database field. For example, you might tidy up the *
* format of a date or currency field. Define {@link saveInto()} to totally * Define a {@link dataValue()} method that returns a value suitable for inserting into a single
* customise saving. For example, data might be saved to the filesystem instead * database field.
* of the data record, or saved to a component of the data record instead of *
* the data record itself. Define {@link validate()} to validate the form field * For example, you might tidy up the format of a date or currency field. Define {@link saveInto()}
* and ensure that the content provided is valid. * to totally customise saving.
*
* For example, data might be saved to the filesystem instead of the data record, or saved to a
* component of the data record instead of the data record itself.
* *
* @package forms * @package forms
* @subpackage core * @subpackage core
@ -28,16 +30,49 @@ class FormField extends RequestHandler {
*/ */
protected $form; protected $form;
protected $name, $title, $value ,$message, $messageType, $extraClass; /**
* @var string
*/
protected $name;
/** /**
* @var $description string Adds a "title"-attribute to the markup. * @var null|string
*/
protected $title;
/**
* @var mixed
*/
protected $value;
/**
* @var string
*/
protected $message;
/**
* @var string
*/
protected $messageType;
/**
* @var string
*/
protected $extraClass;
/**
* Adds a title attribute to the markup.
*
* @var string
*
* @todo Implement in all subclasses * @todo Implement in all subclasses
*/ */
protected $description; protected $description;
/** /**
* @var $extraClasses array Extra CSS-classes for the formfield-container * Extra CSS classes for the FormField container.
*
* @var array
*/ */
protected $extraClasses; protected $extraClasses;
@ -47,85 +82,111 @@ class FormField extends RequestHandler {
*/ */
private static $default_classes = array(); private static $default_classes = array();
/**
* @var bool
*/
public $dontEscape; public $dontEscape;
/** /**
* @var $rightTitle string Used in SmallFieldHolder to force a right-aligned label, or in FieldHolder * Right-aligned, contextual label for the field.
* to create contextual label. *
* @var string
*/ */
protected $rightTitle; protected $rightTitle;
/** /**
* @var $leftTitle string Used in SmallFieldHolder() to force a left-aligned label with correct spacing. * Left-aligned, contextual label for the field.
* Please use $title for FormFields rendered with FieldHolder(). *
* @var string
*/ */
protected $leftTitle; protected $leftTitle;
/** /**
* Stores a reference to the FieldList that contains this object. * Stores a reference to the FieldList that contains this object.
*
* @var FieldList * @var FieldList
*/ */
protected $containerFieldList; protected $containerFieldList;
/** /**
* @var boolean * @var bool
*/ */
protected $readonly = false; protected $readonly = false;
/** /**
* @var boolean * @var bool
*/ */
protected $disabled = false; protected $disabled = false;
/** /**
* @var string custom validation message for the Field * Custom validation message for the field.
*/
protected $customValidationMessage = "";
/**
* Name of the template used to render this form field. If not set, then
* will look up the class ancestry for the first matching template where
* the template name equals the class name.
*
* To explicitly use a custom template or one named other than the form
* field see {@link setTemplate()}, {@link setFieldHolderTemplate()}
* *
* @var string * @var string
*/ */
protected protected $customValidationMessage = '';
$template,
$fieldHolderTemplate,
$smallFieldHolderTemplate;
/** /**
* @var array All attributes on the form field (not the field holder). * Name of the template used to render this form field. If not set, then will look up the class
* Partially determined based on other instance properties, please use {@link getAttributes()}. * ancestry for the first matching template where the template name equals the class name.
*
* To explicitly use a custom template or one named other than the form field see
* {@link setTemplate()}.
*
* @var string
*/
protected $template;
/**
* Name of the template used to render this form field. If not set, then will look up the class
* ancestry for the first matching template where the template name equals the class name.
*
* To explicitly use a custom template or one named other than the form field see
* {@link setFieldHolderTemplate()}.
*
* @var string
*/
protected $fieldHolderTemplate;
/**
* @var string
*/
protected $smallFieldHolderTemplate;
/**
* All attributes on the form field (not the field holder).
*
* Partially determined based on other instance properties.
*
* @see getAttributes()
*
* @var array
*/ */
protected $attributes = array(); protected $attributes = array();
/** /**
* Takes a fieldname and converts camelcase to spaced * Takes a field name and converts camelcase to spaced words. Also resolves combined field
* words. Also resolves combined fieldnames with dot syntax * names with dot syntax to spaced words.
* to spaced words.
* *
* Examples: * Examples:
*
* - 'TotalAmount' will return 'Total Amount' * - 'TotalAmount' will return 'Total Amount'
* - 'Organisation.ZipCode' will return 'Organisation Zip Code' * - 'Organisation.ZipCode' will return 'Organisation Zip Code'
* *
* @param string $fieldName * @param string $fieldName
*
* @return string * @return string
*/ */
public static function name_to_label($fieldName) { public static function name_to_label($fieldName) {
if(strpos($fieldName, '.') !== false) { if(strpos($fieldName, '.') !== false) {
$parts = explode('.', $fieldName); $parts = explode('.', $fieldName);
$label = $parts[count($parts)-2] . ' ' . $parts[count($parts)-1];
$label = $parts[count($parts) - 2] . ' ' . $parts[count($parts) - 1];
} else { } else {
$label = $fieldName; $label = $fieldName;
} }
$label = preg_replace("/([a-z]+)([A-Z])/","$1 $2", $label); return preg_replace('/([a-z]+)([A-Z])/', '$1 $2', $label);
return $label;
} }
/** /**
@ -133,41 +194,51 @@ class FormField extends RequestHandler {
* *
* @param string $tag * @param string $tag
* @param array $attributes * @param array $attributes
* @param mixed $content * @param null|string $content
* *
* @return string * @return string
*/ */
public static function create_tag($tag, $attributes, $content = null) { public static function create_tag($tag, $attributes, $content = null) {
$preparedAttributes = ''; $preparedAttributes = '';
foreach($attributes as $k => $v) { foreach($attributes as $attributeKey => $attributeValue) {
// Note: as indicated by the $k == value item here; the decisions over what to include in the attributes if(!empty($attributeValue) || $attributeValue === '0' || ($attributeKey == 'value' && $attributeValue !== null)) {
// can sometimes get finicky $preparedAttributes .= sprintf(
if(!empty($v) || $v === '0' || ($k == 'value' && $v !== null) ) { ' %s="%s"', $attributeKey, Convert::raw2att($attributeValue)
$preparedAttributes .= " $k=\"" . Convert::raw2att($v) . "\""; );
} }
} }
if($content || $tag != 'input') { if($content || $tag != 'input') {
return "<$tag$preparedAttributes>$content</$tag>"; return sprintf(
} '<%s%s>%s</%s>', $tag, $preparedAttributes, $content, $tag
else { );
return "<$tag$preparedAttributes />";
} }
return sprintf(
'<%s%s />', $tag, $preparedAttributes
);
} }
/** /**
* Creates a new field. * Creates a new field.
* *
* @param string $name The internal field name, passed to forms. * @param string $name The internal field name, passed to forms.
* @param string $title The human-readable field label. * @param null|string $title The human-readable field label.
* @param mixed $value The value of the field. * @param mixed $value The value of the field.
*/ */
public function __construct($name, $title = null, $value = null) { public function __construct($name, $title = null, $value = null) {
$this->name = $name; $this->name = $name;
$this->title = ($title === null) ? self::name_to_label($name) : $title;
if($value !== NULL) $this->setValue($value); if($title === null) {
$this->title = self::name_to_label($name);
} else {
$this->title = $title;
}
if($value !== null) {
$this->setValue($value);
}
parent::__construct(); parent::__construct();
@ -175,7 +246,7 @@ class FormField extends RequestHandler {
} }
/** /**
* set up the default classes for the form. This is done on construct so that the default classes can be removed * Set up the default classes for the form. This is done on construct so that the default classes can be removed
* after instantiation * after instantiation
*/ */
protected function setupDefaultClasses() { protected function setupDefaultClasses() {
@ -199,10 +270,10 @@ class FormField extends RequestHandler {
} }
/** /**
* Returns the HTML ID of the field - used in the template by label tags. * Returns the HTML ID of the field.
* *
* The ID is generated as FormName_FieldName. All Field functions should ensure * The ID is generated as FormName_FieldName. All Field functions should ensure that this ID is
* that this ID is included in the field. * included in the field.
* *
* @return string * @return string
*/ */
@ -237,7 +308,7 @@ class FormField extends RequestHandler {
} }
/** /**
* Returns the raw field name. * Returns the field name.
* *
* @return string * @return string
*/ */
@ -257,10 +328,11 @@ class FormField extends RequestHandler {
} }
/** /**
* Returns the field message type, used by form validation. * Returns the field message type.
* *
* Arbitrary value which is mostly used for CSS classes in the rendered HTML, * Arbitrary value which is mostly used for CSS classes in the rendered HTML, e.g "required".
* e.g. "required". Use {@link setError()} to set this property. *
* Use {@link setError()} to set this property.
* *
* @return string * @return string
*/ */
@ -269,7 +341,9 @@ class FormField extends RequestHandler {
} }
/** /**
* Returns the field value - used by templates. * Returns the field value.
*
* @return mixed
*/ */
public function Value() { public function Value() {
return $this->value; return $this->value;
@ -289,8 +363,7 @@ class FormField extends RequestHandler {
} }
/** /**
* Returns the field value suitable for insertion into the * Returns the field value suitable for insertion into the data object.
* {@link DataObject}.
* *
* @return mixed * @return mixed
*/ */
@ -308,12 +381,13 @@ class FormField extends RequestHandler {
} }
/** /**
* @param string $val * @param string $title
* *
* @return FormField * @return $this
*/ */
public function setTitle($val) { public function setTitle($title) {
$this->title = $val; $this->title = $title;
return $this; return $this;
} }
@ -328,56 +402,78 @@ class FormField extends RequestHandler {
} }
/** /**
* Sets the contextual label. * @param string $rightTitle
* *
* @param $val string Text to set on the label. * @return $this
*/ */
public function setRightTitle($val) { public function setRightTitle($rightTitle) {
$this->rightTitle = $val; $this->rightTitle = $rightTitle;
return $this;
}
public function LeftTitle() {
return $this->leftTitle;
}
public function setLeftTitle($val) {
$this->leftTitle = $val;
return $this; return $this;
} }
/** /**
* Compiles all CSS-classes. Optionally includes a "nolabel"-class * @return string
* if no title was set on the formfield. */
* Uses {@link Message()} and {@link MessageType()} to add validatoin public function LeftTitle() {
* error classes which can be used to style the contained tags. return $this->leftTitle;
}
/**
* @param string $leftTitle
* *
* @return string CSS-classnames * @return $this
*/
public function setLeftTitle($leftTitle) {
$this->leftTitle = $leftTitle;
return $this;
}
/**
* Compiles all CSS-classes. Optionally includes a "nolabel" class if no title was set on the
* FormField.
*
* Uses {@link Message()} and {@link MessageType()} to add validation error classes which can
* be used to style the contained tags.
*
* @return string
*/ */
public function extraClass() { public function extraClass() {
$classes = array(); $classes = array();
$classes[] = $this->Type(); $classes[] = $this->Type();
if($this->extraClasses) $classes = array_merge($classes, array_values($this->extraClasses)); if($this->extraClasses) {
$classes = array_merge(
$classes,
array_values($this->extraClasses)
);
}
// Allow customization of label and field tag positioning if(!$this->Title()) {
if(!$this->Title()) $classes[] = "nolabel"; $classes[] = 'nolabel';
}
// Allow custom styling of any element in the container based // Allow custom styling of any element in the container based on validation errors,
// on validation errors, e.g. red borders on input tags. // e.g. red borders on input tags.
// CSS-Class needs to be different from the one rendered //
// through {@link FieldHolder()} // CSS class needs to be different from the one rendered through {@link FieldHolder()}.
if($this->Message()) $classes[] .= "holder-" . $this->MessageType(); if($this->Message()) {
$classes[] .= 'holder-' . $this->MessageType();
}
return implode(' ', $classes); return implode(' ', $classes);
} }
/** /**
* Add one or more CSS-classes to the formfield-container. Multiple class * Add one or more CSS-classes to the FormField container.
* names should be space delimited. *
* Multiple class names should be space delimited.
* *
* @param string $class * @param string $class
*
* @return $this
*/ */
public function addExtraClass($class) { public function addExtraClass($class) {
$classes = preg_split('/\s+/', $class); $classes = preg_split('/\s+/', $class);
@ -390,9 +486,11 @@ class FormField extends RequestHandler {
} }
/** /**
* Remove one or more CSS-classes from the formfield-container. * Remove one or more CSS-classes from the FormField container.
* *
* @param string $class * @param string $class
*
* @return $this
*/ */
public function removeExtraClass($class) { public function removeExtraClass($class) {
$classes = preg_split('/\s+/', $class); $classes = preg_split('/\s+/', $class);
@ -407,34 +505,43 @@ class FormField extends RequestHandler {
/** /**
* Set an HTML attribute on the field element, mostly an <input> tag. * Set an HTML attribute on the field element, mostly an <input> tag.
* *
* Some attributes are best set through more specialized methods, to avoid interfering with built-in behaviour: * Some attributes are best set through more specialized methods, to avoid interfering with
* built-in behaviour:
*
* - 'class': {@link addExtraClass()} * - 'class': {@link addExtraClass()}
* - 'title': {@link setDescription()} * - 'title': {@link setDescription()}
* - 'value': {@link setValue} * - 'value': {@link setValue}
* - 'name': {@link setName} * - 'name': {@link setName}
* *
* CAUTION Doesn't work on most fields which are composed of more than one HTML form field: * Caution: this doesn't work on most fields which are composed of more than one HTML form
* AjaxUniqueTextField, CheckboxSetField, CompositeField, ConfirmedPasswordField, * field.
* CountryDropdownField, CreditCardField, CurrencyField, DateField, DatetimeField, FieldGroup, GridField,
* HtmlEditorField, ImageField, ImageFormAction, InlineFormAction, ListBoxField, etc.
* *
* @param string * @param string $name
* @param string * @param string $value
*
* @return $this
*/ */
public function setAttribute($name, $value) { public function setAttribute($name, $value) {
$this->attributes[$name] = $value; $this->attributes[$name] = $value;
return $this; return $this;
} }
/** /**
* Get an HTML attribute defined by the field, or added through {@link setAttribute()}. * Get an HTML attribute defined by the field, or added through {@link setAttribute()}.
* Caution: Doesn't work on all fields, see {@link setAttribute()}.
* *
* @return string * Caution: this doesn't work on all fields, see {@link setAttribute()}.
*
* @return null|string
*/ */
public function getAttribute($name) { public function getAttribute($name) {
$attrs = $this->getAttributes(); $attributes = $this->getAttributes();
if(isset($attrs[$name])) return $attrs[$name];
if(isset($attributes[$name])) {
return $attributes[$name];
}
return null;
} }
/** /**
@ -445,7 +552,7 @@ class FormField extends RequestHandler {
* @return array * @return array
*/ */
public function getAttributes() { public function getAttributes() {
$attrs = array( $attributes = array(
'type' => 'text', 'type' => 'text',
'name' => $this->getName(), 'name' => $this->getName(),
'value' => $this->Value(), 'value' => $this->Value(),
@ -455,54 +562,67 @@ class FormField extends RequestHandler {
'readonly' => $this->isReadonly() 'readonly' => $this->isReadonly()
); );
if ($this->Required()) { if($this->Required()) {
$attrs['required'] = 'required'; $attributes['required'] = 'required';
$attrs['aria-required'] = 'true'; $attributes['aria-required'] = 'true';
} }
$attrs = array_merge($attrs, $this->attributes); $attributes = array_merge($attributes, $this->attributes);
$this->extend('updateAttributes', $attrs); $this->extend('updateAttributes', $attributes);
return $attrs; return $attributes;
} }
/** /**
* @param Array Custom attributes to process. Falls back to {@link getAttributes()}. * Custom attributes to process. Falls back to {@link getAttributes()}.
* If at least one argument is passed as a string, all arguments act as excludes by name.
* *
* @return string HTML attributes, ready for insertion into an HTML tag * If at least one argument is passed as a string, all arguments act as excludes, by name.
*
* @param array $attributes
*
* @return string
*/ */
public function getAttributesHTML($attrs = null) { public function getAttributesHTML($attributes = null) {
$exclude = (is_string($attrs)) ? func_get_args() : null; $exclude = null;
if(!$attrs || is_string($attrs)) { if(is_string($attributes)) {
$attrs = $this->getAttributes(); $exclude = func_get_args();
} }
// Remove empty if(!$attributes || is_string($attributes)) {
$attrs = array_filter((array)$attrs, function($v) { $attributes = $this->getAttributes();
}
$attributes = (array) $attributes;
$attributes = array_filter($attributes, function ($v) {
return ($v || $v === 0 || $v === '0'); return ($v || $v === 0 || $v === '0');
}); });
// Remove excluded
if($exclude) { if($exclude) {
$attrs = array_diff_key($attrs, array_flip($exclude)); $attributes = array_diff_key(
$attributes,
array_flip($exclude)
);
} }
// Create markup // Create markup
$parts = array(); $parts = array();
foreach($attrs as $name => $value) { foreach($attributes as $name => $value) {
$parts[] = ($value === true) ? "{$name}=\"{$name}\"" : "{$name}=\"" . Convert::raw2att($value) . "\""; if($value === true) {
$parts[] = sprintf('%s="%s"', $name, $name);
} else {
$parts[] = sprintf('%s="%s"', $name, Convert::raw2att($value));
}
} }
return implode(' ', $parts); return implode(' ', $parts);
} }
/** /**
* Returns a version of a title suitable for insertion into an HTML * Returns a version of a title suitable for insertion into an HTML attribute.
* attribute.
* *
* @return string * @return string
*/ */
@ -511,8 +631,7 @@ class FormField extends RequestHandler {
} }
/** /**
* Returns a version of a title suitable for insertion into an HTML * Returns a version of a title suitable for insertion into an HTML attribute.
* attribute.
* *
* @return string * @return string
*/ */
@ -524,8 +643,9 @@ class FormField extends RequestHandler {
* Set the field value. * Set the field value.
* *
* @param mixed $value * @param mixed $value
* @param mixed $data Optional data source passed in by {@see Form::loadDataFrom} * @param null|array|DataObject $data {@see Form::loadDataFrom}
* @return FormField Self reference *
* @return $this
*/ */
public function setValue($value) { public function setValue($value) {
$this->value = $value; $this->value = $value;
@ -534,11 +654,11 @@ class FormField extends RequestHandler {
} }
/** /**
* Set the field name * Set the field name.
* *
* @param string $name * @param string $name
* *
* @return FormField * @return $this
*/ */
public function setName($name) { public function setName($name) {
$this->name = $name; $this->name = $name;
@ -549,12 +669,11 @@ class FormField extends RequestHandler {
/** /**
* Set the container form. * Set the container form.
* *
* This is called whenever you create a new form and put fields inside it, * This is called automatically when fields are added to forms.
* so that you don't have to worry about linking the two.
* *
* @param Form * @param Form $form
* *
* @return FormField * @return $this
*/ */
public function setForm($form) { public function setForm($form) {
$this->form = $form; $this->form = $form;
@ -572,8 +691,7 @@ class FormField extends RequestHandler {
} }
/** /**
* Return TRUE if security token protection is enabled on the parent * Return true if security token protection is enabled on the parent {@link Form}.
* {@link Form}.
* *
* @return bool * @return bool
*/ */
@ -588,10 +706,14 @@ class FormField extends RequestHandler {
} }
/** /**
* @param string $message Message to show to the user. Allows HTML content, * Sets the error message to be displayed on the form field.
* which means you need to use Convert::raw2xml() for any user supplied data. *
* Allows HTML content, so remember to use Convert::raw2xml().
*
* @param string $message
* @param string $messageType * @param string $messageType
* @return FormField *
* @return $this
*/ */
public function setError($message, $messageType) { public function setError($message, $messageType) {
$this->message = $message; $this->message = $message;
@ -601,24 +723,23 @@ class FormField extends RequestHandler {
} }
/** /**
* Set the custom error message to show instead of the default * Set the custom error message to show instead of the default format.
* format of Please Fill In XXX. Different from setError() as
* that appends it to the standard error messaging.
* *
* @param string $msg Message for the error * Different from setError() as that appends it to the standard error messaging.
* *
* @return FormField * @param string $customValidationMessage
*
* @return $this
*/ */
public function setCustomValidationMessage($msg) { public function setCustomValidationMessage($customValidationMessage) {
$this->customValidationMessage = $msg; $this->customValidationMessage = $customValidationMessage;
return $this; return $this;
} }
/** /**
* Get the custom error message for this form field. If a custom * Get the custom error message for this form field. If a custom message has not been defined
* message has not been defined then just return blank. The default * then just return blank. The default error is defined on {@link Validator}.
* error is defined on {@link Validator}.
* *
* @return string * @return string
*/ */
@ -629,12 +750,12 @@ class FormField extends RequestHandler {
/** /**
* Set name of template (without path or extension). * Set name of template (without path or extension).
* *
* Caution: Not consistently implemented in all subclasses, please check * Caution: Not consistently implemented in all subclasses, please check the {@link Field()}
* the {@link Field()} method on the subclass for support. * method on the subclass for support.
* *
* @param string $template * @param string $template
* *
* @return FormField * @return $this
*/ */
public function setTemplate($template) { public function setTemplate($template) {
$this->template = $template; $this->template = $template;
@ -657,18 +778,18 @@ class FormField extends RequestHandler {
} }
/** /**
* Set name of template (without path or extension) for the holder, * Set name of template (without path or extension) for the holder, which in turn is
* which in turn is responsible for rendering {@link Field()}. * responsible for rendering {@link Field()}.
* *
* Caution: Not consistently implemented in all subclasses, * Caution: Not consistently implemented in all subclasses, please check the {@link Field()}
* please check the {@link Field()} method on the subclass for support. * method on the subclass for support.
* *
* @param string $template * @param string $fieldHolderTemplate
* *
* @return FormField * @return $this
*/ */
public function setFieldHolderTemplate($template) { public function setFieldHolderTemplate($fieldHolderTemplate) {
$this->fieldHolderTemplate = $template; $this->fieldHolderTemplate = $fieldHolderTemplate;
return $this; return $this;
} }
@ -681,50 +802,67 @@ class FormField extends RequestHandler {
} }
/** /**
* Set name of template (without path or extension) for the small holder, * Set name of template (without path or extension) for the small holder, which in turn is
* which in turn is responsible for rendering {@link Field()}. * responsible for rendering {@link Field()}.
* *
* Caution: Not consistently implemented in all subclasses, * Caution: Not consistently implemented in all subclasses, please check the {@link Field()}
* please check the {@link Field()} method on the subclass for support. * method on the subclass for support.
* *
* @param string * @param string $smallFieldHolderTemplate
*
* @return $this
*/ */
public function setSmallFieldHolderTemplate($template) { public function setSmallFieldHolderTemplate($smallFieldHolderTemplate) {
$this->smallFieldHolderTemplate = $template; $this->smallFieldHolderTemplate = $smallFieldHolderTemplate;
return $this; return $this;
} }
/** /**
* Returns the form field - used by templates. * Returns the form field.
*
* Although FieldHolder is generally what is inserted into templates, all of the field holder * Although FieldHolder is generally what is inserted into templates, all of the field holder
* templates make use of $Field. It's expected that FieldHolder will give you the "complete" * templates make use of $Field. It's expected that FieldHolder will give you the "complete"
* representation of the field on the form, whereas Field will give you the core editing widget, * representation of the field on the form, whereas Field will give you the core editing widget,
* such as an input tag. * such as an input tag.
* *
* @param array $properties key value pairs of template variables * @param array $properties
*
* @return string * @return string
*/ */
public function Field($properties = array()) { public function Field($properties = array()) {
$obj = ($properties) ? $this->customise($properties) : $this; $context = $this;
if(count($properties)) {
$context = $context->customise($properties);
}
$this->extend('onBeforeRender', $this); $this->extend('onBeforeRender', $this);
return $obj->renderWith($this->getTemplates());
return $context->renderWith($this->getTemplates());
} }
/** /**
* Returns a "field holder" for this field - used by templates. * Returns a "field holder" for this field.
* *
* Forms are constructed by concatenating a number of these field holders. * Forms are constructed by concatenating a number of these field holders.
*
* The default field holder is a label and a form field inside a div. * The default field holder is a label and a form field inside a div.
*
* @see FieldHolder.ss * @see FieldHolder.ss
* *
* @param array $properties key value pairs of template variables * @param array $properties
*
* @return string * @return string
*/ */
public function FieldHolder($properties = array()) { public function FieldHolder($properties = array()) {
$obj = ($properties) ? $this->customise($properties) : $this; $context = $this;
return $obj->renderWith($this->getFieldHolderTemplates()); if(count($properties)) {
$context = $this->customise($properties);
}
return $context->renderWith($this->getFieldHolderTemplates());
} }
/** /**
@ -735,13 +873,17 @@ class FormField extends RequestHandler {
* @return string * @return string
*/ */
public function SmallFieldHolder($properties = array()) { public function SmallFieldHolder($properties = array()) {
$obj = ($properties) ? $this->customise($properties) : $this; $context = $this;
return $obj->renderWith($this->getSmallFieldHolderTemplates()); if(count($properties)) {
$context = $this->customise($properties);
}
return $context->renderWith($this->getSmallFieldHolderTemplates());
} }
/** /**
* Returns an array of templates to use for rendering {@link FieldH} * Returns an array of templates to use for rendering {@link FieldHolder}.
* *
* @return array * @return array
*/ */
@ -750,7 +892,7 @@ class FormField extends RequestHandler {
} }
/** /**
* Returns an array of templates to use for rendering {@link FieldHolder} * Returns an array of templates to use for rendering {@link FieldHolder}.
* *
* @return array * @return array
*/ */
@ -762,7 +904,7 @@ class FormField extends RequestHandler {
} }
/** /**
* Returns an array of templates to use for rendering {@link SmallFieldHolder} * Returns an array of templates to use for rendering {@link SmallFieldHolder}.
* *
* @return array * @return array
*/ */
@ -775,31 +917,37 @@ class FormField extends RequestHandler {
/** /**
* Generate an array of classname strings to use for rendering this form * Generate an array of class name strings to use for rendering this form field into HTML.
* field into HTML
* *
* @param string $custom custom template (if set) * @param string $customTemplate
* @param string $suffix template suffix * @param string $customTemplateSuffix
* *
* @return array $stack a stack of * @return array
*/ */
private function _templates($custom = null, $suffix = null) { private function _templates($customTemplate = null, $customTemplateSuffix = null) {
$matches = array(); $matches = array();
foreach(array_reverse(ClassInfo::ancestry($this)) as $className) { foreach(array_reverse(ClassInfo::ancestry($this)) as $className) {
$matches[] = $className . $suffix; $matches[] = $className . $customTemplateSuffix;
if($className == "FormField") break; if($className == "FormField") {
break;
}
} }
if($custom) array_unshift($matches, $custom); if($customTemplate) {
array_unshift($matches, $customTemplate);
}
return $matches; return $matches;
} }
/** /**
* Returns true if this field is a composite field. * Returns true if this field is a composite field.
*
* To create composite field types, you should subclass {@link CompositeField}. * To create composite field types, you should subclass {@link CompositeField}.
*
* @return bool
*/ */
public function isComposite() { public function isComposite() {
return false; return false;
@ -807,99 +955,138 @@ class FormField extends RequestHandler {
/** /**
* Returns true if this field has its own data. * Returns true if this field has its own data.
* Some fields, such as titles and composite fields, don't actually have any data. It doesn't *
* make sense for data-focused methods to look at them. By overloading hasData() to return false, * Some fields, such as titles and composite fields, don't actually have any data. It doesn't
* you can prevent any data-focused methods from looking at it. * make sense for data-focused methods to look at them. By overloading hasData() to return
* false, you can prevent any data-focused methods from looking at it.
* *
* @see FieldList::collateDataFields() * @see FieldList::collateDataFields()
*
* @return bool
*/ */
public function hasData() { public function hasData() {
return true; return true;
} }
/** /**
* @return boolean * @return bool
*/ */
public function isReadonly() { public function isReadonly() {
return $this->readonly; return $this->readonly;
} }
/** /**
* Sets readonly-flag on form-field. Please use performReadonlyTransformation() * Sets a read-only flag on this FormField.
* to actually transform this instance. *
* @param $bool boolean Setting "false" has no effect on the field-state. * Use performReadonlyTransformation() to transform this instance.
*
* Setting this to false has no effect on the field.
*
* @param bool $readonly
*
* @return $this
*/ */
public function setReadonly($bool) { public function setReadonly($readonly) {
$this->readonly = $bool; $this->readonly = $readonly;
return $this; return $this;
} }
/** /**
* @return boolean * @return bool
*/ */
public function isDisabled() { public function isDisabled() {
return $this->disabled; return $this->disabled;
} }
/** /**
* Sets disabed-flag on form-field. Please use performDisabledTransformation() * Sets a disabled flag on this FormField.
* to actually transform this instance. *
* @param $bool boolean Setting "false" has no effect on the field-state. * Use performDisabledTransformation() to transform this instance.
*
* Setting this to false has no effect on the field.
*
* @param bool $disabled
*
* @return $this
*/ */
public function setDisabled($bool) { public function setDisabled($disabled) {
$this->disabled = $bool; $this->disabled = $disabled;
return $this; return $this;
} }
/** /**
* Returns a readonly version of this field * Returns a read-only version of this field.
*
* @return FormField
*/ */
public function performReadonlyTransformation() { public function performReadonlyTransformation() {
$readonlyClassName = $this->class . '_Disabled'; $readonlyClassName = $this->class . '_Readonly';
if(ClassInfo::exists($readonlyClassName)) { if(ClassInfo::exists($readonlyClassName)) {
$clone = $this->castedCopy($readonlyClassName); $clone = $this->castedCopy($readonlyClassName);
} else { } else {
$clone = $this->castedCopy('ReadonlyField'); $clone = $this->castedCopy('ReadonlyField');
$clone->setReadonly(true); }
}
$clone->setReadonly(true);
return $clone; return $clone;
} }
/** /**
* Return a disabled version of this field. * Return a disabled version of this field.
* Tries to find a class of the class name of this field suffixed with "_Disabled", *
* failing that, finds a method {@link setDisabled()}. * Tries to find a class of the class name of this field suffixed with "_Disabled", failing
* that, finds a method {@link setDisabled()}.
* *
* @return FormField * @return FormField
*/ */
public function performDisabledTransformation() { public function performDisabledTransformation() {
$disabledClassName = $this->class . '_Disabled'; $disabledClassName = $this->class . '_Disabled';
if(ClassInfo::exists($disabledClassName)) { if(ClassInfo::exists($disabledClassName)) {
$clone = $this->castedCopy($disabledClassName); $clone = $this->castedCopy($disabledClassName);
} else { } else {
$clone = clone $this; $clone = clone $this;
$clone->setDisabled(true);
} }
return $clone; $clone->setDisabled(true);
}
public function transform(FormTransformation $trans) { return $clone;
return $trans->transform($this);
} }
public function hasClass($class){ /**
$patten = '/'.strtolower($class).'/i'; * @param FormTransformation $transformation
$subject = strtolower($this->class." ".$this->extraClass()); *
* @return mixed
*/
public function transform(FormTransformation $transformation) {
return $transformation->transform($this);
}
/**
* @param string $class
*
* @return int
*/
public function hasClass($class) {
$patten = '/' . strtolower($class) . '/i';
$subject = strtolower($this->class . ' ' . $this->extraClass());
return preg_match($patten, $subject); return preg_match($patten, $subject);
} }
/** /**
* Returns the field type - used by templates. * Returns the field type.
*
* The field type is the class name with the word Field dropped off the end, all lowercase. * The field type is the class name with the word Field dropped off the end, all lowercase.
* It's handy for assigning HTML classes. Doesn't signify the <input type> attribute, *
* see {link getAttributes()}. * It's handy for assigning HTML classes. Doesn't signify the <input type> attribute.
*
* @see {link getAttributes()}.
* *
* @return string * @return string
*/ */
@ -908,19 +1095,29 @@ class FormField extends RequestHandler {
} }
/** /**
* @deprecated 3.2 Use FormField::create_tag() * @deprecated 4.0 Use FormField::create_tag()
*
* @param string $tag
* @param array $attributes
* @param null|string $content
*
* @return string
*/ */
public function createTag($tag, $attributes, $content = null) { public function createTag($tag, $attributes, $content = null) {
Deprecation::notice('3.2', 'Use FormField::create_tag()'); Deprecation::notice('4.0', 'Use FormField::create_tag()');
return self::create_tag($tag, $attributes, $content); return self::create_tag($tag, $attributes, $content);
} }
/** /**
* Validation method each {@link FormField} subclass should implement, * Abstract method each {@link FormField} subclass must implement, determines whether the field
* determining whether the field is valid or not based on the value. * is valid or not based on the value.
*
* @todo Make this abstract.
* *
* @param Validator * @param Validator
* @return boolean *
* @return bool
*/ */
public function validate($validator) { public function validate($validator) {
return true; return true;
@ -928,13 +1125,16 @@ class FormField extends RequestHandler {
/** /**
* Describe this field, provide help text for it. * Describe this field, provide help text for it.
* By default, renders as a <span class="description">
* underneath the form field.
* *
* @return string Description * By default, renders as a <span class="description"> underneath the form field.
*
* @param string $description
*
* @return $this
*/ */
public function setDescription($description) { public function setDescription($description) {
$this->description = $description; $this->description = $description;
return $this; return $this;
} }
@ -945,37 +1145,51 @@ class FormField extends RequestHandler {
return $this->description; return $this->description;
} }
/**
* @return string
*/
public function debug() { public function debug() {
return "$this->class ($this->name: $this->title : <font style='color:red;'>$this->message</font>)" return sprintf(
. " = $this->value"; '%s (%s: %s : <span style="color:red;">%s</span>) = %s',
$this->class,
$this->name,
$this->title,
$this->message,
$this->value
);
} }
/** /**
* This function is used by the template processor. If you refer to a field as a $ variable, it * This function is used by the template processor. If you refer to a field as a $ variable, it
* will return the $Field value. * will return the $Field value.
*
* @return string
*/ */
public function forTemplate() { public function forTemplate() {
return $this->Field(); return $this->Field();
} }
/** /**
* @uses Validator->fieldIsRequired() * @return bool
* @return boolean
*/ */
public function Required() { public function Required() {
if($this->form && ($validator = $this->form->Validator)) { if($this->form && ($validator = $this->form->Validator)) {
return $validator->fieldIsRequired($this->name); return $validator->fieldIsRequired($this->name);
} }
return false;
} }
/** /**
* Set the FieldList that contains this field. * Set the FieldList that contains this field.
* *
* @param FieldList $list * @param FieldList $containerFieldList
*
* @return FieldList * @return FieldList
*/ */
public function setContainerFieldList($list) { public function setContainerFieldList($containerFieldList) {
$this->containerFieldList = $list; $this->containerFieldList = $containerFieldList;
return $this; return $this;
} }
@ -988,31 +1202,46 @@ class FormField extends RequestHandler {
return $this->containerFieldList; return $this->containerFieldList;
} }
/**
* @return null|FieldList
*/
public function rootFieldList() { public function rootFieldList() {
if(is_object($this->containerFieldList)) return $this->containerFieldList->rootFieldList(); if(is_object($this->containerFieldList)) {
else user_error("rootFieldList() called on $this->class object without a containerFieldList", E_USER_ERROR); return $this->containerFieldList->rootFieldList();
}
user_error(
"rootFieldList() called on $this->class object without a containerFieldList",
E_USER_ERROR
);
return null;
} }
/** /**
* Returns another instance of this field, but "cast" to a different class. * Returns another instance of this field, but "cast" to a different class. The logic tries to
* The logic tries to retain all of the instance properties, * retain all of the instance properties, and may be overloaded by subclasses to set additional
* and may be overloaded by subclasses to set additional ones. * ones.
* *
* Assumes the standard FormField parameter signature with * Assumes the standard FormField parameter signature with its name as the only mandatory
* its name as the only mandatory argument. Mainly geared towards * argument. Mainly geared towards creating *_Readonly or *_Disabled subclasses of the same
* creating *_Readonly or *_Disabled subclasses of the same type, * type, or casting to a {@link ReadonlyField}.
* or casting to a {@link ReadonlyField}.
* *
* Does not copy custom field templates, since they probably won't apply to * Does not copy custom field templates, since they probably won't apply to the new instance.
* the new instance. *
* @param mixed $classOrCopy Class name for copy, or existing copy instance to update
* *
* @param String $classOrCopy Class name for copy, or existing copy instance to update
* @return FormField * @return FormField
*/ */
public function castedCopy($classOrCopy) { public function castedCopy($classOrCopy) {
$field = (is_object($classOrCopy)) ? $classOrCopy : new $classOrCopy($this->name); $field = $classOrCopy;
if(!is_object($field)) {
$field = new $classOrCopy($this->name);
}
$field $field
->setValue($this->value) // get value directly from property, avoid any conversions ->setValue($this->value)
->setForm($this->form) ->setForm($this->form)
->setTitle($this->Title()) ->setTitle($this->Title())
->setLeftTitle($this->LeftTitle()) ->setLeftTitle($this->LeftTitle())
@ -1020,12 +1249,12 @@ class FormField extends RequestHandler {
->addExtraClass($this->extraClass()) ->addExtraClass($this->extraClass())
->setDescription($this->getDescription()); ->setDescription($this->getDescription());
// Only include built-in attributes, ignore anything // Only include built-in attributes, ignore anything set through getAttributes().
// set through getAttributes(), since those might change important characteristics // Those might change important characteristics of the field, e.g. its "type" attribute.
// of the field, e.g. its "type" attribute. foreach($this->attributes as $attributeKey => $attributeValue) {
foreach($this->attributes as $k => $v) { $field->setAttribute($attributeKey, $attributeValue);
$field->setAttribute($k, $v); }
}
$field->dontEscape = $this->dontEscape; $field->dontEscape = $this->dontEscape;
return $field; return $field;

View File

@ -10,7 +10,7 @@ class HiddenField extends FormField {
/** /**
* @param array $properties * @param array $properties
* *
* @return string * @return HTMLText
*/ */
public function FieldHolder($properties = array()) { public function FieldHolder($properties = array()) {
return $this->Field($properties); return $this->Field($properties);

View File

@ -29,7 +29,7 @@ class HtmlEditorField extends TextareaField {
protected $rows = 30; protected $rows = 30;
/** /**
* @deprecated since version 3.2 * @deprecated since version 4.0
*/ */
public static function include_js() { public static function include_js() {
Deprecation::notice('4.0', 'Use HtmlEditorConfig::require_js() instead'); Deprecation::notice('4.0', 'Use HtmlEditorConfig::require_js() instead');

View File

@ -27,14 +27,25 @@ class InlineFormAction extends FormField {
return $this->castedCopy('InlineFormAction_ReadOnly'); return $this->castedCopy('InlineFormAction_ReadOnly');
} }
/**
* @param array $properties
* @return HTMLText
*/
public function Field($properties = array()) { public function Field($properties = array()) {
if($this->includeDefaultJS) { if($this->includeDefaultJS) {
Requirements::javascriptTemplate(FRAMEWORK_DIR . '/javascript/InlineFormAction.js', Requirements::javascriptTemplate(FRAMEWORK_DIR . '/javascript/InlineFormAction.js',
array('ID'=>$this->id())); array('ID'=>$this->id()));
} }
return "<input type=\"submit\" name=\"action_{$this->name}\" value=\"{$this->title}\" id=\"{$this->id()}\"" return DBField::create_field(
. " class=\"action{$this->extraClass}\" />"; 'HTMLText',
FormField::create('input', array(
'name' => sprintf('action_%s', $this->getName()),
'value' => $this->title,
'id' => $this->ID(),
'class' => sprintf('action%s', $this->extraClass),
))
);
} }
public function Title() { public function Title() {
@ -61,9 +72,21 @@ class InlineFormAction_ReadOnly extends FormField {
protected $readonly = true; protected $readonly = true;
/**
* @param array $properties
* @return HTMLText
*/
public function Field($properties = array()) { public function Field($properties = array()) {
return "<input type=\"submit\" name=\"action_{$this->name}\" value=\"{$this->title}\" id=\"{$this->id()}\"" return DBField::create_field('HTMLText',
. " disabled=\"disabled\" class=\"action disabled$this->extraClass\" />"; FormField::create_tag('input', array(
'type' => 'submit',
'name' => sprintf('action_%s', $this->name),
'value' => $this->title,
'id' => $this->id(),
'disabled' => 'disabled',
'class' => 'action disabled ' . $this->extraClass,
))
);
} }
public function Title() { public function Title() {

View File

@ -41,13 +41,16 @@ class MoneyField extends FormField {
} }
/** /**
* @return string * @param array
* @return HTMLText
*/ */
public function Field($properties = array()) { public function Field($properties = array()) {
return "<div class=\"fieldgroup\">" . return DBField::create_field('HTMLText',
"<div class=\"fieldgroup\">" .
"<div class=\"fieldgroup-field\">" . $this->fieldCurrency->SmallFieldHolder() . "</div>" . "<div class=\"fieldgroup-field\">" . $this->fieldCurrency->SmallFieldHolder() . "</div>" .
"<div class=\"fieldgroup-field\">" . $this->fieldAmount->SmallFieldHolder() . "</div>" . "<div class=\"fieldgroup-field\">" . $this->fieldAmount->SmallFieldHolder() . "</div>" .
"</div>"; "</div>"
);
} }
/** /**

View File

@ -1,75 +1,98 @@
<?php <?php
/** /**
* NullableField is a field that wraps other fields when you want to allow the user to specify whether the value of * NullableField is a field that wraps other fields when you want to allow the user to specify
* the field is null or not. * 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.
* *
* 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")); * $a = new NullableField(new TextField("Field1", "Field 1", "abc"));
* *
* It displays the field that is wrapped followed by a checkbox that is used to specify if the value is null or not. * It displays the field that is wrapped followed by a checkbox that is used to specify if the
* It uses the Title of the wrapped field for its title. * value is null or not. It uses the Title of the wrapped field for its title.
* When a form is submitted the field tests the value of the "is null" checkbox and sets its value accordingly. *
* You can retrieve the value of the wrapped field from the NullableField as follows: * When a form is submitted the field tests the value of the "is null" checkbox and sets its value
* accordingly. You can retrieve the value of the wrapped field from the NullableField as follows:
*
* $field->Value() or $field->dataValue() * $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 * You can specify the label to use for the "is null" checkbox. If you want to use i18n for this
* like this: * label then specify it like this:
* *
* $field->setIsNullLabel(_T(SOME_MODULE_ISNULL_LABEL, "Is Null"); * $field->setIsNullLabel(_T(SOME_MODULE_ISNULL_LABEL, "Is Null"));
* *
* @author Pete Bacon Darwin * @author Pete Bacon Darwin
*
* @package forms * @package forms
* @subpackage fields-basic * @subpackage fields-basic
*/ */
class NullableField extends FormField { class NullableField extends FormField {
/** /**
* The field that holds the value of this field * The field that holds the value of this field
*
* @var FormField * @var FormField
*/ */
protected $valueField; protected $valueField;
/** /**
* The label to show next to the is null check box. * The label to show next to the is null check box.
*
* @var string * @var string
*/ */
protected $isNullLabel; protected $isNullLabel;
/** /**
* Create a new nullable field * Create a new nullable field
* @param $valueField *
* @return NullableField * @param FormField $valueField
* @param null|string $isNullLabel
*/ */
public function __construct(FormField $valueField, $isNullLabel = null) { public function __construct(FormField $valueField, $isNullLabel = null) {
$this->valueField = $valueField; $this->valueField = $valueField;
$this->isNullLabel = $isNullLabel;
if ( is_null($this->isNullLabel) ) { if(isset($isNullLabel)) {
// Set a default label if one is not provided. $this->setIsNullLabel($isNullLabel);
} else {
$this->isNullLabel = _t('NullableField.IsNullLabel', 'Is Null'); $this->isNullLabel = _t('NullableField.IsNullLabel', 'Is Null');
} }
parent::__construct($valueField->getName(), $valueField->Title(), $valueField->Value(),
$valueField->getForm(), $valueField->RightTitle()); parent::__construct(
$this->readonly = $valueField->isReadonly(); $valueField->getName(),
$valueField->Title(),
$valueField->Value()
);
$this->setForm($valueField->getForm());
$this->setRightTitle($valueField->RightTitle());
$this->setReadonly($valueField->isReadonly());
} }
/** /**
* Get the label used for the Is Null checkbox. * Get the label used for the Is Null checkbox.
*
* @return string * @return string
*/ */
public function getIsNullLabel() { public function getIsNullLabel() {
return $this->isNullLabel; return $this->isNullLabel;
} }
/** /**
* Set the label used for the Is Null checkbox. * Set the label used for the Is Null checkbox.
*
* @param $isNulLabel string * @param $isNulLabel string
*
* @return $this
*/ */
public function setIsNullLabel(string $isNulLabel){ public function setIsNullLabel($isNulLabel) {
$this->isNullLabel = $isNulLabel; $this->isNullLabel = $isNulLabel;
return $this; return $this;
} }
/** /**
* Get the id used for the Is Null check box. * Get the id used for the Is Null check box.
*
* @return string * @return string
*/ */
public function getIsNullId() { public function getIsNullId() {
@ -77,54 +100,81 @@ class NullableField extends FormField {
} }
/** /**
* (non-PHPdoc) * @param array $properties
* @see framework/forms/FormField#Field() *
* @return HTMLText
*/ */
public function Field($properties = array()) { public function Field($properties = array()) {
if ( $this->isReadonly()) { if($this->isReadonly()) {
$nullableCheckbox = new CheckboxField_Readonly($this->getIsNullId()); $nullableCheckbox = new CheckboxField_Readonly($this->getIsNullId());
} else { } else {
$nullableCheckbox = new CheckboxField($this->getIsNullId()); $nullableCheckbox = new CheckboxField($this->getIsNullId());
} }
$nullableCheckbox->setValue(is_null($this->dataValue())); $nullableCheckbox->setValue(is_null($this->dataValue()));
return $this->valueField->Field() . ' ' . $nullableCheckbox->Field() return DBField::create_field('HTMLText', sprintf(
. '&nbsp;<span>' . $this->getIsNullLabel().'</span>'; '%s %s&nbsp;<span>%s</span>',
$this->valueField->Field(),
$nullableCheckbox->Field(),
$this->getIsNullLabel()
));
} }
/** /**
* Value is sometimes an array, and sometimes a single value, so we need to handle both cases * Value is sometimes an array, and sometimes a single value, so we need to handle both cases
*
* @param mixed $value
* @param null|array $data
*
* @return $this
*/ */
public function setValue($value, $data = null) { public function setValue($value, $data = null) {
if ( is_array($data) && array_key_exists($this->getIsNullId(), $data) && $data[$this->getIsNullId()] ) { $id = $this->getIsNullId();
if(is_array($data) && array_key_exists($id, $data) && $data[$id]) {
$value = null; $value = null;
} }
$this->valueField->setValue($value); $this->valueField->setValue($value);
parent::setValue($value); parent::setValue($value);
return $this; return $this;
} }
/** /**
* (non-PHPdoc) * @param string $name
* @see forms/FormField#setName($name) *
* @return $this
*/ */
public function setName($name) { public function setName($name) {
// We need to pass through the name change to the underlying value field.
$this->valueField->setName($name); $this->valueField->setName($name);
parent::setName($name); parent::setName($name);
return $this; return $this;
} }
/** /**
* (non-PHPdoc) * @return string
* @see framework/forms/FormField#debug()
*/ */
public function debug() { public function debug() {
$result = "$this->class ($this->name: $this->title : <font style='color:red;'>$this->message</font>) = "; $result = sprintf(
$result .= (is_null($this->value)) ? "<<null>>" : $this->value; '%s (%s: $s : <span style="color: red">%s</span>) = ',
return result; $this->class,
$this->name,
$this->title,
$this->message
);
if($this->value === null) {
$result .= "<<null>>";
} else {
$result .= (string) $this->value;
}
return $result;
} }
} }

View File

@ -9,56 +9,75 @@
* @subpackage fields-formattedinput * @subpackage fields-formattedinput
*/ */
class NumericField extends TextField { class NumericField extends TextField {
/** /**
* Override locale for this field * Override locale for this field.
* *
* @var string * @var string
*/ */
protected $locale = null; protected $locale = null;
/**
* @param mixed $value
* @param array $data
*
* @return $this
*
* @throws Zend_Locale_Exception
*/
public function setValue($value, $data = array()) { public function setValue($value, $data = array()) {
require_once "Zend/Locale/Format.php"; require_once "Zend/Locale/Format.php";
// If passing in a non-string number, or a value // If passing in a non-string number, or a value
// directly from a dataobject then localise this number // directly from a DataObject then localise this number
if ((is_numeric($value) && !is_string($value)) ||
($value && $data instanceof DataObject) if(is_int($value) || is_float($value) || $data instanceof DataObject) {
){
$locale = new Zend_Locale($this->getLocale()); $locale = new Zend_Locale($this->getLocale());
$this->value = Zend_Locale_Format::toNumber($value, array('locale' => $locale));
$this->value = Zend_Locale_Format::toNumber(
$value,
array('locale' => $locale)
);
} else { } else {
// If an invalid number, store it anyway, but validate() will fail
$this->value = $this->clean($value); $this->value = $this->clean($value);
} }
return $this; return $this;
} }
/** /**
* In some cases and locales, validation expects non-breaking spaces * In some cases and locales, validation expects non-breaking spaces.
*
* Returns the value, with all spaces replaced with non-breaking spaces.
* *
* @param string $input * @param string $input
* @return string The input value, with all spaces replaced with non-breaking spaces *
* @return string
*/ */
protected function clean($input) { protected function clean($input) {
$nbsp = html_entity_decode('&nbsp;', null, 'UTF-8'); $replacement = html_entity_decode('&nbsp;', null, 'UTF-8');
return str_replace(' ', $nbsp, trim($input));
return str_replace(' ', $replacement, trim($input));
} }
/** /**
* Determine if the current value is a valid number in the current locale * Determine if the current value is a valid number in the current locale.
* *
* @return bool * @return bool
*/ */
protected function isNumeric() { protected function isNumeric() {
require_once "Zend/Locale/Format.php"; require_once "Zend/Locale/Format.php";
$locale = new Zend_Locale($this->getLocale()); $locale = new Zend_Locale($this->getLocale());
return Zend_Locale_Format::isNumber( return Zend_Locale_Format::isNumber(
$this->clean($this->value), $this->clean($this->value),
array('locale' => $locale) array('locale' => $locale)
); );
} }
/**
* {@inheritdoc}
*/
public function Type() { public function Type() {
return 'numeric text'; return 'numeric text';
} }
@ -81,74 +100,111 @@ class NumericField extends TextField {
return true; return true;
} }
if($this->isNumeric()) return true; if($this->isNumeric()) {
return true;
}
$validator->validationError( $validator->validationError(
$this->name, $this->name,
_t( _t(
'NumericField.VALIDATION', "'{value}' is not a number, only numbers can be accepted for this field", 'NumericField.VALIDATION',
"'{value}' is not a number, only numbers can be accepted for this field",
array('value' => $this->value) array('value' => $this->value)
), ),
"validation" "validation"
); );
return false; return false;
} }
/** /**
* Extracts the number value from the localised string value * Extracts the number value from the localised string value.
* *
* @return string number value * @return string
*/ */
public function dataValue() { public function dataValue() {
require_once "Zend/Locale/Format.php"; require_once "Zend/Locale/Format.php";
if(!$this->isNumeric()) return 0;
if(!$this->isNumeric()) {
return 0;
}
$locale = new Zend_Locale($this->getLocale()); $locale = new Zend_Locale($this->getLocale());
$number = Zend_Locale_Format::getNumber( $number = Zend_Locale_Format::getNumber(
$this->clean($this->value), $this->clean($this->value),
array('locale' => $locale) array('locale' => $locale)
); );
return $number; return $number;
} }
/** /**
* Returns a readonly version of this field * Creates a read-only version of the field.
*
* @return NumericField_Readonly
*/ */
public function performReadonlyTransformation() { public function performReadonlyTransformation() {
$field = new NumericField_Readonly($this->name, $this->title, $this->value); $field = new NumericField_Readonly(
$this->name,
$this->title,
$this->value
);
$field->setForm($this->form); $field->setForm($this->form);
return $field; return $field;
} }
/** /**
* Gets the current locale this field is set to * Gets the current locale this field is set to.
* *
* @return string * @return string
*/ */
public function getLocale() { public function getLocale() {
return $this->locale ?: i18n::get_locale(); if($this->locale) {
return $this->locale;
}
return i18n::get_locale();
} }
/** /**
* Override the locale for this field * Override the locale for this field.
* *
* @param string $locale * @param string $locale
*
* @return $this * @return $this
*/ */
public function setLocale($locale) { public function setLocale($locale) {
$this->locale = $locale; $this->locale = $locale;
return $this; return $this;
} }
} }
/**
* Readonly version of a numeric field.
*
* @package forms
* @subpackage fields-basic
*/
class NumericField_Readonly extends ReadonlyField { class NumericField_Readonly extends ReadonlyField {
/**
* @return static
*/
public function performReadonlyTransformation() { public function performReadonlyTransformation() {
return clone $this; return clone $this;
} }
/**
* @return string
*/
public function Value() { public function Value() {
return Convert::raw2xml($this->value ? "$this->value" : "0"); if($this->value) {
} return Convert::raw2xml((string) $this->value);
}
return '0';
}
} }

View File

@ -27,6 +27,10 @@ class PhoneNumberField extends FormField {
parent::__construct($name, $title, $value); parent::__construct($name, $title, $value);
} }
/**
* @param array $properties
* @return FieldGroup|HTMLText
*/
public function Field($properties = array()) { public function Field($properties = array()) {
$fields = new FieldGroup( $this->name ); $fields = new FieldGroup( $this->name );
$fields->setID("{$this->name}_Holder"); $fields->setID("{$this->name}_Holder");

View File

@ -36,6 +36,10 @@ class ReadonlyField extends FormField {
return clone $this; return clone $this;
} }
/**
* @param array $properties
* @return HTMLText
*/
public function Field($properties = array()) { public function Field($properties = array()) {
// Include a hidden field in the HTML // Include a hidden field in the HTML
if($this->includeHiddenField && $this->readonly) { if($this->includeHiddenField && $this->readonly) {

View File

@ -34,8 +34,11 @@ class RequiredFields extends Validator {
parent::__construct(); parent::__construct();
} }
/**
* @deprecated since version 4.0
*/
public function useLabels($flag) { public function useLabels($flag) {
Deprecation::notice('3.2', 'useLabels will be removed from 3.2, please do not use it or implement it yourself'); Deprecation::notice('4.0', 'useLabels will be removed from 4.0, please do not use it or implement it yourself');
$this->useLabels = $flag; $this->useLabels = $flag;
return $this; return $this;
} }

View File

@ -204,7 +204,7 @@ class TreeDropdownField extends FormField {
} }
/** /**
* @return string * @return HTMLText
*/ */
public function Field($properties = array()) { public function Field($properties = array()) {
Requirements::add_i18n_javascript(FRAMEWORK_DIR . '/javascript/lang'); Requirements::add_i18n_javascript(FRAMEWORK_DIR . '/javascript/lang');

View File

@ -3,12 +3,11 @@
/** /**
* Displays a {@link SS_List} in a grid format. * Displays a {@link SS_List} in a grid format.
* *
* GridField is a field that takes an SS_List and displays it in an table with rows * GridField is a field that takes an SS_List and displays it in an table with rows and columns.
* and columns. It reminds of the old TableFields but works with SS_List types * It reminds of the old TableFields but works with SS_List types and only loads the necessary
* and only loads the necessary rows from the list. * rows from the list.
* *
* The minimum configuration is to pass in name and title of the field and a * The minimum configuration is to pass in name and title of the field and a SS_List.
* SS_List.
* *
* <code> * <code>
* $gridField = new GridField('ExampleGrid', 'Example grid', new DataList('Page')); * $gridField = new GridField('ExampleGrid', 'Example grid', new DataList('Page'));
@ -21,45 +20,44 @@
* @property GridState_Data $State The gridstate of this object * @property GridState_Data $State The gridstate of this object
*/ */
class GridField extends FormField { class GridField extends FormField {
/** /**
*
* @var array * @var array
*/ */
private static $allowed_actions = array( private static $allowed_actions = array(
'index', 'index',
'gridFieldAlterAction' 'gridFieldAlterAction',
); );
/** /**
* The datasource * Data source.
* *
* @var SS_List * @var SS_List
*/ */
protected $list = null; protected $list = null;
/** /**
* The classname of the DataObject that the GridField will display. Defaults to the value of $this->list->dataClass * Class name of the DataObject that the GridField will display.
*
* Defaults to the value of $this->list->dataClass.
* *
* @var string * @var string
*/ */
protected $modelClassName = ''; protected $modelClassName = '';
/** /**
* the current state of the GridField * Current state of the GridField.
* *
* @var GridState * @var GridState
*/ */
protected $state = null; protected $state = null;
/** /**
*
* @var GridFieldConfig * @var GridFieldConfig
*/ */
protected $config = null; protected $config = null;
/** /**
* The components list * Components list.
* *
* @var array * @var array
*/ */
@ -67,14 +65,15 @@ class GridField extends FormField {
/** /**
* Internal dispatcher for column handlers. * Internal dispatcher for column handlers.
* Keys are column names and values are GridField_ColumnProvider objects *
* Keys are column names and values are GridField_ColumnProvider objects.
* *
* @var array * @var array
*/ */
protected $columnDispatch = null; protected $columnDispatch = null;
/** /**
* Map of callbacks for custom data fields * Map of callbacks for custom data fields.
* *
* @var array * @var array
*/ */
@ -86,8 +85,6 @@ class GridField extends FormField {
protected $name = ''; protected $name = '';
/** /**
* Creates a new GridField field
*
* @param string $name * @param string $name
* @param string $title * @param string $title
* @param SS_List $dataList * @param SS_List $dataList
@ -95,13 +92,18 @@ class GridField extends FormField {
*/ */
public function __construct($name, $title = null, SS_List $dataList = null, GridFieldConfig $config = null) { public function __construct($name, $title = null, SS_List $dataList = null, GridFieldConfig $config = null) {
parent::__construct($name, $title, null); parent::__construct($name, $title, null);
$this->name = $name; $this->name = $name;
if($dataList) { if($dataList) {
$this->setList($dataList); $this->setList($dataList);
} }
$this->setConfig($config ?: GridFieldConfig_Base::create()); if(!$config) {
$config = GridFieldConfig_Base::create();
}
$this->setConfig($config);
$this->config->addComponent(new GridState_Component()); $this->config->addComponent(new GridState_Component());
$this->state = new GridState($this); $this->state = new GridState($this);
@ -109,44 +111,58 @@ class GridField extends FormField {
$this->addExtraClass('ss-gridfield'); $this->addExtraClass('ss-gridfield');
} }
/**
* @param SS_HTTPRequest $request
*
* @return string
*/
public function index($request) { public function index($request) {
return $this->gridFieldAlterAction(array(), $this->getForm(), $request); return $this->gridFieldAlterAction(array(), $this->getForm(), $request);
} }
/** /**
* Set the modelClass (dataobject) that this field will get it column headers from. * Set the modelClass (data object) that this field will get it column headers from.
* If no $displayFields has been set, the displayfields will be fetched from *
* this modelclass $summary_fields * If no $displayFields has been set, the display fields will be $summary_fields.
*
* @see GridFieldDataColumns::getDisplayFields()
* *
* @param string $modelClassName * @param string $modelClassName
* *
* @see GridFieldDataColumns::getDisplayFields() * @return $this
*/ */
public function setModelClass($modelClassName) { public function setModelClass($modelClassName) {
$this->modelClassName = $modelClassName; $this->modelClassName = $modelClassName;
return $this; return $this;
} }
/** /**
* Returns a dataclass that is a DataObject type that this GridField should look like. * Returns a data class that is a DataObject type that this GridField should look like.
* *
* @throws Exception
* @return string * @return string
*
* @throws LogicException
*/ */
public function getModelClass() { public function getModelClass() {
if($this->modelClassName) return $this->modelClassName; if($this->modelClassName) {
if($this->list && method_exists($this->list, 'dataClass')) { return $this->modelClassName;
$class = $this->list->dataClass();
if($class) return $class;
} }
throw new LogicException('GridField doesn\'t have a modelClassName,' if($this->list && method_exists($this->list, 'dataClass')) {
. ' so it doesn\'t know the columns of this grid.'); $class = $this->list->dataClass();
if($class) {
return $class;
}
}
throw new LogicException(
'GridField doesn\'t have a modelClassName, so it doesn\'t know the columns of this grid.'
);
} }
/** /**
* Get the GridFieldConfig
*
* @return GridFieldConfig * @return GridFieldConfig
*/ */
public function getConfig() { public function getConfig() {
@ -156,61 +172,69 @@ class GridField extends FormField {
/** /**
* @param GridFieldConfig $config * @param GridFieldConfig $config
* *
* @return GridField * @return $this
*/ */
public function setConfig(GridFieldConfig $config) { public function setConfig(GridFieldConfig $config) {
$this->config = $config; $this->config = $config;
return $this; return $this;
} }
/**
* @return ArrayList
*/
public function getComponents() { public function getComponents() {
return $this->config->getComponents(); return $this->config->getComponents();
} }
/** /**
* Cast a arbitrary value with the help of a castingDefintion * Cast an arbitrary value with the help of a $castingDefinition.
*
* @param $value
* @param $castingDefinition
* *
* @todo refactor this into GridFieldComponent * @todo refactor this into GridFieldComponent
*
* @param mixed $value
* @param string|array $castingDefinition
*
* @return mixed
*/ */
public function getCastedValue($value, $castingDefinition) { public function getCastedValue($value, $castingDefinition) {
$castingParams = array();
if(is_array($castingDefinition)) { if(is_array($castingDefinition)) {
$castingParams = $castingDefinition; $castingParams = $castingDefinition;
array_shift($castingParams); array_shift($castingParams);
$castingDefinition = array_shift($castingDefinition); $castingDefinition = array_shift($castingDefinition);
} else {
$castingParams = array();
} }
if(strpos($castingDefinition, '->') === false) { if(strpos($castingDefinition, '->') === false) {
$castingFieldType = $castingDefinition; $castingFieldType = $castingDefinition;
$castingField = DBField::create_field($castingFieldType, $value); $castingField = DBField::create_field($castingFieldType, $value);
$value = call_user_func_array(array($castingField, 'XML'), $castingParams);
} else { return call_user_func_array(array($castingField, 'XML'), $castingParams);
$fieldTypeParts = explode('->', $castingDefinition);
$castingFieldType = $fieldTypeParts[0];
$castingMethod = $fieldTypeParts[1];
$castingField = DBField::create_field($castingFieldType, $value);
$value = call_user_func_array(array($castingField, $castingMethod), $castingParams);
} }
return $value; list($castingFieldType, $castingMethod) = explode('->', $castingDefinition);
$castingField = DBField::create_field($castingFieldType, $value);
return call_user_func_array(array($castingField, $castingMethod), $castingParams);
} }
/** /**
* Set the datasource * Set the data source.
* *
* @param SS_List $list * @param SS_List $list
*
* @return $this
*/ */
public function setList(SS_List $list) { public function setList(SS_List $list) {
$this->list = $list; $this->list = $list;
return $this; return $this;
} }
/** /**
* Get the datasource * Get the data source.
* *
* @return SS_List * @return SS_List
*/ */
@ -219,26 +243,28 @@ class GridField extends FormField {
} }
/** /**
* Get the datasource after applying the {@link GridField_DataManipulator}s to it. * Get the data source after applying every {@link GridField_DataManipulator} to it.
* *
* @return SS_List * @return SS_List
*/ */
public function getManipulatedList() { public function getManipulatedList() {
$list = $this->getList(); $list = $this->getList();
foreach($this->getComponents() as $item) { foreach($this->getComponents() as $item) {
if($item instanceof GridField_DataManipulator) { if($item instanceof GridField_DataManipulator) {
$list = $item->getManipulatedData($this, $list); $list = $item->getManipulatedData($this, $list);
} }
} }
return $list; return $list;
} }
/** /**
* Get the current GridState_Data or the GridState * Get the current GridState_Data or the GridState.
* *
* @param bool $getData - flag for returning the GridState_Data or the GridState * @param bool $getData
* *
* @return GridState_data|GridState * @return GridState_Data|GridState
*/ */
public function getState($getData = true) { public function getState($getData = true) {
if($getData) { if($getData) {
@ -249,9 +275,11 @@ class GridField extends FormField {
} }
/** /**
* Returns the whole gridfield rendered with all the attached components * Returns the whole gridfield rendered with all the attached components.
* *
* @return string * @param array $properties
*
* @return HTMLText
*/ */
public function FieldHolder($properties = array()) { public function FieldHolder($properties = array()) {
Requirements::css(THIRDPARTY_DIR . '/jquery-ui-themes/smoothness/jquery-ui.css'); Requirements::css(THIRDPARTY_DIR . '/jquery-ui-themes/smoothness/jquery-ui.css');
@ -265,88 +293,115 @@ class GridField extends FormField {
Requirements::javascript(THIRDPARTY_DIR . '/jquery-entwine/dist/jquery.entwine-dist.js'); Requirements::javascript(THIRDPARTY_DIR . '/jquery-entwine/dist/jquery.entwine-dist.js');
Requirements::javascript(FRAMEWORK_DIR . '/javascript/GridField.js'); Requirements::javascript(FRAMEWORK_DIR . '/javascript/GridField.js');
// Get columns
$columns = $this->getColumns(); $columns = $this->getColumns();
// Get data
$list = $this->getManipulatedList(); $list = $this->getManipulatedList();
// Render headers, footers, etc
$content = array( $content = array(
"before" => "", 'before' => '',
"after" => "", 'after' => '',
"header" => "", 'header' => '',
"footer" => "", 'footer' => '',
); );
foreach($this->getComponents() as $item) { foreach($this->getComponents() as $item) {
if($item instanceof GridField_HTMLProvider) { if($item instanceof GridField_HTMLProvider) {
$fragments = $item->getHTMLFragments($this); $fragments = $item->getHTMLFragments($this);
if($fragments) foreach($fragments as $k => $v) {
$k = strtolower($k);
if(!isset($content[$k])) $content[$k] = "";
$content[$k] .= $v . "\n";
}
}
}
foreach($content as $k => $v) { if($fragments) {
$content[$k] = trim($v); foreach($fragments as $fragmentKey => $fragmentValue) {
} $fragmentKey = strtolower($fragmentKey);
// Replace custom fragments and check which fragments are defined if(!isset($content[$fragmentKey])) {
// Nested dependencies are handled by deferring the rendering of any content item that $content[$fragmentKey] = '';
// Circular dependencies are detected by disallowing any item to be deferred more than 5 times
// It's a fairly crude algorithm but it works
$fragmentDefined = array('header' => true, 'footer' => true, 'before' => true, 'after' => true);
reset($content);
while(list($k, $v) = each($content)) {
if(preg_match_all('/\$DefineFragment\(([a-z0-9\-_]+)\)/i', $v, $matches)) {
foreach($matches[1] as $match) {
$fragmentName = strtolower($match);
$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(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) {
throw new LogicException("GridField HTML fragment '$fragmentName' and '$matches[1]' " .
"appear to have a circular dependency.");
} }
// Otherwise we can push to the end of the content array $content[$fragmentKey] .= $fragmentValue . "\n";
unset($content[$k]);
$content[$k] = $v;
if(!isset($fragmentDeferred[$k])) {
$fragmentDeferred[$k] = 1;
} else {
$fragmentDeferred[$k]++;
}
break;
} else {
$content[$k] = preg_replace('/\$DefineFragment\(' . $fragmentName . '\)/i', $fragment,
$content[$k]);
} }
} }
} }
} }
// Check for any undefined fragments, and if so throw an exception foreach($content as $contentKey => $contentValue) {
// While we're at it, trim whitespace off the elements $content[$contentKey] = trim($contentValue);
foreach($content as $k => $v) { }
if(empty($fragmentDefined[$k])) {
throw new LogicException("GridField HTML fragment '$k' was given content," // Replace custom fragments and check which fragments are defined. Circular dependencies
. " but not defined. Perhaps there is a supporting GridField component you need to add?"); // are detected by disallowing any item to be deferred more than 5 times.
$fragmentDefined = array(
'header' => true,
'footer' => true,
'before' => true,
'after' => true,
);
reset($content);
while(list($contentKey, $contentValue) = each($content)) {
if(preg_match_all('/\$DefineFragment\(([a-z0-9\-_]+)\)/i', $contentValue, $matches)) {
foreach($matches[1] as $match) {
$fragmentName = strtolower($match);
$fragmentDefined[$fragmentName] = true;
$fragment = '';
if(isset($content[$fragmentName])) {
$fragment = $content[$fragmentName];
}
// 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(isset($fragmentDeferred[$contentKey]) && $fragmentDeferred[$contentKey] > 5) {
throw new LogicException(sprintf(
'GridField HTML fragment "%s" and "%s" appear to have a circular dependency.',
$fragmentName,
$matches[1]
));
}
unset($content[$contentKey]);
$content[$contentKey] = $contentValue;
if(!isset($fragmentDeferred[$contentKey])) {
$fragmentDeferred[$contentKey] = 0;
}
$fragmentDeferred[$contentKey]++;
break;
} else {
$content[$contentKey] = preg_replace(
sprintf('/\$DefineFragment\(%s\)/i', $fragmentName),
$fragment,
$content[$contentKey]
);
}
}
}
}
// Check for any undefined fragments, and if so throw an exception.
// While we're at it, trim whitespace off the elements.
foreach($content as $contentKey => $contentValue) {
if(empty($fragmentDefined[$contentKey])) {
throw new LogicException(sprintf(
'GridField HTML fragment "%s" was given content, but not defined. Perhaps there is a supporting GridField component you need to add?',
$contentKey
));
} }
} }
$total = count($list); $total = count($list);
if($total > 0) { if($total > 0) {
$rows = array(); $rows = array();
foreach($list as $idx => $record) {
foreach($list as $index => $record) {
if($record->hasMethod('canView') && !$record->canView()) { if($record->hasMethod('canView') && !$record->canView()) {
continue; continue;
} }
@ -356,58 +411,80 @@ class GridField extends FormField {
foreach($this->getColumns() as $column) { foreach($this->getColumns() as $column) {
$colContent = $this->getColumnContent($record, $column); $colContent = $this->getColumnContent($record, $column);
// A return value of null means this columns should be skipped altogether. // Null means this columns should be skipped altogether.
if($colContent === null) { if($colContent === null) {
continue; continue;
} }
$colAttributes = $this->getColumnAttributes($record, $column); $colAttributes = $this->getColumnAttributes($record, $column);
$rowContent .= $this->newCell($total, $idx, $record, $colAttributes, $colContent); $rowContent .= $this->newCell(
$total,
$index,
$record,
$colAttributes,
$colContent
);
} }
$rowAttributes = $this->getRowAttributes($total, $idx, $record); $rowAttributes = $this->getRowAttributes($total, $index, $record);
$rows[] = $this->newRow($total, $idx, $record, $rowAttributes, $rowContent); $rows[] = $this->newRow($total, $index, $record, $rowAttributes, $rowContent);
} }
$content['body'] = implode("\n", $rows); $content['body'] = implode("\n", $rows);
} }
// Display a message when the grid field is empty // Display a message when the grid field is empty.
if(!(isset($content['body']) && $content['body'])) {
$content['body'] = FormField::create_tag( if(empty($content['body'])) {
'tr', $cell = FormField::create_tag(
array("class" => 'ss-gridfield-item ss-gridfield-no-items'), 'td',
FormField::create_tag( array(
'td', 'colspan' => count($columns),
array('colspan' => count($columns)), ),
_t('GridField.NoItemsFound', 'No items found') _t('GridField.NoItemsFound', 'No items found')
)
); );
$row = FormField::create_tag(
'tr',
array(
'class' => 'ss-gridfield-item ss-gridfield-no-items',
),
$cell
);
$content['body'] = $row;
} }
// Turn into the relevant parts of a table $header = $this->getOptionalTableHeader($content);
$head = $content['header'] $body = $this->getOptionalTableBody($content);
? FormField::create_tag('thead', array(), $content['header']) $footer = $this->getOptionalTableFooter($content);
: '';
$body = $content['body']
? FormField::create_tag('tbody', array('class' => 'ss-gridfield-items'), $content['body'])
: '';
$foot = $content['footer']
? FormField::create_tag('tfoot', array(), $content['footer'])
: '';
$this->addExtraClass('ss-gridfield field'); $this->addExtraClass('ss-gridfield field');
$attrs = array_diff_key(
$fieldsetAttributes = array_diff_key(
$this->getAttributes(), $this->getAttributes(),
array('value' => false, 'type' => false, 'name' => false) array(
'value' => false,
'type' => false,
'name' => false,
)
); );
$attrs['data-name'] = $this->getName();
$tableAttrs = array( $fieldsetAttributes['data-name'] = $this->getName();
'id' => isset($this->id) ? $this->id : null,
$tableId = null;
if($this->id) {
$tableId = $this->id;
}
$tableAttributes = array(
'id' => $tableId,
'class' => 'ss-gridfield-table', 'class' => 'ss-gridfield-table',
'cellpadding' => '0', 'cellpadding' => '0',
'cellspacing' => '0' 'cellspacing' => '0',
); );
if($this->getDescription()) { if($this->getDescription()) {
@ -418,12 +495,17 @@ class GridField extends FormField {
); );
} }
return $table = FormField::create_tag(
FormField::create_tag('fieldset', $attrs, 'table',
$content['before'] . $tableAttributes,
FormField::create_tag('table', $tableAttrs, $head . "\n" . $foot . "\n" . $body) . $header . "\n" . $footer . "\n" . $body
$content['after'] );
);
return DBField::create_field('HTMLText', FormField::create_tag(
'fieldset',
$fieldsetAttributes,
$content['before'] . $table . $content['after']
));
} }
/** /**
@ -495,27 +577,44 @@ class GridField extends FormField {
$classes[] = 'last'; $classes[] = 'last';
} }
$classes[] = ($index % 2) ? 'even' : 'odd'; if($index % 2) {
$classes[] = 'even';
} else {
$classes[] = 'odd';
}
return $classes; return $classes;
} }
/**
* @param array $properties
*
* @return HTMLText
*/
public function Field($properties = array()) { public function Field($properties = array()) {
return $this->FieldHolder($properties); return $this->FieldHolder($properties);
} }
/**
* {@inheritdoc}
*/
public function getAttributes() { public function getAttributes() {
return array_merge(parent::getAttributes(), array('data-url' => $this->Link())); return array_merge(
parent::getAttributes(),
array(
'data-url' => $this->Link(),
)
);
} }
/** /**
* Get the columns of this GridField, they are provided by attached GridField_ColumnProvider * Get the columns of this GridField, they are provided by attached GridField_ColumnProvider.
* *
* @return array * @return array
*/ */
public function getColumns() { public function getColumns() {
// Get column list
$columns = array(); $columns = array();
foreach($this->getComponents() as $item) { foreach($this->getComponents() as $item) {
if($item instanceof GridField_ColumnProvider) { if($item instanceof GridField_ColumnProvider) {
$item->augmentColumns($this, $columns); $item->augmentColumns($this, $columns);
@ -526,28 +625,36 @@ class GridField extends FormField {
} }
/** /**
* Get the value from a column * Get the value from a column.
* *
* @param DataObject $record * @param DataObject $record
* @param string $column * @param string $column
* *
* @return string * @return string
*
* @throws InvalidArgumentException * @throws InvalidArgumentException
*/ */
public function getColumnContent($record, $column) { public function getColumnContent($record, $column) {
// Build the column dispatch
if(!$this->columnDispatch) { if(!$this->columnDispatch) {
$this->buildColumnDispatch(); $this->buildColumnDispatch();
} }
if(!empty($this->columnDispatch[$column])) { if(!empty($this->columnDispatch[$column])) {
$content = ""; $content = '';
foreach($this->columnDispatch[$column] as $handler) { foreach($this->columnDispatch[$column] as $handler) {
/**
* @var GridField_ColumnProvider $handler
*/
$content .= $handler->getColumnContent($this, $record, $column); $content .= $handler->getColumnContent($this, $record, $column);
} }
return $content; return $content;
} else { } else {
throw new InvalidArgumentException("Bad column '$column'"); throw new InvalidArgumentException(sprintf(
'Bad column "%s"',
$column
));
} }
} }
@ -567,111 +674,139 @@ class GridField extends FormField {
/** /**
* Get the value of a named field on the given record. * Get the value of a named field on the given record.
* Use of this method ensures that any special rules around the data for this gridfield are followed. *
* Use of this method ensures that any special rules around the data for this gridfield are
* followed.
*
* @param DataObject $record
* @param string $fieldName
*
* @return mixed
*/ */
public function getDataFieldValue($record, $fieldName) { public function getDataFieldValue($record, $fieldName) {
// Custom callbacks
if(isset($this->customDataFields[$fieldName])) { if(isset($this->customDataFields[$fieldName])) {
$callback = $this->customDataFields[$fieldName]; $callback = $this->customDataFields[$fieldName];
return $callback($record); return $callback($record);
} }
// Default implementation
if($record->hasMethod('relField')) { if($record->hasMethod('relField')) {
return $record->relField($fieldName); return $record->relField($fieldName);
} elseif($record->hasMethod($fieldName)) {
return $record->$fieldName();
} else {
return $record->$fieldName;
} }
if($record->hasMethod($fieldName)) {
return $record->$fieldName();
}
return $record->$fieldName;
} }
/** /**
* Get extra columns attributes used as HTML attributes * Get extra columns attributes used as HTML attributes.
* *
* @param DataObject $record * @param DataObject $record
* @param string $column * @param string $column
* *
* @return array * @return array
*
* @throws LogicException * @throws LogicException
* @throws InvalidArgumentException * @throws InvalidArgumentException
*/ */
public function getColumnAttributes($record, $column) { public function getColumnAttributes($record, $column) {
// Build the column dispatch
if(!$this->columnDispatch) { if(!$this->columnDispatch) {
$this->buildColumnDispatch(); $this->buildColumnDispatch();
} }
if(!empty($this->columnDispatch[$column])) { if(!empty($this->columnDispatch[$column])) {
$attrs = array(); $attributes = array();
foreach($this->columnDispatch[$column] as $handler) { foreach($this->columnDispatch[$column] as $handler) {
$column_attrs = $handler->getColumnAttributes($this, $record, $column); /**
* @var GridField_ColumnProvider $handler
*/
$columnAttributes = $handler->getColumnAttributes($this, $record, $column);
if(is_array($column_attrs)) { if(is_array($columnAttributes)) {
$attrs = array_merge($attrs, $column_attrs); $attributes = array_merge($attributes, $columnAttributes);
} elseif($column_attrs) { continue;
$methodSignature = get_class($handler) . "::getColumnAttributes()";
throw new LogicException("Non-array response from $methodSignature.");
} }
throw new LogicException(sprintf(
'Non-array response from %s::getColumnAttributes().',
get_class($handler)
));
} }
return $attrs; return $attributes;
} else {
throw new InvalidArgumentException("Bad column '$column'");
} }
throw new InvalidArgumentException(sprintf(
'Bad column "%s"',
$column
));
} }
/** /**
* Get metadata for a column, example array('Title'=>'Email address') * Get metadata for a column.
*
* @example "array('Title'=>'Email address')"
* *
* @param string $column * @param string $column
* *
* @return array * @return array
*
* @throws LogicException * @throws LogicException
* @throws InvalidArgumentException * @throws InvalidArgumentException
*/ */
public function getColumnMetadata($column) { public function getColumnMetadata($column) {
// Build the column dispatch
if(!$this->columnDispatch) { if(!$this->columnDispatch) {
$this->buildColumnDispatch(); $this->buildColumnDispatch();
} }
if(!empty($this->columnDispatch[$column])) { if(!empty($this->columnDispatch[$column])) {
$metadata = array(); $metaData = array();
foreach($this->columnDispatch[$column] as $handler) { foreach($this->columnDispatch[$column] as $handler) {
$column_metadata = $handler->getColumnMetadata($this, $column); /**
* @var GridField_ColumnProvider $handler
*/
$columnMetaData = $handler->getColumnMetadata($this, $column);
if(is_array($column_metadata)) { if(is_array($columnMetaData)) {
$metadata = array_merge($metadata, $column_metadata); $metaData = array_merge($metaData, $columnMetaData);
} else { continue;
$methodSignature = get_class($handler) . "::getColumnMetadata()";
throw new LogicException("Non-array response from $methodSignature.");
} }
throw new LogicException(sprintf(
'Non-array response from %s::getColumnMetadata().',
get_class($handler)
));
} }
return $metadata; return $metaData;
} }
throw new InvalidArgumentException("Bad column '$column'");
throw new InvalidArgumentException(sprintf(
'Bad column "%s"',
$column
));
} }
/** /**
* Return how many columns the grid will have * Return how many columns the grid will have.
* *
* @return int * @return int
*/ */
public function getColumnCount() { public function getColumnCount() {
// Build the column dispatch if(!$this->columnDispatch) {
if(!$this->columnDispatch) $this->buildColumnDispatch(); $this->buildColumnDispatch();
}
return count($this->columnDispatch); return count($this->columnDispatch);
} }
/** /**
* Build an columnDispatch that maps a GridField_ColumnProvider to a column * Build an columnDispatch that maps a GridField_ColumnProvider to a column for reference later.
* for reference later
*
*/ */
protected function buildColumnDispatch() { protected function buildColumnDispatch() {
$this->columnDispatch = array(); $this->columnDispatch = array();
@ -691,140 +826,172 @@ class GridField extends FormField {
* This is the action that gets executed when a GridField_AlterAction gets clicked. * This is the action that gets executed when a GridField_AlterAction gets clicked.
* *
* @param array $data * @param array $data
* @param Form $form
* @param SS_HTTPRequest $request
* *
* @return string * @return string
*/ */
public function gridFieldAlterAction($data, $form, SS_HTTPRequest $request) { public function gridFieldAlterAction($data, $form, SS_HTTPRequest $request) {
$html = '';
$data = $request->requestVars(); $data = $request->requestVars();
$name = $this->getName(); $name = $this->getName();
$fieldData = isset($data[$name]) ? $data[$name] : null;
// Update state from client $fieldData = null;
if(isset($data[$name])) {
$fieldData = $data[$name];
}
$state = $this->getState(false); $state = $this->getState(false);
if(isset($fieldData['GridState'])) { if(isset($fieldData['GridState'])) {
$state->setValue($fieldData['GridState']); $state->setValue($fieldData['GridState']);
} }
// Try to execute alter action foreach($data as $dataKey => $dataValue) {
foreach($data as $k => $v) { if(preg_match('/^action_gridFieldAlterAction\?StateID=(.*)/', $dataKey, $matches)) {
if(preg_match('/^action_gridFieldAlterAction\?StateID=(.*)/', $k, $matches)) { $stateChange = Session::get($matches[1]);
$id = $matches[1];
$stateChange = Session::get($id);
$actionName = $stateChange['actionName']; $actionName = $stateChange['actionName'];
$args = isset($stateChange['args']) ? $stateChange['args'] : array(); $arguments = array();
$html = $this->handleAlterAction($actionName, $args, $data);
// A field can optionally return its own HTML if(isset($stateChange['args'])) {
if($html) return $html; $arguments = $stateChange['args'];
};
$html = $this->handleAlterAction($actionName, $arguments, $data);
if($html) {
return $html;
}
} }
} }
switch($request->getHeader('X-Pjax')) { if($request->getHeader('X-Pjax') === 'CurrentField') {
case 'CurrentField': return $this->FieldHolder();
return $this->FieldHolder();
break;
case 'CurrentForm':
return $form->forTemplate();
break;
default:
return $form->forTemplate();
break;
} }
return $form->forTemplate();
} }
/** /**
* Pass an action on the first GridField_ActionProvider that matches the $actionName * Pass an action on the first GridField_ActionProvider that matches the $actionName.
* *
* @param string $actionName * @param string $actionName
* @param mixed $args * @param mixed $arguments
* @param array $data - send data from a form * @param array $data
* *
* @return mixed * @return mixed
*
* @throws InvalidArgumentException * @throws InvalidArgumentException
*/ */
public function handleAlterAction($actionName, $args, $data) { public function handleAlterAction($actionName, $arguments, $data) {
$actionName = strtolower($actionName); $actionName = strtolower($actionName);
foreach($this->getComponents() as $component) {
if(!($component instanceof GridField_ActionProvider)) {
continue;
}
if(in_array($actionName, array_map('strtolower', (array) $component->getActions($this)))) { foreach($this->getComponents() as $component) {
return $component->handleAction($this, $actionName, $args, $data); if($component instanceof GridField_ActionProvider) {
$actions = array_map('strtolower', (array) $component->getActions($this));
if(in_array($actionName, $actions)) {
return $component->handleAction($this, $actionName, $arguments, $data);
}
} }
} }
throw new InvalidArgumentException("Can't handle action '$actionName'");
throw new InvalidArgumentException(sprintf(
'Can\'t handle action "%s"',
$actionName
));
} }
/** /**
* Custom request handler that will check component handlers before proceeding to the default implementation. * Custom request handler that will check component handlers before proceeding to the default
* implementation.
* *
* @todo There is too much code copied from RequestHandler here. * @todo copy less code from RequestHandler.
*
* @param SS_HTTPRequest $request
* @param DataModel $model
*
* @return array|RequestHandler|SS_HTTPResponse|string|void
*
* @throws SS_HTTPResponse_Exception
*/ */
public function handleRequest(SS_HTTPRequest $request, DataModel $model) { public function handleRequest(SS_HTTPRequest $request, DataModel $model) {
if($this->brokenOnConstruct) { if($this->brokenOnConstruct) {
user_error("parent::__construct() needs to be called on {$handlerClass}::__construct()", E_USER_WARNING); user_error(
sprintf(
"parent::__construct() needs to be called on %s::__construct()",
__CLASS__
),
E_USER_WARNING
);
} }
$this->setRequest($request); $this->setRequest($request);
$this->setDataModel($model); $this->setDataModel($model);
$fieldData = $this->getRequest()->requestVar($this->getName()); $fieldData = $this->getRequest()->requestVar($this->getName());
if($fieldData && isset($fieldData['GridState'])) $this->getState(false)->setValue($fieldData['GridState']);
if($fieldData && isset($fieldData['GridState'])) {
$this->getState(false)->setValue($fieldData['GridState']);
}
foreach($this->getComponents() as $component) { foreach($this->getComponents() as $component) {
if(!($component instanceof GridField_URLHandler)) { if($component instanceof GridField_URLHandler && $urlHandlers = $component->getURLHandlers($this)) {
continue; foreach($urlHandlers as $rule => $action) {
} if($params = $request->match($rule, true)) {
// Actions can reference URL parameters.
// e.g. '$Action/$ID/$OtherID' → '$Action'
$urlHandlers = $component->getURLHandlers($this); if($action[0] == '$') {
$action = $params[substr($action, 1)];
if($urlHandlers) foreach($urlHandlers as $rule => $action) {
if($params = $request->match($rule, true)) {
// Actions can reference URL parameters, eg, '$Action/$ID/$OtherID' => '$Action',
if($action[0] == '$') $action = $params[substr($action, 1)];
if(!method_exists($component, 'checkAccessAction') || $component->checkAccessAction($action)) {
if(!$action) {
$action = "index";
} else if(!is_string($action)) {
throw new LogicException("Non-string method name: " . var_export($action, true));
} }
try { if(!method_exists($component, 'checkAccessAction') || $component->checkAccessAction($action)) {
$result = $component->$action($this, $request); if(!$action) {
} catch(SS_HTTPResponse_Exception $responseException) { $action = "index";
$result = $responseException->getResponse();
}
if($result instanceof SS_HTTPResponse && $result->isError()) {
return $result;
}
if($this !== $result && !$request->isEmptyPattern($rule) && is_object($result)
&& $result instanceof RequestHandler
) {
$returnValue = $result->handleRequest($request, $model);
if(is_array($returnValue)) {
throw new LogicException("GridField_URLHandler handlers can't return arrays");
} }
return $returnValue; if(!is_string($action)) {
throw new LogicException(sprintf(
'Non-string method name: %s',
var_export($action, true)
));
}
// If we return some other data, and all the URL is parsed, then return that try {
} else if($request->allParsed()) { $result = $component->$action($this, $request);
return $result; } catch(SS_HTTPResponse_Exception $responseException) {
$result = $responseException->getResponse();
}
// But if we have more content on the URL and we don't know what to do with it, return an error if($result instanceof SS_HTTPResponse && $result->isError()) {
} else { return $result;
return $this->httpError(404, }
"I can't handle sub-URLs of a " . get_class($result) . " object.");
if($this !== $result && !$request->isEmptyPattern($rule) && is_object($result) && $result instanceof RequestHandler) {
$returnValue = $result->handleRequest($request, $model);
if(is_array($returnValue)) {
throw new LogicException(
'GridField_URLHandler handlers can\'t return arrays'
);
}
return $returnValue;
}
if($request->allParsed()) {
return $result;
}
return $this->httpError(
404,
sprintf(
'I can\'t handle sub-URLs of a %s object.',
get_class($result)
)
);
} }
} }
} }
@ -834,6 +1001,9 @@ class GridField extends FormField {
return parent::handleRequest($request, $model); return parent::handleRequest($request, $model);
} }
/**
* {@inheritdoc}
*/
public function saveInto(DataObjectInterface $record) { public function saveInto(DataObjectInterface $record) {
foreach($this->getComponents() as $component) { foreach($this->getComponents() as $component) {
if($component instanceof GridField_SaveHandler) { if($component instanceof GridField_SaveHandler) {
@ -842,18 +1012,61 @@ class GridField extends FormField {
} }
} }
/**
* @param array $content
*
* @return string
*/
protected function getOptionalTableHeader(array $content) {
if($content['header']) {
return FormField::create_tag(
'thead', array(), $content['header']
);
}
return '';
}
/**
* @param array $content
*
* @return string
*/
protected function getOptionalTableBody(array $content) {
if($content['body']) {
return FormField::create_tag(
'tbody', array('class' => 'ss-gridfield-items'), $content['body']
);
}
return '';
}
/**
* @param $content
*
* @return string
*/
protected function getOptionalTableFooter($content) {
if($content['footer']) {
return FormField::create_tag(
'tfoot', array(), $content['footer']
);
}
return '';
}
} }
/** /**
* This class is the base class when you want to have an action that alters * This class is the base class when you want to have an action that alters the state of the
* the state of the {@link GridField}, rendered as a button element. * {@link GridField}, rendered as a button element.
* *
* @package forms * @package forms
* @subpackage fields-gridfield * @subpackage fields-gridfield
*/ */
class GridField_FormAction extends FormAction { class GridField_FormAction extends FormAction {
/** /**
* @var GridField * @var GridField
*/ */
@ -882,7 +1095,7 @@ class GridField_FormAction extends FormAction {
/** /**
* @param GridField $gridField * @param GridField $gridField
* @param string $name * @param string $name
* @param string $label * @param string $title
* @param string $actionName * @param string $actionName
* @param array $args * @param array $args
*/ */
@ -895,19 +1108,20 @@ class GridField_FormAction extends FormAction {
} }
/** /**
* urlencode encodes less characters in percent form than we need - we * Encode all non-word characters.
* need everything that isn't a \w.
* *
* @param string $val * @param string $value
*
* @return string
*/ */
public function nameEncode($val) { public function nameEncode($value) {
return preg_replace_callback('/[^\w]/', array($this, '_nameEncode'), $val); return (string) preg_replace_callback('/[^\w]/', array($this, '_nameEncode'), $value);
} }
/** /**
* The callback for nameEncode * @param array $match
* *
* @param string $val * @return string
*/ */
public function _nameEncode($match) { public function _nameEncode($match) {
return '%' . dechex(ord($match[0])); return '%' . dechex(ord($match[0]));
@ -941,9 +1155,7 @@ class GridField_FormAction extends FormAction {
} }
/** /**
* Calculate the name of the gridfield relative to the Form * Calculate the name of the gridfield relative to the form.
*
* @param GridField $base
* *
* @return string * @return string
*/ */

View File

@ -107,7 +107,7 @@ class GridFieldDeleteAction implements GridField_ColumnProvider, GridField_Actio
if($this->removeRelation) { if($this->removeRelation) {
if(!$record->canEdit()) return; if(!$record->canEdit()) return;
$field = GridField_FormAction::create($gridField, 'UnlinkRelation'.$record->ID, _t('GridAction.UnlinkRelation', "Unlink"), $field = GridField_FormAction::create($gridField, 'UnlinkRelation'.$record->ID, false,
"unlinkrelation", array('RecordID' => $record->ID)) "unlinkrelation", array('RecordID' => $record->ID))
->addExtraClass('gridfield-button-unlink') ->addExtraClass('gridfield-button-unlink')
->setAttribute('title', _t('GridAction.UnlinkRelation', "Unlink")) ->setAttribute('title', _t('GridAction.UnlinkRelation', "Unlink"))
@ -115,14 +115,13 @@ class GridFieldDeleteAction implements GridField_ColumnProvider, GridField_Actio
} else { } else {
if(!$record->canDelete()) return; if(!$record->canDelete()) return;
$field = GridField_FormAction::create($gridField, 'DeleteRecord'.$record->ID, _t('GridAction.Delete', "Delete"), "deleterecord", $field = GridField_FormAction::create($gridField, 'DeleteRecord'.$record->ID, false, "deleterecord",
array('RecordID' => $record->ID)) array('RecordID' => $record->ID))
->addExtraClass('gridfield-button-delete') ->addExtraClass('gridfield-button-delete')
->setAttribute('title', _t('GridAction.Delete', "Delete")) ->setAttribute('title', _t('GridAction.Delete', "Delete"))
->setAttribute('data-icon', 'cross-circle') ->setAttribute('data-icon', 'cross-circle')
->setDescription(_t('GridAction.DELETE_DESCRIPTION','Delete')); ->setDescription(_t('GridAction.DELETE_DESCRIPTION','Delete'));
} }
return $field->Field(); return $field->Field();
} }

View File

@ -19,8 +19,9 @@ require_once 'i18nSSLegacyAdapter.php';
* *
* Templates: * Templates:
* <code> * <code>
* <% _t('MyNamespace.MYENTITY', 'My default natural language value') %> * <%t MyNamespace.MYENTITY 'My default natural language value' %>
* <% sprintf(_t('MyNamespace.MYENTITY','Counting %s things'),$ThingsCount) %> * <%t MyNamespace.MYENTITY 'Counting %s things' s=$ThingsCount %>
* <%t MyNamespace.MYENTITY 'Counting {count} things' count=$ThingsCount %>
* </code> * </code>
* *
* Javascript (see framework/javascript/i18n.js): * Javascript (see framework/javascript/i18n.js):
@ -137,54 +138,56 @@ class i18n extends Object implements TemplateGlobalProvider, Flushable {
* *
* @see Requirements::process_i18n_javascript() * @see Requirements::process_i18n_javascript()
* *
* @deprecated 3.2 Use the "i18n.js_i18n" config setting instead * @deprecated 4.0 Use the "i18n.js_i18n" config setting instead
* @param bool $bool * @param bool $bool
*/ */
public static function set_js_i18n($bool) { public static function set_js_i18n($bool) {
Deprecation::notice('3.2', 'Use the "i18n.js_i18n" config setting instead'); Deprecation::notice('4.0', 'Use the "i18n.js_i18n" config setting instead');
Config::inst()->update('i18n', 'js_i18n', $bool); Config::inst()->update('i18n', 'js_i18n', $bool);
} }
/** /**
* @deprecated 3.2 Use the "i18n.js_i18n" config setting instead * @deprecated 4.0 Use the "i18n.js_i18n" config setting instead
* @return bool * @return bool
*/ */
public static function get_js_i18n() { public static function get_js_i18n() {
Deprecation::notice('3.2', 'Use the "i18n.js_i18n" config setting instead'); Deprecation::notice('4.0', 'Use the "i18n.js_i18n" config setting instead');
return Config::inst()->get('i18n', 'js_i18n'); return Config::inst()->get('i18n', 'js_i18n');
} }
/** /**
* @deprecated 3.2 Use the "i18n.date_format" config setting instead * @deprecated 4.0 Use the "i18n.date_format" config setting instead
* @param string ISO date format * @param string ISO date format
*/ */
public static function set_date_format($format) { public static function set_date_format($format) {
Deprecation::notice('3.2', 'Use the "i18n.date_format" config setting instead'); Deprecation::notice('4.0', 'Use the "i18n.date_format" config setting instead');
Config::inst()->update('i18n', 'date_format', $format); Config::inst()->update('i18n', 'date_format', $format);
} }
/** /**
* @deprecated since version 4.0
* @return string ISO date format * @return string ISO date format
*/ */
public static function get_date_format() { public static function get_date_format() {
Deprecation::notice('3.2', 'Use the "i18n.date_format" config setting instead'); Deprecation::notice('4.0', 'Use the "i18n.date_format" config setting instead');
return Config::inst()->get('i18n', 'date_format'); return Config::inst()->get('i18n', 'date_format');
} }
/** /**
* @deprecated 3.2 Use the "i18n.time_format" config setting instead * @deprecated 4.0 Use the "i18n.time_format" config setting instead
* @param string ISO time format * @param string ISO time format
*/ */
public static function set_time_format($format) { public static function set_time_format($format) {
Deprecation::notice('3.2', 'Use the "i18n.time_format" config setting instead'); Deprecation::notice('4.0', 'Use the "i18n.time_format" config setting instead');
Config::inst()->update('i18n', 'time_format', $format); Config::inst()->update('i18n', 'time_format', $format);
} }
/** /**
* @deprecated since version 4.0
* @return string ISO time format * @return string ISO time format
*/ */
public static function get_time_format() { public static function get_time_format() {
Deprecation::notice('3.2', 'Use the "i18n.time_format" config setting instead'); Deprecation::notice('4.0', 'Use the "i18n.time_format" config setting instead');
return Config::inst()->get('i18n', 'time_format'); return Config::inst()->get('i18n', 'time_format');
} }
@ -2224,10 +2227,11 @@ class i18n extends Object implements TemplateGlobalProvider, Flushable {
/** /**
* Get a list of locales (code => language and country) * Get a list of locales (code => language and country)
* *
* @deprecated since version 4.0
* @return list of languages in the form 'code' => 'name' * @return list of languages in the form 'code' => 'name'
*/ */
public static function get_locale_list() { public static function get_locale_list() {
Deprecation::notice('3.2', 'Use the "i18n.all_locales" config setting instead'); Deprecation::notice('4.0', 'Use the "i18n.all_locales" config setting instead');
return (array)Config::inst()->get('i18n', 'all_locales'); return (array)Config::inst()->get('i18n', 'all_locales');
} }

View File

@ -10,6 +10,9 @@
* For any statics containing natural language, never use the static directly - * For any statics containing natural language, never use the static directly -
* always wrap it in a getter. * always wrap it in a getter.
* *
* Classes must be able to be constructed without mandatory arguments, otherwise
* this interface will have no effect.
*
* @package framework * @package framework
* @subpackage i18n * @subpackage i18n
* @uses i18nTextCollector->collectFromEntityProviders() * @uses i18nTextCollector->collectFromEntityProviders()

View File

@ -27,15 +27,28 @@
*/ */
class i18nTextCollector extends Object { class i18nTextCollector extends Object {
/**
* Default (master) locale
*
* @var string
*/
protected $defaultLocale; protected $defaultLocale;
/** /**
* @var string $basePath The directory base on which the collector should act. * The directory base on which the collector should act.
* Usually the webroot set through {@link Director::baseFolder()}. * Usually the webroot set through {@link Director::baseFolder()}.
*
* @todo Fully support changing of basePath through {@link SSViewer} and {@link ManifestBuilder} * @todo Fully support changing of basePath through {@link SSViewer} and {@link ManifestBuilder}
*
* @var string
*/ */
public $basePath; public $basePath;
/**
* Save path
*
* @var string
*/
public $baseSavePath; public $baseSavePath;
/** /**
@ -43,99 +56,267 @@ class i18nTextCollector extends Object {
*/ */
protected $writer; protected $writer;
/**
* List of file extensions to parse
*
* @var array
*/
protected $fileExtensions = array('php', 'ss');
/** /**
* @param $locale * @param $locale
*/ */
public function __construct($locale = null) { public function __construct($locale = null) {
$this->defaultLocale = ($locale) ? $locale : i18n::get_lang_from_locale(i18n::default_locale()); $this->defaultLocale = $locale
? $locale
: i18n::get_lang_from_locale(i18n::default_locale());
$this->basePath = Director::baseFolder(); $this->basePath = Director::baseFolder();
$this->baseSavePath = Director::baseFolder(); $this->baseSavePath = Director::baseFolder();
parent::__construct(); parent::__construct();
} }
/**
* Assign a writer
*
* @param i18nTextCollector_Writer $writer
*/
public function setWriter($writer) { public function setWriter($writer) {
$this->writer = $writer; $this->writer = $writer;
} }
/**
* Gets the currently assigned writer, or the default if none is specified.
*
* @return i18nTextCollector_Writer
*/
public function getWriter() { public function getWriter() {
if(!$this->writer) $this->writer = new i18nTextCollector_Writer_RailsYaml(); if(!$this->writer) {
$this->setWriter(Injector::inst()->get('i18nTextCollector_Writer'));
}
return $this->writer; return $this->writer;
} }
/** /**
* This is the main method to build the master string tables with the original strings. * This is the main method to build the master string tables with the
* It will search for existent modules that use the i18n feature, parse the _t() calls * original strings. It will search for existent modules that use the
* and write the resultant files in the lang folder of each module. * i18n feature, parse the _t() calls and write the resultant files
* in the lang folder of each module.
* *
* @uses DataObject->collectI18nStatics() * @uses DataObject->collectI18nStatics()
* *
* @param array $restrictToModules * @param array $restrictToModules
* @param array $mergeWithExisting Merge new master strings with existing ones * @param bool $mergeWithExisting Merge new master strings with existing
* already defined in language files, rather than replacing them. This can be useful * ones already defined in language files, rather than replacing them.
* for long-term maintenance of translations across releases, because it allows * This can be useful for long-term maintenance of translations across
* "translation backports" to older releases without removing strings these older releases * releases, because it allows "translation backports" to older releases
* still rely on. * without removing strings these older releases still rely on.
*/ */
public function run($restrictToModules = null, $mergeWithExisting = false) { public function run($restrictToModules = null, $mergeWithExisting = false) {
$entitiesByModule = $this->collect($restrictToModules, $mergeWithExisting); $entitiesByModule = $this->collect($restrictToModules, $mergeWithExisting);
if(empty($entitiesByModule)) {
return;
}
// Write each module language file // Write each module language file
if($entitiesByModule) foreach($entitiesByModule as $module => $entities) { foreach($entitiesByModule as $module => $entities) {
$this->getWriter()->write($entities, $this->defaultLocale, $this->baseSavePath . '/' . $module); // Skip empty translations
if(empty($entities)) {
continue;
}
// Clean sorting prior to writing
ksort($entities);
$path = $this->baseSavePath . '/' . $module;
$this->getWriter()->write($entities, $this->defaultLocale, $path);
} }
} }
public function collect($restrictToModules = null, $mergeWithExisting = false) { /**
$modules = scandir($this->basePath); * Gets the list of modules in this installer
$themeFolders = array(); *
* @param string $directory Path to look in
* @return array List of modules as paths relative to base
*/
protected function getModules($directory) {
// Include self as head module
$modules = array();
// A master string tables array (one mst per module) // Get all standard modules
$entitiesByModule = array(); foreach(glob($directory."/*", GLOB_ONLYDIR) as $path) {
// Check for _config
if(!is_file("$path/_config.php") && !is_dir("$path/_config")) {
continue;
}
$modules[] = basename($path);
}
foreach($modules as $index => $module){ // Get all themes
if($module != 'themes') continue; foreach(glob($directory."/themes/*", GLOB_ONLYDIR) as $path) {
else { // Check for templates
$themes = scandir($this->basePath."/themes"); if(is_dir("$path/templates")) {
if(count($themes)){ $modules[] = 'themes/'.basename($path);
foreach($themes as $theme) {
if(is_dir($this->basePath."/themes/".$theme)
&& substr($theme,0,1) != '.'
&& is_dir($this->basePath."/themes/".$theme."/templates")){
$themeFolders[] = 'themes/'.$theme;
}
}
}
$themesInd = $index;
} }
} }
if(isset($themesInd)) { return $modules;
unset($modules[$themesInd]); }
/**
* Extract all strings from modules and return these grouped by module name
*
* @param array $restrictToModules
* @param bool $mergeWithExisting
* @return array
*/
public function collect($restrictToModules = array(), $mergeWithExisting = false) {
$entitiesByModule = $this->getEntitiesByModule();
// Resolve conflicts between duplicate keys across modules
$entitiesByModule = $this->resolveDuplicateConflicts($entitiesByModule);
// Optionally merge with existing master strings
if($mergeWithExisting) {
$entitiesByModule = $this->mergeWithExisting($entitiesByModule);
} }
$modules = array_merge($modules, $themeFolders); // Restrict modules we update to just the specified ones (if any passed)
if(!empty($restrictToModules)) {
foreach (array_diff(array_keys($entitiesByModule), $restrictToModules) as $module) {
unset($entitiesByModule[$module]);
}
}
return $entitiesByModule;
}
foreach($modules as $module) { /**
// Only search for calls in folder with a _config.php file or _config folder * Resolve conflicts between duplicate keys across modules
// (which means they are modules, including themes folder) *
$isValidModuleFolder = ( * @param array $entitiesByModule List of all modules with keys
is_dir("$this->basePath/$module") * @return array Filtered listo of modules with duplicate keys unassigned
&& substr($module,0,1) != '.' */
&& ( protected function resolveDuplicateConflicts($entitiesByModule) {
is_file("$this->basePath/$module/_config.php") // Find all keys that exist across multiple modules
|| is_dir("$this->basePath/$module/_config") $conflicts = $this->getConflicts($entitiesByModule);
) foreach($conflicts as $conflict) {
) || ( // Determine if we can narrow down the ownership
substr($module,0,7) == 'themes/' $bestModule = $this->getBestModuleForKey($entitiesByModule, $conflict);
&& is_dir("$this->basePath/$module") if(!$bestModule) {
continue;
}
// Remove foreign duplicates
foreach($entitiesByModule as $module => $entities) {
if($module !== $bestModule) {
unset($entitiesByModule[$module][$conflict]);
}
}
}
return $entitiesByModule;
}
/**
* Find all keys in the entity list that are duplicated across modules
*
* @param array $entitiesByModule
* @return array List of keys
*/
protected function getConflicts($entitiesByModule) {
$modules = array_keys($entitiesByModule);
$allConflicts = array();
// bubble-compare each group of modules
for($i = 0; $i < count($modules) - 1; $i++) {
$left = array_keys($entitiesByModule[$modules[$i]]);
for($j = $i+1; $j < count($modules); $j++) {
$right = array_keys($entitiesByModule[$modules[$j]]);
$conflicts = array_intersect($left, $right);
$allConflicts = array_merge($allConflicts, $conflicts);
}
}
return array_unique($allConflicts);
}
/**
* Determine the best module to be given ownership over this key
*
* @param array $entitiesByModule
* @param string $key
* @return string Best module, if found
*/
protected function getBestModuleForKey($entitiesByModule, $key) {
// Check classes
$class = current(explode('.', $key));
$owner = i18n::get_owner_module($class);
if($owner) {
return $owner;
}
// @todo - How to determine ownership of templates? Templates can
// exist in multiple locations with the same name.
// Display notice if not found
Debug::message(
"Duplicate key {$key} detected in multiple modules with no obvious owner",
false
);
// Fall back to framework then cms modules
foreach(array('framework', 'cms') as $module) {
if(isset($entitiesByModule[$module][$key])) {
return $module;
}
}
// Do nothing
return null;
}
/**
* Merge all entities with existing strings
*
* @param array $entitiesByModule
* @return array
*/
protected function mergeWithExisting($entitiesByModule) {
// TODO Support all defined source formats through i18n::get_translators().
// Currently not possible because adapter instances can't be fully reset through the Zend API,
// meaning master strings accumulate across modules
foreach($entitiesByModule as $module => $entities) {
$adapter = Injector::inst()->create('i18nRailsYamlAdapter');
$fileName = $adapter->getFilenameForLocale($this->defaultLocale);
$masterFile = "{$this->basePath}/{$module}/lang/{$fileName}";
if(!file_exists($masterFile)) {
continue;
}
$adapter->addTranslation(array(
'content' => $masterFile,
'locale' => $this->defaultLocale
));
$entitiesByModule[$module] = array_merge(
array_map(
// Transform each master string from scalar value to array of strings
function($v) {return array($v);},
$adapter->getMessages($this->defaultLocale)
),
$entities
); );
}
return $entitiesByModule;
}
if(!$isValidModuleFolder) continue; /**
* Collect all entities grouped by module
*
* @return array
*/
protected function getEntitiesByModule() {
// A master string tables array (one mst per module)
$entitiesByModule = array();
$modules = $this->getModules($this->basePath);
foreach($modules as $module) {
// we store the master string tables // we store the master string tables
$processedEntities = $this->processModule($module); $processedEntities = $this->processModule($module);
if(isset($entitiesByModule[$module])) { if(isset($entitiesByModule[$module])) {
$entitiesByModule[$module] = array_merge_recursive($entitiesByModule[$module], $processedEntities); $entitiesByModule[$module] = array_merge_recursive($entitiesByModule[$module], $processedEntities);
} else { } else {
@ -143,54 +324,23 @@ class i18nTextCollector extends Object {
} }
// extract all entities for "foreign" modules (fourth argument) // extract all entities for "foreign" modules (fourth argument)
// @see CMSMenu::provideI18nEntities for an example usage
foreach($entitiesByModule[$module] as $fullName => $spec) { foreach($entitiesByModule[$module] as $fullName => $spec) {
if(isset($spec[2]) && $spec[2] && $spec[2] != $module) { if(!empty($spec[2]) && $spec[2] !== $module) {
$othermodule = $spec[2]; $othermodule = $spec[2];
if(!isset($entitiesByModule[$othermodule])) $entitiesByModule[$othermodule] = array(); if(!isset($entitiesByModule[$othermodule])) {
$entitiesByModule[$othermodule] = array();
}
unset($spec[2]); unset($spec[2]);
$entitiesByModule[$othermodule][$fullName] = $spec; $entitiesByModule[$othermodule][$fullName] = $spec;
unset($entitiesByModule[$module][$fullName]); unset($entitiesByModule[$module][$fullName]);
} }
} }
// Optionally merge with existing master strings
// TODO Support all defined source formats through i18n::get_translators().
// Currently not possible because adapter instances can't be fully reset through the Zend API,
// meaning master strings accumulate across modules
if($mergeWithExisting) {
$adapter = Injector::inst()->create(
'i18nRailsYamlAdapter',
array('locale' => 'auto')
);
$masterFile = "{$this->basePath}/{$module}/lang/"
. $adapter->getFilenameForLocale($this->defaultLocale);
if(!file_exists($masterFile)) continue;
$adapter->addTranslation(array(
'content' => $masterFile,
'locale' => $this->defaultLocale
));
$entitiesByModule[$module] = array_merge(
array_map(
// Transform each master string from scalar value to array of strings
function($v) {return array($v);},
$adapter->getMessages($this->defaultLocale)
),
$entitiesByModule[$module]
);
}
} }
// Restrict modules we update to just the specified ones (if any passed)
if($restrictToModules && count($restrictToModules)) {
foreach (array_diff(array_keys($entitiesByModule), $restrictToModules) as $module) {
unset($entitiesByModule[$module]);
}
}
return $entitiesByModule; return $entitiesByModule;
} }
public function write($module, $entities) { public function write($module, $entities) {
$this->getWriter()->write($entities, $this->defaultLocale, $this->baseSavePath . '/' . $module); $this->getWriter()->write($entities, $this->defaultLocale, $this->baseSavePath . '/' . $module);
return $this; return $this;
@ -200,38 +350,31 @@ class i18nTextCollector extends Object {
* Builds a master string table from php and .ss template files for the module passed as the $module param * Builds a master string table from php and .ss template files for the module passed as the $module param
* @see collectFromCode() and collectFromTemplate() * @see collectFromCode() and collectFromTemplate()
* *
* @param string $module A module's name or just 'themes' * @param string $module A module's name or just 'themes/<themename>'
* @return array $entities An array of entities found in the files that comprise the module * @return array An array of entities found in the files that comprise the module
* @todo Why the type juggling for $this->collectFromBlah()? They always return arrays.
*/ */
protected function processModule($module) { protected function processModule($module) {
$entities = array(); $entities = array();
// Search for calls in code files if these exists // Search for calls in code files if these exists
$fileList = array(); $fileList = $this->getFileListForModule($module);
if(is_dir("$this->basePath/$module/code")) {
$fileList = $this->getFilesRecursive("$this->basePath/$module/code");
} else if($module == FRAMEWORK_DIR || substr($module, 0, 7) == 'themes/') {
// framework doesn't have the usual module structure, so we'll scan all subfolders
$fileList = $this->getFilesRecursive("$this->basePath/$module", null, null, '/\/(tests|dev)$/');
}
foreach($fileList as $filePath) { foreach($fileList as $filePath) {
// exclude ss-templates, they're scanned separately $extension = pathinfo($filePath, PATHINFO_EXTENSION);
if(substr($filePath,-3) == 'php') { $content = file_get_contents($filePath);
$content = file_get_contents($filePath); // Filter based on extension
$entities = array_merge($entities,(array)$this->collectFromCode($content, $module)); if($extension === 'php') {
$entities = array_merge($entities, (array)$this->collectFromEntityProviders($filePath, $module)); $entities = array_merge(
} $entities,
} $this->collectFromCode($content, $module),
$this->collectFromEntityProviders($filePath, $module)
// Search for calls in template files if these exists );
if(is_dir("$this->basePath/$module/")) { } elseif($extension === 'ss') {
$fileList = $this->getFilesRecursive("$this->basePath/$module/", null, 'ss');
foreach($fileList as $index => $filePath) {
$content = file_get_contents($filePath);
// templates use their filename as a namespace // templates use their filename as a namespace
$namespace = basename($filePath); $namespace = basename($filePath);
$entities = array_merge($entities, (array)$this->collectFromTemplate($content, $module, $namespace)); $entities = array_merge(
$entities,
$this->collectFromTemplate($content, $module, $namespace)
);
} }
} }
@ -241,11 +384,44 @@ class i18nTextCollector extends Object {
return $entities; return $entities;
} }
/**
* Retrieves the list of files for this module
*
* @param type $module
* @return array List of files to parse
*/
protected function getFileListForModule($module) {
$modulePath = "{$this->basePath}/{$module}";
// Search all .ss files in themes
if(stripos($module, 'themes/') === 0) {
return $this->getFilesRecursive($modulePath, null, 'ss');
}
// If Framework or non-standard module structure, so we'll scan all subfolders
if($module === FRAMEWORK_DIR || !is_dir("{$modulePath}/code")) {
return $this->getFilesRecursive($modulePath);
}
// Get code files
$files = $this->getFilesRecursive("{$modulePath}/code", null, 'php');
// Search for templates in this module
if(is_dir("{$modulePath}/templates")) {
$templateFiles = $this->getFilesRecursive("{$modulePath}/templates", null, 'ss');
} else {
$templateFiles = $this->getFilesRecursive($modulePath, null, 'ss');
}
return array_merge($files, $templateFiles);
}
/** /**
* Extracts translatables from .php files. * Extracts translatables from .php files.
* *
* @param string $content The text content of a parsed template-file * @param string $content The text content of a parsed template-file
* @param string $module Module's name or 'themes' * @param string $module Module's name or 'themes'. Could also be a namespace
* Generated by templates includes. E.g. 'UploadField.ss'
* @return array $entities An array of entities representing the extracted translation function calls in code * @return array $entities An array of entities representing the extracted translation function calls in code
*/ */
public function collectFromCode($content, $module) { public function collectFromCode($content, $module) {
@ -328,33 +504,10 @@ class i18nTextCollector extends Object {
* @param string $module Module's name or 'themes' * @param string $module Module's name or 'themes'
* @param string $fileName The name of a template file when method is used in self-referencing mode * @param string $fileName The name of a template file when method is used in self-referencing mode
* @return array $entities An array of entities representing the extracted template function calls * @return array $entities An array of entities representing the extracted template function calls
*
* @todo Why the type juggling for $this->collectFromTemplate()? It always returns an array.
*/ */
public function collectFromTemplate($content, $fileName, $module, &$parsedFiles = array()) { public function collectFromTemplate($content, $fileName, $module, &$parsedFiles = array()) {
$entities = array();
// Search for included templates
preg_match_all('/<' . '% include +([A-Za-z0-9_]+) +%' . '>/', $content, $regs, PREG_SET_ORDER);
foreach($regs as $reg) {
$includeName = $reg[1];
$includeFileName = "{$includeName}.ss";
$filePath = SSViewer::getTemplateFileByType($includeName, 'Includes');
if(!$filePath) $filePath = SSViewer::getTemplateFileByType($includeName, 'main');
if($filePath && !in_array($filePath, $parsedFiles)) {
$parsedFiles[] = $filePath;
$includeContent = file_get_contents($filePath);
$entities = array_merge(
$entities,
(array)$this->collectFromTemplate($includeContent, $module, $includeFileName, $parsedFiles)
);
}
// @todo Will get massively confused if you include the includer -> infinite loop
}
// use parser to extract <%t style translatable entities // use parser to extract <%t style translatable entities
$translatables = i18nTextCollector_Parser::GetTranslatables($content); $entities = i18nTextCollector_Parser::GetTranslatables($content);
$entities = array_merge($entities,(array)$translatables);
// use the old method of getting _t() style translatable entities // use the old method of getting _t() style translatable entities
// Collect in actual template // Collect in actual template
@ -374,31 +527,34 @@ class i18nTextCollector extends Object {
} }
/** /**
* Allows classes which implement i18nEntityProvider to provide
* additional translation strings.
*
* Not all classes can be instanciated without mandatory arguments,
* so entity collection doesn't work for all SilverStripe classes currently
*
* @uses i18nEntityProvider * @uses i18nEntityProvider
* @param string $filePath
* @param string $module
* @return array
*/ */
public function collectFromEntityProviders($filePath, $module = null) { public function collectFromEntityProviders($filePath, $module = null) {
$entities = array(); $entities = array();
// HACK Ugly workaround to avoid "Cannot redeclare class PHPUnit_Framework_TestResult" error
// when running text collector with PHPUnit 3.4. There really shouldn't be any dependencies
// here, but the class reflection enforces autloading of seemingly unrelated classes.
// The main problem here is the CMSMenu class, which iterates through test classes,
// which in turn trigger autoloading of PHPUnit.
$phpunitwrapper = PhpUnitWrapper::inst();
$phpunitwrapper->init();
$classes = ClassInfo::classes_for_file($filePath); $classes = ClassInfo::classes_for_file($filePath);
if($classes) foreach($classes as $class) { foreach($classes as $class) {
// Not all classes can be instanciated without mandatory arguments, // Skip non-implementing classes
// so entity collection doesn't work for all SilverStripe classes currently if(!class_exists($class) || !in_array('i18nEntityProvider', class_implements($class))) {
// Requires PHP 5.1+ continue;
if(class_exists($class) && in_array('i18nEntityProvider', class_implements($class))) {
$reflectionClass = new ReflectionClass($class);
if($reflectionClass->isAbstract()) continue;
$obj = singleton($class);
$entities = array_merge($entities,(array)$obj->provideI18nEntities());
} }
// Skip abstract classes
$reflectionClass = new ReflectionClass($class);
if($reflectionClass->isAbstract()) {
continue;
}
$obj = singleton($class);
$entities = array_merge($entities, (array)$obj->provideI18nEntities());
} }
ksort($entities); ksort($entities);
@ -442,30 +598,35 @@ class i18nTextCollector extends Object {
* *
* @param string $folder base directory to scan (will scan recursively) * @param string $folder base directory to scan (will scan recursively)
* @param array $fileList Array to which potential files will be appended * @param array $fileList Array to which potential files will be appended
* @param string $type Optional, "php" or "ss" * @param string $type Optional, "php" or "ss" only
* @param string $folderExclude Regular expression matching folder names to exclude * @param string $folderExclude Regular expression matching folder names to exclude
* @return array $fileList An array of files * @return array $fileList An array of files
*/ */
protected function getFilesRecursive($folder, $fileList = null, $type = null, $folderExclude = null) { protected function getFilesRecursive($folder, $fileList = array(), $type = null, $folderExclude = '/\/(tests)$/') {
if(!$folderExclude) $folderExclude = '/\/(tests)$/'; if(!$fileList) {
if(!$fileList) $fileList = array(); $fileList = array();
$items = scandir($folder); }
$isValidFolder = ( // Skip ignored folders
!in_array('_manifest_exclude', $items) if(is_file("{$folder}/_manifest_exclude") || preg_match($folderExclude, $folder)) {
&& !preg_match($folderExclude, $folder) return $fileList;
); }
if($items && $isValidFolder) foreach($items as $item) { foreach(glob($folder.'/*') as $path) {
if(substr($item,0,1) == '.') continue; // Recurse if directory
if(substr($item,-4) == '.php' && (!$type || $type == 'php')) { if(is_dir($path)) {
$fileList[substr($item,0,-4)] = "$folder/$item";
} else if(substr($item,-3) == '.ss' && (!$type || $type == 'ss')) {
$fileList[$item] = "$folder/$item";
} else if(is_dir("$folder/$item")) {
$fileList = array_merge( $fileList = array_merge(
$fileList, $fileList,
$this->getFilesRecursive("$folder/$item", $fileList, $type, $folderExclude) $this->getFilesRecursive($path, $fileList, $type, $folderExclude)
); );
continue;
}
// Check if this extension is included
$extension = pathinfo($path, PATHINFO_EXTENSION);
if(in_array($extension, $this->fileExtensions)
&& (!$type || $type === $extension)
) {
$fileList[$path] = $path;
} }
} }
return $fileList; return $fileList;
@ -691,7 +852,9 @@ class i18nTextCollector_Parser extends SSTemplateParser {
// Run the parser and throw away the result // Run the parser and throw away the result
$parser = new i18nTextCollector_Parser($template); $parser = new i18nTextCollector_Parser($template);
if(substr($template, 0,3) == pack("CCC", 0xef, 0xbb, 0xbf)) $parser->pos = 3; if(substr($template, 0,3) == pack("CCC", 0xef, 0xbb, 0xbf)) {
$parser->pos = 3;
}
$parser->match_TopTemplate(); $parser->match_TopTemplate();
return self::$entities; return self::$entities;

View File

@ -7,22 +7,59 @@
IgnoreTabState: false, IgnoreTabState: false,
onadd: function() { onadd: function() {
var hash = window.location.hash;
// Can't name redraw() as it clashes with other CMS entwine classes // Can't name redraw() as it clashes with other CMS entwine classes
this.redrawTabs(); this.redrawTabs();
if (hash !== '') {
this.openTabFromURL(hash);
}
this._super(); this._super();
}, },
onremove: function() { onremove: function() {
if(this.data('tabs')) this.tabs('destroy'); if(this.data('tabs')) this.tabs('destroy');
this._super(); this._super();
}, },
redrawTabs: function() { redrawTabs: function() {
this.rewriteHashlinks(); this.rewriteHashlinks();
this.tabs(); this.tabs();
}, },
/** /**
* Ensure hash links are prefixed with the current page URL, * @func openTabFromURL
* otherwise jQuery interprets them as being external. * @param {string} hash
* @desc Allows linking to a specific tab.
*/
openTabFromURL: function (hash) {
var $trigger;
// Make sure the hash relates to a valid tab.
$.each(this.find('.cms-panel-link'), function () {
// The hash in in the button's href and there is exactly one tab with that id.
if (this.href.indexOf(hash) !== -1 && $(hash).length === 1) {
$trigger = $(this);
return false; // break the loop
}
});
// If there's no tab, it means the hash is invalid, so do nothing.
if ($trigger === void 0) {
return;
}
// Switch to the correct tab when AJAX loading completes.
$(window).one('ajaxComplete', function () {
$trigger.click();
});
},
/**
* @func rewriteHashlinks
* @desc Ensure hash links are prefixed with the current page URL, otherwise jQuery interprets them as being external.
*/ */
rewriteHashlinks: function() { rewriteHashlinks: function() {
$(this).find('ul a').each(function() { $(this).find('ul a').each(function() {

View File

@ -60,8 +60,8 @@ en:
ERRORNOTREC: 'That username / password isn''t recognised' ERRORNOTREC: 'That username / password isn''t recognised'
Boolean: Boolean:
ANY: Any ANY: Any
NOANSWER: No NOANSWER: 'No'
YESANSWER: Yes YESANSWER: 'Yes'
CMSLoadingScreen_ss: CMSLoadingScreen_ss:
LOADING: Loading... LOADING: Loading...
REQUIREJS: 'The CMS requires that you have JavaScript enabled.' REQUIREJS: 'The CMS requires that you have JavaScript enabled.'
@ -81,8 +81,8 @@ en:
HELLO: Hi HELLO: Hi
PASSWORD: Password PASSWORD: Password
CheckboxField: CheckboxField:
NOANSWER: No NOANSWER: 'No'
YESANSWER: Yes YESANSWER: 'Yes'
CheckboxFieldSetField: CheckboxFieldSetField:
SOURCE_VALIDATION: 'Please select a value within the list provided. {value} is not a valid option' SOURCE_VALIDATION: 'Please select a value within the list provided. {value} is not a valid option'
CMSMemberLoginForm: CMSMemberLoginForm:
@ -259,7 +259,7 @@ en:
many_many_Members: Members many_many_Members: Members
GroupImportForm: 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>' 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>'
Help2: "<div class=\"advanced\">\n <h4>Advanced usage</h4>\n <ul>\n <li>Allowed columns: <em>%s</em></li>\n <li>Existing groups are matched by their unique <em>Code</em> value, and updated with any new values from the \n imported file</li>\n <li>Group hierarchies can be created by using a <em>ParentCode</em> column.</li>\n <li>Permission codes can be assigned by the <em>PermissionCode</em> column. Existing permission codes are not\n cleared.</li>\n </ul>\n</div>" Help2: "<div class=\"advanced\">\n <h4>Advanced usage</h4>\n <ul>\n <li>Allowed columns: <em>%s</em></li>\n <li>Existing groups are matched by their unique <em>Code</em> value, and updated with any new values from the\n imported file</li>\n <li>Group hierarchies can be created by using a <em>ParentCode</em> column.</li>\n <li>Permission codes can be assigned by the <em>PermissionCode</em> column. Existing permission codes are not\n cleared.</li>\n </ul>\n</div>"
ResultCreated: 'Created {count} groups' ResultCreated: 'Created {count} groups'
ResultDeleted: 'Deleted %d groups' ResultDeleted: 'Deleted %d groups'
ResultUpdated: 'Updated %d groups' ResultUpdated: 'Updated %d groups'
@ -313,6 +313,7 @@ en:
URL: URL URL: URL
URLNOTANOEMBEDRESOURCE: 'The URL ''{url}'' could not be turned into a media resource.' URLNOTANOEMBEDRESOURCE: 'The URL ''{url}'' could not be turned into a media resource.'
UpdateMEDIA: 'Update Media' UpdateMEDIA: 'Update Media'
SUBJECT: 'Email subject'
Image: Image:
PLURALNAME: Files PLURALNAME: Files
SINGULARNAME: File SINGULARNAME: File
@ -330,7 +331,7 @@ en:
PERMAGAIN: 'You have been logged out of the CMS. If you would like to log in again, enter a username and password below.' PERMAGAIN: 'You have been logged out of the CMS. If you would like to log in again, enter a username and password below.'
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' 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'
PERMDEFAULT: 'Please choose an authentication method and enter your credentials to access the CMS.' PERMDEFAULT: 'Please choose an authentication method and enter your credentials to access the CMS.'
PLEASESAVE: 'Please Save Page: This page could not be upated because it hasn''t been saved yet.' PLEASESAVE: 'Please Save Page: This page could not be updated because it hasn''t been saved yet.'
PreviewButton: Preview PreviewButton: Preview
REORGANISATIONSUCCESSFUL: 'Reorganised the site tree successfully.' REORGANISATIONSUCCESSFUL: 'Reorganised the site tree successfully.'
SAVEDUP: Saved. SAVEDUP: Saved.
@ -342,7 +343,7 @@ en:
Hello: Hi Hello: Hi
LOGOUT: 'Log out' LOGOUT: 'Log out'
ListboxField: ListboxField:
SOURCE_VALIDATION: 'Please select a value within the list provided. {value} is not a valid option' SOURCE_VALIDATION: 'Please select a value within the list provided. %s is not a valid option'
LoginAttempt: LoginAttempt:
Email: 'Email Address' Email: 'Email Address'
IP: 'IP Address' IP: 'IP Address'
@ -585,3 +586,5 @@ en:
UPLOADSINTO: 'saves into /{path}' UPLOADSINTO: 'saves into /{path}'
Versioned: Versioned:
has_many_Versions: Versions has_many_Versions: Versions
CheckboxSetField:
SOURCE_VALIDATION: 'Please select a value within the list provided. ''{value}'' is not a valid option'

View File

@ -67,37 +67,44 @@ if(!empty($_SERVER['HTTP_X_ORIGINAL_URL'])) {
*/ */
global $url; global $url;
// PHP 5.4's built-in webserver uses this // Helper to safely parse and load a querystring fragment
if (php_sapi_name() == 'cli-server') { $parseQuery = function($query) {
$url = $_SERVER['REQUEST_URI']; parse_str($query, $_GET);
if ($_GET) $_REQUEST = array_merge((array)$_REQUEST, (array)$_GET);
};
// Querystring args need to be explicitly parsed // Apache rewrite rules and IIS use this
if(strpos($url,'?') !== false) { if (isset($_GET['url']) && php_sapi_name() !== 'cli-server') {
list($url, $query) = explode('?',$url,2);
parse_str($query, $_GET); // Prevent injection of url= querystring argument by prioritising any leading url argument
if ($_GET) $_REQUEST = array_merge((array)$_REQUEST, (array)$_GET); if(isset($_SERVER['QUERY_STRING']) &&
preg_match('/^(?<url>url=[^&?]*)(?<query>.*[&?]url=.*)$/', $_SERVER['QUERY_STRING'], $results)
) {
$queryString = $results['query'].'&'.$results['url'];
$parseQuery($queryString);
} }
// Pass back to the webserver for files that exist
if(file_exists(BASE_PATH . $url) && is_file(BASE_PATH . $url)) return false;
// Apache rewrite rules use this
} else if (isset($_GET['url'])) {
$url = $_GET['url']; $url = $_GET['url'];
// IIS includes get variables in url // IIS includes get variables in url
$i = strpos($url, '?'); $i = strpos($url, '?');
if($i !== false) { if($i !== false) {
$url = substr($url, 0, $i); $url = substr($url, 0, $i);
} }
// Lighttpd uses this // Lighttpd and PHP 5.4's built-in webserver use this
} else { } else {
if(strpos($_SERVER['REQUEST_URI'],'?') !== false) { $url = $_SERVER['REQUEST_URI'];
list($url, $query) = explode('?', $_SERVER['REQUEST_URI'], 2);
parse_str($query, $_GET); // Querystring args need to be explicitly parsed
if ($_GET) $_REQUEST = array_merge((array)$_REQUEST, (array)$_GET); if(strpos($url,'?') !== false) {
} else { list($url, $query) = explode('?',$url,2);
$url = $_SERVER["REQUEST_URI"]; $parseQuery($query);
}
// Pass back to the webserver for files that exist
if(php_sapi_name() === 'cli-server' && file_exists(BASE_PATH . $url) && is_file(BASE_PATH . $url)) {
return false;
} }
} }

View File

@ -50,10 +50,10 @@ class DB {
} }
/** /**
* @deprecated since version 3.3 Use DB::set_conn instead * @deprecated since version 4.0 Use DB::set_conn instead
*/ */
public static function setConn(SS_Database $connection, $name = 'default') { public static function setConn(SS_Database $connection, $name = 'default') {
Deprecation::notice('3.3', 'Use DB::set_conn instead'); Deprecation::notice('4.0', 'Use DB::set_conn instead');
self::set_conn($connection, $name); self::set_conn($connection, $name);
} }
@ -71,10 +71,10 @@ class DB {
} }
/** /**
* @deprecated since version 3.3 Use DB::get_conn instead * @deprecated since version 4.0 Use DB::get_conn instead
*/ */
public static function getConn($name = 'default') { public static function getConn($name = 'default') {
Deprecation::notice('3.3', 'Use DB::get_conn instead'); Deprecation::notice('4.0', 'Use DB::get_conn instead');
return self::get_conn($name); return self::get_conn($name);
} }
@ -135,6 +135,10 @@ class DB {
* Set it to null to revert to the main database. * Set it to null to revert to the main database.
*/ */
public static function set_alternative_database_name($name = null) { public static function set_alternative_database_name($name = null) {
// Skip if CLI
if(Director::is_cli()) {
return;
}
if($name) { if($name) {
if(!self::valid_alternative_database_name($name)) { if(!self::valid_alternative_database_name($name)) {
throw new InvalidArgumentException(sprintf( throw new InvalidArgumentException(sprintf(
@ -254,10 +258,10 @@ class DB {
} }
/** /**
* @deprecated since version 3.2 DB::getConnect was never implemented and is obsolete * @deprecated since version 4.0 DB::getConnect was never implemented and is obsolete
*/ */
public static function getConnect($parameters) { public static function getConnect($parameters) {
Deprecation::notice('3.2', 'DB::getConnect was never implemented and is obsolete'); Deprecation::notice('4.0', 'DB::getConnect was never implemented and is obsolete');
} }
/** /**
@ -363,10 +367,10 @@ class DB {
} }
/** /**
* @deprecated since version 3.3 Use DB::get_generated_id instead * @deprecated since version 4.0 Use DB::get_generated_id instead
*/ */
public static function getGeneratedID($table) { public static function getGeneratedID($table) {
Deprecation::notice('3.3', 'Use DB::get_generated_id instead'); Deprecation::notice('4.0', 'Use DB::get_generated_id instead');
return self::get_generated_id($table); return self::get_generated_id($table);
} }
@ -380,10 +384,10 @@ class DB {
} }
/** /**
* @deprecated since version 3.3 Use DB::is_active instead * @deprecated since version 4.0 Use DB::is_active instead
*/ */
public static function isActive() { public static function isActive() {
Deprecation::notice('3.3', 'Use DB::is_active instead'); Deprecation::notice('4.0', 'Use DB::is_active instead');
return self::is_active(); return self::is_active();
} }
@ -400,10 +404,10 @@ class DB {
} }
/** /**
* @deprecated since version 3.3 Use DB::create_database instead * @deprecated since version 4.0 Use DB::create_database instead
*/ */
public static function createDatabase($connect, $username, $password, $database) { public static function createDatabase($connect, $username, $password, $database) {
Deprecation::notice('3.3', 'Use DB::create_database instead'); Deprecation::notice('4.0', 'Use DB::create_database instead');
return self::create_database($database); return self::create_database($database);
} }
@ -426,10 +430,10 @@ class DB {
} }
/** /**
* @deprecated since version 3.3 Use DB::create_table instead * @deprecated since version 4.0 Use DB::create_table instead
*/ */
public static function createTable($table, $fields = null, $indexes = null, $options = null) { public static function createTable($table, $fields = null, $indexes = null, $options = null) {
Deprecation::notice('3.3', 'Use DB::create_table instead'); Deprecation::notice('4.0', 'Use DB::create_table instead');
return self::create_table($table, $fields, $indexes, $options); return self::create_table($table, $fields, $indexes, $options);
} }
@ -444,10 +448,10 @@ class DB {
} }
/** /**
* @deprecated since version 3.3 Use DB::create_field instead * @deprecated since version 4.0 Use DB::create_field instead
*/ */
public static function createField($table, $field, $spec) { public static function createField($table, $field, $spec) {
Deprecation::notice('3.3', 'Use DB::create_field instead'); Deprecation::notice('4.0', 'Use DB::create_field instead');
return self::create_field($table, $field, $spec); return self::create_field($table, $field, $spec);
} }
@ -474,12 +478,12 @@ class DB {
} }
/** /**
* @deprecated since version 3.3 Use DB::require_table instead * @deprecated since version 4.0 Use DB::require_table instead
*/ */
public static function requireTable($table, $fieldSchema = null, $indexSchema = null, $hasAutoIncPK = true, public static function requireTable($table, $fieldSchema = null, $indexSchema = null, $hasAutoIncPK = true,
$options = null, $extensions = null $options = null, $extensions = null
) { ) {
Deprecation::notice('3.3', 'Use DB::require_table instead'); Deprecation::notice('4.0', 'Use DB::require_table instead');
return self::require_table($table, $fieldSchema, $indexSchema, $hasAutoIncPK, $options, $extensions); return self::require_table($table, $fieldSchema, $indexSchema, $hasAutoIncPK, $options, $extensions);
} }
@ -495,10 +499,10 @@ class DB {
} }
/** /**
* @deprecated since version 3.3 Use DB::require_field instead * @deprecated since version 4.0 Use DB::require_field instead
*/ */
public static function requireField($table, $field, $spec) { public static function requireField($table, $field, $spec) {
Deprecation::notice('3.3', 'Use DB::require_field instead'); Deprecation::notice('4.0', 'Use DB::require_field instead');
return self::require_field($table, $field, $spec); return self::require_field($table, $field, $spec);
} }
@ -514,10 +518,10 @@ class DB {
} }
/** /**
* @deprecated since version 3.3 Use DB::require_index instead * @deprecated since version 4.0 Use DB::require_index instead
*/ */
public static function requireIndex($table, $index, $spec) { public static function requireIndex($table, $index, $spec) {
Deprecation::notice('3.3', 'Use DB::require_index instead'); Deprecation::notice('4.0', 'Use DB::require_index instead');
self::require_index($table, $index, $spec); self::require_index($table, $index, $spec);
} }
@ -531,10 +535,10 @@ class DB {
} }
/** /**
* @deprecated since version 3.3 Use DB::dont_require_table instead * @deprecated since version 4.0 Use DB::dont_require_table instead
*/ */
public static function dontRequireTable($table) { public static function dontRequireTable($table) {
Deprecation::notice('3.3', 'Use DB::dont_require_table instead'); Deprecation::notice('4.0', 'Use DB::dont_require_table instead');
self::dont_require_table($table); self::dont_require_table($table);
} }
@ -549,10 +553,10 @@ class DB {
} }
/** /**
* @deprecated since version 3.3 Use DB::dont_require_field instead * @deprecated since version 4.0 Use DB::dont_require_field instead
*/ */
public static function dontRequireField($table, $fieldName) { public static function dontRequireField($table, $fieldName) {
Deprecation::notice('3.3', 'Use DB::dont_require_field instead'); Deprecation::notice('4.0', 'Use DB::dont_require_field instead');
self::dont_require_field($table, $fieldName); self::dont_require_field($table, $fieldName);
} }
@ -567,10 +571,10 @@ class DB {
} }
/** /**
* @deprecated since version 3.3 Use DB::check_and_repair_table instead * @deprecated since version 4.0 Use DB::check_and_repair_table instead
*/ */
public static function checkAndRepairTable($table) { public static function checkAndRepairTable($table) {
Deprecation::notice('3.3', 'Use DB::check_and_repair_table instead'); Deprecation::notice('4.0', 'Use DB::check_and_repair_table instead');
self::check_and_repair_table($table); self::check_and_repair_table($table);
} }
@ -584,10 +588,10 @@ class DB {
} }
/** /**
* @deprecated since version 3.3 Use DB::affected_rows instead * @deprecated since version 4.0 Use DB::affected_rows instead
*/ */
public static function affectedRows() { public static function affectedRows() {
Deprecation::notice('3.3', 'Use DB::affected_rows instead'); Deprecation::notice('4.0', 'Use DB::affected_rows instead');
return self::affected_rows(); return self::affected_rows();
} }
@ -602,10 +606,10 @@ class DB {
} }
/** /**
* @deprecated since version 3.3 Use DB::table_list instead * @deprecated since version 4.0 Use DB::table_list instead
*/ */
public static function tableList() { public static function tableList() {
Deprecation::notice('3.3', 'Use DB::table_list instead'); Deprecation::notice('4.0', 'Use DB::table_list instead');
return self::table_list(); return self::table_list();
} }
@ -621,10 +625,10 @@ class DB {
} }
/** /**
* @deprecated since version 3.3 Use DB::field_list instead * @deprecated since version 4.0 Use DB::field_list instead
*/ */
public static function fieldList($table) { public static function fieldList($table) {
Deprecation::notice('3.3', 'Use DB::field_list instead'); Deprecation::notice('4.0', 'Use DB::field_list instead');
return self::field_list($table); return self::field_list($table);
} }

View File

@ -24,7 +24,7 @@ abstract class DataExtension extends Extension {
* Edit the given query object to support queries for this extension * Edit the given query object to support queries for this extension
* *
* @param SQLSelect $query Query to augment. * @param SQLSelect $query Query to augment.
* @param DataQuery $dataQuery Container DataQuery for this SQLQuery * @param DataQuery $dataQuery Container DataQuery for this SQLSelect
*/ */
public function augmentSQL(SQLSelect $query, DataQuery $dataQuery = null) { public function augmentSQL(SQLSelect $query, DataQuery $dataQuery = null) {
} }

View File

@ -376,6 +376,8 @@ class DataList extends ViewableData implements SS_List, SS_Filterable, SS_Sortab
/** /**
* Return a new instance of the list with an added filter * Return a new instance of the list with an added filter
*
* @param array $filterArray
*/ */
public function addFilter($filterArray) { public function addFilter($filterArray) {
$list = $this; $list = $this;
@ -423,9 +425,9 @@ class DataList extends ViewableData implements SS_List, SS_Filterable, SS_Sortab
$whereArguments = func_get_arg(0); $whereArguments = func_get_arg(0);
} elseif($numberFuncArgs == 2) { } elseif($numberFuncArgs == 2) {
$whereArguments[func_get_arg(0)] = func_get_arg(1); $whereArguments[func_get_arg(0)] = func_get_arg(1);
} else { } else {
throw new InvalidArgumentException('Incorrect number of arguments passed to exclude()'); throw new InvalidArgumentException('Incorrect number of arguments passed to exclude()');
} }
return $this->alterDataQuery(function($query, $list) use ($whereArguments) { return $this->alterDataQuery(function($query, $list) use ($whereArguments) {
$subquery = $query->disjunctiveGroup(); $subquery = $query->disjunctiveGroup();
@ -440,16 +442,16 @@ class DataList extends ViewableData implements SS_List, SS_Filterable, SS_Sortab
$t = singleton($list->dataClass())->dbObject($field); $t = singleton($list->dataClass())->dbObject($field);
if($filterType) { if($filterType) {
$className = "{$filterType}Filter"; $className = "{$filterType}Filter";
} else { } else {
$className = 'ExactMatchFilter'; $className = 'ExactMatchFilter';
} }
if(!class_exists($className)){ if(!class_exists($className)){
$className = 'ExactMatchFilter'; $className = 'ExactMatchFilter';
array_unshift($modifiers, $filterType); array_unshift($modifiers, $filterType);
} }
$t = new $className($field, $value, $modifiers); $t = new $className($field, $value, $modifiers);
$t->apply($subquery); $t->apply($subquery);
} }
}); });
} }

View File

@ -452,6 +452,10 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
$record = null; $record = null;
} }
if(is_a($record, "stdClass")) {
$record = (array)$record;
}
// Set $this->record to $record, but ignore NULLs // Set $this->record to $record, but ignore NULLs
$this->record = array(); $this->record = array();
foreach($record as $k => $v) { foreach($record as $k => $v) {
@ -1263,6 +1267,7 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
continue; continue;
} }
// if database column doesn't correlate to a DBField instance... // if database column doesn't correlate to a DBField instance...
$fieldObj = $this->dbObject($fieldName); $fieldObj = $this->dbObject($fieldName);
if(!$fieldObj) { if(!$fieldObj) {
@ -1771,8 +1776,11 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
$componentClass, $table, $componentField, $parentField, $componentClass, $table, $componentField, $parentField,
$this->manyManyExtraFieldsForComponent($componentName) $this->manyManyExtraFieldsForComponent($componentName)
); );
if($this->model) $result->setDataModel($this->model); if($this->model) $result->setDataModel($this->model);
$this->extend('updateManyManyComponents', $result);
// If this is called on a singleton, then we return an 'orphaned relation' that can have the // If this is called on a singleton, then we return an 'orphaned relation' that can have the
// foreignID set elsewhere. // foreignID set elsewhere.
return $result return $result
@ -2739,6 +2747,7 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
public static function has_own_table($dataClass) { public static function has_own_table($dataClass) {
if(!is_subclass_of($dataClass,'DataObject')) return false; if(!is_subclass_of($dataClass,'DataObject')) return false;
$dataClass = ClassInfo::class_name($dataClass);
if(!isset(DataObject::$cache_has_own_table[$dataClass])) { if(!isset(DataObject::$cache_has_own_table[$dataClass])) {
if(get_parent_class($dataClass) == 'DataObject') { if(get_parent_class($dataClass) == 'DataObject') {
DataObject::$cache_has_own_table[$dataClass] = true; DataObject::$cache_has_own_table[$dataClass] = true;
@ -3126,6 +3135,7 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
return $result; return $result;
} }
/** /**
* Return the first item matching the given query. * Return the first item matching the given query.
* All calls to get_one() are cached. * All calls to get_one() are cached.

View File

@ -196,7 +196,7 @@ class DataQuery {
$tableClasses = $ancestorTables; $tableClasses = $ancestorTables;
} }
$tableNames = array_keys($tableClasses); $tableNames = array_values($tableClasses);
$baseClass = $tableNames[0]; $baseClass = $tableNames[0];
// Iterate over the tables and check what we need to select from them. If any selects are made (or the table is // Iterate over the tables and check what we need to select from them. If any selects are made (or the table is
@ -836,6 +836,10 @@ class DataQuery {
*/ */
class DataQuery_SubGroup extends DataQuery implements SQLConditionGroup { class DataQuery_SubGroup extends DataQuery implements SQLConditionGroup {
/**
*
* @var SQLSelect
*/
protected $whereQuery; protected $whereQuery;
public function __construct(DataQuery $base, $connective) { public function __construct(DataQuery $base, $connective) {
@ -868,7 +872,9 @@ class DataQuery_SubGroup extends DataQuery implements SQLConditionGroup {
// Ignore empty conditions // Ignore empty conditions
$where = $this->whereQuery->getWhere(); $where = $this->whereQuery->getWhere();
if(empty($where)) return null; if(empty($where)) {
return null;
}
// Allow database to manage joining of conditions // Allow database to manage joining of conditions
$sql = DB::get_conn()->getQueryBuilder()->buildWhereFragment($this->whereQuery, $parameters); $sql = DB::get_conn()->getQueryBuilder()->buildWhereFragment($this->whereQuery, $parameters);

View File

@ -265,10 +265,10 @@ class DatabaseAdmin extends Controller {
/** /**
* Clear all data out of the database * Clear all data out of the database
* *
* @deprecated since version 3.2 * @deprecated since version 4.0
*/ */
public function clearAllData() { public function clearAllData() {
Deprecation::notice('3.2', 'Use DB::get_conn()->clearAllData() instead'); Deprecation::notice('4.0', 'Use DB::get_conn()->clearAllData() instead');
DB::get_conn()->clearAllData(); DB::get_conn()->clearAllData();
} }

View File

@ -122,18 +122,6 @@ class Image extends File implements Flushable {
return $fields; return $fields;
} }
/**
* An image exists if it has a filename.
* Does not do any filesystem checks.
*
* @return boolean
*/
public function exists() {
if(isset($this->record["Filename"])) {
return true;
}
}
/** /**
* Return an XHTML img tag for this Image, * Return an XHTML img tag for this Image,
* or NULL if the image file doesn't exist on the filesystem. * or NULL if the image file doesn't exist on the filesystem.
@ -141,7 +129,7 @@ class Image extends File implements Flushable {
* @return string * @return string
*/ */
public function getTag() { public function getTag() {
if(file_exists(Director::baseFolder() . '/' . $this->Filename)) { if($this->exists()) {
$url = $this->getURL(); $url = $this->getURL();
$title = ($this->Title) ? $this->Title : $this->Filename; $title = ($this->Title) ? $this->Title : $this->Filename;
if($this->Title) { if($this->Title) {
@ -195,10 +183,10 @@ class Image extends File implements Flushable {
* File names are filtered through {@link FileNameFilter}, see class documentation * File names are filtered through {@link FileNameFilter}, see class documentation
* on how to influence this behaviour. * on how to influence this behaviour.
* *
* @deprecated 3.2 * @deprecated 4.0
*/ */
public function loadUploadedImage($tmpFile) { public function loadUploadedImage($tmpFile) {
Deprecation::notice('3.2', 'Use the Upload::loadIntoFile()'); Deprecation::notice('4.0', 'Use the Upload::loadIntoFile()');
if(!is_array($tmpFile)) { if(!is_array($tmpFile)) {
user_error("Image::loadUploadedImage() Not passed an array. Most likely, the form hasn't got the right" user_error("Image::loadUploadedImage() Not passed an array. Most likely, the form hasn't got the right"
@ -713,7 +701,7 @@ class Image extends File implements Flushable {
public function getFormattedImage($format) { public function getFormattedImage($format) {
$args = func_get_args(); $args = func_get_args();
if($this->ID && $this->Filename && Director::fileExists($this->Filename)) { if($this->exists()) {
$cacheFile = call_user_func_array(array($this, "cacheFilename"), $args); $cacheFile = call_user_func_array(array($this, "cacheFilename"), $args);
if(!file_exists(Director::baseFolder()."/".$cacheFile) || self::$flush) { if(!file_exists(Director::baseFolder()."/".$cacheFile) || self::$flush) {
@ -979,8 +967,8 @@ class Image extends File implements Flushable {
public function getDimensions($dim = "string") { public function getDimensions($dim = "string") {
if($this->getField('Filename')) { if($this->getField('Filename')) {
$imagefile = Director::baseFolder() . '/' . $this->getField('Filename'); $imagefile = $this->getFullPath();
if(file_exists($imagefile)) { if($this->exists()) {
$size = getimagesize($imagefile); $size = getimagesize($imagefile);
return ($dim === "string") ? "$size[0]x$size[1]" : $size[$dim]; return ($dim === "string") ? "$size[0]x$size[1]" : $size[$dim];
} else { } else {
@ -1062,6 +1050,16 @@ class Image_Cached extends Image {
return $this->getSourceURL(); return $this->getSourceURL();
} }
/**
* Override the parent's exists method becuase the ID is explicitly set to -1 on a cached image we can't use the
* default check
*
* @return bool Whether the cached image exists
*/
public function exists() {
return file_exists($this->getFullPath());
}
public function getRelativePath() { public function getRelativePath() {
return $this->getField('Filename'); return $this->getField('Filename');
} }

View File

@ -363,7 +363,7 @@ class ManyManyList extends RelationList {
foreach ($this->extraFields as $fieldName => $dbFieldSpec) { foreach ($this->extraFields as $fieldName => $dbFieldSpec) {
$cleanExtraFields[] = "\"{$fieldName}\""; $cleanExtraFields[] = "\"{$fieldName}\"";
} }
$query = new SQLQuery($cleanExtraFields, "\"{$this->joinTable}\""); $query = new SQLSelect($cleanExtraFields, "\"{$this->joinTable}\"");
$filter = $this->foreignIDWriteFilter($this->getForeignID()); $filter = $this->foreignIDWriteFilter($this->getForeignID());
if($filter) { if($filter) {
$query->setWhere($filter); $query->setWhere($filter);

View File

@ -10,11 +10,20 @@ abstract class DBConnector {
/** /**
* List of operations to treat as write * List of operations to treat as write
* Implicitly includes all ddl_operations
* *
* @config * @config
* @var array * @var array
*/ */
private static $write_operations = array('insert', 'update', 'delete', 'replace', 'alter', 'drop'); private static $write_operations = array('insert', 'update', 'delete', 'replace');
/**
* List of operations to treat as DDL
*
* @config
* @var array
*/
private static $ddl_operations = array('alter', 'drop', 'create', 'truncate');
/** /**
* Error handler for database errors. * Error handler for database errors.
@ -27,6 +36,7 @@ abstract class DBConnector {
* @param integer $errorLevel The level of the error to throw. * @param integer $errorLevel The level of the error to throw.
* @param string $sql The SQL related to this query * @param string $sql The SQL related to this query
* @param array $parameters Parameters passed to the query * @param array $parameters Parameters passed to the query
* @throws SS_DatabaseException
*/ */
protected function databaseError($msg, $errorLevel = E_USER_ERROR, $sql = null, $parameters = array()) { protected function databaseError($msg, $errorLevel = E_USER_ERROR, $sql = null, $parameters = array()) {
// Prevent errors when error checking is set at zero level // Prevent errors when error checking is set at zero level
@ -49,46 +59,57 @@ abstract class DBConnector {
} }
/** /**
* Determines if the query should be previewed, and thus interrupted silently. * Determine if this SQL statement is a destructive operation (write or ddl)
* If so, this function also displays the query via the debuging system.
* Subclasess should respect the results of this call for each query, and not
* execute any queries that generate a true response.
* *
* @param string $sql The query to be executed * @param string $sql
* @return boolean Flag indicating that the query was previewed * @return bool
*/ */
protected function previewWrite($sql) { public function isQueryMutable($sql) {
// Break if not requested $operations = array_merge(
if (!isset($_REQUEST['previewwrite'])) return false; Config::inst()->get(get_class($this), 'write_operations'),
Config::inst()->get(get_class($this), 'ddl_operations')
// Break if non-write operation );
$operation = strtolower(substr($sql, 0, strpos($sql, ' '))); return $this->isQueryType($sql, $operations);
$writeOperations = Config::inst()->get(get_class($this), 'write_operations');
if (!in_array($operation, $writeOperations)) {
return false;
}
// output preview message
Debug::message("Will execute: $sql");
return true;
} }
/** /**
* Allows the display and benchmarking of queries as they are being run * Determine if this SQL statement is a DDL operation
* *
* @param string $sql Query to run, and single parameter to callback * @param string $sql
* @param callable $callback Callback to execute code * @return bool
* @return mixed Result of query
*/ */
protected function benchmarkQuery($sql, $callback) { public function isQueryDDL($sql) {
if (isset($_REQUEST['showqueries']) && Director::isDev(true)) { $operations = Config::inst()->get(get_class($this), 'ddl_operations');
$starttime = microtime(true); return $this->isQueryType($sql, $operations);
$result = $callback($sql); }
$endtime = round(microtime(true) - $starttime, 4);
Debug::message("\n$sql\n{$endtime}s\n", false); /**
return $result; * Determine if this SQL statement is a write operation
* (alters content but not structure)
*
* @param string $sql
* @return bool
*/
public function isQueryWrite($sql) {
$operations = Config::inst()->get(get_class($this), 'write_operations');
return $this->isQueryType($sql, $operations);
}
/**
* Determine if a query is of the given type
*
* @param string $sql Raw SQL
* @param string|array $type Type or list of types (first word in the query). Must be lowercase
*/
protected function isQueryType($sql, $type) {
if(!preg_match('/^(?<operation>\w+)\b/', $sql, $matches)) {
return false;
}
$operation = $matches['operation'];
if(is_array($type)) {
return in_array(strtolower($operation), $type);
} else { } else {
return $callback($sql); return strcasecmp($sql, $type) === 0;
} }
} }

View File

@ -96,7 +96,19 @@ abstract class SS_Database {
* @return SS_Query * @return SS_Query
*/ */
public function query($sql, $errorLevel = E_USER_ERROR) { public function query($sql, $errorLevel = E_USER_ERROR) {
return $this->connector->query($sql, $errorLevel); // Check if we should only preview this query
if ($this->previewWrite($sql)) {
return;
}
// Benchmark query
$connector = $this->connector;
return $this->benchmarkQuery(
$sql,
function($sql) use($connector, $errorLevel) {
return $connector->query($sql, $errorLevel);
}
);
} }
@ -109,7 +121,62 @@ abstract class SS_Database {
* @return SS_Query * @return SS_Query
*/ */
public function preparedQuery($sql, $parameters, $errorLevel = E_USER_ERROR) { public function preparedQuery($sql, $parameters, $errorLevel = E_USER_ERROR) {
return $this->connector->preparedQuery($sql, $parameters, $errorLevel); // Check if we should only preview this query
if ($this->previewWrite($sql)) {
return;
}
// Benchmark query
$connector = $this->connector;
return $this->benchmarkQuery(
$sql,
function($sql) use($connector, $parameters, $errorLevel) {
return $connector->preparedQuery($sql, $parameters, $errorLevel);
}
);
}
/**
* Determines if the query should be previewed, and thus interrupted silently.
* If so, this function also displays the query via the debuging system.
* Subclasess should respect the results of this call for each query, and not
* execute any queries that generate a true response.
*
* @param string $sql The query to be executed
* @return boolean Flag indicating that the query was previewed
*/
protected function previewWrite($sql) {
// Only preview if previewWrite is set, we are in dev mode, and
// the query is mutable
if (isset($_REQUEST['previewwrite'])
&& Director::isDev()
&& $this->connector->isQueryMutable($sql)
) {
// output preview message
Debug::message("Will execute: $sql");
return true;
} else {
return false;
}
}
/**
* Allows the display and benchmarking of queries as they are being run
*
* @param string $sql Query to run, and single parameter to callback
* @param callable $callback Callback to execute code
* @return mixed Result of query
*/
protected function benchmarkQuery($sql, $callback) {
if (isset($_REQUEST['showqueries']) && Director::isDev()) {
$starttime = microtime(true);
$result = $callback($sql);
$endtime = round(microtime(true) - $starttime, 4);
Debug::message("\n$sql\n{$endtime}s\n", false);
return $result;
} else {
return $callback($sql);
}
} }
/** /**
@ -628,70 +695,70 @@ abstract class SS_Database {
abstract public function random(); abstract public function random();
/** /**
* @deprecated since version 3.3 Use DB::get_schema()->dbDataType($type) instead * @deprecated since version 4.0 Use DB::get_schema()->dbDataType($type) instead
*/ */
public function dbDataType($type){ public function dbDataType($type){
Deprecation::notice('3.3', 'Use DB::get_schema()->dbDataType($type) instead'); Deprecation::notice('4.0', 'Use DB::get_schema()->dbDataType($type) instead');
return $this->getSchemaManager()->dbDataType($type); return $this->getSchemaManager()->dbDataType($type);
} }
/** /**
* @deprecated since version 3.3 Use selectDatabase('dbname', true) instead * @deprecated since version 4.0 Use selectDatabase('dbname', true) instead
*/ */
public function createDatabase() { public function createDatabase() {
Deprecation::notice('3.3', 'Use selectDatabase(\'dbname\',true) instead'); Deprecation::notice('4.0', 'Use selectDatabase(\'dbname\',true) instead');
$database = $this->connector->getSelectedDatabase(); $database = $this->connector->getSelectedDatabase();
$this->selectDatabase($database, true); $this->selectDatabase($database, true);
return $this->isActive(); return $this->isActive();
} }
/** /**
* @deprecated since version 3.3 SS_Database::getConnect was never implemented and is obsolete * @deprecated since version 4.0 SS_Database::getConnect was never implemented and is obsolete
*/ */
public function getConnect($parameters) { public function getConnect($parameters) {
Deprecation::notice('3.3', 'SS_Database::getConnect was never implemented and is obsolete'); Deprecation::notice('4.0', 'SS_Database::getConnect was never implemented and is obsolete');
} }
/** /**
* @deprecated since version 3.3 Use Convert::raw2sql($string, true) instead * @deprecated since version 4.0 Use Convert::raw2sql($string, true) instead
*/ */
public function prepStringForDB($string) { public function prepStringForDB($string) {
Deprecation::notice('3.3', 'Use Convert::raw2sql($string, true) instead'); Deprecation::notice('4.0', 'Use Convert::raw2sql($string, true) instead');
return $this->quoteString($string); return $this->quoteString($string);
} }
/** /**
* @deprecated since version 3.3 Use dropSelectedDatabase instead * @deprecated since version 4.0 Use dropSelectedDatabase instead
*/ */
public function dropDatabase() { public function dropDatabase() {
Deprecation::notice('3.3', 'Use dropSelectedDatabase instead'); Deprecation::notice('4.0', 'Use dropSelectedDatabase instead');
$this->dropSelectedDatabase(); $this->dropSelectedDatabase();
} }
/** /**
* @deprecated since version 3.3 Use databaseList instead * @deprecated since version 4.0 Use databaseList instead
*/ */
public function allDatabaseNames() { public function allDatabaseNames() {
Deprecation::notice('3.3', 'Use databaseList instead'); Deprecation::notice('4.0', 'Use databaseList instead');
return $this->databaseList(); return $this->databaseList();
} }
/** /**
* @deprecated since version 3.3 Use DB::create_table instead * @deprecated since version 4.0 Use DB::create_table instead
*/ */
public function createTable($table, $fields = null, $indexes = null, $options = null, $advancedOptions = null) { public function createTable($table, $fields = null, $indexes = null, $options = null, $advancedOptions = null) {
Deprecation::notice('3.3', 'Use DB::create_table instead'); Deprecation::notice('4.0', 'Use DB::create_table instead');
return $this->getSchemaManager()->createTable($table, $fields, $indexes, $options, $advancedOptions); return $this->getSchemaManager()->createTable($table, $fields, $indexes, $options, $advancedOptions);
} }
/** /**
* @deprecated since version 3.3 Use DB::get_schema()->alterTable() instead * @deprecated since version 4.0 Use DB::get_schema()->alterTable() instead
*/ */
public function alterTable($table, $newFields = null, $newIndexes = null, public function alterTable($table, $newFields = null, $newIndexes = null,
$alteredFields = null, $alteredIndexes = null, $alteredOptions = null, $alteredFields = null, $alteredIndexes = null, $alteredOptions = null,
$advancedOptions = null $advancedOptions = null
) { ) {
Deprecation::notice('3.3', 'Use DB::get_schema()->alterTable() instead'); Deprecation::notice('4.0', 'Use DB::get_schema()->alterTable() instead');
return $this->getSchemaManager()->alterTable( return $this->getSchemaManager()->alterTable(
$table, $newFields, $newIndexes, $alteredFields, $table, $newFields, $newIndexes, $alteredFields,
$alteredIndexes, $alteredOptions, $advancedOptions $alteredIndexes, $alteredOptions, $advancedOptions
@ -699,74 +766,74 @@ abstract class SS_Database {
} }
/** /**
* @deprecated since version 3.3 Use DB::get_schema()->renameTable() instead * @deprecated since version 4.0 Use DB::get_schema()->renameTable() instead
*/ */
public function renameTable($oldTableName, $newTableName) { public function renameTable($oldTableName, $newTableName) {
Deprecation::notice('3.3', 'Use DB::get_schema()->renameTable() instead'); Deprecation::notice('4.0', 'Use DB::get_schema()->renameTable() instead');
$this->getSchemaManager()->renameTable($oldTableName, $newTableName); $this->getSchemaManager()->renameTable($oldTableName, $newTableName);
} }
/** /**
* @deprecated since version 3.3 Use DB::create_field() instead * @deprecated since version 4.0 Use DB::create_field() instead
*/ */
public function createField($table, $field, $spec) { public function createField($table, $field, $spec) {
Deprecation::notice('3.3', 'Use DB::create_field() instead'); Deprecation::notice('4.0', 'Use DB::create_field() instead');
$this->getSchemaManager()->createField($table, $field, $spec); $this->getSchemaManager()->createField($table, $field, $spec);
} }
/** /**
* @deprecated since version 3.3 Use DB::get_schema()->renameField() instead * @deprecated since version 4.0 Use DB::get_schema()->renameField() instead
*/ */
public function renameField($tableName, $oldName, $newName) { public function renameField($tableName, $oldName, $newName) {
Deprecation::notice('3.3', 'Use DB::get_schema()->renameField() instead'); Deprecation::notice('4.0', 'Use DB::get_schema()->renameField() instead');
$this->getSchemaManager()->renameField($tableName, $oldName, $newName); $this->getSchemaManager()->renameField($tableName, $oldName, $newName);
} }
/** /**
* @deprecated since version 3.3 Use getSelectedDatabase instead * @deprecated since version 4.0 Use getSelectedDatabase instead
*/ */
public function currentDatabase() { public function currentDatabase() {
Deprecation::notice('3.3', 'Use getSelectedDatabase instead'); Deprecation::notice('4.0', 'Use getSelectedDatabase instead');
return $this->getSelectedDatabase(); return $this->getSelectedDatabase();
} }
/** /**
* @deprecated since version 3.3 Use DB::field_list instead * @deprecated since version 4.0 Use DB::field_list instead
*/ */
public function fieldList($table) { public function fieldList($table) {
Deprecation::notice('3.3', 'Use DB::field_list instead'); Deprecation::notice('4.0', 'Use DB::field_list instead');
return $this->getSchemaManager()->fieldList($table); return $this->getSchemaManager()->fieldList($table);
} }
/** /**
* @deprecated since version 3.3 Use DB::table_list instead * @deprecated since version 4.0 Use DB::table_list instead
*/ */
public function tableList() { public function tableList() {
Deprecation::notice('3.3', 'Use DB::table_list instead'); Deprecation::notice('4.0', 'Use DB::table_list instead');
return $this->getSchemaManager()->tableList(); return $this->getSchemaManager()->tableList();
} }
/** /**
* @deprecated since version 3.3 Use DB::get_schema()->hasTable() instead * @deprecated since version 4.0 Use DB::get_schema()->hasTable() instead
*/ */
public function hasTable($tableName) { public function hasTable($tableName) {
Deprecation::notice('3.3', 'Use DB::get_schema()->hasTable() instead'); Deprecation::notice('4.0', 'Use DB::get_schema()->hasTable() instead');
return $this->getSchemaManager()->hasTable($tableName); return $this->getSchemaManager()->hasTable($tableName);
} }
/** /**
* @deprecated since version 3.3 Use DB::get_schema()->enumValuesForField() instead * @deprecated since version 4.0 Use DB::get_schema()->enumValuesForField() instead
*/ */
public function enumValuesForField($tableName, $fieldName) { public function enumValuesForField($tableName, $fieldName) {
Deprecation::notice('3.3', 'Use DB::get_schema()->enumValuesForField() instead'); Deprecation::notice('4.0', 'Use DB::get_schema()->enumValuesForField() instead');
return $this->getSchemaManager()->enumValuesForField($tableName, $fieldName); return $this->getSchemaManager()->enumValuesForField($tableName, $fieldName);
} }
/** /**
* @deprecated since version 3.3 Use Convert::raw2sql instead * @deprecated since version 4.0 Use Convert::raw2sql instead
*/ */
public function addslashes($value) { public function addslashes($value) {
Deprecation::notice('3.3', 'Use Convert::raw2sql instead'); Deprecation::notice('4.0', 'Use Convert::raw2sql instead');
return $this->escapeString($value); return $this->escapeString($value);
} }
@ -789,134 +856,134 @@ abstract class SS_Database {
} }
/** /**
* @deprecated since version 3.3 Use DB::get_schema()->cancelSchemaUpdate instead * @deprecated since version 4.0 Use DB::get_schema()->cancelSchemaUpdate instead
*/ */
public function cancelSchemaUpdate() { public function cancelSchemaUpdate() {
Deprecation::notice('3.3', 'Use DB::get_schema()->cancelSchemaUpdate instead'); Deprecation::notice('4.0', 'Use DB::get_schema()->cancelSchemaUpdate instead');
$this->getSchemaManager()->cancelSchemaUpdate(); $this->getSchemaManager()->cancelSchemaUpdate();
} }
/** /**
* @deprecated since version 3.3 Use DB::get_schema()->isSchemaUpdating() instead * @deprecated since version 4.0 Use DB::get_schema()->isSchemaUpdating() instead
*/ */
public function isSchemaUpdating() { public function isSchemaUpdating() {
Deprecation::notice('3.3', 'Use DB::get_schema()->isSchemaUpdating() instead'); Deprecation::notice('4.0', 'Use DB::get_schema()->isSchemaUpdating() instead');
return $this->getSchemaManager()->isSchemaUpdating(); return $this->getSchemaManager()->isSchemaUpdating();
} }
/** /**
* @deprecated since version 3.3 Use DB::get_schema()->doesSchemaNeedUpdating() instead * @deprecated since version 4.0 Use DB::get_schema()->doesSchemaNeedUpdating() instead
*/ */
public function doesSchemaNeedUpdating() { public function doesSchemaNeedUpdating() {
Deprecation::notice('3.3', 'Use DB::get_schema()->doesSchemaNeedUpdating() instead'); Deprecation::notice('4.0', 'Use DB::get_schema()->doesSchemaNeedUpdating() instead');
return $this->getSchemaManager()->doesSchemaNeedUpdating(); return $this->getSchemaManager()->doesSchemaNeedUpdating();
} }
/** /**
* @deprecated since version 3.3 Use DB::get_schema()->transCreateTable() instead * @deprecated since version 4.0 Use DB::get_schema()->transCreateTable() instead
*/ */
public function transCreateTable($table, $options = null, $advanced_options = null) { public function transCreateTable($table, $options = null, $advanced_options = null) {
Deprecation::notice('3.3', 'Use DB::get_schema()->transCreateTable() instead'); Deprecation::notice('4.0', 'Use DB::get_schema()->transCreateTable() instead');
$this->getSchemaManager()->transCreateTable($table, $options, $advanced_options); $this->getSchemaManager()->transCreateTable($table, $options, $advanced_options);
} }
/** /**
* @deprecated since version 3.3 Use DB::get_schema()->transAlterTable() instead * @deprecated since version 4.0 Use DB::get_schema()->transAlterTable() instead
*/ */
public function transAlterTable($table, $options, $advanced_options) { public function transAlterTable($table, $options, $advanced_options) {
Deprecation::notice('3.3', 'Use DB::get_schema()->transAlterTable() instead'); Deprecation::notice('4.0', 'Use DB::get_schema()->transAlterTable() instead');
$this->getSchemaManager()->transAlterTable($table, $index, $schema); $this->getSchemaManager()->transAlterTable($table, $index, $schema);
} }
/** /**
* @deprecated since version 3.3 Use DB::get_schema()->transCreateField() instead * @deprecated since version 4.0 Use DB::get_schema()->transCreateField() instead
*/ */
public function transCreateField($table, $field, $schema) { public function transCreateField($table, $field, $schema) {
Deprecation::notice('3.3', 'Use DB::get_schema()->transCreateField() instead'); Deprecation::notice('4.0', 'Use DB::get_schema()->transCreateField() instead');
$this->getSchemaManager()->transCreateField($table, $index, $schema); $this->getSchemaManager()->transCreateField($table, $index, $schema);
} }
/** /**
* @deprecated since version 3.3 Use DB::get_schema()->transCreateIndex() instead * @deprecated since version 4.0 Use DB::get_schema()->transCreateIndex() instead
*/ */
public function transCreateIndex($table, $index, $schema) { public function transCreateIndex($table, $index, $schema) {
Deprecation::notice('3.3', 'Use DB::get_schema()->transCreateIndex() instead'); Deprecation::notice('4.0', 'Use DB::get_schema()->transCreateIndex() instead');
$this->getSchemaManager()->transCreateIndex($table, $index, $schema); $this->getSchemaManager()->transCreateIndex($table, $index, $schema);
} }
/** /**
* @deprecated since version 3.3 Use DB::get_schema()->transAlterField() instead * @deprecated since version 4.0 Use DB::get_schema()->transAlterField() instead
*/ */
public function transAlterField($table, $field, $schema) { public function transAlterField($table, $field, $schema) {
Deprecation::notice('3.3', 'Use DB::get_schema()->transAlterField() instead'); Deprecation::notice('4.0', 'Use DB::get_schema()->transAlterField() instead');
$this->getSchemaManager()->transAlterField($table, $index, $schema); $this->getSchemaManager()->transAlterField($table, $index, $schema);
} }
/** /**
* @deprecated since version 3.3 Use DB::get_schema()->transAlterIndex() instead * @deprecated since version 4.0 Use DB::get_schema()->transAlterIndex() instead
*/ */
public function transAlterIndex($table, $index, $schema) { public function transAlterIndex($table, $index, $schema) {
Deprecation::notice('3.3', 'Use DB::get_schema()->transAlterIndex() instead'); Deprecation::notice('4.0', 'Use DB::get_schema()->transAlterIndex() instead');
$this->getSchemaManager()->transAlterIndex($table, $index, $schema); $this->getSchemaManager()->transAlterIndex($table, $index, $schema);
} }
/** /**
* @deprecated since version 3.3 Use DB::require_table() instead * @deprecated since version 4.0 Use DB::require_table() instead
*/ */
public function requireTable($table, $fieldSchema = null, $indexSchema = null, public function requireTable($table, $fieldSchema = null, $indexSchema = null,
$hasAutoIncPK = true, $options = array(), $extensions = false $hasAutoIncPK = true, $options = array(), $extensions = false
) { ) {
Deprecation::notice('3.3', 'Use DB::require_table() instead'); Deprecation::notice('4.0', 'Use DB::require_table() instead');
return $this->getSchemaManager()->requireTable( return $this->getSchemaManager()->requireTable(
$table, $fieldSchema, $indexSchema, $hasAutoIncPK, $options, $extensions $table, $fieldSchema, $indexSchema, $hasAutoIncPK, $options, $extensions
); );
} }
/** /**
* @deprecated since version 3.3 Use DB::dont_require_table() instead * @deprecated since version 4.0 Use DB::dont_require_table() instead
*/ */
public function dontRequireTable($table) { public function dontRequireTable($table) {
Deprecation::notice('3.3', 'Use DB::dont_require_table() instead'); Deprecation::notice('4.0', 'Use DB::dont_require_table() instead');
$this->getSchemaManager()->dontRequireTable($table); $this->getSchemaManager()->dontRequireTable($table);
} }
/** /**
* @deprecated since version 3.3 Use DB::require_index() instead * @deprecated since version 4.0 Use DB::require_index() instead
*/ */
public function requireIndex($table, $index, $spec) { public function requireIndex($table, $index, $spec) {
Deprecation::notice('3.3', 'Use DB::require_index() instead'); Deprecation::notice('4.0', 'Use DB::require_index() instead');
$this->getSchemaManager()->requireIndex($table, $index, $spec); $this->getSchemaManager()->requireIndex($table, $index, $spec);
} }
/** /**
* @deprecated since version 3.3 Use DB::get_schema()->hasField() instead * @deprecated since version 4.0 Use DB::get_schema()->hasField() instead
*/ */
public function hasField($tableName, $fieldName) { public function hasField($tableName, $fieldName) {
Deprecation::notice('3.3', 'Use DB::get_schema()->hasField() instead'); Deprecation::notice('4.0', 'Use DB::get_schema()->hasField() instead');
return $this->getSchemaManager()->hasField($tableName, $fieldName); return $this->getSchemaManager()->hasField($tableName, $fieldName);
} }
/** /**
* @deprecated since version 3.3 Use DB::require_field() instead * @deprecated since version 4.0 Use DB::require_field() instead
*/ */
public function requireField($table, $field, $spec) { public function requireField($table, $field, $spec) {
Deprecation::notice('3.3', 'Use DB::require_field() instead'); Deprecation::notice('4.0', 'Use DB::require_field() instead');
$this->getSchemaManager()->requireField($table, $field, $spec); $this->getSchemaManager()->requireField($table, $field, $spec);
} }
/** /**
* @deprecated since version 3.3 Use DB::dont_require_field() instead * @deprecated since version 4.0 Use DB::dont_require_field() instead
*/ */
public function dontRequireField($table, $fieldName) { public function dontRequireField($table, $fieldName) {
Deprecation::notice('3.3', 'Use DB::dont_require_field() instead'); Deprecation::notice('4.0', 'Use DB::dont_require_field() instead');
$this->getSchemaManager()->dontRequireField($table, $fieldName); $this->getSchemaManager()->dontRequireField($table, $fieldName);
} }
/** /**
* @deprecated since version 3.3 Use DB::build_sql() instead * @deprecated since version 4.0 Use DB::build_sql() instead
*/ */
public function sqlQueryToString(SQLExpression $query, &$parameters = array()) { public function sqlQueryToString(SQLExpression $query, &$parameters = array()) {
Deprecation::notice('3.3', 'Use DB::build_sql() instead'); Deprecation::notice('4.0', 'Use DB::build_sql() instead');
return $this->getQueryBuilder()->buildSQL($query, $parameters); return $this->getQueryBuilder()->buildSQL($query, $parameters);
} }
} }

View File

@ -12,25 +12,13 @@ class MySQLSchemaManager extends DBSchemaManager {
* Identifier for this schema, used for configuring schema-specific table * Identifier for this schema, used for configuring schema-specific table
* creation options * creation options
*/ */
const ID = 'MySQL'; const ID = 'MySQLDatabase';
public function createTable($table, $fields = null, $indexes = null, $options = null, $advancedOptions = null) { public function createTable($table, $fields = null, $indexes = null, $options = null, $advancedOptions = null) {
$fieldSchemas = $indexSchemas = ""; $fieldSchemas = $indexSchemas = "";
if (!empty($options[self::ID])) { if (!empty($options[self::ID])) {
$addOptions = $options[self::ID]; $addOptions = $options[self::ID];
} elseif (!empty($options[get_class($this)])) {
Deprecation::notice(
'3.2',
'Use MySQLSchemaManager::ID for referencing mysql-specific table creation options'
);
$addOptions = $options[get_class($this)];
} elseif (!empty($options[get_parent_class($this)])) {
Deprecation::notice(
'3.2',
'Use MySQLSchemaManager::ID for referencing mysql-specific table creation options'
);
$addOptions = $options[get_parent_class($this)];
} else { } else {
$addOptions = "ENGINE=InnoDB"; $addOptions = "ENGINE=InnoDB";
} }

View File

@ -58,12 +58,13 @@ class MySQLStatement extends SS_Query {
$variables[] = &$this->boundValues[$field->name]; $variables[] = &$this->boundValues[$field->name];
} }
call_user_func_array(array($this->statement, 'bind_result'), $variables);
$this->bound = true; $this->bound = true;
$this->metadata->free(); $this->metadata->free();
// Buffer all results // Buffer all results
$this->statement->store_result(); $this->statement->store_result();
call_user_func_array(array($this->statement, 'bind_result'), $variables);
} }
/** /**

View File

@ -104,21 +104,21 @@ class MySQLiConnector extends DBConnector {
return $this->dbConn->server_info; return $this->dbConn->server_info;
} }
protected function benchmarkQuery($sql, $callback) { /**
* Invoked before any query is executed
*
* @param string $sql
*/
protected function beforeQuery($sql) {
// Clear the last statement // Clear the last statement
$this->setLastStatement(null); $this->setLastStatement(null);
return parent::benchmarkQuery($sql, $callback);
} }
public function query($sql, $errorLevel = E_USER_ERROR) { public function query($sql, $errorLevel = E_USER_ERROR) {
// Check if we should only preview this query $this->beforeQuery($sql);
if ($this->previewWrite($sql)) return;
// Benchmark query // Benchmark query
$conn = $this->dbConn; $handle = $this->dbConn->query($sql, MYSQLI_STORE_RESULT);
$handle = $this->benchmarkQuery($sql, function($sql) use($conn) {
return $conn->query($sql, MYSQLI_STORE_RESULT);
});
if (!$handle || $this->dbConn->error) { if (!$handle || $this->dbConn->error) {
$this->databaseError($this->getLastError(), $errorLevel, $sql); $this->databaseError($this->getLastError(), $errorLevel, $sql);
@ -208,23 +208,20 @@ class MySQLiConnector extends DBConnector {
public function preparedQuery($sql, $parameters, $errorLevel = E_USER_ERROR) { public function preparedQuery($sql, $parameters, $errorLevel = E_USER_ERROR) {
// Shortcut to basic query when not given parameters // Shortcut to basic query when not given parameters
if(empty($parameters)) return $this->query($sql, $errorLevel); if(empty($parameters)) {
return $this->query($sql, $errorLevel);
}
// Check if we should only preview this query $this->beforeQuery($sql);
if ($this->previewWrite($sql)) return;
// Type check, identify, and prepare parameters for passing to the statement bind function // Type check, identify, and prepare parameters for passing to the statement bind function
$parsedParameters = $this->parsePreparedParameters($parameters, $blobs); $parsedParameters = $this->parsePreparedParameters($parameters, $blobs);
// Benchmark query // Benchmark query
$self = $this; $statement = $this->prepareStatement($sql, $success);
$lastStatement = $this->benchmarkQuery($sql, function($sql) use($parsedParameters, $blobs, $self) { if($success) {
$statement = $self->prepareStatement($sql, $success);
if(!$success) return null;
if($parsedParameters) { if($parsedParameters) {
$self->bindParameters($statement, $parsedParameters); $this->bindParameters($statement, $parsedParameters);
} }
// Bind any blobs given // Bind any blobs given
@ -234,18 +231,18 @@ class MySQLiConnector extends DBConnector {
// Safely execute the statement // Safely execute the statement
$statement->execute(); $statement->execute();
return $statement; }
});
if (!$lastStatement || $lastStatement->error) { if (!$success || $statement->error) {
$values = $this->parameterValues($parameters); $values = $this->parameterValues($parameters);
$this->databaseError($this->getLastError(), $errorLevel, $sql, $values); $this->databaseError($this->getLastError(), $errorLevel, $sql, $values);
return null; return null;
} }
// Non-select queries will have no result data // Non-select queries will have no result data
if($lastStatement && ($metaData = $lastStatement->result_metadata())) { $metaData = $statement->result_metadata();
return new MySQLStatement($lastStatement, $metaData); if($metaData) {
return new MySQLStatement($statement, $metaData);
} else { } else {
// Replicate normal behaviour of ->query() on non-select calls // Replicate normal behaviour of ->query() on non-select calls
return new MySQLQuery($this, true); return new MySQLQuery($this, true);

View File

@ -30,11 +30,18 @@ class PDOConnector extends DBConnector {
protected $databaseName = null; protected $databaseName = null;
/** /**
* The most recent statement returned from PDODatabase->query * If available, the row count of the last executed statement
* *
* @var PDOStatement * @var int|null
*/ */
protected $lastStatement = null; protected $rowCount = null;
/**
* Error generated by the errorInfo() method of the last PDOStatement
*
* @var array|null
*/
protected $lastStatementError = null;
/** /**
* List of prepared statements, cached by SQL string * List of prepared statements, cached by SQL string
@ -58,13 +65,22 @@ class PDOConnector extends DBConnector {
* @return PDOStatement * @return PDOStatement
*/ */
public function getOrPrepareStatement($sql) { public function getOrPrepareStatement($sql) {
if(empty($this->cachedStatements[$sql])) { // Return cached statements
$this->cachedStatements[$sql] = $this->pdoConnection->prepare( if(isset($this->cachedStatements[$sql])) {
$sql, return $this->cachedStatements[$sql];
array(PDO::ATTR_CURSOR => PDO::CURSOR_FWDONLY)
);
} }
return $this->cachedStatements[$sql];
// Generate new statement
$statement = $this->pdoConnection->prepare(
$sql,
array(PDO::ATTR_CURSOR => PDO::CURSOR_FWDONLY)
);
// Only cache select statements
if(preg_match('/^(\s*)select\b/i', $sql)) {
$this->cachedStatements[$sql] = $statement;
}
return $statement;
} }
/** /**
@ -168,52 +184,52 @@ class PDOConnector extends DBConnector {
} }
/** /**
* Executes a query that doesn't return a resultset * Invoked before any query is executed
* *
* @param string $sql * @param string $sql
*/
protected function beforeQuery($sql) {
// Reset state
$this->rowCount = 0;
$this->lastStatementError = null;
// Flush if necessary
if($this->isQueryDDL($sql)) {
$this->flushStatements();
}
}
/**
* Executes a query that doesn't return a resultset
*
* @param string $sql The SQL query to execute * @param string $sql The SQL query to execute
* @param integer $errorLevel For errors to this query, raise PHP errors * @param integer $errorLevel For errors to this query, raise PHP errors
* using this error level. * using this error level.
* @return int
*/ */
public function exec($sql, $errorLevel = E_USER_ERROR) { public function exec($sql, $errorLevel = E_USER_ERROR) {
// Check if we should only preview this query $this->beforeQuery($sql);
if ($this->previewWrite($sql)) return;
// Reset last statement to prevent interference in case of error // Directly exec this query
$this->lastStatement = null; $result = $this->pdoConnection->exec($sql);
// Benchmark query
$pdo = $this->pdoConnection;
$result = $this->benchmarkQuery($sql, function($sql) use($pdo) {
return $pdo->exec($sql);
});
// Check for errors // Check for errors
if ($result === false) { if ($result !== false) {
$this->databaseError($this->getLastError(), $errorLevel, $sql); return $this->rowCount = $result;
return null;
} }
return $result; $this->databaseError($this->getLastError(), $errorLevel, $sql);
return null;
} }
public function query($sql, $errorLevel = E_USER_ERROR) { public function query($sql, $errorLevel = E_USER_ERROR) {
// Check if we should only preview this query $this->beforeQuery($sql);
if ($this->previewWrite($sql)) return;
// Benchmark query // Directly query against connection
$pdo = $this->pdoConnection; $statement = $this->pdoConnection->query($sql);
$this->lastStatement = $this->benchmarkQuery($sql, function($sql) use($pdo) {
return $pdo->query($sql);
});
// Check for errors // Generate results
if (!$this->lastStatement || $this->hasError($this->lastStatement)) { return $this->prepareResults($statement, $errorLevel, $sql);
$this->databaseError($this->getLastError(), $errorLevel, $sql);
return null;
}
return new PDOQuery($this->lastStatement);
} }
/** /**
@ -272,33 +288,54 @@ class PDOConnector extends DBConnector {
} }
public function preparedQuery($sql, $parameters, $errorLevel = E_USER_ERROR) { public function preparedQuery($sql, $parameters, $errorLevel = E_USER_ERROR) {
// Check if we should only preview this query $this->beforeQuery($sql);
if ($this->previewWrite($sql)) return;
// Benchmark query // Prepare statement
$self = $this; $statement = $this->getOrPrepareStatement($sql);
$this->lastStatement = $this->benchmarkQuery($sql, function($sql) use($parameters, $self) {
// Prepare statement // Bind and invoke statement safely
$statement = $self->getOrPrepareStatement($sql); if($statement) {
if(!$statement) return null; $this->bindParameters($statement, $parameters);
// Inject parameters
$self->bindParameters($statement, $parameters);
// Safely execute the statement
$statement->execute($parameters); $statement->execute($parameters);
return $statement;
});
// Check for errors
if (!$this->lastStatement || $this->hasError($this->lastStatement)) {
$values = $this->parameterValues($parameters);
$this->databaseError($this->getLastError(), $errorLevel, $sql, $values);
return null;
} }
return new PDOQuery($this->lastStatement); // Generate results
return $this->prepareResults($statement, $errorLevel, $sql);
}
/**
* Given a PDOStatement that has just been executed, generate results
* and report any errors
*
* @param PDOStatement $statement
* @param int $errorLevel
* @param string $sql
* @param array $parameters
* @return \PDOQuery
*/
protected function prepareResults($statement, $errorLevel, $sql, $parameters = array()) {
// Record row-count and errors of last statement
if($this->hasError($statement)) {
$this->lastStatementError = $statement->errorInfo();
} elseif($statement) {
// Count and return results
$this->rowCount = $statement->rowCount();
return new PDOQuery($statement);
}
// Ensure statement is closed
if($statement) {
$statement->closeCursor();
unset($statement);
}
// Report any errors
if($parameters) {
$parameters = $this->parameterValues($parameters);
}
$this->databaseError($this->getLastError(), $errorLevel, $sql, $parameters);
return null;
} }
/** /**
@ -309,11 +346,15 @@ class PDOConnector extends DBConnector {
*/ */
protected function hasError($resource) { protected function hasError($resource) {
// No error if no resource // No error if no resource
if(empty($resource)) return false; if(empty($resource)) {
return false;
}
// If the error code is empty the statement / connection has not been run yet // If the error code is empty the statement / connection has not been run yet
$code = $resource->errorCode(); $code = $resource->errorCode();
if(empty($code)) return false; if(empty($code)) {
return false;
}
// Skip 'ok' and undefined 'warning' types. // Skip 'ok' and undefined 'warning' types.
// @see http://docstore.mik.ua/orelly/java-ent/jenut/ch08_06.htm // @see http://docstore.mik.ua/orelly/java-ent/jenut/ch08_06.htm
@ -321,12 +362,13 @@ class PDOConnector extends DBConnector {
} }
public function getLastError() { public function getLastError() {
if ($this->hasError($this->lastStatement)) { $error = null;
$error = $this->lastStatement->errorInfo(); if ($this->lastStatementError) {
$error = $this->lastStatementError;
} elseif($this->hasError($this->pdoConnection)) { } elseif($this->hasError($this->pdoConnection)) {
$error = $this->pdoConnection->errorInfo(); $error = $this->pdoConnection->errorInfo();
} }
if (isset($error)) { if ($error) {
return sprintf("%s-%s: %s", $error[0], $error[1], $error[2]); return sprintf("%s-%s: %s", $error[0], $error[1], $error[2]);
} }
} }
@ -336,8 +378,7 @@ class PDOConnector extends DBConnector {
} }
public function affectedRows() { public function affectedRows() {
if (empty($this->lastStatement)) return 0; return $this->rowCount;
return $this->lastStatement->rowCount();
} }
public function selectDatabase($name) { public function selectDatabase($name) {

View File

@ -146,6 +146,7 @@ abstract class SS_Query implements Iterator {
public function rewind() { public function rewind() {
if ($this->queryHasBegun && $this->numRecords() > 0) { if ($this->queryHasBegun && $this->numRecords() > 0) {
$this->queryHasBegun = false; $this->queryHasBegun = false;
$this->currentRecord = null;
return $this->seek(0); return $this->seek(0);
} }
} }

View File

@ -60,12 +60,12 @@ class Currency extends Decimal {
} }
/** /**
* @deprecated 3.2 Use the "Currency.currency_symbol" config setting instead * @deprecated 4.0 Use the "Currency.currency_symbol" config setting instead
* @param [type] $value [description] * @param [type] $value [description]
*/ */
public static function setCurrencySymbol($value) { public static function setCurrencySymbol($value) {
Deprecation::notice('3.2', 'Use the "Currency.currency_symbol" config setting instead'); Deprecation::notice('4.0', 'Use the "Currency.currency_symbol" config setting instead');
Currency::config()->currency_symbol = $value; Currency::config()->currency_symbol = $value;
} }
} }

View File

@ -84,7 +84,9 @@ abstract class StringField extends DBField {
* @see core/model/fieldtypes/DBField#exists() * @see core/model/fieldtypes/DBField#exists()
*/ */
public function exists() { public function exists() {
return ($this->value || $this->value == '0') || ( !$this->nullifyEmpty && $this->value === ''); return $this->getValue() // All truthy values exist
|| (is_string($this->getValue()) && strlen($this->getValue())) // non-empty strings exist ('0' but not (int)0)
|| (!$this->getNullifyEmpty() && $this->getValue() === ''); // Remove this stupid exemption in 4.0
} }
/** /**

View File

@ -246,7 +246,7 @@ abstract class SQLConditionalExpression extends SQLExpression {
public function getJoins(&$parameters = array()) { public function getJoins(&$parameters = array()) {
if(func_num_args() == 0) { if(func_num_args() == 0) {
Deprecation::notice( Deprecation::notice(
'3.2', '4.0',
'SQLConditionalExpression::getJoins() now may produce parameters which are necessary to 'SQLConditionalExpression::getJoins() now may produce parameters which are necessary to
execute this query' execute this query'
); );
@ -371,7 +371,7 @@ abstract class SQLConditionalExpression extends SQLExpression {
/** /**
* Set a WHERE clause. * Set a WHERE clause.
* *
* @see SQLSelect::addWhere() for syntax examples * @see SQLConditionalExpression::addWhere() for syntax examples
* *
* @param mixed $where Predicate(s) to set, as escaped SQL statements or paramaterised queries * @param mixed $where Predicate(s) to set, as escaped SQL statements or paramaterised queries
* @param mixed $where,... Unlimited additional predicates * @param mixed $where,... Unlimited additional predicates
@ -473,7 +473,7 @@ abstract class SQLConditionalExpression extends SQLExpression {
} }
/** /**
* @see SQLSelect::addWhere() * @see SQLConditionalExpression::addWhere()
* *
* @param mixed $filters Predicate(s) to set, as escaped SQL statements or paramaterised queries * @param mixed $filters Predicate(s) to set, as escaped SQL statements or paramaterised queries
* @param mixed $filters,... Unlimited additional predicates * @param mixed $filters,... Unlimited additional predicates
@ -487,7 +487,7 @@ abstract class SQLConditionalExpression extends SQLExpression {
} }
/** /**
* @see SQLSelect::addWhere() * @see SQLConditionalExpression::addWhere()
* *
* @param mixed $filters Predicate(s) to set, as escaped SQL statements or paramaterised queries * @param mixed $filters Predicate(s) to set, as escaped SQL statements or paramaterised queries
* @param mixed $filters,... Unlimited additional predicates * @param mixed $filters,... Unlimited additional predicates

View File

@ -24,18 +24,18 @@ abstract class SQLExpression {
protected $replacementsNew = array(); protected $replacementsNew = array();
/** /**
* @deprecated since version 3.2 * @deprecated since version 4.0
*/ */
public function __get($field) { public function __get($field) {
Deprecation::notice('3.2', 'use get{Field} to get the necessary protected field\'s value'); Deprecation::notice('4.0', 'use get{Field} to get the necessary protected field\'s value');
return $this->$field; return $this->$field;
} }
/** /**
* @deprecated since version 3.2 * @deprecated since version 4.0
*/ */
public function __set($field, $value) { public function __set($field, $value) {
Deprecation::notice('3.2', 'use set{Field} to set the necessary protected field\'s value'); Deprecation::notice('4.0', 'use set{Field} to set the necessary protected field\'s value');
return $this->$field = $value; return $this->$field = $value;
} }

View File

@ -10,6 +10,14 @@
*/ */
class SQLQuery extends SQLSelect { class SQLQuery extends SQLSelect {
/**
* If this is true, this statement will delete rather than select.
*
* @deprecated since version 4.0
* @var boolean
*/
protected $isDelete = false;
/** /**
* @deprecated since version 4.0 * @deprecated since version 4.0
*/ */
@ -21,19 +29,180 @@ class SQLQuery extends SQLSelect {
} }
/** /**
* @deprecated since version 3.2 * @deprecated since version 4.0
*/ */
public function setDelete($value) { public function setDelete($value) {
$message = 'SQLQuery->setDelete no longer works. Create a SQLDelete object instead, or use toDelete()'; Deprecation::notice('4.0', 'SQLQuery::setDelete is deprecated. Use toDelete instead');
Deprecation::notice('3.2', $message); $this->isDelete = $value;
throw new BadMethodCallException($message);
} }
/** /**
* @deprecated since version 3.2 * @deprecated since version 4.0
*/ */
public function getDelete() { public function getDelete() {
Deprecation::notice('3.2', 'Use SQLDelete object instead'); Deprecation::notice('4.0', 'SQLQuery::getDelete is deprecated. Use SQLSelect or SQLDelete instead');
return false; return $this->isDelete;
}
public function sql(&$parameters = array()) {
return $this->toAppropriateExpression()->sql($parameters);
}
/**
* Get helper class for flattening parameterised conditions
*
* @return SQLQuery_ParameterInjector
*/
protected function getParameterInjector() {
return Injector::inst()->get('SQLQuery_ParameterInjector');
}
/**
* Return a list of SQL where conditions (flattened as a list of strings)
*
* @return array
*/
public function getWhere() {
Deprecation::notice(
'4.0',
'SQLQuery::getWhere is non-parameterised for backwards compatibility. '.
'Use ->toAppropriateExpression()->getWhere() instead'
);
$conditions = parent::getWhere();
// This is where any benefits of parameterised queries die
return $this
->getParameterInjector()
->injectConditions($conditions);
}
/**
* Convert this SQLQuery to a SQLExpression based on its
* internal $delete state (Normally SQLSelect or SQLDelete)
*
* @return SQLExpression
*/
public function toAppropriateExpression() {
if($this->isDelete) {
return parent::toDelete();
} else {
return parent::toSelect();
}
}
public function toSelect() {
if($this->isDelete) {
user_error(
'SQLQuery::toSelect called when $isDelete is true. Use ' .
'toAppropriateExpression() instead',
E_USER_WARNING
);
}
return parent::toSelect();
}
public function toDelete() {
if(!$this->isDelete) {
user_error(
'SQLQuery::toDelete called when $isDelete is false. Use ' .
'toAppropriateExpression() instead',
E_USER_WARNING
);
}
parent::toDelete();
}
}
/**
* Provides conversion of parameterised SQL to flattened SQL strings
*
* @deprecated since version 4.0
*/
class SQLQuery_ParameterInjector {
public function __construct() {
Deprecation::notice('4.0', "Use SQLSelect / SQLDelete instead of SQLQuery");
}
/**
* Given a list of parameterised conditions, return a flattened
* list of condition strings
*
* @param array $conditions
* @return array
*/
public function injectConditions($conditions) {
$result = array();
foreach($conditions as $condition) {
// Evaluate the result of SQLConditionGroup here
if($condition instanceof SQLConditionGroup) {
$predicate = $condition->conditionSQL($parameters);
if(!empty($predicate)) {
$result[] = $this->injectValues($predicate, $parameters);
}
} else {
foreach($condition as $predicate => $parameters) {
$result[] = $this->injectValues($predicate, $parameters);
}
}
}
return $result;
}
/**
* Merge parameters into a SQL prepared condition
*
* @param string $sql
* @param array $parameters
* @return string
*/
protected function injectValues($sql, $parameters) {
$segments = preg_split('/\?/', $sql);
$joined = '';
$inString = false;
for($i = 0; $i < count($segments); $i++) {
// Append next segment
$joined .= $segments[$i];
// Don't add placeholder after last segment
if($i === count($segments) - 1) {
break;
}
// check string escape on previous fragment
if($this->checkStringTogglesLiteral($segments[$i])) {
$inString = !$inString;
}
// Append placeholder replacement
if($inString) {
// Literal questionmark
$joined .= '?';
continue;
}
// Encode and insert next parameter
$next = array_shift($parameters);
if(is_array($next) && isset($next['value'])) {
$next = $next['value'];
}
$joined .= "'".Convert::raw2sql($next)."'";
}
return $joined;
}
/**
* Determines if the SQL fragment either breaks into or out of a string literal
* by counting single quotes
*
* Handles double-quote escaped quotes as well as slash escaped quotes
*
* @param string $input The SQL fragment
* @return boolean True if the string breaks into or out of a string literal
*/
protected function checkStringTogglesLiteral($input) {
// Remove escaped backslashes, count them!
$input = preg_replace('/\\\\\\\\/', '', $input);
// Count quotes
$totalQuotes = substr_count($input, "'"); // Includes double quote escaped quotes
$escapedQuotes = substr_count($input, "\\'");
return (($totalQuotes - $escapedQuotes) % 2) !== 0;
} }
} }

View File

@ -36,10 +36,10 @@ class BBCodeParser extends TextParser {
private static $smilies_location = null; private static $smilies_location = null;
/** /**
* @deprecated 3.2 Use the "BBCodeParser.smilies_location" config setting instead * @deprecated 4.0 Use the "BBCodeParser.smilies_location" config setting instead
*/ */
public static function smilies_location() { public static function smilies_location() {
Deprecation::notice('3.2', 'Use the "BBCodeParser.smilies_location" config setting instead'); Deprecation::notice('4.0', 'Use the "BBCodeParser.smilies_location" config setting instead');
if(!BBCodeParser::$smilies_location) { if(!BBCodeParser::$smilies_location) {
return FRAMEWORK_DIR . '/images/smilies'; return FRAMEWORK_DIR . '/images/smilies';
} }
@ -47,42 +47,42 @@ class BBCodeParser extends TextParser {
} }
/** /**
* @deprecated 3.2 Use the "BBCodeParser.smilies_location" config setting instead * @deprecated 4.0 Use the "BBCodeParser.smilies_location" config setting instead
*/ */
public static function set_icon_folder($path) { public static function set_icon_folder($path) {
Deprecation::notice('3.2', 'Use the "BBCodeParser.smilies_location" config setting instead'); Deprecation::notice('4.0', 'Use the "BBCodeParser.smilies_location" config setting instead');
static::config()->smilies_location = $path; static::config()->smilies_location = $path;
} }
/** /**
* @deprecated 3.2 Use the "BBCodeParser.autolink_urls" config setting instead * @deprecated 4.0 Use the "BBCodeParser.autolink_urls" config setting instead
*/ */
public static function autolinkUrls() { public static function autolinkUrls() {
Deprecation::notice('3.2', 'Use the "BBCodeParser.autolink_urls" config setting instead'); Deprecation::notice('4.0', 'Use the "BBCodeParser.autolink_urls" config setting instead');
return static::config()->autolink_urls; return static::config()->autolink_urls;
} }
/** /**
* @deprecated 3.2 Use the "BBCodeParser.autolink_urls" config setting instead * @deprecated 4.0 Use the "BBCodeParser.autolink_urls" config setting instead
*/ */
public static function disable_autolink_urls($autolink = false) { public static function disable_autolink_urls($autolink = false) {
Deprecation::notice('3.2', 'Use the "BBCodeParser.autolink_urls" config setting instead'); Deprecation::notice('4.0', 'Use the "BBCodeParser.autolink_urls" config setting instead');
static::config()->autolink_urls = $autolink; static::config()->autolink_urls = $autolink;
} }
/** /**
* @deprecated 3.2 Use the "BBCodeParser.allow_smilies" config setting instead * @deprecated 4.0 Use the "BBCodeParser.allow_smilies" config setting instead
*/ */
public static function smiliesAllowed() { public static function smiliesAllowed() {
Deprecation::notice('3.2', 'Use the "BBCodeParser.allow_smilies" config setting instead'); Deprecation::notice('4.0', 'Use the "BBCodeParser.allow_smilies" config setting instead');
return static::config()->allow_smilies; return static::config()->allow_smilies;
} }
/** /**
* @deprecated 3.2 Use the "BBCodeParser.allow_smilies" config setting instead * @deprecated 4.0 Use the "BBCodeParser.allow_smilies" config setting instead
*/ */
public static function enable_smilies() { public static function enable_smilies() {
Deprecation::notice('3.2', 'Use the "BBCodeParser.allow_smilies" config setting instead'); Deprecation::notice('4.0', 'Use the "BBCodeParser.allow_smilies" config setting instead');
static::config()->allow_similies = true; static::config()->allow_similies = true;
} }

View File

@ -103,11 +103,47 @@ class ShortcodeParser extends Object {
$this->shortcodes = array(); $this->shortcodes = array();
} }
/**
* Call a shortcode and return its replacement text
* Returns false if the shortcode isn't registered
*/
public function callShortcode($tag, $attributes, $content, $extra = array()) { public function callShortcode($tag, $attributes, $content, $extra = array()) {
if (!isset($this->shortcodes[$tag])) return false; if (!$tag || !isset($this->shortcodes[$tag])) return false;
return call_user_func($this->shortcodes[$tag], $attributes, $content, $this, $tag, $extra); return call_user_func($this->shortcodes[$tag], $attributes, $content, $this, $tag, $extra);
} }
/**
* Return the text to insert in place of a shoprtcode.
* Behaviour in the case of missing shortcodes depends on the setting of ShortcodeParser::$error_behavior.
* @param $tag A map containing the the following keys:
* - 'open': The name of the tag
* - 'attrs': Attributes of the tag
* - 'content': Content of the tag
* @param $extra Extra-meta data
* @param $isHTMLAllowed A boolean indicating whether it's okay to insert HTML tags into the result
*/
function getShortcodeReplacementText($tag, $extra = array(), $isHTMLAllowed = true) {
$content = $this->callShortcode($tag['open'], $tag['attrs'], $tag['content'], $extra);
// Missing tag
if ($content === false) {
if(ShortcodeParser::$error_behavior == ShortcodeParser::ERROR) {
user_error('Unknown shortcode tag '.$tag['open'], E_USER_ERRROR);
}
else if (self::$error_behavior == self::WARN && $isHTMLAllowed) {
$content = '<strong class="warning">'.$tag['text'].'</strong>';
}
else if(ShortcodeParser::$error_behavior == ShortcodeParser::STRIP) {
return '';
}
else {
return $tag['text'];
}
}
return $content;
}
// -------------------------------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------------------------------
protected function removeNode($node) { protected function removeNode($node) {
@ -207,6 +243,7 @@ class ShortcodeParser extends Object {
protected function extractTags($content) { protected function extractTags($content) {
$tags = array(); $tags = array();
// Step 1: perform basic regex scan of individual tags
if(preg_match_all(static::tagrx(), $content, $matches, PREG_SET_ORDER | PREG_OFFSET_CAPTURE)) { if(preg_match_all(static::tagrx(), $content, $matches, PREG_SET_ORDER | PREG_OFFSET_CAPTURE)) {
foreach($matches as $match) { foreach($matches as $match) {
// Ignore any elements // Ignore any elements
@ -236,8 +273,9 @@ class ShortcodeParser extends Object {
'escaped' => !empty($match['oesc'][0]) || !empty($match['cesc1'][0]) || !empty($match['cesc2'][0]) 'escaped' => !empty($match['oesc'][0]) || !empty($match['cesc1'][0]) || !empty($match['cesc2'][0])
); );
} }
} }
// Step 2: cluster open/close tag pairs into single entries
$i = count($tags); $i = count($tags);
while($i--) { while($i--) {
if(!empty($tags[$i]['close'])) { if(!empty($tags[$i]['close'])) {
@ -283,6 +321,17 @@ class ShortcodeParser extends Object {
} }
} }
// Step 3: remove any tags that don't have handlers registered
// Only do this if self::$error_behavior == self::LEAVE
// This is optional but speeds things up.
if(self::$error_behavior == self::LEAVE) {
foreach($tags as $i => $tag) {
if(empty($this->shortcodes[$tag['open']])) {
unset($tags[$i]);
}
}
}
return array_values($tags); return array_values($tags);
} }
@ -337,24 +386,11 @@ class ShortcodeParser extends Object {
if($tags) { if($tags) {
$node->nodeValue = $this->replaceTagsWithText($node->nodeValue, $tags, $node->nodeValue = $this->replaceTagsWithText($node->nodeValue, $tags,
function($idx, $tag) use ($parser, $extra){ function($idx, $tag) use ($parser, $extra) {
$content = $parser->callShortcode($tag['open'], $tag['attrs'], $tag['content'], $extra); return $parser->getShortcodeReplacementText($tag, $extra, false);
if ($content === false) {
if(ShortcodeParser::$error_behavior == ShortcodeParser::ERROR) {
user_error('Unknown shortcode tag '.$tag['open'], E_USER_ERRROR);
}
else if(ShortcodeParser::$error_behavior == ShortcodeParser::STRIP) {
return '';
}
else {
return $tag['text'];
}
} }
);
return $content; }
});
}
} }
} }
@ -372,10 +408,10 @@ class ShortcodeParser extends Object {
$content = $this->replaceTagsWithText($content, $tags, function($idx, $tag) use ($markerClass) { $content = $this->replaceTagsWithText($content, $tags, function($idx, $tag) use ($markerClass) {
return '<img class="'.$markerClass.'" data-tagid="'.$idx.'" />'; return '<img class="'.$markerClass.'" data-tagid="'.$idx.'" />';
}); });
} }
return array($content, $tags); return array($content, $tags);
} }
protected function findParentsForMarkers($nodes) { protected function findParentsForMarkers($nodes) {
$parents = array(); $parents = array();
@ -477,23 +513,7 @@ class ShortcodeParser extends Object {
* @param array $tag * @param array $tag
*/ */
protected function replaceMarkerWithContent($node, $tag) { protected function replaceMarkerWithContent($node, $tag) {
$content = false; $content = $this->getShortcodeReplacementText($tag);
if($tag['open']) $content = $this->callShortcode($tag['open'], $tag['attrs'], $tag['content']);
if ($content === false) {
if(self::$error_behavior == self::ERROR) {
user_error('Unknown shortcode tag '.$tag['open'], E_USER_ERRROR);
}
if (self::$error_behavior == self::WARN) {
$content = '<strong class="warning">'.$tag['text'].'</strong>';
}
else if (self::$error_behavior == self::LEAVE) {
$content = $tag['text'];
}
else {
// self::$error_behavior == self::STRIP - NOP
}
}
if ($content) { if ($content) {
$parsed = Injector::inst()->create('HTMLValue', $content); $parsed = Injector::inst()->create('HTMLValue', $content);
@ -567,8 +587,21 @@ class ShortcodeParser extends Object {
$this->replaceMarkerWithContent($shortcode, $tag); $this->replaceMarkerWithContent($shortcode, $tag);
} }
return $htmlvalue->getContent(); $content = $htmlvalue->getContent();
// Clean up any marker classes left over, for example, those injected into <script> tags
$parser = $this;
$content = preg_replace_callback(
// Not a general-case parser; assumes that the HTML generated in replaceElementTagsWithMarkers()
// hasn't been heavily modified
'/<img[^>]+class="'.preg_quote(self::$marker_class).'"[^>]+data-tagid="([^"]+)"[^>]+>/i',
function ($matches) use ($tags, $parser) {
$tag = $tags[$matches[1]];
return $parser->getShortcodeReplacementText($tag);
},
$content
);
return $content;
} }
} }

View File

@ -18,7 +18,7 @@
// TODO .middleColumn styling should probably be theme specific (eg cms ui will look different than blackcandy) // TODO .middleColumn styling should probably be theme specific (eg cms ui will look different than blackcandy)
// so we should move this style into the cms and black candy files // so we should move this style into the cms and black candy files
min-width: 510px; min-width: 510px;
max-width: 600px; // Capped width. 600px is about the average size of a preview split view max-width: $grid-x * 87; // Capped width to line up with text fields
width:100%; width:100%;
margin-left:0; margin-left:0;
clear:both; clear:both;

40
search/filters/FulltextFilter.php Normal file → Executable file
View File

@ -17,22 +17,23 @@
* database table, using the {$indexes} hash in your DataObject subclass: * database table, using the {$indexes} hash in your DataObject subclass:
* *
* <code> * <code>
* static $indexes = array( * private static $indexes = array(
* 'SearchFields' => 'fulltext(Name, Title, Description)' * 'SearchFields' => 'fulltext(Name, Title, Description)'
* ); * );
* </code> * </code>
* *
* @package framework * @todo Add support for databases besides MySQL
* @subpackage search
*/ */
class FulltextFilter extends SearchFilter { class FulltextFilter extends SearchFilter {
protected function applyOne(DataQuery $query) { protected function applyOne(DataQuery $query) {
$this->model = $query->applyRelation($this->relation);
$predicate = sprintf("MATCH (%s) AGAINST (?)", $this->getDbName()); $predicate = sprintf("MATCH (%s) AGAINST (?)", $this->getDbName());
return $query->where(array($predicate => $this->getValue())); return $query->where(array($predicate => $this->getValue()));
} }
protected function excludeOne(DataQuery $query) { protected function excludeOne(DataQuery $query) {
$this->model = $query->applyRelation($this->relation);
$predicate = sprintf("NOT MATCH (%s) AGAINST (?)", $this->getDbName()); $predicate = sprintf("NOT MATCH (%s) AGAINST (?)", $this->getDbName());
return $query->where(array($predicate => $this->getValue())); return $query->where(array($predicate => $this->getValue()));
} }
@ -40,4 +41,37 @@ class FulltextFilter extends SearchFilter {
public function isEmpty() { public function isEmpty() {
return $this->getValue() === array() || $this->getValue() === null || $this->getValue() === ''; return $this->getValue() === array() || $this->getValue() === null || $this->getValue() === '';
} }
/**
* This implementation allows for a list of columns to be passed into MATCH() instead of just one.
*
* @example
* <code>
* MyDataObject::get()->filter('SearchFields:fulltext', 'search term')
* </code>
*
* @return string
*/
public function getDbName() {
$indexes = Config::inst()->get($this->model, "indexes");
if(is_array($indexes) && array_key_exists($this->getName(), $indexes)) {
$index = $indexes[$this->getName()];
if(is_array($index) && array_key_exists("value", $index)) {
return $index['value'];
} else {
// Parse a fulltext string (eg. fulltext ("ColumnA", "ColumnB")) to figure out which columns
// we need to search.
if(preg_match('/^fulltext\s+\((.+)\)$/i', $index, $matches)) {
return $matches[1];
} else {
throw new Exception("Invalid fulltext index format for '" . $this->getName()
. "' on '" . $this->model . "'");
}
}
}
return parent::getDbName();
}
} }

View File

@ -164,6 +164,12 @@ abstract class SearchFilter extends Object {
if($this->name == "NULL") { if($this->name == "NULL") {
return $this->name; return $this->name;
} }
// Ensure that we're dealing with a DataObject.
if (!is_subclass_of($this->model, 'DataObject')) {
throw new InvalidArgumentException(
"Model supplied to " . get_class($this) . " should be an instance of DataObject."
);
}
$candidateClass = ClassInfo::table_for_object_field( $candidateClass = ClassInfo::table_for_object_field(
$this->model, $this->model,
@ -177,7 +183,7 @@ abstract class SearchFilter extends Object {
return '"' . implode('"."', $parts) . '"'; return '"' . implode('"."', $parts) . '"';
} }
return "\"$candidateClass\".\"$this->name\""; return sprintf('"%s"."%s"', $candidateClass, $this->name);
} }
/** /**
@ -194,7 +200,6 @@ abstract class SearchFilter extends Object {
return $dbField->RAW(); return $dbField->RAW();
} }
/** /**
* Apply filter criteria to a SQL query. * Apply filter criteria to a SQL query.
* *

View File

@ -129,9 +129,10 @@ class ChangePasswordForm extends Form {
_t( _t(
'Member.INVALIDNEWPASSWORD', 'Member.INVALIDNEWPASSWORD',
"We couldn't accept that password: {password}", "We couldn't accept that password: {password}",
array('password' => nl2br("\n".$isValid->starredList())) array('password' => nl2br("\n".Convert::raw2xml($isValid->starredList())))
), ),
"bad" "bad",
false
); );
// redirect back to the form, instead of using redirectBack() which could send the user elsewhere. // redirect back to the form, instead of using redirectBack() which could send the user elsewhere.

View File

@ -59,7 +59,9 @@ class Member extends DataObject implements TemplateGlobalProvider {
private static $has_one = array(); private static $has_one = array();
private static $has_many = array(); private static $has_many = array(
'LoggedPasswords' => 'MemberPassword',
);
private static $many_many = array(); private static $many_many = array();
@ -202,10 +204,10 @@ class Member extends DataObject implements TemplateGlobalProvider {
private static $temp_id_lifetime = 259200; private static $temp_id_lifetime = 259200;
/** /**
* @deprecated 3.2 Use the "Member.session_regenerate_id" config setting instead * @deprecated 4.0 Use the "Member.session_regenerate_id" config setting instead
*/ */
public static function set_session_regenerate_id($bool) { public static function set_session_regenerate_id($bool) {
Deprecation::notice('3.2', 'Use the "Member.session_regenerate_id" config setting instead'); Deprecation::notice('4.0', 'Use the "Member.session_regenerate_id" config setting instead');
self::config()->session_regenerate_id = $bool; self::config()->session_regenerate_id = $bool;
} }
@ -275,11 +277,11 @@ class Member extends DataObject implements TemplateGlobalProvider {
* RewriteCond %{HTTP_COOKIE} !SS_LOGGED_IN=1 * RewriteCond %{HTTP_COOKIE} !SS_LOGGED_IN=1
* </pre> * </pre>
* *
* @deprecated 3.2 Use the "Member.login_marker_cookie" config setting instead * @deprecated 4.0 Use the "Member.login_marker_cookie" config setting instead
* @param $cookieName string The name of the cookie to set. * @param $cookieName string The name of the cookie to set.
*/ */
public static function set_login_marker_cookie($cookieName) { public static function set_login_marker_cookie($cookieName) {
Deprecation::notice('3.2', 'Use the "Member.login_marker_cookie" config setting instead'); Deprecation::notice('4.0', 'Use the "Member.login_marker_cookie" config setting instead');
self::config()->login_marker_cookie = $cookieName; self::config()->login_marker_cookie = $cookieName;
} }
@ -369,11 +371,11 @@ class Member extends DataObject implements TemplateGlobalProvider {
* Get the field used for uniquely identifying a member * Get the field used for uniquely identifying a member
* in the database. {@see Member::$unique_identifier_field} * in the database. {@see Member::$unique_identifier_field}
* *
* @deprecated 3.2 Use the "Member.unique_identifier_field" config setting instead * @deprecated 4.0 Use the "Member.unique_identifier_field" config setting instead
* @return string * @return string
*/ */
public static function get_unique_identifier_field() { public static function get_unique_identifier_field() {
Deprecation::notice('3.2', 'Use the "Member.unique_identifier_field" config setting instead'); Deprecation::notice('4.0', 'Use the "Member.unique_identifier_field" config setting instead');
return Member::config()->unique_identifier_field; return Member::config()->unique_identifier_field;
} }
@ -381,11 +383,11 @@ class Member extends DataObject implements TemplateGlobalProvider {
* Set the field used for uniquely identifying a member * Set the field used for uniquely identifying a member
* in the database. {@see Member::$unique_identifier_field} * in the database. {@see Member::$unique_identifier_field}
* *
* @deprecated 3.2 Use the "Member.unique_identifier_field" config setting instead * @deprecated 4.0 Use the "Member.unique_identifier_field" config setting instead
* @param $field The field name to set as the unique field * @param $field The field name to set as the unique field
*/ */
public static function set_unique_identifier_field($field) { public static function set_unique_identifier_field($field) {
Deprecation::notice('3.2', 'Use the "Member.unique_identifier_field" config setting instead'); Deprecation::notice('4.0', 'Use the "Member.unique_identifier_field" config setting instead');
Member::config()->unique_identifier_field = $field; Member::config()->unique_identifier_field = $field;
} }
@ -407,20 +409,20 @@ class Member extends DataObject implements TemplateGlobalProvider {
* Set the number of days that a password should be valid for. * Set the number of days that a password should be valid for.
* Set to null (the default) to have passwords never expire. * Set to null (the default) to have passwords never expire.
* *
* @deprecated 3.2 Use the "Member.password_expiry_days" config setting instead * @deprecated 4.0 Use the "Member.password_expiry_days" config setting instead
*/ */
public static function set_password_expiry($days) { public static function set_password_expiry($days) {
Deprecation::notice('3.2', 'Use the "Member.password_expiry_days" config setting instead'); Deprecation::notice('4.0', 'Use the "Member.password_expiry_days" config setting instead');
self::config()->password_expiry_days = $days; self::config()->password_expiry_days = $days;
} }
/** /**
* Configure the security system to lock users out after this many incorrect logins * Configure the security system to lock users out after this many incorrect logins
* *
* @deprecated 3.2 Use the "Member.lock_out_after_incorrect_logins" config setting instead * @deprecated 4.0 Use the "Member.lock_out_after_incorrect_logins" config setting instead
*/ */
public static function lock_out_after_incorrect_logins($numLogins) { public static function lock_out_after_incorrect_logins($numLogins) {
Deprecation::notice('3.2', 'Use the "Member.lock_out_after_incorrect_logins" config setting instead'); Deprecation::notice('4.0', 'Use the "Member.lock_out_after_incorrect_logins" config setting instead');
self::config()->lock_out_after_incorrect_logins = $numLogins; self::config()->lock_out_after_incorrect_logins = $numLogins;
} }
@ -879,6 +881,26 @@ class Member extends DataObject implements TemplateGlobalProvider {
} }
} }
public function onAfterDelete() {
parent::onAfterDelete();
//prevent orphaned records remaining in the DB
$this->deletePasswordLogs();
}
/**
* Delete the MemberPassword objects that are associated to this user
*
* @return self
*/
protected function deletePasswordLogs() {
foreach ($this->LoggedPasswords() as $password) {
$password->delete();
$password->destroy();
}
return $this;
}
/** /**
* If any admin groups are requested, deny the whole save operation. * If any admin groups are requested, deny the whole save operation.
* *
@ -1304,6 +1326,9 @@ class Member extends DataObject implements TemplateGlobalProvider {
// Members are displayed within group edit form in SecurityAdmin // Members are displayed within group edit form in SecurityAdmin
$fields->removeByName('Groups'); $fields->removeByName('Groups');
// Members shouldn't be able to directly view/edit logged passwords
$fields->removeByName('LoggedPasswords');
if(Permission::check('EDIT_PERMISSIONS')) { if(Permission::check('EDIT_PERMISSIONS')) {
$groupsMap = array(); $groupsMap = array();
foreach(Group::get() as $group) { foreach(Group::get() as $group) {

View File

@ -558,26 +558,26 @@ class Permission extends DataObject implements TemplateGlobalProvider {
/** /**
* add a permission represented by the $code to the {@link slef::$hidden_permissions} list * add a permission represented by the $code to the {@link slef::$hidden_permissions} list
* *
* @deprecated 3.2 Use "Permission.hidden_permissions" config setting instead * @deprecated 4.0 Use "Permission.hidden_permissions" config setting instead
* @param $code string - the permissions code * @param $code string - the permissions code
* @return void * @return void
*/ */
public static function add_to_hidden_permissions($code){ public static function add_to_hidden_permissions($code){
if(is_string($codes)) $codes = array($codes); if(is_string($codes)) $codes = array($codes);
Deprecation::notice('3.2', 'Use "Permission.hidden_permissions" config setting instead'); Deprecation::notice('4.0', 'Use "Permission.hidden_permissions" config setting instead');
Config::inst()->update('Permission', 'hidden_permissions', $codes); Config::inst()->update('Permission', 'hidden_permissions', $codes);
} }
/** /**
* remove a permission represented by the $code from the {@link slef::$hidden_permissions} list * remove a permission represented by the $code from the {@link slef::$hidden_permissions} list
* *
* @deprecated 3.2 Use "Permission.hidden_permissions" config setting instead * @deprecated 4.0 Use "Permission.hidden_permissions" config setting instead
* @param $code string - the permissions code * @param $code string - the permissions code
* @return void * @return void
*/ */
public static function remove_from_hidden_permissions($code){ public static function remove_from_hidden_permissions($code){
if(is_string($codes)) $codes = array($codes); if(is_string($codes)) $codes = array($codes);
Deprecation::notice('3.2', 'Use "Permission.hidden_permissions" config setting instead'); Deprecation::notice('4.0', 'Use "Permission.hidden_permissions" config setting instead');
Config::inst()->remove('Permission', 'hidden_permissions', $codes); Config::inst()->remove('Permission', 'hidden_permissions', $codes);
} }
@ -587,12 +587,12 @@ class Permission extends DataObject implements TemplateGlobalProvider {
* Permissions can be grouped by nesting arrays. Scalar values are always * Permissions can be grouped by nesting arrays. Scalar values are always
* treated as permissions. * treated as permissions.
* *
* @deprecated 3.2 Use "Permission.declared_permissions" config setting instead * @deprecated 4.0 Use "Permission.declared_permissions" config setting instead
* @param array $permArray A (possibly nested) array of permissions to * @param array $permArray A (possibly nested) array of permissions to
* declare for the system. * declare for the system.
*/ */
public static function declare_permissions($permArray) { public static function declare_permissions($permArray) {
Deprecation::notice('3.2', 'Use "Permission.declared_permissions" config setting instead'); Deprecation::notice('4.0', 'Use "Permission.declared_permissions" config setting instead');
self::config()->declared_permissions = $permArray; self::config()->declared_permissions = $permArray;
} }

View File

@ -71,6 +71,10 @@ class PermissionCheckboxSetField extends FormField {
return $this->hiddenPermissions; return $this->hiddenPermissions;
} }
/**
* @param array $properties
* @return HTMLText
*/
public function Field($properties = array()) { public function Field($properties = array()) {
Requirements::css(FRAMEWORK_DIR . '/css/CheckboxSetField.css'); Requirements::css(FRAMEWORK_DIR . '/css/CheckboxSetField.css');
Requirements::javascript(FRAMEWORK_DIR . '/javascript/PermissionCheckboxSetField.js'); Requirements::javascript(FRAMEWORK_DIR . '/javascript/PermissionCheckboxSetField.js');
@ -227,7 +231,8 @@ class PermissionCheckboxSetField extends FormField {
} }
} }
if($this->readonly) { if($this->readonly) {
return "<ul id=\"{$this->id()}\" class=\"optionset checkboxsetfield{$this->extraClass()}\">\n" . return DBField::create_field('HTMLText',
"<ul id=\"{$this->id()}\" class=\"optionset checkboxsetfield{$this->extraClass()}\">\n" .
"<li class=\"help\">" . "<li class=\"help\">" .
_t( _t(
'Permissions.UserPermissionsIntro', 'Permissions.UserPermissionsIntro',
@ -236,11 +241,14 @@ class PermissionCheckboxSetField extends FormField {
) . ) .
"</li>" . "</li>" .
$options . $options .
"</ul>\n"; "</ul>\n"
);
} else { } else {
return "<ul id=\"{$this->id()}\" class=\"optionset checkboxsetfield{$this->extraClass()}\">\n" . return DBField::create_field('HTMLText',
"<ul id=\"{$this->id()}\" class=\"optionset checkboxsetfield{$this->extraClass()}\">\n" .
$options . $options .
"</ul>\n"; "</ul>\n"
);
} }
} }

View File

@ -132,10 +132,10 @@ class Security extends Controller implements TemplateGlobalProvider {
/** /**
* Get location of word list file * Get location of word list file
* *
* @deprecated 3.2 Use the "Security.word_list" config setting instead * @deprecated 4.0 Use the "Security.word_list" config setting instead
*/ */
public static function get_word_list() { public static function get_word_list() {
Deprecation::notice('3.2', 'Use the "Security.word_list" config setting instead'); Deprecation::notice('4.0', 'Use the "Security.word_list" config setting instead');
return self::config()->word_list; return self::config()->word_list;
} }
@ -165,22 +165,22 @@ class Security extends Controller implements TemplateGlobalProvider {
/** /**
* Set location of word list file * Set location of word list file
* *
* @deprecated 3.2 Use the "Security.word_list" config setting instead * @deprecated 4.0 Use the "Security.word_list" config setting instead
* @param string $wordListFile Location of word list file * @param string $wordListFile Location of word list file
*/ */
public static function set_word_list($wordListFile) { public static function set_word_list($wordListFile) {
Deprecation::notice('3.2', 'Use the "Security.word_list" config setting instead'); Deprecation::notice('4.0', 'Use the "Security.word_list" config setting instead');
self::config()->word_list = $wordListFile; self::config()->word_list = $wordListFile;
} }
/** /**
* Set the default message set used in permissions failures. * Set the default message set used in permissions failures.
* *
* @deprecated 3.2 Use the "Security.default_message_set" config setting instead * @deprecated 4.0 Use the "Security.default_message_set" config setting instead
* @param string|array $messageSet * @param string|array $messageSet
*/ */
public static function set_default_message_set($messageSet) { public static function set_default_message_set($messageSet) {
Deprecation::notice('3.2', 'Use the "Security.default_message_set" config setting instead'); Deprecation::notice('4.0', 'Use the "Security.default_message_set" config setting instead');
self::config()->default_message_set = $messageSet; self::config()->default_message_set = $messageSet;
} }
@ -888,12 +888,12 @@ class Security extends Controller implements TemplateGlobalProvider {
* This prevents sharing of the session across several sites in the * This prevents sharing of the session across several sites in the
* domain. * domain.
* *
* @deprecated 3.2 Use the "Security.strict_path_checking" config setting instead * @deprecated 4.0 Use the "Security.strict_path_checking" config setting instead
* @param boolean $strictPathChecking To enable or disable strict patch * @param boolean $strictPathChecking To enable or disable strict patch
* checking. * checking.
*/ */
public static function setStrictPathChecking($strictPathChecking) { public static function setStrictPathChecking($strictPathChecking) {
Deprecation::notice('3.2', 'Use the "Security.strict_path_checking" config setting instead'); Deprecation::notice('4.0', 'Use the "Security.strict_path_checking" config setting instead');
self::config()->strict_path_checking = $strictPathChecking; self::config()->strict_path_checking = $strictPathChecking;
} }
@ -901,11 +901,11 @@ class Security extends Controller implements TemplateGlobalProvider {
/** /**
* Get strict path checking * Get strict path checking
* *
* @deprecated 3.2 Use the "Security.strict_path_checking" config setting instead * @deprecated 4.0 Use the "Security.strict_path_checking" config setting instead
* @return boolean Status of strict path checking * @return boolean Status of strict path checking
*/ */
public static function getStrictPathChecking() { public static function getStrictPathChecking() {
Deprecation::notice('3.2', 'Use the "Security.strict_path_checking" config setting instead'); Deprecation::notice('4.0', 'Use the "Security.strict_path_checking" config setting instead');
return self::config()->strict_path_checking; return self::config()->strict_path_checking;
} }
@ -913,23 +913,23 @@ class Security extends Controller implements TemplateGlobalProvider {
/** /**
* Set the password encryption algorithm * Set the password encryption algorithm
* *
* @deprecated 3.2 Use the "Security.password_encryption_algorithm" config setting instead * @deprecated 4.0 Use the "Security.password_encryption_algorithm" config setting instead
* @param string $algorithm One of the available password encryption * @param string $algorithm One of the available password encryption
* algorithms determined by {@link Security::get_encryption_algorithms()} * algorithms determined by {@link Security::get_encryption_algorithms()}
* @return bool Returns TRUE if the passed algorithm was valid, otherwise FALSE. * @return bool Returns TRUE if the passed algorithm was valid, otherwise FALSE.
*/ */
public static function set_password_encryption_algorithm($algorithm) { public static function set_password_encryption_algorithm($algorithm) {
Deprecation::notice('3.2', 'Use the "Security.password_encryption_algorithm" config setting instead'); Deprecation::notice('4.0', 'Use the "Security.password_encryption_algorithm" config setting instead');
self::config()->password_encryption_algorithm = $algorithm; self::config()->password_encryption_algorithm = $algorithm;
} }
/** /**
* @deprecated 3.2 Use the "Security.password_encryption_algorithm" config setting instead * @deprecated 4.0 Use the "Security.password_encryption_algorithm" config setting instead
* @return String * @return String
*/ */
public static function get_password_encryption_algorithm() { public static function get_password_encryption_algorithm() {
Deprecation::notice('3.2', 'Use the "Security.password_encryption_algorithm" config setting instead'); Deprecation::notice('4.0', 'Use the "Security.password_encryption_algorithm" config setting instead');
return self::config()->password_encryption_algorithm; return self::config()->password_encryption_algorithm;
} }
@ -1022,20 +1022,20 @@ class Security extends Controller implements TemplateGlobalProvider {
* Enable or disable recording of login attempts * Enable or disable recording of login attempts
* through the {@link LoginRecord} object. * through the {@link LoginRecord} object.
* *
* @deprecated 3.2 Use the "Security.login_recording" config setting instead * @deprecated 4.0 Use the "Security.login_recording" config setting instead
* @param boolean $bool * @param boolean $bool
*/ */
public static function set_login_recording($bool) { public static function set_login_recording($bool) {
Deprecation::notice('3.2', 'Use the "Security.login_recording" config setting instead'); Deprecation::notice('4.0', 'Use the "Security.login_recording" config setting instead');
self::$login_recording = (bool)$bool; self::$login_recording = (bool)$bool;
} }
/** /**
* @deprecated 3.2 Use the "Security.login_recording" config setting instead * @deprecated 4.0 Use the "Security.login_recording" config setting instead
* @return boolean * @return boolean
*/ */
public static function login_recording() { public static function login_recording() {
Deprecation::notice('3.2', 'Use the "Security.login_recording" config setting instead'); Deprecation::notice('4.0', 'Use the "Security.login_recording" config setting instead');
return self::$login_recording; return self::$login_recording;
} }
@ -1049,20 +1049,20 @@ class Security extends Controller implements TemplateGlobalProvider {
private static $default_login_dest = ""; private static $default_login_dest = "";
/** /**
* @deprecated 3.2 Use the "Security.default_login_dest" config setting instead * @deprecated 4.0 Use the "Security.default_login_dest" config setting instead
*/ */
public static function set_default_login_dest($dest) { public static function set_default_login_dest($dest) {
Deprecation::notice('3.2', 'Use the "Security.default_login_dest" config setting instead'); Deprecation::notice('4.0', 'Use the "Security.default_login_dest" config setting instead');
self::config()->default_login_dest = $dest; self::config()->default_login_dest = $dest;
} }
/** /**
* Get the default login dest. * Get the default login dest.
* *
* @deprecated 3.2 Use the "Security.default_login_dest" config setting instead * @deprecated 4.0 Use the "Security.default_login_dest" config setting instead
*/ */
public static function default_login_dest() { public static function default_login_dest() {
Deprecation::notice('3.2', 'Use the "Security.default_login_dest" config setting instead'); Deprecation::notice('4.0', 'Use the "Security.default_login_dest" config setting instead');
return self::config()->default_login_dest; return self::config()->default_login_dest;
} }
@ -1085,10 +1085,10 @@ class Security extends Controller implements TemplateGlobalProvider {
/** /**
* Set a custom log-in URL if you have built your own log-in page. * Set a custom log-in URL if you have built your own log-in page.
* *
* @deprecated 3.2 Use the "Security.login_url" config setting instead. * @deprecated 4.0 Use the "Security.login_url" config setting instead.
*/ */
public static function set_login_url($loginUrl) { public static function set_login_url($loginUrl) {
Deprecation::notice('3.2', 'Use the "Security.login_url" config setting instead'); Deprecation::notice('4.0', 'Use the "Security.login_url" config setting instead');
self::config()->update("login_url", $loginUrl); self::config()->update("login_url", $loginUrl);
} }
@ -1118,7 +1118,7 @@ class Security extends Controller implements TemplateGlobalProvider {
/** /**
* Defines global accesible templates variables. * Defines global accessible templates variables.
* *
* @return array * @return array
*/ */

View File

@ -22,7 +22,9 @@ class i18nTextCollectorTask extends BuildTask {
parent::init(); parent::init();
$canAccess = (Director::isDev() || Director::is_cli() || Permission::check("ADMIN")); $canAccess = (Director::isDev() || Director::is_cli() || Permission::check("ADMIN"));
if(!$canAccess) return Security::permissionFailure($this); if(!$canAccess) {
return Security::permissionFailure($this);
}
} }
/** /**
@ -31,13 +33,50 @@ class i18nTextCollectorTask extends BuildTask {
* and write the resultant files in the lang folder of each module. * and write the resultant files in the lang folder of each module.
* *
* @uses DataObject->collectI18nStatics() * @uses DataObject->collectI18nStatics()
*
* @param SS_HTTPRequest $request
*/ */
public function run($request) { public function run($request) {
increase_time_limit_to(); increase_time_limit_to();
$c = new i18nTextCollector($request->getVar('locale')); $collector = i18nTextCollector::create($request->getVar('locale'));
$writer = $request->getVar('writer');
if($writer) $c->setWriter(new $writer()); $merge = $this->getIsMerge($request);
$restrictModules = ($request->getVar('module')) ? explode(',', $request->getVar('module')) : null;
return $c->run($restrictModules, (bool)$request->getVar('merge')); // Custom writer
$writerName = $request->getVar('writer');
if($writerName) {
$writer = Injector::inst()->get($writerName);
$collector->setWriter($writer);
}
// Get restrictions
$restrictModules = ($request->getVar('module'))
? explode(',', $request->getVar('module'))
: null;
$collector->run($restrictModules, $merge);
Debug::message(__CLASS__ . " completed!", false);
}
/**
* Check if we should merge
*
* @param SS_HTTPRequest $request
*/
protected function getIsMerge($request) {
$merge = $request->getVar('merge');
// Default to false if not given
if(!isset($merge)) {
Deprecation::notice(
"4.0",
"merge will be enabled by default in 4.0. Please use merge=false if you do not want to merge."
);
return false;
}
// merge=0 or merge=false will disable merge
return !in_array($merge, array('0', 'false'));
} }
} }

View File

@ -3,34 +3,34 @@
<h3> <h3>
<span class="step-label"> <span class="step-label">
<span class="flyout">1</span><span class="arrow"></span> <span class="flyout">1</span><span class="arrow"></span>
<span class="title"><% _t('AssetUploadField.ChooseFiles', 'Choose files') %></span> <span class="title"><%t AssetUploadField.ChooseFiles 'Choose files' %></span>
</span> </span>
</h3> </h3>
<div class="ss-uploadfield-item-info"> <div class="ss-uploadfield-item-info">
<label class="ss-uploadfield-fromcomputer ss-ui-button ss-ui-action-constructive" title="<% _t('AssetUploadField.FROMCOMPUTERINFO', 'Upload from your computer') %>" data-icon="drive-upload-large"> <label class="ss-uploadfield-fromcomputer ss-ui-button ss-ui-action-constructive" title="<%t AssetUploadField.FROMCOMPUTERINFO 'Upload from your computer' %>" data-icon="drive-upload-large">
<% _t('AssetUploadField.TOUPLOAD', 'Choose files to upload...') %> <%t AssetUploadField.TOUPLOAD 'Choose files to upload...' %>
<input id="$id" name="$getName" class="$extraClass ss-uploadfield-fromcomputer-fileinput" data-config="$configString" type="file"<% if $multiple %> multiple="multiple"<% end_if %> title="<% _t('AssetUploadField.FROMCOMPUTER', 'Choose files from your computer') %>" /> <input id="$id" name="$getName" class="$extraClass ss-uploadfield-fromcomputer-fileinput" data-config="$configString" type="file"<% if $multiple %> multiple="multiple"<% end_if %> title="<%t AssetUploadField.FROMCOMPUTER 'Choose files from your computer' %>" />
</label> </label>
<div class="clear"><!-- --></div> <div class="clear"><!-- --></div>
</div> </div>
<div class="ss-uploadfield-item-uploador"> <div class="ss-uploadfield-item-uploador">
<% _t('AssetUploadField.UPLOADOR', 'OR') %> <%t AssetUploadField.UPLOADOR 'OR' %>
</div> </div>
<div class="ss-uploadfield-item-preview ss-uploadfield-dropzone"> <div class="ss-uploadfield-item-preview ss-uploadfield-dropzone">
<div> <div>
<% _t('AssetUploadField.DROPAREA', 'Drop Area') %> <%t AssetUploadField.DROPAREA 'Drop Area' %>
<span><% _t('AssetUploadField.DRAGFILESHERE', 'Drag files here') %></span> <span><%t AssetUploadField.DRAGFILESHERE 'Drag files here' %></span>
</div> </div>
</div> </div>
<span class="ss-uploadfield-view-allowed-extensions"> <span class="ss-uploadfield-view-allowed-extensions">
<span class="description"> <span class="description">
<a href="#" class="toggle"><% _t('AssetAdmin.SHOWALLOWEDEXTS', 'Show allowed extensions') %></a> <a href="#" class="toggle"><%t AssetAdmin.SHOWALLOWEDEXTS 'Show allowed extensions' %></a>
<p class="toggle-content">$Extensions</p> <p class="toggle-content">$Extensions</p>
</span> </span>
</span> </span>
@ -42,13 +42,13 @@
<h3> <h3>
<span class="step-label"> <span class="step-label">
<span class="flyout">2</span><span class="arrow"></span> <span class="flyout">2</span><span class="arrow"></span>
<span class="title"><% _t('AssetUploadField.EDITANDORGANIZE', 'Edit & organize') %></span> <span class="title"><%t AssetUploadField.EDITANDORGANIZE 'Edit & organize' %></span>
</span> </span>
</h3> </h3>
<div class="ss-uploadfield-item-actions edit-all"> <div class="ss-uploadfield-item-actions edit-all">
<button class="ss-uploadfield-item-edit-all ss-ui-button ui-corner-all" title="<% _t('AssetUploadField.EDITINFO', 'Edit files') %>" style="display:none;"> <button class="ss-uploadfield-item-edit-all ss-ui-button ui-corner-all" title="<%t AssetUploadField.EDITINFO 'Edit files' %>" style="display:none;">
<% _t('AssetUploadField.EDITALL', 'Edit all') %> <%t AssetUploadField.EDITALL 'Edit all' %>
<span class="toggle-details-icon"></span> <span class="toggle-details-icon"></span>
</button> </button>
</div> </div>
@ -56,9 +56,9 @@
<ul class="ss-uploadfield-files files"></ul> <ul class="ss-uploadfield-files files"></ul>
<div class="fileOverview"> <div class="fileOverview">
<div class="uploadStatus message notice"> <div class="uploadStatus message notice">
<div class="state"><% _t('AssetUploadField.UPLOADINPROGRESS', 'Please wait upload in progress') %></div> <div class="state"><%t AssetUploadField.UPLOADINPROGRESS 'Please wait upload in progress' %></div>
<div class="details"><% _t('AssetUploadField.TOTAL', 'Total') %>: <div class="details"><%t AssetUploadField.TOTAL 'Total' %>:
<span class="total"></span> <% _t('AssetUploadField.FILES', 'Files') %> <span class="total"></span> <%t AssetUploadField.FILES 'Files' %>
<span class="fileSize"></span> <span class="fileSize"></span>
</div> </div>
</div> </div>

View File

@ -3,24 +3,24 @@
<h4> <h4>
<span class="step-label"> <span class="step-label">
<span class="flyout">1</span><span class="arrow"></span> <span class="flyout">1</span><span class="arrow"></span>
<span class="title"><% _t('AssetUploadField.ChooseFiles', 'Choose files') %></span> <span class="title"><%t AssetUploadField.ChooseFiles 'Choose files' %></span>
</span> </span>
</h4> </h4>
<div class="ss-uploadfield-item-info"> <div class="ss-uploadfield-item-info">
<label class="ss-uploadfield-fromcomputer ss-ui-button ss-ui-action-constructive" title="<% _t('AssetUploadField.FROMCOMPUTERINFO', 'Upload from your computer') %>" data-icon="drive-upload-large"> <label class="ss-uploadfield-fromcomputer ss-ui-button ss-ui-action-constructive" title="<%t AssetUploadField.FROMCOMPUTERINFO 'Upload from your computer' %>" data-icon="drive-upload-large">
<% _t('AssetUploadField.TOUPLOAD', 'Choose files to upload...') %> <%t AssetUploadField.TOUPLOAD 'Choose files to upload...' %>
<input id="$id" name="$getName" class="$extraClass ss-uploadfield-fromcomputer-fileinput" data-config="$configString" type="file"<% if $multiple %> multiple="multiple"<% end_if %> title="<% _t('AssetUploadField.FROMCOMPUTER', 'Choose files from your computer') %>" /> <input id="$id" name="$getName" class="$extraClass ss-uploadfield-fromcomputer-fileinput" data-config="$configString" type="file"<% if $multiple %> multiple="multiple"<% end_if %> title="<%t AssetUploadField.FROMCOMPUTER 'Choose files from your computer' %>" />
</label> </label>
<div class="clear"><!-- --></div> <div class="clear"><!-- --></div>
</div> </div>
<div class="ss-uploadfield-item-uploador"> <div class="ss-uploadfield-item-uploador">
<% _t('AssetUploadField.UPLOADOR', 'OR') %> <%t AssetUploadField.UPLOADOR 'OR' %>
</div> </div>
<div class="ss-uploadfield-item-preview ss-uploadfield-dropzone"> <div class="ss-uploadfield-item-preview ss-uploadfield-dropzone">
<div> <div>
<span><% _t('AssetUploadField.DRAGFILESHERE', 'Drag files here') %></span> <span><%t AssetUploadField.DRAGFILESHERE 'Drag files here' %></span>
</div> </div>
</div> </div>
<div class="clear"><!-- --></div> <div class="clear"><!-- --></div>

View File

@ -2,7 +2,7 @@
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" > <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" >
<head> <head>
<% base_tag %> <% base_tag %>
<title><% _t('Image_iframe_ss.TITLE', 'Image Uploading Iframe') %></title> <title><%t Image_iframe_ss.TITLE 'Image Uploading Iframe' %></title>
<meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7"> <meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7">
</head> </head>

View File

@ -1 +1 @@
<a class="action action-detail edit-link" href="$Link" title="<% _t('GridFieldEditButton_ss.EDIT', 'Edit') %>"><% _t('GridFieldEditButton_ss.EDIT', 'Edit') %></a> <a class="action action-detail edit-link" href="$Link" title="<%t GridFieldEditButton_ss.EDIT 'Edit' %>"><%t GridFieldEditButton_ss.EDIT 'Edit' %></a>

View File

@ -4,7 +4,7 @@
<span class="pagination-records-number"> <span class="pagination-records-number">
$FirstShownRecord - $FirstShownRecord -
$LastShownRecord $LastShownRecord
<% _t('TableListField_PageControls_ss.OF', 'of', 'Example: View 1 of 2') %> <%t TableListField_PageControls_ss.OF 'of' is 'Example: View 1 of 2' %>
$NumRecords $NumRecords
</span> </span>
<% end_if %> <% end_if %>

View File

@ -1,5 +1,5 @@
<% if Backlink %> <% if Backlink %>
<a href="$Backlink"><% _t('GridFieldItemEditView.Go_back', 'Go back' ) %></a> <a href="$Backlink"><%t GridFieldItemEditView.Go_back 'Go back' %></a>
<% end_if %> <% end_if %>
$ItemEditForm $ItemEditForm

View File

@ -1,6 +1,6 @@
<span class="pagination-records-number"> <span class="pagination-records-number">
<% _t('Pagination.View', 'View', 'Verb. Example: View 1 of 2') %> <%t Pagination.View 'View' is 'Verb. Example: View 1 of 2' %>
{$FirstShownRecord}&ndash;{$LastShownRecord} {$FirstShownRecord}&ndash;{$LastShownRecord}
<% _t('TableListField_PageControls_ss.OF', 'of', 'Example: View 1 of 2') %> <%t TableListField_PageControls_ss.OF 'of' is 'Example: View 1 of 2' %>
$NumRecords $NumRecords
</span> </span>

View File

@ -5,18 +5,18 @@
<div class="datagrid-pagination"> <div class="datagrid-pagination">
$FirstPage $PreviousPage $FirstPage $PreviousPage
<span class="pagination-page-number"> <span class="pagination-page-number">
<% _t('Pagination.Page', 'Page') %> <%t Pagination.Page 'Page' %>
<input class="text" value="$CurrentPageNum" data-skip-autofocus="true" /> <input class="text" value="$CurrentPageNum" data-skip-autofocus="true" />
<% _t('TableListField_PageControls_ss.OF', 'of', 'Example: View 1 of 2') %> <%t TableListField_PageControls_ss.OF 'of' is 'Example: View 1 of 2' %>
$NumPages $NumPages
</span> </span>
$NextPage $LastPage $NextPage $LastPage
</div> </div>
<% end_if %> <% end_if %>
<span class="pagination-records-number"> <span class="pagination-records-number">
<% _t('Pagination.View', 'View', 'Verb. Example: View 1 of 2') %> <%t Pagination.View 'View' is 'Verb. Example: View 1 of 2' %>
{$FirstShownRecord}&ndash;{$LastShownRecord} {$FirstShownRecord}&ndash;{$LastShownRecord}
<% _t('TableListField_PageControls_ss.OF', 'of', 'Example: View 1 of 2') %> <%t TableListField_PageControls_ss.OF 'of' is 'Example: View 1 of 2' %>
$NumRecords $NumRecords
</span> </span>
</td> </td>

View File

@ -16,15 +16,15 @@
<% end_loop %> <% end_loop %>
<% else %> <% else %>
<tr> <tr>
<td colspan="$Header.Count"><p><% _t('GridField.NoItemsFound', 'No items found') %></p></td> <td colspan="$Header.Count"><p><%t GridField.NoItemsFound 'No items found' %></p></td>
</tr> </tr>
<% end_if %> <% end_if %>
</tbody> </tbody>
</table> </table>
<p> <p>
<% _t('GridField.PRINTEDAT', 'Printed at') %> $Datetime.Time, $Datetime.Date <%t GridField.PRINTEDAT 'Printed at' %> $Datetime.Time, $Datetime.Date
<br /> <br />
<% _t('GridField.PRINTEDBY', 'Printed by') %> $Member.Name <%t GridField.PRINTEDBY 'Printed by' %> $Member.Name
</p> </p>
</body> </body>

View File

@ -15,7 +15,7 @@
$Name $Name
</span> </span>
<% if $Width %> <% if $Width %>
<div class="ss-uploadfield-item-status ui-state-success-text" title="<% _t('UploadField.Dimensions', 'Dimensions') %>"> <div class="ss-uploadfield-item-status ui-state-success-text" title="<%t UploadField.Dimensions 'Dimensions' %>">
{$Width} x {$Height} (px) {$Width} x {$Height} (px)
</div> </div>
<% end_if %> <% end_if %>
@ -23,13 +23,13 @@
<div class="clear"><!-- --></div> <div class="clear"><!-- --></div>
</label> </label>
<div class="ss-uploadfield-item-actions"> <div class="ss-uploadfield-item-actions">
<button data-icon="deleteLight" class="ss-uploadfield-item-cancel ss-uploadfield-item-remove" title="<% _t('UploadField.REMOVE', 'Remove') %>"> <button data-icon="deleteLight" class="ss-uploadfield-item-cancel ss-uploadfield-item-remove" title="<%t UploadField.REMOVE 'Remove' %>">
<% _t('UploadField.REMOVE', 'Remove') %> <%t UploadField.REMOVE 'Remove' %>
</button> </button>
<div class="ss-uploadfield-item-edit edit"> <div class="ss-uploadfield-item-edit edit">
<button class="ss-uploadfield-item-edit ss-ui-button ui-corner-all" title="<% _t('UploadField.EDITINFO', 'Edit this file') %>" data-icon="pencil"> <button class="ss-uploadfield-item-edit ss-ui-button ui-corner-all" title="<%t UploadField.EDITINFO 'Edit this file' %>" data-icon="pencil">
<% _t('UploadField.EDIT', 'Edit') %> <%t UploadField.EDIT 'Edit' %>
<span class="toggle-details"> <span class="toggle-details">
<span class="toggle-details-icon"></span> <span class="toggle-details-icon"></span>
</span> </span>

View File

@ -1,12 +1,12 @@
<span id="$SelectID" class="preview-mode-selector preview-selector field dropdown"> <span id="$SelectID" class="preview-mode-selector preview-selector field dropdown">
<select title="<% _t('SilverStripeNavigator.ChangeViewMode', 'Change view mode') %>" id="$SelectID-select" class="preview-dropdown dropdown nolabel no-change-track" autocomplete="off" name="Action"> <select title="<%t SilverStripeNavigator.ChangeViewMode 'Change view mode' %>" id="$SelectID-select" class="preview-dropdown dropdown nolabel no-change-track" autocomplete="off" name="Action">
<option data-icon="icon-split" class="icon-split icon-view first" value="split"><% _t('SilverStripeNavigator.SplitView', 'Split mode') %></option> <option data-icon="icon-split" class="icon-split icon-view first" value="split"><%t SilverStripeNavigator.SplitView 'Split mode' %></option>
<option data-icon="icon-preview" class="icon-preview icon-view" value="preview"><% _t('SilverStripeNavigator.PreviewView', 'Preview mode') %></option> <option data-icon="icon-preview" class="icon-preview icon-view" value="preview"><%t SilverStripeNavigator.PreviewView 'Preview mode' %></option>
<option data-icon="icon-edit" class="icon-edit icon-view last" value="content"><% _t('SilverStripeNavigator.EditView', 'Edit mode') %></option> <option data-icon="icon-edit" class="icon-edit icon-view last" value="content"><%t SilverStripeNavigator.EditView 'Edit mode' %></option>
<!-- Dual window not implemented yet --> <!-- Dual window not implemented yet -->
<!-- <!--
<option data-icon="icon-window" class="icon-window icon-view last" value="window"><% _t('SilverStripeNavigator.DualWindowView', 'Dual Window') %></option> <option data-icon="icon-window" class="icon-window icon-view last" value="window"><%t SilverStripeNavigator.DualWindowView 'Dual Window' %></option>
--> -->
</select> </select>
</span> </span>

View File

@ -1,17 +1,17 @@
<% if $canEdit %> <% if $canEdit %>
<button class="ss-uploadfield-item-edit ss-ui-button ui-corner-all" title="<% _t('UploadField.EDITINFO', 'Edit this file') %>" data-icon="pencil"> <button class="ss-uploadfield-item-edit ss-ui-button ui-corner-all" title="<%t UploadField.EDITINFO 'Edit this file' %>" data-icon="pencil">
<% _t('UploadField.EDIT', 'Edit') %> <%t UploadField.EDIT 'Edit' %>
<span class="toggle-details"> <span class="toggle-details">
<span class="toggle-details-icon"></span> <span class="toggle-details-icon"></span>
</span> </span>
</button> </button>
<% end_if %> <% end_if %>
<button class="ss-uploadfield-item-remove ss-ui-button ui-corner-all" title="<% _t('UploadField.REMOVEINFO', 'Remove this file from here, but do not delete it from the file store') %>" data-icon="plug-disconnect-prohibition"> <button class="ss-uploadfield-item-remove ss-ui-button ui-corner-all" title="<%t UploadField.REMOVEINFO 'Remove this file from here, but do not delete it from the file store' %>" data-icon="plug-disconnect-prohibition">
<% _t('UploadField.REMOVE', 'Remove') %></button> <%t UploadField.REMOVE 'Remove' %></button>
<% if $canDelete %> <% if $canDelete %>
<button data-href="$UploadFieldDeleteLink" class="ss-uploadfield-item-delete ss-ui-button ui-corner-all" title="<% _t('UploadField.DELETEINFO', 'Permanently delete this file from the file store') %>" data-icon="minus-circle"><% _t('UploadField.DELETE', 'Delete from files') %></button> <button data-href="$UploadFieldDeleteLink" class="ss-uploadfield-item-delete ss-ui-button ui-corner-all" title="<%t UploadField.DELETEINFO 'Permanently delete this file from the file store' %>" data-icon="minus-circle"><%t UploadField.DELETE 'Delete from files' %></button>
<% end_if %> <% end_if %>
<% if $UploadField.canAttachExisting %> <% if $UploadField.canAttachExisting %>
<button class="ss-uploadfield-item-choose-another ss-uploadfield-fromfiles ss-ui-button ui-corner-all" title="<% _t('UploadField.CHOOSEANOTHERINFO', 'Replace this file with another one from the file store') %>" data-icon="network-cloud"> <button class="ss-uploadfield-item-choose-another ss-uploadfield-fromfiles ss-ui-button ui-corner-all" title="<%t UploadField.CHOOSEANOTHERINFO 'Replace this file with another one from the file store' %>" data-icon="network-cloud">
<% _t('UploadField.CHOOSEANOTHERFILE', 'Choose another file') %></button> <%t UploadField.CHOOSEANOTHERFILE 'Choose another file' %></button>
<% end_if %> <% end_if %>

View File

@ -30,26 +30,26 @@
<% if canUpload %> <% if canUpload %>
<div class="ss-uploadfield-item-preview ss-uploadfield-dropzone ui-corner-all"> <div class="ss-uploadfield-item-preview ss-uploadfield-dropzone ui-corner-all">
<% if $multiple %> <% if $multiple %>
<% _t('UploadField.DROPFILES', 'drop files') %> <%t UploadField.DROPFILES 'drop files' %>
<% else %> <% else %>
<% _t('UploadField.DROPFILE', 'drop a file') %> <%t UploadField.DROPFILE 'drop a file' %>
<% end_if %> <% end_if %>
</div> </div>
<% end_if %> <% end_if %>
<div class="ss-uploadfield-item-info"> <div class="ss-uploadfield-item-info">
<label class="ss-uploadfield-item-name"> <label class="ss-uploadfield-item-name">
<% if $multiple %> <% if $multiple %>
<b><% _t('UploadField.ATTACHFILES', 'Attach files') %></b> <b><%t UploadField.ATTACHFILES 'Attach files' %></b>
<% else %> <% else %>
<b><% _t('UploadField.ATTACHFILE', 'Attach a file') %></b> <b><%t UploadField.ATTACHFILE 'Attach a file' %></b>
<% end_if %> <% end_if %>
<% if $canPreviewFolder %> <% if $canPreviewFolder %>
<small>(<%t UploadField.UPLOADSINTO 'saves into /{path}' path=$FolderName %>)</small> <small>(<%t UploadField.UPLOADSINTO 'saves into /{path}' path=$FolderName %>)</small>
<% end_if %> <% end_if %>
</label> </label>
<% if $canUpload %> <% if $canUpload %>
<label class="ss-uploadfield-fromcomputer ss-ui-button ui-corner-all" title="<% _t('UploadField.FROMCOMPUTERINFO', 'Upload from your computer') %>" data-icon="drive-upload"> <label class="ss-uploadfield-fromcomputer ss-ui-button ui-corner-all" title="<%t UploadField.FROMCOMPUTERINFO 'Upload from your computer' %>" data-icon="drive-upload">
<% _t('UploadField.FROMCOMPUTER', 'From your computer') %> <%t UploadField.FROMCOMPUTER 'From your computer' %>
<input id="$id" name="{$Name}[Uploads][]" class="$extraClass ss-uploadfield-fromcomputer-fileinput" data-config="$configString" type="file"<% if $multiple %> multiple="multiple"<% end_if %> /> <input id="$id" name="{$Name}[Uploads][]" class="$extraClass ss-uploadfield-fromcomputer-fileinput" data-config="$configString" type="file"<% if $multiple %> multiple="multiple"<% end_if %> />
</label> </label>
<% else %> <% else %>
@ -57,11 +57,11 @@
<% end_if %> <% end_if %>
<% if $canAttachExisting %> <% if $canAttachExisting %>
<button class="ss-uploadfield-fromfiles ss-ui-button ui-corner-all" title="<% _t('UploadField.FROMCOMPUTERINFO', 'Select from files') %>" data-icon="network-cloud"><% _t('UploadField.FROMFILES', 'From files') %></button> <button class="ss-uploadfield-fromfiles ss-ui-button ui-corner-all" title="<%t UploadField.FROMCOMPUTERINFO 'Select from files' %>" data-icon="network-cloud"><%t UploadField.FROMFILES 'From files' %></button>
<% end_if %> <% end_if %>
<% if $canUpload %> <% if $canUpload %>
<% if not $autoUpload %> <% if not $autoUpload %>
<button class="ss-uploadfield-startall ss-ui-button ui-corner-all" data-icon="navigation"><% _t('UploadField.STARTALL', 'Start all') %></button> <button class="ss-uploadfield-startall ss-ui-button ui-corner-all" data-icon="navigation"><%t UploadField.STARTALL 'Start all' %></button>
<% end_if %> <% end_if %>
<% end_if %> <% end_if %>
<div class="clear"><!-- --></div> <div class="clear"><!-- --></div>

View File

@ -1,11 +1,11 @@
<p><% _t('ChangePasswordEmail_ss.HELLO', 'Hi') %> $FirstName,</p> <p><%t ChangePasswordEmail_ss.HELLO 'Hi' %> $FirstName,</p>
<p> <p>
<% _t('ChangePasswordEmail_ss.CHANGEPASSWORDTEXT1', 'You changed your password for', 'for a url') %> $AbsoluteBaseURL.<br /> <%t ChangePasswordEmail_ss.CHANGEPASSWORDTEXT1 'You changed your password for' is 'for a url' %> $AbsoluteBaseURL.<br />
<% _t('ChangePasswordEmail_ss.CHANGEPASSWORDTEXT2', 'You can now use the following credentials to log in:') %> <%t ChangePasswordEmail_ss.CHANGEPASSWORDTEXT2 'You can now use the following credentials to log in:' %>
</p> </p>
<p> <p>
<% _t('ChangePasswordEmail_ss.EMAIL', 'Email') %>: $Email<br /> <%t ChangePasswordEmail_ss.EMAIL 'Email' %>: $Email<br />
<% _t('ChangePasswordEmail_ss.PASSWORD', 'Password') %>: $CleartextPassword <%t ChangePasswordEmail_ss.PASSWORD 'Password' %>: $CleartextPassword
</p> </p>

View File

@ -1,4 +1,4 @@
<p><% _t('ForgotPasswordEmail_ss.HELLO', 'Hi') %> $FirstName,</p> <p><%t ForgotPasswordEmail_ss.HELLO 'Hi' %> $FirstName,</p>
<p><% _t('ForgotPasswordEmail_ss.TEXT1', 'Here is your') %> <a href="$PasswordResetLink"><% _t('ForgotPasswordEmail_ss.TEXT2', 'password reset link') %></a> <% _t('ForgotPasswordEmail_ss.TEXT3', 'for') %> $AbsoluteBaseURL.</p> <p><%t ForgotPasswordEmail_ss.TEXT1 'Here is your' %> <a href="$PasswordResetLink"><%t ForgotPasswordEmail_ss.TEXT2 'password reset link' %></a> <%t ForgotPasswordEmail_ss.TEXT3 'for' %> $AbsoluteBaseURL.</p>

View File

@ -1,4 +1,4 @@
<ul id="$HolderID" class="$extraClass"> <ul $AttributesHTML>
<% if $Options.Count %> <% if $Options.Count %>
<% loop $Options %> <% loop $Options %>
<li class="$Class"> <li class="$Class">

View File

@ -1,4 +1,4 @@
<ul id="$HolderID" class="$extraClass"> <ul $AttributesHTML>
<% loop $Options %> <% loop $Options %>
<li class="$Class"> <li class="$Class">
<input id="$ID" class="radio" name="$Name" type="radio" value="$Value"<% if $isChecked %> checked<% end_if %><% if $isDisabled %> disabled<% end_if %> /> <input id="$ID" class="radio" name="$Name" type="radio" value="$Value"<% if $isChecked %> checked<% end_if %><% if $isDisabled %> disabled<% end_if %> />

View File

@ -213,6 +213,29 @@ class CmsUiContext extends BehatContext {
} }
} }
/**
* @When /^I (expand|collapse) the content filters$/
*/
public function iExpandTheContentFilters($action) {
$page = $this->getSession()->getPage();
$filterButton = $page->find('css', '#filters-button');
assertNotNull($filterButton, sprintf('Filter button link not found'));
$filterButtonCssClass = $filterButton->getAttribute('class');
if($action == 'expand') {
if(strpos($filterButtonCssClass, 'active') === false) {
$filterButton->click();
}
} else {
if(strpos($filterButtonCssClass, 'active') !== false) {
$filterButton->click();
}
}
$this->getSession()->wait(2000, 'window.jQuery(".cms-content-filters:animated").length === 0');
}
/** /**
* @When /^I (expand|collapse) "([^"]*)" in the tree$/ * @When /^I (expand|collapse) "([^"]*)" in the tree$/
*/ */

View File

@ -12,6 +12,18 @@ class ControllerTest extends FunctionalTest {
) )
); );
protected $depSettings = null;
public function setUp() {
parent::setUp();
$this->depSettings = Deprecation::dump_settings();
}
public function tearDown() {
Deprecation::restore_settings($this->depSettings);
parent::tearDown();
}
public function testDefaultAction() { public function testDefaultAction() {
/* For a controller with a template, the default action will simple run that template. */ /* For a controller with a template, the default action will simple run that template. */
$response = $this->get("ControllerTest_Controller/"); $response = $this->get("ControllerTest_Controller/");
@ -208,6 +220,7 @@ class ControllerTest extends FunctionalTest {
* @expectedExceptionMessage Wildcards (*) are no longer valid * @expectedExceptionMessage Wildcards (*) are no longer valid
*/ */
public function testWildcardAllowedActions() { public function testWildcardAllowedActions() {
Deprecation::set_enabled(true);
$this->get('ControllerTest_AccessWildcardSecuredController'); $this->get('ControllerTest_AccessWildcardSecuredController');
} }

View File

@ -18,8 +18,6 @@ class DirectorTest extends SapphireTest {
public function setUp() { public function setUp() {
parent::setUp(); parent::setUp();
// Required for testRequestFilterInDirectorTest
Injector::nest();
// Hold the original request URI once so it doesn't get overwritten // Hold the original request URI once so it doesn't get overwritten
if(!self::$originalRequestURI) { if(!self::$originalRequestURI) {
@ -53,9 +51,6 @@ class DirectorTest extends SapphireTest {
public function tearDown() { public function tearDown() {
// TODO Remove director rule, currently API doesnt allow this // TODO Remove director rule, currently API doesnt allow this
// Remove base URL override (setting to false reverts to default behaviour)
Config::inst()->update('Director', 'alternate_base_url', false);
$_GET = $this->originalGet; $_GET = $this->originalGet;
$_SESSION = $this->originalSession; $_SESSION = $this->originalSession;
@ -68,7 +63,6 @@ class DirectorTest extends SapphireTest {
} }
} }
Injector::unnest();
parent::tearDown(); parent::tearDown();
} }

View File

@ -7,6 +7,26 @@
*/ */
class HTTPTest extends FunctionalTest { class HTTPTest extends FunctionalTest {
public function testAddCacheHeaders() {
$body = "<html><head></head><body><h1>Mysite</h1></body></html>";
$response = new SS_HTTPResponse($body, 200);
$this->assertEmpty($response->getHeader('Cache-Control'));
HTTP::set_cache_age(30);
HTTP::add_cache_headers($response);
$this->assertNotEmpty($response->getHeader('Cache-Control'));
Config::inst()->update('Director', 'environment_type', 'dev');
HTTP::add_cache_headers($response);
$this->assertContains('max-age=0', $response->getHeader('Cache-Control'));
Config::inst()->update('Director', 'environment_type', 'live');
HTTP::add_cache_headers($response);
$this->assertContains('max-age=30', explode(', ', $response->getHeader('Cache-Control')));
$this->assertNotContains('max-age=0', $response->getHeader('Cache-Control'));
}
/** /**
* Tests {@link HTTP::getLinksIn()} * Tests {@link HTTP::getLinksIn()}
*/ */

View File

@ -14,10 +14,18 @@ class ClassInfoTest extends SapphireTest {
'ClassInfoTest_NoFields', 'ClassInfoTest_NoFields',
); );
public function setUp() {
parent::setUp();
ClassInfo::reset_db_cache();
}
public function testExists() { public function testExists() {
$this->assertTrue(ClassInfo::exists('Object')); $this->assertTrue(ClassInfo::exists('Object'));
$this->assertTrue(ClassInfo::exists('object'));
$this->assertTrue(ClassInfo::exists('ClassInfoTest')); $this->assertTrue(ClassInfo::exists('ClassInfoTest'));
$this->assertTrue(ClassInfo::exists('CLASSINFOTEST'));
$this->assertTrue(ClassInfo::exists('stdClass')); $this->assertTrue(ClassInfo::exists('stdClass'));
$this->assertTrue(ClassInfo::exists('stdCLASS'));
} }
public function testSubclassesFor() { public function testSubclassesFor() {
@ -30,6 +38,16 @@ class ClassInfoTest extends SapphireTest {
), ),
'ClassInfo::subclassesFor() returns only direct subclasses and doesnt include base class' 'ClassInfo::subclassesFor() returns only direct subclasses and doesnt include base class'
); );
ClassInfo::reset_db_cache();
$this->assertEquals(
ClassInfo::subclassesFor('classinfotest_baseclass'),
array(
'ClassInfoTest_BaseClass' => 'ClassInfoTest_BaseClass',
'ClassInfoTest_ChildClass' => 'ClassInfoTest_ChildClass',
'ClassInfoTest_GrandChildClass' => 'ClassInfoTest_GrandChildClass'
),
'ClassInfo::subclassesFor() is acting in a case sensitive way when it should not'
);
} }
public function testClassesForFolder() { public function testClassesForFolder() {
@ -42,11 +60,11 @@ class ClassInfoTest extends SapphireTest {
$classes, $classes,
'ClassInfo::classes_for_folder() returns classes matching the filename' 'ClassInfo::classes_for_folder() returns classes matching the filename'
); );
// $this->assertContains( $this->assertContains(
// 'ClassInfoTest_BaseClass', 'classinfotest_baseclass',
// $classes, $classes,
// 'ClassInfo::classes_for_folder() returns additional classes not matching the filename' 'ClassInfo::classes_for_folder() returns additional classes not matching the filename'
// ); );
} }
/** /**
@ -54,8 +72,11 @@ class ClassInfoTest extends SapphireTest {
*/ */
public function testBaseDataClass() { public function testBaseDataClass() {
$this->assertEquals('ClassInfoTest_BaseClass', ClassInfo::baseDataClass('ClassInfoTest_BaseClass')); $this->assertEquals('ClassInfoTest_BaseClass', ClassInfo::baseDataClass('ClassInfoTest_BaseClass'));
$this->assertEquals('ClassInfoTest_BaseClass', ClassInfo::baseDataClass('classinfotest_baseclass'));
$this->assertEquals('ClassInfoTest_BaseClass', ClassInfo::baseDataClass('ClassInfoTest_ChildClass')); $this->assertEquals('ClassInfoTest_BaseClass', ClassInfo::baseDataClass('ClassInfoTest_ChildClass'));
$this->assertEquals('ClassInfoTest_BaseClass', ClassInfo::baseDataClass('CLASSINFOTEST_CHILDCLASS'));
$this->assertEquals('ClassInfoTest_BaseClass', ClassInfo::baseDataClass('ClassInfoTest_GrandChildClass')); $this->assertEquals('ClassInfoTest_BaseClass', ClassInfo::baseDataClass('ClassInfoTest_GrandChildClass'));
$this->assertEquals('ClassInfoTest_BaseClass', ClassInfo::baseDataClass('ClassInfoTest_GRANDChildClass'));
$this->setExpectedException('InvalidArgumentException'); $this->setExpectedException('InvalidArgumentException');
ClassInfo::baseDataClass('DataObject'); ClassInfo::baseDataClass('DataObject');
@ -75,6 +96,13 @@ class ClassInfoTest extends SapphireTest {
)); ));
$this->assertEquals($expect, $ancestry); $this->assertEquals($expect, $ancestry);
ClassInfo::reset_db_cache();
$this->assertEquals($expect, ClassInfo::ancestry('classINFOTest_Childclass'));
ClassInfo::reset_db_cache();
$this->assertEquals($expect, ClassInfo::ancestry('classINFOTest_Childclass'));
ClassInfo::reset_db_cache();
$ancestry = ClassInfo::ancestry('ClassInfoTest_ChildClass', true); $ancestry = ClassInfo::ancestry('ClassInfoTest_ChildClass', true);
$this->assertEquals(array('ClassInfoTest_BaseClass' => 'ClassInfoTest_BaseClass'), $ancestry, $this->assertEquals(array('ClassInfoTest_BaseClass' => 'ClassInfoTest_BaseClass'), $ancestry,
'$tablesOnly option excludes memory-only inheritance classes' '$tablesOnly option excludes memory-only inheritance classes'
@ -97,8 +125,11 @@ class ClassInfoTest extends SapphireTest {
'ClassInfoTest_HasFields', 'ClassInfoTest_HasFields',
); );
ClassInfo::reset_db_cache();
$this->assertEquals($expect, ClassInfo::dataClassesFor($classes[0])); $this->assertEquals($expect, ClassInfo::dataClassesFor($classes[0]));
ClassInfo::reset_db_cache();
$this->assertEquals($expect, ClassInfo::dataClassesFor(strtoupper($classes[0])));
ClassInfo::reset_db_cache();
$this->assertEquals($expect, ClassInfo::dataClassesFor($classes[1])); $this->assertEquals($expect, ClassInfo::dataClassesFor($classes[1]));
$expect = array( $expect = array(
@ -106,7 +137,10 @@ class ClassInfoTest extends SapphireTest {
'ClassInfoTest_HasFields' => 'ClassInfoTest_HasFields', 'ClassInfoTest_HasFields' => 'ClassInfoTest_HasFields',
); );
ClassInfo::reset_db_cache();
$this->assertEquals($expect, ClassInfo::dataClassesFor($classes[2])); $this->assertEquals($expect, ClassInfo::dataClassesFor($classes[2]));
ClassInfo::reset_db_cache();
$this->assertEquals($expect, ClassInfo::dataClassesFor(strtolower($classes[2])));
} }
public function testTableForObjectField() { public function testTableForObjectField() {
@ -114,6 +148,10 @@ class ClassInfoTest extends SapphireTest {
ClassInfo::table_for_object_field('ClassInfoTest_WithRelation', 'RelationID') ClassInfo::table_for_object_field('ClassInfoTest_WithRelation', 'RelationID')
); );
$this->assertEquals('ClassInfoTest_WithRelation',
ClassInfo::table_for_object_field('ClassInfoTest_withrelation', 'RelationID')
);
$this->assertEquals('ClassInfoTest_BaseDataClass', $this->assertEquals('ClassInfoTest_BaseDataClass',
ClassInfo::table_for_object_field('ClassInfoTest_BaseDataClass', 'Title') ClassInfo::table_for_object_field('ClassInfoTest_BaseDataClass', 'Title')
); );
@ -126,6 +164,10 @@ class ClassInfoTest extends SapphireTest {
ClassInfo::table_for_object_field('ClassInfoTest_NoFields', 'Title') ClassInfo::table_for_object_field('ClassInfoTest_NoFields', 'Title')
); );
$this->assertEquals('ClassInfoTest_BaseDataClass',
ClassInfo::table_for_object_field('classinfotest_nofields', 'Title')
);
$this->assertEquals('ClassInfoTest_HasFields', $this->assertEquals('ClassInfoTest_HasFields',
ClassInfo::table_for_object_field('ClassInfoTest_HasFields', 'Description') ClassInfo::table_for_object_field('ClassInfoTest_HasFields', 'Description')
); );

View File

@ -83,6 +83,19 @@ class ConfigTest_TestNest extends Object implements TestOnly {
class ConfigTest extends SapphireTest { class ConfigTest extends SapphireTest {
protected $depSettings = null;
public function setUp() {
parent::setUp();
$this->depSettings = Deprecation::dump_settings();
Deprecation::set_enabled(false);
}
public function tearDown() {
Deprecation::restore_settings($this->depSettings);
parent::tearDown();
}
public function testNest() { public function testNest() {
// Check basic config // Check basic config
@ -189,8 +202,7 @@ class ConfigTest extends SapphireTest {
// But it won't affect subclasses - this is *uninherited* static // But it won't affect subclasses - this is *uninherited* static
$this->assertNotContains('test_2b', $this->assertNotContains('test_2b',
Config::inst()->get('ConfigStaticTest_Third', 'first', Config::UNINHERITED)); Config::inst()->get('ConfigStaticTest_Third', 'first', Config::UNINHERITED));
$this->assertNotContains('test_2b', $this->assertNull(Config::inst()->get('ConfigStaticTest_Fourth', 'first', Config::UNINHERITED));
Config::inst()->get('ConfigStaticTest_Fourth', 'first', Config::UNINHERITED));
// Subclasses that don't have the static explicitly defined should allow definition, also // 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() // This also checks that set can be called after the first uninherited get()
@ -283,12 +295,6 @@ class ConfigTest extends SapphireTest {
} }
public function testLRUDiscarding() { public function testLRUDiscarding() {
$depSettings = Deprecation::dump_settings();
Deprecation::restore_settings(array(
'level' => false,
'version' => false,
'moduleVersions' => false,
));
$cache = new ConfigTest_Config_LRU(); $cache = new ConfigTest_Config_LRU();
for ($i = 0; $i < Config_LRU::SIZE*2; $i++) $cache->set($i, $i); for ($i = 0; $i < Config_LRU::SIZE*2; $i++) $cache->set($i, $i);
$this->assertEquals( $this->assertEquals(
@ -302,16 +308,9 @@ class ConfigTest extends SapphireTest {
Config_LRU::SIZE, count($cache->indexing), Config_LRU::SIZE, count($cache->indexing),
'Heterogenous usage gives sufficient discarding' 'Heterogenous usage gives sufficient discarding'
); );
Deprecation::restore_settings($depSettings);
} }
public function testLRUCleaning() { public function testLRUCleaning() {
$depSettings = Deprecation::dump_settings();
Deprecation::restore_settings(array(
'level' => false,
'version' => false,
'moduleVersions' => false,
));
$cache = new ConfigTest_Config_LRU(); $cache = new ConfigTest_Config_LRU();
for ($i = 0; $i < Config_LRU::SIZE; $i++) $cache->set($i, $i); for ($i = 0; $i < Config_LRU::SIZE; $i++) $cache->set($i, $i);
$this->assertEquals(Config_LRU::SIZE, count($cache->indexing)); $this->assertEquals(Config_LRU::SIZE, count($cache->indexing));
@ -328,7 +327,6 @@ class ConfigTest extends SapphireTest {
$cache->clean('Bar'); $cache->clean('Bar');
$this->assertEquals(0, count($cache->indexing), 'Clean items with any single matching tag'); $this->assertEquals(0, count($cache->indexing), 'Clean items with any single matching tag');
$this->assertFalse($cache->get(1), 'Clean items with any single matching tag'); $this->assertFalse($cache->get(1), 'Clean items with any single matching tag');
Deprecation::restore_settings($depSettings);
} }
} }

View File

@ -391,7 +391,7 @@ class ConfigManifestTest extends SapphireTest {
public function testEnvironmentRules() { public function testEnvironmentRules() {
foreach (array('dev', 'test', 'live') as $env) { foreach (array('dev', 'test', 'live') as $env) {
Config::inst()->nest(); Config::nest();
Config::inst()->update('Director', 'environment_type', $env); Config::inst()->update('Director', 'environment_type', $env);
$config = $this->getConfigFixtureValue('Environment'); $config = $this->getConfigFixtureValue('Environment');
@ -403,13 +403,11 @@ class ConfigManifestTest extends SapphireTest {
); );
} }
Config::inst()->unnest(); Config::unnest();
} }
} }
public function testDynamicEnvironmentRules() { public function testDynamicEnvironmentRules() {
Config::inst()->nest();
// First, make sure environment_type is live // First, make sure environment_type is live
Config::inst()->update('Director', 'environment_type', 'live'); Config::inst()->update('Director', 'environment_type', 'live');
$this->assertEquals('live', Config::inst()->get('Director', 'environment_type')); $this->assertEquals('live', Config::inst()->get('Director', 'environment_type'));
@ -423,8 +421,6 @@ class ConfigManifestTest extends SapphireTest {
// And that the dynamic rule was calculated correctly // And that the dynamic rule was calculated correctly
$this->assertEquals('dev', Config::inst()->get('ConfigManifestTest', 'DynamicEnvironment')); $this->assertEquals('dev', Config::inst()->get('ConfigManifestTest', 'DynamicEnvironment'));
Config::inst()->unnest();
} }
public function testMultipleRules() { public function testMultipleRules() {

View File

@ -15,6 +15,152 @@ class NamespacedClassManifestTest extends SapphireTest {
$this->base = dirname(__FILE__) . '/fixtures/namespaced_classmanifest'; $this->base = dirname(__FILE__) . '/fixtures/namespaced_classmanifest';
$this->manifest = new SS_ClassManifest($this->base, false, true, false); $this->manifest = new SS_ClassManifest($this->base, false, true, false);
SS_ClassLoader::instance()->pushManifest($this->manifest, false);
}
public function tearDown() {
parent::tearDown();
SS_ClassLoader::instance()->popManifest();
}
public function testGetImportedNamespaceParser() {
$file = file_get_contents($this->base . DIRECTORY_SEPARATOR . 'module/classes/ClassI.php');
$tokens = token_get_all($file);
$parsedTokens = SS_ClassManifest::get_imported_namespace_parser()->findAll($tokens);
$expectedItems = array(
array('ModelAdmin'),
array('Controller', ' ', 'as', ' ', 'Cont'),
array(
'SS_HTTPRequest', ' ', 'as', ' ', 'Request', ',',
'SS_HTTPResponse', ' ', 'AS', ' ', 'Response', ',',
'PermissionProvider', ' ', 'AS', ' ', 'P',
),
array('silverstripe', '\\', 'test', '\\', 'ClassA'),
array('\\', 'DataObject'),
);
$this->assertEquals(count($expectedItems), count($parsedTokens));
foreach ($expectedItems as $i => $item) {
$this->assertEquals($item, $parsedTokens[$i]['importString']);
}
}
public function testGetImportsFromTokens() {
$file = file_get_contents($this->base . DIRECTORY_SEPARATOR . 'module/classes/ClassI.php');
$tokens = token_get_all($file);
$method = new ReflectionMethod($this->manifest, 'getImportsFromTokens');
$method->setAccessible(true);
$expectedImports = array(
'ModelAdmin',
'Cont' => 'Controller',
'Request' => 'SS_HTTPRequest',
'Response' => 'SS_HTTPResponse',
'P' => 'PermissionProvider',
'silverstripe\test\ClassA',
'\DataObject',
);
$imports = $method->invoke($this->manifest, $tokens);
$this->assertEquals($expectedImports, $imports);
}
public function testClassInfoIsCorrect() {
$this->assertContains('SilverStripe\Framework\Tests\ClassI', ClassInfo::implementorsOf('PermissionProvider'));
//because we're using a nested manifest we have to "coalesce" the descendants again to correctly populate the
// descendants of the core classes we want to test against - this is a limitation of the test manifest not
// including all core classes
$method = new ReflectionMethod($this->manifest, 'coalesceDescendants');
$method->setAccessible(true);
$method->invoke($this->manifest, 'ModelAdmin');
$this->assertContains('SilverStripe\Framework\Tests\ClassI', ClassInfo::subclassesFor('ModelAdmin'));
}
public function testFindClassOrInterfaceFromCandidateImports() {
$method = new ReflectionMethod($this->manifest, 'findClassOrInterfaceFromCandidateImports');
$method->setAccessible(true);
$this->assertTrue(ClassInfo::exists('silverstripe\test\ClassA'));
$this->assertEquals('PermissionProvider', $method->invokeArgs($this->manifest, array(
'\PermissionProvider',
'Test\Namespace',
array(
'TestOnly',
'Controller',
),
)));
$this->assertEquals('PermissionProvider', $method->invokeArgs($this->manifest, array(
'PermissionProvider',
'Test\NAmespace',
array(
'PermissionProvider',
)
)));
$this->assertEmpty($method->invokeArgs($this->manifest, array(
'',
'TextNamespace',
array(
'PermissionProvider',
),
)));
$this->assertEmpty($method->invokeArgs($this->manifest, array(
'',
'',
array()
)));
$this->assertEquals('silverstripe\test\ClassA', $method->invokeArgs($this->manifest, array(
'ClassA',
'Test\Namespace',
array(
'silverstripe\test\ClassA',
'PermissionProvider',
),
)));
$this->assertEquals('ClassA', $method->invokeArgs($this->manifest, array(
'\ClassA',
'Test\Namespace',
array(
'silverstripe\test',
),
)));
$this->assertEquals('ClassA', $method->invokeArgs($this->manifest, array(
'ClassA',
'silverstripe\test',
array(
'\ClassA',
),
)));
$this->assertEquals('ClassA', $method->invokeArgs($this->manifest, array(
'Alias',
'silverstripe\test',
array(
'Alias' => '\ClassA',
),
)));
$this->assertEquals('silverstripe\test\ClassA', $method->invokeArgs($this->manifest, array(
'ClassA',
'silverstripe\test',
array(
'silverstripe\test\ClassB',
),
)));
} }
public function testGetItemPath() { public function testGetItemPath() {
@ -43,7 +189,8 @@ class NamespacedClassManifestTest extends SapphireTest {
'silverstripe\test\classg' => "{$this->base}/module/classes/ClassG.php", 'silverstripe\test\classg' => "{$this->base}/module/classes/ClassG.php",
'silverstripe\test\classh' => "{$this->base}/module/classes/ClassH.php", 'silverstripe\test\classh' => "{$this->base}/module/classes/ClassH.php",
'sstemplateparser' => FRAMEWORK_PATH."/view/SSTemplateParser.php", 'sstemplateparser' => FRAMEWORK_PATH."/view/SSTemplateParser.php",
'sstemplateparseexception' => FRAMEWORK_PATH."/view/SSTemplateParser.php" 'sstemplateparseexception' => FRAMEWORK_PATH."/view/SSTemplateParser.php",
'silverstripe\framework\tests\classi' => "{$this->base}/module/classes/ClassI.php",
); );
$this->assertEquals($expect, $this->manifest->getClasses()); $this->assertEquals($expect, $this->manifest->getClasses());
@ -54,7 +201,7 @@ class NamespacedClassManifestTest extends SapphireTest {
array('sstemplateparser', 'sstemplateparseexception', 'silverstripe\test\classa', array('sstemplateparser', 'sstemplateparseexception', 'silverstripe\test\classa',
'silverstripe\test\classb', 'silverstripe\test\classc', 'silverstripe\test\classd', 'silverstripe\test\classb', 'silverstripe\test\classc', 'silverstripe\test\classd',
'silverstripe\test\classe', 'silverstripe\test\classf', 'silverstripe\test\classg', 'silverstripe\test\classe', 'silverstripe\test\classf', 'silverstripe\test\classg',
'silverstripe\test\classh'), 'silverstripe\test\classh', 'silverstripe\framework\tests\classi'),
$this->manifest->getClassNames()); $this->manifest->getClassNames());
} }
@ -88,7 +235,8 @@ class NamespacedClassManifestTest extends SapphireTest {
$expect = array( $expect = array(
'silverstripe\test\interfacea' => array('silverstripe\test\ClassE'), 'silverstripe\test\interfacea' => array('silverstripe\test\ClassE'),
'interfacea' => array('silverstripe\test\ClassF'), 'interfacea' => array('silverstripe\test\ClassF'),
'silverstripe\test\subtest\interfacea' => array('silverstripe\test\ClassG') 'silverstripe\test\subtest\interfacea' => array('silverstripe\test\ClassG'),
'permissionprovider' => array('SilverStripe\Framework\Tests\ClassI'),
); );
$this->assertEquals($expect, $this->manifest->getImplementors()); $this->assertEquals($expect, $this->manifest->getImplementors());
} }

View File

@ -0,0 +1,12 @@
<?php
namespace SilverStripe\Framework\Tests;
//whitespace here is important for tests, please don't change it
use ModelAdmin;
use Controller as Cont ;
use SS_HTTPRequest as Request,SS_HTTPResponse AS Response, PermissionProvider AS P;
use silverstripe\test\ClassA;
use \DataObject;
class ClassI extends ModelAdmin implements P {
}

View File

@ -15,12 +15,16 @@ class DeprecationTest extends SapphireTest {
static $originalVersionInfo; static $originalVersionInfo;
public function setUp() { public function setUp() {
parent::setUp();
self::$originalVersionInfo = Deprecation::dump_settings(); self::$originalVersionInfo = Deprecation::dump_settings();
Deprecation::$notice_level = E_USER_NOTICE; Deprecation::$notice_level = E_USER_NOTICE;
Deprecation::set_enabled(true);
} }
public function tearDown() { public function tearDown() {
Deprecation::restore_settings(self::$originalVersionInfo); Deprecation::restore_settings(self::$originalVersionInfo);
parent::tearDown();
} }
public function testLesserVersionTriggersNoNotice() { public function testLesserVersionTriggersNoNotice() {

View File

@ -11,7 +11,7 @@ class DevAdminControllerTest extends FunctionalTest {
public function setUp(){ public function setUp(){
parent::setUp(); parent::setUp();
Config::nest()->update('DevelopmentAdmin', 'registered_controllers', array( Config::inst()->update('DevelopmentAdmin', 'registered_controllers', array(
'x1' => array( 'x1' => array(
'controller' => 'DevAdminControllerTest_Controller1', 'controller' => 'DevAdminControllerTest_Controller1',
'links' => array( 'links' => array(
@ -28,11 +28,6 @@ class DevAdminControllerTest extends FunctionalTest {
)); ));
} }
public function tearDown(){
parent::tearDown();
Config::unnest();
}
public function testGoodRegisteredControllerOutput(){ public function testGoodRegisteredControllerOutput(){
// Check for the controller running from the registered url above // Check for the controller running from the registered url above

View File

@ -111,7 +111,7 @@ class EmailTest extends SapphireTest {
$oldProject = $project; $oldProject = $project;
$project = 'emailtest'; $project = 'emailtest';
Email::set_mailer(new EmailTest_Mailer()); Injector::inst()->registerService(new EmailTest_Mailer(), 'Mailer');
$email = new Email( $email = new Email(
'from@example.com', 'from@example.com',
'to@example.com', 'to@example.com',
@ -160,7 +160,7 @@ class EmailTest extends SapphireTest {
$oldProject = $project; $oldProject = $project;
$project = 'emailtest'; $project = 'emailtest';
Email::set_mailer(new EmailTest_Mailer()); Injector::inst()->registerService(new EmailTest_Mailer(), 'Mailer');
$email = new Email( $email = new Email(
'from@example.com', 'from@example.com',
'to@example.com', 'to@example.com',

View File

@ -250,6 +250,9 @@ class FileTest extends SapphireTest {
$file = $this->objFromFixture('File', 'pdf'); $file = $this->objFromFixture('File', 'pdf');
$this->assertEquals("Adobe Acrobat PDF file", $file->FileType); $this->assertEquals("Adobe Acrobat PDF file", $file->FileType);
$file = $this->objFromFixture('File', 'gifupper');
$this->assertEquals("GIF image - good for diagrams", $file->FileType);
/* Only a few file types are given special descriptions; the rest are unknown */ /* Only a few file types are given special descriptions; the rest are unknown */
$file = $this->objFromFixture('File', 'asdf'); $file = $this->objFromFixture('File', 'asdf');
$this->assertEquals("unknown", $file->FileType); $this->assertEquals("unknown", $file->FileType);

View File

@ -13,6 +13,8 @@ File:
Filename: assets/FileTest.txt Filename: assets/FileTest.txt
gif: gif:
Filename: assets/FileTest.gif Filename: assets/FileTest.gif
gifupper:
Filename: assets/FileTest.GIF
pdf: pdf:
Filename: assets/FileTest.pdf Filename: assets/FileTest.pdf
setfromname: setfromname:

View File

@ -214,6 +214,24 @@ class DatetimeFieldTest extends SapphireTest {
); );
} }
public function testGetName() {
$field = new DatetimeField('Datetime');
$this->assertEquals('Datetime', $field->getName());
$this->assertEquals('Datetime[date]', $field->getDateField()->getName());
$this->assertEquals('Datetime[time]', $field->getTimeField()->getName());
$this->assertEquals('Datetime[timezone]', $field->getTimezoneField()->getName());
}
public function testSetName() {
$field = new DatetimeField('Datetime', 'Datetime');
$field->setName('CustomDatetime');
$this->assertEquals('CustomDatetime', $field->getName());
$this->assertEquals('CustomDatetime[date]', $field->getDateField()->getName());
$this->assertEquals('CustomDatetime[time]', $field->getTimeField()->getName());
$this->assertEquals('CustomDatetime[timezone]', $field->getTimezoneField()->getName());
}
protected function getMockForm() { protected function getMockForm() {
return new Form( return new Form(
new Controller(), new Controller(),

View File

@ -91,6 +91,14 @@ class TimeFieldTest extends SapphireTest {
$f = new TimeField('Time', 'Time'); $f = new TimeField('Time', 'Time');
$f->setValue('23:59:38'); $f->setValue('23:59:38');
$this->assertEquals($f->dataValue(), '23:59:38'); $this->assertEquals($f->dataValue(), '23:59:38');
$f = new TimeField('Time', 'Time');
$f->setValue('12:00 am');
$this->assertEquals($f->dataValue(), '00:00:00');
$f = new TimeField('Time', 'Time');
$f->setValue('12:00:01 am');
$this->assertEquals($f->dataValue(), '00:00:01');
} }
public function testOverrideWithNull() { public function testOverrideWithNull() {

View File

@ -253,23 +253,49 @@ class UploadFieldTest extends FunctionalTest {
// two should work and the third will fail. // two should work and the third will fail.
$record = $this->objFromFixture('UploadFieldTest_Record', 'record1'); $record = $this->objFromFixture('UploadFieldTest_Record', 'record1');
$record->HasManyFilesMaxTwo()->removeAll(); $record->HasManyFilesMaxTwo()->removeAll();
$this->assertCount(0, $record->HasManyFilesMaxTwo());
// Get references for each file to upload // Get references for each file to upload
$file1 = $this->objFromFixture('File', 'file1'); $file1 = $this->objFromFixture('File', 'file1');
$file2 = $this->objFromFixture('File', 'file2'); $file2 = $this->objFromFixture('File', 'file2');
$file3 = $this->objFromFixture('File', 'file3'); $file3 = $this->objFromFixture('File', 'file3');
$this->assertTrue($file1->exists());
$this->assertTrue($file2->exists());
$this->assertTrue($file3->exists());
// Write the first element, should be okay. // Write the first element, should be okay.
$response = $this->mockUploadFileIDs('HasManyFilesMaxTwo', array($file1->ID)); $response = $this->mockUploadFileIDs('HasManyFilesMaxTwo', array($file1->ID));
$this->assertEmpty($response['errors']); $this->assertEmpty($response['errors']);
$this->assertCount(1, $record->HasManyFilesMaxTwo());
$this->assertContains($file1->ID, $record->HasManyFilesMaxTwo()->getIDList());
$record->HasManyFilesMaxTwo()->removeAll();
$this->assertCount(0, $record->HasManyFilesMaxTwo());
$this->assertTrue($file1->exists());
$this->assertTrue($file2->exists());
$this->assertTrue($file3->exists());
// Write the second element, should be okay. // Write the second element, should be okay.
$response = $this->mockUploadFileIDs('HasManyFilesMaxTwo', array($file1->ID, $file2->ID)); $response = $this->mockUploadFileIDs('HasManyFilesMaxTwo', array($file1->ID, $file2->ID));
$this->assertEmpty($response['errors']); $this->assertEmpty($response['errors']);
$this->assertCount(2, $record->HasManyFilesMaxTwo());
$this->assertContains($file1->ID, $record->HasManyFilesMaxTwo()->getIDList());
$this->assertContains($file2->ID, $record->HasManyFilesMaxTwo()->getIDList());
$record->HasManyFilesMaxTwo()->removeAll();
$this->assertCount(0, $record->HasManyFilesMaxTwo());
$this->assertTrue($file1->exists());
$this->assertTrue($file2->exists());
$this->assertTrue($file3->exists());
// Write the third element, should result in error. // Write the third element, should result in error.
$response = $this->mockUploadFileIDs('HasManyFilesMaxTwo', array($file1->ID, $file2->ID, $file3->ID)); $response = $this->mockUploadFileIDs('HasManyFilesMaxTwo', array($file1->ID, $file2->ID, $file3->ID));
$this->assertNotEmpty($response['errors']); $this->assertNotEmpty($response['errors']);
$this->assertCount(0, $record->HasManyFilesMaxTwo());
} }
/** /**
@ -948,22 +974,21 @@ class UploadFieldTest extends FunctionalTest {
} }
public function setUp() { public function setUp() {
Config::inst()->update('File', 'update_filesystem', false);
parent::setUp(); parent::setUp();
if(!file_exists(ASSETS_PATH)) mkdir(ASSETS_PATH); if(!file_exists(ASSETS_PATH)) mkdir(ASSETS_PATH);
/* Create a test folders for each of the fixture references */ /* Create a test folders for each of the fixture references */
$folderIDs = $this->allFixtureIDs('Folder'); $folders = Folder::get()->byIDs($this->allFixtureIDs('Folder'));
foreach($folderIDs as $folderID) { foreach($folders as $folder) {
$folder = DataObject::get_by_id('Folder', $folderID); if(!file_exists($folder->getFullPath())) mkdir($folder->getFullPath());
if(!file_exists(BASE_PATH."/$folder->Filename")) mkdir(BASE_PATH."/$folder->Filename");
} }
/* Create a test files for each of the fixture references */ /* Create a test files for each of the fixture references */
$fileIDs = $this->allFixtureIDs('File'); $files = File::get()->byIDs($this->allFixtureIDs('File'));
foreach($fileIDs as $fileID) { foreach($files as $file) {
$file = DataObject::get_by_id('File', $fileID); $fh = fopen($file->getFullPath(), "w");
$fh = fopen(BASE_PATH."/$file->Filename", "w");
fwrite($fh, str_repeat('x',1000000)); fwrite($fh, str_repeat('x',1000000));
fclose($fh); fclose($fh);
} }

View File

@ -3,48 +3,48 @@ Folder:
Name: UploadFieldTest Name: UploadFieldTest
folder1-subfolder1: folder1-subfolder1:
Name: subfolder1 Name: subfolder1
ParentID: =>Folder.folder1 Parent: =>Folder.folder1
File: File:
file1: file1:
Title: File1 Title: File1
Filename: assets/UploadFieldTest/file1.txt Filename: assets/UploadFieldTest/file1.txt
ParentID: =>Folder.folder1 Parent: =>Folder.folder1
file2: file2:
Title: File2 Title: File2
Filename: assets/UploadFieldTest/file2.txt Filename: assets/UploadFieldTest/file2.txt
ParentID: =>Folder.folder1 Parent: =>Folder.folder1
file3: file3:
Title: File3 Title: File3
Filename: assets/UploadFieldTest/file3.txt Filename: assets/UploadFieldTest/file3.txt
ParentID: =>Folder.folder1 Parent: =>Folder.folder1
file4: file4:
Title: File4 Title: File4
Filename: assets/UploadFieldTest/file4.txt Filename: assets/UploadFieldTest/file4.txt
ParentID: =>Folder.folder1 Parent: =>Folder.folder1
file5: file5:
Title: File5 Title: File5
Filename: assets/UploadFieldTest/file5.txt Filename: assets/UploadFieldTest/file5.txt
ParentID: =>Folder.folder1 Parent: =>Folder.folder1
file-noview: file-noview:
Title: noview.txt Title: noview.txt
Name: noview.txt Name: noview.txt
Filename: assets/UploadFieldTest/noview.txt Filename: assets/UploadFieldTest/noview.txt
ParentID: =>Folder.folder1 Parent: =>Folder.folder1
file-noedit: file-noedit:
Title: noedit.txt Title: noedit.txt
Name: noedit.txt Name: noedit.txt
Filename: assets/UploadFieldTest/noedit.txt Filename: assets/UploadFieldTest/noedit.txt
ParentID: =>Folder.folder1 Parent: =>Folder.folder1
file-nodelete: file-nodelete:
Title: nodelete.txt Title: nodelete.txt
Name: nodelete.txt Name: nodelete.txt
Filename: assets/UploadFieldTest/nodelete.txt Filename: assets/UploadFieldTest/nodelete.txt
ParentID: =>Folder.folder1 Parent: =>Folder.folder1
file-subfolder: file-subfolder:
Title: file-subfolder.txt Title: file-subfolder.txt
Name: file-subfolder.txt Name: file-subfolder.txt
Filename: assets/UploadFieldTest/subfolder1/file-subfolder.txt Filename: assets/UploadFieldTest/subfolder1/file-subfolder.txt
ParentID: =>Folder.folder1-subfolder1 Parent: =>Folder.folder1-subfolder1
UploadFieldTest_Record: UploadFieldTest_Record:
record1: record1:
Title: Record 1 Title: Record 1

View File

View File

@ -0,0 +1 @@
<?php

View File

@ -0,0 +1 @@
<p></p>

View File

@ -0,0 +1 @@
<?php

View File

@ -0,0 +1 @@
<?php

View File

@ -0,0 +1,2 @@
<?php

View File

@ -442,13 +442,11 @@ YAML;
$html = file_get_contents($templateFilePath); $html = file_get_contents($templateFilePath);
$matches = $c->collectFromTemplate($html, 'mymodule', 'RandomNamespace'); $matches = $c->collectFromTemplate($html, 'mymodule', 'RandomNamespace');
/* $this->assertArrayHasKey('RandomNamespace.LAYOUTTEMPLATENONAMESPACE', $matches);
$this->assertArrayHasKey('i18nTestModule.ss.LAYOUTTEMPLATENONAMESPACE', $matches);
$this->assertEquals( $this->assertEquals(
$matches['i18nTestModule.ss.LAYOUTTEMPLATENONAMESPACE'], $matches['RandomNamespace.LAYOUTTEMPLATENONAMESPACE'],
array('Layout Template no namespace') array('Layout Template no namespace')
); );
*/
$this->assertArrayHasKey('RandomNamespace.SPRINTFNONAMESPACE', $matches); $this->assertArrayHasKey('RandomNamespace.SPRINTFNONAMESPACE', $matches);
$this->assertEquals( $this->assertEquals(
$matches['RandomNamespace.SPRINTFNONAMESPACE'], $matches['RandomNamespace.SPRINTFNONAMESPACE'],
@ -464,85 +462,57 @@ YAML;
$matches['i18nTestModule.SPRINTFNAMESPACE'], $matches['i18nTestModule.SPRINTFNAMESPACE'],
array('My replacement: %s') array('My replacement: %s')
); );
$this->assertArrayHasKey('i18nTestModule.WITHNAMESPACE', $matches);
$this->assertEquals( // Includes should not automatically inject translations into parent templates
$matches['i18nTestModule.WITHNAMESPACE'], $this->assertArrayNotHasKey('i18nTestModule.WITHNAMESPACE', $matches);
array('Include Entity with Namespace') $this->assertArrayNotHasKey('i18nTestModuleInclude.ss.NONAMESPACE', $matches);
); $this->assertArrayNotHasKey('i18nTestModuleInclude.ss.SPRINTFINCLUDENAMESPACE', $matches);
$this->assertArrayHasKey('i18nTestModuleInclude.ss.NONAMESPACE', $matches); $this->assertArrayNotHasKey('i18nTestModuleInclude.ss.SPRINTFINCLUDENONAMESPACE', $matches);
$this->assertEquals(
$matches['i18nTestModuleInclude.ss.NONAMESPACE'],
array('Include Entity without Namespace')
);
$this->assertArrayHasKey('i18nTestModuleInclude.ss.SPRINTFINCLUDENAMESPACE', $matches);
$this->assertEquals(
$matches['i18nTestModuleInclude.ss.SPRINTFINCLUDENAMESPACE'],
array('My include replacement: %s')
);
$this->assertArrayHasKey('i18nTestModuleInclude.ss.SPRINTFINCLUDENONAMESPACE', $matches);
$this->assertEquals(
$matches['i18nTestModuleInclude.ss.SPRINTFINCLUDENONAMESPACE'],
array('My include replacement no namespace: %s')
);
} }
public function testCollectFromThemesTemplates() { public function testCollectFromThemesTemplates() {
$c = new i18nTextCollector(); $c = new i18nTextCollector();
$theme = Config::inst()->get('SSViewer', 'theme');
Config::inst()->update('SSViewer', 'theme', 'testtheme1'); Config::inst()->update('SSViewer', 'theme', 'testtheme1');
$templateFilePath = $this->alternateBasePath . '/themes/testtheme1/templates/Layout/i18nTestTheme1.ss'; // Collect from layout
$html = file_get_contents($templateFilePath); $layoutFilePath = $this->alternateBasePath . '/themes/testtheme1/templates/Layout/i18nTestTheme1.ss';
$matches = $c->collectFromTemplate($html, 'themes/testtheme1', 'i18nTestTheme1.ss'); $layoutHTML = file_get_contents($layoutFilePath);
$layoutMatches = $c->collectFromTemplate($layoutHTML, 'themes/testtheme1', 'i18nTestTheme1.ss');
// all entities from i18nTestTheme1.ss // all entities from i18nTestTheme1.ss
$this->assertEquals( $this->assertEquals(
$matches['i18nTestTheme1.LAYOUTTEMPLATE'], array(
array('Theme1 Layout Template') 'i18nTestTheme1.LAYOUTTEMPLATE'
=> array('Theme1 Layout Template'),
'i18nTestTheme1.SPRINTFNAMESPACE'
=> array('Theme1 My replacement: %s'),
'i18nTestTheme1.ss.LAYOUTTEMPLATENONAMESPACE'
=> array('Theme1 Layout Template no namespace'),
'i18nTestTheme1.ss.SPRINTFNONAMESPACE'
=> array('Theme1 My replacement no namespace: %s'),
),
$layoutMatches
); );
$this->assertArrayHasKey('i18nTestTheme1.ss.LAYOUTTEMPLATENONAMESPACE', $matches); // Collect from include
$this->assertEquals( $includeFilePath = $this->alternateBasePath . '/themes/testtheme1/templates/Includes/i18nTestTheme1Include.ss';
$matches['i18nTestTheme1.ss.LAYOUTTEMPLATENONAMESPACE'], $includeHTML = file_get_contents($includeFilePath);
array('Theme1 Layout Template no namespace') $includeMatches = $c->collectFromTemplate($includeHTML, 'themes/testtheme1', 'i18nTestTheme1Include.ss');
);
$this->assertEquals(
$matches['i18nTestTheme1.SPRINTFNAMESPACE'],
array('Theme1 My replacement: %s')
);
$this->assertArrayHasKey('i18nTestTheme1.ss.SPRINTFNONAMESPACE', $matches);
$this->assertEquals(
$matches['i18nTestTheme1.ss.SPRINTFNONAMESPACE'],
array('Theme1 My replacement no namespace: %s')
);
// all entities from i18nTestTheme1Include.ss // all entities from i18nTestTheme1Include.ss
$this->assertEquals( $this->assertEquals(
$matches['i18nTestTheme1Include.WITHNAMESPACE'], array(
array('Theme1 Include Entity with Namespace') 'i18nTestTheme1Include.SPRINTFINCLUDENAMESPACE'
=> array('Theme1 My include replacement: %s'),
'i18nTestTheme1Include.WITHNAMESPACE'
=> array('Theme1 Include Entity with Namespace'),
'i18nTestTheme1Include.ss.NONAMESPACE'
=> array('Theme1 Include Entity without Namespace'),
'i18nTestTheme1Include.ss.SPRINTFINCLUDENONAMESPACE'
=> array('Theme1 My include replacement no namespace: %s')
),
$includeMatches
); );
$this->assertArrayHasKey('i18nTestTheme1Include.ss.NONAMESPACE', $matches);
$this->assertEquals(
$matches['i18nTestTheme1Include.ss.NONAMESPACE'],
array('Theme1 Include Entity without Namespace')
);
$this->assertEquals(
$matches['i18nTestTheme1Include.SPRINTFINCLUDENAMESPACE'],
array('Theme1 My include replacement: %s')
);
$this->assertArrayHasKey('i18nTestTheme1Include.ss.SPRINTFINCLUDENONAMESPACE', $matches);
$this->assertEquals(
$matches['i18nTestTheme1Include.ss.SPRINTFINCLUDENONAMESPACE'],
array('Theme1 My include replacement no namespace: %s')
);
Config::inst()->update('SSViewer', 'theme', $theme);
} }
public function testCollectMergesWithExisting() { public function testCollectMergesWithExisting() {
@ -714,4 +684,163 @@ YAML;
); );
} }
/**
* Test that duplicate keys are resolved to the appropriate modules
*/
public function testResolveDuplicates() {
$collector = new i18nTextCollectorTest_Collector();
// Dummy data as collected
$data1 = array(
'framework' => array(
'DataObject.PLURALNAME' => array('Data Objects'),
'DataObject.SINGULARNAME' => array('Data Object')
),
'mymodule' => array(
'DataObject.PLURALNAME' => array('Ignored String'),
'DataObject.STREETNAME' => array('Shortland Street')
)
);
$expected = array(
'framework' => array(
'DataObject.PLURALNAME' => array('Data Objects'),
// Because DataObject is in framework module
'DataObject.SINGULARNAME' => array('Data Object')
),
'mymodule' => array(
// Because this key doesn't exist in framework strings
'DataObject.STREETNAME' => array('Shortland Street')
)
);
$resolved = $collector->resolveDuplicateConflicts_Test($data1);
$this->assertEquals($expected, $resolved);
// Test getConflicts
$data2 = array(
'module1' => array(
'DataObject.ONE' => array('One'),
'DataObject.TWO' => array('Two'),
'DataObject.THREE' => array('Three'),
),
'module2' => array(
'DataObject.THREE' => array('Three'),
),
'module3' => array(
'DataObject.TWO' => array('Two'),
'DataObject.THREE' => array('Three'),
)
);
$conflictsA = $collector->getConflicts_Test($data2);
sort($conflictsA);
$this->assertEquals(
array('DataObject.THREE', 'DataObject.TWO'),
$conflictsA
);
// Removing module3 should remove a conflict
unset($data2['module3']);
$conflictsB = $collector->getConflicts_Test($data2);
$this->assertEquals(
array('DataObject.THREE'),
$conflictsB
);
}
/**
* Test ability for textcollector to detect modules
*/
public function testModuleDetection() {
$collector = new i18nTextCollectorTest_Collector();
$modules = $collector->getModules_Test($this->alternateBasePath);
$this->assertEquals(
array(
'i18nnonstandardmodule',
'i18nothermodule',
'i18ntestmodule',
'themes/testtheme1',
'themes/testtheme2'
),
$modules
);
}
/**
* Test that text collector can detect module file lists properly
*/
public function testModuleFileList() {
$collector = new i18nTextCollectorTest_Collector();
$collector->basePath = $this->alternateBasePath;
$collector->baseSavePath = $this->alternateBaseSavePath;
// Non-standard modules can't be safely filtered, so just index everything
$nonStandardFiles = $collector->getFileListForModule_Test('i18nnonstandardmodule');
$nonStandardRoot = $this->alternateBasePath . '/i18nnonstandardmodule';
$this->assertEquals(3, count($nonStandardFiles));
$this->assertArrayHasKey("{$nonStandardRoot}/_config.php", $nonStandardFiles);
$this->assertArrayHasKey("{$nonStandardRoot}/phpfile.php", $nonStandardFiles);
$this->assertArrayHasKey("{$nonStandardRoot}/template.ss", $nonStandardFiles);
// Normal module should have predictable dir structure
$testFiles = $collector->getFileListForModule_Test('i18ntestmodule');
$testRoot = $this->alternateBasePath . '/i18ntestmodule';
$this->assertEquals(6, count($testFiles));
// Code in code folder is detected
$this->assertArrayHasKey("{$testRoot}/code/i18nTestModule.php", $testFiles);
$this->assertArrayHasKey("{$testRoot}/code/subfolder/_config.php", $testFiles);
$this->assertArrayHasKey("{$testRoot}/code/subfolder/i18nTestSubModule.php", $testFiles);
// Templates in templates folder is detected
$this->assertArrayHasKey("{$testRoot}/templates/Includes/i18nTestModuleInclude.ss", $testFiles);
$this->assertArrayHasKey("{$testRoot}/templates/Layout/i18nTestModule.ss", $testFiles);
$this->assertArrayHasKey("{$testRoot}/templates/i18nTestModule.ss", $testFiles);
// Standard modules with code in odd places should only have code in those directories detected
$otherFiles = $collector->getFileListForModule_Test('i18nothermodule');
$otherRoot = $this->alternateBasePath . '/i18nothermodule';
$this->assertEquals(3, count($otherFiles));
// Only detect well-behaved files
$this->assertArrayHasKey("{$otherRoot}/code/i18nOtherModule.php", $otherFiles);
$this->assertArrayHasKey("{$otherRoot}/code/i18nTestModuleDecorator.php", $otherFiles);
$this->assertArrayHasKey("{$otherRoot}/templates/i18nOtherModule.ss", $otherFiles);
// Themes should detect all ss files only
$theme1Files = $collector->getFileListForModule_Test('themes/testtheme1');
$theme1Root = $this->alternateBasePath . '/themes/testtheme1/templates';
$this->assertEquals(3, count($theme1Files));
// Find only ss files
$this->assertArrayHasKey("{$theme1Root}/Includes/i18nTestTheme1Include.ss", $theme1Files);
$this->assertArrayHasKey("{$theme1Root}/Layout/i18nTestTheme1.ss", $theme1Files);
$this->assertArrayHasKey("{$theme1Root}/i18nTestTheme1Main.ss", $theme1Files);
// Only 1 file here
$theme2Files = $collector->getFileListForModule_Test('themes/testtheme2');
$this->assertEquals(1, count($theme2Files));
$this->assertArrayHasKey(
$this->alternateBasePath . '/themes/testtheme2/templates/i18nTestTheme2.ss',
$theme2Files
);
}
}
/**
* Assist with testing of specific protected methods
*/
class i18nTextCollectorTest_Collector extends i18nTextCollector implements TestOnly {
public function getModules_Test($directory) {
return $this->getModules($directory);
}
public function resolveDuplicateConflicts_Test($entitiesByModule) {
return $this->resolveDuplicateConflicts($entitiesByModule);
}
public function getFileListForModule_Test($module) {
return parent::getFileListForModule($module);
}
public function getConflicts_Test($entitiesByModule) {
return parent::getConflicts($entitiesByModule);
}
} }

View File

@ -146,6 +146,11 @@ class DataListTest extends SapphireTest {
$this->assertEquals('DataObjectTest_TeamComment',$list->dataClass()); $this->assertEquals('DataObjectTest_TeamComment',$list->dataClass());
} }
public function testDataClassCaseInsensitive() {
$list = DataList::create('dataobjecttest_teamcomment');
$this->assertTrue($list->exists());
}
public function testClone() { public function testClone() {
$list = DataObjectTest_TeamComment::get(); $list = DataObjectTest_TeamComment::get();
$this->assertEquals($list, clone($list)); $this->assertEquals($list, clone($list));

View File

@ -46,7 +46,6 @@ class DataObjectSchemaGenerationTest extends SapphireTest {
// Table will have been initially created by the $extraDataObjects setting // Table will have been initially created by the $extraDataObjects setting
// Let's insert a new field here // Let's insert a new field here
Config::nest();
Config::inst()->update('DataObjectSchemaGenerationTest_DO', 'db', array( Config::inst()->update('DataObjectSchemaGenerationTest_DO', 'db', array(
'SecretField' => 'Varchar(100)' 'SecretField' => 'Varchar(100)'
)); ));
@ -59,9 +58,6 @@ class DataObjectSchemaGenerationTest extends SapphireTest {
$schema->cancelSchemaUpdate(); $schema->cancelSchemaUpdate();
$test->assertTrue($needsUpdating); $test->assertTrue($needsUpdating);
}); });
// Restore db configuration
Config::unnest();
} }
/** /**
@ -84,7 +80,6 @@ class DataObjectSchemaGenerationTest extends SapphireTest {
}); });
// Test with alternate index format, although these indexes are the same // Test with alternate index format, although these indexes are the same
Config::nest();
Config::inst()->remove('DataObjectSchemaGenerationTest_IndexDO', 'indexes'); Config::inst()->remove('DataObjectSchemaGenerationTest_IndexDO', 'indexes');
Config::inst()->update('DataObjectSchemaGenerationTest_IndexDO', 'indexes', Config::inst()->update('DataObjectSchemaGenerationTest_IndexDO', 'indexes',
Config::inst()->get('DataObjectSchemaGenerationTest_IndexDO', 'indexes_alt') Config::inst()->get('DataObjectSchemaGenerationTest_IndexDO', 'indexes_alt')
@ -98,9 +93,6 @@ class DataObjectSchemaGenerationTest extends SapphireTest {
$schema->cancelSchemaUpdate(); $schema->cancelSchemaUpdate();
$test->assertFalse($needsUpdating); $test->assertFalse($needsUpdating);
}); });
// Restore old index format
Config::unnest();
} }
/** /**
@ -114,7 +106,6 @@ class DataObjectSchemaGenerationTest extends SapphireTest {
// Table will have been initially created by the $extraDataObjects setting // Table will have been initially created by the $extraDataObjects setting
// Update the SearchFields index here // Update the SearchFields index here
Config::nest();
Config::inst()->update('DataObjectSchemaGenerationTest_IndexDO', 'indexes', array( Config::inst()->update('DataObjectSchemaGenerationTest_IndexDO', 'indexes', array(
'SearchFields' => array( 'SearchFields' => array(
'value' => 'Title' 'value' => 'Title'
@ -129,9 +120,6 @@ class DataObjectSchemaGenerationTest extends SapphireTest {
$schema->cancelSchemaUpdate(); $schema->cancelSchemaUpdate();
$test->assertTrue($needsUpdating); $test->assertTrue($needsUpdating);
}); });
// Restore old indexes
Config::unnest();
} }
/** /**

View File

@ -61,6 +61,30 @@ class DataObjectTest extends SapphireTest {
$this->assertEquals('Comment', key($dbFields), 'DataObject::db returns fields in correct order'); $this->assertEquals('Comment', key($dbFields), 'DataObject::db returns fields in correct order');
} }
public function testConstructAcceptsValues() {
// Values can be an array...
$player = new DataObjectTest_Player(array(
'FirstName' => 'James',
'Surname' => 'Smith'
));
$this->assertEquals('James', $player->FirstName);
$this->assertEquals('Smith', $player->Surname);
// ... or a stdClass inst
$data = new stdClass();
$data->FirstName = 'John';
$data->Surname = 'Doe';
$player = new DataObjectTest_Player($data);
$this->assertEquals('John', $player->FirstName);
$this->assertEquals('Doe', $player->Surname);
// IDs should be stored as integers, not strings
$player = new DataObjectTest_Player(array('ID' => '5'));
$this->assertSame(5, $player->ID);
}
public function testValidObjectsForBaseFields() { public function testValidObjectsForBaseFields() {
$obj = new DataObjectTest_ValidatedObject(); $obj = new DataObjectTest_ValidatedObject();

View File

@ -199,7 +199,7 @@ class DataQueryTest extends SapphireTest {
public function testComparisonClauseInt() { public function testComparisonClauseInt() {
DB::query("INSERT INTO \"DataQueryTest_F\" (\"SortOrder\") VALUES (2)"); DB::query("INSERT INTO \"DataQueryTest_F\" (\"SortOrder\") VALUES (2)");
$query = new DataQuery('DataQueryTest_F'); $query = new DataQuery('DataQueryTest_F');
$query->where(DB::getConn()->comparisonClause('"SortOrder"', '2')); $query->where(DB::get_conn()->comparisonClause('"SortOrder"', '2'));
$this->assertGreaterThan(0, $query->count(), "Couldn't find SortOrder"); $this->assertGreaterThan(0, $query->count(), "Couldn't find SortOrder");
$this->resetDBSchema(true); $this->resetDBSchema(true);
} }
@ -207,7 +207,7 @@ class DataQueryTest extends SapphireTest {
public function testComparisonClauseDateFull() { public function testComparisonClauseDateFull() {
DB::query("INSERT INTO \"DataQueryTest_F\" (\"MyDate\") VALUES ('1988-03-04 06:30')"); DB::query("INSERT INTO \"DataQueryTest_F\" (\"MyDate\") VALUES ('1988-03-04 06:30')");
$query = new DataQuery('DataQueryTest_F'); $query = new DataQuery('DataQueryTest_F');
$query->where(DB::getConn()->comparisonClause('"MyDate"', '1988-03-04%')); $query->where(DB::get_conn()->comparisonClause('"MyDate"', '1988-03-04%'));
$this->assertGreaterThan(0, $query->count(), "Couldn't find MyDate"); $this->assertGreaterThan(0, $query->count(), "Couldn't find MyDate");
$this->resetDBSchema(true); $this->resetDBSchema(true);
} }
@ -215,7 +215,7 @@ class DataQueryTest extends SapphireTest {
public function testComparisonClauseDateStartsWith() { public function testComparisonClauseDateStartsWith() {
DB::query("INSERT INTO \"DataQueryTest_F\" (\"MyDate\") VALUES ('1988-03-04 06:30')"); DB::query("INSERT INTO \"DataQueryTest_F\" (\"MyDate\") VALUES ('1988-03-04 06:30')");
$query = new DataQuery('DataQueryTest_F'); $query = new DataQuery('DataQueryTest_F');
$query->where(DB::getConn()->comparisonClause('"MyDate"', '1988%')); $query->where(DB::get_conn()->comparisonClause('"MyDate"', '1988%'));
$this->assertGreaterThan(0, $query->count(), "Couldn't find MyDate"); $this->assertGreaterThan(0, $query->count(), "Couldn't find MyDate");
$this->resetDBSchema(true); $this->resetDBSchema(true);
} }
@ -223,7 +223,7 @@ class DataQueryTest extends SapphireTest {
public function testComparisonClauseDateStartsPartial() { public function testComparisonClauseDateStartsPartial() {
DB::query("INSERT INTO \"DataQueryTest_F\" (\"MyDate\") VALUES ('1988-03-04 06:30')"); DB::query("INSERT INTO \"DataQueryTest_F\" (\"MyDate\") VALUES ('1988-03-04 06:30')");
$query = new DataQuery('DataQueryTest_F'); $query = new DataQuery('DataQueryTest_F');
$query->where(DB::getConn()->comparisonClause('"MyDate"', '%03-04%')); $query->where(DB::get_conn()->comparisonClause('"MyDate"', '%03-04%'));
$this->assertGreaterThan(0, $query->count(), "Couldn't find MyDate"); $this->assertGreaterThan(0, $query->count(), "Couldn't find MyDate");
$this->resetDBSchema(true); $this->resetDBSchema(true);
} }
@ -231,7 +231,7 @@ class DataQueryTest extends SapphireTest {
public function testComparisonClauseTextCaseInsensitive() { public function testComparisonClauseTextCaseInsensitive() {
DB::query("INSERT INTO \"DataQueryTest_F\" (\"MyString\") VALUES ('HelloWorld')"); DB::query("INSERT INTO \"DataQueryTest_F\" (\"MyString\") VALUES ('HelloWorld')");
$query = new DataQuery('DataQueryTest_F'); $query = new DataQuery('DataQueryTest_F');
$query->where(DB::getConn()->comparisonClause('"MyString"', 'helloworld')); $query->where(DB::get_conn()->comparisonClause('"MyString"', 'helloworld'));
$this->assertGreaterThan(0, $query->count(), "Couldn't find MyString"); $this->assertGreaterThan(0, $query->count(), "Couldn't find MyString");
$this->resetDBSchema(true); $this->resetDBSchema(true);
} }
@ -239,11 +239,11 @@ class DataQueryTest extends SapphireTest {
public function testComparisonClauseTextCaseSensitive() { public function testComparisonClauseTextCaseSensitive() {
DB::query("INSERT INTO \"DataQueryTest_F\" (\"MyString\") VALUES ('HelloWorld')"); DB::query("INSERT INTO \"DataQueryTest_F\" (\"MyString\") VALUES ('HelloWorld')");
$query = new DataQuery('DataQueryTest_F'); $query = new DataQuery('DataQueryTest_F');
$query->where(DB::getConn()->comparisonClause('"MyString"', 'HelloWorld', false, false, true)); $query->where(DB::get_conn()->comparisonClause('"MyString"', 'HelloWorld', false, false, true));
$this->assertGreaterThan(0, $query->count(), "Couldn't find MyString"); $this->assertGreaterThan(0, $query->count(), "Couldn't find MyString");
$query2 = new DataQuery('DataQueryTest_F'); $query2 = new DataQuery('DataQueryTest_F');
$query2->where(DB::getConn()->comparisonClause('"MyString"', 'helloworld', false, false, true)); $query2->where(DB::get_conn()->comparisonClause('"MyString"', 'helloworld', false, false, true));
$this->assertEquals(0, $query2->count(), "Found mystring. Shouldn't be able too."); $this->assertEquals(0, $query2->count(), "Found mystring. Shouldn't be able too.");
$this->resetDBSchema(true); $this->resetDBSchema(true);
} }

View File

@ -250,13 +250,13 @@ class ManyManyListTest extends SapphireTest {
// ensure that ManyManyListTest_ExtraFields_Clients.ValueCurrency is // ensure that ManyManyListTest_ExtraFields_Clients.ValueCurrency is
// selected. // selected.
$db = DB::getConn(); $db = DB::get_conn();
$expected = 'SELECT DISTINCT "ManyManyListTest_ExtraFields_Clients"."WorthCurrency",' $expected = 'SELECT DISTINCT "ManyManyListTest_ExtraFields_Clients"."WorthCurrency",'
.' "ManyManyListTest_ExtraFields_Clients"."WorthAmount", "ManyManyListTest_ExtraFields_Clients"."Reference",' .' "ManyManyListTest_ExtraFields_Clients"."WorthAmount", "ManyManyListTest_ExtraFields_Clients"."Reference",'
.' "ManyManyListTest_ExtraFields"."ClassName", "ManyManyListTest_ExtraFields"."LastEdited",' .' "ManyManyListTest_ExtraFields"."ClassName", "ManyManyListTest_ExtraFields"."LastEdited",'
.' "ManyManyListTest_ExtraFields"."Created", "ManyManyListTest_ExtraFields"."ID",' .' "ManyManyListTest_ExtraFields"."Created", "ManyManyListTest_ExtraFields"."ID",'
.' CASE WHEN "ManyManyListTest_ExtraFields"."ClassName" IS NOT NULL THEN' .' CASE WHEN "ManyManyListTest_ExtraFields"."ClassName" IS NOT NULL THEN'
.' "ManyManyListTest_ExtraFields"."ClassName" ELSE '. $db->prepStringForDB('ManyManyListTest_ExtraFields') .' "ManyManyListTest_ExtraFields"."ClassName" ELSE '. Convert::raw2sql('ManyManyListTest_ExtraFields', true)
.' END AS "RecordClassName" FROM "ManyManyListTest_ExtraFields" INNER JOIN' .' END AS "RecordClassName" FROM "ManyManyListTest_ExtraFields" INNER JOIN'
.' "ManyManyListTest_ExtraFields_Clients" ON' .' "ManyManyListTest_ExtraFields_Clients" ON'
.' "ManyManyListTest_ExtraFields_Clients"."ManyManyListTest_ExtraFieldsID" =' .' "ManyManyListTest_ExtraFields_Clients"."ManyManyListTest_ExtraFieldsID" ='

View File

@ -0,0 +1,113 @@
<?php
/**
* @package framework
* @subpackage testing
*/
class PDODatabaseTest extends SapphireTest {
protected static $fixture_file = 'MySQLDatabaseTest.yml';
protected $extraDataObjects = array(
'MySQLDatabaseTest_Data'
);
public function testPreparedStatements() {
if(!(DB::get_connector() instanceof PDOConnector)) {
$this->markTestSkipped('This test requires the current DB connector is PDO');
}
// Test preparation of equivalent statemetns
$result1 = DB::get_connector()->preparedQuery(
'SELECT "Sort", "Title" FROM "MySQLDatabaseTest_Data" WHERE "Sort" > ? ORDER BY "Sort"',
array(0)
);
$result2 = DB::get_connector()->preparedQuery(
'SELECT "Sort", "Title" FROM "MySQLDatabaseTest_Data" WHERE "Sort" > ? ORDER BY "Sort"',
array(2)
);
$this->assertInstanceOf('PDOQuery', $result1);
$this->assertInstanceOf('PDOQuery', $result2);
// Also select non-prepared statement
$result3 = DB::get_connector()->query('SELECT "Sort", "Title" FROM "MySQLDatabaseTest_Data" ORDER BY "Sort"');
$this->assertInstanceOf('PDOQuery', $result3);
// Iterating one level should not buffer, but return the right result
$this->assertEquals(
array(
'Sort' => 1,
'Title' => 'First Item'
),
$result1->next()
);
$this->assertEquals(
array(
'Sort' => 2,
'Title' => 'Second Item'
),
$result1->next()
);
// Test first
$this->assertEquals(
array(
'Sort' => 1,
'Title' => 'First Item'
),
$result1->first()
);
// Test seek
$this->assertEquals(
array(
'Sort' => 2,
'Title' => 'Second Item'
),
$result1->seek(1)
);
// Test count
$this->assertEquals(4, $result1->numRecords());
// Test second statement
$this->assertEquals(
array(
'Sort' => 3,
'Title' => 'Third Item'
),
$result2->next()
);
// Test non-prepared query
$this->assertEquals(
array(
'Sort' => 1,
'Title' => 'First Item'
),
$result3->next()
);
}
public function testAffectedRows() {
if(!(DB::get_connector() instanceof PDOConnector)) {
$this->markTestSkipped('This test requires the current DB connector is PDO');
}
$query = new SQLUpdate('MySQLDatabaseTest_Data');
$query->setAssignments(array('Title' => 'New Title'));
// Test update which affects no rows
$query->setWhere(array('Title' => 'Bob'));
$result = $query->execute();
$this->assertInstanceOf('PDOQuery', $result);
$this->assertEquals(0, DB::affected_rows());
// Test update which affects some rows
$query->setWhere(array('Title' => 'First Item'));
$result = $query->execute();
$this->assertInstanceOf('PDOQuery', $result);
$this->assertEquals(1, DB::affected_rows());
}
}

View File

@ -14,6 +14,18 @@ class SQLQueryTest extends SapphireTest {
'SQLQueryTestChild' 'SQLQueryTestChild'
); );
protected $oldDeprecation = null;
public function setUp() {
parent::setUp();
$this->oldDeprecation = Deprecation::dump_settings();
}
public function tearDown() {
Deprecation::restore_settings($this->oldDeprecation);
parent::tearDown();
}
public function testCount() { public function testCount() {
//basic counting //basic counting
@ -583,7 +595,7 @@ class SQLQueryTest extends SapphireTest {
} }
public function testSelect() { public function testSelect() {
$query = new SQLQuery('"Title"', '"MyTable"'); $query = new SQLSelect('"Title"', '"MyTable"');
$query->addSelect('"TestField"'); $query->addSelect('"TestField"');
$this->assertSQLEquals( $this->assertSQLEquals(
'SELECT "Title", "TestField" FROM "MyTable"', 'SELECT "Title", "TestField" FROM "MyTable"',
@ -670,6 +682,100 @@ class SQLQueryTest extends SapphireTest {
$this->assertEquals(array('%MyName%', '2012-08-08 12:00'), $parameters); $this->assertEquals(array('%MyName%', '2012-08-08 12:00'), $parameters);
$query->execute(); $query->execute();
} }
/**
* Test deprecation of SQLQuery::getWhere working appropriately
*/
public function testDeprecatedGetWhere() {
// Temporarily disable deprecation
Deprecation::notification_version(null);
$query = new SQLQuery();
$query->setSelect(array('"SQLQueryTest_DO"."Name"'));
$query->setFrom('"SQLQueryTest_DO"');
$query->addWhere(array(
'"SQLQueryTest_DO"."Date" > ?' => '2012-08-08 12:00'
));
$query->addWhere('"SQLQueryTest_DO"."Name" = \'Richard\'');
$query->addWhere(array(
'"SQLQueryTest_DO"."Meta" IN (?, \'Who?\', ?)' => array('Left', 'Right')
));
$expectedSQL = <<<EOS
SELECT "SQLQueryTest_DO"."Name"
FROM "SQLQueryTest_DO"
WHERE ("SQLQueryTest_DO"."Date" > ?)
AND ("SQLQueryTest_DO"."Name" = 'Richard')
AND ("SQLQueryTest_DO"."Meta" IN (?, 'Who?', ?))
EOS
;
$expectedParameters = array('2012-08-08 12:00', 'Left', 'Right');
// Check sql evaluation of this query maintains the parameters
$sql = $query->sql($parameters);
$this->assertSQLEquals($expectedSQL, $sql);
$this->assertEquals($expectedParameters, $parameters);
// Check that ->toAppropriateExpression()->setWhere doesn't modify the query
$query->setWhere($query->toAppropriateExpression()->getWhere());
$sql = $query->sql($parameters);
$this->assertSQLEquals($expectedSQL, $sql);
$this->assertEquals($expectedParameters, $parameters);
// Check that getWhere are all flattened queries
$expectedFlattened = array(
'"SQLQueryTest_DO"."Date" > \'2012-08-08 12:00\'',
'"SQLQueryTest_DO"."Name" = \'Richard\'',
'"SQLQueryTest_DO"."Meta" IN (\'Left\', \'Who?\', \'Right\')'
);
$this->assertEquals($expectedFlattened, $query->getWhere());
}
/**
* Test deprecation of SQLQuery::setDelete/getDelete
*/
public function testDeprecatedSetDelete() {
// Temporarily disable deprecation
Deprecation::notification_version(null);
$query = new SQLQuery();
$query->setSelect(array('"SQLQueryTest_DO"."Name"'));
$query->setFrom('"SQLQueryTest_DO"');
$query->setWhere(array('"SQLQueryTest_DO"."Name"' => 'Andrew'));
// Check SQL for select
$this->assertSQLEquals(<<<EOS
SELECT "SQLQueryTest_DO"."Name" FROM "SQLQueryTest_DO"
WHERE ("SQLQueryTest_DO"."Name" = ?)
EOS
,
$query->sql($parameters)
);
$this->assertEquals(array('Andrew'), $parameters);
// Check setDelete works
$query->setDelete(true);
$this->assertSQLEquals(<<<EOS
DELETE FROM "SQLQueryTest_DO"
WHERE ("SQLQueryTest_DO"."Name" = ?)
EOS
,
$query->sql($parameters)
);
$this->assertEquals(array('Andrew'), $parameters);
// Check that setDelete back to false restores the state
$query->setDelete(false);
$this->assertSQLEquals(<<<EOS
SELECT "SQLQueryTest_DO"."Name" FROM "SQLQueryTest_DO"
WHERE ("SQLQueryTest_DO"."Name" = ?)
EOS
,
$query->sql($parameters)
);
$this->assertEquals(array('Andrew'), $parameters);
}
} }
class SQLQueryTest_DO extends DataObject implements TestOnly { class SQLQueryTest_DO extends DataObject implements TestOnly {

View File

@ -36,6 +36,22 @@ class StringFieldTest extends SapphireTest {
); );
} }
public function testExists() {
// True exists
$this->assertTrue(DBField::create_field('StringFieldTest_MyStringField', true)->exists());
$this->assertTrue(DBField::create_field('StringFieldTest_MyStringField', '0')->exists());
$this->assertTrue(DBField::create_field('StringFieldTest_MyStringField', '1')->exists());
$this->assertTrue(DBField::create_field('StringFieldTest_MyStringField', 1)->exists());
$this->assertTrue(DBField::create_field('StringFieldTest_MyStringField', 1.1)->exists());
// false exists
$this->assertFalse(DBField::create_field('StringFieldTest_MyStringField', false)->exists());
$this->assertFalse(DBField::create_field('StringFieldTest_MyStringField', '')->exists());
$this->assertFalse(DBField::create_field('StringFieldTest_MyStringField', null)->exists());
$this->assertFalse(DBField::create_field('StringFieldTest_MyStringField', 0)->exists());
$this->assertFalse(DBField::create_field('StringFieldTest_MyStringField', 0.0)->exists());
}
} }
class StringFieldTest_MyStringField extends StringField implements TestOnly { class StringFieldTest_MyStringField extends StringField implements TestOnly {

View File

@ -1,16 +1,6 @@
<?php <?php
class OembedTest extends SapphireTest { class OembedTest extends SapphireTest {
public function setUp() {
parent::setUp();
Config::nest();
}
public function tearDown() {
Config::unnest();
parent::tearDown();
}
public function testGetOembedFromUrl() { public function testGetOembedFromUrl() {
Config::inst()->update('Oembed', 'providers', array( Config::inst()->update('Oembed', 'providers', array(
'http://*.silverstripe.com/watch*'=>'http://www.silverstripe.com/oembed/' 'http://*.silverstripe.com/watch*'=>'http://www.silverstripe.com/oembed/'

View File

@ -211,6 +211,37 @@ class ShortcodeParserTest extends SapphireTest {
); );
} }
public function testShortcodesInsideScriptTag() {
$this->assertEqualsIgnoringWhitespace(
'<script>hello</script>',
$this->parser->parse('<script>[test_shortcode]hello[/test_shortcode]</script>')
);
}
public function testNumericShortcodes() {
$this->assertEqualsIgnoringWhitespace(
'[2]',
$this->parser->parse('[2]')
);
$this->assertEqualsIgnoringWhitespace(
'<script>[2]</script>',
$this->parser->parse('<script>[2]</script>')
);
$this->parser->register('2', function($attributes, $content, $this, $tag, $extra) {
return 'this is 2';
});
$this->assertEqualsIgnoringWhitespace(
'this is 2',
$this->parser->parse('[2]')
);
$this->assertEqualsIgnoringWhitespace(
'<script>this is 2</script>',
$this->parser->parse('<script>[2]</script>')
);
}
public function testExtraContext() { public function testExtraContext() {
$this->parser->parse('<a href="[test_shortcode]">Test</a>'); $this->parser->parse('<a href="[test_shortcode]">Test</a>');

View File

@ -0,0 +1,111 @@
<?php
class FulltextFilterTest extends SapphireTest {
protected $extraDataObjects = array(
'FulltextFilterTest_DataObject'
);
protected static $fixture_file = "FulltextFilterTest.yml";
public function testFilter() {
if(DB::get_conn() instanceof MySQLDatabase) {
$baseQuery = FulltextFilterTest_DataObject::get();
$this->assertEquals(3, $baseQuery->count(), "FulltextFilterTest_DataObject count does not match.");
// First we'll text the 'SearchFields' which has been set using an array
$search = $baseQuery->filter("SearchFields:fulltext", 'SilverStripe');
$this->assertEquals(1, $search->count());
$search = $baseQuery->exclude("SearchFields:fulltext", "SilverStripe");
$this->assertEquals(2, $search->count());
// Now we'll run the same tests on 'OtherSearchFields' which should yield the same resutls
// but has been set using a string.
$search = $baseQuery->filter("OtherSearchFields:fulltext", 'SilverStripe');
$this->assertEquals(1, $search->count());
$search = $baseQuery->exclude("OtherSearchFields:fulltext", "SilverStripe");
$this->assertEquals(2, $search->count());
// Search on a single field
$search = $baseQuery->filter("ColumnE:fulltext", 'Dragons');
$this->assertEquals(1, $search->count());
$search = $baseQuery->exclude("ColumnE:fulltext", "Dragons");
$this->assertEquals(2, $search->count());
} else {
$this->markTestSkipped("FulltextFilter only supports MySQL syntax.");
}
}
public function testGenerateQuery() {
// Test SearchFields
$filter1 = new FulltextFilter('SearchFields', 'SilverStripe');
$filter1->setModel('FulltextFilterTest_DataObject');
$query1 = FulltextFilterTest_DataObject::get()->dataQuery();
$filter1->apply($query1);
$this->assertEquals('"ColumnA", "ColumnB"', $filter1->getDbName());
$this->assertEquals(
array(array(
"MATCH (\"ColumnA\", \"ColumnB\") AGAINST (?)" => array('SilverStripe')
)),
$query1->query()->getWhere()
);
// Test Other searchfields
$filter2 = new FulltextFilter('OtherSearchFields', 'SilverStripe');
$filter2->setModel('FulltextFilterTest_DataObject');
$query2 = FulltextFilterTest_DataObject::get()->dataQuery();
$filter2->apply($query2);
$this->assertEquals('"ColumnC", "ColumnD"', $filter2->getDbName());
$this->assertEquals(
array(array(
"MATCH (\"ColumnC\", \"ColumnD\") AGAINST (?)" => array('SilverStripe')
)),
$query2->query()->getWhere()
);
// Test fallback to single field
$filter3 = new FulltextFilter('ColumnA', 'SilverStripe');
$filter3->setModel('FulltextFilterTest_DataObject');
$query3 = FulltextFilterTest_DataObject::get()->dataQuery();
$filter3->apply($query3);
$this->assertEquals('"FulltextFilterTest_DataObject"."ColumnA"', $filter3->getDbName());
$this->assertEquals(
array(array(
"MATCH (\"FulltextFilterTest_DataObject\".\"ColumnA\") AGAINST (?)" => array('SilverStripe')
)),
$query3->query()->getWhere()
);
}
}
class FulltextFilterTest_DataObject extends DataObject implements TestOnly {
private static $db = array(
"ColumnA" => "Varchar(255)",
"ColumnB" => "HTMLText",
"ColumnC" => "Varchar(255)",
"ColumnD" => "HTMLText",
"ColumnE" => 'Varchar(255)'
);
private static $indexes = array(
'SearchFields' => array(
'type' => 'fulltext',
'name' => 'SearchFields',
'value' => '"ColumnA", "ColumnB"',
),
'OtherSearchFields' => 'fulltext ("ColumnC", "ColumnD")',
'SingleIndex' => 'fulltext ("ColumnE")'
);
private static $create_table_options = array(
"MySQLDatabase" => "ENGINE=MyISAM",
);
}

View File

@ -0,0 +1,19 @@
FulltextFilterTest_DataObject:
object1:
ColumnA: 'SilverStripe'
CluumnB: '<p>Some content about SilverStripe.</p>'
ColumnC: 'SilverStripe'
ColumnD: '<p>Some content about SilverStripe.</p>'
ColumnE: 'Dragons be here'
object2:
ColumnA: 'Test Row'
ColumnB: '<p>Some information about this test row.</p>'
ColumnC: 'Test Row'
ColumnD: '<p>Some information about this test row.</p>'
ColumnE: 'No'
object3:
ColumnA: 'Fulltext Search'
ColumnB: '<p>Testing fulltext search.</p>'
ColumnC: 'Fulltext Search'
ColumnD: '<p>Testing fulltext search.</p>'
ColumnE: ''

View File

@ -14,17 +14,11 @@ class BasicAuthTest extends FunctionalTest {
parent::setUp(); parent::setUp();
// Fixtures assume Email is the field used to identify the log in identity // Fixtures assume Email is the field used to identify the log in identity
Config::nest();
Member::config()->unique_identifier_field = 'Email'; Member::config()->unique_identifier_field = 'Email';
Security::$force_database_is_ready = true; // Prevents Member test subclasses breaking ready test Security::$force_database_is_ready = true; // Prevents Member test subclasses breaking ready test
Member::config()->lock_out_after_incorrect_logins = 10; Member::config()->lock_out_after_incorrect_logins = 10;
} }
public function tearDown() {
Config::unnest();
parent::tearDown();
}
public function testBasicAuthEnabledWithoutLogin() { public function testBasicAuthEnabledWithoutLogin() {
$origUser = isset($_SERVER['PHP_AUTH_USER']) ? $_SERVER['PHP_AUTH_USER'] : null; $origUser = isset($_SERVER['PHP_AUTH_USER']) ? $_SERVER['PHP_AUTH_USER'] : null;
$origPw = isset($_SERVER['PHP_AUTH_PW']) ? $_SERVER['PHP_AUTH_PW'] : null; $origPw = isset($_SERVER['PHP_AUTH_PW']) ? $_SERVER['PHP_AUTH_PW'] : null;

View File

@ -172,6 +172,13 @@ class MemberTest extends FunctionalTest {
$this->assertInstanceOf('DataObject', $passwords->current()); $this->assertInstanceOf('DataObject', $passwords->current());
$this->assertTrue($passwords->current()->checkPassword('1nitialPassword'), $this->assertTrue($passwords->current()->checkPassword('1nitialPassword'),
"Password 1nitialPassword not found in MemberRecord"); "Password 1nitialPassword not found in MemberRecord");
//check we don't retain orphaned records when a member is deleted
$member->delete();
$passwords = MemberPassword::get()->filter('MemberID', $member->OldID);
$this->assertCount(0, $passwords);
} }
/** /**

View File

@ -1 +1 @@
$Title <% if ArgA %>- $ArgA <% end_if %>- <%if First %>First-<% end_if %><% if Last %>Last-<% end_if %><%if MultipleOf(2) %>EVEN<% else %>ODD<% end_if %> top:$Top.Title <% if $Title %>$Title<% else %>Untitled<% end_if %> <% if $ArgA %>_ $ArgA <% end_if %>- <% if $First %>First-<% end_if %><% if $Last %>Last-<% end_if %><%if $MultipleOf(2) %>EVEN<% else %>ODD<% end_if %> top:$Top.Title

View File

@ -52,18 +52,46 @@ class SSViewerTest extends SapphireTest {
// reset results for the tests that include arguments (the title is passed as an arg) // reset results for the tests that include arguments (the title is passed as an arg)
$expected = array( $expected = array(
'Item 1 - Item 1 - First-ODD top:Item 1', 'Item 1 _ Item 1 - First-ODD top:Item 1',
'Item 2 - Item 2 - EVEN top:Item 2', 'Item 2 _ Item 2 - EVEN top:Item 2',
'Item 3 - Item 3 - ODD top:Item 3', 'Item 3 _ Item 3 - ODD top:Item 3',
'Item 4 - Item 4 - EVEN top:Item 4', 'Item 4 _ Item 4 - EVEN top:Item 4',
'Item 5 - Item 5 - ODD top:Item 5', 'Item 5 _ Item 5 - ODD top:Item 5',
'Item 6 - Item 6 - Last-EVEN top:Item 6', 'Item 6 _ Item 6 - Last-EVEN top:Item 6',
); );
$result = $data->renderWith('SSViewerTestIncludeScopeInheritanceWithArgs'); $result = $data->renderWith('SSViewerTestIncludeScopeInheritanceWithArgs');
$this->assertExpectedStrings($result, $expected); $this->assertExpectedStrings($result, $expected);
} }
public function testIncludeTruthyness() {
$data = new ArrayData(array(
'Title' => 'TruthyTest',
'Items' => new ArrayList(array(
new ArrayData(array('Title' => 'Item 1')),
new ArrayData(array('Title' => '')),
new ArrayData(array('Title' => true)),
new ArrayData(array('Title' => false)),
new ArrayData(array('Title' => null)),
new ArrayData(array('Title' => 0)),
new ArrayData(array('Title' => 7))
))
));
$result = $data->renderWith('SSViewerTestIncludeScopeInheritanceWithArgs');
// We should not end up with empty values appearing as empty
$expected = array(
'Item 1 _ Item 1 - First-ODD top:Item 1',
'Untitled - EVEN top:',
'1 _ 1 - ODD top:1',
'Untitled - EVEN top:',
'Untitled - ODD top:',
'Untitled - EVEN top:0',
'7 _ 7 - Last-ODD top:7'
);
$this->assertExpectedStrings($result, $expected);
}
private function getScopeInheritanceTestData() { private function getScopeInheritanceTestData() {
return new ArrayData(array( return new ArrayData(array(
'Title' => 'TopTitleValue', 'Title' => 'TopTitleValue',

View File

@ -441,6 +441,15 @@ class SSViewer_DataPresenter extends SSViewer_Scope {
} }
} }
/**
* Get the injected value
*
* @param string $property Name of property
* @param array $params
* @param bool $cast If true, an object is always returned even if not an object.
* @return array Result array with the keys 'value' for raw value, or 'obj' if contained in an object
* @throws InvalidArgumentException
*/
public function getInjectedValue($property, $params, $cast = true) { public function getInjectedValue($property, $params, $cast = true) {
$on = $this->itemIterator ? $this->itemIterator->current() : $this->item; $on = $this->itemIterator ? $this->itemIterator->current() : $this->item;
@ -524,32 +533,25 @@ class SSViewer_DataPresenter extends SSViewer_Scope {
if (isset($arguments[1]) && $arguments[1] != null) $params = $arguments[1]; if (isset($arguments[1]) && $arguments[1] != null) $params = $arguments[1];
else $params = array(); else $params = array();
$hasInjected = $res = null; $val = $this->getInjectedValue($property, $params);
if ($val) {
if ($name == 'hasValue') { $obj = $val['obj'];
if ($val = $this->getInjectedValue($property, $params, false)) { if ($name === 'hasValue') {
$hasInjected = true; $res = (bool)$val['value']; $res = $obj instanceof Object
} ? $obj->exists()
} : (bool)$obj;
else { // XML_val } else {
if ($val = $this->getInjectedValue($property, $params)) { // XML_val
$hasInjected = true;
$obj = $val['obj'];
$res = $obj->forTemplate(); $res = $obj->forTemplate();
} }
}
if ($hasInjected) {
$this->resetLocalScope(); $this->resetLocalScope();
return $res; return $res;
} } else {
else {
return parent::__call($name, $arguments); return parent::__call($name, $arguments);
} }
} }
} }
/** /**
* Parses a template file with an *.ss file extension. * Parses a template file with an *.ss file extension.
* *
@ -597,20 +599,20 @@ class SSViewer implements Flushable {
* Set whether HTML comments indicating the source .SS file used to render this page should be * Set whether HTML comments indicating the source .SS file used to render this page should be
* included in the output. This is enabled by default * included in the output. This is enabled by default
* *
* @deprecated 3.2 Use the "SSViewer.source_file_comments" config setting instead * @deprecated 4.0 Use the "SSViewer.source_file_comments" config setting instead
* @param boolean $val * @param boolean $val
*/ */
public static function set_source_file_comments($val) { public static function set_source_file_comments($val) {
Deprecation::notice('3.2', 'Use the "SSViewer.source_file_comments" config setting instead'); Deprecation::notice('4.0', 'Use the "SSViewer.source_file_comments" config setting instead');
Config::inst()->update('SSViewer', 'source_file_comments', $val); Config::inst()->update('SSViewer', 'source_file_comments', $val);
} }
/** /**
* @deprecated 3.2 Use the "SSViewer.source_file_comments" config setting instead * @deprecated 4.0 Use the "SSViewer.source_file_comments" config setting instead
* @return boolean * @return boolean
*/ */
public static function get_source_file_comments() { public static function get_source_file_comments() {
Deprecation::notice('3.2', 'Use the "SSViewer.source_file_comments" config setting instead'); Deprecation::notice('4.0', 'Use the "SSViewer.source_file_comments" config setting instead');
return Config::inst()->get('SSViewer', 'source_file_comments'); return Config::inst()->get('SSViewer', 'source_file_comments');
} }
@ -684,20 +686,20 @@ class SSViewer implements Flushable {
} }
/** /**
* @deprecated 3.2 Use the "SSViewer.theme" config setting instead * @deprecated 4.0 Use the "SSViewer.theme" config setting instead
* @param string $theme The "base theme" name (without underscores). * @param string $theme The "base theme" name (without underscores).
*/ */
public static function set_theme($theme) { public static function set_theme($theme) {
Deprecation::notice('3.2', 'Use the "SSViewer.theme" config setting instead'); Deprecation::notice('4.0', 'Use the "SSViewer.theme" config setting instead');
Config::inst()->update('SSViewer', 'theme', $theme); Config::inst()->update('SSViewer', 'theme', $theme);
} }
/** /**
* @deprecated 3.2 Use the "SSViewer.theme" config setting instead * @deprecated 4.0 Use the "SSViewer.theme" config setting instead
* @return string * @return string
*/ */
public static function current_theme() { public static function current_theme() {
Deprecation::notice('3.2', 'Use the "SSViewer.theme" config setting instead'); Deprecation::notice('4.0', 'Use the "SSViewer.theme" config setting instead');
return Config::inst()->get('SSViewer', 'theme'); return Config::inst()->get('SSViewer', 'theme');
} }
@ -736,10 +738,11 @@ class SSViewer implements Flushable {
} }
/** /**
* @deprecated since version 4.0
* @return string * @return string
*/ */
public static function current_custom_theme(){ public static function current_custom_theme(){
Deprecation::notice('3.2', 'Use the "SSViewer.theme" and "SSViewer.theme_enabled" config settings instead'); Deprecation::notice('4.0', 'Use the "SSViewer.theme" and "SSViewer.theme_enabled" config settings instead');
return Config::inst()->get('SSViewer', 'theme_enabled') ? Config::inst()->get('SSViewer', 'theme') : null; return Config::inst()->get('SSViewer', 'theme_enabled') ? Config::inst()->get('SSViewer', 'theme') : null;
} }
@ -785,7 +788,9 @@ class SSViewer implements Flushable {
* </code> * </code>
*/ */
public function __construct($templateList, TemplateParser $parser = null) { public function __construct($templateList, TemplateParser $parser = null) {
$this->setParser($parser ?: Injector::inst()->get('SSTemplateParser')); if ($parser) {
$this->setParser($parser);
}
if(!is_array($templateList) && substr((string) $templateList,-3) == '.ss') { if(!is_array($templateList) && substr((string) $templateList,-3) == '.ss') {
$this->chosenTemplates['main'] = $templateList; $this->chosenTemplates['main'] = $templateList;
@ -829,6 +834,9 @@ class SSViewer implements Flushable {
*/ */
public function getParser() public function getParser()
{ {
if (!$this->parser) {
$this->setParser(Injector::inst()->get('SSTemplateParser'));
}
return $this->parser; return $this->parser;
} }
@ -865,31 +873,31 @@ class SSViewer implements Flushable {
* links: "<?php echo $_SERVER['REQUEST_URI']; ?>". This is useful if you're generating a * links: "<?php echo $_SERVER['REQUEST_URI']; ?>". This is useful if you're generating a
* page that will be saved to a .php file and may be accessed from different URLs. * page that will be saved to a .php file and may be accessed from different URLs.
* *
* @deprecated 3.2 Use the "SSViewer.rewrite_hash_links" config setting instead * @deprecated 4.0 Use the "SSViewer.rewrite_hash_links" config setting instead
* @param string $optionName * @param string $optionName
* @param mixed $optionVal * @param mixed $optionVal
*/ */
public static function setOption($optionName, $optionVal) { public static function setOption($optionName, $optionVal) {
if($optionName == 'rewriteHashlinks') { if($optionName == 'rewriteHashlinks') {
Deprecation::notice('3.2', 'Use the "SSViewer.rewrite_hash_links" config setting instead'); Deprecation::notice('4.0', 'Use the "SSViewer.rewrite_hash_links" config setting instead');
Config::inst()->update('SSViewer', 'rewrite_hash_links', $optionVal); Config::inst()->update('SSViewer', 'rewrite_hash_links', $optionVal);
} else { } else {
Deprecation::notice('3.2', 'Use the "SSViewer.' . $optionName . '" config setting instead'); Deprecation::notice('4.0', 'Use the "SSViewer.' . $optionName . '" config setting instead');
Config::inst()->update('SSViewer', $optionName, $optionVal); Config::inst()->update('SSViewer', $optionName, $optionVal);
} }
} }
/** /**
* @deprecated 3.2 Use the "SSViewer.rewrite_hash_links" config setting instead * @deprecated 4.0 Use the "SSViewer.rewrite_hash_links" config setting instead
* @param string * @param string
* @return mixed * @return mixed
*/ */
public static function getOption($optionName) { public static function getOption($optionName) {
if($optionName == 'rewriteHashlinks') { if($optionName == 'rewriteHashlinks') {
Deprecation::notice('3.2', 'Use the "SSViewer.rewrite_hash_links" config setting instead'); Deprecation::notice('4.0', 'Use the "SSViewer.rewrite_hash_links" config setting instead');
return Config::inst()->get('SSViewer', 'rewrite_hash_links'); return Config::inst()->get('SSViewer', 'rewrite_hash_links');
} else { } else {
Deprecation::notice('3.2', 'Use the "SSViewer.' . $optionName . '" config setting instead'); Deprecation::notice('4.0', 'Use the "SSViewer.' . $optionName . '" config setting instead');
return Config::inst()->get('SSViewer', $optionName); return Config::inst()->get('SSViewer', $optionName);
} }
} }
@ -1105,9 +1113,11 @@ class SSViewer implements Flushable {
// through $Content and $Layout placeholders. // through $Content and $Layout placeholders.
foreach(array('Content', 'Layout') as $subtemplate) { foreach(array('Content', 'Layout') as $subtemplate) {
if(isset($this->chosenTemplates[$subtemplate])) { if(isset($this->chosenTemplates[$subtemplate])) {
$subtemplateViewer = new SSViewer($this->chosenTemplates[$subtemplate], $this->parser); $subtemplateViewer = clone $this;
// Disable requirements - this will be handled by the parent template
$subtemplateViewer->includeRequirements(false); $subtemplateViewer->includeRequirements(false);
$subtemplateViewer->setPartialCacheStore($this->getPartialCacheStore()); // The subtemplate is the only file we want to process, so set it as the "main" template file
$subtemplateViewer->chosenTemplates = array('main' => $this->chosenTemplates[$subtemplate]);
$underlay[$subtemplate] = $subtemplateViewer->process($item, $arguments); $underlay[$subtemplate] = $subtemplateViewer->process($item, $arguments);
} }
@ -1173,7 +1183,7 @@ class SSViewer implements Flushable {
} }
public function parseTemplateContent($content, $template="") { public function parseTemplateContent($content, $template="") {
return $this->parser->compileString( return $this->getParser()->compileString(
$content, $content,
$template, $template,
Director::isDev() && Config::inst()->get('SSViewer', 'source_file_comments') Director::isDev() && Config::inst()->get('SSViewer', 'source_file_comments')
@ -1242,7 +1252,10 @@ class SSViewer_FromString extends SSViewer {
protected $cacheTemplate; protected $cacheTemplate;
public function __construct($content, TemplateParser $parser = null) { public function __construct($content, TemplateParser $parser = null) {
$this->setParser($parser ?: Injector::inst()->get('SSTemplateParser')); if ($parser) {
$this->setParser($parser);
}
$this->content = $content; $this->content = $content;
} }

View File

@ -177,7 +177,8 @@ class ViewableData extends Object implements IteratorAggregate {
foreach($this->allMethodNames() as $method) { foreach($this->allMethodNames() as $method) {
if($method[0] == '_' && $method[1] != '_') { if($method[0] == '_' && $method[1] != '_') {
$this->createMethod( $this->createMethod(
substr($method, 1), "return \$obj->cachedCall('$method', \$args, '" . substr($method, 1) . "');" substr($method, 1),
"return \$obj->deprecatedCachedCall('$method', \$args, '" . substr($method, 1) . "');"
); );
} }
} }
@ -185,6 +186,23 @@ class ViewableData extends Object implements IteratorAggregate {
parent::defineMethods(); parent::defineMethods();
} }
/**
* Method to facilitate deprecation of underscore-prefixed methods automatically being cached.
*
* @param string $field
* @param array $arguments
* @param string $identifier an optional custom cache identifier
* @return unknown
*/
public function deprecatedCachedCall($method, $args = null, $identifier = null) {
Deprecation::notice(
'4.0',
'You are calling an underscore-prefixed method (e.g. _mymethod()) without the underscore. This behaviour,
and the caching logic behind it, has been deprecated.',
Deprecation::SCOPE_GLOBAL
);
return $this->cachedCall($method, $args, $identifier);
}
/** /**
* Merge some arbitrary data in with this object. This method returns a {@link ViewableData_Customised} instance * Merge some arbitrary data in with this object. This method returns a {@link ViewableData_Customised} instance
* with references to both this and the new custom data. * with references to both this and the new custom data.