mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 12:05:37 +00:00
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:
commit
09210efbc0
1
.gitattributes
vendored
Normal file
1
.gitattributes
vendored
Normal file
@ -0,0 +1 @@
|
||||
docs/ export-ignore
|
10
.travis.yml
10
.travis.yml
@ -1,5 +1,12 @@
|
||||
language: php
|
||||
|
||||
sudo: false
|
||||
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- tidy
|
||||
|
||||
php:
|
||||
- 5.4
|
||||
|
||||
@ -38,9 +45,6 @@ matrix:
|
||||
env: DB=MYSQL
|
||||
- php: hhvm
|
||||
env: DB=MYSQL
|
||||
before_install:
|
||||
- sudo apt-get update -qq
|
||||
- sudo apt-get install -y tidy
|
||||
|
||||
before_script:
|
||||
- composer self-update || true
|
||||
|
@ -13,3 +13,8 @@ Name: defaulti18n
|
||||
i18n:
|
||||
module_priority:
|
||||
- other_modules
|
||||
---
|
||||
Name: textcollector
|
||||
---
|
||||
Injector:
|
||||
i18nTextCollector_Writer: 'i18nTextCollector_Writer_RailsYaml'
|
||||
|
@ -131,7 +131,11 @@ class CMSBatchActionHandler extends RequestHandler {
|
||||
$ids = $this->cleanIDs($csvIDs);
|
||||
|
||||
// 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->addHeader("Content-type", "application/json");
|
||||
|
@ -1,22 +1,22 @@
|
||||
<?php
|
||||
/**
|
||||
* The object manages the main CMS menu. See {@link LeftAndMain::init()} for
|
||||
* The object manages the main CMS menu. See {@link LeftAndMain::init()} for
|
||||
* example usage.
|
||||
*
|
||||
* The menu will be automatically populated with menu items for subclasses of
|
||||
* {@link LeftAndMain}. That is, for each class in the CMS that creates an
|
||||
* administration panel, a CMS menu item will be created. The default
|
||||
* configuration will also include a 'help' link to the SilverStripe user
|
||||
*
|
||||
* The menu will be automatically populated with menu items for subclasses of
|
||||
* {@link LeftAndMain}. That is, for each class in the CMS that creates an
|
||||
* administration panel, a CMS menu item will be created. The default
|
||||
* configuration will also include a 'help' link to the SilverStripe user
|
||||
* documentation.
|
||||
*
|
||||
* Additional CMSMenu items can be added through {@link LeftAndMainExtension::init()}
|
||||
* extensions added to {@link LeftAndMain}.
|
||||
*
|
||||
*
|
||||
* @package framework
|
||||
* @subpackage admin
|
||||
*/
|
||||
class CMSMenu extends Object implements IteratorAggregate, i18nEntityProvider {
|
||||
|
||||
|
||||
/**
|
||||
* An array of changes to be made to the menu items, in the order that the changes should be
|
||||
* applied. Each item is a map in one of the two forms:
|
||||
@ -24,7 +24,7 @@ class CMSMenu extends Object implements IteratorAggregate, i18nEntityProvider {
|
||||
* - array('type' => 'remove', 'code' => 'codename' )
|
||||
*/
|
||||
protected static $menu_item_changes = array();
|
||||
|
||||
|
||||
/**
|
||||
* Set to true if clear_menu() is called, to indicate that the default menu shouldn't be
|
||||
* included
|
||||
@ -32,7 +32,7 @@ class CMSMenu extends Object implements IteratorAggregate, i18nEntityProvider {
|
||||
protected static $menu_is_cleared = false;
|
||||
|
||||
/**
|
||||
* Generate CMS main menu items by collecting valid
|
||||
* Generate CMS main menu items by collecting valid
|
||||
* subclasses of {@link LeftAndMain}
|
||||
*/
|
||||
public static function populate_menu() {
|
||||
@ -46,13 +46,13 @@ class CMSMenu extends Object implements IteratorAggregate, i18nEntityProvider {
|
||||
* @return The result of the operation
|
||||
* @todo A director rule is added when a controller link is added, but it won't be removed
|
||||
* when the item is removed. Functionality needed in {@link Director}.
|
||||
*/
|
||||
*/
|
||||
public static function add_controller($controllerClass) {
|
||||
if($menuItem = self::menuitem_for_controller($controllerClass)) {
|
||||
self::add_menu_item_obj($controllerClass, $menuItem);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return a CMSMenuItem to add the given controller to the CMSMenu
|
||||
*/
|
||||
@ -60,7 +60,7 @@ class CMSMenu extends Object implements IteratorAggregate, i18nEntityProvider {
|
||||
$urlBase = Config::inst()->get($controllerClass, 'url_base', Config::FIRST_SET);
|
||||
$urlSegment = Config::inst()->get($controllerClass, 'url_segment', Config::FIRST_SET);
|
||||
$menuPriority = Config::inst()->get($controllerClass, 'menu_priority', Config::FIRST_SET);
|
||||
|
||||
|
||||
// Don't add menu items defined the old way
|
||||
if($urlSegment === null && $controllerClass != "CMSMain") return;
|
||||
|
||||
@ -75,7 +75,7 @@ class CMSMenu extends Object implements IteratorAggregate, i18nEntityProvider {
|
||||
return new CMSMenuItem($menuTitle, $link, $controllerClass, $menuPriority);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Add an arbitrary URL to the CMS menu.
|
||||
*
|
||||
@ -91,7 +91,7 @@ class CMSMenu extends Object implements IteratorAggregate, i18nEntityProvider {
|
||||
public static function add_link($code, $menuTitle, $url, $priority = -1, $attributes = null) {
|
||||
return self::add_menu_item($code, $menuTitle, $url, null, $priority, $attributes);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add a navigation item to the main administration menu showing in the top bar.
|
||||
*
|
||||
@ -99,10 +99,10 @@ class CMSMenu extends Object implements IteratorAggregate, i18nEntityProvider {
|
||||
*
|
||||
* @param string $code Unique identifier for this menu item (e.g. used by {@link replace_menu_item()} and
|
||||
* {@link remove_menu_item}. Also used as a CSS-class for icon customization.
|
||||
* @param string $menuTitle Localized title showing in the menu bar
|
||||
* @param string $menuTitle Localized title showing in the menu bar
|
||||
* @param string $url A relative URL that will be linked in the menu bar.
|
||||
* @param string $controllerClass The controller class for this menu, used to check permisssions.
|
||||
* If blank, it's assumed that this is public, and always shown to users who
|
||||
* @param string $controllerClass The controller class for this menu, used to check permisssions.
|
||||
* If blank, it's assumed that this is public, and always shown to users who
|
||||
* have the rights to access some other part of the admin area.
|
||||
* @param array $attributes an array of attributes to include on the link.
|
||||
*
|
||||
@ -114,10 +114,10 @@ class CMSMenu extends Object implements IteratorAggregate, i18nEntityProvider {
|
||||
if($controllerClass) {
|
||||
$code = $controllerClass;
|
||||
}
|
||||
|
||||
|
||||
return self::replace_menu_item($code, $menuTitle, $url, $controllerClass, $priority, $attributes);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get a single menu item by its code value.
|
||||
*
|
||||
@ -126,9 +126,9 @@ class CMSMenu extends Object implements IteratorAggregate, i18nEntityProvider {
|
||||
*/
|
||||
public static function get_menu_item($code) {
|
||||
$menuItems = self::get_menu_items();
|
||||
return (isset($menuItems[$code])) ? $menuItems[$code] : false;
|
||||
return (isset($menuItems[$code])) ? $menuItems[$code] : false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get all menu entries.
|
||||
*
|
||||
@ -142,26 +142,26 @@ class CMSMenu extends Object implements IteratorAggregate, i18nEntityProvider {
|
||||
$cmsClasses = self::get_cms_classes();
|
||||
foreach($cmsClasses as $cmsClass) {
|
||||
$menuItem = self::menuitem_for_controller($cmsClass);
|
||||
if($menuItem) $menuItems[$cmsClass] = $menuItem;
|
||||
if($menuItem) $menuItems[Convert::raw2htmlname(str_replace('\\', '-', $cmsClass))] = $menuItem;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Apply changes
|
||||
foreach(self::$menu_item_changes as $change) {
|
||||
switch($change['type']) {
|
||||
case 'add':
|
||||
$menuItems[$change['code']] = $change['item'];
|
||||
break;
|
||||
|
||||
|
||||
case 'remove':
|
||||
unset($menuItems[$change['code']]);
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
user_error("Bad menu item change type {$change[type]}", E_USER_WARNING);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Sort menu items according to priority, then title asc
|
||||
$menuPriority = array();
|
||||
$menuTitle = array();
|
||||
@ -170,14 +170,14 @@ class CMSMenu extends Object implements IteratorAggregate, i18nEntityProvider {
|
||||
$menuTitle[$key] = $menuItem->title;
|
||||
}
|
||||
array_multisort($menuPriority, SORT_DESC, $menuTitle, SORT_ASC, $menuItems);
|
||||
|
||||
|
||||
return $menuItems;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get all menu items that the passed member can view.
|
||||
* Defaults to {@link Member::currentUser()}.
|
||||
*
|
||||
*
|
||||
* @param Member $member
|
||||
* @return array
|
||||
*/
|
||||
@ -185,7 +185,7 @@ class CMSMenu extends Object implements IteratorAggregate, i18nEntityProvider {
|
||||
if(!$member && $member !== FALSE) {
|
||||
$member = Member::currentUser();
|
||||
}
|
||||
|
||||
|
||||
$viewableMenuItems = array();
|
||||
$allMenuItems = self::get_menu_items();
|
||||
if($allMenuItems) foreach($allMenuItems as $code => $menuItem) {
|
||||
@ -200,13 +200,13 @@ class CMSMenu extends Object implements IteratorAggregate, i18nEntityProvider {
|
||||
if(!$controllerObj->canView($member)) continue;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$viewableMenuItems[$code] = $menuItem;
|
||||
}
|
||||
|
||||
|
||||
return $viewableMenuItems;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Removes an existing item from the menu.
|
||||
*
|
||||
@ -215,7 +215,7 @@ class CMSMenu extends Object implements IteratorAggregate, i18nEntityProvider {
|
||||
public static function remove_menu_item($code) {
|
||||
self::$menu_item_changes[] = array('type' => 'remove', 'code' => $code);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Clears the entire menu
|
||||
*/
|
||||
@ -229,11 +229,11 @@ class CMSMenu extends Object implements IteratorAggregate, i18nEntityProvider {
|
||||
*
|
||||
* @param string $code Unique identifier for this menu item (e.g. used by {@link replace_menu_item()} and
|
||||
* {@link remove_menu_item}. Also used as a CSS-class for icon customization.
|
||||
* @param string $menuTitle Localized title showing in the menu bar
|
||||
* @param string $menuTitle Localized title showing in the menu bar
|
||||
* @param string $url A relative URL that will be linked in the menu bar.
|
||||
* Make sure to add a matching route via {@link Director::$rules} to this url.
|
||||
* @param string $controllerClass The controller class for this menu, used to check permisssions.
|
||||
* If blank, it's assumed that this is public, and always shown to users who
|
||||
* @param string $controllerClass The controller class for this menu, used to check permisssions.
|
||||
* If blank, it's assumed that this is public, and always shown to users who
|
||||
* have the rights to access some other part of the admin area.
|
||||
* @param array $attributes an array of attributes to include on the link.
|
||||
*
|
||||
@ -253,7 +253,7 @@ class CMSMenu extends Object implements IteratorAggregate, i18nEntityProvider {
|
||||
'item' => $item,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add a previously built menu item object to the menu
|
||||
*/
|
||||
@ -292,10 +292,10 @@ class CMSMenu extends Object implements IteratorAggregate, i18nEntityProvider {
|
||||
if(!$classReflection->isInstantiable()) unset($subClasses[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return $subClasses;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* IteratorAggregate Interface Method. Iterates over the menu items.
|
||||
*/
|
||||
|
@ -76,7 +76,7 @@ class LeftAndMain extends Controller implements PermissionProvider {
|
||||
* @config
|
||||
* @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
|
||||
@ -555,7 +555,7 @@ class LeftAndMain extends Controller implements PermissionProvider {
|
||||
public static function menu_icon_for_class($class) {
|
||||
$icon = Config::inst()->get($class, 'menu_icon', Config::FIRST_SET);
|
||||
if (!empty($icon)) {
|
||||
$class = strtolower($class);
|
||||
$class = strtolower(Convert::raw2htmlname(str_replace('\\', '-', $class)));
|
||||
return ".icon.icon-16.icon-{$class} { background: url('{$icon}'); } ";
|
||||
}
|
||||
return '';
|
||||
@ -1397,8 +1397,10 @@ class LeftAndMain extends Controller implements PermissionProvider {
|
||||
*/
|
||||
public function BatchActionsForm() {
|
||||
$actions = $this->batchactions()->batchActionList();
|
||||
$actionsMap = array('-1' => _t('LeftAndMain.DropdownBatchActionsDefault', 'Actions'));
|
||||
foreach($actions as $action) $actionsMap[$action->Link] = $action->Title;
|
||||
$actionsMap = array();
|
||||
foreach($actions as $action) {
|
||||
$actionsMap[$action->Link] = $action->Title;
|
||||
}
|
||||
|
||||
$form = new Form(
|
||||
$this,
|
||||
@ -1409,7 +1411,9 @@ class LeftAndMain extends Controller implements PermissionProvider {
|
||||
'Action',
|
||||
false,
|
||||
$actionsMap
|
||||
)->setAttribute('autocomplete', 'off')
|
||||
)
|
||||
->setAttribute('autocomplete', 'off')
|
||||
->setAttribute('data-placeholder', _t('LeftAndMain.DropdownBatchActionsDefault', 'Actions'))
|
||||
),
|
||||
new FieldList(
|
||||
// TODO i18n
|
||||
@ -1618,15 +1622,17 @@ class LeftAndMain extends Controller implements PermissionProvider {
|
||||
* @config
|
||||
* @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
|
||||
*
|
||||
* @deprecated since version 4.0
|
||||
*
|
||||
* @param String $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);
|
||||
}
|
||||
|
||||
@ -1648,9 +1654,10 @@ class LeftAndMain extends Controller implements PermissionProvider {
|
||||
|
||||
/**
|
||||
* @param String $name
|
||||
* @deprecated since version 4.0
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
@ -1747,21 +1754,24 @@ class LeftAndMain extends Controller implements PermissionProvider {
|
||||
/**
|
||||
* Register the given javascript file as required in the CMS.
|
||||
* Filenames should be relative to the base, eg, FRAMEWORK_DIR . '/javascript/loader.js'
|
||||
*
|
||||
* @deprecated since version 4.0
|
||||
*/
|
||||
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()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 $media String Comma-separated list of media-types (e.g. "screen,projector")
|
||||
* @see http://www.w3.org/TR/REC-CSS2/media.html
|
||||
*/
|
||||
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)));
|
||||
}
|
||||
|
||||
@ -1769,12 +1779,14 @@ class LeftAndMain extends Controller implements PermissionProvider {
|
||||
* Register the given "themeable stylesheet" as required.
|
||||
* 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.
|
||||
*
|
||||
* @deprecated since version 4.0
|
||||
*
|
||||
* @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")
|
||||
*/
|
||||
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)));
|
||||
}
|
||||
|
||||
@ -1950,8 +1962,9 @@ class LeftAndMain_TreeNode extends ViewableData {
|
||||
$obj = $this->obj;
|
||||
return "<li id=\"record-$obj->ID\" data-id=\"$obj->ID\" data-pagetype=\"$obj->ClassName\" class=\""
|
||||
. $this->getClasses() . "\">" . "<ins class=\"jstree-icon\"> </ins>"
|
||||
. "<a href=\"" . $this->getLink() . "\" title=\"" . _t('LeftAndMain.PAGETYPE','Page type: ')
|
||||
. "$obj->class\" ><ins class=\"jstree-icon\"> </ins><span class=\"text\">" . ($obj->TreeTitle)
|
||||
. "<a href=\"" . $this->getLink() . "\" title=\"("
|
||||
. trim(_t('LeftAndMain.PAGETYPE','Page type'), " :") // account for inconsistencies in translations
|
||||
. ": " . $obj->i18n_singular_name() . ") $obj->Title\" ><ins class=\"jstree-icon\"> </ins><span class=\"text\">" . ($obj->TreeTitle)
|
||||
. "</span></a>";
|
||||
}
|
||||
|
||||
|
@ -465,20 +465,20 @@ abstract class ModelAdmin extends LeftAndMain {
|
||||
* overwrite the static page_length of the admin panel,
|
||||
* 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){
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(){
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -334,41 +334,41 @@ class SecurityAdmin extends LeftAndMain implements PermissionProvider {
|
||||
* The permissions represented in the $codes will not appearing in the form
|
||||
* 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
|
||||
*/
|
||||
public static function add_hidden_permission($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);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated 3.2 Use "Permission.hidden_permissions" config setting instead
|
||||
* @deprecated 4.0 Use "Permission.hidden_permissions" config setting instead
|
||||
* @param $codes String|Array
|
||||
*/
|
||||
public static function remove_hidden_permission($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);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated 3.2 Use "Permission.hidden_permissions" config setting instead
|
||||
* @deprecated 4.0 Use "Permission.hidden_permissions" config setting instead
|
||||
* @return Array
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(){
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
@ -13,10 +13,16 @@ output_style = :compact
|
||||
# To enable relative paths to assets via compass helper functions. Uncomment:
|
||||
relative_assets = true
|
||||
|
||||
# disable comments in the output. We want admin comments
|
||||
# to be verbose
|
||||
# Disable comments in the output. We want admin comments to be verbose.
|
||||
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"
|
||||
|
@ -1 +1,3 @@
|
||||
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
7
admin/css/editor.css.map
Normal 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"
|
||||
}
|
@ -35,6 +35,7 @@
|
||||
|
||||
.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 input.file { margin-left: -132px; }
|
||||
.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; }
|
||||
|
||||
/** 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 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 .fieldgroup-field.last { margin-left: 16px; }
|
||||
|
||||
/*# sourceMappingURL=ie7.css.map */
|
||||
|
7
admin/css/ie7.css.map
Normal file
7
admin/css/ie7.css.map
Normal 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"
|
||||
}
|
@ -35,6 +35,7 @@
|
||||
|
||||
.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 input.file { margin-left: -132px; }
|
||||
.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; }
|
||||
|
||||
/** 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; }
|
||||
|
||||
.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.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; }
|
||||
|
||||
/*# sourceMappingURL=ie8.css.map */
|
||||
|
7
admin/css/ie8.css.map
Normal file
7
admin/css/ie8.css.map
Normal 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"
|
||||
}
|
@ -111,8 +111,31 @@ Used in side panels and action tabs
|
||||
.icon.icon-16.icon-help { background-position: 0 -96px; }
|
||||
|
||||
/** ----------------------------- 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. */
|
||||
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 { 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; }
|
||||
|
||||
/** 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 .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-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-content { border: 1px solid #c0c0c2; border-top: none; }
|
||||
.ui-accordion .ui-accordion-header { border-color: #d0d3d5; margin-bottom: 0; }
|
||||
.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-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 */
|
||||
/** ---------------------------------------------------- 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 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:after { content: "\0020"; display: block; height: 0; clear: both; overflow: hidden; visibility: hidden; }
|
||||
.field.nolabel .middleColumn { 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.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 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 .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 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 { 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: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.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; }
|
||||
@ -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 .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 .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: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-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: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; }
|
||||
@ -353,6 +376,32 @@ fieldset.switch-states.size_5 input:checked:nth-of-type(5) ~ .slide-button { lef
|
||||
|
||||
@-webkit-keyframes bugfix { from { 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,
|
||||
* 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-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 .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 * { 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 .section-icon { opacity: 0.2; margin-right: 4px; background-repeat: no-repeat; }
|
||||
.cms-content-header .ss-ui-button { line-height: 24px; }
|
||||
@ -398,51 +447,112 @@ body.cms { overflow: hidden; }
|
||||
|
||||
.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 -------------------------------------------- */
|
||||
.ui-tabs { padding: 0; background: none; }
|
||||
.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-panel-padded { padding: 16px 16px; }
|
||||
.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 ~ .ui-tabs-panel { border-top: 1px solid #c0c0c2; clear: both; }
|
||||
.ui-tabs .ui-tabs-nav li { top: 0; float: left; border-bottom: 0 !important; }
|
||||
.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 { float: right; margin: 16px 0 -1px 0; padding: 0 16px 0 0; border-bottom: 0; }
|
||||
.ui-tabs .ui-tabs-nav ~ .ui-tabs-panel { clear: both; }
|
||||
.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: 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.ui-tabs-active { margin-bottom: 0; }
|
||||
.ui-tabs .ui-tabs-nav .ui-state-default { border: 1px solid #c0c0c2; background: #ced7dc; }
|
||||
.ui-tabs .ui-tabs-nav .ui-state-default a { color: #5e5e5e; text-shadow: #e6e6e6 0 1px 0; }
|
||||
.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 a { color: #444; }
|
||||
.ui-tabs .ui-tabs-nav .ui-state-default { border: 0; background: transparent; }
|
||||
.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:hover { color: #66727d; }
|
||||
.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 { 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 li.cms-tabset-icon { 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.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.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.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.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.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 { text-indent: -9999em; }
|
||||
.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.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.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.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.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.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.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.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.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.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.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 .ui-tabs-panel { padding: 8px 0 0 0; }
|
||||
.ui-tabs .cms-panel-padded .Actions { padding: 0; }
|
||||
.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() */
|
||||
.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 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-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-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 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-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: 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: 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 ------------------------------------------------------- */
|
||||
.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%; }
|
||||
|
||||
/** -------------------------------------------- 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 -------------------------------------------- */
|
||||
.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; }
|
||||
|
||||
/** -------------------------------------------- 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 .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 * 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 .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 .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 .chzn-container-single .chzn-single span { padding-top: 1px; }
|
||||
.cms-content-toolbar .chzn-container-single .chzn-single div { border-left: none; width: 100%; }
|
||||
.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 .ss-ui-button { margin-bottom: 8px; }
|
||||
.cms-content-toolbar .ss-ui-button { margin-bottom: 12px; }
|
||||
.cms-content-toolbar .cms-actions-buttons-row { clear: both; }
|
||||
.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 .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 .cms-actions-tools-row { clear: both; }
|
||||
.cms-content-toolbar .tool-action { display: none; }
|
||||
|
||||
/* -------------------------------------------------------- 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; }
|
||||
/** 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 #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 .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-content { width: 272px; padding: 8.8px 8px 0; overflow: auto; height: 100%; }
|
||||
.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: 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-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; }
|
||||
@ -532,7 +641,7 @@ body.cms { overflow: hidden; }
|
||||
.cms-content-tools h4 { font-size: 12px; margin: 5px 0; }
|
||||
.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 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 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; }
|
||||
@ -551,29 +660,27 @@ body.cms { overflow: hidden; }
|
||||
/** ------------------------------------------------------------------
|
||||
* 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-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 .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 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 .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; }
|
||||
.cms-content-batchactions .view-mode-batchactions-wrapper .view-mode-batchactions-label { vertical-align: middle; display: none; }
|
||||
.cms-content-batchactions .checkbox { margin-top: 2px; vertical-align: middle; }
|
||||
|
||||
#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 -------------------------------------------- */
|
||||
.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; }
|
||||
|
||||
.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 .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; }
|
||||
@ -614,13 +721,13 @@ form.member-profile-form #Permissions .optionset li { float: none; width: auto;
|
||||
#ViewerGroups select, #EditorGroups select, #CreateTopLevelGroups select { width: 512px; }
|
||||
|
||||
/** -------------------------------------------- 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.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 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-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 .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; }
|
||||
@ -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 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
|
||||
@ -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 .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 .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 .clear { clear: both; }
|
||||
.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.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 .cms-content-header { padding: 0; width: 100%; height: 40px; }
|
||||
.htmleditorfield-dialog .cms-content-header h3 { padding: 3px 8px; margin: 10px; }
|
||||
.htmleditorfield-dialog .cms-content-header { padding: 0; width: 100%; height: 53px; }
|
||||
.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 .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; }
|
||||
@ -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; }
|
||||
|
||||
/** Import forms */
|
||||
body.SecurityAdmin { background-color: #e6eaed; }
|
||||
|
||||
form.import-form ul { list-style: disc; }
|
||||
form.import-form ul li { margin-left: 20px; }
|
||||
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: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.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-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; }
|
||||
@ -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 > 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.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 > .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; }
|
||||
@ -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; }
|
||||
|
||||
/* 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 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: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-ErrorPage > a .jstree-pageicon { background-position: 0 -112px; }
|
||||
|
||||
/* tree status icons and labels */
|
||||
.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; }
|
||||
/* Tree status labels and dots */
|
||||
.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 .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.multiple li > a > .jstree-icon { display: none; }
|
||||
.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 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; }
|
||||
.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 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 .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 { 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: 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 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 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 span.text { 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-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 .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; }
|
||||
@ -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 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-drop ul.chzn-results li.result-selected { 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: #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: #66727d; }
|
||||
.cms-content-controls .cms-preview-states { float: right; }
|
||||
.cms-content-controls .cms-preview-states select { 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.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 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 .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; }
|
||||
@ -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 .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.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.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 { 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: #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 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; }
|
||||
@ -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 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 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 { 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; }
|
||||
@ -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 .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.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.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 { 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: #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 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; }
|
||||
@ -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 .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-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 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 .last .ui-tabs-panel.ss-ui-action-tab { left: auto; right: -1px; }
|
||||
.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-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-filters { padding-top: 16px; }
|
||||
|
||||
.permissioncheckboxset h5, .permissioncheckboxsetfield_readonly h5 { margin: 0; }
|
||||
.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 */
|
||||
.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.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 */
|
||||
.cms-logo a { background-image: url("../images/logo_small@2x.png"); background-size: 22px 22px; }
|
||||
/* 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-commentadmin { background-position: 0 0; }
|
||||
.icon.icon-16.icon-help { background-position: 0 -96px; } }
|
||||
|
||||
/*# sourceMappingURL=screen.css.map */
|
||||
|
7
admin/css/screen.css.map
Normal file
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
12
admin/font/LICENSE.txt
Normal 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
BIN
admin/font/fontello.eot
Normal file
Binary file not shown.
21
admin/font/fontello.svg
Normal file
21
admin/font/fontello.svg
Normal 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="" 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="" 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="" 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="" 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="" 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="" 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="" 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="" 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="" 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="" 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
BIN
admin/font/fontello.ttf
Normal file
Binary file not shown.
BIN
admin/font/fontello.woff
Normal file
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 |
@ -40,7 +40,8 @@
|
||||
},
|
||||
|
||||
/**
|
||||
* Register default bulk confirmation dialogs
|
||||
* @func registerDefault
|
||||
* @desc Register default bulk confirmation dialogs
|
||||
*/
|
||||
registerDefault: function() {
|
||||
// Publish selected pages action
|
||||
@ -115,60 +116,15 @@
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Constructor: onmatch
|
||||
*/
|
||||
onadd: function() {
|
||||
this._updateStateFromViewMode();
|
||||
this.registerDefault();
|
||||
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.
|
||||
*/
|
||||
_updateStateFromViewMode: function() {
|
||||
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 - ...
|
||||
* @func register
|
||||
* @param {string} type
|
||||
* @param {function} callback
|
||||
*/
|
||||
register: function(type, callback) {
|
||||
this.trigger('register', {type: type, callback: callback});
|
||||
@ -176,46 +132,24 @@
|
||||
actions[type] = callback;
|
||||
this.setActions(actions);
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Function: unregister
|
||||
*
|
||||
* Remove an existing action.
|
||||
*
|
||||
* Parameters:
|
||||
*
|
||||
* {String} type
|
||||
* @func unregister
|
||||
* @param {string} type
|
||||
* @desc Remove an existing action.
|
||||
*/
|
||||
unregister: function(type) {
|
||||
this.trigger('unregister', {type: type});
|
||||
|
||||
|
||||
var actions = this.getActions();
|
||||
if(actions[type]) delete actions[type];
|
||||
this.setActions(actions);
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Function: _isActive
|
||||
*
|
||||
* Determines if we should allow and track tree selections.
|
||||
*
|
||||
* 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
|
||||
* @func refreshSelected
|
||||
* @param {object} rootNode
|
||||
* @desc Ajax callbacks determine which pages is selectable in a certain batch action.
|
||||
*/
|
||||
refreshSelected : function(rootNode) {
|
||||
var self = this,
|
||||
@ -232,7 +166,7 @@
|
||||
}
|
||||
|
||||
// If no action is selected, enable all nodes
|
||||
if(selectedAction == -1) {
|
||||
if(!selectedAction || selectedAction == -1) {
|
||||
$(rootNode).find('li').each(function() {
|
||||
$(this).setEnabled(true);
|
||||
});
|
||||
@ -267,10 +201,8 @@
|
||||
},
|
||||
|
||||
/**
|
||||
* Function: serializeFromTree
|
||||
*
|
||||
* Returns:
|
||||
* (boolean)
|
||||
* @func serializeFromTree
|
||||
* @return {boolean}
|
||||
*/
|
||||
serializeFromTree: function() {
|
||||
var tree = this.getTree(), ids = tree.getSelectedIDs();
|
||||
@ -282,20 +214,16 @@
|
||||
},
|
||||
|
||||
/**
|
||||
* Function: setIDS
|
||||
*
|
||||
* Parameters:
|
||||
* {Array} ids
|
||||
* @func setIDS
|
||||
* @param {array} ids
|
||||
*/
|
||||
setIDs: function(ids) {
|
||||
this.find(':input[name=csvIDs]').val(ids ? ids.join(',') : null);
|
||||
},
|
||||
|
||||
/**
|
||||
* Function: getIDS
|
||||
*
|
||||
* Returns:
|
||||
* {Array}
|
||||
* @func getIDS
|
||||
* @return {array}
|
||||
*/
|
||||
getIDs: function() {
|
||||
// Map empty value to empty array
|
||||
@ -304,13 +232,7 @@
|
||||
? value.split(',')
|
||||
: [];
|
||||
},
|
||||
|
||||
/**
|
||||
* Function: onsubmit
|
||||
*
|
||||
* Parameters:
|
||||
* (Event) e
|
||||
*/
|
||||
|
||||
onsubmit: function(e) {
|
||||
var self = this, ids = this.getIDs(), tree = this.getTree(), actions = this.getActions();
|
||||
|
||||
@ -350,8 +272,9 @@
|
||||
complete: function(xmlhttp, status) {
|
||||
button.removeClass('loading');
|
||||
|
||||
// Deselect all nodes
|
||||
tree.jstree('uncheck_all');
|
||||
// Refresh the tree.
|
||||
// Makes sure all nodes have the correct CSS classes applied.
|
||||
tree.jstree('refresh', -1);
|
||||
self.setIDs([]);
|
||||
|
||||
// Reset action
|
||||
@ -395,28 +318,45 @@
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
||||
$('.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]
|
||||
*/
|
||||
$('#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) {
|
||||
var form = $(e.target.form), btn = form.find(':submit');
|
||||
if($(e.target).val() == -1) {
|
||||
var form = $(e.target.form),
|
||||
btn = form.find(':submit'),
|
||||
selected = $(e.target).val();
|
||||
if(!selected || selected == -1) {
|
||||
btn.attr('disabled', 'disabled').button('refresh');
|
||||
} else {
|
||||
btn.removeAttr('disabled').button('refresh');
|
||||
|
@ -176,7 +176,7 @@
|
||||
updateMenuFromResponse: function(xhr) {
|
||||
var controller = xhr.getResponseHeader('X-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();
|
||||
}
|
||||
this.updateItems();
|
||||
|
@ -250,7 +250,7 @@
|
||||
*/
|
||||
createNode: function(html, data, callback) {
|
||||
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);
|
||||
|
||||
// Extract the state for the new node from the properties taken from the provided HTML template.
|
||||
@ -487,26 +487,5 @@
|
||||
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));
|
||||
|
@ -1290,7 +1290,7 @@ jQuery.noConflict();
|
||||
form.find(".dropdown select").prop('selectedIndex', 0).trigger("liszt:updated"); // Reset chosen.js
|
||||
form.submit();
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
/**
|
||||
* 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));
|
||||
|
@ -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.CONFIRMUNSAVEDSHORT": "UPOZORNĚNÍ: Vaše změny nebyly uloženy.",
|
||||
"SecurityAdmin.BATCHACTIONSDELETECONFIRM": "Skutečně chcete smazat %s skupiny?",
|
||||
|
@ -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.CONFIRMUNSAVEDSHORT": "WARNUNG: Ihre Änderungen wurden nicht gespeichert.",
|
||||
"SecurityAdmin.BATCHACTIONSDELETECONFIRM": "Möchten Sie wirklich %s Gruppen löschen?",
|
||||
|
@ -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.CONFIRMUNSAVEDSHORT": "AVERTO: Viaj ŝanĝoj ne estas konservitaj.",
|
||||
"SecurityAdmin.BATCHACTIONSDELETECONFIRM": "Ĉu vi vere volas forigi %s grupojn?",
|
||||
|
@ -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.CONFIRMUNSAVEDSHORT": "ADVERTENCIA: Tus cambios no han sido guardados.",
|
||||
"SecurityAdmin.BATCHACTIONSDELETECONFIRM": "¿Realmente quieres eliminar el grupo %s?",
|
||||
|
@ -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.CONFIRMUNSAVEDSHORT": "VAROITUS: Muutoksiasi ei ole tallennettu.",
|
||||
"SecurityAdmin.BATCHACTIONSDELETECONFIRM": "Haluatko varmasti poistaa %s ryhmät?",
|
||||
|
@ -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.CONFIRMUNSAVEDSHORT": "WARNING: Your changes have not been saved.",
|
||||
"SecurityAdmin.BATCHACTIONSDELETECONFIRM": "Do you really want to delete %s groups?",
|
||||
|
@ -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.CONFIRMUNSAVEDSHORT": "PERINGATAN: Perubahan tidak akan disimpan.",
|
||||
"SecurityAdmin.BATCHACTIONSDELETECONFIRM": "Anda ingin menghapus kelompok %s?",
|
||||
|
@ -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.CONFIRMUNSAVEDSHORT": "PERINGATAN: Perubahan tidak akan disimpan.",
|
||||
"SecurityAdmin.BATCHACTIONSDELETECONFIRM": "Anda ingin menghapus kelompok %s?",
|
||||
|
@ -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.CONFIRMUNSAVEDSHORT": "WARNING: Your changes have not been saved.",
|
||||
"SecurityAdmin.BATCHACTIONSDELETECONFIRM": "Do you really want to delete %s groups?",
|
||||
|
@ -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.CONFIRMUNSAVEDSHORT": "警告: あなたの変更は保存されていません.",
|
||||
"SecurityAdmin.BATCHACTIONSDELETECONFIRM": "%sグループを本当に削除しても良いですか?",
|
||||
|
@ -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.CONFIRMUNSAVEDSHORT": "DĖMESIO: Jūsų pakeitimai neišsaugoti.",
|
||||
"SecurityAdmin.BATCHACTIONSDELETECONFIRM": "Ar tikrai norite ištrinti %s grupes?",
|
||||
|
@ -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.CONFIRMUNSAVEDSHORT": "WHAKATŪPATO: Kāore anō ō huringa kia tiakina.",
|
||||
"SecurityAdmin.BATCHACTIONSDELETECONFIRM": "Kei te tino hiahia muku i te %s rōpū?",
|
||||
|
@ -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.CONFIRMUNSAVEDSHORT": "ADVARSEL: Endringene dine har ikke blitt lagret.",
|
||||
"SecurityAdmin.BATCHACTIONSDELETECONFIRM": "Vil du virkelig slette %s grupper?",
|
||||
|
@ -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.CONFIRMUNSAVEDSHORT": "WAARSCHUWING: Uw veranderingen zijn niet opgeslagen",
|
||||
"SecurityAdmin.BATCHACTIONSDELETECONFIRM": "Weet u zeker dat u deze groep %s wilt verwijderen?",
|
||||
|
@ -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.CONFIRMUNSAVEDSHORT": "UWAGA: Twoje zmiany nie zostały zapisane.",
|
||||
"SecurityAdmin.BATCHACTIONSDELETECONFIRM": "Czy na pewno chcesz usunąć %s grup?",
|
||||
|
@ -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.CONFIRMUNSAVEDSHORT": "AVERTISMENT: Modificările nu au fost salvate.",
|
||||
"SecurityAdmin.BATCHACTIONSDELETECONFIRM": "Sigur doriți să ștergeți grupurile %s?",
|
||||
|
@ -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.CONFIRMUNSAVEDSHORT": "ВНИМАНИЕ: Ваши изменения не были сохранены",
|
||||
"SecurityAdmin.BATCHACTIONSDELETECONFIRM": "Вы действительно хотите удалить %s групп?",
|
||||
|
@ -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.CONFIRMUNSAVEDSHORT": "UPOZORNENIE: Vaše zmeny neboli uložené.",
|
||||
"SecurityAdmin.BATCHACTIONSDELETECONFIRM": "Skutočne chcete zmazať % skupiny?",
|
||||
|
@ -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.CONFIRMUNSAVEDSHORT": "OPOZORILO: spremembe niso bile shranjene.",
|
||||
"SecurityAdmin.BATCHACTIONSDELETECONFIRM": "Izbrišem %s skupin?",
|
||||
|
@ -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.CONFIRMUNSAVEDSHORT": "УПОЗОРЕЊЕ: Ваше измене нису сачуване.",
|
||||
"SecurityAdmin.BATCHACTIONSDELETECONFIRM": "Да ли заиста желите да се избришете %s групе?",
|
||||
|
@ -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.CONFIRMUNSAVEDSHORT": "УПОЗОРЕЊЕ: Ваше измене нису сачуване.",
|
||||
"SecurityAdmin.BATCHACTIONSDELETECONFIRM": "Да ли заиста желите да се избришете %s групе?",
|
||||
|
@ -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.CONFIRMUNSAVEDSHORT": "WARNING: Your changes have not been saved.",
|
||||
"SecurityAdmin.BATCHACTIONSDELETECONFIRM": "Vill du verkligen radera %s grupper?",
|
||||
|
@ -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.CONFIRMUNSAVEDSHORT": "警告:您所做的更改尚未保存。",
|
||||
"SecurityAdmin.BATCHACTIONSDELETECONFIRM": "您真的要删除 %s 小组吗?",
|
||||
|
@ -1,9 +1,16 @@
|
||||
.ModelAdmin .cms-content-fields {
|
||||
overflow:hidden; //removes scrolling from filter panel
|
||||
.cms-edit-form {
|
||||
overflow-y:auto; //adds scrolling only to the datagrid
|
||||
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-search-form {
|
||||
.resetformaction {
|
||||
@ -13,5 +20,9 @@
|
||||
#Form_ImportForm {
|
||||
overflow:hidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.cms-content-filters {
|
||||
padding-top: 16px;
|
||||
}
|
||||
}
|
||||
|
@ -36,7 +36,7 @@ $border: 1px solid darken(#D9D9D9, 15%);
|
||||
-webkit-box-shadow: none;
|
||||
}
|
||||
li{
|
||||
@include background-image(linear-gradient(top, #f8f8f8, #D9D9D9));
|
||||
@include background-image(linear-gradient(top, #f8f8f8, #D9D9D9));
|
||||
@include border-radius(0);
|
||||
background: #eaeaea;
|
||||
border: none;
|
||||
@ -125,7 +125,7 @@ $border: 1px solid darken(#D9D9D9, 15%);
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
#Form_AddForm_PageType_Holder ul {
|
||||
#Form_AddForm_PageType ul {
|
||||
padding: 0;
|
||||
|
||||
li{
|
||||
@ -235,6 +235,9 @@ $border: 1px solid darken(#D9D9D9, 15%);
|
||||
font-weight: normal;
|
||||
line-height: 24px;
|
||||
padding:0 25px 0 10px;
|
||||
border-bottom: 0;
|
||||
margin: 0;
|
||||
|
||||
&:hover, &:active{
|
||||
@include box-shadow(none);
|
||||
outline:none;
|
||||
|
43
admin/scss/_fonts.scss
Normal file
43
admin/scss/_fonts.scss
Normal 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'; }
|
@ -911,3 +911,167 @@ fieldset.switch-states{
|
||||
}
|
||||
//old web-kit browser fix
|
||||
@-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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
#Form_ImportForm {
|
||||
div.file {
|
||||
|
@ -33,13 +33,13 @@
|
||||
.cms-logo {
|
||||
border-bottom: 1px solid lighten($color-dark-bg,4%);
|
||||
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;
|
||||
vertical-align: middle;
|
||||
font-size: $font-base-size;
|
||||
min-height: 20px;
|
||||
min-height: 28px;
|
||||
|
||||
.collapsed &{
|
||||
.collapsed & {
|
||||
padding:0;
|
||||
}
|
||||
.version {
|
||||
@ -60,30 +60,37 @@
|
||||
font-weight: bold;
|
||||
font-size: $font-base-size;
|
||||
line-height: 16px;
|
||||
padding: 2px 0;
|
||||
padding: 6px 0;
|
||||
margin-left: 30px;
|
||||
}
|
||||
}
|
||||
|
||||
.cms-login-status {
|
||||
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;
|
||||
font-size: $font-base-size - 1;
|
||||
min-height: 28px;
|
||||
|
||||
.logout-link {
|
||||
display: inline-block;
|
||||
height: 16px;
|
||||
height: 28px;
|
||||
width: 16px;
|
||||
float: left;
|
||||
margin: 0 8px 0 5px;
|
||||
background: sprite($sprites32, logout) no-repeat;
|
||||
background-position: 0 -766px;
|
||||
text-indent: -9999em;
|
||||
opacity:0.9;
|
||||
|
||||
&:hover, &:focus{
|
||||
opacity:1;
|
||||
}
|
||||
}
|
||||
|
||||
span {
|
||||
padding-top: 6px;
|
||||
}
|
||||
}
|
||||
|
||||
.cms-menu {
|
||||
@ -134,15 +141,15 @@
|
||||
}
|
||||
|
||||
.cms-login-status {
|
||||
height:16px;
|
||||
height: 28px;
|
||||
span {
|
||||
display:none;
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.cms-logo {
|
||||
height:20px;
|
||||
padding: 10px 0 9px;
|
||||
height: 28px;
|
||||
padding: 12px 0 11px;
|
||||
}
|
||||
|
||||
&.cms-panel .cms-panel-content {
|
||||
|
@ -71,7 +71,7 @@
|
||||
|
||||
&.loading {
|
||||
background-image: url(../images/spinner@2x.gif);
|
||||
background-size: 43px 22px;
|
||||
background-size: 43px 43px;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -79,6 +79,11 @@ body.cms {
|
||||
.cms-menu,
|
||||
.cms-content,
|
||||
.cms-content-header,
|
||||
/**
|
||||
* DEPRECATED:
|
||||
* .cms-content-tools will be removed in 4.0
|
||||
* Use .cms-content-filters instead.
|
||||
*/
|
||||
.cms-content-tools,
|
||||
.cms-content-fields,
|
||||
.cms-edit-form,
|
||||
@ -92,7 +97,7 @@ body.cms {
|
||||
.cms-content-header {
|
||||
padding-left: $grid-x * 2;
|
||||
z-index: 60;
|
||||
min-height: 40px;
|
||||
min-height: 52px;
|
||||
background: {
|
||||
image: url(../images/textures/cms_content_header.png);
|
||||
repeat: repeat;
|
||||
@ -123,7 +128,7 @@ body.cms {
|
||||
|
||||
.cms-content-header-info {
|
||||
float:left;
|
||||
padding-top: 6px;
|
||||
padding-top: $grid-y - 1;
|
||||
|
||||
& * {
|
||||
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
|
||||
* -------------------------------------------- */
|
||||
@ -170,16 +458,13 @@ body.cms {
|
||||
}
|
||||
|
||||
.ui-tabs-panel {
|
||||
padding: $grid-x*3;
|
||||
padding: $grid-y*1.5 $grid-x*2;
|
||||
background: transparent; // default it's white
|
||||
border: 0; // suppress default borders
|
||||
|
||||
&.cms-edit-form {
|
||||
padding: 0;
|
||||
}
|
||||
&.cms-panel-padded {
|
||||
padding: $grid-y*2 $grid-x*2;
|
||||
}
|
||||
}
|
||||
|
||||
.ui-widget-header {
|
||||
@ -187,57 +472,66 @@ body.cms {
|
||||
background: none;
|
||||
}
|
||||
|
||||
.ui-tabs-nav {
|
||||
float: right;
|
||||
margin: $grid-x*2 0 -1px 0;
|
||||
padding: 0 $grid-x*1.5 0 0;
|
||||
border-bottom: none;
|
||||
.ui-tabs-nav {
|
||||
float: right;
|
||||
margin: $grid-x*2 0 -1px 0;
|
||||
padding: 0 $grid-x*2 0 0;
|
||||
border-bottom: 0;
|
||||
|
||||
~ .ui-tabs-panel {
|
||||
border-top:1px solid $color-button-generic-border;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
li {
|
||||
top: 0;
|
||||
float: left;
|
||||
border-bottom: 0 !important;
|
||||
|
||||
a {
|
||||
@include inline-block;
|
||||
float: none;
|
||||
font-weight: bold;
|
||||
color: $color-text;
|
||||
line-height: $grid-y * 4;
|
||||
padding: 0 $grid-x*2 0;
|
||||
}
|
||||
~ .ui-tabs-panel {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
li {
|
||||
top: 0;
|
||||
float: left;
|
||||
margin-top: 0;
|
||||
|
||||
a {
|
||||
@include inline-block;
|
||||
float: none;
|
||||
font-weight: normal;
|
||||
color: $color-text;
|
||||
line-height: $grid-y * 4;
|
||||
padding: 0 $grid-x*1.5 0;
|
||||
|
||||
&:last-child {
|
||||
// correctly right-align last tab
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
&.ui-tabs-active {
|
||||
margin-bottom: 0;
|
||||
&.icon-button {
|
||||
@extend a.icon-button;
|
||||
}
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
// correctly right-align last tab
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.ui-state-default {
|
||||
border:1px solid $color-button-generic-border;
|
||||
background: darken($color-widget-bg, 10%);
|
||||
border: 0;
|
||||
background: transparent;
|
||||
|
||||
a {
|
||||
line-height: 28px;
|
||||
padding-top: 12px;
|
||||
padding-bottom: 8px;
|
||||
color: lighten($color-text, 10%);
|
||||
text-shadow: lighten($color-tab, 5%) 0 1px 0;
|
||||
|
||||
&:hover {
|
||||
color: $color-text;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ui-state-active {
|
||||
padding-bottom: 1px;
|
||||
border: 1px solid $color-button-generic-border;
|
||||
background-color: darken($tab-panel-texture-color, 2%);
|
||||
background-color: transparent;
|
||||
cursor: text;
|
||||
|
||||
a {
|
||||
border-bottom: 4px solid #66727d;
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
margin: 0 12px 0;
|
||||
color: $color-text;
|
||||
}
|
||||
}
|
||||
@ -246,13 +540,14 @@ body.cms {
|
||||
border-color: $color-medium-separator;
|
||||
}
|
||||
|
||||
li.cms-tabset-icon {
|
||||
li.cms-tabset-icon.ui-corner-top {
|
||||
text-indent:-9999em;
|
||||
|
||||
a {
|
||||
display: block;
|
||||
padding-left: 40px; // icon width
|
||||
padding-right: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
&.list a {background: sprite($sprites64, tab-list) no-repeat;}
|
||||
@ -268,7 +563,7 @@ body.cms {
|
||||
&.search.ui-state-active a {background: sprite($sprites64, tab-search-hover) no-repeat;}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.cms-panel-padded {
|
||||
.ui-tabs-panel {
|
||||
padding: 0; // Avoid double padding with parent
|
||||
@ -276,15 +571,16 @@ body.cms {
|
||||
.ui-tabs-panel {
|
||||
padding: $grid-x 0 0 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.Actions {
|
||||
padding: 0; // Avoid double padding with parent
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.ss-tabset-tabshidden .ui-tabs-panel {
|
||||
border-top: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -295,7 +591,6 @@ body.cms {
|
||||
.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 darken($color-tab, 15%);
|
||||
float: none; // parent container is already right floated
|
||||
|
||||
li {
|
||||
@ -305,6 +600,8 @@ body.cms {
|
||||
margin: 0;
|
||||
// overlap divider line below
|
||||
line-height: 40px - 1px;
|
||||
padding-top: 0;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@ -313,38 +610,56 @@ body.cms {
|
||||
}
|
||||
|
||||
.ui-state-default {
|
||||
@include box-shadow($color-shadow-light 0 0 2px);
|
||||
background-color: $color-base;
|
||||
@include background-image(
|
||||
linear-gradient(lighten($color-base, 12%), $color-base)
|
||||
);
|
||||
|
||||
border-top:none;
|
||||
border: {
|
||||
right-color: darken($color-base, 15%);
|
||||
left-color: lighten($color-base, 10%);
|
||||
}
|
||||
}
|
||||
background: none;
|
||||
border-top: none;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.ui-state-active {
|
||||
@include box-shadow(none);
|
||||
background:darken($tab-panel-texture-color,2%);
|
||||
background: transparent;
|
||||
border-top: none;
|
||||
border: {
|
||||
right-color: darken($color-tab, 15%); // same color as divider between header and body, needed for IE
|
||||
left-color: $tab-panel-texture-color;
|
||||
}
|
||||
border: none;
|
||||
z-index:2;
|
||||
|
||||
a {
|
||||
border-bottom: none;
|
||||
}
|
||||
a {
|
||||
border-bottom: 4px solid #66727d;
|
||||
padding: 0;
|
||||
margin: 0 12px 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.cms-content-header-tabs {
|
||||
.cms-content-header-tabs {
|
||||
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
|
||||
@ -428,9 +743,9 @@ body.cms {
|
||||
|
||||
.cms-content-actions, .cms-preview-controls {
|
||||
margin: 0;
|
||||
padding: $grid-y*1.5 $grid-y*1.5;
|
||||
padding: $grid-y*1.5 $grid-x*2;
|
||||
z-index: 999;
|
||||
border-top: 1px solid lighten($color-separator, 4%);
|
||||
border-top: 1px solid $color-light-separator;
|
||||
@include box-shadow(
|
||||
1px 0 0 $tab-panel-texture-color,
|
||||
$box-shadow-shine 0 1px 0px inset,
|
||||
@ -642,11 +957,11 @@ body.cms {
|
||||
.cms-content-toolbar {
|
||||
min-height: 29px;
|
||||
display: block;
|
||||
margin: 0 0 $grid-y*1.5 0;
|
||||
margin: $grid-y*1.5 0 0;
|
||||
padding-bottom: 0;
|
||||
|
||||
@include doubleborder(bottom, $color-light-separator, $box-shadow-shine);
|
||||
@include legacy-pie-clearfix();
|
||||
border-bottom: 0;
|
||||
box-shadow: none;
|
||||
|
||||
.cms-tree-view-modes {
|
||||
float:right;
|
||||
@ -659,55 +974,49 @@ body.cms {
|
||||
}
|
||||
}
|
||||
|
||||
/* smaller treedropdown */
|
||||
.chzn-container-single .chzn-single {
|
||||
height: 26px;
|
||||
line-height: 26px;
|
||||
padding-left:25px;
|
||||
color: darken($color-dark-grey, 15%);
|
||||
@include background-image(
|
||||
linear-gradient($color-button-generic, darken($color-button-generic, 10%))
|
||||
);
|
||||
font: {
|
||||
size:13px;
|
||||
weight:bold;
|
||||
}
|
||||
text-shadow:darken($color-text-shadow, 10%) 0 -1px 1px;
|
||||
box-shadow:none;
|
||||
&:hover {
|
||||
@include box-shadow(0 0 5px darken($color-button-generic, 20%));
|
||||
@include background-image(
|
||||
linear-gradient(lighten($color-button-generic, 2%), darken($color-button-generic, 8%))
|
||||
);
|
||||
}
|
||||
|
||||
&:active {
|
||||
@include box-shadow(inset 0 1px 3px darken($color-button-generic, 60%));
|
||||
}
|
||||
|
||||
span {
|
||||
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 {
|
||||
margin-bottom: $grid-y;
|
||||
margin-bottom: $grid-y*1.5;
|
||||
}
|
||||
|
||||
.cms-actions-buttons-row {
|
||||
clear: both;
|
||||
|
||||
.ss-ui-button,
|
||||
.ss-ui-button:hover,
|
||||
.ss-ui-button:active,
|
||||
.ss-ui-button:focus {
|
||||
color: $color-text;
|
||||
border: 0;
|
||||
background: none;
|
||||
box-shadow: none;
|
||||
text-shadow: none;
|
||||
text-decoration: none;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.ss-ui-button {
|
||||
&.active,
|
||||
&:active,
|
||||
&:hover {
|
||||
background-color: #dee3e8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.cms-actions-tools-row {
|
||||
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
|
||||
* panel
|
||||
*/
|
||||
@ -737,7 +1046,7 @@ body.cms {
|
||||
|
||||
.cms-panel-content {
|
||||
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;
|
||||
height:100%;
|
||||
|
||||
@ -787,7 +1096,7 @@ body.cms {
|
||||
label {
|
||||
float: none;
|
||||
width: auto;
|
||||
font-size: 11px;
|
||||
font-size: $font-base-size;
|
||||
padding: 0 $grid-x 4px 0;
|
||||
}
|
||||
|
||||
@ -885,6 +1194,14 @@ body.cms {
|
||||
/**
|
||||
* 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 {
|
||||
float: left;
|
||||
@ -907,79 +1224,69 @@ body.cms {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
label {
|
||||
.view-mode-batchactions-label {
|
||||
vertical-align: middle;
|
||||
display: none;
|
||||
}
|
||||
|
||||
fieldset, .Actions {
|
||||
display: inline-block;
|
||||
}
|
||||
#view-mode-batchactions {
|
||||
margin-top: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
&.inactive .view-mode-batchactions-wrapper {
|
||||
border-radius: 4px;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
.checkbox {
|
||||
margin-top: 2px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
.cms-content-batchactions-dropdown {
|
||||
@include inline-block;
|
||||
|
||||
#Form_BatchActionsForm select {
|
||||
width: 200px;
|
||||
// Context specific rules for when batch actions are in the SiteTree panel.
|
||||
.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
|
||||
overflow-y: auto;
|
||||
overflow-x: auto;
|
||||
background: darken($tab-panel-texture-color, 2%);
|
||||
width:100%;
|
||||
background: $tab-panel-texture-color;
|
||||
width: 100%;
|
||||
#Root_Main {
|
||||
.confirmedpassword {
|
||||
border-bottom:none;
|
||||
@ -1163,7 +1470,7 @@ form.member-profile-form {
|
||||
.cms-panel-toggle {
|
||||
@include box-shadow(0 0 1px $box-shadow-shine);
|
||||
&.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);
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
@ -1188,12 +1495,18 @@ 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 {
|
||||
&.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);
|
||||
}
|
||||
}
|
||||
}
|
||||
&.collapsed {
|
||||
cursor: pointer;
|
||||
.cms-panel-header *,
|
||||
@ -1279,11 +1592,21 @@ form.member-profile-form {
|
||||
|
||||
.cms {
|
||||
.cms-panel-padded {
|
||||
padding: $grid-y*2 $grid-x*2;
|
||||
margin:0;
|
||||
padding: 0 $grid-x*2 $grid-y*1.5;
|
||||
|
||||
&.ReportAdmin {
|
||||
> fieldset {
|
||||
padding-top: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.CMSPageAddController {
|
||||
.cms-panel-padded {
|
||||
padding: $grid-y*2 $grid-x*2;
|
||||
}
|
||||
}
|
||||
/** ------------------------------------------------------------------
|
||||
* 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)
|
||||
.ui-tabs-nav {
|
||||
position:absolute;
|
||||
top:0;
|
||||
top: 9px;
|
||||
// Tabs nav is included in scollbar. By absolutely positioning it off the edge,
|
||||
// we avoid tabs shifting position when scrollbar toggles
|
||||
right: 40px;
|
||||
@ -1471,10 +1794,12 @@ body.cms-dialog {
|
||||
.cms-content-header{
|
||||
padding:0;
|
||||
width:100%;
|
||||
height: 40px;
|
||||
height: 53px;
|
||||
|
||||
h3{
|
||||
padding: 3px 8px;
|
||||
margin: 10px;
|
||||
padding: $grid-y*1.5 $grid-x*2;
|
||||
margin: 0;
|
||||
line-height: 28px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1731,10 +2056,6 @@ form.small {
|
||||
* Import forms
|
||||
*/
|
||||
|
||||
body.SecurityAdmin {
|
||||
background-color: darken($tab-panel-texture-color,2%); //adds background to import members/groups iframe
|
||||
}
|
||||
|
||||
form.import-form {
|
||||
ul {
|
||||
list-style: disc;
|
||||
|
@ -64,8 +64,19 @@
|
||||
height: 16px;
|
||||
width: 12px;
|
||||
&.jstree-checkbox {
|
||||
height: 19px; //Larger to help avoid accidental page loads when trying to click checkboxes
|
||||
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 {
|
||||
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 {
|
||||
li {
|
||||
min-width: 159px;
|
||||
min-width: 187px;
|
||||
}
|
||||
a {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
display: block;
|
||||
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 {
|
||||
content:"";
|
||||
display: block;
|
||||
width:5px;
|
||||
height: 5px;
|
||||
width:6px;
|
||||
height: 6px;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
background: #fce2d0;
|
||||
border: 1px solid #ff9344;
|
||||
border: 1px solid #fff;
|
||||
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 {
|
||||
@extend %tree-status-icon-before;
|
||||
}
|
||||
// Labels
|
||||
.jstree .status-#{$label} > .jstree-hovered,
|
||||
.jstree .status-#{$label} > .jstree-clicked,
|
||||
.cms-tree.jstree span.badge.status-#{$label},
|
||||
.cms-tree.jstree .status-#{$label} > a .jstree-pageicon:before {
|
||||
.cms-tree.jstree span.badge.status-#{$label} {
|
||||
background-color:$bgColor;
|
||||
border-color:$color;
|
||||
}
|
||||
#cms-content-tools-CMSMain .cms-tree.jstree span.badge.status-#{$label} {
|
||||
box-shadow: 0px 0px 6px 2px $bgColor;
|
||||
border-color:$textColor;
|
||||
}
|
||||
.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', #EE6214, #FFF4ED);
|
||||
@include tree-status-icon('deletedonlive', #5F7688, #F5F5F5);
|
||||
@include tree-status-icon('archived', #5F7688, #F5F5F5);
|
||||
@include tree-status-icon('removedfromdraft', #5F7688, #F5F5F5);
|
||||
@include tree-status-icon('workflow-approval', #0070B4, #E8FAFF);
|
||||
@include tree-status-icon('addedtodraft', #ff7f22, #F46B00, #fff7f2);
|
||||
@include tree-status-icon('modified', #fff2e8, #F46B00, #fff7f2);
|
||||
@include tree-status-icon('archived', #5F7688, #455b6c, #f7f7f7);
|
||||
@include tree-status-icon('deletedonlive', #f7f7f7, #455b6c, #f7f7f7);
|
||||
@include tree-status-icon('removedfromdraft', #f7f7f7, #455b6c, #f7f7f7);
|
||||
@include tree-status-icon('workflow-approval', #0070B4, #0070B4, #E8FAFF);
|
||||
|
||||
.cms-tree {
|
||||
visibility: hidden; // enabled by JS to avoid layout glitches
|
||||
|
@ -91,7 +91,7 @@
|
||||
overflow-y: auto;
|
||||
|
||||
/** 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-position: 97% center !important;
|
||||
background-repeat: no-repeat !important;
|
||||
|
@ -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 {
|
||||
.cms-search-form {
|
||||
overflow:hidden;
|
||||
|
@ -17,6 +17,11 @@
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED:
|
||||
* .cms-content-tools will be removed in 4.0
|
||||
* Use .cms-content-filters instead.
|
||||
*/
|
||||
.cms .cms-content-tools {
|
||||
|
||||
//fix for width of dropdowns in filter panel
|
||||
|
@ -28,7 +28,7 @@ imported compass/support file and enables svg gradients in IE9.
|
||||
It was put here because there didn't seem to be a more logical place to put it.
|
||||
If more variables exist in the future, consider creating a variables file.*/
|
||||
|
||||
$experimental-support-for-svg: true;
|
||||
$experimental-support-for-svg: true;
|
||||
|
||||
/** -----------------------------
|
||||
* Theme
|
||||
@ -48,6 +48,7 @@ $experimental-support-for-svg: true;
|
||||
/** -----------------------------
|
||||
* CMS Components
|
||||
* ------------------------------ */
|
||||
@import "fonts.scss";
|
||||
@import "typography.scss";
|
||||
@import "uitheme.scss";
|
||||
@import "forms.scss";
|
||||
|
@ -11,12 +11,12 @@ $color-base: #b0bec7 !default;
|
||||
$color-widget-bg: lighten($color-base, 20%) !default;
|
||||
|
||||
/* 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-separator: #19435c !default;
|
||||
$color-medium-separator: #808080 !default;
|
||||
$color-separator: #C0C0C2 !default;
|
||||
$color-light-separator: #d0d3d5 !default;
|
||||
$color-separator: #C1C7CC !default; // Vertical dividers
|
||||
$color-light-separator: #D2D5D8 !default; // Horiontal dividers
|
||||
|
||||
$color-tab: #d9d9d9 !default;
|
||||
$color-dark-grey: #7B8C91 !default;
|
||||
@ -37,7 +37,7 @@ $color-menu-background: #c6d7df !default;
|
||||
$color-menu-border: #8c99a1 !default;
|
||||
$color-panel-background: #c6d7df !default;
|
||||
|
||||
$color-text: #444 !default;
|
||||
$color-text: #66727d !default;
|
||||
$color-text-light: white !default;
|
||||
$color-text-light-link: white !default;
|
||||
$color-text-disabled: #aaa !default;
|
||||
@ -48,7 +48,7 @@ $color-text-shadow: white !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-border: #708284 !default;
|
||||
@ -67,7 +67,7 @@ $color-good: #72c34b !default; // green
|
||||
/*$color-optional: #a1d2eb !default; */ // orange
|
||||
|
||||
$color-cms-batchactions-menu-background: #f5f5f5 !default;
|
||||
$color-cms-batchactions-menu-selected-background: #efe999 !default;
|
||||
$color-cms-batchactions-menu-selected-background: #fffcdc !default;
|
||||
|
||||
/** -----------------------------------------------
|
||||
* Textures
|
||||
|
@ -1,13 +1,5 @@
|
||||
<div class="breadcrumbs-wrapper" data-pjax-fragment="Breadcrumbs">
|
||||
<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 %>
|
||||
<% if $Last %>
|
||||
<span class="cms-panel-link crumb last">$Title.XML</span>
|
||||
|
@ -1,7 +1,7 @@
|
||||
<% if $Backlink %>
|
||||
<div class="cms_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>
|
||||
</div>
|
||||
<% end_if %>
|
||||
|
@ -1,6 +1,6 @@
|
||||
<div class="ss-loading-screen">
|
||||
<div class="loading-logo">
|
||||
<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>
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,5 +1,5 @@
|
||||
<div class="cms-navigator">
|
||||
<a href="#" class="ss-ui-button cms-preview-toggle-link" data-icon="preview">
|
||||
« <% _t('SilverStripeNavigator.Edit', 'Edit') %>
|
||||
« <%t SilverStripeNavigator.Edit 'Edit' %>
|
||||
</a>
|
||||
</div>
|
@ -3,7 +3,6 @@
|
||||
<% end_if %>
|
||||
<div class="cms-content-header north">
|
||||
<div class="cms-content-header-info">
|
||||
<% include BackLink_Button %>
|
||||
<% with $Controller %>
|
||||
<% include CMSBreadcrumbs %>
|
||||
<% end_with %>
|
||||
@ -51,7 +50,7 @@
|
||||
<% end_loop %>
|
||||
<% if $Controller.LinkPreview %>
|
||||
<a href="$Controller.LinkPreview" class="cms-preview-toggle-link ss-ui-button" data-icon="preview">
|
||||
<% _t('LeftAndMain.PreviewButton', 'Preview') %> »
|
||||
<%t LeftAndMain.PreviewButton 'Preview' %> »
|
||||
</a>
|
||||
<% end_if %>
|
||||
</div>
|
||||
|
@ -8,10 +8,10 @@
|
||||
</div>
|
||||
|
||||
<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 %>
|
||||
<span>
|
||||
<% _t('LeftAndMain_Menu_ss.Hello','Hi') %>
|
||||
<%t LeftAndMain_Menu_ss.Hello 'Hi' %>
|
||||
<a href="{$AbsoluteBaseURL}admin/myprofile" class="profile-link">
|
||||
<% if $FirstName && $Surname %>$FirstName $Surname<% else_if $FirstName %>$FirstName<% else %>$Email<% end_if %>
|
||||
</a>
|
||||
|
@ -3,18 +3,18 @@
|
||||
<% include LeftAndMain_ViewModeSelector SelectID="preview-mode-dropdown-in-preview" %>
|
||||
|
||||
<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">
|
||||
<option data-icon="icon-auto" data-description="<% _t('SilverStripeNavigator.Responsive', 'Responsive') %>" class="icon-auto icon-view first" value="auto">
|
||||
<% _t('SilverStripeNavigator.Auto', 'Auto') %>
|
||||
<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">
|
||||
<%t SilverStripeNavigator.Auto 'Auto' %>
|
||||
</option>
|
||||
<option data-icon="icon-desktop" data-description="1024px <% _t('SilverStripeNavigator.Width', 'width') %>" class="icon-desktop icon-view" value="desktop">
|
||||
<% _t('SilverStripeNavigator.Desktop', 'Desktop') %>
|
||||
<option data-icon="icon-desktop" data-description="1024px <%t SilverStripeNavigator.Width 'width' %>" class="icon-desktop icon-view" value="desktop">
|
||||
<%t SilverStripeNavigator.Desktop 'Desktop' %>
|
||||
</option>
|
||||
<option data-icon="icon-tablet" data-description="800px <% _t('SilverStripeNavigator.Width', 'width') %>" class="icon-tablet icon-view" value="tablet">
|
||||
<% _t('SilverStripeNavigator.Tablet', 'Tablet') %>
|
||||
<option data-icon="icon-tablet" data-description="800px <%t SilverStripeNavigator.Width 'width' %>" class="icon-tablet icon-view" value="tablet">
|
||||
<%t SilverStripeNavigator.Tablet 'Tablet' %>
|
||||
</option>
|
||||
<option data-icon="icon-mobile" data-description="400px <% _t('SilverStripeNavigator.Width', 'width') %>" class="icon-mobile icon-view last" value="mobile">
|
||||
<% _t('SilverStripeNavigator.Mobile', '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' %>
|
||||
</option>
|
||||
</select>
|
||||
</span>
|
||||
@ -32,7 +32,7 @@
|
||||
</fieldset>
|
||||
<% else %>
|
||||
<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 %>
|
||||
<option name="$Name" data-name="$Name" data-link="$Link" class="state-name $FirstLast" value="$Link" <% if $isActive %>selected<% end_if %>>
|
||||
$Title
|
||||
|
@ -2,30 +2,37 @@
|
||||
|
||||
<div class="cms-content-header north">
|
||||
<div class="cms-content-header-info">
|
||||
<h2>
|
||||
<% include CMSSectionIcon %>
|
||||
<% if $SectionTitle %>
|
||||
$SectionTitle
|
||||
<% else %>
|
||||
<% _t('ModelAdmin.Title', 'Data Models') %>
|
||||
<% end_if %>
|
||||
</h2>
|
||||
<div class="breadcrumbs-wrapper">
|
||||
<h2 id="page-title-heading">
|
||||
<span class="cms-panel-link crumb last">
|
||||
<% if $SectionTitle %>
|
||||
$SectionTitle
|
||||
<% else %>
|
||||
<%t ModelAdmin.Title 'Data Models' %>
|
||||
<% end_if %>
|
||||
</span>
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="cms-content-header-tabs cms-tabset-nav-primary ss-ui-tabs-nav">
|
||||
<ul>
|
||||
<% loop $ManagedModelTabs %>
|
||||
<button id="filters-button" class="icon-button font-icon-search" title="<%t CMSPagesController_Tools_ss.FILTER 'Filter' %>"></button>
|
||||
<ul class="cms-tabset-nav-primary">
|
||||
<% loop $ManagedModelTabs %>
|
||||
<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>
|
||||
</li>
|
||||
<% end_loop %>
|
||||
<% end_loop %>
|
||||
</ul>
|
||||
</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
|
||||
$EditForm
|
||||
|
||||
<div class="cms-content-view">
|
||||
$EditForm
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
@ -1,8 +1,8 @@
|
||||
<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}">
|
||||
<h4><% sprintf(_t('ModelAdmin_ImportSpec_ss.IMPORTSPECTITLE', 'Specification for %s'),$ModelName) %></h4>
|
||||
<h5><% _t('ModelAdmin_ImportSpec_ss.IMPORTSPECFIELDS', 'Database columns') %></h5>
|
||||
<h4><%t ModelAdmin_ImportSpec_ss.IMPORTSPECTITLE 'Specification for %s' s=$ModelName %></h4>
|
||||
<h5><%t ModelAdmin_ImportSpec_ss.IMPORTSPECFIELDS 'Database columns' %></h5>
|
||||
<% loop $Fields %>
|
||||
<dl>
|
||||
<dt><em>$Name</em></dt>
|
||||
@ -10,7 +10,7 @@
|
||||
</dl>
|
||||
<% end_loop %>
|
||||
|
||||
<h5><% _t('ModelAdmin_ImportSpec_ss.IMPORTSPECRELATIONS', 'Relations') %></h5>
|
||||
<h5><%t ModelAdmin_ImportSpec_ss.IMPORTSPECRELATIONS 'Relations' %></h5>
|
||||
<% loop $Relations %>
|
||||
<dl>
|
||||
<dt><em>$Name</em></dt>
|
||||
|
@ -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 class="cms-panel-content center">
|
||||
<% if $SearchForm %>
|
||||
<h3 class="cms-panel-header"><% _t('ModelAdmin_Tools_ss.FILTER', 'Filter') %></h3>
|
||||
$SearchForm
|
||||
<% end_if %>
|
||||
<div id="cms-content-tools-ModelAdmin" class="cms-content-filters">
|
||||
<% if $SearchForm %>
|
||||
<h3 class="cms-panel-header"><%t ModelAdmin_Tools_ss.FILTER 'Filter' %></h3>
|
||||
$SearchForm
|
||||
<% end_if %>
|
||||
|
||||
<% if $ImportForm %>
|
||||
<h3 class="cms-panel-header"><% _t('ModelAdmin_Tools_ss.IMPORT', 'Import') %></h3>
|
||||
$ImportForm
|
||||
<% end_if %>
|
||||
</div>
|
||||
<div class="cms-panel-content-collapsed">
|
||||
<h3 class="cms-panel-header"><% _t('ModelAdmin_Tools_ss.FILTER', 'Filter') %></h3>
|
||||
</div>
|
||||
<% if $ImportForm %>
|
||||
<h3 class="cms-panel-header"><%t ModelAdmin_Tools_ss.IMPORT 'Import' %></h3>
|
||||
$ImportForm
|
||||
<% end_if %>
|
||||
</div>
|
||||
|
@ -16,7 +16,7 @@
|
||||
$Content
|
||||
|
||||
<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-device-outer">
|
||||
<div class="preview-device-inner">
|
||||
|
@ -1,7 +1,7 @@
|
||||
<h3><% _t('ModelSidebar_ss.SEARCHLISTINGS','Search') %></h3>
|
||||
<h3><%t ModelSidebar_ss.SEARCHLISTINGS 'Search' %></h3>
|
||||
$SearchForm
|
||||
|
||||
<% if $ImportForm %>
|
||||
<h3><% _t('ModelSidebar_ss.IMPORT_TAB_HEADER', 'Import') %></h3>
|
||||
<h3><%t ModelSidebar_ss.IMPORT_TAB_HEADER 'Import' %></h3>
|
||||
$ImportForm
|
||||
<% end_if %>
|
||||
|
@ -135,7 +135,7 @@ class LeftAndMainTest extends FunctionalTest {
|
||||
$link = $menuItem->Link;
|
||||
|
||||
// don't test external links
|
||||
if(preg_match('/^https?:\/\//',$link)) continue;
|
||||
if(preg_match('/^(https?:)?\/\//',$link)) continue;
|
||||
|
||||
$response = $this->get($link);
|
||||
|
||||
|
@ -47,22 +47,22 @@ class RestfulService extends ViewableData implements Flushable {
|
||||
* 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}
|
||||
*
|
||||
* @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 mixed $value The cURL opt 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));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
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);
|
||||
}
|
||||
|
||||
@ -74,12 +74,12 @@ class RestfulService extends ViewableData implements Flushable {
|
||||
* @param string $user The proxy auth user name
|
||||
* @param string $password The proxy auth password
|
||||
* @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
|
||||
*/
|
||||
public static function set_default_proxy($proxy, $port = 80, $user = "", $password = "", $socks = false) {
|
||||
Deprecation::notice(
|
||||
'3.1',
|
||||
'4.0',
|
||||
'Use the "RestfulService.default_curl_options" config setting instead, '
|
||||
. 'with direct reference to the CURL_* options'
|
||||
);
|
||||
@ -144,8 +144,11 @@ class RestfulService extends ViewableData implements Flushable {
|
||||
$this->customHeaders[] = $header;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since version 4.0
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
@ -616,9 +619,10 @@ class RestfulService_Response extends SS_HTTPResponse {
|
||||
|
||||
/**
|
||||
* @param string
|
||||
* @deprecated since version 4.0
|
||||
*/
|
||||
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) {
|
||||
$this->cachedResponse = new RestfulService_Response($content);
|
||||
}
|
||||
|
@ -17,10 +17,10 @@
|
||||
],
|
||||
"require": {
|
||||
"php": ">=5.3.3",
|
||||
"composer/installers": "*"
|
||||
"composer/installers": "~1.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/PHPUnit": "~3.7@stable"
|
||||
"phpunit/PHPUnit": "~3.7"
|
||||
},
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
@ -29,6 +29,5 @@
|
||||
},
|
||||
"autoload": {
|
||||
"classmap": ["tests/behat/features/bootstrap"]
|
||||
},
|
||||
"minimum-stability": "dev"
|
||||
}
|
||||
}
|
||||
|
12
config.rb
12
config.rb
@ -13,10 +13,16 @@ output_style = :compact
|
||||
# To enable relative paths to assets via compass helper functions. Uncomment:
|
||||
relative_assets = true
|
||||
|
||||
# disable comments in the output. We want admin comments
|
||||
# to be verbose
|
||||
# Disable comments in the output. We want admin comments to be verbose.
|
||||
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"
|
||||
|
@ -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,
|
||||
* 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) {
|
||||
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);
|
||||
}
|
||||
|
||||
@ -67,30 +67,30 @@ class ContentNegotiator extends Object {
|
||||
* 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.
|
||||
*
|
||||
* @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() {
|
||||
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');
|
||||
}
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -84,13 +84,13 @@ class Director implements TemplateGlobalProvider {
|
||||
*
|
||||
* 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
|
||||
* priority 100 for your site's rules. The built-in rules are priority 10, standard modules are
|
||||
* priority 50.
|
||||
*/
|
||||
public static function addRules($priority, $rules) {
|
||||
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);
|
||||
}
|
||||
@ -610,10 +610,10 @@ class Director implements TemplateGlobalProvider {
|
||||
* Sets the root URL for the website.
|
||||
* 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) {
|
||||
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);
|
||||
}
|
||||
|
||||
@ -630,10 +630,10 @@ class Director implements TemplateGlobalProvider {
|
||||
* Sets the root folder for the website.
|
||||
* 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) {
|
||||
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);
|
||||
}
|
||||
|
||||
@ -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()},
|
||||
* 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.
|
||||
*/
|
||||
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",
|
||||
E_USER_WARNING);
|
||||
} 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);
|
||||
}
|
||||
}
|
||||
|
@ -103,7 +103,7 @@ class HTTP {
|
||||
*/
|
||||
public static function urlRewriter($content, $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
|
||||
@ -308,7 +308,7 @@ class HTTP {
|
||||
/**
|
||||
* Add the appropriate caching headers to the response, including If-Modified-Since / 304 handling.
|
||||
*
|
||||
* @param SS_HTTPResponse The SS_HTTPResponse object to augment. Omitted the argument or passing a string is
|
||||
* @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.
|
||||
*/
|
||||
public static function add_cache_headers($body = null) {
|
||||
@ -328,21 +328,17 @@ class HTTP {
|
||||
// us trying.
|
||||
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();
|
||||
|
||||
$config = Config::inst();
|
||||
$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')) {
|
||||
$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') {
|
||||
$cacheAge = 0;
|
||||
}
|
||||
@ -383,13 +379,16 @@ class HTTP {
|
||||
foreach($cacheControlHeaders as $header => $value) {
|
||||
if(is_null($value)) {
|
||||
unset($cacheControlHeaders[$header]);
|
||||
} elseif(is_bool($value) || $value === "true") {
|
||||
} elseif((is_bool($value) && $value) || $value === "true") {
|
||||
$cacheControlHeaders[$header] = $header;
|
||||
} else {
|
||||
$cacheControlHeaders[$header] = $header."=".$value;
|
||||
}
|
||||
}
|
||||
|
||||
$responseHeaders['Cache-Control'] = implode(', ', $cacheControlHeaders);
|
||||
unset($cacheControlHeaders, $header, $value);
|
||||
|
||||
if(self::$modification_date && $cacheAge > 0) {
|
||||
$responseHeaders["Last-Modified"] = self::gmt_date(self::$modification_date);
|
||||
|
||||
|
@ -162,24 +162,24 @@ class Session {
|
||||
* To make cookies visible on all subdomains then the domain
|
||||
* 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
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
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');
|
||||
}
|
||||
|
||||
@ -187,24 +187,24 @@ class Session {
|
||||
* Path to set on the domain where the session cookie will work.
|
||||
* 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
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
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')) {
|
||||
return Config::inst()->get('Session', 'cookie_path');
|
||||
} else {
|
||||
@ -215,45 +215,46 @@ class Session {
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
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');
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the session store path
|
||||
* @return string
|
||||
* @deprecated since version 4.0
|
||||
*/
|
||||
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');
|
||||
}
|
||||
|
||||
@ -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.
|
||||
*
|
||||
* @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.
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
@ -605,20 +606,20 @@ class Session {
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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() {
|
||||
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');
|
||||
}
|
||||
}
|
||||
|
@ -272,7 +272,16 @@ class Injector {
|
||||
* @return Injector Reference to restored active Injector instance
|
||||
*/
|
||||
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
|
||||
*
|
||||
* @deprecated since 3.1.1
|
||||
* @deprecated since 4.0
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -11,21 +11,22 @@ class SilverStripeServiceConfigurationLocator extends ServiceConfigurationLocato
|
||||
|
||||
/**
|
||||
* List of Injector configurations cached from Config in class => config format.
|
||||
* If any config is false, this denotes that this class and all its parents
|
||||
* If any config is false, this denotes that this class and all its parents
|
||||
* have no configuration specified.
|
||||
*
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $configs = array();
|
||||
|
||||
public function locateConfigFor($name) {
|
||||
|
||||
// Check direct or cached result
|
||||
$config = $this->configFor($name);
|
||||
if($config !== null) return $config;
|
||||
|
||||
|
||||
// do parent lookup if it's a class
|
||||
if (class_exists($name)) {
|
||||
$parents = array_reverse(array_keys(ClassInfo::ancestry($name)));
|
||||
$parents = array_reverse(array_values(ClassInfo::ancestry($name)));
|
||||
array_shift($parents);
|
||||
|
||||
foreach ($parents as $parent) {
|
||||
@ -38,27 +39,27 @@ class SilverStripeServiceConfigurationLocator extends ServiceConfigurationLocato
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// there is no parent config, so we'll record that as false so we don't do the expensive
|
||||
// lookup through parents again
|
||||
$this->configs[$name] = false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieves the config for a named service without performing a hierarchy walk
|
||||
*
|
||||
*
|
||||
* @param string $name Name of service
|
||||
* @return mixed Returns either the configuration data, if there is any. A missing config is denoted
|
||||
* @return mixed Returns either the configuration data, if there is any. A missing config is denoted
|
||||
* by a value of either null (there is no direct config assigned and a hierarchy walk is necessary)
|
||||
* or false (there is no config for this class, nor within the hierarchy for this class).
|
||||
* or false (there is no config for this class, nor within the hierarchy for this class).
|
||||
*/
|
||||
protected function configFor($name) {
|
||||
|
||||
|
||||
// Return cached result
|
||||
if (isset($this->configs[$name])) {
|
||||
return $this->configs[$name]; // Potentially false
|
||||
}
|
||||
|
||||
|
||||
$config = Config::inst()->get('Injector', $name);
|
||||
if ($config) {
|
||||
$this->configs[$name] = $config;
|
||||
@ -67,4 +68,4 @@ class SilverStripeServiceConfigurationLocator extends ServiceConfigurationLocato
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,8 +3,8 @@
|
||||
/**
|
||||
* Provides introspection information about the class tree.
|
||||
*
|
||||
* It's a cached wrapper around the built-in class functions. SilverStripe uses
|
||||
* class introspection heavily and without the caching it creates an unfortunate
|
||||
* It's a cached wrapper around the built-in class functions. SilverStripe uses
|
||||
* class introspection heavily and without the caching it creates an unfortunate
|
||||
* performance hit.
|
||||
*
|
||||
* @package framework
|
||||
@ -61,6 +61,7 @@ class ClassInfo {
|
||||
* @return array List of subclasses
|
||||
*/
|
||||
public static function getValidSubClasses($class = 'SiteTree', $includeUnbacked = false) {
|
||||
$class = self::class_name($class);
|
||||
$classes = DB::get_schema()->enumValuesForField($class, 'ClassName');
|
||||
if (!$includeUnbacked) $classes = array_filter($classes, array('ClassInfo', 'exists'));
|
||||
return $classes;
|
||||
@ -77,9 +78,7 @@ class ClassInfo {
|
||||
public static function dataClassesFor($class) {
|
||||
$result = array();
|
||||
|
||||
if (is_object($class)) {
|
||||
$class = get_class($class);
|
||||
}
|
||||
$class = self::class_name($class);
|
||||
|
||||
$classes = array_merge(
|
||||
self::ancestry($class),
|
||||
@ -101,7 +100,7 @@ class ClassInfo {
|
||||
* @return string
|
||||
*/
|
||||
public static function baseDataClass($class) {
|
||||
if (is_object($class)) $class = get_class($class);
|
||||
$class = self::class_name($class);
|
||||
|
||||
if (!is_subclass_of($class, 'DataObject')) {
|
||||
throw new InvalidArgumentException("$class is not a subclass of DataObject");
|
||||
@ -125,7 +124,7 @@ class ClassInfo {
|
||||
* <code>
|
||||
* ClassInfo::subclassesFor('BaseClass');
|
||||
* array(
|
||||
* 0 => 'BaseClass',
|
||||
* 'BaseClass' => 'BaseClass',
|
||||
* 'ChildClass' => 'ChildClass',
|
||||
* 'GrandChildClass' => 'GrandChildClass'
|
||||
* )
|
||||
@ -135,8 +134,10 @@ class ClassInfo {
|
||||
* @return array Names of all subclasses as an associative array.
|
||||
*/
|
||||
public static function subclassesFor($class) {
|
||||
//normalise class case
|
||||
$className = self::class_name($class);
|
||||
$descendants = SS_ClassLoader::instance()->getManifest()->getDescendantsOf($class);
|
||||
$result = array($class => $class);
|
||||
$result = array($className => $className);
|
||||
|
||||
if ($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
|
||||
* array, sorted with the root class first.
|
||||
@ -154,9 +172,11 @@ class ClassInfo {
|
||||
* @return array
|
||||
*/
|
||||
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;
|
||||
if(!isset(self::$_cache_ancestry[$cacheKey])) {
|
||||
$ancestry = array();
|
||||
@ -183,7 +203,7 @@ class ClassInfo {
|
||||
* Returns true if the given class implements the given interface
|
||||
*/
|
||||
public static function classImplements($className, $interfaceName) {
|
||||
return in_array($className, SS_ClassLoader::instance()->getManifest()->getImplementorsOf($interfaceName));
|
||||
return in_array($className, self::implementorsOf($interfaceName));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -232,24 +252,28 @@ class ClassInfo {
|
||||
private static $method_from_cache = array();
|
||||
|
||||
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])) {
|
||||
self::$method_from_cache[$class][$method] = false;
|
||||
if (!array_key_exists($lMethod, self::$method_from_cache[$lClass])) {
|
||||
self::$method_from_cache[$lClass][$lMethod] = false;
|
||||
|
||||
$classRef = new ReflectionClass($class);
|
||||
|
||||
if ($classRef->hasMethod($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
|
||||
* will return null.
|
||||
*
|
||||
@ -259,23 +283,26 @@ class ClassInfo {
|
||||
* @return string
|
||||
*/
|
||||
public static function table_for_object_field($candidateClass, $fieldName) {
|
||||
if(!$candidateClass || !$fieldName) {
|
||||
if(!$candidateClass || !$fieldName || !is_subclass_of($candidateClass, 'DataObject')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$exists = class_exists($candidateClass);
|
||||
//normalise class name
|
||||
$candidateClass = self::class_name($candidateClass);
|
||||
|
||||
$exists = self::exists($candidateClass);
|
||||
|
||||
while($candidateClass && $candidateClass != 'DataObject' && $exists) {
|
||||
if(DataObject::has_own_table($candidateClass)) {
|
||||
$inst = singleton($candidateClass);
|
||||
|
||||
|
||||
if($inst->hasOwnTableDatabaseField($fieldName)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$candidateClass = get_parent_class($candidateClass);
|
||||
$exists = class_exists($candidateClass);
|
||||
$exists = $candidateClass && self::exists($candidateClass);
|
||||
}
|
||||
|
||||
if(!$candidateClass || !$exists) {
|
||||
|
@ -237,7 +237,16 @@ class Config {
|
||||
* @return Config Reference to new active Config instance
|
||||
*/
|
||||
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
|
||||
* @subpackage core
|
||||
* @deprecated 3.2
|
||||
* @deprecated 4.0
|
||||
*/
|
||||
class Config_LRU {
|
||||
const SIZE = 1000;
|
||||
@ -693,7 +702,7 @@ class Config_LRU {
|
||||
protected $c = 0;
|
||||
|
||||
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', '<')) {
|
||||
// SplFixedArray causes seg faults before PHP 5.3.7
|
||||
$this->cache = array();
|
||||
|
@ -111,6 +111,7 @@ if(!defined('TRUSTED_PROXY')) {
|
||||
*/
|
||||
if(!isset($_SERVER['HTTP_HOST'])) {
|
||||
// HTTP_HOST, REQUEST_PORT, SCRIPT_NAME, and PHP_SELF
|
||||
global $_FILE_TO_URL_MAPPING;
|
||||
if(isset($_FILE_TO_URL_MAPPING)) {
|
||||
$fullPath = $testPath = realpath($_SERVER['SCRIPT_FILENAME']);
|
||||
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);
|
||||
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);
|
||||
die();
|
||||
}
|
||||
|
@ -27,6 +27,9 @@ error_reporting(E_ALL | E_STRICT);
|
||||
*/
|
||||
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
|
||||
*/
|
||||
@ -255,7 +258,7 @@ function translate_memstring($memString) {
|
||||
*/
|
||||
function increase_time_limit_to($timeLimit = null) {
|
||||
$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(!$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()};
|
||||
*
|
||||
|
@ -584,7 +584,7 @@ abstract class Object {
|
||||
$sources[] = $extensionClass;
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
@ -995,6 +995,8 @@ abstract class Object {
|
||||
* @return mixed the cached data
|
||||
*/
|
||||
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)) {
|
||||
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
|
||||
*/
|
||||
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;
|
||||
if(!is_array($arguments)) $arguments = array($arguments);
|
||||
if($ID) $cacheName .= '_' . $ID;
|
||||
@ -1039,6 +1043,8 @@ abstract class Object {
|
||||
* @return mixed
|
||||
*/
|
||||
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);
|
||||
|
||||
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)
|
||||
*/
|
||||
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));
|
||||
}
|
||||
|
||||
@ -1065,6 +1072,7 @@ abstract class Object {
|
||||
* @return string the name with all special cahracters replaced with underscores
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
* 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) {
|
||||
if ($basename == self::CONF_FILE) {
|
||||
$this->configs[] = $pathname;
|
||||
@ -342,6 +487,7 @@ class SS_ClassManifest {
|
||||
$classes = null;
|
||||
$interfaces = null;
|
||||
$namespace = null;
|
||||
$imports = null;
|
||||
|
||||
// The results of individual file parses are cached, since only a few
|
||||
// files will have changed and TokenisedRegularExpression is quite
|
||||
@ -352,14 +498,17 @@ class SS_ClassManifest {
|
||||
|
||||
if ($data = $this->cache->load($key)) {
|
||||
$valid = (
|
||||
isset($data['classes']) && isset($data['interfaces']) && isset($data['namespace'])
|
||||
&& is_array($data['classes']) && is_array($data['interfaces']) && is_string($data['namespace'])
|
||||
isset($data['classes']) && is_array($data['classes'])
|
||||
&& isset($data['interfaces']) && is_array($data['interfaces'])
|
||||
&& isset($data['namespace']) && is_string($data['namespace'])
|
||||
&& isset($data['imports']) && is_array($data['imports'])
|
||||
);
|
||||
|
||||
if ($valid) {
|
||||
$classes = $data['classes'];
|
||||
$classes = $data['classes'];
|
||||
$interfaces = $data['interfaces'];
|
||||
$namespace = $data['namespace'];
|
||||
$imports = $data['imports'];
|
||||
}
|
||||
}
|
||||
|
||||
@ -367,28 +516,52 @@ class SS_ClassManifest {
|
||||
$tokens = token_get_all($file);
|
||||
|
||||
$classes = self::get_namespaced_class_parser()->findAll($tokens);
|
||||
|
||||
$namespace = self::get_namespace_parser()->findAll($tokens);
|
||||
|
||||
if($namespace) {
|
||||
$namespace = implode('', $namespace[0]['namespaceName']) . '\\';
|
||||
$namespace = implode('', $namespace[0]['namespaceName']);
|
||||
} else {
|
||||
$namespace = '';
|
||||
}
|
||||
|
||||
$imports = $this->getImportsFromTokens($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);
|
||||
}
|
||||
|
||||
foreach ($classes as $class) {
|
||||
$name = $namespace . $class['className'];
|
||||
$extends = isset($class['extends']) ? implode('', $class['extends']) : null;
|
||||
$name = $class['className'];
|
||||
if ($namespace) {
|
||||
$namespace = rtrim($namespace, '\\');
|
||||
$name = $namespace . '\\' . $name;
|
||||
}
|
||||
$extends = isset($class['extends']) ? implode('', $class['extends']) : null;
|
||||
$implements = isset($class['interfaces']) ? $class['interfaces'] : null;
|
||||
|
||||
if($extends && $extends[0] != '\\') {
|
||||
$extends = $namespace . $extends;
|
||||
} elseif($extends) {
|
||||
$extends = substr($extends, 1);
|
||||
if ($extends) {
|
||||
$extends = $this->findClassOrInterfaceFromCandidateImports($extends, $namespace, $imports);
|
||||
}
|
||||
|
||||
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);
|
||||
@ -414,32 +587,24 @@ class SS_ClassManifest {
|
||||
}
|
||||
|
||||
if ($implements) {
|
||||
$interface = $namespace;
|
||||
for($i = 0; $i < count($implements); ++$i) {
|
||||
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);
|
||||
foreach ($implements as $interface) {
|
||||
$interface = strtolower($interface);
|
||||
|
||||
if (!isset($this->implementors[$interface])) {
|
||||
$this->implementors[$interface] = array($name);
|
||||
} else {
|
||||
$this->implementors[$interface][] = $name;
|
||||
}
|
||||
if (!isset($this->implementors[$interface])) {
|
||||
$this->implementors[$interface] = array($name);
|
||||
} else {
|
||||
$this->implementors[$interface][] = $name;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$interfaceBase = '';
|
||||
if ($namespace) {
|
||||
$interfaceBase = $namespace . '\\';
|
||||
}
|
||||
foreach ($interfaces as $interface) {
|
||||
$this->interfaces[strtolower($namespace . $interface['interfaceName'])] = $pathname;
|
||||
$this->interfaces[strtolower($interfaceBase . $interface['interfaceName'])] = $pathname;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -79,7 +79,7 @@ class SS_ConfigStaticManifest {
|
||||
$static = $this->statics[$class][$name];
|
||||
|
||||
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);
|
||||
// Don't warn more than once per static
|
||||
$this->statics[$class][$name]['access'] = T_PRIVATE;
|
||||
|
@ -16,7 +16,7 @@ Used in side panels and action tabs
|
||||
.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 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; }
|
||||
|
||||
.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 .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 .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-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; }
|
||||
@ -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 .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-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 .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 .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: #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 .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; }
|
||||
@ -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-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 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-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; }
|
||||
@ -87,3 +87,5 @@ body.cms.ss-uploadfield-edit-iframe .fieldholder-small label, .composite.ss-asse
|
||||
/** ---------------------------------------------------- "@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 */
|
||||
.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 */
|
||||
|
7
css/AssetUploadField.css.map
Normal file
7
css/AssetUploadField.css.map
Normal 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"
|
||||
}
|
@ -1,3 +1,5 @@
|
||||
#right form .CheckboxSetField ul { margin: 0; padding: 0; }
|
||||
#right form .CheckboxSetField ul li { list-style: none !important; margin: 0; padding: 0; }
|
||||
#right form .CheckboxSetField input { width: auto; }
|
||||
|
||||
/*# sourceMappingURL=CheckboxSetField.css.map */
|
||||
|
7
css/CheckboxSetField.css.map
Normal file
7
css/CheckboxSetField.css.map
Normal 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"
|
||||
}
|
@ -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; }
|
||||
|
||||
/*# sourceMappingURL=ConfirmedPasswordField.css.map */
|
||||
|
7
css/ConfirmedPasswordField.css.map
Normal file
7
css/ConfirmedPasswordField.css.map
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"version": 3,
|
||||
"mappings": "AAAA,wCAAyC,yGAExC,OAAO,EAAC,IAAI",
|
||||
"sources": ["../scss/ConfirmedPasswordField.scss"],
|
||||
"names": [],
|
||||
"file": "ConfirmedPasswordField.css"
|
||||
}
|
@ -1,3 +1,5 @@
|
||||
.datetime .middleColumn .middleColumn { margin: 0; padding: 0; clear: none; float: left; }
|
||||
|
||||
.datetime .middleColumn .field { margin: 0; border-bottom: none; box-shadow: none; }
|
||||
|
||||
/*# sourceMappingURL=DatetimeField.css.map */
|
||||
|
7
css/DatetimeField.css.map
Normal file
7
css/DatetimeField.css.map
Normal 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"
|
||||
}
|
@ -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; }
|
||||
|
||||
.typography .ss-tabset ul { margin: 0; }
|
||||
|
||||
/*# sourceMappingURL=Form.css.map */
|
||||
|
7
css/Form.css.map
Normal file
7
css/Form.css.map
Normal 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"
|
||||
}
|
@ -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 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); }
|
||||
|
||||
/*# sourceMappingURL=GridField.css.map */
|
||||
|
7
css/GridField.css.map
Normal file
7
css/GridField.css.map
Normal 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"
|
||||
}
|
@ -1,3 +1,5 @@
|
||||
.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; }
|
||||
|
||||
/*# sourceMappingURL=GridFieldPaginator.css.map */
|
||||
|
7
css/GridFieldPaginator.css.map
Normal file
7
css/GridFieldPaginator.css.map
Normal 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"
|
||||
}
|
@ -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 td { border-top: 1px solid #aaa; border-bottom: 1px solid #aaa; text-align: left; padding: 5px 10px; }
|
||||
table td:first-child { padding-left: 0px; }
|
||||
|
||||
/*# sourceMappingURL=GridField_print.css.map */
|
||||
|
7
css/GridField_print.css.map
Normal file
7
css/GridField_print.css.map
Normal 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"
|
||||
}
|
@ -7,3 +7,5 @@ p#Remember label { display: inline-block; margin: 0; }
|
||||
#MemberLoginForm_LoginForm .Actions { padding-left: 12em; }
|
||||
|
||||
#ForgotPassword { margin-top: 1em; }
|
||||
|
||||
/*# sourceMappingURL=Security_login.css.map */
|
||||
|
7
css/Security_login.css.map
Normal file
7
css/Security_login.css.map
Normal 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"
|
||||
}
|
@ -9,3 +9,5 @@
|
||||
.mainblock.allassets ul { padding: 0 0 0 10px; height: 100px; overflow-y: scroll; }
|
||||
|
||||
.mainblock.allassets ul li { width: 150px; }
|
||||
|
||||
/*# sourceMappingURL=SelectionGroup.css.map */
|
||||
|
7
css/SelectionGroup.css.map
Normal file
7
css/SelectionGroup.css.map
Normal 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"
|
||||
}
|
@ -5,3 +5,5 @@ div.report span.submitted { font-style: italic; }
|
||||
div.report table * { font-size: 1em; }
|
||||
|
||||
div.report table td.field { font-weight: bold; padding-right: 10px; padding-left: 0px; }
|
||||
|
||||
/*# sourceMappingURL=SubmittedFormReportField.css.map */
|
||||
|
7
css/SubmittedFormReportField.css.map
Normal file
7
css/SubmittedFormReportField.css.map
Normal 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"
|
||||
}
|
@ -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 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; }
|
||||
|
||||
/*# sourceMappingURL=TreeDropdownField.css.map */
|
||||
|
7
css/TreeDropdownField.css.map
Normal file
7
css/TreeDropdownField.css.map
Normal 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"
|
||||
}
|
@ -35,3 +35,5 @@ CODE.expected { color: green; background: none; font-weight: normal; }
|
||||
CODE.actual { color: red; background: none; font-weight: normal; }
|
||||
|
||||
.typeinfo { color: gray; }
|
||||
|
||||
/*# sourceMappingURL=UnitTesting.css.map */
|
||||
|
7
css/UnitTesting.css.map
Normal file
7
css/UnitTesting.css.map
Normal 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"
|
||||
}
|
@ -12,14 +12,14 @@ Used in side panels and action tabs
|
||||
*/
|
||||
.ss-uploadfield .clear { clear: both; }
|
||||
.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 .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 .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 .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.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; }
|
||||
@ -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.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 .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-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; }
|
||||
@ -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.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 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-upload .clear { clear: both; }
|
||||
@ -59,3 +59,5 @@ Used in side panels and action tabs
|
||||
/** ---------------------------------------------------- "@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; }
|
||||
.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
7
css/UploadField.css.map
Normal 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"
|
||||
}
|
@ -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; }
|
||||
|
||||
.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
7
css/debug.css.map
Normal 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"
|
||||
}
|
@ -524,7 +524,8 @@ function exceptionHandler($exception) {
|
||||
$file = $exception->getFile();
|
||||
$line = $exception->getLine();
|
||||
$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_USER_ERROR:
|
||||
default:
|
||||
return Debug::fatalHandler($errno, $errstr, $errfile, $errline, debug_backtrace());
|
||||
Debug::fatalHandler($errno, $errstr, $errfile, $errline, debug_backtrace());
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
@ -42,6 +42,19 @@ class Deprecation {
|
||||
*/
|
||||
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
|
||||
@ -116,20 +129,47 @@ class Deprecation {
|
||||
return $called['function'];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* than or equal to the check version set via ::notification_version
|
||||
*
|
||||
* @static
|
||||
* @param $string - The notice to raise
|
||||
* @param $atVersion - The version at which this notice should start being raised
|
||||
* @param Boolean $scope - Notice relates to the method or class context its called in.
|
||||
* @return void
|
||||
* @param string $atVersion The version at which this notice should start being raised
|
||||
* @param string $string The notice to raise
|
||||
* @param bool $scope Notice relates to the method or class context its called in.
|
||||
*/
|
||||
public static function notice($atVersion, $string = '', $scope = Deprecation::SCOPE_METHOD) {
|
||||
// Never raise deprecation notices in a live environment
|
||||
if(Director::isLive(true)) return;
|
||||
if(!static::get_enabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$checkVersion = self::$version;
|
||||
// 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
|
||||
* @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() {
|
||||
return array(
|
||||
'level' => self::$notice_level,
|
||||
'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
|
||||
* @static
|
||||
* @param $settings array - An array as returned by ::dump_version_settings
|
||||
* @return void
|
||||
*
|
||||
* @param $settings array An array as returned by {@see Deprecation::dump_settings()}
|
||||
*/
|
||||
public static function restore_settings($settings) {
|
||||
self::$notice_level = $settings['level'];
|
||||
self::$version = $settings['version'];
|
||||
self::$module_version_overrides = $settings['moduleVersions'];
|
||||
self::$enabled = $settings['enabled'];
|
||||
}
|
||||
}
|
||||
|
@ -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) {
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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() {
|
||||
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');
|
||||
}
|
||||
|
||||
|
@ -16,7 +16,7 @@
|
||||
* protected $description = "Description"; // description of what it does
|
||||
*
|
||||
* public function run($request) {
|
||||
* if ($request->param('Direction') == 'down') {
|
||||
* if ($request->getVar('Direction') == 'down') {
|
||||
* $this->down();
|
||||
* } else {
|
||||
* $this->up();
|
||||
|
@ -5,12 +5,12 @@ require_once 'TestRunner.php';
|
||||
* Test case class for the Sapphire framework.
|
||||
* Sapphire unit testing is based on PHPUnit, but provides a number of hooks into our data model that make it easier
|
||||
* to work with.
|
||||
*
|
||||
*
|
||||
* @package framework
|
||||
* @subpackage testing
|
||||
*/
|
||||
class SapphireTest extends PHPUnit_Framework_TestCase {
|
||||
|
||||
|
||||
/** @config */
|
||||
private static $dependencies = array(
|
||||
'fixtureFactory' => '%$FixtureFactory',
|
||||
@ -21,7 +21,7 @@ class SapphireTest extends PHPUnit_Framework_TestCase {
|
||||
* If passed as an array, multiple fixture files will be loaded.
|
||||
* Please note that you won't be able to refer with "=>" notation
|
||||
* between the fixtures, they act independent of each other.
|
||||
*
|
||||
*
|
||||
* @var string|array
|
||||
*/
|
||||
protected static $fixture_file = null;
|
||||
@ -30,53 +30,57 @@ class SapphireTest extends PHPUnit_Framework_TestCase {
|
||||
* @var FixtureFactory
|
||||
*/
|
||||
protected $fixtureFactory;
|
||||
|
||||
|
||||
/**
|
||||
* @var bool Set whether to include this test in the TestRunner or to skip this.
|
||||
*/
|
||||
protected $skipTest = false;
|
||||
|
||||
|
||||
/**
|
||||
* @var Boolean If set to TRUE, this will force a test database to be generated
|
||||
* in {@link setUp()}. Note that this flag is overruled by the presence of a
|
||||
* in {@link setUp()}. Note that this flag is overruled by the presence of a
|
||||
* {@link $fixture_file}, which always forces a database build.
|
||||
*/
|
||||
protected $usesDatabase = null;
|
||||
|
||||
|
||||
/**
|
||||
* @deprecated since version 4.0
|
||||
*/
|
||||
protected $originalMailer;
|
||||
|
||||
protected $originalMemberPasswordValidator;
|
||||
protected $originalRequirements;
|
||||
protected $originalIsRunningTest;
|
||||
protected $originalTheme;
|
||||
protected $originalNestedURLsState;
|
||||
protected $originalMemoryLimit;
|
||||
|
||||
|
||||
protected $mailer;
|
||||
|
||||
|
||||
/**
|
||||
* Pointer to the manifest that isn't a test manifest
|
||||
*/
|
||||
protected static $regular_manifest;
|
||||
|
||||
|
||||
/**
|
||||
* @var boolean
|
||||
*/
|
||||
protected static $is_running_test = false;
|
||||
|
||||
|
||||
protected static $test_class_manifest;
|
||||
|
||||
|
||||
/**
|
||||
* By default, setUp() does not require default records. Pass
|
||||
* class names in here, and the require/augment default records
|
||||
* function will be called on them.
|
||||
*/
|
||||
protected $requireDefaultRecordsFrom = array();
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A list of extensions that can't be applied during the execution of this run. If they are
|
||||
* applied, they will be temporarily removed and a database migration called.
|
||||
*
|
||||
*
|
||||
* The keys of the are the classes that the extensions can't be applied the extensions to, and
|
||||
* the values are an array of illegal extensions on that class.
|
||||
*/
|
||||
@ -86,10 +90,10 @@ class SapphireTest extends PHPUnit_Framework_TestCase {
|
||||
/**
|
||||
* A list of extensions that must be applied during the execution of this run. If they are
|
||||
* not applied, they will be temporarily added and a database migration called.
|
||||
*
|
||||
*
|
||||
* The keys of the are the classes to apply the extensions to, and the values are an array
|
||||
* of required extensions on that class.
|
||||
*
|
||||
*
|
||||
* Example:
|
||||
* <code>
|
||||
* array("MyTreeDataObject" => array("Versioned", "Hierarchy"))
|
||||
@ -97,35 +101,35 @@ class SapphireTest extends PHPUnit_Framework_TestCase {
|
||||
*/
|
||||
protected $requiredExtensions = array(
|
||||
);
|
||||
|
||||
|
||||
/**
|
||||
* By default, the test database won't contain any DataObjects that have the interface TestOnly.
|
||||
* This variable lets you define additional TestOnly DataObjects to set up for this test.
|
||||
* Set it to an array of DataObject subclass names.
|
||||
*/
|
||||
protected $extraDataObjects = array();
|
||||
|
||||
|
||||
/**
|
||||
* We need to disabling backing up of globals to avoid overriding
|
||||
* the few globals SilverStripe relies on, like $lang for the i18n subsystem.
|
||||
*
|
||||
*
|
||||
* @see http://sebastian-bergmann.de/archives/797-Global-Variables-and-PHPUnit.html
|
||||
*/
|
||||
protected $backupGlobals = FALSE;
|
||||
|
||||
/**
|
||||
/**
|
||||
* Helper arrays for illegalExtensions/requiredExtensions code
|
||||
*/
|
||||
private $extensionsToReapply = array(), $extensionsToRemove = array();
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Determines if unit tests are currently run (via {@link TestRunner}).
|
||||
* This is used as a cheap replacement for fully mockable state
|
||||
* in certain contiditions (e.g. access checks).
|
||||
* Caution: When set to FALSE, certain controllers might bypass
|
||||
* access checks, so this is a very security sensitive setting.
|
||||
*
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public static function is_running_test() {
|
||||
@ -133,7 +137,7 @@ class SapphireTest extends PHPUnit_Framework_TestCase {
|
||||
}
|
||||
|
||||
public static function set_is_running_test($bool) {
|
||||
self::$is_running_test = $bool;
|
||||
self::$is_running_test = $bool;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -157,20 +161,31 @@ class SapphireTest extends PHPUnit_Framework_TestCase {
|
||||
return static::$fixture_file;
|
||||
}
|
||||
|
||||
/**
|
||||
* @var array $fixtures Array of {@link YamlFixture} instances
|
||||
* @deprecated 3.1 Use $fixtureFactory instad
|
||||
*/
|
||||
protected $fixtures = array();
|
||||
|
||||
protected $model;
|
||||
|
||||
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.
|
||||
if(get_class($this) == "SapphireTest") $this->skipTest = true;
|
||||
|
||||
|
||||
if($this->skipTest) {
|
||||
$this->markTestSkipped(sprintf(
|
||||
'Skipping %s ', get_class($this)
|
||||
));
|
||||
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Mark test as being run
|
||||
$this->originalIsRunningTest = self::$is_running_test;
|
||||
self::$is_running_test = true;
|
||||
@ -179,16 +194,16 @@ class SapphireTest extends PHPUnit_Framework_TestCase {
|
||||
i18n::set_locale(i18n::default_locale());
|
||||
i18n::config()->date_format = null;
|
||||
i18n::config()->time_format = null;
|
||||
|
||||
|
||||
// Set default timezone consistently to avoid NZ-specific dependencies
|
||||
date_default_timezone_set('UTC');
|
||||
|
||||
|
||||
// Remove password validation
|
||||
$this->originalMemberPasswordValidator = Member::password_validator();
|
||||
$this->originalRequirements = Requirements::backend();
|
||||
Member::set_password_validator(null);
|
||||
Config::inst()->update('Cookie', 'report_errors', false);
|
||||
|
||||
|
||||
if(class_exists('RootURLController')) RootURLController::reset();
|
||||
if(class_exists('Translatable')) Translatable::reset();
|
||||
Versioned::reset();
|
||||
@ -210,9 +225,9 @@ class SapphireTest extends PHPUnit_Framework_TestCase {
|
||||
// Set up email
|
||||
$this->originalMailer = Email::mailer();
|
||||
$this->mailer = new TestMailer();
|
||||
Email::set_mailer($this->mailer);
|
||||
Injector::inst()->registerService($this->mailer, 'Mailer');
|
||||
Config::inst()->remove('Email', 'send_all_emails_to');
|
||||
|
||||
|
||||
// Todo: this could be a special test model
|
||||
$this->model = DataModel::inst();
|
||||
|
||||
@ -227,9 +242,9 @@ class SapphireTest extends PHPUnit_Framework_TestCase {
|
||||
}
|
||||
|
||||
singleton('DataObject')->flushCache();
|
||||
|
||||
|
||||
self::empty_temp_db();
|
||||
|
||||
|
||||
foreach($this->requireDefaultRecordsFrom as $className) {
|
||||
$instance = singleton($className);
|
||||
if (method_exists($instance, 'requireDefaultRecords')) $instance->requireDefaultRecords();
|
||||
@ -244,14 +259,14 @@ class SapphireTest extends PHPUnit_Framework_TestCase {
|
||||
foreach($fixtureFiles as $fixtureFilePath) {
|
||||
// Support fixture paths relative to the test class, rather than relative to webroot
|
||||
// String checking is faster than file_exists() calls.
|
||||
$isRelativeToFile = (strpos('/', $fixtureFilePath) === false
|
||||
$isRelativeToFile = (strpos('/', $fixtureFilePath) === false
|
||||
|| preg_match('/^\.\./', $fixtureFilePath));
|
||||
|
||||
if($isRelativeToFile) {
|
||||
$resolvedPath = realpath($pathForClass . '/' . $fixtureFilePath);
|
||||
if($resolvedPath) $fixtureFilePath = $resolvedPath;
|
||||
}
|
||||
|
||||
|
||||
$fixture = Injector::inst()->create('YamlFixture', $fixtureFilePath);
|
||||
$fixture->writeInto($this->getFixtureFactory());
|
||||
$this->fixtures[] = $fixture;
|
||||
@ -261,20 +276,20 @@ class SapphireTest extends PHPUnit_Framework_TestCase {
|
||||
$i++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$this->logInWithPermission("ADMIN");
|
||||
}
|
||||
|
||||
|
||||
// Preserve memory settings
|
||||
$this->originalMemoryLimit = ini_get('memory_limit');
|
||||
|
||||
|
||||
// turn off template debugging
|
||||
Config::inst()->update('SSViewer', 'source_file_comments', false);
|
||||
|
||||
|
||||
// Clear requirements
|
||||
Requirements::clear();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Called once per test case ({@link SapphireTest} subclass).
|
||||
* This is different to {@link setUp()}, which gets called once
|
||||
@ -284,6 +299,10 @@ class SapphireTest extends PHPUnit_Framework_TestCase {
|
||||
* for tearing down the state again.
|
||||
*/
|
||||
public function setUpOnce() {
|
||||
|
||||
//nest config and injector for each suite so they are effectively sandboxed
|
||||
Config::nest();
|
||||
Injector::nest();
|
||||
$isAltered = false;
|
||||
|
||||
if(!Director::isDev()) {
|
||||
@ -314,46 +333,34 @@ class SapphireTest extends PHPUnit_Framework_TestCase {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// If we have made changes to the extensions present, then migrate the database schema.
|
||||
if($isAltered || $this->extensionsToReapply || $this->extensionsToRemove || $this->extraDataObjects) {
|
||||
if(!self::using_temp_db()) self::create_temp_db();
|
||||
$this->resetDBSchema(true);
|
||||
}
|
||||
// clear singletons, they're caching old extension info
|
||||
// clear singletons, they're caching old extension info
|
||||
// which is used in DatabaseAdmin->doBuild()
|
||||
Injector::inst()->unregisterAllObjects();
|
||||
|
||||
// Set default timezone consistently to avoid NZ-specific dependencies
|
||||
date_default_timezone_set('UTC');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* tearDown method that's called once per test class rather once per test method.
|
||||
*/
|
||||
public function tearDownOnce() {
|
||||
// If we have made changes to the extensions present, then migrate the database schema.
|
||||
if($this->extensionsToReapply || $this->extensionsToRemove) {
|
||||
// Remove extensions added for testing
|
||||
foreach($this->extensionsToRemove as $class => $extensions) {
|
||||
foreach($extensions as $extension) {
|
||||
$class::remove_extension($extension);
|
||||
}
|
||||
}
|
||||
//unnest injector / config now that the test suite is over
|
||||
// this will reset all the extensions on the object too (see setUpOnce)
|
||||
Injector::unnest();
|
||||
Config::unnest();
|
||||
|
||||
// Reapply ones removed
|
||||
foreach($this->extensionsToReapply as $class => $extensions) {
|
||||
foreach($extensions as $extension) {
|
||||
$class::add_extension($extension);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if($this->extensionsToReapply || $this->extensionsToRemove || $this->extraDataObjects) {
|
||||
if(!empty($this->extensionsToReapply) || !empty($this->extensionsToRemove) || !empty($this->extraDataObjects)) {
|
||||
$this->resetDBSchema();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return FixtureFactory
|
||||
*/
|
||||
@ -366,10 +373,10 @@ class SapphireTest extends PHPUnit_Framework_TestCase {
|
||||
$this->fixtureFactory = $factory;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the ID of an object from the fixture.
|
||||
*
|
||||
*
|
||||
* @param $className The data class, as specified in your fixture file. Parent classes won't work
|
||||
* @param $identifier The identifier string, as provided in your fixture file
|
||||
* @return int
|
||||
@ -415,12 +422,12 @@ class SapphireTest extends PHPUnit_Framework_TestCase {
|
||||
"Couldn't find object '%s' (class: %s)",
|
||||
$identifier,
|
||||
$className
|
||||
), E_USER_ERROR);
|
||||
), E_USER_ERROR);
|
||||
}
|
||||
|
||||
|
||||
return $obj;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Load a YAML fixture file into the database.
|
||||
* Once loaded, you can use idFromFixture() and objFromFixture() to get items from the fixture.
|
||||
@ -433,18 +440,18 @@ class SapphireTest extends PHPUnit_Framework_TestCase {
|
||||
$fixture->writeInto($this->getFixtureFactory());
|
||||
$this->fixtures[] = $fixture;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Clear all fixtures which were previously loaded through
|
||||
* {@link loadFixture()}
|
||||
* {@link loadFixture()}
|
||||
*/
|
||||
public function clearFixtures() {
|
||||
$this->getFixtureFactory()->clear();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Useful for writing unit tests without hardcoding folder structures.
|
||||
*
|
||||
*
|
||||
* @return String Absolute path to current class.
|
||||
*/
|
||||
protected function getCurrentAbsolutePath() {
|
||||
@ -452,7 +459,7 @@ class SapphireTest extends PHPUnit_Framework_TestCase {
|
||||
if(!$filename) throw new LogicException("getItemPath returned null for " . get_class($this));
|
||||
return dirname($filename);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return String File path relative to webroot
|
||||
*/
|
||||
@ -462,26 +469,23 @@ class SapphireTest extends PHPUnit_Framework_TestCase {
|
||||
if(substr($path,0,strlen($base)) == $base) $path = preg_replace('/^\/*/', '', substr($path,strlen($base)));
|
||||
return $path;
|
||||
}
|
||||
|
||||
|
||||
public function tearDown() {
|
||||
// Preserve memory settings
|
||||
ini_set('memory_limit', ($this->originalMemoryLimit) ? $this->originalMemoryLimit : -1);
|
||||
|
||||
// Restore email configuration
|
||||
if($this->originalMailer) {
|
||||
Email::set_mailer($this->originalMailer);
|
||||
$this->originalMailer = null;
|
||||
}
|
||||
$this->mailer = null;
|
||||
$this->originalMailer = null;
|
||||
$this->mailer = null;
|
||||
|
||||
// Restore password validation
|
||||
if($this->originalMemberPasswordValidator) {
|
||||
Member::set_password_validator($this->originalMemberPasswordValidator);
|
||||
Member::set_password_validator($this->originalMemberPasswordValidator);
|
||||
}
|
||||
|
||||
|
||||
// Restore requirements
|
||||
if($this->originalRequirements) {
|
||||
Requirements::set_backend($this->originalRequirements);
|
||||
Requirements::set_backend($this->originalRequirements);
|
||||
}
|
||||
|
||||
// Mark test as no longer being run - we use originalIsRunningTest to allow for nested SapphireTest calls
|
||||
@ -490,15 +494,18 @@ class SapphireTest extends PHPUnit_Framework_TestCase {
|
||||
|
||||
// Reset mocked datetime
|
||||
SS_Datetime::clear_mock_now();
|
||||
|
||||
|
||||
// Stop the redirection that might have been requested in the test.
|
||||
// Note: Ideally a clean Controller should be created for each test.
|
||||
// Note: Ideally a clean Controller should be created for each test.
|
||||
// Now all tests executed in a batch share the same controller.
|
||||
$controller = Controller::has_curr() ? Controller::curr() : null;
|
||||
if ( $controller && $controller->response && $controller->response->getHeader('Location') ) {
|
||||
$controller->response->setStatusCode(200);
|
||||
$controller->response->removeHeader('Location');
|
||||
}
|
||||
//unnest injector / config now that tests are over
|
||||
Injector::unnest();
|
||||
Config::unnest();
|
||||
}
|
||||
|
||||
public static function assertContains(
|
||||
@ -545,7 +552,7 @@ class SapphireTest extends PHPUnit_Framework_TestCase {
|
||||
public function findEmail($to, $from = null, $subject = null, $content = null) {
|
||||
return $this->mailer->findEmail($to, $from, $subject, $content);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Assert that the matching email was sent since the last call to clearEmails()
|
||||
* All of the parameters can either be a string, or, if they start with "/", a PREG-compatible regular expression.
|
||||
@ -577,7 +584,7 @@ class SapphireTest extends PHPUnit_Framework_TestCase {
|
||||
/**
|
||||
* Assert that the given {@link SS_List} includes DataObjects matching the given key-value
|
||||
* pairs. Each match must correspond to 1 distinct record.
|
||||
*
|
||||
*
|
||||
* @param $matches The patterns to match. Each pattern is a map of key-value pairs. You can
|
||||
* either pass a single pattern or an array of patterns.
|
||||
* @param $dataObjectSet The {@link SS_List} to test.
|
||||
@ -585,19 +592,19 @@ class SapphireTest extends PHPUnit_Framework_TestCase {
|
||||
* Examples
|
||||
* --------
|
||||
* Check that $members includes an entry with Email = sam@example.com:
|
||||
* $this->assertDOSContains(array('Email' => '...@example.com'), $members);
|
||||
*
|
||||
* Check that $members includes entries with Email = sam@example.com and with
|
||||
* $this->assertDOSContains(array('Email' => '...@example.com'), $members);
|
||||
*
|
||||
* Check that $members includes entries with Email = sam@example.com and with
|
||||
* Email = ingo@example.com:
|
||||
* $this->assertDOSContains(array(
|
||||
* array('Email' => '...@example.com'),
|
||||
* array('Email' => 'i...@example.com'),
|
||||
* ), $members);
|
||||
* $this->assertDOSContains(array(
|
||||
* array('Email' => '...@example.com'),
|
||||
* array('Email' => 'i...@example.com'),
|
||||
* ), $members);
|
||||
*/
|
||||
public function assertDOSContains($matches, $dataObjectSet) {
|
||||
$extracted = array();
|
||||
foreach($dataObjectSet as $item) $extracted[] = $item->toMap();
|
||||
|
||||
|
||||
foreach($matches as $match) {
|
||||
$matched = false;
|
||||
foreach($extracted as $i => $item) {
|
||||
@ -613,35 +620,35 @@ class SapphireTest extends PHPUnit_Framework_TestCase {
|
||||
$this->assertTrue(
|
||||
$matched,
|
||||
"Failed asserting that the SS_List contains an item matching "
|
||||
. var_export($match, true) . "\n\nIn the following SS_List:\n"
|
||||
. var_export($match, true) . "\n\nIn the following SS_List:\n"
|
||||
. $this->DOSSummaryForMatch($dataObjectSet, $match)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert that the given {@link SS_List} includes only DataObjects matching the given
|
||||
* Assert that the given {@link SS_List} includes only DataObjects matching the given
|
||||
* key-value pairs. Each match must correspond to 1 distinct record.
|
||||
*
|
||||
*
|
||||
* @param $matches The patterns to match. Each pattern is a map of key-value pairs. You can
|
||||
* either pass a single pattern or an array of patterns.
|
||||
* @param $dataObjectSet The {@link SS_List} to test.
|
||||
*
|
||||
* Example
|
||||
* --------
|
||||
* Check that *only* the entries Sam Minnee and Ingo Schommer exist in $members. Order doesn't
|
||||
* Check that *only* the entries Sam Minnee and Ingo Schommer exist in $members. Order doesn't
|
||||
* matter:
|
||||
* $this->assertDOSEquals(array(
|
||||
* array('FirstName' =>'Sam', 'Surname' => 'Minnee'),
|
||||
* array('FirstName' => 'Ingo', 'Surname' => 'Schommer'),
|
||||
* ), $members);
|
||||
* $this->assertDOSEquals(array(
|
||||
* array('FirstName' =>'Sam', 'Surname' => 'Minnee'),
|
||||
* array('FirstName' => 'Ingo', 'Surname' => 'Schommer'),
|
||||
* ), $members);
|
||||
*/
|
||||
public function assertDOSEquals($matches, $dataObjectSet) {
|
||||
if(!$dataObjectSet) return false;
|
||||
|
||||
|
||||
$extracted = array();
|
||||
foreach($dataObjectSet as $item) $extracted[] = $item->toMap();
|
||||
|
||||
|
||||
foreach($matches as $match) {
|
||||
$matched = false;
|
||||
foreach($extracted as $i => $item) {
|
||||
@ -657,11 +664,11 @@ class SapphireTest extends PHPUnit_Framework_TestCase {
|
||||
$this->assertTrue(
|
||||
$matched,
|
||||
"Failed asserting that the SS_List contains an item matching "
|
||||
. var_export($match, true) . "\n\nIn the following SS_List:\n"
|
||||
. var_export($match, true) . "\n\nIn the following SS_List:\n"
|
||||
. $this->DOSSummaryForMatch($dataObjectSet, $match)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
// If we have leftovers than the DOS has extra data that shouldn't be there
|
||||
$this->assertTrue(
|
||||
(count($extracted) == 0),
|
||||
@ -669,19 +676,19 @@ class SapphireTest extends PHPUnit_Framework_TestCase {
|
||||
"Failed asserting that the SS_List contained only the given items, the "
|
||||
. "following items were left over:\n" . var_export($extracted, true)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert that the every record in the given {@link SS_List} matches the given key-value
|
||||
* pairs.
|
||||
*
|
||||
*
|
||||
* @param $match The pattern to match. The pattern is a map of key-value pairs.
|
||||
* @param $dataObjectSet The {@link SS_List} to test.
|
||||
*
|
||||
* Example
|
||||
* --------
|
||||
* Check that every entry in $members has a Status of 'Active':
|
||||
* $this->assertDOSAllMatch(array('Status' => 'Active'), $members);
|
||||
* $this->assertDOSAllMatch(array('Status' => 'Active'), $members);
|
||||
*/
|
||||
public function assertDOSAllMatch($match, $dataObjectSet) {
|
||||
$extracted = array();
|
||||
@ -690,7 +697,7 @@ class SapphireTest extends PHPUnit_Framework_TestCase {
|
||||
foreach($extracted as $i => $item) {
|
||||
$this->assertTrue(
|
||||
$this->dataObjectArrayMatch($item, $match),
|
||||
"Failed asserting that the the following item matched "
|
||||
"Failed asserting that the the following item matched "
|
||||
. var_export($match, true) . ": " . var_export($item, true)
|
||||
);
|
||||
}
|
||||
@ -763,7 +770,6 @@ class SapphireTest extends PHPUnit_Framework_TestCase {
|
||||
|
||||
$this->assertNotContains($needleSQL, $haystackSQL, $message, $ignoreCase, $checkForObjectIdentity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function for the DOS matchers
|
||||
*/
|
||||
@ -792,14 +798,14 @@ class SapphireTest extends PHPUnit_Framework_TestCase {
|
||||
return $dbConn && (substr($dbConn->getSelectedDatabase(), 0, strlen($prefix) + 5)
|
||||
== strtolower(sprintf('%stmpdb', $prefix)));
|
||||
}
|
||||
|
||||
|
||||
public static function kill_temp_db() {
|
||||
// Delete our temporary database
|
||||
if(self::using_temp_db()) {
|
||||
$dbConn = DB::get_conn();
|
||||
$dbName = $dbConn->getSelectedDatabase();
|
||||
if($dbName && DB::get_conn()->databaseExists($dbName)) {
|
||||
// Some DataExtensions keep a static cache of information that needs to
|
||||
// Some DataExtensions keep a static cache of information that needs to
|
||||
// be reset whenever the database is killed
|
||||
foreach(ClassInfo::subclassesFor('DataExtension') as $class) {
|
||||
$toCall = array($class, 'on_db_reset');
|
||||
@ -811,15 +817,15 @@ class SapphireTest extends PHPUnit_Framework_TestCase {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Remove all content from the temporary database.
|
||||
*/
|
||||
public static function empty_temp_db() {
|
||||
if(self::using_temp_db()) {
|
||||
DB::get_conn()->clearAllData();
|
||||
|
||||
// Some DataExtensions keep a static cache of information that needs to
|
||||
|
||||
// Some DataExtensions keep a static cache of information that needs to
|
||||
// be reset whenever the database is cleaned out
|
||||
$classes = array_merge(ClassInfo::subclassesFor('DataExtension'), ClassInfo::subclassesFor('DataObject'));
|
||||
foreach($classes as $class) {
|
||||
@ -828,7 +834,7 @@ class SapphireTest extends PHPUnit_Framework_TestCase {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static function create_temp_db() {
|
||||
// Disable PHPUnit error handling
|
||||
restore_error_handler();
|
||||
@ -848,13 +854,13 @@ class SapphireTest extends PHPUnit_Framework_TestCase {
|
||||
|
||||
$st = Injector::inst()->create('SapphireTest');
|
||||
$st->resetDBSchema();
|
||||
|
||||
|
||||
// Reinstate PHPUnit error handling
|
||||
set_error_handler(array('PHPUnit_Util_ErrorHandler', 'handleError'));
|
||||
|
||||
|
||||
return $dbname;
|
||||
}
|
||||
|
||||
|
||||
public static function delete_all_temp_dbs() {
|
||||
$prefix = defined('SS_DATABASE_PREFIX') ? SS_DATABASE_PREFIX : 'ss_';
|
||||
foreach(DB::get_schema()->databaseList() as $dbName) {
|
||||
@ -869,7 +875,7 @@ class SapphireTest extends PHPUnit_Framework_TestCase {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Reset the testing database's schema.
|
||||
* @param $includeExtraDataObjects If true, the extraDataObjects tables will also be included
|
||||
@ -909,7 +915,7 @@ class SapphireTest extends PHPUnit_Framework_TestCase {
|
||||
singleton('DataObject')->flushCache();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create a member and group with the given permission code, and log in with it.
|
||||
* Returns the member ID.
|
||||
@ -924,25 +930,25 @@ class SapphireTest extends PHPUnit_Framework_TestCase {
|
||||
$permission->Code = $permCode;
|
||||
$permission->write();
|
||||
$group->Permissions()->add($permission);
|
||||
|
||||
|
||||
$member = DataObject::get_one('Member', array(
|
||||
'"Member"."Email"' => "$permCode@example.org"
|
||||
));
|
||||
if(!$member) $member = Injector::inst()->create('Member');
|
||||
|
||||
|
||||
$member->FirstName = $permCode;
|
||||
$member->Surname = "User";
|
||||
$member->Email = "$permCode@example.org";
|
||||
$member->write();
|
||||
$group->Members()->add($member);
|
||||
|
||||
|
||||
$this->cache_generatedMembers[$permCode] = $member;
|
||||
}
|
||||
|
||||
|
||||
$this->cache_generatedMembers[$permCode]->logIn();
|
||||
return $this->cache_generatedMembers[$permCode]->ID;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Cache for logInWithPermission()
|
||||
*/
|
||||
|
@ -415,6 +415,13 @@ class InstallRequirements {
|
||||
"Is the mysite/_config.php file writeable?",
|
||||
null
|
||||
));
|
||||
|
||||
$this->requireWriteable('mysite/_config/config.yml', array(
|
||||
"File permissions",
|
||||
"Is the mysite/_config/config.yml file writeable?",
|
||||
null
|
||||
));
|
||||
|
||||
if(!$this->checkModuleExists('cms')) {
|
||||
$this->requireWriteable('mysite/code/RootURLController.php', array(
|
||||
"File permissions",
|
||||
|
@ -8,7 +8,7 @@ Our web-based [PHP installer](installation/) can check if you meet the requireme
|
||||
|
||||
## 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).
|
||||
* 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.
|
||||
|
@ -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_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_DEPRECATION_ENABLED` | Enable deprecation notices for this environment.|
|
||||
| `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_PASSWORD`| The password of the default admin. This will not be stored in the database.|
|
||||
|
@ -9,8 +9,8 @@ lets you install and upgrade SilverStripe and its modules. Although installing
|
||||
|
||||
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 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 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.
|
||||
|
||||
## 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:
|
||||
|
||||
* [Install using Apache](webserver) - our preferred platform
|
||||
* [Install using Lighttpd](lighttpd) - fast, but a bit tricker to get going
|
||||
* [Install using Nginx](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 Apache](installation) - our preferred platform
|
||||
* [Install using Lighttpd](installation/how_to/configure_lighttpd) - fast, but a bit trickier to get going
|
||||
* [Install using Nginx](installation/how_to/configure_nginx) - Super fast at serving static files. Great for large traffic sites.
|
||||
* [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
|
||||
|
||||
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:
|
||||
|
||||
* [Install using IIS](windows-manual-iis)
|
||||
* [Install using Apache/WAMP](windows-wamp)
|
||||
* [Install using IIS](installation/other_installation_options/windows_iis7)
|
||||
* [Install using Apache/WAMP](installation/windows)
|
||||
|
||||
### Mac OS X
|
||||
|
||||
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)
|
||||
|
||||
### Virtual Machines through Vagrant
|
||||
@ -61,10 +61,9 @@ ready for download or installation on a cloud platform.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
If you run into trouble, see [common-problems](common-problems) or post to the
|
||||
[SilverStripe forums](http://silverstripe.com/silverstripe-forum/).
|
||||
If you run into trouble, see [common-problems](installation/common_problems) or post to the
|
||||
[SilverStripe forums](http://silverstripe.org/community/forums/).
|
||||
|
||||
## Related
|
||||
|
||||
* [Module installation](../topics/modules)
|
||||
* [Suggested web hosts](http://doc.silverstripe.org/old/suggested-web-hosts)
|
||||
* [Module installation](/developer_guides/extending/modules)
|
||||
|
@ -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 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 18: Dealing with Arbitrary Template Data](http://www.silverstripe.org/learn/lessons/dealing-with-arbitrary-template-data)
|
||||
|
||||
## Help: If you get stuck
|
||||
|
||||
|
@ -493,4 +493,4 @@ for adding notes for other developers but for things you don't want published in
|
||||
## API Documentation
|
||||
|
||||
* [api:SSViewer]
|
||||
* [api:SS_TemplateManifest]
|
||||
* [api:SS_TemplateManifest]
|
||||
|
@ -80,7 +80,7 @@ does, such as `ArrayData` or `ArrayList`.
|
||||
'Name' => 'John',
|
||||
'Role' => 'Head Coach',
|
||||
'Experience' => $experience
|
||||
))->renderWith("AjaxTemplate");
|
||||
)))->renderWith("AjaxTemplate");
|
||||
} else {
|
||||
return $this->httpError(404);
|
||||
}
|
||||
|
@ -56,7 +56,7 @@ First we need to define a callback for the shortcode.
|
||||
'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.";
|
||||
}
|
||||
}
|
||||
|
@ -182,18 +182,10 @@ end of each test.
|
||||
$page->publish('Stage', 'Live');
|
||||
}
|
||||
|
||||
// reset configuration for the test.
|
||||
Config::nest();
|
||||
// set custom configuration for the test.
|
||||
Config::inst()->update('Foo', 'bar', 'Hello!');
|
||||
}
|
||||
|
||||
public function tearDown() {
|
||||
// restores the config variables
|
||||
Config::unnest();
|
||||
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
public function testMyMethod() {
|
||||
// ..
|
||||
}
|
||||
@ -223,6 +215,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
|
||||
|
||||
@ -266,4 +284,4 @@ some `thirdparty/` directories add the following to the `phpunit.xml` configurat
|
||||
|
||||
* [api:TestRunner]
|
||||
* [api:SapphireTest]
|
||||
* [api:FunctionalTest]
|
||||
* [api:FunctionalTest]
|
||||
|
@ -117,14 +117,16 @@ To use this backend, you need a memcached daemon and the memcache PECL extension
|
||||
'primary_memcached',
|
||||
'Memcached',
|
||||
array(
|
||||
'host' => 'localhost',
|
||||
'port' => 11211,
|
||||
'persistent' => true,
|
||||
'weight' => 1,
|
||||
'timeout' => 5,
|
||||
'retry_interval' => 15,
|
||||
'status' => true,
|
||||
'failure_callback' => ''
|
||||
'servers' => array(
|
||||
'host' => 'localhost',
|
||||
'port' => 11211,
|
||||
'persistent' => true,
|
||||
'weight' => 1,
|
||||
'timeout' => 5,
|
||||
'retry_interval' => 15,
|
||||
'status' => true,
|
||||
'failure_callback' => ''
|
||||
)
|
||||
)
|
||||
);
|
||||
SS_Cache::pick_backend('primary_memcached', 'any', 10);
|
||||
|
@ -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.
|
||||
</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:FulltextSearchable]
|
@ -1,6 +1,6 @@
|
||||
# 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,
|
||||
SilverStripe will automatically create a new `[api:CMSMenuItem]` for it
|
||||
|
@ -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.
|
||||
|
||||
* "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.
|
||||
* 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.
|
||||
|
@ -55,7 +55,7 @@
|
||||
* 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: `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
|
||||
* `DataObject::doValidate()` method visibility added to access `DataObject::validate` externally
|
||||
* `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
|
||||
UI layout. A new template, Security_MultiAuthenticatorLogin.ss is available.
|
||||
* 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.
|
||||
* `SearchForm::getSearchQuery` no longer pre-escapes search keywords and must
|
||||
be cast in your template
|
||||
@ -455,6 +457,26 @@ After:
|
||||
$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:
|
||||
|
||||
|
||||
@ -463,7 +485,8 @@ Alternatively:
|
||||
$query = SQLQuery::create()
|
||||
->setFrom('"SiteTree"')
|
||||
->setWhere(array('"SiteTree"."ShowInMenus"' => 0))
|
||||
->toDelete();
|
||||
->setDelete(true)
|
||||
->toAppropriateExpression();
|
||||
$query->execute();
|
||||
|
||||
|
||||
@ -616,12 +639,14 @@ E.g.
|
||||
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()`
|
||||
will not always equate with that in FrameWork 3.1. Once this would be a list of strings, what will
|
||||
now be returned is a list of conditions, each of which is an associative array mapping the
|
||||
condition string to a list of parameters provided.
|
||||
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:
|
||||
|
||||
@ -630,6 +655,7 @@ Before:
|
||||
<?php
|
||||
|
||||
// Increment value of a single condition
|
||||
$query = new SQLQuery(/*...*/);
|
||||
$conditions = $query->getWhere();
|
||||
$new = array();
|
||||
foreach($conditions as $condition) {
|
||||
@ -646,6 +672,7 @@ After:
|
||||
|
||||
:::php
|
||||
// Increment value of a single condition
|
||||
$query = new SQLSelect(/*...*/);
|
||||
$conditions = $query->getWhere();
|
||||
$new = array();
|
||||
foreach($conditions as $condition) {
|
||||
@ -665,7 +692,7 @@ replace this method call with the new `getWhereParameterised($parameters)` metho
|
||||
applicable.
|
||||
|
||||
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
|
||||
through the `$parameters` argument which is passed by reference.
|
||||
|
||||
|
923
docs/en/04_Changelogs/beta/3.2.0-beta1.md
Normal file
923
docs/en/04_Changelogs/beta/3.2.0-beta1.md
Normal 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 -> 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)
|
@ -1,11 +1,11 @@
|
||||
title: Changelogs
|
||||
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).
|
||||
|
||||
We also keep an overview of [security-related releases](http://silverstripe.org/security-releases/).
|
||||
|
||||
For information on how to upgrade to newer versions consult the [upgrading](/upgrading) guide.
|
||||
|
||||
[CHILDREN]
|
||||
[CHILDREN]
|
||||
|
@ -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-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
|
||||
|
@ -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
|
||||
|
||||
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
|
||||
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
|
||||
|
||||
_**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)".
|
||||
|
||||
@ -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]
|
||||
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).
|
||||
|
||||
git fetch upstream
|
||||
git rebase -i upstream/3.1
|
||||
git rebase -i upstream/3.2
|
||||
|
||||
7. Push release candidate branch to GitHub
|
||||
|
||||
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
|
||||
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:
|
||||
|
||||
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)
|
||||
3. Effort - How much effort is required to fix this issue?
|
||||
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
|
||||
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)
|
||||
3. *Effort* - How much effort is required to fix this issue?
|
||||
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
|
||||
|
||||
| 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/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
|
||||
|
||||
@ -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.
|
||||
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.
|
||||
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,
|
||||
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).
|
||||
* **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
|
||||
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).
|
||||
|
||||
### 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
|
||||
|
||||
* 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)
|
||||
* 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
|
||||
* Write [unit tests](../developer_guides/testing/unit_testing)
|
||||
* 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
|
||||
* It's better to submit multiple patches with separate bits of functionality than a big patch containing lots of
|
||||
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.
|
||||
* Document your code inline through [PHPDoc](http://en.wikipedia.org/wiki/PHPDoc) syntax. See our
|
||||
* 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)
|
||||
* 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
|
||||
* Write [unit tests](../developer_guides/testing/unit_testing)
|
||||
* 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
|
||||
* It's better to submit multiple patches with separate bits of functionality than a big patch containing lots of 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.
|
||||
* 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.
|
||||
* 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)
|
||||
* 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
|
||||
|
||||
@ -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.
|
||||
Please prefix **noteworthy** commit messages with one of the following tags:
|
||||
|
||||
* `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.
|
||||
Includes any change developers should be aware of when upgrading.
|
||||
* `BUG`: Bugfix or minor enhancement on something developers or users are likely to encounter.
|
||||
* `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. Includes any change developers should be aware of when upgrading.
|
||||
* `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
|
||||
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
|
||||
|
||||
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 branch 100-dataobject-get-one
|
||||
$ git checkout 100-dataobject-get-one
|
||||
$ git checkout -b 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.
|
||||
|
||||
$ 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.
|
||||
|
||||
@ -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:
|
||||
|
||||
# [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 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:
|
||||
|
||||
# [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 checkout master
|
||||
$ git rebase upstream/master
|
||||
@ -247,11 +248,15 @@ To squash four commits into one, do the following:
|
||||
|
||||
$ 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:
|
||||
|
||||
$ 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:
|
||||
|
||||
@ -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'
|
||||
|
||||
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?
|
||||
|
||||
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:
|
||||
|
||||
1---2---3 #212-my-new-plugin
|
||||
/
|
||||
A---B #master
|
||||
1---2---3 #212-my-new-plugin
|
||||
/
|
||||
A---B #master
|
||||
|
||||
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
|
||||
/
|
||||
A---B--C--D--E--F #master
|
||||
1---2---3 #212-my-new-plugin
|
||||
/
|
||||
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.
|
||||
|
||||
@ -297,9 +304,9 @@ git rebase master 212-my-new-plugin
|
||||
|
||||
git will rewrite your commits like this:
|
||||
|
||||
1---2---3 #212-my-new-plugin
|
||||
/
|
||||
A---B--C--D--E--F #master
|
||||
1---2---3 #212-my-new-plugin
|
||||
/
|
||||
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).
|
||||
|
||||
|
@ -2,7 +2,7 @@ summary: Describes the process followed for "core" releases.
|
||||
|
||||
# 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
|
||||
|
||||
@ -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
|
||||
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
|
||||
|
||||
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
|
||||
the *3.1.1* release contained new features rather than just bugfixes.
|
||||
|
||||
@ -43,7 +43,7 @@ patch release
|
||||
## Deprecation
|
||||
|
||||
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.
|
||||
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".
|
||||
@ -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.
|
||||
* 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.
|
||||
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 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.
|
||||
* 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))
|
||||
* 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
|
||||
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.
|
||||
* 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.
|
||||
* 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()`:
|
||||
|
||||
@ -77,8 +75,27 @@ Here's an example for replacing `Director::isDev()` with a (theoretical) `Env::i
|
||||
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
|
||||
(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.
|
||||
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*), 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
|
||||
|
||||
@ -104,7 +121,7 @@ previous major release (if applicable).
|
||||
[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
|
||||
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.
|
||||
|
||||
### Severity rating
|
||||
@ -114,7 +131,7 @@ each vulnerability. The rating indicates how important an update is:
|
||||
|
||||
| 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.* |
|
||||
| **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.* |
|
||||
|
@ -3,31 +3,30 @@ summary: Writing guide for contributing to SilverStripe developer and CMS user h
|
||||
|
||||
# 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
|
||||
[Markdown](http://daringfireball.net/projects/markdown/) knowledge, and a GitHub user account.
|
||||
Modifying documentation requires basic knowledge of [PHPDoc](http://en.wikipedia.org/wiki/PHPDoc) and
|
||||
[Markdown](http://daringfireball.net/projects/markdown/) as well as a GitHub user account.
|
||||
|
||||
## 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
|
||||
[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 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
|
||||
* documentation changes are for and then submit the form. Your changes will be sent to the core committers for approval.
|
||||
* 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.
|
||||
|
||||
<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>
|
||||
|
||||
## Editing on your computer
|
||||
|
||||
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-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.
|
||||
|
||||
@ -37,8 +36,8 @@ If you submit a new feature or an API change, we strongly recommend that your pa
|
||||
|
||||
## Repositories
|
||||
|
||||
* End-user: [userhelp.silverstripe.org](http://github.com/silverstripe/userhelp.silverstripe.org)
|
||||
* Developer guides: [doc.silverstripe.org](http://github.com/silverstripe/doc.silverstripe.org)
|
||||
* End-user help: [userhelp.silverstripe.org](http://github.com/silverstripe/userhelp.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)
|
||||
|
||||
## Source control
|
||||
@ -52,12 +51,12 @@ for documenting open source software.
|
||||
|
||||
## 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?
|
||||
* 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 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`).
|
||||
|
||||
## 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.
|
||||
* 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.
|
||||
* 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
|
||||
documenting, there shouldn't be any "frequently asked questions" left.
|
||||
* "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).
|
||||
* 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.
|
||||
* 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
|
||||
|
||||
@ -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).
|
||||
</div>
|
||||
|
||||
Code:
|
||||
Code for a Tip box:
|
||||
|
||||
<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.
|
||||
</div>
|
||||
|
||||
Code:
|
||||
Code for a Notification box:
|
||||
|
||||
<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.
|
||||
</div>
|
||||
|
||||
Code:
|
||||
Code for a Warning box:
|
||||
|
||||
<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
|
||||
[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
|
||||
|
||||
|
@ -1,12 +1,12 @@
|
||||
title: Implement Internationalization
|
||||
summary: Implement SilverStripe's internationalization system in your own modules.
|
||||
title: Implement Internationalisation
|
||||
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).
|
||||
|
||||
## Set up your own module for localization
|
||||
## Set up your own module for localisation
|
||||
|
||||
### Collecting translatable text
|
||||
|
||||
@ -33,7 +33,7 @@ source_lang = en
|
||||
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.
|
||||
|
||||
### 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
|
||||
(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
|
||||
[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
|
||||
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
|
||||
[buildtools](https://github.com/silverstripe/silverstripe-buildtools).
|
||||
|
||||
@ -130,4 +130,4 @@ files back into the JS files SilverStripe can actually read. This requires an in
|
||||
* [i18n](/developer_guides/i18n/): Developer-level documentation of Silverstripe's i18n capabilities
|
||||
* [Contributing Translations](/contributing/translations): Information for translators looking to contribute translations of the SilverStripe UI.
|
||||
* [translatable](https://github.com/silverstripe/silverstripe-translatable): DataObject-interface powering the website-content translations
|
||||
* ["Translatable ModelAdmin" module](http://silverstripe.org/translatablemodeladmin-module/): An extension which allows translations of DataObjects inside ModelAdmin
|
||||
* ["Translatable ModelAdmin" module](http://silverstripe.org/translatablemodeladmin-module/): An extension which allows translations of DataObjects inside ModelAdmin
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
* 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.
|
||||
* The community is first and foremost about people sharing their knowledge to build something great together.
|
||||
|
||||
## Be empathetic, friendly and considerate.
|
||||
* Welcome and encourage participation from everyone that wishes to join our community.
|
||||
* Remember growing the community socially is as important as code related matters.
|
||||
* Welcome and encourage participation from everyone that wishes to join and contribute to our community.
|
||||
* 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.
|
||||
* Keep your actions lawful, non-discriminatory, and unthreatening towards others.
|
||||
|
||||
## Respect the reader’s time.
|
||||
* 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.
|
||||
* 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.
|
||||
* 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 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.
|
||||
* 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.
|
||||
* 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 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.
|
||||
* 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 who have to solve similar problems.
|
||||
* 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
|
||||
* 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.
|
||||
* Involve a 3rd party or if necessary inform a core committer if conflicts continue 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).
|
||||
* 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.
|
||||
* 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. If necessary, inform a core committer if a conflict continues to escalate.
|
||||
* 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 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.
|
||||
* Call others on their behaviour within the community and remind them of this code where necessary.
|
||||
* Refer to [these helpful guides on conflict resolution](http://www.crnhq.org/pages.php?pID=10) to aid you.
|
||||
* 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 when dealing with a conflict.
|
||||
|
||||
## 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.
|
||||
|
||||
|
@ -62,10 +62,10 @@ class Email extends ViewableData {
|
||||
protected $bcc;
|
||||
|
||||
/**
|
||||
* @deprecated since version 3.3
|
||||
* @deprecated since version 4.0
|
||||
*/
|
||||
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');
|
||||
}
|
||||
|
||||
@ -162,7 +162,7 @@ class Email extends ViewableData {
|
||||
if($bcc != null) $this->bcc = $bcc;
|
||||
|
||||
if($bounceHandlerURL != null) {
|
||||
Deprecation::notice('3.1', 'Use "emailbouncehandler" module');
|
||||
Deprecation::notice('4.0', 'Use "emailbouncehandler" module');
|
||||
}
|
||||
|
||||
parent::__construct();
|
||||
@ -177,8 +177,11 @@ class Email extends ViewableData {
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since version 4.0
|
||||
*/
|
||||
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) {
|
||||
@ -538,20 +541,20 @@ class Email extends ViewableData {
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated 3.2 Use the "Email.admin_email" config setting instead
|
||||
* @deprecated 4.0 Use the "Email.admin_email" config setting instead
|
||||
* @return string
|
||||
*/
|
||||
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');
|
||||
}
|
||||
|
||||
@ -562,10 +565,10 @@ class Email extends ViewableData {
|
||||
*
|
||||
* 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) {
|
||||
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);
|
||||
}
|
||||
|
||||
@ -580,10 +583,10 @@ class Email extends ViewableData {
|
||||
*
|
||||
* 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) {
|
||||
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);
|
||||
}
|
||||
|
||||
@ -598,10 +601,10 @@ class Email extends ViewableData {
|
||||
*
|
||||
* 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) {
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -477,17 +477,17 @@ class Mailer extends Object {
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since version 3.2.0
|
||||
* @deprecated since version 4.0
|
||||
*/
|
||||
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) {
|
||||
Deprecation::notice('3.2.0', 'wrapImagesInline_rewriter is deprecated');
|
||||
Deprecation::notice('4.0', 'wrapImagesInline_rewriter is deprecated');
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -140,10 +140,10 @@ class File extends DataObject {
|
||||
private static $app_categories = array(
|
||||
'audio' => array(
|
||||
"aif" ,"au" ,"mid" ,"midi" ,"mp3" ,"ra" ,"ram" ,"rm","mp3" ,"wav" ,"m4a" ,"snd" ,"aifc" ,"aiff" ,"wma",
|
||||
"apl", "avr" ,"cda" ,"mp4" ,"ogg"
|
||||
"apl", "avr" ,"cda" ,"ogg"
|
||||
),
|
||||
'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(
|
||||
"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.
|
||||
*
|
||||
@ -293,7 +302,7 @@ class File extends DataObject {
|
||||
// ensure that the record is synced with the filesystem before deleting
|
||||
$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());
|
||||
}
|
||||
}
|
||||
@ -637,7 +646,7 @@ class File extends DataObject {
|
||||
|
||||
// If it's changed, check for duplicates
|
||||
if($oldName && $oldName != $name) {
|
||||
$base = pathinfo($name, PATHINFO_BASENAME);
|
||||
$base = pathinfo($name, PATHINFO_FILENAME);
|
||||
$ext = self::get_file_extension($name);
|
||||
$suffix = 1;
|
||||
|
||||
@ -649,7 +658,7 @@ class File extends DataObject {
|
||||
))->first()
|
||||
) {
|
||||
$suffix++;
|
||||
$name = "$base-$suffix$ext";
|
||||
$name = "$base-$suffix.$ext";
|
||||
}
|
||||
}
|
||||
|
||||
@ -836,7 +845,7 @@ class File extends DataObject {
|
||||
'htm' => _t('File.HtmlType', 'HTML file')
|
||||
);
|
||||
|
||||
$ext = $this->getExtension();
|
||||
$ext = strtolower($this->getExtension());
|
||||
|
||||
return isset($types[$ext]) ? $types[$ext] : 'unknown';
|
||||
}
|
||||
|
@ -25,11 +25,11 @@ class GDBackend extends Object implements Image_Backend {
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
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) {
|
||||
config::inst()->update('GDBackend', 'default_quality', (int) $quality);
|
||||
}
|
||||
@ -569,10 +569,10 @@ class GDBackend extends Object implements Image_Backend {
|
||||
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) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -43,12 +43,12 @@ class ImagickBackend extends Imagick implements Image_Backend {
|
||||
/**
|
||||
* 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
|
||||
* @return void
|
||||
*/
|
||||
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) {
|
||||
Config::inst()->update('ImagickBackend', 'default_quality', (int) $quality);
|
||||
}
|
||||
|
@ -96,8 +96,12 @@ class CheckboxSetField extends OptionsetField {
|
||||
}
|
||||
}
|
||||
} elseif($values && is_string($values)) {
|
||||
$items = explode(',', $values);
|
||||
$items = str_replace('{comma}', ',', $items);
|
||||
if(!empty($values)) {
|
||||
$items = explode(',', $values);
|
||||
$items = str_replace('{comma}', ',', $items);
|
||||
} else {
|
||||
$items = array();
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@ -109,8 +113,12 @@ class CheckboxSetField extends OptionsetField {
|
||||
$items = array();
|
||||
}
|
||||
else {
|
||||
$items = explode(',', $values);
|
||||
$items = str_replace('{comma}', ',', $items);
|
||||
if(!empty($values)) {
|
||||
$items = explode(',', $values);
|
||||
$items = str_replace('{comma}', ',', $items);
|
||||
} else {
|
||||
$items = array();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -120,7 +120,7 @@ class ConfirmedPasswordField extends FormField {
|
||||
/**
|
||||
* @param array $properties
|
||||
*
|
||||
* @return string
|
||||
* @return HTMLText
|
||||
*/
|
||||
public function Field($properties = array()) {
|
||||
Requirements::javascript(FRAMEWORK_DIR . '/thirdparty/jquery/jquery.js');
|
||||
|
@ -31,6 +31,8 @@ class DatalessField extends FormField {
|
||||
/**
|
||||
* Returns the field's representation in the form.
|
||||
* For dataless fields, this defaults to $Field.
|
||||
*
|
||||
* @return HTMLText
|
||||
*/
|
||||
public function FieldHolder($properties = array()) {
|
||||
return $this->Field($properties);
|
||||
|
@ -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 mixed $v
|
||||
* @return boolean
|
||||
*/
|
||||
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));
|
||||
}
|
||||
|
||||
|
@ -63,7 +63,7 @@ class DatetimeField extends FormField {
|
||||
->addExtraClass('fieldgroup-field');
|
||||
$this->timeField = TimeField::create($name . '[time]', false)
|
||||
->addExtraClass('fieldgroup-field');
|
||||
$this->timezoneField = new HiddenField($this->getName() . '[timezone]');
|
||||
$this->timezoneField = new HiddenField($name . '[timezone]');
|
||||
|
||||
parent::__construct($name, $title, $value);
|
||||
}
|
||||
@ -88,7 +88,10 @@ class DatetimeField extends FormField {
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param array $properties
|
||||
* @return HTMLText
|
||||
*/
|
||||
public function FieldHolder($properties = array()) {
|
||||
$config = array(
|
||||
'datetimeorder' => $this->getConfig('datetimeorder'),
|
||||
@ -100,14 +103,19 @@ class DatetimeField extends FormField {
|
||||
return parent::FieldHolder($properties);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $properties
|
||||
* @return HTMLText
|
||||
*/
|
||||
public function Field($properties = array()) {
|
||||
Requirements::css(FRAMEWORK_DIR . '/css/DatetimeField.css');
|
||||
|
||||
$tzField = ($this->getConfig('usertimezone')) ? $this->timezoneField->FieldHolder() : '';
|
||||
return $this->dateField->FieldHolder() .
|
||||
return DBField::create_field('HTMLText', $this->dateField->FieldHolder() .
|
||||
$this->timeField->FieldHolder() .
|
||||
$tzField .
|
||||
'<div class="clear"><!-- --></div>';
|
||||
'<div class="clear"><!-- --></div>'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -128,6 +128,10 @@ class DropdownField extends FormField {
|
||||
parent::__construct($name, ($title===null) ? $name : $title, $value, $form);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $properties
|
||||
* @return HTMLText
|
||||
*/
|
||||
public function Field($properties = array()) {
|
||||
$source = $this->getSource();
|
||||
$options = array();
|
||||
|
@ -83,6 +83,10 @@ class FileField extends FormField {
|
||||
parent::__construct($name, $title, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $properties
|
||||
* @return HTMLText
|
||||
*/
|
||||
public function Field($properties = array()) {
|
||||
$properties = array_merge($properties, array(
|
||||
'MaxFileSize' => $this->getValidator()->getAllowedMaxFileSize()
|
||||
|
@ -49,7 +49,7 @@ class FormAction extends FormField {
|
||||
public function __construct($action, $title = "", $form = null) {
|
||||
$this->action = "action_$action";
|
||||
$this->setForm($form);
|
||||
|
||||
|
||||
parent::__construct($this->action, $title);
|
||||
}
|
||||
|
||||
@ -74,6 +74,10 @@ class FormAction extends FormField {
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $properties
|
||||
* @return HTMLText
|
||||
*/
|
||||
public function Field($properties = array()) {
|
||||
$properties = array_merge(
|
||||
$properties,
|
||||
@ -87,6 +91,10 @@ class FormAction extends FormField {
|
||||
return parent::Field($properties);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $properties
|
||||
* @return HTMLText
|
||||
*/
|
||||
public function FieldHolder($properties = array()) {
|
||||
return $this->Field($properties);
|
||||
}
|
||||
@ -100,7 +108,7 @@ class FormAction extends FormField {
|
||||
|
||||
// Remove this method override in 4.0
|
||||
$decoded = Convert::xml2raw($title);
|
||||
if($decoded !== $title) {
|
||||
if($title && $decoded !== $title) {
|
||||
Deprecation::notice(
|
||||
'4.0',
|
||||
'The FormAction title field should not be html encoded. Use buttonContent to set custom html instead'
|
||||
|
@ -3,20 +3,22 @@
|
||||
/**
|
||||
* Represents a field in a form.
|
||||
*
|
||||
* A FieldList contains a number of FormField objects which make up the whole
|
||||
* 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.
|
||||
* A FieldList contains a number of FormField objects which make up the whole of a form.
|
||||
*
|
||||
* <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
|
||||
* 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
|
||||
* 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. Define {@link validate()} to validate the form field
|
||||
* and ensure that the content provided is valid.
|
||||
* To subclass:
|
||||
*
|
||||
* Define a {@link dataValue()} method that returns a value suitable for 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 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
|
||||
* @subpackage core
|
||||
@ -28,16 +30,49 @@ class FormField extends RequestHandler {
|
||||
*/
|
||||
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
|
||||
*/
|
||||
protected $description;
|
||||
|
||||
/**
|
||||
* @var $extraClasses array Extra CSS-classes for the formfield-container
|
||||
* Extra CSS classes for the FormField container.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $extraClasses;
|
||||
|
||||
@ -47,85 +82,111 @@ class FormField extends RequestHandler {
|
||||
*/
|
||||
private static $default_classes = array();
|
||||
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
public $dontEscape;
|
||||
|
||||
/**
|
||||
* @var $rightTitle string Used in SmallFieldHolder to force a right-aligned label, or in FieldHolder
|
||||
* to create contextual label.
|
||||
* Right-aligned, contextual label for the field.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $rightTitle;
|
||||
|
||||
/**
|
||||
* @var $leftTitle string Used in SmallFieldHolder() to force a left-aligned label with correct spacing.
|
||||
* Please use $title for FormFields rendered with FieldHolder().
|
||||
* Left-aligned, contextual label for the field.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $leftTitle;
|
||||
|
||||
/**
|
||||
* Stores a reference to the FieldList that contains this object.
|
||||
*
|
||||
* @var FieldList
|
||||
*/
|
||||
protected $containerFieldList;
|
||||
|
||||
/**
|
||||
* @var boolean
|
||||
* @var bool
|
||||
*/
|
||||
protected $readonly = false;
|
||||
|
||||
/**
|
||||
* @var boolean
|
||||
* @var bool
|
||||
*/
|
||||
protected $disabled = false;
|
||||
|
||||
/**
|
||||
* @var string 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()}
|
||||
* Custom validation message for the field.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected
|
||||
$template,
|
||||
$fieldHolderTemplate,
|
||||
$smallFieldHolderTemplate;
|
||||
protected $customValidationMessage = '';
|
||||
|
||||
/**
|
||||
* @var array All attributes on the form field (not the field holder).
|
||||
* Partially determined based on other instance properties, please use {@link getAttributes()}.
|
||||
* 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()}.
|
||||
*
|
||||
* @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();
|
||||
|
||||
/**
|
||||
* Takes a fieldname and converts camelcase to spaced
|
||||
* words. Also resolves combined fieldnames with dot syntax
|
||||
* to spaced words.
|
||||
* Takes a field name and converts camelcase to spaced words. Also resolves combined field
|
||||
* names with dot syntax to spaced words.
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* - 'TotalAmount' will return 'Total Amount'
|
||||
* - 'Organisation.ZipCode' will return 'Organisation Zip Code'
|
||||
*
|
||||
* @param string $fieldName
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function name_to_label($fieldName) {
|
||||
if(strpos($fieldName, '.') !== false) {
|
||||
$parts = explode('.', $fieldName);
|
||||
$label = $parts[count($parts)-2] . ' ' . $parts[count($parts)-1];
|
||||
|
||||
$label = $parts[count($parts) - 2] . ' ' . $parts[count($parts) - 1];
|
||||
} else {
|
||||
$label = $fieldName;
|
||||
}
|
||||
|
||||
$label = preg_replace("/([a-z]+)([A-Z])/","$1 $2", $label);
|
||||
|
||||
return $label;
|
||||
return preg_replace('/([a-z]+)([A-Z])/', '$1 $2', $label);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -133,41 +194,51 @@ class FormField extends RequestHandler {
|
||||
*
|
||||
* @param string $tag
|
||||
* @param array $attributes
|
||||
* @param mixed $content
|
||||
* @param null|string $content
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function create_tag($tag, $attributes, $content = null) {
|
||||
$preparedAttributes = '';
|
||||
|
||||
foreach($attributes as $k => $v) {
|
||||
// Note: as indicated by the $k == value item here; the decisions over what to include in the attributes
|
||||
// can sometimes get finicky
|
||||
if(!empty($v) || $v === '0' || ($k == 'value' && $v !== null) ) {
|
||||
$preparedAttributes .= " $k=\"" . Convert::raw2att($v) . "\"";
|
||||
foreach($attributes as $attributeKey => $attributeValue) {
|
||||
if(!empty($attributeValue) || $attributeValue === '0' || ($attributeKey == 'value' && $attributeValue !== null)) {
|
||||
$preparedAttributes .= sprintf(
|
||||
' %s="%s"', $attributeKey, Convert::raw2att($attributeValue)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if($content || $tag != 'input') {
|
||||
return "<$tag$preparedAttributes>$content</$tag>";
|
||||
}
|
||||
else {
|
||||
return "<$tag$preparedAttributes />";
|
||||
return sprintf(
|
||||
'<%s%s>%s</%s>', $tag, $preparedAttributes, $content, $tag
|
||||
);
|
||||
}
|
||||
|
||||
return sprintf(
|
||||
'<%s%s />', $tag, $preparedAttributes
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new field.
|
||||
*
|
||||
* @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.
|
||||
*/
|
||||
public function __construct($name, $title = null, $value = null) {
|
||||
$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();
|
||||
|
||||
@ -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
|
||||
*/
|
||||
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
|
||||
* that this ID is included in the field.
|
||||
* The ID is generated as FormName_FieldName. All Field functions should ensure that this ID is
|
||||
* included in the field.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
@ -237,7 +308,7 @@ class FormField extends RequestHandler {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the raw field name.
|
||||
* Returns the field name.
|
||||
*
|
||||
* @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,
|
||||
* e.g. "required". Use {@link setError()} to set this property.
|
||||
* Arbitrary value which is mostly used for CSS classes in the rendered HTML, e.g "required".
|
||||
*
|
||||
* Use {@link setError()} to set this property.
|
||||
*
|
||||
* @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() {
|
||||
return $this->value;
|
||||
@ -289,8 +363,7 @@ class FormField extends RequestHandler {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the field value suitable for insertion into the
|
||||
* {@link DataObject}.
|
||||
* Returns the field value suitable for insertion into the data object.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
@ -308,12 +381,13 @@ class FormField extends RequestHandler {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $val
|
||||
* @param string $title
|
||||
*
|
||||
* @return FormField
|
||||
* @return $this
|
||||
*/
|
||||
public function setTitle($val) {
|
||||
$this->title = $val;
|
||||
public function setTitle($title) {
|
||||
$this->title = $title;
|
||||
|
||||
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) {
|
||||
$this->rightTitle = $val;
|
||||
return $this;
|
||||
}
|
||||
public function setRightTitle($rightTitle) {
|
||||
$this->rightTitle = $rightTitle;
|
||||
|
||||
public function LeftTitle() {
|
||||
return $this->leftTitle;
|
||||
}
|
||||
|
||||
public function setLeftTitle($val) {
|
||||
$this->leftTitle = $val;
|
||||
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 validatoin
|
||||
* error classes which can be used to style the contained tags.
|
||||
* @return string
|
||||
*/
|
||||
public function LeftTitle() {
|
||||
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() {
|
||||
$classes = array();
|
||||
|
||||
$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()) $classes[] = "nolabel";
|
||||
if(!$this->Title()) {
|
||||
$classes[] = 'nolabel';
|
||||
}
|
||||
|
||||
// Allow custom styling of any element in the container based
|
||||
// on validation errors, e.g. red borders on input tags.
|
||||
// CSS-Class needs to be different from the one rendered
|
||||
// through {@link FieldHolder()}
|
||||
if($this->Message()) $classes[] .= "holder-" . $this->MessageType();
|
||||
// Allow custom styling of any element in the container based on validation errors,
|
||||
// e.g. red borders on input tags.
|
||||
//
|
||||
// CSS class needs to be different from the one rendered through {@link FieldHolder()}.
|
||||
if($this->Message()) {
|
||||
$classes[] .= 'holder-' . $this->MessageType();
|
||||
}
|
||||
|
||||
return implode(' ', $classes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add one or more CSS-classes to the formfield-container. Multiple class
|
||||
* names should be space delimited.
|
||||
* Add one or more CSS-classes to the FormField container.
|
||||
*
|
||||
* Multiple class names should be space delimited.
|
||||
*
|
||||
* @param string $class
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function addExtraClass($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
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function removeExtraClass($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.
|
||||
*
|
||||
* 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()}
|
||||
* - 'title': {@link setDescription()}
|
||||
* - 'value': {@link setValue}
|
||||
* - 'name': {@link setName}
|
||||
*
|
||||
* CAUTION Doesn't work on most fields which are composed of more than one HTML form field:
|
||||
* AjaxUniqueTextField, CheckboxSetField, CompositeField, ConfirmedPasswordField,
|
||||
* CountryDropdownField, CreditCardField, CurrencyField, DateField, DatetimeField, FieldGroup, GridField,
|
||||
* HtmlEditorField, ImageField, ImageFormAction, InlineFormAction, ListBoxField, etc.
|
||||
* Caution: this doesn't work on most fields which are composed of more than one HTML form
|
||||
* field.
|
||||
*
|
||||
* @param string
|
||||
* @param string
|
||||
* @param string $name
|
||||
* @param string $value
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setAttribute($name, $value) {
|
||||
$this->attributes[$name] = $value;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
$attrs = $this->getAttributes();
|
||||
if(isset($attrs[$name])) return $attrs[$name];
|
||||
$attributes = $this->getAttributes();
|
||||
|
||||
if(isset($attributes[$name])) {
|
||||
return $attributes[$name];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -445,7 +552,7 @@ class FormField extends RequestHandler {
|
||||
* @return array
|
||||
*/
|
||||
public function getAttributes() {
|
||||
$attrs = array(
|
||||
$attributes = array(
|
||||
'type' => 'text',
|
||||
'name' => $this->getName(),
|
||||
'value' => $this->Value(),
|
||||
@ -455,54 +562,67 @@ class FormField extends RequestHandler {
|
||||
'readonly' => $this->isReadonly()
|
||||
);
|
||||
|
||||
if ($this->Required()) {
|
||||
$attrs['required'] = 'required';
|
||||
$attrs['aria-required'] = 'true';
|
||||
if($this->Required()) {
|
||||
$attributes['required'] = 'required';
|
||||
$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()}.
|
||||
* If at least one argument is passed as a string, all arguments act as excludes by name.
|
||||
* Custom attributes to process. Falls back to {@link getAttributes()}.
|
||||
*
|
||||
* @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) {
|
||||
$exclude = (is_string($attrs)) ? func_get_args() : null;
|
||||
public function getAttributesHTML($attributes = null) {
|
||||
$exclude = null;
|
||||
|
||||
if(!$attrs || is_string($attrs)) {
|
||||
$attrs = $this->getAttributes();
|
||||
if(is_string($attributes)) {
|
||||
$exclude = func_get_args();
|
||||
}
|
||||
|
||||
// Remove empty
|
||||
$attrs = array_filter((array)$attrs, function($v) {
|
||||
if(!$attributes || is_string($attributes)) {
|
||||
$attributes = $this->getAttributes();
|
||||
}
|
||||
|
||||
$attributes = (array) $attributes;
|
||||
|
||||
$attributes = array_filter($attributes, function ($v) {
|
||||
return ($v || $v === 0 || $v === '0');
|
||||
});
|
||||
|
||||
// Remove excluded
|
||||
if($exclude) {
|
||||
$attrs = array_diff_key($attrs, array_flip($exclude));
|
||||
$attributes = array_diff_key(
|
||||
$attributes,
|
||||
array_flip($exclude)
|
||||
);
|
||||
}
|
||||
|
||||
// Create markup
|
||||
$parts = array();
|
||||
|
||||
foreach($attrs as $name => $value) {
|
||||
$parts[] = ($value === true) ? "{$name}=\"{$name}\"" : "{$name}=\"" . Convert::raw2att($value) . "\"";
|
||||
foreach($attributes as $name => $value) {
|
||||
if($value === true) {
|
||||
$parts[] = sprintf('%s="%s"', $name, $name);
|
||||
} else {
|
||||
$parts[] = sprintf('%s="%s"', $name, Convert::raw2att($value));
|
||||
}
|
||||
}
|
||||
|
||||
return implode(' ', $parts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a version of a title suitable for insertion into an HTML
|
||||
* attribute.
|
||||
* Returns a version of a title suitable for insertion into an HTML attribute.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
@ -511,8 +631,7 @@ class FormField extends RequestHandler {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a version of a title suitable for insertion into an HTML
|
||||
* attribute.
|
||||
* Returns a version of a title suitable for insertion into an HTML attribute.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
@ -524,8 +643,9 @@ class FormField extends RequestHandler {
|
||||
* Set the field value.
|
||||
*
|
||||
* @param mixed $value
|
||||
* @param mixed $data Optional data source passed in by {@see Form::loadDataFrom}
|
||||
* @return FormField Self reference
|
||||
* @param null|array|DataObject $data {@see Form::loadDataFrom}
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setValue($value) {
|
||||
$this->value = $value;
|
||||
@ -534,11 +654,11 @@ class FormField extends RequestHandler {
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the field name
|
||||
* Set the field name.
|
||||
*
|
||||
* @param string $name
|
||||
*
|
||||
* @return FormField
|
||||
* @return $this
|
||||
*/
|
||||
public function setName($name) {
|
||||
$this->name = $name;
|
||||
@ -549,12 +669,11 @@ class FormField extends RequestHandler {
|
||||
/**
|
||||
* Set the container form.
|
||||
*
|
||||
* This is called whenever you create a new form and put fields inside it,
|
||||
* so that you don't have to worry about linking the two.
|
||||
* This is called automatically when fields are added to forms.
|
||||
*
|
||||
* @param Form
|
||||
* @param Form $form
|
||||
*
|
||||
* @return FormField
|
||||
* @return $this
|
||||
*/
|
||||
public function setForm($form) {
|
||||
$this->form = $form;
|
||||
@ -572,8 +691,7 @@ class FormField extends RequestHandler {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return TRUE if security token protection is enabled on the parent
|
||||
* {@link Form}.
|
||||
* Return true if security token protection is enabled on the parent {@link Form}.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
@ -588,10 +706,14 @@ class FormField extends RequestHandler {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $message Message to show to the user. Allows HTML content,
|
||||
* which means you need to use Convert::raw2xml() for any user supplied data.
|
||||
* Sets the error message to be displayed on the form field.
|
||||
*
|
||||
* Allows HTML content, so remember to use Convert::raw2xml().
|
||||
*
|
||||
* @param string $message
|
||||
* @param string $messageType
|
||||
* @return FormField
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setError($message, $messageType) {
|
||||
$this->message = $message;
|
||||
@ -601,24 +723,23 @@ class FormField extends RequestHandler {
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the custom error message to show instead of the default
|
||||
* format of Please Fill In XXX. Different from setError() as
|
||||
* that appends it to the standard error messaging.
|
||||
* Set the custom error message to show instead of the default format.
|
||||
*
|
||||
* @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) {
|
||||
$this->customValidationMessage = $msg;
|
||||
public function setCustomValidationMessage($customValidationMessage) {
|
||||
$this->customValidationMessage = $customValidationMessage;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the custom error message for this form field. If a custom
|
||||
* message has not been defined then just return blank. The default
|
||||
* error is defined on {@link Validator}.
|
||||
* Get the custom error message for this form field. If a custom message has not been defined
|
||||
* then just return blank. The default error is defined on {@link Validator}.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
@ -629,12 +750,12 @@ class FormField extends RequestHandler {
|
||||
/**
|
||||
* Set name of template (without path or extension).
|
||||
*
|
||||
* Caution: Not consistently implemented in all subclasses, please check
|
||||
* the {@link Field()} method on the subclass for support.
|
||||
* Caution: Not consistently implemented in all subclasses, please check the {@link Field()}
|
||||
* method on the subclass for support.
|
||||
*
|
||||
* @param string $template
|
||||
*
|
||||
* @return FormField
|
||||
* @return $this
|
||||
*/
|
||||
public function setTemplate($template) {
|
||||
$this->template = $template;
|
||||
@ -657,18 +778,18 @@ class FormField extends RequestHandler {
|
||||
}
|
||||
|
||||
/**
|
||||
* Set name of template (without path or extension) for the holder,
|
||||
* which in turn is responsible for rendering {@link Field()}.
|
||||
* Set name of template (without path or extension) for the holder, which in turn is
|
||||
* responsible for rendering {@link Field()}.
|
||||
*
|
||||
* Caution: Not consistently implemented in all subclasses,
|
||||
* please check the {@link Field()} method on the subclass for support.
|
||||
* Caution: Not consistently implemented in all subclasses, please check the {@link Field()}
|
||||
* method on the subclass for support.
|
||||
*
|
||||
* @param string $template
|
||||
* @param string $fieldHolderTemplate
|
||||
*
|
||||
* @return FormField
|
||||
* @return $this
|
||||
*/
|
||||
public function setFieldHolderTemplate($template) {
|
||||
$this->fieldHolderTemplate = $template;
|
||||
public function setFieldHolderTemplate($fieldHolderTemplate) {
|
||||
$this->fieldHolderTemplate = $fieldHolderTemplate;
|
||||
|
||||
return $this;
|
||||
}
|
||||
@ -681,50 +802,67 @@ class FormField extends RequestHandler {
|
||||
}
|
||||
|
||||
/**
|
||||
* Set name of template (without path or extension) for the small holder,
|
||||
* which in turn is responsible for rendering {@link Field()}.
|
||||
* Set name of template (without path or extension) for the small holder, which in turn is
|
||||
* responsible for rendering {@link Field()}.
|
||||
*
|
||||
* Caution: Not consistently implemented in all subclasses,
|
||||
* please check the {@link Field()} method on the subclass for support.
|
||||
* Caution: Not consistently implemented in all subclasses, please check the {@link Field()}
|
||||
* method on the subclass for support.
|
||||
*
|
||||
* @param string
|
||||
* @param string $smallFieldHolderTemplate
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setSmallFieldHolderTemplate($template) {
|
||||
$this->smallFieldHolderTemplate = $template;
|
||||
public function setSmallFieldHolderTemplate($smallFieldHolderTemplate) {
|
||||
$this->smallFieldHolderTemplate = $smallFieldHolderTemplate;
|
||||
|
||||
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
|
||||
* 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,
|
||||
* such as an input tag.
|
||||
*
|
||||
* @param array $properties key value pairs of template variables
|
||||
* @param array $properties
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function Field($properties = array()) {
|
||||
$obj = ($properties) ? $this->customise($properties) : $this;
|
||||
$context = $this;
|
||||
|
||||
if(count($properties)) {
|
||||
$context = $context->customise($properties);
|
||||
}
|
||||
|
||||
$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.
|
||||
*
|
||||
* The default field holder is a label and a form field inside a div.
|
||||
*
|
||||
* @see FieldHolder.ss
|
||||
*
|
||||
* @param array $properties key value pairs of template variables
|
||||
* @param array $properties
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
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
|
||||
*/
|
||||
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
|
||||
*/
|
||||
@ -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
|
||||
*/
|
||||
@ -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
|
||||
*/
|
||||
@ -775,31 +917,37 @@ class FormField extends RequestHandler {
|
||||
|
||||
|
||||
/**
|
||||
* Generate an array of classname strings to use for rendering this form
|
||||
* field into HTML
|
||||
* Generate an array of class name strings to use for rendering this form field into HTML.
|
||||
*
|
||||
* @param string $custom custom template (if set)
|
||||
* @param string $suffix template suffix
|
||||
* @param string $customTemplate
|
||||
* @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();
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this field is a composite field.
|
||||
*
|
||||
* To create composite field types, you should subclass {@link CompositeField}.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isComposite() {
|
||||
return false;
|
||||
@ -807,99 +955,138 @@ class FormField extends RequestHandler {
|
||||
|
||||
/**
|
||||
* 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,
|
||||
* you can prevent any data-focused methods from looking at it.
|
||||
*
|
||||
* 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, you can prevent any data-focused methods from looking at it.
|
||||
*
|
||||
* @see FieldList::collateDataFields()
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasData() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return boolean
|
||||
* @return bool
|
||||
*/
|
||||
public function isReadonly() {
|
||||
return $this->readonly;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets readonly-flag on form-field. Please use performReadonlyTransformation()
|
||||
* to actually transform this instance.
|
||||
* @param $bool boolean Setting "false" has no effect on the field-state.
|
||||
* Sets a read-only flag on this FormField.
|
||||
*
|
||||
* 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) {
|
||||
$this->readonly = $bool;
|
||||
public function setReadonly($readonly) {
|
||||
$this->readonly = $readonly;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return boolean
|
||||
* @return bool
|
||||
*/
|
||||
public function isDisabled() {
|
||||
return $this->disabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets disabed-flag on form-field. Please use performDisabledTransformation()
|
||||
* to actually transform this instance.
|
||||
* @param $bool boolean Setting "false" has no effect on the field-state.
|
||||
* Sets a disabled flag on this FormField.
|
||||
*
|
||||
* 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) {
|
||||
$this->disabled = $bool;
|
||||
public function setDisabled($disabled) {
|
||||
$this->disabled = $disabled;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a readonly version of this field
|
||||
* Returns a read-only version of this field.
|
||||
*
|
||||
* @return FormField
|
||||
*/
|
||||
public function performReadonlyTransformation() {
|
||||
$readonlyClassName = $this->class . '_Disabled';
|
||||
$readonlyClassName = $this->class . '_Readonly';
|
||||
|
||||
if(ClassInfo::exists($readonlyClassName)) {
|
||||
$clone = $this->castedCopy($readonlyClassName);
|
||||
} else {
|
||||
$clone = $this->castedCopy('ReadonlyField');
|
||||
$clone->setReadonly(true);
|
||||
}
|
||||
}
|
||||
|
||||
$clone->setReadonly(true);
|
||||
|
||||
return $clone;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public function performDisabledTransformation() {
|
||||
$disabledClassName = $this->class . '_Disabled';
|
||||
|
||||
if(ClassInfo::exists($disabledClassName)) {
|
||||
$clone = $this->castedCopy($disabledClassName);
|
||||
} else {
|
||||
$clone = clone $this;
|
||||
$clone->setDisabled(true);
|
||||
}
|
||||
|
||||
return $clone;
|
||||
}
|
||||
$clone->setDisabled(true);
|
||||
|
||||
public function transform(FormTransformation $trans) {
|
||||
return $trans->transform($this);
|
||||
return $clone;
|
||||
}
|
||||
|
||||
public function hasClass($class){
|
||||
$patten = '/'.strtolower($class).'/i';
|
||||
$subject = strtolower($this->class." ".$this->extraClass());
|
||||
/**
|
||||
* @param FormTransformation $transformation
|
||||
*
|
||||
* @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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* 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
|
||||
*/
|
||||
@ -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) {
|
||||
Deprecation::notice('3.2', 'Use FormField::create_tag()');
|
||||
Deprecation::notice('4.0', 'Use FormField::create_tag()');
|
||||
|
||||
return self::create_tag($tag, $attributes, $content);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validation method each {@link FormField} subclass should implement,
|
||||
* determining whether the field is valid or not based on the value.
|
||||
* Abstract method each {@link FormField} subclass must implement, determines whether the field
|
||||
* is valid or not based on the value.
|
||||
*
|
||||
* @todo Make this abstract.
|
||||
*
|
||||
* @param Validator
|
||||
* @return boolean
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function validate($validator) {
|
||||
return true;
|
||||
@ -928,13 +1125,16 @@ class FormField extends RequestHandler {
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
$this->description = $description;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@ -945,37 +1145,51 @@ class FormField extends RequestHandler {
|
||||
return $this->description;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function debug() {
|
||||
return "$this->class ($this->name: $this->title : <font style='color:red;'>$this->message</font>)"
|
||||
. " = $this->value";
|
||||
return sprintf(
|
||||
'%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.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function forTemplate() {
|
||||
return $this->Field();
|
||||
}
|
||||
|
||||
/**
|
||||
* @uses Validator->fieldIsRequired()
|
||||
* @return boolean
|
||||
* @return bool
|
||||
*/
|
||||
public function Required() {
|
||||
if($this->form && ($validator = $this->form->Validator)) {
|
||||
return $validator->fieldIsRequired($this->name);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the FieldList that contains this field.
|
||||
*
|
||||
* @param FieldList $list
|
||||
* @param FieldList $containerFieldList
|
||||
*
|
||||
* @return FieldList
|
||||
*/
|
||||
public function setContainerFieldList($list) {
|
||||
$this->containerFieldList = $list;
|
||||
public function setContainerFieldList($containerFieldList) {
|
||||
$this->containerFieldList = $containerFieldList;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@ -988,31 +1202,46 @@ class FormField extends RequestHandler {
|
||||
return $this->containerFieldList;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return null|FieldList
|
||||
*/
|
||||
public function rootFieldList() {
|
||||
if(is_object($this->containerFieldList)) return $this->containerFieldList->rootFieldList();
|
||||
else user_error("rootFieldList() called on $this->class object without a containerFieldList", E_USER_ERROR);
|
||||
if(is_object($this->containerFieldList)) {
|
||||
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.
|
||||
* The logic tries to retain all of the instance properties,
|
||||
* and may be overloaded by subclasses to set additional ones.
|
||||
* Returns another instance of this field, but "cast" to a different class. The logic tries to
|
||||
* retain all of the instance properties, and may be overloaded by subclasses to set additional
|
||||
* ones.
|
||||
*
|
||||
* Assumes the standard FormField parameter signature with
|
||||
* its name as the only mandatory argument. Mainly geared towards
|
||||
* creating *_Readonly or *_Disabled subclasses of the same type,
|
||||
* or casting to a {@link ReadonlyField}.
|
||||
* Assumes the standard FormField parameter signature with its name as the only mandatory
|
||||
* argument. Mainly geared towards creating *_Readonly or *_Disabled subclasses of the same
|
||||
* type, or casting to a {@link ReadonlyField}.
|
||||
*
|
||||
* Does not copy custom field templates, since they probably won't apply to
|
||||
* the new instance.
|
||||
* Does not copy custom field templates, since they probably won't apply to 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
|
||||
*/
|
||||
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
|
||||
->setValue($this->value) // get value directly from property, avoid any conversions
|
||||
->setValue($this->value)
|
||||
->setForm($this->form)
|
||||
->setTitle($this->Title())
|
||||
->setLeftTitle($this->LeftTitle())
|
||||
@ -1020,12 +1249,12 @@ class FormField extends RequestHandler {
|
||||
->addExtraClass($this->extraClass())
|
||||
->setDescription($this->getDescription());
|
||||
|
||||
// Only include built-in attributes, ignore anything
|
||||
// set through getAttributes(), since those might change important characteristics
|
||||
// of the field, e.g. its "type" attribute.
|
||||
foreach($this->attributes as $k => $v) {
|
||||
$field->setAttribute($k, $v);
|
||||
}
|
||||
// Only include built-in attributes, ignore anything set through getAttributes().
|
||||
// Those might change important characteristics of the field, e.g. its "type" attribute.
|
||||
foreach($this->attributes as $attributeKey => $attributeValue) {
|
||||
$field->setAttribute($attributeKey, $attributeValue);
|
||||
}
|
||||
|
||||
$field->dontEscape = $this->dontEscape;
|
||||
|
||||
return $field;
|
||||
|
@ -10,7 +10,7 @@ class HiddenField extends FormField {
|
||||
/**
|
||||
* @param array $properties
|
||||
*
|
||||
* @return string
|
||||
* @return HTMLText
|
||||
*/
|
||||
public function FieldHolder($properties = array()) {
|
||||
return $this->Field($properties);
|
||||
|
@ -29,7 +29,7 @@ class HtmlEditorField extends TextareaField {
|
||||
protected $rows = 30;
|
||||
|
||||
/**
|
||||
* @deprecated since version 3.2
|
||||
* @deprecated since version 4.0
|
||||
*/
|
||||
public static function include_js() {
|
||||
Deprecation::notice('4.0', 'Use HtmlEditorConfig::require_js() instead');
|
||||
|
@ -27,14 +27,25 @@ class InlineFormAction extends FormField {
|
||||
return $this->castedCopy('InlineFormAction_ReadOnly');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $properties
|
||||
* @return HTMLText
|
||||
*/
|
||||
public function Field($properties = array()) {
|
||||
if($this->includeDefaultJS) {
|
||||
Requirements::javascriptTemplate(FRAMEWORK_DIR . '/javascript/InlineFormAction.js',
|
||||
array('ID'=>$this->id()));
|
||||
}
|
||||
|
||||
return "<input type=\"submit\" name=\"action_{$this->name}\" value=\"{$this->title}\" id=\"{$this->id()}\""
|
||||
. " class=\"action{$this->extraClass}\" />";
|
||||
return DBField::create_field(
|
||||
'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() {
|
||||
@ -61,9 +72,21 @@ class InlineFormAction_ReadOnly extends FormField {
|
||||
|
||||
protected $readonly = true;
|
||||
|
||||
/**
|
||||
* @param array $properties
|
||||
* @return HTMLText
|
||||
*/
|
||||
public function Field($properties = array()) {
|
||||
return "<input type=\"submit\" name=\"action_{$this->name}\" value=\"{$this->title}\" id=\"{$this->id()}\""
|
||||
. " disabled=\"disabled\" class=\"action disabled$this->extraClass\" />";
|
||||
return DBField::create_field('HTMLText',
|
||||
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() {
|
||||
|
@ -41,13 +41,16 @@ class MoneyField extends FormField {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
* @param array
|
||||
* @return HTMLText
|
||||
*/
|
||||
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->fieldAmount->SmallFieldHolder() . "</div>" .
|
||||
"</div>";
|
||||
"</div>"
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,75 +1,98 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* NullableField is a field that wraps other fields when you want to allow the user to specify whether the value of
|
||||
* the field is null or not.
|
||||
* NullableField is a field that wraps other fields when you want to allow the user to specify
|
||||
* whether the value of the field is null or not.
|
||||
*
|
||||
* The classic case is to wrap a TextField so that the user can distinguish between an empty string
|
||||
* and a null string.
|
||||
*
|
||||
* 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"));
|
||||
*
|
||||
* It displays the field that is wrapped followed by a checkbox that is used to specify if the 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:
|
||||
* It displays the field that is wrapped followed by a checkbox that is used to specify if the
|
||||
* 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:
|
||||
*
|
||||
* $field->Value() or $field->dataValue()
|
||||
*
|
||||
* You can specify the label to use for the "is null" checkbox. If you want to use I8N for this label then specify it
|
||||
* like this:
|
||||
* You can specify the label to use for the "is null" checkbox. If you want to use i18n for 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
|
||||
*
|
||||
* @package forms
|
||||
* @subpackage fields-basic
|
||||
*/
|
||||
class NullableField extends FormField {
|
||||
/**
|
||||
* The field that holds the value of this field
|
||||
*
|
||||
* @var FormField
|
||||
*/
|
||||
protected $valueField;
|
||||
|
||||
/**
|
||||
* The label to show next to the is null check box.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $isNullLabel;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new nullable field
|
||||
* @param $valueField
|
||||
* @return NullableField
|
||||
*
|
||||
* @param FormField $valueField
|
||||
* @param null|string $isNullLabel
|
||||
*/
|
||||
public function __construct(FormField $valueField, $isNullLabel = null) {
|
||||
$this->valueField = $valueField;
|
||||
$this->isNullLabel = $isNullLabel;
|
||||
if ( is_null($this->isNullLabel) ) {
|
||||
// Set a default label if one is not provided.
|
||||
|
||||
if(isset($isNullLabel)) {
|
||||
$this->setIsNullLabel($isNullLabel);
|
||||
} else {
|
||||
$this->isNullLabel = _t('NullableField.IsNullLabel', 'Is Null');
|
||||
}
|
||||
parent::__construct($valueField->getName(), $valueField->Title(), $valueField->Value(),
|
||||
$valueField->getForm(), $valueField->RightTitle());
|
||||
$this->readonly = $valueField->isReadonly();
|
||||
|
||||
parent::__construct(
|
||||
$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.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getIsNullLabel() {
|
||||
return $this->isNullLabel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the label used for the Is Null checkbox.
|
||||
*
|
||||
* @param $isNulLabel string
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setIsNullLabel(string $isNulLabel){
|
||||
public function setIsNullLabel($isNulLabel) {
|
||||
$this->isNullLabel = $isNulLabel;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the id used for the Is Null check box.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getIsNullId() {
|
||||
@ -77,54 +100,81 @@ class NullableField extends FormField {
|
||||
}
|
||||
|
||||
/**
|
||||
* (non-PHPdoc)
|
||||
* @see framework/forms/FormField#Field()
|
||||
* @param array $properties
|
||||
*
|
||||
* @return HTMLText
|
||||
*/
|
||||
public function Field($properties = array()) {
|
||||
if ( $this->isReadonly()) {
|
||||
if($this->isReadonly()) {
|
||||
$nullableCheckbox = new CheckboxField_Readonly($this->getIsNullId());
|
||||
} else {
|
||||
$nullableCheckbox = new CheckboxField($this->getIsNullId());
|
||||
}
|
||||
|
||||
$nullableCheckbox->setValue(is_null($this->dataValue()));
|
||||
|
||||
return $this->valueField->Field() . ' ' . $nullableCheckbox->Field()
|
||||
. ' <span>' . $this->getIsNullLabel().'</span>';
|
||||
return DBField::create_field('HTMLText', sprintf(
|
||||
'%s %s <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
|
||||
*
|
||||
* @param mixed $value
|
||||
* @param null|array $data
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
$this->valueField->setValue($value);
|
||||
|
||||
parent::setValue($value);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* (non-PHPdoc)
|
||||
* @see forms/FormField#setName($name)
|
||||
* @param string $name
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setName($name) {
|
||||
// We need to pass through the name change to the underlying value field.
|
||||
$this->valueField->setName($name);
|
||||
|
||||
parent::setName($name);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* (non-PHPdoc)
|
||||
* @see framework/forms/FormField#debug()
|
||||
* @return string
|
||||
*/
|
||||
public function debug() {
|
||||
$result = "$this->class ($this->name: $this->title : <font style='color:red;'>$this->message</font>) = ";
|
||||
$result .= (is_null($this->value)) ? "<<null>>" : $this->value;
|
||||
return result;
|
||||
$result = sprintf(
|
||||
'%s (%s: $s : <span style="color: red">%s</span>) = ',
|
||||
$this->class,
|
||||
$this->name,
|
||||
$this->title,
|
||||
$this->message
|
||||
);
|
||||
|
||||
if($this->value === null) {
|
||||
$result .= "<<null>>";
|
||||
} else {
|
||||
$result .= (string) $this->value;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -9,56 +9,75 @@
|
||||
* @subpackage fields-formattedinput
|
||||
*/
|
||||
class NumericField extends TextField {
|
||||
|
||||
/**
|
||||
* Override locale for this field
|
||||
*
|
||||
* Override locale for this field.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $locale = null;
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
* @param array $data
|
||||
*
|
||||
* @return $this
|
||||
*
|
||||
* @throws Zend_Locale_Exception
|
||||
*/
|
||||
public function setValue($value, $data = array()) {
|
||||
require_once "Zend/Locale/Format.php";
|
||||
|
||||
// If passing in a non-string number, or a value
|
||||
// directly from a dataobject then localise this number
|
||||
if ((is_numeric($value) && !is_string($value)) ||
|
||||
($value && $data instanceof DataObject)
|
||||
){
|
||||
// directly from a DataObject then localise this number
|
||||
|
||||
if(is_int($value) || is_float($value) || $data instanceof DataObject) {
|
||||
$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 {
|
||||
// If an invalid number, store it anyway, but validate() will fail
|
||||
$this->value = $this->clean($value);
|
||||
}
|
||||
|
||||
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
|
||||
* @return string The input value, with all spaces replaced with non-breaking spaces
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function clean($input) {
|
||||
$nbsp = html_entity_decode(' ', null, 'UTF-8');
|
||||
return str_replace(' ', $nbsp, trim($input));
|
||||
$replacement = html_entity_decode(' ', null, 'UTF-8');
|
||||
|
||||
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
|
||||
*/
|
||||
protected function isNumeric() {
|
||||
require_once "Zend/Locale/Format.php";
|
||||
|
||||
$locale = new Zend_Locale($this->getLocale());
|
||||
|
||||
return Zend_Locale_Format::isNumber(
|
||||
$this->clean($this->value),
|
||||
array('locale' => $locale)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function Type() {
|
||||
return 'numeric text';
|
||||
}
|
||||
@ -81,74 +100,111 @@ class NumericField extends TextField {
|
||||
return true;
|
||||
}
|
||||
|
||||
if($this->isNumeric()) return true;
|
||||
if($this->isNumeric()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$validator->validationError(
|
||||
$this->name,
|
||||
_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)
|
||||
),
|
||||
"validation"
|
||||
);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the number value from the localised string value
|
||||
*
|
||||
* @return string number value
|
||||
* Extracts the number value from the localised string value.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function dataValue() {
|
||||
require_once "Zend/Locale/Format.php";
|
||||
if(!$this->isNumeric()) return 0;
|
||||
|
||||
if(!$this->isNumeric()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$locale = new Zend_Locale($this->getLocale());
|
||||
|
||||
$number = Zend_Locale_Format::getNumber(
|
||||
$this->clean($this->value),
|
||||
array('locale' => $locale)
|
||||
);
|
||||
|
||||
return $number;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a readonly version of this field
|
||||
* Creates a read-only version of the field.
|
||||
*
|
||||
* @return NumericField_Readonly
|
||||
*/
|
||||
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);
|
||||
|
||||
return $field;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current locale this field is set to
|
||||
*
|
||||
* Gets the current locale this field is set to.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
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
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setLocale($locale) {
|
||||
$this->locale = $locale;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Readonly version of a numeric field.
|
||||
*
|
||||
* @package forms
|
||||
* @subpackage fields-basic
|
||||
*/
|
||||
class NumericField_Readonly extends ReadonlyField {
|
||||
|
||||
/**
|
||||
* @return static
|
||||
*/
|
||||
public function performReadonlyTransformation() {
|
||||
return clone $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function Value() {
|
||||
return Convert::raw2xml($this->value ? "$this->value" : "0");
|
||||
}
|
||||
if($this->value) {
|
||||
return Convert::raw2xml((string) $this->value);
|
||||
}
|
||||
|
||||
return '0';
|
||||
}
|
||||
}
|
||||
|
@ -27,6 +27,10 @@ class PhoneNumberField extends FormField {
|
||||
parent::__construct($name, $title, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $properties
|
||||
* @return FieldGroup|HTMLText
|
||||
*/
|
||||
public function Field($properties = array()) {
|
||||
$fields = new FieldGroup( $this->name );
|
||||
$fields->setID("{$this->name}_Holder");
|
||||
|
@ -36,6 +36,10 @@ class ReadonlyField extends FormField {
|
||||
return clone $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $properties
|
||||
* @return HTMLText
|
||||
*/
|
||||
public function Field($properties = array()) {
|
||||
// Include a hidden field in the HTML
|
||||
if($this->includeHiddenField && $this->readonly) {
|
||||
|
@ -34,8 +34,11 @@ class RequiredFields extends Validator {
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since version 4.0
|
||||
*/
|
||||
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;
|
||||
return $this;
|
||||
}
|
||||
|
@ -204,7 +204,7 @@ class TreeDropdownField extends FormField {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
* @return HTMLText
|
||||
*/
|
||||
public function Field($properties = array()) {
|
||||
Requirements::add_i18n_javascript(FRAMEWORK_DIR . '/javascript/lang');
|
||||
|
@ -3,12 +3,11 @@
|
||||
/**
|
||||
* 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
|
||||
* and columns. It reminds of the old TableFields but works with SS_List types
|
||||
* and only loads the necessary rows from the list.
|
||||
* GridField is a field that takes an SS_List and displays it in an table with rows and columns.
|
||||
* It reminds of the old TableFields but works with SS_List types and only loads the necessary
|
||||
* rows from the list.
|
||||
*
|
||||
* The minimum configuration is to pass in name and title of the field and a
|
||||
* SS_List.
|
||||
* The minimum configuration is to pass in name and title of the field and a SS_List.
|
||||
*
|
||||
* <code>
|
||||
* $gridField = new GridField('ExampleGrid', 'Example grid', new DataList('Page'));
|
||||
@ -21,45 +20,44 @@
|
||||
* @property GridState_Data $State The gridstate of this object
|
||||
*/
|
||||
class GridField extends FormField {
|
||||
|
||||
/**
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private static $allowed_actions = array(
|
||||
'index',
|
||||
'gridFieldAlterAction'
|
||||
'gridFieldAlterAction',
|
||||
);
|
||||
|
||||
/**
|
||||
* The datasource
|
||||
* Data source.
|
||||
*
|
||||
* @var SS_List
|
||||
*/
|
||||
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
|
||||
*/
|
||||
protected $modelClassName = '';
|
||||
|
||||
/**
|
||||
* the current state of the GridField
|
||||
* Current state of the GridField.
|
||||
*
|
||||
* @var GridState
|
||||
*/
|
||||
protected $state = null;
|
||||
|
||||
/**
|
||||
*
|
||||
* @var GridFieldConfig
|
||||
*/
|
||||
protected $config = null;
|
||||
|
||||
/**
|
||||
* The components list
|
||||
* Components list.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
@ -67,14 +65,15 @@ class GridField extends FormField {
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
protected $columnDispatch = null;
|
||||
|
||||
/**
|
||||
* Map of callbacks for custom data fields
|
||||
* Map of callbacks for custom data fields.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
@ -86,8 +85,6 @@ class GridField extends FormField {
|
||||
protected $name = '';
|
||||
|
||||
/**
|
||||
* Creates a new GridField field
|
||||
*
|
||||
* @param string $name
|
||||
* @param string $title
|
||||
* @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) {
|
||||
parent::__construct($name, $title, null);
|
||||
|
||||
$this->name = $name;
|
||||
|
||||
if($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->state = new GridState($this);
|
||||
@ -109,44 +111,58 @@ class GridField extends FormField {
|
||||
$this->addExtraClass('ss-gridfield');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param SS_HTTPRequest $request
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function index($request) {
|
||||
return $this->gridFieldAlterAction(array(), $this->getForm(), $request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the modelClass (dataobject) 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
|
||||
* Set the modelClass (data object) that this field will get it column headers from.
|
||||
*
|
||||
* If no $displayFields has been set, the display fields will be $summary_fields.
|
||||
*
|
||||
* @see GridFieldDataColumns::getDisplayFields()
|
||||
*
|
||||
* @param string $modelClassName
|
||||
*
|
||||
* @see GridFieldDataColumns::getDisplayFields()
|
||||
* @return $this
|
||||
*/
|
||||
public function setModelClass($modelClassName) {
|
||||
$this->modelClassName = $modelClassName;
|
||||
|
||||
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
|
||||
*
|
||||
* @throws LogicException
|
||||
*/
|
||||
public function getModelClass() {
|
||||
if($this->modelClassName) return $this->modelClassName;
|
||||
if($this->list && method_exists($this->list, 'dataClass')) {
|
||||
$class = $this->list->dataClass();
|
||||
if($class) return $class;
|
||||
if($this->modelClassName) {
|
||||
return $this->modelClassName;
|
||||
}
|
||||
|
||||
throw new LogicException('GridField doesn\'t have a modelClassName,'
|
||||
. ' so it doesn\'t know the columns of this grid.');
|
||||
if($this->list && method_exists($this->list, 'dataClass')) {
|
||||
$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
|
||||
*/
|
||||
public function getConfig() {
|
||||
@ -156,61 +172,69 @@ class GridField extends FormField {
|
||||
/**
|
||||
* @param GridFieldConfig $config
|
||||
*
|
||||
* @return GridField
|
||||
* @return $this
|
||||
*/
|
||||
public function setConfig(GridFieldConfig $config) {
|
||||
$this->config = $config;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ArrayList
|
||||
*/
|
||||
public function getComponents() {
|
||||
return $this->config->getComponents();
|
||||
}
|
||||
|
||||
/**
|
||||
* Cast a arbitrary value with the help of a castingDefintion
|
||||
*
|
||||
* @param $value
|
||||
* @param $castingDefinition
|
||||
* Cast an arbitrary value with the help of a $castingDefinition.
|
||||
*
|
||||
* @todo refactor this into GridFieldComponent
|
||||
*
|
||||
* @param mixed $value
|
||||
* @param string|array $castingDefinition
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getCastedValue($value, $castingDefinition) {
|
||||
$castingParams = array();
|
||||
|
||||
if(is_array($castingDefinition)) {
|
||||
$castingParams = $castingDefinition;
|
||||
array_shift($castingParams);
|
||||
$castingDefinition = array_shift($castingDefinition);
|
||||
} else {
|
||||
$castingParams = array();
|
||||
}
|
||||
|
||||
if(strpos($castingDefinition, '->') === false) {
|
||||
$castingFieldType = $castingDefinition;
|
||||
$castingField = DBField::create_field($castingFieldType, $value);
|
||||
$value = call_user_func_array(array($castingField, 'XML'), $castingParams);
|
||||
} else {
|
||||
$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 call_user_func_array(array($castingField, 'XML'), $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
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setList(SS_List $list) {
|
||||
$this->list = $list;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the datasource
|
||||
* Get the data source.
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
public function getManipulatedList() {
|
||||
$list = $this->getList();
|
||||
|
||||
foreach($this->getComponents() as $item) {
|
||||
if($item instanceof GridField_DataManipulator) {
|
||||
$list = $item->getManipulatedData($this, $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) {
|
||||
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()) {
|
||||
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(FRAMEWORK_DIR . '/javascript/GridField.js');
|
||||
|
||||
// Get columns
|
||||
$columns = $this->getColumns();
|
||||
|
||||
// Get data
|
||||
$list = $this->getManipulatedList();
|
||||
|
||||
// Render headers, footers, etc
|
||||
$content = array(
|
||||
"before" => "",
|
||||
"after" => "",
|
||||
"header" => "",
|
||||
"footer" => "",
|
||||
'before' => '',
|
||||
'after' => '',
|
||||
'header' => '',
|
||||
'footer' => '',
|
||||
);
|
||||
|
||||
foreach($this->getComponents() as $item) {
|
||||
if($item instanceof GridField_HTMLProvider) {
|
||||
$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) {
|
||||
$content[$k] = trim($v);
|
||||
}
|
||||
if($fragments) {
|
||||
foreach($fragments as $fragmentKey => $fragmentValue) {
|
||||
$fragmentKey = strtolower($fragmentKey);
|
||||
|
||||
// Replace custom fragments and check which fragments are defined
|
||||
// Nested dependencies are handled by deferring the rendering of any content item that
|
||||
// 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.");
|
||||
if(!isset($content[$fragmentKey])) {
|
||||
$content[$fragmentKey] = '';
|
||||
}
|
||||
|
||||
// Otherwise we can push to the end of the content array
|
||||
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]);
|
||||
$content[$fragmentKey] .= $fragmentValue . "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check for any undefined fragments, and if so throw an exception
|
||||
// While we're at it, trim whitespace off the elements
|
||||
foreach($content as $k => $v) {
|
||||
if(empty($fragmentDefined[$k])) {
|
||||
throw new LogicException("GridField HTML fragment '$k' was given content,"
|
||||
. " but not defined. Perhaps there is a supporting GridField component you need to add?");
|
||||
foreach($content as $contentKey => $contentValue) {
|
||||
$content[$contentKey] = trim($contentValue);
|
||||
}
|
||||
|
||||
// Replace custom fragments and check which fragments are defined. Circular dependencies
|
||||
// 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);
|
||||
|
||||
if($total > 0) {
|
||||
$rows = array();
|
||||
foreach($list as $idx => $record) {
|
||||
|
||||
foreach($list as $index => $record) {
|
||||
if($record->hasMethod('canView') && !$record->canView()) {
|
||||
continue;
|
||||
}
|
||||
@ -356,58 +411,80 @@ class GridField extends FormField {
|
||||
foreach($this->getColumns() as $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) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$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);
|
||||
}
|
||||
|
||||
// Display a message when the grid field is empty
|
||||
if(!(isset($content['body']) && $content['body'])) {
|
||||
$content['body'] = FormField::create_tag(
|
||||
'tr',
|
||||
array("class" => 'ss-gridfield-item ss-gridfield-no-items'),
|
||||
FormField::create_tag(
|
||||
'td',
|
||||
array('colspan' => count($columns)),
|
||||
_t('GridField.NoItemsFound', 'No items found')
|
||||
)
|
||||
// Display a message when the grid field is empty.
|
||||
|
||||
if(empty($content['body'])) {
|
||||
$cell = FormField::create_tag(
|
||||
'td',
|
||||
array(
|
||||
'colspan' => count($columns),
|
||||
),
|
||||
_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
|
||||
$head = $content['header']
|
||||
? FormField::create_tag('thead', array(), $content['header'])
|
||||
: '';
|
||||
$body = $content['body']
|
||||
? FormField::create_tag('tbody', array('class' => 'ss-gridfield-items'), $content['body'])
|
||||
: '';
|
||||
$foot = $content['footer']
|
||||
? FormField::create_tag('tfoot', array(), $content['footer'])
|
||||
: '';
|
||||
$header = $this->getOptionalTableHeader($content);
|
||||
$body = $this->getOptionalTableBody($content);
|
||||
$footer = $this->getOptionalTableFooter($content);
|
||||
|
||||
$this->addExtraClass('ss-gridfield field');
|
||||
$attrs = array_diff_key(
|
||||
|
||||
$fieldsetAttributes = array_diff_key(
|
||||
$this->getAttributes(),
|
||||
array('value' => false, 'type' => false, 'name' => false)
|
||||
array(
|
||||
'value' => false,
|
||||
'type' => false,
|
||||
'name' => false,
|
||||
)
|
||||
);
|
||||
$attrs['data-name'] = $this->getName();
|
||||
$tableAttrs = array(
|
||||
'id' => isset($this->id) ? $this->id : null,
|
||||
|
||||
$fieldsetAttributes['data-name'] = $this->getName();
|
||||
|
||||
$tableId = null;
|
||||
|
||||
if($this->id) {
|
||||
$tableId = $this->id;
|
||||
}
|
||||
|
||||
$tableAttributes = array(
|
||||
'id' => $tableId,
|
||||
'class' => 'ss-gridfield-table',
|
||||
'cellpadding' => '0',
|
||||
'cellspacing' => '0'
|
||||
'cellspacing' => '0',
|
||||
);
|
||||
|
||||
if($this->getDescription()) {
|
||||
@ -418,12 +495,17 @@ class GridField extends FormField {
|
||||
);
|
||||
}
|
||||
|
||||
return
|
||||
FormField::create_tag('fieldset', $attrs,
|
||||
$content['before'] .
|
||||
FormField::create_tag('table', $tableAttrs, $head . "\n" . $foot . "\n" . $body) .
|
||||
$content['after']
|
||||
);
|
||||
$table = FormField::create_tag(
|
||||
'table',
|
||||
$tableAttributes,
|
||||
$header . "\n" . $footer . "\n" . $body
|
||||
);
|
||||
|
||||
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[] = ($index % 2) ? 'even' : 'odd';
|
||||
if($index % 2) {
|
||||
$classes[] = 'even';
|
||||
} else {
|
||||
$classes[] = 'odd';
|
||||
}
|
||||
|
||||
return $classes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $properties
|
||||
*
|
||||
* @return HTMLText
|
||||
*/
|
||||
public function Field($properties = array()) {
|
||||
return $this->FieldHolder($properties);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
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
|
||||
*/
|
||||
public function getColumns() {
|
||||
// Get column list
|
||||
$columns = array();
|
||||
|
||||
foreach($this->getComponents() as $item) {
|
||||
if($item instanceof GridField_ColumnProvider) {
|
||||
$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 string $column
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function getColumnContent($record, $column) {
|
||||
// Build the column dispatch
|
||||
if(!$this->columnDispatch) {
|
||||
$this->buildColumnDispatch();
|
||||
}
|
||||
|
||||
if(!empty($this->columnDispatch[$column])) {
|
||||
$content = "";
|
||||
$content = '';
|
||||
|
||||
foreach($this->columnDispatch[$column] as $handler) {
|
||||
/**
|
||||
* @var GridField_ColumnProvider $handler
|
||||
*/
|
||||
$content .= $handler->getColumnContent($this, $record, $column);
|
||||
}
|
||||
|
||||
return $content;
|
||||
} 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.
|
||||
* 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) {
|
||||
// Custom callbacks
|
||||
if(isset($this->customDataFields[$fieldName])) {
|
||||
$callback = $this->customDataFields[$fieldName];
|
||||
|
||||
return $callback($record);
|
||||
}
|
||||
|
||||
// Default implementation
|
||||
if($record->hasMethod('relField')) {
|
||||
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 string $column
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @throws LogicException
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function getColumnAttributes($record, $column) {
|
||||
// Build the column dispatch
|
||||
if(!$this->columnDispatch) {
|
||||
$this->buildColumnDispatch();
|
||||
}
|
||||
|
||||
if(!empty($this->columnDispatch[$column])) {
|
||||
$attrs = array();
|
||||
$attributes = array();
|
||||
|
||||
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)) {
|
||||
$attrs = array_merge($attrs, $column_attrs);
|
||||
} elseif($column_attrs) {
|
||||
$methodSignature = get_class($handler) . "::getColumnAttributes()";
|
||||
throw new LogicException("Non-array response from $methodSignature.");
|
||||
if(is_array($columnAttributes)) {
|
||||
$attributes = array_merge($attributes, $columnAttributes);
|
||||
continue;
|
||||
}
|
||||
|
||||
throw new LogicException(sprintf(
|
||||
'Non-array response from %s::getColumnAttributes().',
|
||||
get_class($handler)
|
||||
));
|
||||
}
|
||||
|
||||
return $attrs;
|
||||
} else {
|
||||
throw new InvalidArgumentException("Bad column '$column'");
|
||||
return $attributes;
|
||||
}
|
||||
|
||||
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
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @throws LogicException
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function getColumnMetadata($column) {
|
||||
// Build the column dispatch
|
||||
if(!$this->columnDispatch) {
|
||||
$this->buildColumnDispatch();
|
||||
}
|
||||
|
||||
if(!empty($this->columnDispatch[$column])) {
|
||||
$metadata = array();
|
||||
$metaData = array();
|
||||
|
||||
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)) {
|
||||
$metadata = array_merge($metadata, $column_metadata);
|
||||
} else {
|
||||
$methodSignature = get_class($handler) . "::getColumnMetadata()";
|
||||
throw new LogicException("Non-array response from $methodSignature.");
|
||||
if(is_array($columnMetaData)) {
|
||||
$metaData = array_merge($metaData, $columnMetaData);
|
||||
continue;
|
||||
}
|
||||
|
||||
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
|
||||
*/
|
||||
public function getColumnCount() {
|
||||
// Build the column dispatch
|
||||
if(!$this->columnDispatch) $this->buildColumnDispatch();
|
||||
if(!$this->columnDispatch) {
|
||||
$this->buildColumnDispatch();
|
||||
}
|
||||
|
||||
return count($this->columnDispatch);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build an columnDispatch that maps a GridField_ColumnProvider to a column
|
||||
* for reference later
|
||||
*
|
||||
* Build an columnDispatch that maps a GridField_ColumnProvider to a column for reference later.
|
||||
*/
|
||||
protected function buildColumnDispatch() {
|
||||
$this->columnDispatch = array();
|
||||
@ -691,140 +826,172 @@ class GridField extends FormField {
|
||||
* This is the action that gets executed when a GridField_AlterAction gets clicked.
|
||||
*
|
||||
* @param array $data
|
||||
* @param Form $form
|
||||
* @param SS_HTTPRequest $request
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function gridFieldAlterAction($data, $form, SS_HTTPRequest $request) {
|
||||
$html = '';
|
||||
$data = $request->requestVars();
|
||||
$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);
|
||||
|
||||
if(isset($fieldData['GridState'])) {
|
||||
$state->setValue($fieldData['GridState']);
|
||||
}
|
||||
|
||||
// Try to execute alter action
|
||||
foreach($data as $k => $v) {
|
||||
if(preg_match('/^action_gridFieldAlterAction\?StateID=(.*)/', $k, $matches)) {
|
||||
$id = $matches[1];
|
||||
$stateChange = Session::get($id);
|
||||
|
||||
foreach($data as $dataKey => $dataValue) {
|
||||
if(preg_match('/^action_gridFieldAlterAction\?StateID=(.*)/', $dataKey, $matches)) {
|
||||
$stateChange = Session::get($matches[1]);
|
||||
$actionName = $stateChange['actionName'];
|
||||
|
||||
$args = isset($stateChange['args']) ? $stateChange['args'] : array();
|
||||
$html = $this->handleAlterAction($actionName, $args, $data);
|
||||
// A field can optionally return its own HTML
|
||||
if($html) return $html;
|
||||
$arguments = array();
|
||||
|
||||
if(isset($stateChange['args'])) {
|
||||
$arguments = $stateChange['args'];
|
||||
};
|
||||
|
||||
$html = $this->handleAlterAction($actionName, $arguments, $data);
|
||||
|
||||
if($html) {
|
||||
return $html;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch($request->getHeader('X-Pjax')) {
|
||||
case 'CurrentField':
|
||||
return $this->FieldHolder();
|
||||
break;
|
||||
|
||||
case 'CurrentForm':
|
||||
return $form->forTemplate();
|
||||
break;
|
||||
|
||||
default:
|
||||
return $form->forTemplate();
|
||||
break;
|
||||
if($request->getHeader('X-Pjax') === 'CurrentField') {
|
||||
return $this->FieldHolder();
|
||||
}
|
||||
|
||||
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 mixed $args
|
||||
* @param array $data - send data from a form
|
||||
* @param mixed $arguments
|
||||
* @param array $data
|
||||
*
|
||||
* @return mixed
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function handleAlterAction($actionName, $args, $data) {
|
||||
public function handleAlterAction($actionName, $arguments, $data) {
|
||||
$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)))) {
|
||||
return $component->handleAction($this, $actionName, $args, $data);
|
||||
foreach($this->getComponents() as $component) {
|
||||
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) {
|
||||
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->setDataModel($model);
|
||||
|
||||
$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) {
|
||||
if(!($component instanceof GridField_URLHandler)) {
|
||||
continue;
|
||||
}
|
||||
if($component instanceof GridField_URLHandler && $urlHandlers = $component->getURLHandlers($this)) {
|
||||
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($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));
|
||||
if($action[0] == '$') {
|
||||
$action = $params[substr($action, 1)];
|
||||
}
|
||||
|
||||
try {
|
||||
$result = $component->$action($this, $request);
|
||||
} catch(SS_HTTPResponse_Exception $responseException) {
|
||||
$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");
|
||||
if(!method_exists($component, 'checkAccessAction') || $component->checkAccessAction($action)) {
|
||||
if(!$action) {
|
||||
$action = "index";
|
||||
}
|
||||
|
||||
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
|
||||
} else if($request->allParsed()) {
|
||||
return $result;
|
||||
try {
|
||||
$result = $component->$action($this, $request);
|
||||
} 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
|
||||
} else {
|
||||
return $this->httpError(404,
|
||||
"I can't handle sub-URLs of a " . get_class($result) . " object.");
|
||||
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($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);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function saveInto(DataObjectInterface $record) {
|
||||
foreach($this->getComponents() as $component) {
|
||||
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
|
||||
* the state of the {@link GridField}, rendered as a button element.
|
||||
* This class is the base class when you want to have an action that alters the state of the
|
||||
* {@link GridField}, rendered as a button element.
|
||||
*
|
||||
* @package forms
|
||||
* @package forms
|
||||
* @subpackage fields-gridfield
|
||||
*/
|
||||
class GridField_FormAction extends FormAction {
|
||||
|
||||
/**
|
||||
* @var GridField
|
||||
*/
|
||||
@ -882,7 +1095,7 @@ class GridField_FormAction extends FormAction {
|
||||
/**
|
||||
* @param GridField $gridField
|
||||
* @param string $name
|
||||
* @param string $label
|
||||
* @param string $title
|
||||
* @param string $actionName
|
||||
* @param array $args
|
||||
*/
|
||||
@ -895,19 +1108,20 @@ class GridField_FormAction extends FormAction {
|
||||
}
|
||||
|
||||
/**
|
||||
* urlencode encodes less characters in percent form than we need - we
|
||||
* need everything that isn't a \w.
|
||||
* Encode all non-word characters.
|
||||
*
|
||||
* @param string $val
|
||||
* @param string $value
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function nameEncode($val) {
|
||||
return preg_replace_callback('/[^\w]/', array($this, '_nameEncode'), $val);
|
||||
public function nameEncode($value) {
|
||||
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) {
|
||||
return '%' . dechex(ord($match[0]));
|
||||
@ -941,9 +1155,7 @@ class GridField_FormAction extends FormAction {
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the name of the gridfield relative to the Form
|
||||
*
|
||||
* @param GridField $base
|
||||
* Calculate the name of the gridfield relative to the form.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
|
@ -107,7 +107,7 @@ class GridFieldDeleteAction implements GridField_ColumnProvider, GridField_Actio
|
||||
if($this->removeRelation) {
|
||||
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))
|
||||
->addExtraClass('gridfield-button-unlink')
|
||||
->setAttribute('title', _t('GridAction.UnlinkRelation', "Unlink"))
|
||||
@ -115,14 +115,13 @@ class GridFieldDeleteAction implements GridField_ColumnProvider, GridField_Actio
|
||||
} else {
|
||||
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))
|
||||
->addExtraClass('gridfield-button-delete')
|
||||
->setAttribute('title', _t('GridAction.Delete', "Delete"))
|
||||
->setAttribute('data-icon', 'cross-circle')
|
||||
->setDescription(_t('GridAction.DELETE_DESCRIPTION','Delete'));
|
||||
}
|
||||
|
||||
return $field->Field();
|
||||
}
|
||||
|
||||
|
@ -19,8 +19,9 @@ require_once 'i18nSSLegacyAdapter.php';
|
||||
*
|
||||
* Templates:
|
||||
* <code>
|
||||
* <% _t('MyNamespace.MYENTITY', 'My default natural language value') %>
|
||||
* <% sprintf(_t('MyNamespace.MYENTITY','Counting %s things'),$ThingsCount) %>
|
||||
* <%t MyNamespace.MYENTITY 'My default natural language value' %>
|
||||
* <%t MyNamespace.MYENTITY 'Counting %s things' s=$ThingsCount %>
|
||||
* <%t MyNamespace.MYENTITY 'Counting {count} things' count=$ThingsCount %>
|
||||
* </code>
|
||||
*
|
||||
* Javascript (see framework/javascript/i18n.js):
|
||||
@ -137,54 +138,56 @@ class i18n extends Object implements TemplateGlobalProvider, Flushable {
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated 3.2 Use the "i18n.js_i18n" config setting instead
|
||||
* @deprecated 4.0 Use the "i18n.js_i18n" config setting instead
|
||||
* @return bool
|
||||
*/
|
||||
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');
|
||||
}
|
||||
|
||||
/**
|
||||
* @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
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since version 4.0
|
||||
* @return string ISO 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');
|
||||
}
|
||||
|
||||
/**
|
||||
* @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
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since version 4.0
|
||||
* @return string ISO 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');
|
||||
}
|
||||
|
||||
@ -2224,10 +2227,11 @@ class i18n extends Object implements TemplateGlobalProvider, Flushable {
|
||||
/**
|
||||
* Get a list of locales (code => language and country)
|
||||
*
|
||||
* @deprecated since version 4.0
|
||||
* @return list of languages in the form 'code' => 'name'
|
||||
*/
|
||||
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');
|
||||
}
|
||||
|
||||
|
@ -9,13 +9,16 @@
|
||||
* of your values.
|
||||
* For any statics containing natural language, never use the static directly -
|
||||
* 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
|
||||
* @subpackage i18n
|
||||
* @uses i18nTextCollector->collectFromEntityProviders()
|
||||
*/
|
||||
interface i18nEntityProvider {
|
||||
|
||||
|
||||
/**
|
||||
* Example usage:
|
||||
* <code>
|
||||
@ -25,13 +28,13 @@ interface i18nEntityProvider {
|
||||
* foreach($this->stat('my_static_array) as $key => $value) {
|
||||
* $entities["MyTestClass.my_static_array_{$key}"] = array(
|
||||
* $value,
|
||||
*
|
||||
*
|
||||
* 'My context description'
|
||||
* );
|
||||
* }
|
||||
* return $entities;
|
||||
* }
|
||||
*
|
||||
*
|
||||
* public static function my_static_array() {
|
||||
* $t_my_static_array = array();
|
||||
* foreach(self::$my_static_array as $k => $v) {
|
||||
@ -41,9 +44,9 @@ interface i18nEntityProvider {
|
||||
* }
|
||||
* }
|
||||
* </code>
|
||||
*
|
||||
*
|
||||
* Example usage in {@link DataObject->provideI18nEntities()}.
|
||||
*
|
||||
*
|
||||
* You can ask textcollector to add the provided entity to a different module
|
||||
* than the class is contained in by adding a 4th argument to the array:
|
||||
* <code>
|
||||
@ -52,7 +55,7 @@ interface i18nEntityProvider {
|
||||
* $entities = array();
|
||||
* $entities["MyOtherModuleClass.MYENTITY"] = array(
|
||||
* $value,
|
||||
*
|
||||
*
|
||||
* 'My context description',
|
||||
* 'myothermodule'
|
||||
* );
|
||||
@ -60,7 +63,7 @@ interface i18nEntityProvider {
|
||||
* return $entities;
|
||||
* }
|
||||
* </code>
|
||||
*
|
||||
*
|
||||
* @return array All entites in an associative array, with
|
||||
* entity name as the key, and a numerical array of pseudo-arguments
|
||||
* for _t() as a value.
|
||||
|
@ -27,15 +27,28 @@
|
||||
*/
|
||||
class i18nTextCollector extends Object {
|
||||
|
||||
/**
|
||||
* Default (master) locale
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
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()}.
|
||||
*
|
||||
* @todo Fully support changing of basePath through {@link SSViewer} and {@link ManifestBuilder}
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $basePath;
|
||||
|
||||
/**
|
||||
* Save path
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $baseSavePath;
|
||||
|
||||
/**
|
||||
@ -43,99 +56,267 @@ class i18nTextCollector extends Object {
|
||||
*/
|
||||
protected $writer;
|
||||
|
||||
/**
|
||||
* List of file extensions to parse
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $fileExtensions = array('php', 'ss');
|
||||
|
||||
/**
|
||||
* @param $locale
|
||||
*/
|
||||
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->baseSavePath = Director::baseFolder();
|
||||
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Assign a writer
|
||||
*
|
||||
* @param i18nTextCollector_Writer $writer
|
||||
*/
|
||||
public function setWriter($writer) {
|
||||
$this->writer = $writer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the currently assigned writer, or the default if none is specified.
|
||||
*
|
||||
* @return i18nTextCollector_Writer
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is the main method to build the master string tables with the original strings.
|
||||
* It will search for existent modules that use the i18n feature, parse the _t() calls
|
||||
* and write the resultant files in the lang folder of each module.
|
||||
* This is the main method to build the master string tables with the
|
||||
* original strings. It will search for existent modules that use the
|
||||
* i18n feature, parse the _t() calls and write the resultant files
|
||||
* in the lang folder of each module.
|
||||
*
|
||||
* @uses DataObject->collectI18nStatics()
|
||||
*
|
||||
* @param array $restrictToModules
|
||||
* @param array $mergeWithExisting Merge new master strings with existing ones
|
||||
* already defined in language files, rather than replacing them. This can be useful
|
||||
* for long-term maintenance of translations across releases, because it allows
|
||||
* "translation backports" to older releases without removing strings these older releases
|
||||
* still rely on.
|
||||
* @param bool $mergeWithExisting Merge new master strings with existing
|
||||
* ones already defined in language files, rather than replacing them.
|
||||
* This can be useful for long-term maintenance of translations across
|
||||
* releases, because it allows "translation backports" to older releases
|
||||
* without removing strings these older releases still rely on.
|
||||
*/
|
||||
public function run($restrictToModules = null, $mergeWithExisting = false) {
|
||||
$entitiesByModule = $this->collect($restrictToModules, $mergeWithExisting);
|
||||
if(empty($entitiesByModule)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Write each module language file
|
||||
if($entitiesByModule) foreach($entitiesByModule as $module => $entities) {
|
||||
$this->getWriter()->write($entities, $this->defaultLocale, $this->baseSavePath . '/' . $module);
|
||||
foreach($entitiesByModule as $module => $entities) {
|
||||
// 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);
|
||||
$themeFolders = array();
|
||||
/**
|
||||
* Gets the list of modules in this installer
|
||||
*
|
||||
* @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)
|
||||
$entitiesByModule = array();
|
||||
// Get all standard modules
|
||||
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){
|
||||
if($module != 'themes') continue;
|
||||
else {
|
||||
$themes = scandir($this->basePath."/themes");
|
||||
if(count($themes)){
|
||||
foreach($themes as $theme) {
|
||||
if(is_dir($this->basePath."/themes/".$theme)
|
||||
&& substr($theme,0,1) != '.'
|
||||
&& is_dir($this->basePath."/themes/".$theme."/templates")){
|
||||
|
||||
$themeFolders[] = 'themes/'.$theme;
|
||||
}
|
||||
}
|
||||
}
|
||||
$themesInd = $index;
|
||||
// Get all themes
|
||||
foreach(glob($directory."/themes/*", GLOB_ONLYDIR) as $path) {
|
||||
// Check for templates
|
||||
if(is_dir("$path/templates")) {
|
||||
$modules[] = 'themes/'.basename($path);
|
||||
}
|
||||
}
|
||||
|
||||
if(isset($themesInd)) {
|
||||
unset($modules[$themesInd]);
|
||||
return $modules;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
// (which means they are modules, including themes folder)
|
||||
$isValidModuleFolder = (
|
||||
is_dir("$this->basePath/$module")
|
||||
&& substr($module,0,1) != '.'
|
||||
&& (
|
||||
is_file("$this->basePath/$module/_config.php")
|
||||
|| is_dir("$this->basePath/$module/_config")
|
||||
)
|
||||
) || (
|
||||
substr($module,0,7) == 'themes/'
|
||||
&& is_dir("$this->basePath/$module")
|
||||
/**
|
||||
* Resolve conflicts between duplicate keys across modules
|
||||
*
|
||||
* @param array $entitiesByModule List of all modules with keys
|
||||
* @return array Filtered listo of modules with duplicate keys unassigned
|
||||
*/
|
||||
protected function resolveDuplicateConflicts($entitiesByModule) {
|
||||
// Find all keys that exist across multiple modules
|
||||
$conflicts = $this->getConflicts($entitiesByModule);
|
||||
foreach($conflicts as $conflict) {
|
||||
// Determine if we can narrow down the ownership
|
||||
$bestModule = $this->getBestModuleForKey($entitiesByModule, $conflict);
|
||||
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
|
||||
$processedEntities = $this->processModule($module);
|
||||
|
||||
if(isset($entitiesByModule[$module])) {
|
||||
$entitiesByModule[$module] = array_merge_recursive($entitiesByModule[$module], $processedEntities);
|
||||
} else {
|
||||
@ -143,54 +324,23 @@ class i18nTextCollector extends Object {
|
||||
}
|
||||
|
||||
// extract all entities for "foreign" modules (fourth argument)
|
||||
// @see CMSMenu::provideI18nEntities for an example usage
|
||||
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];
|
||||
if(!isset($entitiesByModule[$othermodule])) $entitiesByModule[$othermodule] = array();
|
||||
if(!isset($entitiesByModule[$othermodule])) {
|
||||
$entitiesByModule[$othermodule] = array();
|
||||
}
|
||||
unset($spec[2]);
|
||||
$entitiesByModule[$othermodule][$fullName] = $spec;
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
public function write($module, $entities) {
|
||||
$this->getWriter()->write($entities, $this->defaultLocale, $this->baseSavePath . '/' . $module);
|
||||
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
|
||||
* @see collectFromCode() and collectFromTemplate()
|
||||
*
|
||||
* @param string $module A module's name or just 'themes'
|
||||
* @return array $entities An array of entities found in the files that comprise the module
|
||||
* @todo Why the type juggling for $this->collectFromBlah()? They always return arrays.
|
||||
* @param string $module A module's name or just 'themes/<themename>'
|
||||
* @return array An array of entities found in the files that comprise the module
|
||||
*/
|
||||
protected function processModule($module) {
|
||||
$entities = array();
|
||||
|
||||
// Search for calls in code files if these exists
|
||||
$fileList = array();
|
||||
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)$/');
|
||||
}
|
||||
$fileList = $this->getFileListForModule($module);
|
||||
foreach($fileList as $filePath) {
|
||||
// exclude ss-templates, they're scanned separately
|
||||
if(substr($filePath,-3) == 'php') {
|
||||
$content = file_get_contents($filePath);
|
||||
$entities = array_merge($entities,(array)$this->collectFromCode($content, $module));
|
||||
$entities = array_merge($entities, (array)$this->collectFromEntityProviders($filePath, $module));
|
||||
}
|
||||
}
|
||||
|
||||
// Search for calls in template files if these exists
|
||||
if(is_dir("$this->basePath/$module/")) {
|
||||
$fileList = $this->getFilesRecursive("$this->basePath/$module/", null, 'ss');
|
||||
foreach($fileList as $index => $filePath) {
|
||||
$content = file_get_contents($filePath);
|
||||
$extension = pathinfo($filePath, PATHINFO_EXTENSION);
|
||||
$content = file_get_contents($filePath);
|
||||
// Filter based on extension
|
||||
if($extension === 'php') {
|
||||
$entities = array_merge(
|
||||
$entities,
|
||||
$this->collectFromCode($content, $module),
|
||||
$this->collectFromEntityProviders($filePath, $module)
|
||||
);
|
||||
} elseif($extension === 'ss') {
|
||||
// templates use their filename as a namespace
|
||||
$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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
public function collectFromCode($content, $module) {
|
||||
@ -328,33 +504,10 @@ class i18nTextCollector extends Object {
|
||||
* @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
|
||||
* @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()) {
|
||||
$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
|
||||
$translatables = i18nTextCollector_Parser::GetTranslatables($content);
|
||||
$entities = array_merge($entities,(array)$translatables);
|
||||
$entities = i18nTextCollector_Parser::GetTranslatables($content);
|
||||
|
||||
// use the old method of getting _t() style translatable entities
|
||||
// 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
|
||||
* @param string $filePath
|
||||
* @param string $module
|
||||
* @return array
|
||||
*/
|
||||
public function collectFromEntityProviders($filePath, $module = null) {
|
||||
$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);
|
||||
if($classes) foreach($classes as $class) {
|
||||
// Not all classes can be instanciated without mandatory arguments,
|
||||
// so entity collection doesn't work for all SilverStripe classes currently
|
||||
// Requires PHP 5.1+
|
||||
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());
|
||||
foreach($classes as $class) {
|
||||
// Skip non-implementing classes
|
||||
if(!class_exists($class) || !in_array('i18nEntityProvider', class_implements($class))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip abstract classes
|
||||
$reflectionClass = new ReflectionClass($class);
|
||||
if($reflectionClass->isAbstract()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$obj = singleton($class);
|
||||
$entities = array_merge($entities, (array)$obj->provideI18nEntities());
|
||||
}
|
||||
|
||||
ksort($entities);
|
||||
@ -442,30 +598,35 @@ class i18nTextCollector extends Object {
|
||||
*
|
||||
* @param string $folder base directory to scan (will scan recursively)
|
||||
* @param array $fileList Array to which potential files will be appended
|
||||
* @param string $type Optional, "php" or "ss"
|
||||
* @param string $folderExclude Regular expression matching folder names to exclude
|
||||
* @param string $type Optional, "php" or "ss" only
|
||||
* @param string $folderExclude Regular expression matching folder names to exclude
|
||||
* @return array $fileList An array of files
|
||||
*/
|
||||
protected function getFilesRecursive($folder, $fileList = null, $type = null, $folderExclude = null) {
|
||||
if(!$folderExclude) $folderExclude = '/\/(tests)$/';
|
||||
if(!$fileList) $fileList = array();
|
||||
$items = scandir($folder);
|
||||
$isValidFolder = (
|
||||
!in_array('_manifest_exclude', $items)
|
||||
&& !preg_match($folderExclude, $folder)
|
||||
);
|
||||
protected function getFilesRecursive($folder, $fileList = array(), $type = null, $folderExclude = '/\/(tests)$/') {
|
||||
if(!$fileList) {
|
||||
$fileList = array();
|
||||
}
|
||||
// Skip ignored folders
|
||||
if(is_file("{$folder}/_manifest_exclude") || preg_match($folderExclude, $folder)) {
|
||||
return $fileList;
|
||||
}
|
||||
|
||||
if($items && $isValidFolder) foreach($items as $item) {
|
||||
if(substr($item,0,1) == '.') continue;
|
||||
if(substr($item,-4) == '.php' && (!$type || $type == 'php')) {
|
||||
$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")) {
|
||||
foreach(glob($folder.'/*') as $path) {
|
||||
// Recurse if directory
|
||||
if(is_dir($path)) {
|
||||
$fileList = array_merge(
|
||||
$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;
|
||||
@ -691,7 +852,9 @@ class i18nTextCollector_Parser extends SSTemplateParser {
|
||||
|
||||
// Run the parser and throw away the result
|
||||
$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();
|
||||
|
||||
return self::$entities;
|
||||
|
@ -7,22 +7,59 @@
|
||||
IgnoreTabState: false,
|
||||
|
||||
onadd: function() {
|
||||
var hash = window.location.hash;
|
||||
|
||||
// Can't name redraw() as it clashes with other CMS entwine classes
|
||||
this.redrawTabs();
|
||||
|
||||
if (hash !== '') {
|
||||
this.openTabFromURL(hash);
|
||||
}
|
||||
|
||||
this._super();
|
||||
},
|
||||
|
||||
onremove: function() {
|
||||
if(this.data('tabs')) this.tabs('destroy');
|
||||
this._super();
|
||||
},
|
||||
|
||||
redrawTabs: function() {
|
||||
this.rewriteHashlinks();
|
||||
this.tabs();
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Ensure hash links are prefixed with the current page URL,
|
||||
* otherwise jQuery interprets them as being external.
|
||||
* @func openTabFromURL
|
||||
* @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() {
|
||||
$(this).find('ul a').each(function() {
|
||||
|
19
lang/en.yml
19
lang/en.yml
@ -60,8 +60,8 @@ en:
|
||||
ERRORNOTREC: 'That username / password isn''t recognised'
|
||||
Boolean:
|
||||
ANY: Any
|
||||
NOANSWER: No
|
||||
YESANSWER: Yes
|
||||
NOANSWER: 'No'
|
||||
YESANSWER: 'Yes'
|
||||
CMSLoadingScreen_ss:
|
||||
LOADING: Loading...
|
||||
REQUIREJS: 'The CMS requires that you have JavaScript enabled.'
|
||||
@ -81,8 +81,8 @@ en:
|
||||
HELLO: Hi
|
||||
PASSWORD: Password
|
||||
CheckboxField:
|
||||
NOANSWER: No
|
||||
YESANSWER: Yes
|
||||
NOANSWER: 'No'
|
||||
YESANSWER: 'Yes'
|
||||
CheckboxFieldSetField:
|
||||
SOURCE_VALIDATION: 'Please select a value within the list provided. {value} is not a valid option'
|
||||
CMSMemberLoginForm:
|
||||
@ -192,7 +192,7 @@ en:
|
||||
TEXT3: for
|
||||
Form:
|
||||
CSRF_EXPIRED_MESSAGE: 'Your session has expired. Please re-submit the form.'
|
||||
CSRF_FAILED_MESSAGE: 'There seems to have been a technical problem. Please click the back button, refresh your browser, and try again.'
|
||||
CSRF_FAILED_MESSAGE: 'There seems to have been a technical problem. Please click the back button, refresh your browser, and try again.'
|
||||
FIELDISREQUIRED: '{name} is required'
|
||||
SubmitBtnLabel: Go
|
||||
VALIDATIONCREDITNUMBER: 'Please ensure you have entered the {number} credit card number correctly'
|
||||
@ -259,7 +259,7 @@ en:
|
||||
many_many_Members: Members
|
||||
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>'
|
||||
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'
|
||||
ResultDeleted: 'Deleted %d groups'
|
||||
ResultUpdated: 'Updated %d groups'
|
||||
@ -313,6 +313,7 @@ en:
|
||||
URL: URL
|
||||
URLNOTANOEMBEDRESOURCE: 'The URL ''{url}'' could not be turned into a media resource.'
|
||||
UpdateMEDIA: 'Update Media'
|
||||
SUBJECT: 'Email subject'
|
||||
Image:
|
||||
PLURALNAME: Files
|
||||
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.'
|
||||
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.'
|
||||
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
|
||||
REORGANISATIONSUCCESSFUL: 'Reorganised the site tree successfully.'
|
||||
SAVEDUP: Saved.
|
||||
@ -342,7 +343,7 @@ en:
|
||||
Hello: Hi
|
||||
LOGOUT: 'Log out'
|
||||
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:
|
||||
Email: 'Email Address'
|
||||
IP: 'IP Address'
|
||||
@ -585,3 +586,5 @@ en:
|
||||
UPLOADSINTO: 'saves into /{path}'
|
||||
Versioned:
|
||||
has_many_Versions: Versions
|
||||
CheckboxSetField:
|
||||
SOURCE_VALIDATION: 'Please select a value within the list provided. ''{value}'' is not a valid option'
|
||||
|
47
main.php
47
main.php
@ -67,37 +67,44 @@ if(!empty($_SERVER['HTTP_X_ORIGINAL_URL'])) {
|
||||
*/
|
||||
global $url;
|
||||
|
||||
// PHP 5.4's built-in webserver uses this
|
||||
if (php_sapi_name() == 'cli-server') {
|
||||
$url = $_SERVER['REQUEST_URI'];
|
||||
// Helper to safely parse and load a querystring fragment
|
||||
$parseQuery = function($query) {
|
||||
parse_str($query, $_GET);
|
||||
if ($_GET) $_REQUEST = array_merge((array)$_REQUEST, (array)$_GET);
|
||||
};
|
||||
|
||||
// Querystring args need to be explicitly parsed
|
||||
if(strpos($url,'?') !== false) {
|
||||
list($url, $query) = explode('?',$url,2);
|
||||
parse_str($query, $_GET);
|
||||
if ($_GET) $_REQUEST = array_merge((array)$_REQUEST, (array)$_GET);
|
||||
// Apache rewrite rules and IIS use this
|
||||
if (isset($_GET['url']) && php_sapi_name() !== 'cli-server') {
|
||||
|
||||
// Prevent injection of url= querystring argument by prioritising any leading url argument
|
||||
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'];
|
||||
|
||||
// IIS includes get variables in url
|
||||
$i = strpos($url, '?');
|
||||
if($i !== false) {
|
||||
$url = substr($url, 0, $i);
|
||||
}
|
||||
|
||||
// Lighttpd uses this
|
||||
// Lighttpd and PHP 5.4's built-in webserver use this
|
||||
} else {
|
||||
if(strpos($_SERVER['REQUEST_URI'],'?') !== false) {
|
||||
list($url, $query) = explode('?', $_SERVER['REQUEST_URI'], 2);
|
||||
parse_str($query, $_GET);
|
||||
if ($_GET) $_REQUEST = array_merge((array)$_REQUEST, (array)$_GET);
|
||||
} else {
|
||||
$url = $_SERVER["REQUEST_URI"];
|
||||
$url = $_SERVER['REQUEST_URI'];
|
||||
|
||||
// Querystring args need to be explicitly parsed
|
||||
if(strpos($url,'?') !== false) {
|
||||
list($url, $query) = explode('?',$url,2);
|
||||
$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;
|
||||
}
|
||||
}
|
||||
|
||||
|
72
model/DB.php
72
model/DB.php
@ -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') {
|
||||
Deprecation::notice('3.3', 'Use DB::set_conn instead');
|
||||
Deprecation::notice('4.0', 'Use DB::set_conn instead');
|
||||
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') {
|
||||
Deprecation::notice('3.3', 'Use DB::get_conn instead');
|
||||
Deprecation::notice('4.0', 'Use DB::get_conn instead');
|
||||
return self::get_conn($name);
|
||||
}
|
||||
|
||||
@ -135,6 +135,10 @@ class DB {
|
||||
* Set it to null to revert to the main database.
|
||||
*/
|
||||
public static function set_alternative_database_name($name = null) {
|
||||
// Skip if CLI
|
||||
if(Director::is_cli()) {
|
||||
return;
|
||||
}
|
||||
if($name) {
|
||||
if(!self::valid_alternative_database_name($name)) {
|
||||
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) {
|
||||
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) {
|
||||
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);
|
||||
}
|
||||
|
||||
@ -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() {
|
||||
Deprecation::notice('3.3', 'Use DB::is_active instead');
|
||||
Deprecation::notice('4.0', 'Use DB::is_active instead');
|
||||
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) {
|
||||
Deprecation::notice('3.3', 'Use DB::create_database instead');
|
||||
Deprecation::notice('4.0', 'Use DB::create_database instead');
|
||||
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) {
|
||||
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);
|
||||
}
|
||||
|
||||
@ -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) {
|
||||
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);
|
||||
}
|
||||
|
||||
@ -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,
|
||||
$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);
|
||||
}
|
||||
|
||||
@ -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) {
|
||||
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);
|
||||
}
|
||||
|
||||
@ -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) {
|
||||
Deprecation::notice('3.3', 'Use DB::require_index instead');
|
||||
Deprecation::notice('4.0', 'Use DB::require_index instead');
|
||||
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) {
|
||||
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);
|
||||
}
|
||||
|
||||
@ -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) {
|
||||
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);
|
||||
}
|
||||
|
||||
@ -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) {
|
||||
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);
|
||||
}
|
||||
|
||||
@ -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() {
|
||||
Deprecation::notice('3.3', 'Use DB::affected_rows instead');
|
||||
Deprecation::notice('4.0', 'Use DB::affected_rows instead');
|
||||
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() {
|
||||
Deprecation::notice('3.3', 'Use DB::table_list instead');
|
||||
Deprecation::notice('4.0', 'Use DB::table_list instead');
|
||||
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) {
|
||||
Deprecation::notice('3.3', 'Use DB::field_list instead');
|
||||
Deprecation::notice('4.0', 'Use DB::field_list instead');
|
||||
return self::field_list($table);
|
||||
}
|
||||
|
||||
|
@ -24,7 +24,7 @@ abstract class DataExtension extends Extension {
|
||||
* Edit the given query object to support queries for this extension
|
||||
*
|
||||
* @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) {
|
||||
}
|
||||
|
@ -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
|
||||
*
|
||||
* @param array $filterArray
|
||||
*/
|
||||
public function addFilter($filterArray) {
|
||||
$list = $this;
|
||||
@ -423,9 +425,9 @@ class DataList extends ViewableData implements SS_List, SS_Filterable, SS_Sortab
|
||||
$whereArguments = func_get_arg(0);
|
||||
} elseif($numberFuncArgs == 2) {
|
||||
$whereArguments[func_get_arg(0)] = func_get_arg(1);
|
||||
} else {
|
||||
} else {
|
||||
throw new InvalidArgumentException('Incorrect number of arguments passed to exclude()');
|
||||
}
|
||||
}
|
||||
|
||||
return $this->alterDataQuery(function($query, $list) use ($whereArguments) {
|
||||
$subquery = $query->disjunctiveGroup();
|
||||
@ -440,16 +442,16 @@ class DataList extends ViewableData implements SS_List, SS_Filterable, SS_Sortab
|
||||
$t = singleton($list->dataClass())->dbObject($field);
|
||||
if($filterType) {
|
||||
$className = "{$filterType}Filter";
|
||||
} else {
|
||||
} else {
|
||||
$className = 'ExactMatchFilter';
|
||||
}
|
||||
if(!class_exists($className)){
|
||||
$className = 'ExactMatchFilter';
|
||||
array_unshift($modifiers, $filterType);
|
||||
}
|
||||
}
|
||||
$t = new $className($field, $value, $modifiers);
|
||||
$t->apply($subquery);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -369,7 +369,7 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
|
||||
if(!isset(DataObject::$_cache_composite_fields[$class])) {
|
||||
self::cache_composite_fields($class);
|
||||
}
|
||||
|
||||
|
||||
if(isset(DataObject::$_cache_composite_fields[$class][$name])) {
|
||||
$isComposite = DataObject::$_cache_composite_fields[$class][$name];
|
||||
} elseif($aggregated && $class != 'DataObject' && ($parentClass=get_parent_class($class)) != 'DataObject') {
|
||||
@ -452,6 +452,10 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
|
||||
$record = null;
|
||||
}
|
||||
|
||||
if(is_a($record, "stdClass")) {
|
||||
$record = (array)$record;
|
||||
}
|
||||
|
||||
// Set $this->record to $record, but ignore NULLs
|
||||
$this->record = array();
|
||||
foreach($record as $k => $v) {
|
||||
@ -1263,6 +1267,7 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
// if database column doesn't correlate to a DBField instance...
|
||||
$fieldObj = $this->dbObject($fieldName);
|
||||
if(!$fieldObj) {
|
||||
@ -1679,7 +1684,7 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
|
||||
} else {
|
||||
$remoteClass = $this->belongsToComponent($component, false);
|
||||
}
|
||||
|
||||
|
||||
if(empty($remoteClass)) {
|
||||
throw new Exception("Unknown $type component '$component' on class '$this->class'");
|
||||
}
|
||||
@ -1771,7 +1776,10 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
|
||||
$componentClass, $table, $componentField, $parentField,
|
||||
$this->manyManyExtraFieldsForComponent($componentName)
|
||||
);
|
||||
|
||||
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
|
||||
// foreignID set elsewhere.
|
||||
@ -2739,6 +2747,7 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
|
||||
public static function has_own_table($dataClass) {
|
||||
if(!is_subclass_of($dataClass,'DataObject')) return false;
|
||||
|
||||
$dataClass = ClassInfo::class_name($dataClass);
|
||||
if(!isset(DataObject::$cache_has_own_table[$dataClass])) {
|
||||
if(get_parent_class($dataClass) == 'DataObject') {
|
||||
DataObject::$cache_has_own_table[$dataClass] = true;
|
||||
@ -3126,6 +3135,7 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
|
||||
return $result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the first item matching the given query.
|
||||
* All calls to get_one() are cached.
|
||||
|
@ -196,7 +196,7 @@ class DataQuery {
|
||||
$tableClasses = $ancestorTables;
|
||||
}
|
||||
|
||||
$tableNames = array_keys($tableClasses);
|
||||
$tableNames = array_values($tableClasses);
|
||||
$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
|
||||
@ -333,7 +333,7 @@ class DataQuery {
|
||||
|
||||
if(!in_array($qualCol, $query->getSelect())) {
|
||||
unset($newOrderby[$k]);
|
||||
|
||||
|
||||
$newOrderby["\"_SortColumn$i\""] = $dir;
|
||||
$query->selectField($qualCol, "_SortColumn$i");
|
||||
|
||||
@ -836,6 +836,10 @@ class DataQuery {
|
||||
*/
|
||||
class DataQuery_SubGroup extends DataQuery implements SQLConditionGroup {
|
||||
|
||||
/**
|
||||
*
|
||||
* @var SQLSelect
|
||||
*/
|
||||
protected $whereQuery;
|
||||
|
||||
public function __construct(DataQuery $base, $connective) {
|
||||
@ -868,7 +872,9 @@ class DataQuery_SubGroup extends DataQuery implements SQLConditionGroup {
|
||||
|
||||
// Ignore empty conditions
|
||||
$where = $this->whereQuery->getWhere();
|
||||
if(empty($where)) return null;
|
||||
if(empty($where)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Allow database to manage joining of conditions
|
||||
$sql = DB::get_conn()->getQueryBuilder()->buildWhereFragment($this->whereQuery, $parameters);
|
||||
|
@ -265,10 +265,10 @@ class DatabaseAdmin extends Controller {
|
||||
/**
|
||||
* Clear all data out of the database
|
||||
*
|
||||
* @deprecated since version 3.2
|
||||
* @deprecated since version 4.0
|
||||
*/
|
||||
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();
|
||||
}
|
||||
|
||||
|
@ -122,18 +122,6 @@ class Image extends File implements Flushable {
|
||||
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,
|
||||
* or NULL if the image file doesn't exist on the filesystem.
|
||||
@ -141,7 +129,7 @@ class Image extends File implements Flushable {
|
||||
* @return string
|
||||
*/
|
||||
public function getTag() {
|
||||
if(file_exists(Director::baseFolder() . '/' . $this->Filename)) {
|
||||
if($this->exists()) {
|
||||
$url = $this->getURL();
|
||||
$title = ($this->Title) ? $this->Title : $this->Filename;
|
||||
if($this->Title) {
|
||||
@ -195,10 +183,10 @@ class Image extends File implements Flushable {
|
||||
* File names are filtered through {@link FileNameFilter}, see class documentation
|
||||
* on how to influence this behaviour.
|
||||
*
|
||||
* @deprecated 3.2
|
||||
* @deprecated 4.0
|
||||
*/
|
||||
public function loadUploadedImage($tmpFile) {
|
||||
Deprecation::notice('3.2', 'Use the Upload::loadIntoFile()');
|
||||
Deprecation::notice('4.0', 'Use the Upload::loadIntoFile()');
|
||||
|
||||
if(!is_array($tmpFile)) {
|
||||
user_error("Image::loadUploadedImage() Not passed an array. Most likely, the form hasn't got the right"
|
||||
@ -256,7 +244,7 @@ class Image extends File implements Flushable {
|
||||
// Check if image is already sized to the correct dimension
|
||||
$widthRatio = $width / $this->getWidth();
|
||||
$heightRatio = $height / $this->getHeight();
|
||||
|
||||
|
||||
if( $widthRatio < $heightRatio ) {
|
||||
// Target is higher aspect ratio than image, so check width
|
||||
if($this->isWidth($width)) return $this;
|
||||
@ -284,7 +272,7 @@ class Image extends File implements Flushable {
|
||||
/**
|
||||
* Proportionally scale down this image if it is wider or taller than the specified dimensions.
|
||||
* Similar to Fit but without up-sampling. Use in templates with $FitMax.
|
||||
*
|
||||
*
|
||||
* @uses Image::Fit()
|
||||
* @param integer $width The maximum width of the output image
|
||||
* @param integer $height The maximum height of the output image
|
||||
@ -324,7 +312,7 @@ class Image extends File implements Flushable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Crop this image to the aspect ratio defined by the specified width and height,
|
||||
* Crop this image to the aspect ratio defined by the specified width and height,
|
||||
* then scale down the image to those dimensions if it exceeds them.
|
||||
* Similar to Fill but without up-sampling. Use in templates with $FillMax.
|
||||
*
|
||||
@ -339,7 +327,7 @@ class Image extends File implements Flushable {
|
||||
|
||||
// Is the image already the correct size?
|
||||
if ($this->isSize($width, $height)) return $this;
|
||||
|
||||
|
||||
// If not, make sure the image isn't upsampled
|
||||
$imageRatio = $this->getWidth() / $this->getHeight();
|
||||
$cropRatio = $width / $height;
|
||||
@ -347,7 +335,7 @@ class Image extends File implements Flushable {
|
||||
if ($cropRatio < $imageRatio && $this->getHeight() < $height) return $this->Fill($this->getHeight()*$cropRatio, $this->getHeight());
|
||||
// Otherwise we're cropping on the y axis (or not cropping at all) so compare widths
|
||||
if ($this->getWidth() < $width) return $this->Fill($this->getWidth(), $this->getWidth()/$cropRatio);
|
||||
|
||||
|
||||
return $this->Fill($width, $height);
|
||||
}
|
||||
|
||||
@ -400,7 +388,7 @@ class Image extends File implements Flushable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Proportionally scale down this image if it is wider than the specified width.
|
||||
* Proportionally scale down this image if it is wider than the specified width.
|
||||
* Similar to ScaleWidth but without up-sampling. Use in templates with $ScaleMaxWidth.
|
||||
*
|
||||
* @uses Image::ScaleWidth()
|
||||
@ -437,7 +425,7 @@ class Image extends File implements Flushable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Proportionally scale down this image if it is taller than the specified height.
|
||||
* Proportionally scale down this image if it is taller than the specified height.
|
||||
* Similar to ScaleHeight but without up-sampling. Use in templates with $ScaleMaxHeight.
|
||||
*
|
||||
* @uses Image::ScaleHeight()
|
||||
@ -713,7 +701,7 @@ class Image extends File implements Flushable {
|
||||
public function getFormattedImage($format) {
|
||||
$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);
|
||||
|
||||
if(!file_exists(Director::baseFolder()."/".$cacheFile) || self::$flush) {
|
||||
@ -979,8 +967,8 @@ class Image extends File implements Flushable {
|
||||
public function getDimensions($dim = "string") {
|
||||
if($this->getField('Filename')) {
|
||||
|
||||
$imagefile = Director::baseFolder() . '/' . $this->getField('Filename');
|
||||
if(file_exists($imagefile)) {
|
||||
$imagefile = $this->getFullPath();
|
||||
if($this->exists()) {
|
||||
$size = getimagesize($imagefile);
|
||||
return ($dim === "string") ? "$size[0]x$size[1]" : $size[$dim];
|
||||
} else {
|
||||
@ -1061,6 +1049,16 @@ class Image_Cached extends Image {
|
||||
public function getURL() {
|
||||
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() {
|
||||
return $this->getField('Filename');
|
||||
|
@ -363,7 +363,7 @@ class ManyManyList extends RelationList {
|
||||
foreach ($this->extraFields as $fieldName => $dbFieldSpec) {
|
||||
$cleanExtraFields[] = "\"{$fieldName}\"";
|
||||
}
|
||||
$query = new SQLQuery($cleanExtraFields, "\"{$this->joinTable}\"");
|
||||
$query = new SQLSelect($cleanExtraFields, "\"{$this->joinTable}\"");
|
||||
$filter = $this->foreignIDWriteFilter($this->getForeignID());
|
||||
if($filter) {
|
||||
$query->setWhere($filter);
|
||||
|
@ -10,11 +10,20 @@ abstract class DBConnector {
|
||||
|
||||
/**
|
||||
* List of operations to treat as write
|
||||
* Implicitly includes all ddl_operations
|
||||
*
|
||||
* @config
|
||||
* @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.
|
||||
@ -27,6 +36,7 @@ abstract class DBConnector {
|
||||
* @param integer $errorLevel The level of the error to throw.
|
||||
* @param string $sql The SQL related to this query
|
||||
* @param array $parameters Parameters passed to the query
|
||||
* @throws SS_DatabaseException
|
||||
*/
|
||||
protected function databaseError($msg, $errorLevel = E_USER_ERROR, $sql = null, $parameters = array()) {
|
||||
// Prevent errors when error checking is set at zero level
|
||||
@ -47,48 +57,59 @@ abstract class DBConnector {
|
||||
user_error($msg, $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
|
||||
* Determine if this SQL statement is a destructive operation (write or ddl)
|
||||
*
|
||||
* @param string $sql
|
||||
* @return bool
|
||||
*/
|
||||
protected function previewWrite($sql) {
|
||||
// Break if not requested
|
||||
if (!isset($_REQUEST['previewwrite'])) return false;
|
||||
|
||||
// Break if non-write operation
|
||||
$operation = strtolower(substr($sql, 0, strpos($sql, ' ')));
|
||||
$writeOperations = Config::inst()->get(get_class($this), 'write_operations');
|
||||
if (!in_array($operation, $writeOperations)) {
|
||||
public function isQueryMutable($sql) {
|
||||
$operations = array_merge(
|
||||
Config::inst()->get(get_class($this), 'write_operations'),
|
||||
Config::inst()->get(get_class($this), 'ddl_operations')
|
||||
);
|
||||
return $this->isQueryType($sql, $operations);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if this SQL statement is a DDL operation
|
||||
*
|
||||
* @param string $sql
|
||||
* @return bool
|
||||
*/
|
||||
public function isQueryDDL($sql) {
|
||||
$operations = Config::inst()->get(get_class($this), 'ddl_operations');
|
||||
return $this->isQueryType($sql, $operations);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
// output preview message
|
||||
Debug::message("Will execute: $sql");
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(true)) {
|
||||
$starttime = microtime(true);
|
||||
$result = $callback($sql);
|
||||
$endtime = round(microtime(true) - $starttime, 4);
|
||||
Debug::message("\n$sql\n{$endtime}s\n", false);
|
||||
return $result;
|
||||
$operation = $matches['operation'];
|
||||
if(is_array($type)) {
|
||||
return in_array(strtolower($operation), $type);
|
||||
} else {
|
||||
return $callback($sql);
|
||||
return strcasecmp($sql, $type) === 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -96,7 +96,19 @@ abstract class SS_Database {
|
||||
* @return SS_Query
|
||||
*/
|
||||
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
|
||||
*/
|
||||
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();
|
||||
|
||||
/**
|
||||
* @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){
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since version 3.3 Use selectDatabase('dbname', true) instead
|
||||
* @deprecated since version 4.0 Use selectDatabase('dbname', true) instead
|
||||
*/
|
||||
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();
|
||||
$this->selectDatabase($database, true);
|
||||
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) {
|
||||
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) {
|
||||
Deprecation::notice('3.3', 'Use Convert::raw2sql($string, true) instead');
|
||||
Deprecation::notice('4.0', 'Use Convert::raw2sql($string, true) instead');
|
||||
return $this->quoteString($string);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since version 3.3 Use dropSelectedDatabase instead
|
||||
* @deprecated since version 4.0 Use dropSelectedDatabase instead
|
||||
*/
|
||||
public function dropDatabase() {
|
||||
Deprecation::notice('3.3', 'Use dropSelectedDatabase instead');
|
||||
Deprecation::notice('4.0', 'Use dropSelectedDatabase instead');
|
||||
$this->dropSelectedDatabase();
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since version 3.3 Use databaseList instead
|
||||
* @deprecated since version 4.0 Use databaseList instead
|
||||
*/
|
||||
public function allDatabaseNames() {
|
||||
Deprecation::notice('3.3', 'Use databaseList instead');
|
||||
Deprecation::notice('4.0', 'Use databaseList instead');
|
||||
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) {
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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,
|
||||
$alteredFields = null, $alteredIndexes = null, $alteredOptions = 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(
|
||||
$table, $newFields, $newIndexes, $alteredFields,
|
||||
$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) {
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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) {
|
||||
Deprecation::notice('3.3', 'Use DB::create_field() instead');
|
||||
Deprecation::notice('4.0', 'Use DB::create_field() instead');
|
||||
$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) {
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since version 3.3 Use getSelectedDatabase instead
|
||||
* @deprecated since version 4.0 Use getSelectedDatabase instead
|
||||
*/
|
||||
public function currentDatabase() {
|
||||
Deprecation::notice('3.3', 'Use getSelectedDatabase instead');
|
||||
Deprecation::notice('4.0', 'Use getSelectedDatabase instead');
|
||||
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) {
|
||||
Deprecation::notice('3.3', 'Use DB::field_list instead');
|
||||
Deprecation::notice('4.0', 'Use DB::field_list instead');
|
||||
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() {
|
||||
Deprecation::notice('3.3', 'Use DB::table_list instead');
|
||||
Deprecation::notice('4.0', 'Use DB::table_list instead');
|
||||
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) {
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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) {
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since version 3.3 Use Convert::raw2sql instead
|
||||
* @deprecated since version 4.0 Use Convert::raw2sql instead
|
||||
*/
|
||||
public function addslashes($value) {
|
||||
Deprecation::notice('3.3', 'Use Convert::raw2sql instead');
|
||||
Deprecation::notice('4.0', 'Use Convert::raw2sql instead');
|
||||
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() {
|
||||
Deprecation::notice('3.3', 'Use DB::get_schema()->cancelSchemaUpdate instead');
|
||||
Deprecation::notice('4.0', 'Use DB::get_schema()->cancelSchemaUpdate instead');
|
||||
$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() {
|
||||
Deprecation::notice('3.3', 'Use DB::get_schema()->isSchemaUpdating() instead');
|
||||
Deprecation::notice('4.0', 'Use DB::get_schema()->isSchemaUpdating() instead');
|
||||
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() {
|
||||
Deprecation::notice('3.3', 'Use DB::get_schema()->doesSchemaNeedUpdating() instead');
|
||||
Deprecation::notice('4.0', 'Use DB::get_schema()->doesSchemaNeedUpdating() instead');
|
||||
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) {
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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) {
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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) {
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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) {
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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) {
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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) {
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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,
|
||||
$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(
|
||||
$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) {
|
||||
Deprecation::notice('3.3', 'Use DB::dont_require_table() instead');
|
||||
Deprecation::notice('4.0', 'Use DB::dont_require_table() instead');
|
||||
$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) {
|
||||
Deprecation::notice('3.3', 'Use DB::require_index() instead');
|
||||
Deprecation::notice('4.0', 'Use DB::require_index() instead');
|
||||
$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) {
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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) {
|
||||
Deprecation::notice('3.3', 'Use DB::require_field() instead');
|
||||
Deprecation::notice('4.0', 'Use DB::require_field() instead');
|
||||
$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) {
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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()) {
|
||||
Deprecation::notice('3.3', 'Use DB::build_sql() instead');
|
||||
Deprecation::notice('4.0', 'Use DB::build_sql() instead');
|
||||
return $this->getQueryBuilder()->buildSQL($query, $parameters);
|
||||
}
|
||||
}
|
||||
|
@ -12,25 +12,13 @@ class MySQLSchemaManager extends DBSchemaManager {
|
||||
* Identifier for this schema, used for configuring schema-specific table
|
||||
* creation options
|
||||
*/
|
||||
const ID = 'MySQL';
|
||||
const ID = 'MySQLDatabase';
|
||||
|
||||
public function createTable($table, $fields = null, $indexes = null, $options = null, $advancedOptions = null) {
|
||||
$fieldSchemas = $indexSchemas = "";
|
||||
|
||||
if (!empty($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 {
|
||||
$addOptions = "ENGINE=InnoDB";
|
||||
}
|
||||
|
@ -58,12 +58,13 @@ class MySQLStatement extends SS_Query {
|
||||
$variables[] = &$this->boundValues[$field->name];
|
||||
}
|
||||
|
||||
call_user_func_array(array($this->statement, 'bind_result'), $variables);
|
||||
$this->bound = true;
|
||||
$this->metadata->free();
|
||||
|
||||
// Buffer all results
|
||||
$this->statement->store_result();
|
||||
|
||||
call_user_func_array(array($this->statement, 'bind_result'), $variables);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -103,22 +103,22 @@ class MySQLiConnector extends DBConnector {
|
||||
public function getVersion() {
|
||||
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
|
||||
$this->setLastStatement(null);
|
||||
return parent::benchmarkQuery($sql, $callback);
|
||||
}
|
||||
|
||||
public function query($sql, $errorLevel = E_USER_ERROR) {
|
||||
// Check if we should only preview this query
|
||||
if ($this->previewWrite($sql)) return;
|
||||
$this->beforeQuery($sql);
|
||||
|
||||
// Benchmark query
|
||||
$conn = $this->dbConn;
|
||||
$handle = $this->benchmarkQuery($sql, function($sql) use($conn) {
|
||||
return $conn->query($sql, MYSQLI_STORE_RESULT);
|
||||
});
|
||||
$handle = $this->dbConn->query($sql, MYSQLI_STORE_RESULT);
|
||||
|
||||
if (!$handle || $this->dbConn->error) {
|
||||
$this->databaseError($this->getLastError(), $errorLevel, $sql);
|
||||
@ -208,23 +208,20 @@ class MySQLiConnector extends DBConnector {
|
||||
|
||||
public function preparedQuery($sql, $parameters, $errorLevel = E_USER_ERROR) {
|
||||
// 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
|
||||
if ($this->previewWrite($sql)) return;
|
||||
$this->beforeQuery($sql);
|
||||
|
||||
// Type check, identify, and prepare parameters for passing to the statement bind function
|
||||
$parsedParameters = $this->parsePreparedParameters($parameters, $blobs);
|
||||
|
||||
// Benchmark query
|
||||
$self = $this;
|
||||
$lastStatement = $this->benchmarkQuery($sql, function($sql) use($parsedParameters, $blobs, $self) {
|
||||
|
||||
$statement = $self->prepareStatement($sql, $success);
|
||||
if(!$success) return null;
|
||||
|
||||
$statement = $this->prepareStatement($sql, $success);
|
||||
if($success) {
|
||||
if($parsedParameters) {
|
||||
$self->bindParameters($statement, $parsedParameters);
|
||||
$this->bindParameters($statement, $parsedParameters);
|
||||
}
|
||||
|
||||
// Bind any blobs given
|
||||
@ -234,18 +231,18 @@ class MySQLiConnector extends DBConnector {
|
||||
|
||||
// Safely execute the statement
|
||||
$statement->execute();
|
||||
return $statement;
|
||||
});
|
||||
}
|
||||
|
||||
if (!$lastStatement || $lastStatement->error) {
|
||||
if (!$success || $statement->error) {
|
||||
$values = $this->parameterValues($parameters);
|
||||
$this->databaseError($this->getLastError(), $errorLevel, $sql, $values);
|
||||
return null;
|
||||
}
|
||||
|
||||
// Non-select queries will have no result data
|
||||
if($lastStatement && ($metaData = $lastStatement->result_metadata())) {
|
||||
return new MySQLStatement($lastStatement, $metaData);
|
||||
$metaData = $statement->result_metadata();
|
||||
if($metaData) {
|
||||
return new MySQLStatement($statement, $metaData);
|
||||
} else {
|
||||
// Replicate normal behaviour of ->query() on non-select calls
|
||||
return new MySQLQuery($this, true);
|
||||
|
@ -30,11 +30,18 @@ class PDOConnector extends DBConnector {
|
||||
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
|
||||
@ -58,13 +65,22 @@ class PDOConnector extends DBConnector {
|
||||
* @return PDOStatement
|
||||
*/
|
||||
public function getOrPrepareStatement($sql) {
|
||||
if(empty($this->cachedStatements[$sql])) {
|
||||
$this->cachedStatements[$sql] = $this->pdoConnection->prepare(
|
||||
$sql,
|
||||
array(PDO::ATTR_CURSOR => PDO::CURSOR_FWDONLY)
|
||||
);
|
||||
// Return cached statements
|
||||
if(isset($this->cachedStatements[$sql])) {
|
||||
return $this->cachedStatements[$sql];
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -166,54 +182,54 @@ class PDOConnector extends DBConnector {
|
||||
public function quoteString($value) {
|
||||
return $this->pdoConnection->quote($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked before any query is executed
|
||||
*
|
||||
* @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
|
||||
* @param string $sql The SQL query to execute
|
||||
* @param integer $errorLevel For errors to this query, raise PHP errors
|
||||
* using this error level.
|
||||
* @return int
|
||||
*/
|
||||
public function exec($sql, $errorLevel = E_USER_ERROR) {
|
||||
// Check if we should only preview this query
|
||||
if ($this->previewWrite($sql)) return;
|
||||
|
||||
// Reset last statement to prevent interference in case of error
|
||||
$this->lastStatement = null;
|
||||
|
||||
// Benchmark query
|
||||
$pdo = $this->pdoConnection;
|
||||
$result = $this->benchmarkQuery($sql, function($sql) use($pdo) {
|
||||
return $pdo->exec($sql);
|
||||
});
|
||||
$this->beforeQuery($sql);
|
||||
|
||||
// Directly exec this query
|
||||
$result = $this->pdoConnection->exec($sql);
|
||||
|
||||
// Check for errors
|
||||
if ($result === false) {
|
||||
$this->databaseError($this->getLastError(), $errorLevel, $sql);
|
||||
return null;
|
||||
if ($result !== false) {
|
||||
return $this->rowCount = $result;
|
||||
}
|
||||
|
||||
return $result;
|
||||
|
||||
$this->databaseError($this->getLastError(), $errorLevel, $sql);
|
||||
return null;
|
||||
}
|
||||
|
||||
public function query($sql, $errorLevel = E_USER_ERROR) {
|
||||
// Check if we should only preview this query
|
||||
if ($this->previewWrite($sql)) return;
|
||||
|
||||
// Benchmark query
|
||||
$pdo = $this->pdoConnection;
|
||||
$this->lastStatement = $this->benchmarkQuery($sql, function($sql) use($pdo) {
|
||||
return $pdo->query($sql);
|
||||
});
|
||||
|
||||
// Check for errors
|
||||
if (!$this->lastStatement || $this->hasError($this->lastStatement)) {
|
||||
$this->databaseError($this->getLastError(), $errorLevel, $sql);
|
||||
return null;
|
||||
}
|
||||
|
||||
return new PDOQuery($this->lastStatement);
|
||||
$this->beforeQuery($sql);
|
||||
|
||||
// Directly query against connection
|
||||
$statement = $this->pdoConnection->query($sql);
|
||||
|
||||
// Generate results
|
||||
return $this->prepareResults($statement, $errorLevel, $sql);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -272,33 +288,54 @@ class PDOConnector extends DBConnector {
|
||||
}
|
||||
|
||||
public function preparedQuery($sql, $parameters, $errorLevel = E_USER_ERROR) {
|
||||
// Check if we should only preview this query
|
||||
if ($this->previewWrite($sql)) return;
|
||||
|
||||
// Benchmark query
|
||||
$self = $this;
|
||||
$this->lastStatement = $this->benchmarkQuery($sql, function($sql) use($parameters, $self) {
|
||||
|
||||
// Prepare statement
|
||||
$statement = $self->getOrPrepareStatement($sql);
|
||||
if(!$statement) return null;
|
||||
|
||||
// Inject parameters
|
||||
$self->bindParameters($statement, $parameters);
|
||||
|
||||
// Safely execute the statement
|
||||
$this->beforeQuery($sql);
|
||||
|
||||
// Prepare statement
|
||||
$statement = $this->getOrPrepareStatement($sql);
|
||||
|
||||
// Bind and invoke statement safely
|
||||
if($statement) {
|
||||
$this->bindParameters($statement, $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;
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
return new PDOQuery($this->lastStatement);
|
||||
// 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,24 +346,29 @@ class PDOConnector extends DBConnector {
|
||||
*/
|
||||
protected function hasError($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
|
||||
$code = $resource->errorCode();
|
||||
if(empty($code)) return false;
|
||||
if(empty($code)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Skip 'ok' and undefined 'warning' types.
|
||||
// @see http://docstore.mik.ua/orelly/java-ent/jenut/ch08_06.htm
|
||||
return $code !== '00000' && $code !== '01000';
|
||||
}
|
||||
|
||||
|
||||
public function getLastError() {
|
||||
if ($this->hasError($this->lastStatement)) {
|
||||
$error = $this->lastStatement->errorInfo();
|
||||
$error = null;
|
||||
if ($this->lastStatementError) {
|
||||
$error = $this->lastStatementError;
|
||||
} elseif($this->hasError($this->pdoConnection)) {
|
||||
$error = $this->pdoConnection->errorInfo();
|
||||
}
|
||||
if (isset($error)) {
|
||||
if ($error) {
|
||||
return sprintf("%s-%s: %s", $error[0], $error[1], $error[2]);
|
||||
}
|
||||
}
|
||||
@ -336,8 +378,7 @@ class PDOConnector extends DBConnector {
|
||||
}
|
||||
|
||||
public function affectedRows() {
|
||||
if (empty($this->lastStatement)) return 0;
|
||||
return $this->lastStatement->rowCount();
|
||||
return $this->rowCount;
|
||||
}
|
||||
|
||||
public function selectDatabase($name) {
|
||||
|
@ -146,6 +146,7 @@ abstract class SS_Query implements Iterator {
|
||||
public function rewind() {
|
||||
if ($this->queryHasBegun && $this->numRecords() > 0) {
|
||||
$this->queryHasBegun = false;
|
||||
$this->currentRecord = null;
|
||||
return $this->seek(0);
|
||||
}
|
||||
}
|
||||
|
@ -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]
|
||||
*/
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -84,7 +84,9 @@ abstract class StringField extends DBField {
|
||||
* @see core/model/fieldtypes/DBField#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
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -246,7 +246,7 @@ abstract class SQLConditionalExpression extends SQLExpression {
|
||||
public function getJoins(&$parameters = array()) {
|
||||
if(func_num_args() == 0) {
|
||||
Deprecation::notice(
|
||||
'3.2',
|
||||
'4.0',
|
||||
'SQLConditionalExpression::getJoins() now may produce parameters which are necessary to
|
||||
execute this query'
|
||||
);
|
||||
@ -371,7 +371,7 @@ abstract class SQLConditionalExpression extends SQLExpression {
|
||||
/**
|
||||
* 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,... 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,... 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,... Unlimited additional predicates
|
||||
|
@ -24,18 +24,18 @@ abstract class SQLExpression {
|
||||
protected $replacementsNew = array();
|
||||
|
||||
/**
|
||||
* @deprecated since version 3.2
|
||||
* @deprecated since version 4.0
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since version 3.2
|
||||
* @deprecated since version 4.0
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -10,6 +10,14 @@
|
||||
*/
|
||||
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
|
||||
*/
|
||||
@ -21,19 +29,180 @@ class SQLQuery extends SQLSelect {
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since version 3.2
|
||||
* @deprecated since version 4.0
|
||||
*/
|
||||
public function setDelete($value) {
|
||||
$message = 'SQLQuery->setDelete no longer works. Create a SQLDelete object instead, or use toDelete()';
|
||||
Deprecation::notice('3.2', $message);
|
||||
throw new BadMethodCallException($message);
|
||||
Deprecation::notice('4.0', 'SQLQuery::setDelete is deprecated. Use toDelete instead');
|
||||
$this->isDelete = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since version 3.2
|
||||
* @deprecated since version 4.0
|
||||
*/
|
||||
public function getDelete() {
|
||||
Deprecation::notice('3.2', 'Use SQLDelete object instead');
|
||||
return false;
|
||||
Deprecation::notice('4.0', 'SQLQuery::getDelete is deprecated. Use SQLSelect or SQLDelete instead');
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,7 @@
|
||||
* @subpackage model
|
||||
*/
|
||||
class SQLSelect extends SQLConditionalExpression {
|
||||
|
||||
|
||||
/**
|
||||
* An array of SELECT fields, keyed by an optional alias.
|
||||
*
|
||||
|
@ -36,10 +36,10 @@ class BBCodeParser extends TextParser {
|
||||
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() {
|
||||
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) {
|
||||
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) {
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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() {
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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) {
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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() {
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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() {
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -4,27 +4,27 @@
|
||||
* It is a simple regex based parser that allows you to replace simple bbcode-like tags
|
||||
* within a HTMLText or HTMLVarchar field when rendered into a template. The API is inspired by and very similar to the
|
||||
* [Wordpress implementation](http://codex.wordpress.org/Shortcode_API) of shortcodes.
|
||||
*
|
||||
*
|
||||
* @see http://doc.silverstripe.org/framework/en/reference/shortcodes
|
||||
* @package framework
|
||||
* @subpackage misc
|
||||
*/
|
||||
class ShortcodeParser extends Object {
|
||||
|
||||
|
||||
public function img_shortcode($attrs) {
|
||||
return "<img src='".$attrs['src']."'>";
|
||||
}
|
||||
|
||||
|
||||
protected static $instances = array();
|
||||
|
||||
|
||||
protected static $active_instance = 'default';
|
||||
|
||||
|
||||
// --------------------------------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
protected $shortcodes = array();
|
||||
|
||||
|
||||
// --------------------------------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
/**
|
||||
* Get the {@link ShortcodeParser} instance that is attached to a particular identifier.
|
||||
*
|
||||
@ -35,10 +35,10 @@ class ShortcodeParser extends Object {
|
||||
if(!array_key_exists($identifier, self::$instances)) {
|
||||
self::$instances[$identifier] = static::create();
|
||||
}
|
||||
|
||||
|
||||
return self::$instances[$identifier];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the currently active/default {@link ShortcodeParser} instance.
|
||||
*
|
||||
@ -47,7 +47,7 @@ class ShortcodeParser extends Object {
|
||||
public static function get_active() {
|
||||
return static::get(self::$active_instance);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the identifier to use for the current active/default {@link ShortcodeParser} instance.
|
||||
*
|
||||
@ -56,9 +56,9 @@ class ShortcodeParser extends Object {
|
||||
public static function set_active($identifier) {
|
||||
self::$active_instance = (string) $identifier;
|
||||
}
|
||||
|
||||
|
||||
// --------------------------------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
/**
|
||||
* Register a shortcode, and attach it to a PHP callback.
|
||||
*
|
||||
@ -76,7 +76,7 @@ class ShortcodeParser extends Object {
|
||||
public function register($shortcode, $callback) {
|
||||
if(is_callable($callback)) $this->shortcodes[$shortcode] = $callback;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check if a shortcode has been registered.
|
||||
*
|
||||
@ -86,7 +86,7 @@ class ShortcodeParser extends Object {
|
||||
public function registered($shortcode) {
|
||||
return array_key_exists($shortcode, $this->shortcodes);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Remove a specific registered shortcode.
|
||||
*
|
||||
@ -95,28 +95,64 @@ class ShortcodeParser extends Object {
|
||||
public function unregister($shortcode) {
|
||||
if($this->registered($shortcode)) unset($this->shortcodes[$shortcode]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Remove all registered shortcodes.
|
||||
*/
|
||||
public function clear() {
|
||||
$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()) {
|
||||
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 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) {
|
||||
$node->parentNode->removeChild($node);
|
||||
}
|
||||
|
||||
|
||||
protected function insertAfter($new, $after) {
|
||||
$parent = $after->parentNode; $next = $after->nextSibling;
|
||||
|
||||
|
||||
if ($next) {
|
||||
$parent->insertBefore($new, $next);
|
||||
}
|
||||
@ -124,7 +160,7 @@ class ShortcodeParser extends Object {
|
||||
$parent->appendChild($new);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected function insertListAfter($new, $after) {
|
||||
$doc = $after->ownerDocument; $parent = $after->parentNode; $next = $after->nextSibling;
|
||||
|
||||
@ -132,8 +168,8 @@ class ShortcodeParser extends Object {
|
||||
$imported = $doc->importNode($new->item($i), true);
|
||||
|
||||
if ($next) {
|
||||
$parent->insertBefore($imported, $next);
|
||||
}
|
||||
$parent->insertBefore($imported, $next);
|
||||
}
|
||||
else {
|
||||
$parent->appendChild($imported);
|
||||
}
|
||||
@ -147,9 +183,9 @@ class ShortcodeParser extends Object {
|
||||
'figure', 'footer', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'header', 'hgroup', 'ol', 'output', 'p',
|
||||
'pre', 'section', 'table', 'ul'
|
||||
);
|
||||
|
||||
|
||||
protected static $attrrx = '
|
||||
([^\s\/\'"=,]+) # Name
|
||||
([^\s\/\'"=,]+) # Name
|
||||
\s* = \s*
|
||||
(?:
|
||||
(?:\'([^\']+)\') | # Value surrounded by \'
|
||||
@ -157,7 +193,7 @@ class ShortcodeParser extends Object {
|
||||
([^\s,\]]+) # Bare value
|
||||
)
|
||||
';
|
||||
|
||||
|
||||
protected static function attrrx() {
|
||||
return '/'.self::$attrrx.'/xS';
|
||||
}
|
||||
@ -165,21 +201,21 @@ class ShortcodeParser extends Object {
|
||||
protected static $tagrx = '
|
||||
# HTML Tag
|
||||
<(?<element>(?:"[^"]*"[\'"]*|\'[^\']*\'[\'"]*|[^\'">])+)>
|
||||
|
||||
|
||||
| # Opening tag
|
||||
(?<oesc>\[?)
|
||||
\[
|
||||
(?<open>\w+)
|
||||
(?<oesc>\[?)
|
||||
\[
|
||||
(?<open>\w+)
|
||||
[\s,]*
|
||||
(?<attrs> (?: %s [\s,]*)* )
|
||||
\/?\]
|
||||
(?<attrs> (?: %s [\s,]*)* )
|
||||
\/?\]
|
||||
(?<cesc1>\]?)
|
||||
|
||||
|
||||
| # Closing tag
|
||||
\[\/
|
||||
(?<close>\w+)
|
||||
\]
|
||||
(?<cesc2>\]?)
|
||||
\[\/
|
||||
(?<close>\w+)
|
||||
\]
|
||||
(?<cesc2>\]?)
|
||||
';
|
||||
|
||||
protected static function tagrx() {
|
||||
@ -199,14 +235,15 @@ class ShortcodeParser extends Object {
|
||||
* of those tags
|
||||
*
|
||||
* Doesn't support nested shortcode tags
|
||||
*
|
||||
*
|
||||
* @param string $content
|
||||
* @return array - The list of tags found. When using an open/close pair, only one item will be in the array,
|
||||
* with "content" set to the text between the tags
|
||||
*/
|
||||
protected function extractTags($content) {
|
||||
$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)) {
|
||||
foreach($matches as $match) {
|
||||
// Ignore any elements
|
||||
@ -214,7 +251,7 @@ class ShortcodeParser extends Object {
|
||||
|
||||
// Pull the attributes out into a key/value hash
|
||||
$attrs = array();
|
||||
|
||||
|
||||
if (!empty($match['attrs'][0])) {
|
||||
preg_match_all(static::attrrx(), $match['attrs'][0], $attrmatches, PREG_SET_ORDER);
|
||||
|
||||
@ -223,7 +260,7 @@ class ShortcodeParser extends Object {
|
||||
$attrs[$name] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// And store the indexes, tag details, etc
|
||||
$tags[] = array(
|
||||
'text' => $match[0][0],
|
||||
@ -236,14 +273,15 @@ class ShortcodeParser extends Object {
|
||||
'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);
|
||||
while($i--) {
|
||||
if(!empty($tags[$i]['close'])) {
|
||||
// If the tag just before this one isn't the related opening tag, throw an error
|
||||
$err = null;
|
||||
|
||||
|
||||
if ($i == 0) {
|
||||
$err = 'Close tag "'.$tags[$i]['close'].'" is the first found tag, so has no related open tag';
|
||||
}
|
||||
@ -255,10 +293,10 @@ class ShortcodeParser extends Object {
|
||||
$err = 'Close tag "'.$tags[$i]['close'].'" doesn\'t match preceding open tag "'.
|
||||
$tags[$i-1]['open'].'"';
|
||||
}
|
||||
|
||||
|
||||
if($err) {
|
||||
if(self::$error_behavior == self::ERROR) user_error($err, E_USER_ERRROR);
|
||||
}
|
||||
if(self::$error_behavior == self::ERROR) user_error($err, E_USER_ERRROR);
|
||||
}
|
||||
else {
|
||||
if ($tags[$i]['escaped']) {
|
||||
if (!$tags[$i-1]['escaped']) {
|
||||
@ -272,12 +310,23 @@ class ShortcodeParser extends Object {
|
||||
$tags[$i-1]['escaped'] = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Otherwise, grab content between tags, save in opening tag & delete the closing one
|
||||
$tags[$i-1]['text'] = substr($content, $tags[$i-1]['s'], $tags[$i]['e'] - $tags[$i-1]['s']);
|
||||
$tags[$i-1]['content'] = substr($content, $tags[$i-1]['e'], $tags[$i]['s'] - $tags[$i-1]['e']);
|
||||
$tags[$i-1]['e'] = $tags[$i]['e'];
|
||||
|
||||
unset($tags[$i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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]);
|
||||
}
|
||||
}
|
||||
@ -311,7 +360,7 @@ class ShortcodeParser extends Object {
|
||||
else {
|
||||
$str = $generator($i, $tags[$i]) . $tail . $str;
|
||||
}
|
||||
|
||||
|
||||
$li = $tags[$i]['s'];
|
||||
}
|
||||
|
||||
@ -337,27 +386,14 @@ class ShortcodeParser extends Object {
|
||||
|
||||
if($tags) {
|
||||
$node->nodeValue = $this->replaceTagsWithText($node->nodeValue, $tags,
|
||||
function($idx, $tag) use ($parser, $extra){
|
||||
$content = $parser->callShortcode($tag['open'], $tag['attrs'], $tag['content'], $extra);
|
||||
|
||||
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'];
|
||||
}
|
||||
function($idx, $tag) use ($parser, $extra) {
|
||||
return $parser->getShortcodeReplacementText($tag, $extra, false);
|
||||
}
|
||||
|
||||
return $content;
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Replace the element-scoped tags with markers
|
||||
*
|
||||
@ -368,18 +404,18 @@ class ShortcodeParser extends Object {
|
||||
|
||||
if($tags) {
|
||||
$markerClass = self::$marker_class;
|
||||
|
||||
|
||||
$content = $this->replaceTagsWithText($content, $tags, function($idx, $tag) use ($markerClass) {
|
||||
return '<img class="'.$markerClass.'" data-tagid="'.$idx.'" />';
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return array($content, $tags);
|
||||
}
|
||||
}
|
||||
|
||||
protected function findParentsForMarkers($nodes) {
|
||||
$parents = array();
|
||||
|
||||
|
||||
foreach($nodes as $node) {
|
||||
$parent = $node;
|
||||
|
||||
@ -392,34 +428,34 @@ class ShortcodeParser extends Object {
|
||||
$node->setAttribute('data-parentid', count($parents));
|
||||
$parents[] = $parent;
|
||||
}
|
||||
|
||||
|
||||
return $parents;
|
||||
}
|
||||
|
||||
|
||||
const BEFORE = 'before';
|
||||
const AFTER = 'after';
|
||||
const SPLIT = 'split';
|
||||
const INLINE = 'inline';
|
||||
|
||||
|
||||
/**
|
||||
* Given a node with represents a shortcode marker and a location string, mutates the DOM to put the
|
||||
* marker in the compliant location
|
||||
*
|
||||
*
|
||||
* For shortcodes inserted BEFORE, that location is just before the block container that
|
||||
* the marker is in
|
||||
*
|
||||
*
|
||||
* For shortcodes inserted AFTER, that location is just after the block container that
|
||||
* the marker is in
|
||||
*
|
||||
*
|
||||
* For shortcodes inserted SPLIT, that location is where the marker is, but the DOM
|
||||
* is split around it up to the block container the marker is in - for instance,
|
||||
*
|
||||
*
|
||||
* <p>A<span>B<marker />C</span>D</p>
|
||||
*
|
||||
* becomes
|
||||
*
|
||||
*
|
||||
* becomes
|
||||
*
|
||||
* <p>A<span>B</span></p><marker /><p><span>C</span>D</p>
|
||||
*
|
||||
*
|
||||
* For shortcodes inserted INLINE, no modification is needed (but in that case the shortcode handler needs to
|
||||
* generate only inline blocks)
|
||||
*
|
||||
@ -458,7 +494,7 @@ class ShortcodeParser extends Object {
|
||||
if(in_array(strtolower($node->tagName), self::$block_level_elements)) {
|
||||
user_error(
|
||||
'Requested to insert block tag '.$node->tagName.
|
||||
' inline - probably this will break HTML compliance',
|
||||
' inline - probably this will break HTML compliance',
|
||||
E_USER_WARNING
|
||||
);
|
||||
}
|
||||
@ -472,35 +508,19 @@ class ShortcodeParser extends Object {
|
||||
/**
|
||||
* Given a node with represents a shortcode marker and some information about the shortcode, call the
|
||||
* shortcode handler & replace the marker with the actual content
|
||||
*
|
||||
*
|
||||
* @param DOMElement $node
|
||||
* @param array $tag
|
||||
*/
|
||||
protected function replaceMarkerWithContent($node, $tag) {
|
||||
$content = false;
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
$content = $this->getShortcodeReplacementText($tag);
|
||||
|
||||
if ($content) {
|
||||
$parsed = Injector::inst()->create('HTMLValue', $content);
|
||||
$body = $parsed->getBody();
|
||||
if ($body) $this->insertListAfter($body->childNodes, $node);
|
||||
}
|
||||
|
||||
|
||||
$this->removeNode($node);
|
||||
}
|
||||
|
||||
@ -545,11 +565,11 @@ class ShortcodeParser extends Object {
|
||||
foreach($shortcodes as $shortcode) {
|
||||
$tag = $tags[$shortcode->getAttribute('data-tagid')];
|
||||
$parent = $parents[$shortcode->getAttribute('data-parentid')];
|
||||
|
||||
|
||||
$class = null;
|
||||
if(!empty($tag['attrs']['location'])) $class = $tag['attrs']['location'];
|
||||
if(!empty($tag['attrs']['location'])) $class = $tag['attrs']['location'];
|
||||
else if(!empty($tag['attrs']['class'])) $class = $tag['attrs']['class'];
|
||||
|
||||
|
||||
$location = self::INLINE;
|
||||
if($class == 'left' || $class == 'right') $location = self::BEFORE;
|
||||
if($class == 'center' || $class == 'leftALone') $location = self::SPLIT;
|
||||
@ -567,8 +587,21 @@ class ShortcodeParser extends Object {
|
||||
$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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -18,7 +18,7 @@
|
||||
// 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
|
||||
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%;
|
||||
margin-left:0;
|
||||
clear:both;
|
||||
|
40
search/filters/FulltextFilter.php
Normal file → Executable file
40
search/filters/FulltextFilter.php
Normal file → Executable file
@ -17,22 +17,23 @@
|
||||
* database table, using the {$indexes} hash in your DataObject subclass:
|
||||
*
|
||||
* <code>
|
||||
* static $indexes = array(
|
||||
* private static $indexes = array(
|
||||
* 'SearchFields' => 'fulltext(Name, Title, Description)'
|
||||
* );
|
||||
* </code>
|
||||
*
|
||||
* @package framework
|
||||
* @subpackage search
|
||||
* @todo Add support for databases besides MySQL
|
||||
*/
|
||||
class FulltextFilter extends SearchFilter {
|
||||
|
||||
protected function applyOne(DataQuery $query) {
|
||||
$this->model = $query->applyRelation($this->relation);
|
||||
$predicate = sprintf("MATCH (%s) AGAINST (?)", $this->getDbName());
|
||||
return $query->where(array($predicate => $this->getValue()));
|
||||
}
|
||||
|
||||
protected function excludeOne(DataQuery $query) {
|
||||
$this->model = $query->applyRelation($this->relation);
|
||||
$predicate = sprintf("NOT MATCH (%s) AGAINST (?)", $this->getDbName());
|
||||
return $query->where(array($predicate => $this->getValue()));
|
||||
}
|
||||
@ -40,4 +41,37 @@ class FulltextFilter extends SearchFilter {
|
||||
public function isEmpty() {
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -164,6 +164,12 @@ abstract class SearchFilter extends Object {
|
||||
if($this->name == "NULL") {
|
||||
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(
|
||||
$this->model,
|
||||
@ -177,7 +183,7 @@ abstract class SearchFilter extends Object {
|
||||
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();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Apply filter criteria to a SQL query.
|
||||
*
|
||||
|
@ -5,7 +5,7 @@
|
||||
* @subpackage security
|
||||
*/
|
||||
class ChangePasswordForm extends Form {
|
||||
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
@ -28,7 +28,7 @@ class ChangePasswordForm extends Form {
|
||||
|
||||
if(!$fields) {
|
||||
$fields = new FieldList();
|
||||
|
||||
|
||||
// Security/changepassword?h=XXX redirects to Security/changepassword
|
||||
// without GET parameter to avoid potential HTTP referer leakage.
|
||||
// In this case, a user is not logged in, and no 'old password' should be necessary.
|
||||
@ -65,7 +65,7 @@ class ChangePasswordForm extends Form {
|
||||
if(empty($data['OldPassword']) || !$member->checkPassword($data['OldPassword'])->valid()) {
|
||||
$this->clearMessage();
|
||||
$this->sessionMessage(
|
||||
_t('Member.ERRORPASSWORDNOTMATCH', "Your current password does not match, please try again"),
|
||||
_t('Member.ERRORPASSWORDNOTMATCH', "Your current password does not match, please try again"),
|
||||
"bad"
|
||||
);
|
||||
// redirect back to the form, instead of using redirectBack() which could send the user elsewhere.
|
||||
@ -99,7 +99,7 @@ class ChangePasswordForm extends Form {
|
||||
$isValid = $member->changePassword($data['NewPassword1']);
|
||||
if($isValid->valid()) {
|
||||
$member->logIn();
|
||||
|
||||
|
||||
// TODO Add confirmation message to login redirect
|
||||
Session::clear('AutoLoginHash');
|
||||
|
||||
@ -127,11 +127,12 @@ class ChangePasswordForm extends Form {
|
||||
$this->clearMessage();
|
||||
$this->sessionMessage(
|
||||
_t(
|
||||
'Member.INVALIDNEWPASSWORD',
|
||||
'Member.INVALIDNEWPASSWORD',
|
||||
"We couldn't accept that password: {password}",
|
||||
array('password' => nl2br("\n".$isValid->starredList()))
|
||||
),
|
||||
"bad"
|
||||
array('password' => nl2br("\n".Convert::raw2xml($isValid->starredList())))
|
||||
),
|
||||
"bad",
|
||||
false
|
||||
);
|
||||
|
||||
// redirect back to the form, instead of using redirectBack() which could send the user elsewhere.
|
||||
|
@ -59,7 +59,9 @@ class Member extends DataObject implements TemplateGlobalProvider {
|
||||
|
||||
private static $has_one = array();
|
||||
|
||||
private static $has_many = array();
|
||||
private static $has_many = array(
|
||||
'LoggedPasswords' => 'MemberPassword',
|
||||
);
|
||||
|
||||
private static $many_many = array();
|
||||
|
||||
@ -202,10 +204,10 @@ class Member extends DataObject implements TemplateGlobalProvider {
|
||||
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) {
|
||||
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;
|
||||
}
|
||||
|
||||
@ -275,11 +277,11 @@ class Member extends DataObject implements TemplateGlobalProvider {
|
||||
* RewriteCond %{HTTP_COOKIE} !SS_LOGGED_IN=1
|
||||
* </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.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
@ -369,11 +371,11 @@ class Member extends DataObject implements TemplateGlobalProvider {
|
||||
* Get the field used for uniquely identifying a member
|
||||
* 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
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
@ -381,11 +383,11 @@ class Member extends DataObject implements TemplateGlobalProvider {
|
||||
* Set the field used for uniquely identifying a member
|
||||
* 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
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
@ -407,20 +409,20 @@ class Member extends DataObject implements TemplateGlobalProvider {
|
||||
* Set the number of days that a password should be valid for.
|
||||
* 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) {
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
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;
|
||||
}
|
||||
|
||||
@ -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.
|
||||
*
|
||||
@ -1304,6 +1326,9 @@ class Member extends DataObject implements TemplateGlobalProvider {
|
||||
// Members are displayed within group edit form in SecurityAdmin
|
||||
$fields->removeByName('Groups');
|
||||
|
||||
// Members shouldn't be able to directly view/edit logged passwords
|
||||
$fields->removeByName('LoggedPasswords');
|
||||
|
||||
if(Permission::check('EDIT_PERMISSIONS')) {
|
||||
$groupsMap = array();
|
||||
foreach(Group::get() as $group) {
|
||||
|
@ -558,26 +558,26 @@ class Permission extends DataObject implements TemplateGlobalProvider {
|
||||
/**
|
||||
* 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
|
||||
* @return void
|
||||
*/
|
||||
public static function add_to_hidden_permissions($code){
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @return void
|
||||
*/
|
||||
public static function remove_from_hidden_permissions($code){
|
||||
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);
|
||||
}
|
||||
|
||||
@ -587,12 +587,12 @@ class Permission extends DataObject implements TemplateGlobalProvider {
|
||||
* Permissions can be grouped by nesting arrays. Scalar values are always
|
||||
* 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
|
||||
* declare for the system.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -71,6 +71,10 @@ class PermissionCheckboxSetField extends FormField {
|
||||
return $this->hiddenPermissions;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $properties
|
||||
* @return HTMLText
|
||||
*/
|
||||
public function Field($properties = array()) {
|
||||
Requirements::css(FRAMEWORK_DIR . '/css/CheckboxSetField.css');
|
||||
Requirements::javascript(FRAMEWORK_DIR . '/javascript/PermissionCheckboxSetField.js');
|
||||
@ -227,7 +231,8 @@ class PermissionCheckboxSetField extends FormField {
|
||||
}
|
||||
}
|
||||
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\">" .
|
||||
_t(
|
||||
'Permissions.UserPermissionsIntro',
|
||||
@ -236,11 +241,14 @@ class PermissionCheckboxSetField extends FormField {
|
||||
) .
|
||||
"</li>" .
|
||||
$options .
|
||||
"</ul>\n";
|
||||
"</ul>\n"
|
||||
);
|
||||
} 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 .
|
||||
"</ul>\n";
|
||||
"</ul>\n"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -132,10 +132,10 @@ class Security extends Controller implements TemplateGlobalProvider {
|
||||
/**
|
||||
* 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() {
|
||||
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;
|
||||
}
|
||||
|
||||
@ -165,22 +165,22 @@ class Security extends Controller implements TemplateGlobalProvider {
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
@ -888,12 +888,12 @@ class Security extends Controller implements TemplateGlobalProvider {
|
||||
* This prevents sharing of the session across several sites in the
|
||||
* 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
|
||||
* checking.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
@ -901,11 +901,11 @@ class Security extends Controller implements TemplateGlobalProvider {
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
@ -913,23 +913,23 @@ class Security extends Controller implements TemplateGlobalProvider {
|
||||
/**
|
||||
* 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
|
||||
* algorithms determined by {@link Security::get_encryption_algorithms()}
|
||||
* @return bool Returns TRUE if the passed algorithm was valid, otherwise FALSE.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
@ -1022,20 +1022,20 @@ class Security extends Controller implements TemplateGlobalProvider {
|
||||
* Enable or disable recording of login attempts
|
||||
* 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
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated 3.2 Use the "Security.login_recording" config setting instead
|
||||
* @deprecated 4.0 Use the "Security.login_recording" config setting instead
|
||||
* @return boolean
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
@ -1049,20 +1049,20 @@ class Security extends Controller implements TemplateGlobalProvider {
|
||||
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) {
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
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;
|
||||
}
|
||||
|
||||
@ -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.
|
||||
*
|
||||
* @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) {
|
||||
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);
|
||||
}
|
||||
|
||||
@ -1118,7 +1118,7 @@ class Security extends Controller implements TemplateGlobalProvider {
|
||||
|
||||
|
||||
/**
|
||||
* Defines global accesible templates variables.
|
||||
* Defines global accessible templates variables.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
|
@ -4,9 +4,9 @@
|
||||
* @subpackage tasks
|
||||
*/
|
||||
class i18nTextCollectorTask extends BuildTask {
|
||||
|
||||
|
||||
protected $title = "i18n Textcollector Task";
|
||||
|
||||
|
||||
protected $description = "
|
||||
Traverses through files in order to collect the 'entity master tables'
|
||||
stored in each module.
|
||||
@ -17,27 +17,66 @@ class i18nTextCollectorTask extends BuildTask {
|
||||
- module: One or more modules to limit collection (comma-separated)
|
||||
- merge: Merge new strings with existing ones already defined in language files (default: FALSE)
|
||||
";
|
||||
|
||||
|
||||
public function init() {
|
||||
parent::init();
|
||||
|
||||
|
||||
$canAccess = (Director::isDev() || Director::is_cli() || Permission::check("ADMIN"));
|
||||
if(!$canAccess) return Security::permissionFailure($this);
|
||||
if(!$canAccess) {
|
||||
return Security::permissionFailure($this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This is the main method to build the master string tables with the original strings.
|
||||
* It will search for existent modules that use the i18n feature, parse the _t() calls
|
||||
* and write the resultant files in the lang folder of each module.
|
||||
*
|
||||
*
|
||||
* @uses DataObject->collectI18nStatics()
|
||||
*/
|
||||
*
|
||||
* @param SS_HTTPRequest $request
|
||||
*/
|
||||
public function run($request) {
|
||||
increase_time_limit_to();
|
||||
$c = new i18nTextCollector($request->getVar('locale'));
|
||||
$writer = $request->getVar('writer');
|
||||
if($writer) $c->setWriter(new $writer());
|
||||
$restrictModules = ($request->getVar('module')) ? explode(',', $request->getVar('module')) : null;
|
||||
return $c->run($restrictModules, (bool)$request->getVar('merge'));
|
||||
$collector = i18nTextCollector::create($request->getVar('locale'));
|
||||
|
||||
$merge = $this->getIsMerge($request);
|
||||
|
||||
// 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'));
|
||||
}
|
||||
}
|
||||
|
@ -3,34 +3,34 @@
|
||||
<h3>
|
||||
<span class="step-label">
|
||||
<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>
|
||||
</h3>
|
||||
|
||||
|
||||
|
||||
<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">
|
||||
<% _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') %>" />
|
||||
<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...' %>
|
||||
<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>
|
||||
|
||||
<div class="clear"><!-- --></div>
|
||||
</div>
|
||||
<div class="ss-uploadfield-item-uploador">
|
||||
<% _t('AssetUploadField.UPLOADOR', 'OR') %>
|
||||
<%t AssetUploadField.UPLOADOR 'OR' %>
|
||||
</div>
|
||||
<div class="ss-uploadfield-item-preview ss-uploadfield-dropzone">
|
||||
<div>
|
||||
<% _t('AssetUploadField.DROPAREA', 'Drop Area') %>
|
||||
<span><% _t('AssetUploadField.DRAGFILESHERE', 'Drag files here') %></span>
|
||||
<%t AssetUploadField.DROPAREA 'Drop Area' %>
|
||||
<span><%t AssetUploadField.DRAGFILESHERE 'Drag files here' %></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<span class="ss-uploadfield-view-allowed-extensions">
|
||||
<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>
|
||||
</span>
|
||||
</span>
|
||||
@ -42,13 +42,13 @@
|
||||
<h3>
|
||||
<span class="step-label">
|
||||
<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>
|
||||
</h3>
|
||||
|
||||
<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;">
|
||||
<% _t('AssetUploadField.EDITALL', 'Edit all') %>
|
||||
<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' %>
|
||||
<span class="toggle-details-icon"></span>
|
||||
</button>
|
||||
</div>
|
||||
@ -56,9 +56,9 @@
|
||||
<ul class="ss-uploadfield-files files"></ul>
|
||||
<div class="fileOverview">
|
||||
<div class="uploadStatus message notice">
|
||||
<div class="state"><% _t('AssetUploadField.UPLOADINPROGRESS', 'Please wait… upload in progress') %></div>
|
||||
<div class="details"><% _t('AssetUploadField.TOTAL', 'Total') %>:
|
||||
<span class="total"></span> <% _t('AssetUploadField.FILES', 'Files') %>
|
||||
<div class="state"><%t AssetUploadField.UPLOADINPROGRESS 'Please wait… upload in progress' %></div>
|
||||
<div class="details"><%t AssetUploadField.TOTAL 'Total' %>:
|
||||
<span class="total"></span> <%t AssetUploadField.FILES 'Files' %>
|
||||
<span class="fileSize"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -3,24 +3,24 @@
|
||||
<h4>
|
||||
<span class="step-label">
|
||||
<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>
|
||||
</h4>
|
||||
|
||||
<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">
|
||||
<% _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') %>" />
|
||||
<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...' %>
|
||||
<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>
|
||||
|
||||
<div class="clear"><!-- --></div>
|
||||
</div>
|
||||
<div class="ss-uploadfield-item-uploador">
|
||||
<% _t('AssetUploadField.UPLOADOR', 'OR') %>
|
||||
<%t AssetUploadField.UPLOADOR 'OR' %>
|
||||
</div>
|
||||
<div class="ss-uploadfield-item-preview ss-uploadfield-dropzone">
|
||||
<div>
|
||||
<span><% _t('AssetUploadField.DRAGFILESHERE', 'Drag files here') %></span>
|
||||
<span><%t AssetUploadField.DRAGFILESHERE 'Drag files here' %></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="clear"><!-- --></div>
|
||||
|
@ -2,7 +2,7 @@
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" >
|
||||
<head>
|
||||
<% 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">
|
||||
</head>
|
||||
|
||||
|
@ -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>
|
||||
|
@ -4,7 +4,7 @@
|
||||
<span class="pagination-records-number">
|
||||
$FirstShownRecord -
|
||||
$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
|
||||
</span>
|
||||
<% end_if %>
|
||||
|
@ -1,5 +1,5 @@
|
||||
<% if Backlink %>
|
||||
<a href="$Backlink"><% _t('GridFieldItemEditView.Go_back', 'Go back' ) %></a>
|
||||
<a href="$Backlink"><%t GridFieldItemEditView.Go_back 'Go back' %></a>
|
||||
<% end_if %>
|
||||
|
||||
$ItemEditForm
|
||||
|
@ -1,6 +1,6 @@
|
||||
<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}–{$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
|
||||
</span>
|
@ -5,18 +5,18 @@
|
||||
<div class="datagrid-pagination">
|
||||
$FirstPage $PreviousPage
|
||||
<span class="pagination-page-number">
|
||||
<% _t('Pagination.Page', 'Page') %>
|
||||
<%t Pagination.Page 'Page' %>
|
||||
<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
|
||||
</span>
|
||||
$NextPage $LastPage
|
||||
</div>
|
||||
<% end_if %>
|
||||
<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}–{$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
|
||||
</span>
|
||||
</td>
|
||||
|
@ -16,15 +16,15 @@
|
||||
<% end_loop %>
|
||||
<% else %>
|
||||
<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>
|
||||
<% end_if %>
|
||||
</tbody>
|
||||
</table>
|
||||
<p>
|
||||
<% _t('GridField.PRINTEDAT', 'Printed at') %> $Datetime.Time, $Datetime.Date
|
||||
<%t GridField.PRINTEDAT 'Printed at' %> $Datetime.Time, $Datetime.Date
|
||||
<br />
|
||||
<% _t('GridField.PRINTEDBY', 'Printed by') %> $Member.Name
|
||||
<%t GridField.PRINTEDBY 'Printed by' %> $Member.Name
|
||||
</p>
|
||||
</body>
|
||||
|
||||
|
@ -15,7 +15,7 @@
|
||||
$Name
|
||||
</span>
|
||||
<% 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)
|
||||
</div>
|
||||
<% end_if %>
|
||||
@ -23,13 +23,13 @@
|
||||
<div class="clear"><!-- --></div>
|
||||
</label>
|
||||
<div class="ss-uploadfield-item-actions">
|
||||
<button data-icon="deleteLight" class="ss-uploadfield-item-cancel ss-uploadfield-item-remove" title="<% _t('UploadField.REMOVE', 'Remove') %>">
|
||||
<% _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' %>
|
||||
</button>
|
||||
|
||||
<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">
|
||||
<% _t('UploadField.EDIT', 'Edit') %>
|
||||
<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' %>
|
||||
<span class="toggle-details">
|
||||
<span class="toggle-details-icon"></span>
|
||||
</span>
|
||||
|
@ -1,12 +1,12 @@
|
||||
<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-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-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-edit" class="icon-edit icon-view last" value="content"><%t SilverStripeNavigator.EditView 'Edit mode' %></option>
|
||||
<!-- 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>
|
||||
</span>
|
||||
|
@ -1,17 +1,17 @@
|
||||
<% if $canEdit %>
|
||||
<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') %>
|
||||
<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' %>
|
||||
<span class="toggle-details">
|
||||
<span class="toggle-details-icon"></span>
|
||||
</span>
|
||||
</button>
|
||||
<% 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">
|
||||
<% _t('UploadField.REMOVE', 'Remove') %></button>
|
||||
<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>
|
||||
<% 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 %>
|
||||
<% 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">
|
||||
<% _t('UploadField.CHOOSEANOTHERFILE', 'Choose another file') %></button>
|
||||
<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>
|
||||
<% end_if %>
|
||||
|
@ -30,26 +30,26 @@
|
||||
<% if canUpload %>
|
||||
<div class="ss-uploadfield-item-preview ss-uploadfield-dropzone ui-corner-all">
|
||||
<% if $multiple %>
|
||||
<% _t('UploadField.DROPFILES', 'drop files') %>
|
||||
<%t UploadField.DROPFILES 'drop files' %>
|
||||
<% else %>
|
||||
<% _t('UploadField.DROPFILE', 'drop a file') %>
|
||||
<%t UploadField.DROPFILE 'drop a file' %>
|
||||
<% end_if %>
|
||||
</div>
|
||||
<% end_if %>
|
||||
<div class="ss-uploadfield-item-info">
|
||||
<label class="ss-uploadfield-item-name">
|
||||
<% if $multiple %>
|
||||
<b><% _t('UploadField.ATTACHFILES', 'Attach files') %></b>
|
||||
<b><%t UploadField.ATTACHFILES 'Attach files' %></b>
|
||||
<% else %>
|
||||
<b><% _t('UploadField.ATTACHFILE', 'Attach a file') %></b>
|
||||
<b><%t UploadField.ATTACHFILE 'Attach a file' %></b>
|
||||
<% end_if %>
|
||||
<% if $canPreviewFolder %>
|
||||
<small>(<%t UploadField.UPLOADSINTO 'saves into /{path}' path=$FolderName %>)</small>
|
||||
<% end_if %>
|
||||
</label>
|
||||
<% 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">
|
||||
<% _t('UploadField.FROMCOMPUTER', 'From your computer') %>
|
||||
<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' %>
|
||||
<input id="$id" name="{$Name}[Uploads][]" class="$extraClass ss-uploadfield-fromcomputer-fileinput" data-config="$configString" type="file"<% if $multiple %> multiple="multiple"<% end_if %> />
|
||||
</label>
|
||||
<% else %>
|
||||
@ -57,11 +57,11 @@
|
||||
<% end_if %>
|
||||
|
||||
<% 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 %>
|
||||
<% if $canUpload %>
|
||||
<% 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 %>
|
||||
<div class="clear"><!-- --></div>
|
||||
|
@ -1,11 +1,11 @@
|
||||
<p><% _t('ChangePasswordEmail_ss.HELLO', 'Hi') %> $FirstName,</p>
|
||||
<p><%t ChangePasswordEmail_ss.HELLO 'Hi' %> $FirstName,</p>
|
||||
|
||||
<p>
|
||||
<% _t('ChangePasswordEmail_ss.CHANGEPASSWORDTEXT1', 'You changed your password for', 'for a url') %> $AbsoluteBaseURL.<br />
|
||||
<% _t('ChangePasswordEmail_ss.CHANGEPASSWORDTEXT2', 'You can now use the following credentials to log in:') %>
|
||||
<%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:' %>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<% _t('ChangePasswordEmail_ss.EMAIL', 'Email') %>: $Email<br />
|
||||
<% _t('ChangePasswordEmail_ss.PASSWORD', 'Password') %>: $CleartextPassword
|
||||
<%t ChangePasswordEmail_ss.EMAIL 'Email' %>: $Email<br />
|
||||
<%t ChangePasswordEmail_ss.PASSWORD 'Password' %>: $CleartextPassword
|
||||
</p>
|
@ -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>
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
<ul id="$HolderID" class="$extraClass">
|
||||
<ul $AttributesHTML>
|
||||
<% if $Options.Count %>
|
||||
<% loop $Options %>
|
||||
<li class="$Class">
|
||||
|
@ -1,4 +1,4 @@
|
||||
<ul id="$HolderID" class="$extraClass">
|
||||
<ul $AttributesHTML>
|
||||
<% loop $Options %>
|
||||
<li class="$Class">
|
||||
<input id="$ID" class="radio" name="$Name" type="radio" value="$Value"<% if $isChecked %> checked<% end_if %><% if $isDisabled %> disabled<% end_if %> />
|
||||
|
@ -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$/
|
||||
*/
|
||||
|
@ -11,6 +11,18 @@ class ControllerTest extends FunctionalTest {
|
||||
'ControllerTest_AccessBaseControllerExtension'
|
||||
)
|
||||
);
|
||||
|
||||
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() {
|
||||
/* For a controller with a template, the default action will simple run that template. */
|
||||
@ -208,6 +220,7 @@ class ControllerTest extends FunctionalTest {
|
||||
* @expectedExceptionMessage Wildcards (*) are no longer valid
|
||||
*/
|
||||
public function testWildcardAllowedActions() {
|
||||
Deprecation::set_enabled(true);
|
||||
$this->get('ControllerTest_AccessWildcardSecuredController');
|
||||
}
|
||||
|
||||
|
@ -18,19 +18,17 @@ class DirectorTest extends SapphireTest {
|
||||
public function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Required for testRequestFilterInDirectorTest
|
||||
Injector::nest();
|
||||
|
||||
// Hold the original request URI once so it doesn't get overwritten
|
||||
if(!self::$originalRequestURI) {
|
||||
self::$originalRequestURI = $_SERVER['REQUEST_URI'];
|
||||
}
|
||||
$_SERVER['REQUEST_URI'] = 'http://www.mysite.com';
|
||||
|
||||
|
||||
$this->originalGet = $_GET;
|
||||
$this->originalSession = $_SESSION;
|
||||
$_SESSION = array();
|
||||
|
||||
|
||||
Config::inst()->update('Director', 'rules', array(
|
||||
'DirectorTestRule/$Action/$ID/$OtherID' => 'DirectorTestRequest_Controller',
|
||||
'en-nz/$Action/$ID/$OtherID' => array(
|
||||
@ -53,9 +51,6 @@ class DirectorTest extends SapphireTest {
|
||||
public function tearDown() {
|
||||
// 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;
|
||||
$_SESSION = $this->originalSession;
|
||||
|
||||
@ -68,7 +63,6 @@ class DirectorTest extends SapphireTest {
|
||||
}
|
||||
}
|
||||
|
||||
Injector::unnest();
|
||||
|
||||
parent::tearDown();
|
||||
}
|
||||
@ -154,7 +148,7 @@ class DirectorTest extends SapphireTest {
|
||||
public function testAlternativeBaseURL() {
|
||||
// Get original protocol and hostname
|
||||
$rootURL = Director::protocolAndHost();
|
||||
|
||||
|
||||
// relative base URLs - you should end them in a /
|
||||
Config::inst()->update('Director', 'alternate_base_url', '/relativebase/');
|
||||
$_SERVER['REQUEST_URI'] = "$rootURL/relativebase/sub-page/";
|
||||
|
@ -7,6 +7,26 @@
|
||||
*/
|
||||
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()}
|
||||
*/
|
||||
|
@ -14,10 +14,18 @@ class ClassInfoTest extends SapphireTest {
|
||||
'ClassInfoTest_NoFields',
|
||||
);
|
||||
|
||||
public function setUp() {
|
||||
parent::setUp();
|
||||
ClassInfo::reset_db_cache();
|
||||
}
|
||||
|
||||
public function testExists() {
|
||||
$this->assertTrue(ClassInfo::exists('Object'));
|
||||
$this->assertTrue(ClassInfo::exists('object'));
|
||||
$this->assertTrue(ClassInfo::exists('ClassInfoTest'));
|
||||
$this->assertTrue(ClassInfo::exists('CLASSINFOTEST'));
|
||||
$this->assertTrue(ClassInfo::exists('stdClass'));
|
||||
$this->assertTrue(ClassInfo::exists('stdCLASS'));
|
||||
}
|
||||
|
||||
public function testSubclassesFor() {
|
||||
@ -30,6 +38,16 @@ class ClassInfoTest extends SapphireTest {
|
||||
),
|
||||
'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() {
|
||||
@ -42,11 +60,11 @@ class ClassInfoTest extends SapphireTest {
|
||||
$classes,
|
||||
'ClassInfo::classes_for_folder() returns classes matching the filename'
|
||||
);
|
||||
// $this->assertContains(
|
||||
// 'ClassInfoTest_BaseClass',
|
||||
// $classes,
|
||||
// 'ClassInfo::classes_for_folder() returns additional classes not matching the filename'
|
||||
// );
|
||||
$this->assertContains(
|
||||
'classinfotest_baseclass',
|
||||
$classes,
|
||||
'ClassInfo::classes_for_folder() returns additional classes not matching the filename'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -54,8 +72,11 @@ class ClassInfoTest extends SapphireTest {
|
||||
*/
|
||||
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_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->setExpectedException('InvalidArgumentException');
|
||||
ClassInfo::baseDataClass('DataObject');
|
||||
@ -75,6 +96,13 @@ class ClassInfoTest extends SapphireTest {
|
||||
));
|
||||
$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);
|
||||
$this->assertEquals(array('ClassInfoTest_BaseClass' => 'ClassInfoTest_BaseClass'), $ancestry,
|
||||
'$tablesOnly option excludes memory-only inheritance classes'
|
||||
@ -97,16 +125,22 @@ class ClassInfoTest extends SapphireTest {
|
||||
'ClassInfoTest_HasFields',
|
||||
);
|
||||
|
||||
|
||||
ClassInfo::reset_db_cache();
|
||||
$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]));
|
||||
|
||||
|
||||
$expect = array(
|
||||
'ClassInfoTest_BaseDataClass' => 'ClassInfoTest_BaseDataClass',
|
||||
'ClassInfoTest_HasFields' => 'ClassInfoTest_HasFields',
|
||||
);
|
||||
|
||||
ClassInfo::reset_db_cache();
|
||||
$this->assertEquals($expect, ClassInfo::dataClassesFor($classes[2]));
|
||||
ClassInfo::reset_db_cache();
|
||||
$this->assertEquals($expect, ClassInfo::dataClassesFor(strtolower($classes[2])));
|
||||
}
|
||||
|
||||
public function testTableForObjectField() {
|
||||
@ -114,19 +148,27 @@ class ClassInfoTest extends SapphireTest {
|
||||
ClassInfo::table_for_object_field('ClassInfoTest_WithRelation', 'RelationID')
|
||||
);
|
||||
|
||||
$this->assertEquals('ClassInfoTest_BaseDataClass',
|
||||
$this->assertEquals('ClassInfoTest_WithRelation',
|
||||
ClassInfo::table_for_object_field('ClassInfoTest_withrelation', 'RelationID')
|
||||
);
|
||||
|
||||
$this->assertEquals('ClassInfoTest_BaseDataClass',
|
||||
ClassInfo::table_for_object_field('ClassInfoTest_BaseDataClass', 'Title')
|
||||
);
|
||||
|
||||
$this->assertEquals('ClassInfoTest_BaseDataClass',
|
||||
$this->assertEquals('ClassInfoTest_BaseDataClass',
|
||||
ClassInfo::table_for_object_field('ClassInfoTest_HasFields', 'Title')
|
||||
);
|
||||
|
||||
$this->assertEquals('ClassInfoTest_BaseDataClass',
|
||||
$this->assertEquals('ClassInfoTest_BaseDataClass',
|
||||
ClassInfo::table_for_object_field('ClassInfoTest_NoFields', 'Title')
|
||||
);
|
||||
|
||||
$this->assertEquals('ClassInfoTest_HasFields',
|
||||
$this->assertEquals('ClassInfoTest_BaseDataClass',
|
||||
ClassInfo::table_for_object_field('classinfotest_nofields', 'Title')
|
||||
);
|
||||
|
||||
$this->assertEquals('ClassInfoTest_HasFields',
|
||||
ClassInfo::table_for_object_field('ClassInfoTest_HasFields', 'Description')
|
||||
);
|
||||
|
||||
|
@ -82,6 +82,19 @@ class ConfigTest_TestNest extends Object implements TestOnly {
|
||||
}
|
||||
|
||||
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() {
|
||||
|
||||
@ -189,8 +202,7 @@ class ConfigTest extends SapphireTest {
|
||||
// But it won't affect subclasses - this is *uninherited* static
|
||||
$this->assertNotContains('test_2b',
|
||||
Config::inst()->get('ConfigStaticTest_Third', 'first', Config::UNINHERITED));
|
||||
$this->assertNotContains('test_2b',
|
||||
Config::inst()->get('ConfigStaticTest_Fourth', 'first', Config::UNINHERITED));
|
||||
$this->assertNull(Config::inst()->get('ConfigStaticTest_Fourth', 'first', Config::UNINHERITED));
|
||||
|
||||
// Subclasses that don't have the static explicitly defined should allow definition, also
|
||||
// This also checks that set can be called after the first uninherited get()
|
||||
@ -283,12 +295,6 @@ class ConfigTest extends SapphireTest {
|
||||
}
|
||||
|
||||
public function testLRUDiscarding() {
|
||||
$depSettings = Deprecation::dump_settings();
|
||||
Deprecation::restore_settings(array(
|
||||
'level' => false,
|
||||
'version' => false,
|
||||
'moduleVersions' => false,
|
||||
));
|
||||
$cache = new ConfigTest_Config_LRU();
|
||||
for ($i = 0; $i < Config_LRU::SIZE*2; $i++) $cache->set($i, $i);
|
||||
$this->assertEquals(
|
||||
@ -302,16 +308,9 @@ class ConfigTest extends SapphireTest {
|
||||
Config_LRU::SIZE, count($cache->indexing),
|
||||
'Heterogenous usage gives sufficient discarding'
|
||||
);
|
||||
Deprecation::restore_settings($depSettings);
|
||||
}
|
||||
|
||||
public function testLRUCleaning() {
|
||||
$depSettings = Deprecation::dump_settings();
|
||||
Deprecation::restore_settings(array(
|
||||
'level' => false,
|
||||
'version' => false,
|
||||
'moduleVersions' => false,
|
||||
));
|
||||
$cache = new ConfigTest_Config_LRU();
|
||||
for ($i = 0; $i < Config_LRU::SIZE; $i++) $cache->set($i, $i);
|
||||
$this->assertEquals(Config_LRU::SIZE, count($cache->indexing));
|
||||
@ -328,7 +327,6 @@ class ConfigTest extends SapphireTest {
|
||||
$cache->clean('Bar');
|
||||
$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');
|
||||
Deprecation::restore_settings($depSettings);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -391,7 +391,7 @@ class ConfigManifestTest extends SapphireTest {
|
||||
|
||||
public function testEnvironmentRules() {
|
||||
foreach (array('dev', 'test', 'live') as $env) {
|
||||
Config::inst()->nest();
|
||||
Config::nest();
|
||||
|
||||
Config::inst()->update('Director', 'environment_type', $env);
|
||||
$config = $this->getConfigFixtureValue('Environment');
|
||||
@ -403,13 +403,11 @@ class ConfigManifestTest extends SapphireTest {
|
||||
);
|
||||
}
|
||||
|
||||
Config::inst()->unnest();
|
||||
Config::unnest();
|
||||
}
|
||||
}
|
||||
|
||||
public function testDynamicEnvironmentRules() {
|
||||
Config::inst()->nest();
|
||||
|
||||
// First, make sure environment_type is live
|
||||
Config::inst()->update('Director', 'environment_type', 'live');
|
||||
$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
|
||||
$this->assertEquals('dev', Config::inst()->get('ConfigManifestTest', 'DynamicEnvironment'));
|
||||
|
||||
Config::inst()->unnest();
|
||||
}
|
||||
|
||||
public function testMultipleRules() {
|
||||
|
@ -15,6 +15,152 @@ class NamespacedClassManifestTest extends SapphireTest {
|
||||
|
||||
$this->base = dirname(__FILE__) . '/fixtures/namespaced_classmanifest';
|
||||
$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() {
|
||||
@ -43,7 +189,8 @@ class NamespacedClassManifestTest extends SapphireTest {
|
||||
'silverstripe\test\classg' => "{$this->base}/module/classes/ClassG.php",
|
||||
'silverstripe\test\classh' => "{$this->base}/module/classes/ClassH.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());
|
||||
@ -54,7 +201,7 @@ class NamespacedClassManifestTest extends SapphireTest {
|
||||
array('sstemplateparser', 'sstemplateparseexception', 'silverstripe\test\classa',
|
||||
'silverstripe\test\classb', 'silverstripe\test\classc', 'silverstripe\test\classd',
|
||||
'silverstripe\test\classe', 'silverstripe\test\classf', 'silverstripe\test\classg',
|
||||
'silverstripe\test\classh'),
|
||||
'silverstripe\test\classh', 'silverstripe\framework\tests\classi'),
|
||||
$this->manifest->getClassNames());
|
||||
}
|
||||
|
||||
@ -88,7 +235,8 @@ class NamespacedClassManifestTest extends SapphireTest {
|
||||
$expect = array(
|
||||
'silverstripe\test\interfacea' => array('silverstripe\test\ClassE'),
|
||||
'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());
|
||||
}
|
||||
|
@ -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 {
|
||||
}
|
@ -15,12 +15,16 @@ class DeprecationTest extends SapphireTest {
|
||||
static $originalVersionInfo;
|
||||
|
||||
public function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
self::$originalVersionInfo = Deprecation::dump_settings();
|
||||
Deprecation::$notice_level = E_USER_NOTICE;
|
||||
Deprecation::set_enabled(true);
|
||||
}
|
||||
|
||||
public function tearDown() {
|
||||
Deprecation::restore_settings(self::$originalVersionInfo);
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
public function testLesserVersionTriggersNoNotice() {
|
||||
|
@ -1,17 +1,17 @@
|
||||
<?php
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Note: the running of this test is handled by the thing it's testing (DevelopmentAdmin controller).
|
||||
*
|
||||
*
|
||||
* @package framework
|
||||
* @package tests
|
||||
*/
|
||||
class DevAdminControllerTest extends FunctionalTest {
|
||||
|
||||
|
||||
public function setUp(){
|
||||
parent::setUp();
|
||||
|
||||
Config::nest()->update('DevelopmentAdmin', 'registered_controllers', array(
|
||||
Config::inst()->update('DevelopmentAdmin', 'registered_controllers', array(
|
||||
'x1' => array(
|
||||
'controller' => 'DevAdminControllerTest_Controller1',
|
||||
'links' => array(
|
||||
@ -27,45 +27,40 @@ class DevAdminControllerTest extends FunctionalTest {
|
||||
)
|
||||
));
|
||||
}
|
||||
|
||||
public function tearDown(){
|
||||
parent::tearDown();
|
||||
Config::unnest();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public function testGoodRegisteredControllerOutput(){
|
||||
// Check for the controller running from the registered url above
|
||||
// Check for the controller running from the registered url above
|
||||
// (we use contains rather than equals because sometimes you get Warning: You probably want to define an entry in $_FILE_TO_URL_MAPPING)
|
||||
$this->assertContains(DevAdminControllerTest_Controller1::OK_MSG, $this->getCapture('/dev/x1'));
|
||||
$this->assertContains(DevAdminControllerTest_Controller1::OK_MSG, $this->getCapture('/dev/x1/y1'));
|
||||
}
|
||||
|
||||
|
||||
public function testGoodRegisteredControllerStatus(){
|
||||
// Check response code is 200/OK
|
||||
$this->assertEquals(false, $this->getAndCheckForError('/dev/x1'));
|
||||
$this->assertEquals(false, $this->getAndCheckForError('/dev/x1/y1'));
|
||||
|
||||
|
||||
// Check response code is 500/ some sort of error
|
||||
$this->assertEquals(true, $this->getAndCheckForError('/dev/x2'));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
protected function getCapture($url){
|
||||
$this->logInWithPermission('ADMIN');
|
||||
|
||||
|
||||
ob_start();
|
||||
$this->get($url);
|
||||
$r = ob_get_contents();
|
||||
ob_end_clean();
|
||||
|
||||
|
||||
return $r;
|
||||
}
|
||||
|
||||
|
||||
protected function getAndCheckForError($url){
|
||||
$this->logInWithPermission('ADMIN');
|
||||
|
||||
|
||||
if(Director::is_cli()){
|
||||
// when in CLI the admin controller throws exceptions
|
||||
ob_start();
|
||||
@ -75,10 +70,10 @@ class DevAdminControllerTest extends FunctionalTest {
|
||||
ob_end_clean();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
ob_end_clean();
|
||||
return false;
|
||||
|
||||
|
||||
}else{
|
||||
// when in http the admin controller sets a response header
|
||||
ob_start();
|
||||
@ -87,30 +82,30 @@ class DevAdminControllerTest extends FunctionalTest {
|
||||
return $resp->isError();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
class DevAdminControllerTest_Controller1 extends Controller {
|
||||
|
||||
|
||||
const OK_MSG = 'DevAdminControllerTest_Controller1 TEST OK';
|
||||
|
||||
|
||||
private static $url_handlers = array(
|
||||
'' => 'index',
|
||||
'y1' => 'y1Action'
|
||||
);
|
||||
|
||||
|
||||
private static $allowed_actions = array(
|
||||
'index',
|
||||
'y1Action',
|
||||
);
|
||||
|
||||
|
||||
|
||||
|
||||
public function index(){
|
||||
echo self::OK_MSG;
|
||||
}
|
||||
|
||||
|
||||
public function y1Action(){
|
||||
echo self::OK_MSG;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -111,7 +111,7 @@ class EmailTest extends SapphireTest {
|
||||
$oldProject = $project;
|
||||
$project = 'emailtest';
|
||||
|
||||
Email::set_mailer(new EmailTest_Mailer());
|
||||
Injector::inst()->registerService(new EmailTest_Mailer(), 'Mailer');
|
||||
$email = new Email(
|
||||
'from@example.com',
|
||||
'to@example.com',
|
||||
@ -160,7 +160,7 @@ class EmailTest extends SapphireTest {
|
||||
$oldProject = $project;
|
||||
$project = 'emailtest';
|
||||
|
||||
Email::set_mailer(new EmailTest_Mailer());
|
||||
Injector::inst()->registerService(new EmailTest_Mailer(), 'Mailer');
|
||||
$email = new Email(
|
||||
'from@example.com',
|
||||
'to@example.com',
|
||||
|
@ -250,6 +250,9 @@ class FileTest extends SapphireTest {
|
||||
$file = $this->objFromFixture('File', 'pdf');
|
||||
$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 */
|
||||
$file = $this->objFromFixture('File', 'asdf');
|
||||
$this->assertEquals("unknown", $file->FileType);
|
||||
|
@ -13,6 +13,8 @@ File:
|
||||
Filename: assets/FileTest.txt
|
||||
gif:
|
||||
Filename: assets/FileTest.gif
|
||||
gifupper:
|
||||
Filename: assets/FileTest.GIF
|
||||
pdf:
|
||||
Filename: assets/FileTest.pdf
|
||||
setfromname:
|
||||
|
@ -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() {
|
||||
return new Form(
|
||||
new Controller(),
|
||||
|
@ -91,6 +91,14 @@ class TimeFieldTest extends SapphireTest {
|
||||
$f = new TimeField('Time', 'Time');
|
||||
$f->setValue('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() {
|
||||
|
@ -253,23 +253,49 @@ class UploadFieldTest extends FunctionalTest {
|
||||
// two should work and the third will fail.
|
||||
$record = $this->objFromFixture('UploadFieldTest_Record', 'record1');
|
||||
$record->HasManyFilesMaxTwo()->removeAll();
|
||||
$this->assertCount(0, $record->HasManyFilesMaxTwo());
|
||||
|
||||
// Get references for each file to upload
|
||||
$file1 = $this->objFromFixture('File', 'file1');
|
||||
$file2 = $this->objFromFixture('File', 'file2');
|
||||
$file3 = $this->objFromFixture('File', 'file3');
|
||||
$this->assertTrue($file1->exists());
|
||||
$this->assertTrue($file2->exists());
|
||||
$this->assertTrue($file3->exists());
|
||||
|
||||
// Write the first element, should be okay.
|
||||
$response = $this->mockUploadFileIDs('HasManyFilesMaxTwo', array($file1->ID));
|
||||
$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.
|
||||
$response = $this->mockUploadFileIDs('HasManyFilesMaxTwo', array($file1->ID, $file2->ID));
|
||||
$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.
|
||||
$response = $this->mockUploadFileIDs('HasManyFilesMaxTwo', array($file1->ID, $file2->ID, $file3->ID));
|
||||
$this->assertNotEmpty($response['errors']);
|
||||
$this->assertCount(0, $record->HasManyFilesMaxTwo());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -948,22 +974,21 @@ class UploadFieldTest extends FunctionalTest {
|
||||
}
|
||||
|
||||
public function setUp() {
|
||||
Config::inst()->update('File', 'update_filesystem', false);
|
||||
parent::setUp();
|
||||
|
||||
if(!file_exists(ASSETS_PATH)) mkdir(ASSETS_PATH);
|
||||
|
||||
/* Create a test folders for each of the fixture references */
|
||||
$folderIDs = $this->allFixtureIDs('Folder');
|
||||
foreach($folderIDs as $folderID) {
|
||||
$folder = DataObject::get_by_id('Folder', $folderID);
|
||||
if(!file_exists(BASE_PATH."/$folder->Filename")) mkdir(BASE_PATH."/$folder->Filename");
|
||||
$folders = Folder::get()->byIDs($this->allFixtureIDs('Folder'));
|
||||
foreach($folders as $folder) {
|
||||
if(!file_exists($folder->getFullPath())) mkdir($folder->getFullPath());
|
||||
}
|
||||
|
||||
/* Create a test files for each of the fixture references */
|
||||
$fileIDs = $this->allFixtureIDs('File');
|
||||
foreach($fileIDs as $fileID) {
|
||||
$file = DataObject::get_by_id('File', $fileID);
|
||||
$fh = fopen(BASE_PATH."/$file->Filename", "w");
|
||||
$files = File::get()->byIDs($this->allFixtureIDs('File'));
|
||||
foreach($files as $file) {
|
||||
$fh = fopen($file->getFullPath(), "w");
|
||||
fwrite($fh, str_repeat('x',1000000));
|
||||
fclose($fh);
|
||||
}
|
||||
|
@ -3,48 +3,48 @@ Folder:
|
||||
Name: UploadFieldTest
|
||||
folder1-subfolder1:
|
||||
Name: subfolder1
|
||||
ParentID: =>Folder.folder1
|
||||
Parent: =>Folder.folder1
|
||||
File:
|
||||
file1:
|
||||
Title: File1
|
||||
Filename: assets/UploadFieldTest/file1.txt
|
||||
ParentID: =>Folder.folder1
|
||||
Parent: =>Folder.folder1
|
||||
file2:
|
||||
Title: File2
|
||||
Filename: assets/UploadFieldTest/file2.txt
|
||||
ParentID: =>Folder.folder1
|
||||
Parent: =>Folder.folder1
|
||||
file3:
|
||||
Title: File3
|
||||
Filename: assets/UploadFieldTest/file3.txt
|
||||
ParentID: =>Folder.folder1
|
||||
Parent: =>Folder.folder1
|
||||
file4:
|
||||
Title: File4
|
||||
Filename: assets/UploadFieldTest/file4.txt
|
||||
ParentID: =>Folder.folder1
|
||||
Parent: =>Folder.folder1
|
||||
file5:
|
||||
Title: File5
|
||||
Filename: assets/UploadFieldTest/file5.txt
|
||||
ParentID: =>Folder.folder1
|
||||
Parent: =>Folder.folder1
|
||||
file-noview:
|
||||
Title: noview.txt
|
||||
Name: noview.txt
|
||||
Filename: assets/UploadFieldTest/noview.txt
|
||||
ParentID: =>Folder.folder1
|
||||
Parent: =>Folder.folder1
|
||||
file-noedit:
|
||||
Title: noedit.txt
|
||||
Name: noedit.txt
|
||||
Filename: assets/UploadFieldTest/noedit.txt
|
||||
ParentID: =>Folder.folder1
|
||||
Parent: =>Folder.folder1
|
||||
file-nodelete:
|
||||
Title: nodelete.txt
|
||||
Name: nodelete.txt
|
||||
Filename: assets/UploadFieldTest/nodelete.txt
|
||||
ParentID: =>Folder.folder1
|
||||
Parent: =>Folder.folder1
|
||||
file-subfolder:
|
||||
Title: file-subfolder.txt
|
||||
Name: file-subfolder.txt
|
||||
Filename: assets/UploadFieldTest/subfolder1/file-subfolder.txt
|
||||
ParentID: =>Folder.folder1-subfolder1
|
||||
Parent: =>Folder.folder1-subfolder1
|
||||
UploadFieldTest_Record:
|
||||
record1:
|
||||
Title: Record 1
|
||||
|
0
tests/i18n/_fakewebroot/assets/blank
Normal file
0
tests/i18n/_fakewebroot/assets/blank
Normal file
@ -0,0 +1 @@
|
||||
<?php
|
@ -0,0 +1 @@
|
||||
<p></p>
|
@ -0,0 +1 @@
|
||||
<?php
|
@ -0,0 +1 @@
|
||||
<?php
|
2
tests/i18n/_fakewebroot/notmodule/noop.php
Normal file
2
tests/i18n/_fakewebroot/notmodule/noop.php
Normal file
@ -0,0 +1,2 @@
|
||||
<?php
|
||||
|
@ -4,25 +4,25 @@
|
||||
* @subpackage tests
|
||||
*/
|
||||
class i18nTextCollectorTest extends SapphireTest {
|
||||
|
||||
|
||||
/**
|
||||
* @var string $tmpBasePath Used to write language files.
|
||||
* We don't want to store them inside framework (or in any web-accessible place)
|
||||
* in case something goes wrong with the file parsing.
|
||||
*/
|
||||
protected $alternateBaseSavePath;
|
||||
|
||||
|
||||
/**
|
||||
* @var string $alternateBasePath Fake webroot with a single module
|
||||
* /i18ntestmodule which contains some files with _t() calls.
|
||||
*/
|
||||
protected $alternateBasePath;
|
||||
|
||||
|
||||
protected $manifest;
|
||||
|
||||
|
||||
public function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
|
||||
$this->alternateBasePath = $this->getCurrentAbsolutePath() . "/_fakewebroot";
|
||||
$this->alternateBaseSavePath = TEMP_FOLDER . '/i18nTextCollectorTest_webroot';
|
||||
Filesystem::makeFolder($this->alternateBaseSavePath);
|
||||
@ -32,12 +32,12 @@ class i18nTextCollectorTest extends SapphireTest {
|
||||
$this->manifest = new SS_ClassManifest(
|
||||
$this->alternateBasePath, false, true, false
|
||||
);
|
||||
|
||||
|
||||
$manifest = new SS_TemplateManifest($this->alternateBasePath, null, false, true);
|
||||
$manifest->regenerate(false);
|
||||
SS_TemplateLoader::instance()->pushManifest($manifest);
|
||||
}
|
||||
|
||||
|
||||
public function tearDown() {
|
||||
SS_TemplateLoader::instance()->popManifest();
|
||||
parent::tearDown();
|
||||
@ -58,7 +58,7 @@ _t(
|
||||
|
||||
_t(
|
||||
'Test.CONCATENATED2',
|
||||
"Line \"4\" and " .
|
||||
"Line \"4\" and " .
|
||||
"Line 5");
|
||||
PHP;
|
||||
$this->assertEquals(
|
||||
@ -120,7 +120,7 @@ SS;
|
||||
'Test.DOUBLEQUOTE' => array("Double Quote and Spaces")
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
$html = <<<SS
|
||||
<% _t("Test.NOSEMICOLON","No Semicolon") %>
|
||||
SS;
|
||||
@ -166,7 +166,7 @@ SS;
|
||||
<% _t(
|
||||
'Test.PRIOANDCOMMENT',
|
||||
" Prio and Value with 'Single Quotes'",
|
||||
|
||||
|
||||
"Comment with 'Single Quotes'"
|
||||
) %>
|
||||
SS;
|
||||
@ -181,7 +181,7 @@ SS;
|
||||
|
||||
public function testCollectFromCodeSimple() {
|
||||
$c = new i18nTextCollector();
|
||||
|
||||
|
||||
$php = <<<PHP
|
||||
_t('Test.SINGLEQUOTE','Single Quote');
|
||||
PHP;
|
||||
@ -191,7 +191,7 @@ PHP;
|
||||
'Test.SINGLEQUOTE' => array('Single Quote')
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
$php = <<<PHP
|
||||
_t( "Test.DOUBLEQUOTE", "Double Quote and Spaces" );
|
||||
PHP;
|
||||
@ -202,7 +202,7 @@ PHP;
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
public function testCollectFromCodeAdvanced() {
|
||||
$c = new i18nTextCollector();
|
||||
|
||||
@ -218,12 +218,12 @@ PHP;
|
||||
'Test.NEWLINES' => array("New Lines")
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
$php = <<<PHP
|
||||
_t(
|
||||
'Test.PRIOANDCOMMENT',
|
||||
' Value with "Double Quotes"',
|
||||
|
||||
|
||||
'Comment with "Double Quotes"'
|
||||
);
|
||||
PHP;
|
||||
@ -233,12 +233,12 @@ PHP;
|
||||
'Test.PRIOANDCOMMENT' => array(' Value with "Double Quotes"','Comment with "Double Quotes"')
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
$php = <<<PHP
|
||||
_t(
|
||||
'Test.PRIOANDCOMMENT',
|
||||
" Value with 'Single Quotes'",
|
||||
|
||||
|
||||
"Comment with 'Single Quotes'"
|
||||
);
|
||||
PHP;
|
||||
@ -248,7 +248,7 @@ PHP;
|
||||
'Test.PRIOANDCOMMENT' => array(" Value with 'Single Quotes'","Comment with 'Single Quotes'")
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
$php = <<<PHP
|
||||
_t(
|
||||
'Test.PRIOANDCOMMENT',
|
||||
@ -261,7 +261,7 @@ PHP;
|
||||
'Test.PRIOANDCOMMENT' => array("Value with 'Escaped Single Quotes'")
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
$php = <<<PHP
|
||||
_t(
|
||||
'Test.PRIOANDCOMMENT',
|
||||
@ -275,8 +275,8 @@ PHP;
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public function testNewlinesInEntityValues() {
|
||||
$c = new i18nTextCollector();
|
||||
|
||||
@ -364,24 +364,24 @@ PHP;
|
||||
*/
|
||||
public function testPhpWriterLangArrayCodeForEntity() {
|
||||
$c = new i18nTextCollector_Writer_Php();
|
||||
|
||||
|
||||
$this->assertEquals(
|
||||
$c->langArrayCodeForEntitySpec('Test.SIMPLE', array('Simple Value'), 'en_US'),
|
||||
"\$lang['en_US']['Test']['SIMPLE'] = 'Simple Value';" . PHP_EOL
|
||||
);
|
||||
|
||||
|
||||
$this->assertEquals(
|
||||
// single quotes should be properly escaped by the parser already
|
||||
$c->langArrayCodeForEntitySpec('Test.ESCAPEDSINGLEQUOTES',
|
||||
array("Value with 'Escaped Single Quotes'"), 'en_US'),
|
||||
"\$lang['en_US']['Test']['ESCAPEDSINGLEQUOTES'] = 'Value with \'Escaped Single Quotes\'';" . PHP_EOL
|
||||
);
|
||||
|
||||
|
||||
$this->assertEquals(
|
||||
$c->langArrayCodeForEntitySpec('Test.DOUBLEQUOTES', array('Value with "Double Quotes"'), 'en_US'),
|
||||
"\$lang['en_US']['Test']['DOUBLEQUOTES'] = 'Value with \"Double Quotes\"';" . PHP_EOL
|
||||
);
|
||||
|
||||
|
||||
$php = <<<PHP
|
||||
\$lang['en_US']['Test']['PRIOANDCOMMENT'] = array (
|
||||
0 => 'Value with \'Single Quotes\'',
|
||||
@ -394,7 +394,7 @@ PHP;
|
||||
array("Value with 'Single Quotes'","Comment with 'Single Quotes'"), 'en_US'),
|
||||
$php
|
||||
);
|
||||
|
||||
|
||||
$php = <<<PHP
|
||||
\$lang['en_US']['Test']['PRIOANDCOMMENT'] = array (
|
||||
0 => 'Value with "Double Quotes"',
|
||||
@ -434,21 +434,19 @@ de:
|
||||
YAML;
|
||||
$this->assertEquals($yaml, Convert::nl2os($writer->getYaml($entities, 'de')));
|
||||
}
|
||||
|
||||
|
||||
public function testCollectFromIncludedTemplates() {
|
||||
$c = new i18nTextCollector();
|
||||
|
||||
|
||||
$templateFilePath = $this->alternateBasePath . '/i18ntestmodule/templates/Layout/i18nTestModule.ss';
|
||||
$html = file_get_contents($templateFilePath);
|
||||
$matches = $c->collectFromTemplate($html, 'mymodule', 'RandomNamespace');
|
||||
|
||||
/*
|
||||
$this->assertArrayHasKey('i18nTestModule.ss.LAYOUTTEMPLATENONAMESPACE', $matches);
|
||||
$this->assertArrayHasKey('RandomNamespace.LAYOUTTEMPLATENONAMESPACE', $matches);
|
||||
$this->assertEquals(
|
||||
$matches['i18nTestModule.ss.LAYOUTTEMPLATENONAMESPACE'],
|
||||
$matches['RandomNamespace.LAYOUTTEMPLATENONAMESPACE'],
|
||||
array('Layout Template no namespace')
|
||||
);
|
||||
*/
|
||||
$this->assertArrayHasKey('RandomNamespace.SPRINTFNONAMESPACE', $matches);
|
||||
$this->assertEquals(
|
||||
$matches['RandomNamespace.SPRINTFNONAMESPACE'],
|
||||
@ -464,98 +462,70 @@ YAML;
|
||||
$matches['i18nTestModule.SPRINTFNAMESPACE'],
|
||||
array('My replacement: %s')
|
||||
);
|
||||
$this->assertArrayHasKey('i18nTestModule.WITHNAMESPACE', $matches);
|
||||
$this->assertEquals(
|
||||
$matches['i18nTestModule.WITHNAMESPACE'],
|
||||
array('Include Entity with Namespace')
|
||||
);
|
||||
$this->assertArrayHasKey('i18nTestModuleInclude.ss.NONAMESPACE', $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')
|
||||
);
|
||||
|
||||
// Includes should not automatically inject translations into parent templates
|
||||
$this->assertArrayNotHasKey('i18nTestModule.WITHNAMESPACE', $matches);
|
||||
$this->assertArrayNotHasKey('i18nTestModuleInclude.ss.NONAMESPACE', $matches);
|
||||
$this->assertArrayNotHasKey('i18nTestModuleInclude.ss.SPRINTFINCLUDENAMESPACE', $matches);
|
||||
$this->assertArrayNotHasKey('i18nTestModuleInclude.ss.SPRINTFINCLUDENONAMESPACE', $matches);
|
||||
}
|
||||
|
||||
|
||||
public function testCollectFromThemesTemplates() {
|
||||
$c = new i18nTextCollector();
|
||||
|
||||
$theme = Config::inst()->get('SSViewer', 'theme');
|
||||
Config::inst()->update('SSViewer', 'theme', 'testtheme1');
|
||||
|
||||
$templateFilePath = $this->alternateBasePath . '/themes/testtheme1/templates/Layout/i18nTestTheme1.ss';
|
||||
$html = file_get_contents($templateFilePath);
|
||||
$matches = $c->collectFromTemplate($html, 'themes/testtheme1', 'i18nTestTheme1.ss');
|
||||
|
||||
// Collect from layout
|
||||
$layoutFilePath = $this->alternateBasePath . '/themes/testtheme1/templates/Layout/i18nTestTheme1.ss';
|
||||
$layoutHTML = file_get_contents($layoutFilePath);
|
||||
$layoutMatches = $c->collectFromTemplate($layoutHTML, 'themes/testtheme1', 'i18nTestTheme1.ss');
|
||||
|
||||
// all entities from i18nTestTheme1.ss
|
||||
$this->assertEquals(
|
||||
$matches['i18nTestTheme1.LAYOUTTEMPLATE'],
|
||||
array('Theme1 Layout Template')
|
||||
array(
|
||||
'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);
|
||||
$this->assertEquals(
|
||||
$matches['i18nTestTheme1.ss.LAYOUTTEMPLATENONAMESPACE'],
|
||||
array('Theme1 Layout Template no namespace')
|
||||
);
|
||||
|
||||
$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')
|
||||
);
|
||||
|
||||
|
||||
// Collect from include
|
||||
$includeFilePath = $this->alternateBasePath . '/themes/testtheme1/templates/Includes/i18nTestTheme1Include.ss';
|
||||
$includeHTML = file_get_contents($includeFilePath);
|
||||
$includeMatches = $c->collectFromTemplate($includeHTML, 'themes/testtheme1', 'i18nTestTheme1Include.ss');
|
||||
|
||||
// all entities from i18nTestTheme1Include.ss
|
||||
$this->assertEquals(
|
||||
$matches['i18nTestTheme1Include.WITHNAMESPACE'],
|
||||
array('Theme1 Include Entity with Namespace')
|
||||
array(
|
||||
'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() {
|
||||
$defaultlocal = i18n::default_locale();
|
||||
$local = i18n::get_locale();
|
||||
i18n::set_locale('en_US');
|
||||
i18n::set_locale('en_US');
|
||||
i18n::set_default_locale('en_US');
|
||||
|
||||
$c = new i18nTextCollector();
|
||||
$c->setWriter(new i18nTextCollector_Writer_Php());
|
||||
$c->basePath = $this->alternateBasePath;
|
||||
$c->baseSavePath = $this->alternateBaseSavePath;
|
||||
|
||||
|
||||
$entitiesByModule = $c->collect(null, true /* merge */);
|
||||
$this->assertArrayHasKey(
|
||||
'i18nTestModule.ENTITY',
|
||||
@ -568,7 +538,7 @@ YAML;
|
||||
'Adds new entities'
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
public function testCollectFromFilesystemAndWriteMasterTables() {
|
||||
$defaultlocal = i18n::default_locale();
|
||||
$local = i18n::get_locale();
|
||||
@ -579,16 +549,16 @@ YAML;
|
||||
$c->setWriter(new i18nTextCollector_Writer_Php());
|
||||
$c->basePath = $this->alternateBasePath;
|
||||
$c->baseSavePath = $this->alternateBaseSavePath;
|
||||
|
||||
|
||||
$c->run();
|
||||
|
||||
|
||||
// i18ntestmodule
|
||||
$moduleLangFile = "{$this->alternateBaseSavePath}/i18ntestmodule/lang/" . $c->getDefaultLocale() . '.php';
|
||||
$this->assertTrue(
|
||||
file_exists($moduleLangFile),
|
||||
'Master language file can be written to modules /lang folder'
|
||||
);
|
||||
|
||||
|
||||
$moduleLangFileContent = file_get_contents($moduleLangFile);
|
||||
$this->assertContains(
|
||||
"\$lang['en']['i18nTestModule']['ADDITION'] = 'Addition';",
|
||||
@ -617,7 +587,7 @@ YAML;
|
||||
"\$lang['en']['i18nTestModuleInclude.ss']['NONAMESPACE'] = 'Include Entity without Namespace';",
|
||||
$moduleLangFileContent
|
||||
);
|
||||
|
||||
|
||||
// i18nothermodule
|
||||
$otherModuleLangFile = "{$this->alternateBaseSavePath}/i18nothermodule/lang/" . $c->getDefaultLocale() . '.php';
|
||||
$this->assertTrue(
|
||||
@ -633,7 +603,7 @@ YAML;
|
||||
"\$lang['en']['i18nOtherModule']['MAINTEMPLATE'] = 'Main Template Other Module';",
|
||||
$otherModuleLangFileContent
|
||||
);
|
||||
|
||||
|
||||
// testtheme1
|
||||
$theme1LangFile = "{$this->alternateBaseSavePath}/themes/testtheme1/lang/" . $c->getDefaultLocale() . '.php';
|
||||
$this->assertTrue(
|
||||
@ -661,7 +631,7 @@ YAML;
|
||||
"\$lang['en']['i18nTestTheme1.ss']['SPRINTFNONAMESPACE'] = 'Theme1 My replacement no namespace: %s';",
|
||||
$theme1LangFileContent
|
||||
);
|
||||
|
||||
|
||||
$this->assertContains(
|
||||
"\$lang['en']['i18nTestTheme1Include']['SPRINTFINCLUDENAMESPACE'] = 'Theme1 My include replacement: %s';",
|
||||
$theme1LangFileContent
|
||||
@ -679,7 +649,7 @@ YAML;
|
||||
. " 'Theme1 My include replacement no namespace: %s';",
|
||||
$theme1LangFileContent
|
||||
);
|
||||
|
||||
|
||||
// testtheme2
|
||||
$theme2LangFile = "{$this->alternateBaseSavePath}/themes/testtheme2/lang/" . $c->getDefaultLocale() . '.php';
|
||||
$this->assertTrue(
|
||||
@ -695,7 +665,7 @@ YAML;
|
||||
i18n::set_locale($local); //set the locale to the US locale expected in the asserts
|
||||
i18n::set_default_locale($defaultlocal);
|
||||
}
|
||||
|
||||
|
||||
public function testCollectFromEntityProvidersInCustomObject() {
|
||||
$c = new i18nTextCollector();
|
||||
|
||||
@ -713,5 +683,164 @@ YAML;
|
||||
$matches['i18nTextCollectorTestMyObject.SINGULARNAME'][0]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -146,6 +146,11 @@ class DataListTest extends SapphireTest {
|
||||
$this->assertEquals('DataObjectTest_TeamComment',$list->dataClass());
|
||||
}
|
||||
|
||||
public function testDataClassCaseInsensitive() {
|
||||
$list = DataList::create('dataobjecttest_teamcomment');
|
||||
$this->assertTrue($list->exists());
|
||||
}
|
||||
|
||||
public function testClone() {
|
||||
$list = DataObjectTest_TeamComment::get();
|
||||
$this->assertEquals($list, clone($list));
|
||||
|
@ -46,7 +46,6 @@ class DataObjectSchemaGenerationTest extends SapphireTest {
|
||||
// Table will have been initially created by the $extraDataObjects setting
|
||||
|
||||
// Let's insert a new field here
|
||||
Config::nest();
|
||||
Config::inst()->update('DataObjectSchemaGenerationTest_DO', 'db', array(
|
||||
'SecretField' => 'Varchar(100)'
|
||||
));
|
||||
@ -59,9 +58,6 @@ class DataObjectSchemaGenerationTest extends SapphireTest {
|
||||
$schema->cancelSchemaUpdate();
|
||||
$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
|
||||
Config::nest();
|
||||
Config::inst()->remove('DataObjectSchemaGenerationTest_IndexDO', 'indexes');
|
||||
Config::inst()->update('DataObjectSchemaGenerationTest_IndexDO', 'indexes',
|
||||
Config::inst()->get('DataObjectSchemaGenerationTest_IndexDO', 'indexes_alt')
|
||||
@ -98,9 +93,6 @@ class DataObjectSchemaGenerationTest extends SapphireTest {
|
||||
$schema->cancelSchemaUpdate();
|
||||
$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
|
||||
|
||||
// Update the SearchFields index here
|
||||
Config::nest();
|
||||
Config::inst()->update('DataObjectSchemaGenerationTest_IndexDO', 'indexes', array(
|
||||
'SearchFields' => array(
|
||||
'value' => 'Title'
|
||||
@ -129,9 +120,6 @@ class DataObjectSchemaGenerationTest extends SapphireTest {
|
||||
$schema->cancelSchemaUpdate();
|
||||
$test->assertTrue($needsUpdating);
|
||||
});
|
||||
|
||||
// Restore old indexes
|
||||
Config::unnest();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -218,4 +206,4 @@ class DataObjectSchemaGenerationTest_IndexDO extends DataObjectSchemaGenerationT
|
||||
),
|
||||
'SearchFields' => 'fulltext ("Title","Content")'
|
||||
);
|
||||
}
|
||||
}
|
@ -61,6 +61,30 @@ class DataObjectTest extends SapphireTest {
|
||||
$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() {
|
||||
$obj = new DataObjectTest_ValidatedObject();
|
||||
|
||||
|
@ -199,7 +199,7 @@ class DataQueryTest extends SapphireTest {
|
||||
public function testComparisonClauseInt() {
|
||||
DB::query("INSERT INTO \"DataQueryTest_F\" (\"SortOrder\") VALUES (2)");
|
||||
$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->resetDBSchema(true);
|
||||
}
|
||||
@ -207,7 +207,7 @@ class DataQueryTest extends SapphireTest {
|
||||
public function testComparisonClauseDateFull() {
|
||||
DB::query("INSERT INTO \"DataQueryTest_F\" (\"MyDate\") VALUES ('1988-03-04 06:30')");
|
||||
$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->resetDBSchema(true);
|
||||
}
|
||||
@ -215,7 +215,7 @@ class DataQueryTest extends SapphireTest {
|
||||
public function testComparisonClauseDateStartsWith() {
|
||||
DB::query("INSERT INTO \"DataQueryTest_F\" (\"MyDate\") VALUES ('1988-03-04 06:30')");
|
||||
$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->resetDBSchema(true);
|
||||
}
|
||||
@ -223,7 +223,7 @@ class DataQueryTest extends SapphireTest {
|
||||
public function testComparisonClauseDateStartsPartial() {
|
||||
DB::query("INSERT INTO \"DataQueryTest_F\" (\"MyDate\") VALUES ('1988-03-04 06:30')");
|
||||
$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->resetDBSchema(true);
|
||||
}
|
||||
@ -231,7 +231,7 @@ class DataQueryTest extends SapphireTest {
|
||||
public function testComparisonClauseTextCaseInsensitive() {
|
||||
DB::query("INSERT INTO \"DataQueryTest_F\" (\"MyString\") VALUES ('HelloWorld')");
|
||||
$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->resetDBSchema(true);
|
||||
}
|
||||
@ -239,11 +239,11 @@ class DataQueryTest extends SapphireTest {
|
||||
public function testComparisonClauseTextCaseSensitive() {
|
||||
DB::query("INSERT INTO \"DataQueryTest_F\" (\"MyString\") VALUES ('HelloWorld')");
|
||||
$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");
|
||||
|
||||
$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->resetDBSchema(true);
|
||||
}
|
||||
|
@ -250,13 +250,13 @@ class ManyManyListTest extends SapphireTest {
|
||||
|
||||
// ensure that ManyManyListTest_ExtraFields_Clients.ValueCurrency is
|
||||
// selected.
|
||||
$db = DB::getConn();
|
||||
$db = DB::get_conn();
|
||||
$expected = 'SELECT DISTINCT "ManyManyListTest_ExtraFields_Clients"."WorthCurrency",'
|
||||
.' "ManyManyListTest_ExtraFields_Clients"."WorthAmount", "ManyManyListTest_ExtraFields_Clients"."Reference",'
|
||||
.' "ManyManyListTest_ExtraFields"."ClassName", "ManyManyListTest_ExtraFields"."LastEdited",'
|
||||
.' "ManyManyListTest_ExtraFields"."Created", "ManyManyListTest_ExtraFields"."ID",'
|
||||
.' 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'
|
||||
.' "ManyManyListTest_ExtraFields_Clients" ON'
|
||||
.' "ManyManyListTest_ExtraFields_Clients"."ManyManyListTest_ExtraFieldsID" ='
|
||||
|
113
tests/model/PDODatabaseTest.php
Normal file
113
tests/model/PDODatabaseTest.php
Normal 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());
|
||||
}
|
||||
}
|
@ -13,6 +13,18 @@ class SQLQueryTest extends SapphireTest {
|
||||
'SQLQueryTestBase',
|
||||
'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() {
|
||||
|
||||
@ -583,7 +595,7 @@ class SQLQueryTest extends SapphireTest {
|
||||
}
|
||||
|
||||
public function testSelect() {
|
||||
$query = new SQLQuery('"Title"', '"MyTable"');
|
||||
$query = new SQLSelect('"Title"', '"MyTable"');
|
||||
$query->addSelect('"TestField"');
|
||||
$this->assertSQLEquals(
|
||||
'SELECT "Title", "TestField" FROM "MyTable"',
|
||||
@ -670,6 +682,100 @@ class SQLQueryTest extends SapphireTest {
|
||||
$this->assertEquals(array('%MyName%', '2012-08-08 12:00'), $parameters);
|
||||
$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 {
|
||||
|
@ -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 {
|
||||
|
@ -1,16 +1,6 @@
|
||||
<?php
|
||||
|
||||
class OembedTest extends SapphireTest {
|
||||
public function setUp() {
|
||||
parent::setUp();
|
||||
Config::nest();
|
||||
}
|
||||
|
||||
public function tearDown() {
|
||||
Config::unnest();
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
public function testGetOembedFromUrl() {
|
||||
Config::inst()->update('Oembed', 'providers', array(
|
||||
'http://*.silverstripe.com/watch*'=>'http://www.silverstripe.com/oembed/'
|
||||
|
@ -4,17 +4,17 @@
|
||||
* @subpackage tests
|
||||
*/
|
||||
class ShortcodeParserTest extends SapphireTest {
|
||||
|
||||
|
||||
protected $arguments, $contents, $tagName, $parser;
|
||||
protected $extra = array();
|
||||
|
||||
|
||||
public function setUp() {
|
||||
ShortcodeParser::get('test')->register('test_shortcode', array($this, 'shortcodeSaver'));
|
||||
$this->parser = ShortcodeParser::get('test');
|
||||
|
||||
|
||||
parent::setUp();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Tests that valid short codes that have not been registered are not replaced.
|
||||
*/
|
||||
@ -37,7 +37,7 @@ class ShortcodeParserTest extends SapphireTest {
|
||||
'<strong class="warning">[not_shortcode]</strong>',
|
||||
$this->parser->parse('[not_shortcode]')
|
||||
);
|
||||
|
||||
|
||||
ShortcodeParser::$error_behavior = ShortcodeParser::LEAVE;
|
||||
|
||||
$this->assertEquals('[not_shortcode]',
|
||||
@ -56,66 +56,66 @@ class ShortcodeParserTest extends SapphireTest {
|
||||
$this->parser->parse('<img class="[not_shortcode]">')
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
public function testSimpleTag() {
|
||||
$tests = array(
|
||||
'[test_shortcode]',
|
||||
'[test_shortcode]',
|
||||
'[test_shortcode ]', '[test_shortcode,]', '[test_shortcode, ]'.
|
||||
'[test_shortcode/]', '[test_shortcode /]', '[test_shortcode,/]', '[test_shortcode, /]'
|
||||
);
|
||||
|
||||
|
||||
foreach($tests as $test) {
|
||||
$this->parser->parse($test);
|
||||
|
||||
|
||||
$this->assertEquals(array(), $this->arguments, $test);
|
||||
$this->assertEquals('', $this->contents, $test);
|
||||
$this->assertEquals('test_shortcode', $this->tagName, $test);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function testOneArgument() {
|
||||
$tests = array (
|
||||
'[test_shortcode foo="bar"]', '[test_shortcode,foo="bar"]',
|
||||
"[test_shortcode foo='bar']", "[test_shortcode,foo='bar']",
|
||||
'[test_shortcode foo = "bar" /]', '[test_shortcode, foo = "bar" /]'
|
||||
);
|
||||
|
||||
|
||||
foreach($tests as $test) {
|
||||
$this->parser->parse($test);
|
||||
|
||||
|
||||
$this->assertEquals(array('foo' => 'bar'), $this->arguments, $test);
|
||||
$this->assertEquals('', $this->contents, $test);
|
||||
$this->assertEquals('test_shortcode', $this->tagName, $test);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function testMultipleArguments() {
|
||||
$this->parser->parse('[test_shortcode foo = "bar",bar=\'foo\', baz="buz"]');
|
||||
|
||||
|
||||
$this->assertEquals(array('foo' => 'bar', 'bar' => 'foo', 'baz' => 'buz'), $this->arguments);
|
||||
$this->assertEquals('', $this->contents);
|
||||
$this->assertEquals('test_shortcode', $this->tagName);
|
||||
}
|
||||
|
||||
|
||||
public function testEnclosing() {
|
||||
$this->parser->parse('[test_shortcode]foo[/test_shortcode]');
|
||||
|
||||
|
||||
$this->assertEquals(array(), $this->arguments);
|
||||
$this->assertEquals('foo', $this->contents);
|
||||
$this->assertEquals('test_shortcode', $this->tagName);
|
||||
}
|
||||
|
||||
|
||||
public function testEnclosingWithArguments() {
|
||||
$this->parser->parse('[test_shortcode,foo = "bar",bar=\'foo\',baz="buz"]foo[/test_shortcode]');
|
||||
|
||||
|
||||
$this->assertEquals(array('foo' => 'bar', 'bar' => 'foo', 'baz' => 'buz'), $this->arguments);
|
||||
$this->assertEquals('foo', $this->contents);
|
||||
$this->assertEquals('test_shortcode', $this->tagName);
|
||||
}
|
||||
|
||||
|
||||
public function testShortcodeEscaping() {
|
||||
$this->assertEquals(
|
||||
'[test_shortcode]',
|
||||
'[test_shortcode]',
|
||||
$this->parser->parse('[[test_shortcode]]')
|
||||
);
|
||||
|
||||
@ -123,12 +123,12 @@ class ShortcodeParserTest extends SapphireTest {
|
||||
'[test_shortcode /]',
|
||||
$this->parser->parse('[[test_shortcode /]]')
|
||||
);
|
||||
|
||||
|
||||
$this->assertEquals(
|
||||
'[test_shortcode]content[/test_shortcode]',
|
||||
$this->parser->parse('[[test_shortcode]content[/test_shortcode]]'
|
||||
));
|
||||
|
||||
|
||||
$this->assertEquals(
|
||||
'[test_shortcode]content',
|
||||
$this->parser->parse('[[test_shortcode]][test_shortcode]content[/test_shortcode]')
|
||||
@ -138,12 +138,12 @@ class ShortcodeParserTest extends SapphireTest {
|
||||
'[test_shortcode]content[/test_shortcode]content2',
|
||||
$this->parser->parse('[[test_shortcode]content[/test_shortcode]][test_shortcode]content2[/test_shortcode]'
|
||||
));
|
||||
|
||||
|
||||
$this->assertEquals(
|
||||
'[[Doesnt strip double [ character if not a shortcode',
|
||||
$this->parser->parse('[[Doesnt strip double [ character if not a [test_shortcode]shortcode[/test_shortcode]'
|
||||
));
|
||||
|
||||
|
||||
$this->assertEquals(
|
||||
'[[Doesnt shortcode get confused by double ]] characters',
|
||||
$this->parser->parse(
|
||||
@ -155,7 +155,7 @@ class ShortcodeParserTest extends SapphireTest {
|
||||
$this->assertEquals('', $this->parser->parse('[test_shortcode,foo=bar!,baz = buz123]'));
|
||||
$this->assertEquals(array('foo' => 'bar!', 'baz' => 'buz123'), $this->arguments);
|
||||
}
|
||||
|
||||
|
||||
public function testSpacesForDelimiter() {
|
||||
$this->assertEquals('', $this->parser->parse('[test_shortcode foo=bar! baz = buz123]'));
|
||||
$this->assertEquals(array('foo' => 'bar!', 'baz' => 'buz123'), $this->arguments);
|
||||
@ -167,22 +167,22 @@ class ShortcodeParserTest extends SapphireTest {
|
||||
$this->parser->parse('[test_shortcode,id="1"/]more[test_shortcode,id="2"]content[/test_shortcode]'),
|
||||
'Assert that self-closing tags are respected during parsing.'
|
||||
);
|
||||
|
||||
|
||||
$this->assertEquals(2, $this->arguments['id']);
|
||||
}
|
||||
|
||||
public function testConsecutiveTags() {
|
||||
$this->assertEquals('', $this->parser->parse('[test_shortcode][test_shortcode]'));
|
||||
}
|
||||
|
||||
|
||||
protected function assertEqualsIgnoringWhitespace($a, $b, $message = null) {
|
||||
$this->assertEquals(preg_replace('/\s+/', '', $a), preg_replace('/\s+/', '', $b), $message);
|
||||
}
|
||||
|
||||
|
||||
public function testtExtract() {
|
||||
// Left extracts to before the current block
|
||||
$this->assertEqualsIgnoringWhitespace(
|
||||
'Code<div>FooBar</div>',
|
||||
'Code<div>FooBar</div>',
|
||||
$this->parser->parse('<div>Foo[test_shortcode class=left]Code[/test_shortcode]Bar</div>')
|
||||
);
|
||||
|
||||
@ -194,16 +194,16 @@ class ShortcodeParserTest extends SapphireTest {
|
||||
|
||||
// Center splits the current block
|
||||
$this->assertEqualsIgnoringWhitespace(
|
||||
'<div>Foo</div>Code<div>Bar</div>',
|
||||
'<div>Foo</div>Code<div>Bar</div>',
|
||||
$this->parser->parse('<div>Foo[test_shortcode class=center]Code[/test_shortcode]Bar</div>')
|
||||
);
|
||||
|
||||
// Even if the immediate parent isn't a the current block
|
||||
$this->assertEqualsIgnoringWhitespace(
|
||||
'<div>Foo<b>Bar</b></div>Code<div><b>Baz</b>Qux</div>',
|
||||
'<div>Foo<b>Bar</b></div>Code<div><b>Baz</b>Qux</div>',
|
||||
$this->parser->parse('<div>Foo<b>Bar[test_shortcode class=center]Code[/test_shortcode]Baz</b>Qux</div>')
|
||||
);
|
||||
|
||||
|
||||
// No class means don't extract
|
||||
$this->assertEqualsIgnoringWhitespace(
|
||||
'<div>FooCodeBar</div>',
|
||||
@ -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() {
|
||||
$this->parser->parse('<a href="[test_shortcode]">Test</a>');
|
||||
|
||||
@ -220,7 +251,7 @@ class ShortcodeParserTest extends SapphireTest {
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
/**
|
||||
* Stores the result of a shortcode parse in object properties for easy testing access.
|
||||
*/
|
||||
@ -232,5 +263,5 @@ class ShortcodeParserTest extends SapphireTest {
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
111
tests/search/FulltextFilterTest.php
Executable file
111
tests/search/FulltextFilterTest.php
Executable 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",
|
||||
);
|
||||
|
||||
}
|
19
tests/search/FulltextFilterTest.yml
Normal file
19
tests/search/FulltextFilterTest.yml
Normal 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: ''
|
@ -14,17 +14,11 @@ class BasicAuthTest extends FunctionalTest {
|
||||
parent::setUp();
|
||||
|
||||
// Fixtures assume Email is the field used to identify the log in identity
|
||||
Config::nest();
|
||||
Member::config()->unique_identifier_field = 'Email';
|
||||
Security::$force_database_is_ready = true; // Prevents Member test subclasses breaking ready test
|
||||
Member::config()->lock_out_after_incorrect_logins = 10;
|
||||
}
|
||||
|
||||
public function tearDown() {
|
||||
Config::unnest();
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
public function testBasicAuthEnabledWithoutLogin() {
|
||||
$origUser = isset($_SERVER['PHP_AUTH_USER']) ? $_SERVER['PHP_AUTH_USER'] : null;
|
||||
$origPw = isset($_SERVER['PHP_AUTH_PW']) ? $_SERVER['PHP_AUTH_PW'] : null;
|
||||
|
@ -172,6 +172,13 @@ class MemberTest extends FunctionalTest {
|
||||
$this->assertInstanceOf('DataObject', $passwords->current());
|
||||
$this->assertTrue($passwords->current()->checkPassword('1nitialPassword'),
|
||||
"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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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
|
||||
|
@ -52,17 +52,45 @@ class SSViewerTest extends SapphireTest {
|
||||
|
||||
// reset results for the tests that include arguments (the title is passed as an arg)
|
||||
$expected = array(
|
||||
'Item 1 - Item 1 - First-ODD top:Item 1',
|
||||
'Item 2 - Item 2 - EVEN top:Item 2',
|
||||
'Item 3 - Item 3 - ODD top:Item 3',
|
||||
'Item 4 - Item 4 - EVEN top:Item 4',
|
||||
'Item 5 - Item 5 - ODD top:Item 5',
|
||||
'Item 6 - Item 6 - Last-EVEN top:Item 6',
|
||||
'Item 1 _ Item 1 - First-ODD top:Item 1',
|
||||
'Item 2 _ Item 2 - EVEN top:Item 2',
|
||||
'Item 3 _ Item 3 - ODD top:Item 3',
|
||||
'Item 4 _ Item 4 - EVEN top:Item 4',
|
||||
'Item 5 _ Item 5 - ODD top:Item 5',
|
||||
'Item 6 _ Item 6 - Last-EVEN top:Item 6',
|
||||
);
|
||||
|
||||
$result = $data->renderWith('SSViewerTestIncludeScopeInheritanceWithArgs');
|
||||
$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() {
|
||||
return new ArrayData(array(
|
||||
|
@ -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) {
|
||||
$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];
|
||||
else $params = array();
|
||||
|
||||
$hasInjected = $res = null;
|
||||
|
||||
if ($name == 'hasValue') {
|
||||
if ($val = $this->getInjectedValue($property, $params, false)) {
|
||||
$hasInjected = true; $res = (bool)$val['value'];
|
||||
}
|
||||
}
|
||||
else { // XML_val
|
||||
if ($val = $this->getInjectedValue($property, $params)) {
|
||||
$hasInjected = true;
|
||||
$obj = $val['obj'];
|
||||
$val = $this->getInjectedValue($property, $params);
|
||||
if ($val) {
|
||||
$obj = $val['obj'];
|
||||
if ($name === 'hasValue') {
|
||||
$res = $obj instanceof Object
|
||||
? $obj->exists()
|
||||
: (bool)$obj;
|
||||
} else {
|
||||
// XML_val
|
||||
$res = $obj->forTemplate();
|
||||
}
|
||||
}
|
||||
|
||||
if ($hasInjected) {
|
||||
$this->resetLocalScope();
|
||||
return $res;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
return parent::__call($name, $arguments);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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
|
||||
* 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
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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
|
||||
*/
|
||||
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');
|
||||
}
|
||||
|
||||
@ -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).
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated 3.2 Use the "SSViewer.theme" config setting instead
|
||||
* @deprecated 4.0 Use the "SSViewer.theme" config setting instead
|
||||
* @return string
|
||||
*/
|
||||
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');
|
||||
}
|
||||
|
||||
@ -736,10 +738,11 @@ class SSViewer implements Flushable {
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since version 4.0
|
||||
* @return string
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
@ -785,7 +788,9 @@ class SSViewer implements Flushable {
|
||||
* </code>
|
||||
*/
|
||||
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') {
|
||||
$this->chosenTemplates['main'] = $templateList;
|
||||
@ -829,6 +834,9 @@ class SSViewer implements Flushable {
|
||||
*/
|
||||
public function getParser()
|
||||
{
|
||||
if (!$this->parser) {
|
||||
$this->setParser(Injector::inst()->get('SSTemplateParser'));
|
||||
}
|
||||
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
|
||||
* 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 mixed $optionVal
|
||||
*/
|
||||
public static function setOption($optionName, $optionVal) {
|
||||
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);
|
||||
} 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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @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
|
||||
* @return mixed
|
||||
*/
|
||||
public static function getOption($optionName) {
|
||||
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');
|
||||
} 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);
|
||||
}
|
||||
}
|
||||
@ -1105,9 +1113,11 @@ class SSViewer implements Flushable {
|
||||
// through $Content and $Layout placeholders.
|
||||
foreach(array('Content', 'Layout') as $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->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);
|
||||
}
|
||||
@ -1173,7 +1183,7 @@ class SSViewer implements Flushable {
|
||||
}
|
||||
|
||||
public function parseTemplateContent($content, $template="") {
|
||||
return $this->parser->compileString(
|
||||
return $this->getParser()->compileString(
|
||||
$content,
|
||||
$template,
|
||||
Director::isDev() && Config::inst()->get('SSViewer', 'source_file_comments')
|
||||
@ -1242,7 +1252,10 @@ class SSViewer_FromString extends SSViewer {
|
||||
protected $cacheTemplate;
|
||||
|
||||
public function __construct($content, TemplateParser $parser = null) {
|
||||
$this->setParser($parser ?: Injector::inst()->get('SSTemplateParser'));
|
||||
if ($parser) {
|
||||
$this->setParser($parser);
|
||||
}
|
||||
|
||||
$this->content = $content;
|
||||
}
|
||||
|
||||
|
@ -177,7 +177,8 @@ class ViewableData extends Object implements IteratorAggregate {
|
||||
foreach($this->allMethodNames() as $method) {
|
||||
if($method[0] == '_' && $method[1] != '_') {
|
||||
$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();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* with references to both this and the new custom data.
|
||||
|
Loading…
x
Reference in New Issue
Block a user