Merge remote-tracking branch 'origin/3.2' into 3

Conflicts:
	composer.json
This commit is contained in:
Damian Mooyman 2015-06-22 14:16:02 +12:00
commit f90c1a8791
96 changed files with 2706 additions and 955 deletions

1
.gitattributes vendored Normal file
View File

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

View File

@ -76,7 +76,7 @@ class LeftAndMain extends Controller implements PermissionProvider {
* @config * @config
* @var string * @var string
*/ */
private static $help_link = 'http://userhelp.silverstripe.org/framework/en/3.1'; private static $help_link = 'http://userhelp.silverstripe.org/en/3.2/';
/** /**
* @var array * @var array
@ -1623,10 +1623,12 @@ class LeftAndMain extends Controller implements PermissionProvider {
/** /**
* Sets the href for the anchor on the Silverstripe logo in the menu * Sets the href for the anchor on the Silverstripe logo in the menu
* *
* @deprecated since version 4.0
*
* @param String $link * @param String $link
*/ */
public static function set_application_link($link) { public static function set_application_link($link) {
Deprecation::notice('3.2', 'Use the "LeftAndMain.application_link" config setting instead'); Deprecation::notice('4.0', 'Use the "LeftAndMain.application_link" config setting instead');
Config::inst()->update('LeftAndMain', 'application_link', $link); Config::inst()->update('LeftAndMain', 'application_link', $link);
} }
@ -1648,9 +1650,10 @@ class LeftAndMain extends Controller implements PermissionProvider {
/** /**
* @param String $name * @param String $name
* @deprecated since version 4.0
*/ */
public static function setApplicationName($name) { public static function setApplicationName($name) {
Deprecation::notice('3.2', 'Use the "LeftAndMain.application_name" config setting instead'); Deprecation::notice('4.0', 'Use the "LeftAndMain.application_name" config setting instead');
Config::inst()->update('LeftAndMain', 'application_name', $name); Config::inst()->update('LeftAndMain', 'application_name', $name);
} }
@ -1747,21 +1750,24 @@ class LeftAndMain extends Controller implements PermissionProvider {
/** /**
* Register the given javascript file as required in the CMS. * Register the given javascript file as required in the CMS.
* Filenames should be relative to the base, eg, FRAMEWORK_DIR . '/javascript/loader.js' * Filenames should be relative to the base, eg, FRAMEWORK_DIR . '/javascript/loader.js'
*
* @deprecated since version 4.0
*/ */
public static function require_javascript($file) { public static function require_javascript($file) {
Deprecation::notice('3.2', 'Use "LeftAndMain.extra_requirements_javascript" config setting instead'); Deprecation::notice('4.0', 'Use "LeftAndMain.extra_requirements_javascript" config setting instead');
Config::inst()->update('LeftAndMain', 'extra_requirements_javascript', array($file => array())); Config::inst()->update('LeftAndMain', 'extra_requirements_javascript', array($file => array()));
} }
/** /**
* Register the given stylesheet file as required. * Register the given stylesheet file as required.
* @deprecated since version 4.0
* *
* @param $file String Filenames should be relative to the base, eg, THIRDPARTY_DIR . '/tree/tree.css' * @param $file String Filenames should be relative to the base, eg, THIRDPARTY_DIR . '/tree/tree.css'
* @param $media String Comma-separated list of media-types (e.g. "screen,projector") * @param $media String Comma-separated list of media-types (e.g. "screen,projector")
* @see http://www.w3.org/TR/REC-CSS2/media.html * @see http://www.w3.org/TR/REC-CSS2/media.html
*/ */
public static function require_css($file, $media = null) { public static function require_css($file, $media = null) {
Deprecation::notice('3.2', 'Use "LeftAndMain.extra_requirements_css" config setting instead'); Deprecation::notice('4.0', 'Use "LeftAndMain.extra_requirements_css" config setting instead');
Config::inst()->update('LeftAndMain', 'extra_requirements_css', array($file => array('media' => $media))); Config::inst()->update('LeftAndMain', 'extra_requirements_css', array($file => array('media' => $media)));
} }
@ -1770,11 +1776,13 @@ class LeftAndMain extends Controller implements PermissionProvider {
* Themeable stylesheets have globally unique names, just like templates and PHP files. * Themeable stylesheets have globally unique names, just like templates and PHP files.
* Because of this, they can be replaced by similarly named CSS files in the theme directory. * Because of this, they can be replaced by similarly named CSS files in the theme directory.
* *
* @deprecated since version 4.0
*
* @param $name String The identifier of the file. For example, css/MyFile.css would have the identifier "MyFile" * @param $name String The identifier of the file. For example, css/MyFile.css would have the identifier "MyFile"
* @param $media String Comma-separated list of media-types (e.g. "screen,projector") * @param $media String Comma-separated list of media-types (e.g. "screen,projector")
*/ */
public static function require_themed_css($name, $media = null) { public static function require_themed_css($name, $media = null) {
Deprecation::notice('3.2', 'Use "LeftAndMain.extra_requirements_themedCss" config setting instead'); Deprecation::notice('4.0', 'Use "LeftAndMain.extra_requirements_themedCss" config setting instead');
Config::inst()->update('LeftAndMain', 'extra_requirements_themedCss', array($name => array('media' => $media))); Config::inst()->update('LeftAndMain', 'extra_requirements_themedCss', array($name => array('media' => $media)));
} }

View File

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

View File

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

View File

@ -557,11 +557,11 @@ body.cms { overflow: hidden; }
.cms-content-batchactions { float: left; position: relative; display: block; } .cms-content-batchactions { float: left; position: relative; display: block; }
.cms-content-batchactions .view-mode-batchactions-wrapper { height: 18px; float: left; padding: 4px 6px; border: 1px solid #aaa; margin-bottom: 8px; margin-right: -1px; background-color: #D9D9D9; background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2ZmZmZmZiIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iI2Q5ZDlkOSIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA=='); background-size: 100%; background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #ffffff), color-stop(100%, #d9d9d9)); background-image: -moz-linear-gradient(top, #ffffff, #d9d9d9); background-image: -webkit-linear-gradient(top, #ffffff, #d9d9d9); background-image: linear-gradient(to bottom, #ffffff, #d9d9d9); border-top-left-radius: 4px; border-bottom-left-radius: 4px; } .cms-content-batchactions .view-mode-batchactions-wrapper { height: 18px; float: left; padding: 4px 6px; border: 1px solid #aaa; margin-bottom: 8px; margin-right: -1px; background-color: #D9D9D9; background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2ZmZmZmZiIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iI2Q5ZDlkOSIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA=='); background-size: 100%; background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #ffffff), color-stop(100%, #d9d9d9)); background-image: -moz-linear-gradient(top, #ffffff, #d9d9d9); background-image: -webkit-linear-gradient(top, #ffffff, #d9d9d9); background-image: linear-gradient(to bottom, #ffffff, #d9d9d9); border-top-left-radius: 4px; border-bottom-left-radius: 4px; }
.cms-content-batchactions .view-mode-batchactions-wrapper input { vertical-align: middle; } .cms-content-batchactions .view-mode-batchactions-wrapper input { vertical-align: middle; }
.cms-content-batchactions .view-mode-batchactions-wrapper label { vertical-align: middle; display: none; } .cms-content-batchactions .view-mode-batchactions-wrapper .view-mode-batchactions-label { vertical-align: middle; display: none; }
.cms-content-batchactions .view-mode-batchactions-wrapper fieldset, .cms-content-batchactions .view-mode-batchactions-wrapper .Actions { display: inline-block; } .cms-content-batchactions .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 .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 { border-radius: 4px; }
.cms-content-batchactions.inactive .view-mode-batchactions-wrapper label { display: inline; } .cms-content-batchactions.inactive .view-mode-batchactions-wrapper .view-mode-batchactions-label { display: inline; }
.cms-content-batchactions form > * { display: block; float: left; } .cms-content-batchactions form > * { display: block; float: left; }
.cms-content-batchactions form.cms-batch-actions { float: left; } .cms-content-batchactions form.cms-batch-actions { float: left; }
.cms-content-batchactions.inactive form { display: none; } .cms-content-batchactions.inactive form { display: none; }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -907,7 +907,7 @@ body.cms {
vertical-align: middle; vertical-align: middle;
} }
label { .view-mode-batchactions-label {
vertical-align: middle; vertical-align: middle;
display: none; display: none;
} }
@ -923,7 +923,7 @@ body.cms {
&.inactive .view-mode-batchactions-wrapper { &.inactive .view-mode-batchactions-wrapper {
border-radius: 4px; border-radius: 4px;
label { .view-mode-batchactions-label {
display: inline; display: inline;
} }
} }

View File

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

View File

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

View File

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

View File

@ -103,7 +103,7 @@ class HTTP {
*/ */
public static function urlRewriter($content, $code) { public static function urlRewriter($content, $code) {
if(!is_callable($code)) { if(!is_callable($code)) {
Deprecation::notice(3.1, 'HTTP::urlRewriter expects a callable as the second parameter'); Deprecation::notice('4.0', 'HTTP::urlRewriter expects a callable as the second parameter');
} }
// Replace attributes // Replace attributes

View File

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

View File

@ -750,10 +750,10 @@ class Injector {
/** /**
* Register a service with an explicit name * Register a service with an explicit name
* *
* @deprecated since 3.1.1 * @deprecated since 4.0
*/ */
public function registerNamedService($name, $service) { public function registerNamedService($name, $service) {
Deprecation::notice('3.1.1', 'registerNamedService is deprecated, use registerService instead'); Deprecation::notice('4.0', 'registerNamedService is deprecated, use registerService instead');
return $this->registerService($service, $name); return $this->registerService($service, $name);
} }

View File

@ -681,7 +681,7 @@ class Config {
/** /**
* @package framework * @package framework
* @subpackage core * @subpackage core
* @deprecated 3.2 * @deprecated 4.0
*/ */
class Config_LRU { class Config_LRU {
const SIZE = 1000; const SIZE = 1000;
@ -693,7 +693,7 @@ class Config_LRU {
protected $c = 0; protected $c = 0;
public function __construct() { public function __construct() {
Deprecation::notice('3.2', 'Please use Config_MemCache instead', Deprecation::SCOPE_CLASS); Deprecation::notice('4.0', 'Please use Config_MemCache instead', Deprecation::SCOPE_CLASS);
if (version_compare(PHP_VERSION, '5.3.7', '<')) { if (version_compare(PHP_VERSION, '5.3.7', '<')) {
// SplFixedArray causes seg faults before PHP 5.3.7 // SplFixedArray causes seg faults before PHP 5.3.7
$this->cache = array(); $this->cache = array();

View File

@ -584,7 +584,7 @@ abstract class Object {
$sources[] = $extensionClass; $sources[] = $extensionClass;
if(!ClassInfo::has_method_from($extensionClass, 'add_to_class', 'Extension')) { if(!ClassInfo::has_method_from($extensionClass, 'add_to_class', 'Extension')) {
Deprecation::notice('3.2.0', Deprecation::notice('4.0',
"add_to_class deprecated on $extensionClass. Use get_extra_config instead"); "add_to_class deprecated on $extensionClass. Use get_extra_config instead");
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -145,7 +145,7 @@ changes
* Only submit a pull request for work you expect to be ready to merge. Work in progress is best discussed in an issue, or on your own repository fork. * Only submit a pull request for work you expect to be ready to merge. Work in progress is best discussed in an issue, or on your own repository fork.
* Document your code inline through [PHPDoc](http://en.wikipedia.org/wiki/PHPDoc) syntax. See our * Document your code inline through [PHPDoc](http://en.wikipedia.org/wiki/PHPDoc) syntax. See our
[API documentation](http://api.silverstripe.org/3.1/) for good examples. [API documentation](http://api.silverstripe.org/3.1/) for good examples.
* Check and update documentation on [doc.silverstripe.org](http://doc.silverstripe.org). Check for any references to functionality deprecated or extended through your patch. Documentation changes should be included in the patch. * Check and update documentation on [docs.silverstripe.org](http://docs.silverstripe.org). Check for any references to functionality deprecated or extended through your patch. Documentation changes should be included in the patch.
* If you get stuck, please post to the [forum](http://silverstripe.org/forum) or for deeper core problems, to the [core mailinglist](https://groups.google.com/forum/#!forum/silverstripe-dev) * If you get stuck, please post to the [forum](http://silverstripe.org/forum) or for deeper core problems, to the [core mailinglist](https://groups.google.com/forum/#!forum/silverstripe-dev)
* When working with the CMS, please read the ["CMS Architecture Guide"](cms_architecture) first * When working with the CMS, please read the ["CMS Architecture Guide"](cms_architecture) first

View File

@ -16,8 +16,7 @@ page you want to edit. Alternatively, locate the appropriate .md file in the
* After editing the documentation, describe your changes in the "commit summary" and "extended description" fields below then press "Commit Changes". * After editing the documentation, describe your changes in the "commit summary" and "extended description" fields below then press "Commit Changes".
* After that you will see a form to submit a Pull Request: "[pull requests](http://help.github.com/pull-requests/)". You should be able to adjust the version your * After 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 apply to and then submit the form. Your changes will be sent to the core committers for approval.
* documentation changes are for and then submit the form. Your changes will be sent to the core committers for approval.
<div class="warning" markdown='1'> <div class="warning" markdown='1'>
You should make your changes in the lowest branch they apply to. For instance, if you fix a spelling issue that you found in the 3.1 documentation, submit your fix to that branch in Github and it'll be copied to the master (3.2) version of the documentation automatically. *Don't submit multiple pull requests*. You should make your changes in the lowest branch they apply to. For instance, if you fix a spelling issue that you found in the 3.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*.
@ -57,7 +56,7 @@ for documenting open source software.
* Use PHPDoc in source code: Leave low level technical documentation to code comments within PHP, in [PHPDoc](http://en.wikipedia.org/wiki/PHPDoc) format. * Use PHPDoc in source code: Leave low level technical documentation to code comments within PHP, in [PHPDoc](http://en.wikipedia.org/wiki/PHPDoc) format.
* API and developer guides are two forms of source code documentation that complement each other. * API and developer guides are two forms of source code documentation that complement each other.
* API documentation should provide context, ie, the "bigger picture", by referring to developer guides inside your PHPDoc. * API documentation should provide context, ie, the "bigger picture", by referring to developer guides inside your PHPDoc.
* Make your documentation easy to find: Documentation lives by interlinking content so please make sure your contribution doesn't become an inaccessible island. At the very least, put a link to your should on the index page in the same folder. A link to your page can also appear * Make your documentation easy to find: Documentation is useful only when it is interlinked so please make sure your contribution doesn't become an inaccessible island. At the very least, put a link to your index page in the same folder. A link to your page can also appear
as "related content" on other resource (e.g. `/tutorials/site_search` might link to `/developer_guides/forms/introduction`). as "related content" on other resource (e.g. `/tutorials/site_search` might link to `/developer_guides/forms/introduction`).
## Writing style ## Writing style
@ -85,7 +84,7 @@ sparingly.
"Tip box": A tip box is great for adding, deepening or accenting information in the main text. They can be used for background knowledge, or to provide links to further information (ie, a "see also" link). "Tip box": A tip box is great for adding, deepening or accenting information in the main text. They can be used for background knowledge, or to provide links to further information (ie, a "see also" link).
</div> </div>
Code: Code for a Tip box:
<div class="hint" markdown='1'> <div class="hint" markdown='1'>
... ...
@ -95,7 +94,7 @@ Code:
"Notification box": A notification box is good for technical notifications relating to the main text. For example, notifying users about a deprecated feature. "Notification box": A notification box is good for technical notifications relating to the main text. For example, notifying users about a deprecated feature.
</div> </div>
Code: Code for a Notification box:
<div class="notice" markdown='1'> <div class="notice" markdown='1'>
... ...
@ -105,7 +104,7 @@ Code:
"Warning box": A warning box is useful for highlighting a severe bug or a technical issue requiring a user's attention. For example, suppose a rare edge case sometimes leads to a variable being overwritten incorrectly. A warning box can be used to alert the user to this case so they can write their own code to handle it. "Warning box": A warning box is useful for highlighting a severe bug or a technical issue requiring a user's attention. For example, suppose a rare edge case sometimes leads to a variable being overwritten incorrectly. A warning box can be used to alert the user to this case so they can write their own code to handle it.
</div> </div>
Code: Code for a Warning box:
<div class="warning" markdown='1'> <div class="warning" markdown='1'>
... ...

View File

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

View File

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

View File

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

View File

@ -633,7 +633,7 @@ class File extends DataObject {
// If it's changed, check for duplicates // If it's changed, check for duplicates
if($oldName && $oldName != $name) { if($oldName && $oldName != $name) {
$base = pathinfo($name, PATHINFO_BASENAME); $base = pathinfo($name, PATHINFO_FILENAME);
$ext = self::get_file_extension($name); $ext = self::get_file_extension($name);
$suffix = 1; $suffix = 1;
@ -645,7 +645,7 @@ class File extends DataObject {
))->first() ))->first()
) { ) {
$suffix++; $suffix++;
$name = "$base-$suffix$ext"; $name = "$base-$suffix.$ext";
} }
} }

View File

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

View File

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

View File

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

View File

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

View File

@ -63,7 +63,7 @@ class DatetimeField extends FormField {
->addExtraClass('fieldgroup-field'); ->addExtraClass('fieldgroup-field');
$this->timeField = TimeField::create($name . '[time]', false) $this->timeField = TimeField::create($name . '[time]', false)
->addExtraClass('fieldgroup-field'); ->addExtraClass('fieldgroup-field');
$this->timezoneField = new HiddenField($this->getName() . '[timezone]'); $this->timezoneField = new HiddenField($name . '[timezone]');
parent::__construct($name, $title, $value); parent::__construct($name, $title, $value);
} }

View File

@ -100,7 +100,7 @@ class FormAction extends FormField {
// Remove this method override in 4.0 // Remove this method override in 4.0
$decoded = Convert::xml2raw($title); $decoded = Convert::xml2raw($title);
if($decoded !== $title) { if($title && $decoded !== $title) {
Deprecation::notice( Deprecation::notice(
'4.0', '4.0',
'The FormAction title field should not be html encoded. Use buttonContent to set custom html instead' 'The FormAction title field should not be html encoded. Use buttonContent to set custom html instead'

View File

@ -908,10 +908,10 @@ class FormField extends RequestHandler {
} }
/** /**
* @deprecated 3.2 Use FormField::create_tag() * @deprecated 4.0 Use FormField::create_tag()
*/ */
public function createTag($tag, $attributes, $content = null) { public function createTag($tag, $attributes, $content = null) {
Deprecation::notice('3.2', 'Use FormField::create_tag()'); Deprecation::notice('4.0', 'Use FormField::create_tag()');
return self::create_tag($tag, $attributes, $content); return self::create_tag($tag, $attributes, $content);
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -137,54 +137,56 @@ class i18n extends Object implements TemplateGlobalProvider, Flushable {
* *
* @see Requirements::process_i18n_javascript() * @see Requirements::process_i18n_javascript()
* *
* @deprecated 3.2 Use the "i18n.js_i18n" config setting instead * @deprecated 4.0 Use the "i18n.js_i18n" config setting instead
* @param bool $bool * @param bool $bool
*/ */
public static function set_js_i18n($bool) { public static function set_js_i18n($bool) {
Deprecation::notice('3.2', 'Use the "i18n.js_i18n" config setting instead'); Deprecation::notice('4.0', 'Use the "i18n.js_i18n" config setting instead');
Config::inst()->update('i18n', 'js_i18n', $bool); Config::inst()->update('i18n', 'js_i18n', $bool);
} }
/** /**
* @deprecated 3.2 Use the "i18n.js_i18n" config setting instead * @deprecated 4.0 Use the "i18n.js_i18n" config setting instead
* @return bool * @return bool
*/ */
public static function get_js_i18n() { public static function get_js_i18n() {
Deprecation::notice('3.2', 'Use the "i18n.js_i18n" config setting instead'); Deprecation::notice('4.0', 'Use the "i18n.js_i18n" config setting instead');
return Config::inst()->get('i18n', 'js_i18n'); return Config::inst()->get('i18n', 'js_i18n');
} }
/** /**
* @deprecated 3.2 Use the "i18n.date_format" config setting instead * @deprecated 4.0 Use the "i18n.date_format" config setting instead
* @param string ISO date format * @param string ISO date format
*/ */
public static function set_date_format($format) { public static function set_date_format($format) {
Deprecation::notice('3.2', 'Use the "i18n.date_format" config setting instead'); Deprecation::notice('4.0', 'Use the "i18n.date_format" config setting instead');
Config::inst()->update('i18n', 'date_format', $format); Config::inst()->update('i18n', 'date_format', $format);
} }
/** /**
* @deprecated since version 4.0
* @return string ISO date format * @return string ISO date format
*/ */
public static function get_date_format() { public static function get_date_format() {
Deprecation::notice('3.2', 'Use the "i18n.date_format" config setting instead'); Deprecation::notice('4.0', 'Use the "i18n.date_format" config setting instead');
return Config::inst()->get('i18n', 'date_format'); return Config::inst()->get('i18n', 'date_format');
} }
/** /**
* @deprecated 3.2 Use the "i18n.time_format" config setting instead * @deprecated 4.0 Use the "i18n.time_format" config setting instead
* @param string ISO time format * @param string ISO time format
*/ */
public static function set_time_format($format) { public static function set_time_format($format) {
Deprecation::notice('3.2', 'Use the "i18n.time_format" config setting instead'); Deprecation::notice('4.0', 'Use the "i18n.time_format" config setting instead');
Config::inst()->update('i18n', 'time_format', $format); Config::inst()->update('i18n', 'time_format', $format);
} }
/** /**
* @deprecated since version 4.0
* @return string ISO time format * @return string ISO time format
*/ */
public static function get_time_format() { public static function get_time_format() {
Deprecation::notice('3.2', 'Use the "i18n.time_format" config setting instead'); Deprecation::notice('4.0', 'Use the "i18n.time_format" config setting instead');
return Config::inst()->get('i18n', 'time_format'); return Config::inst()->get('i18n', 'time_format');
} }
@ -2224,10 +2226,11 @@ class i18n extends Object implements TemplateGlobalProvider, Flushable {
/** /**
* Get a list of locales (code => language and country) * Get a list of locales (code => language and country)
* *
* @deprecated since version 4.0
* @return list of languages in the form 'code' => 'name' * @return list of languages in the form 'code' => 'name'
*/ */
public static function get_locale_list() { public static function get_locale_list() {
Deprecation::notice('3.2', 'Use the "i18n.all_locales" config setting instead'); Deprecation::notice('4.0', 'Use the "i18n.all_locales" config setting instead');
return (array)Config::inst()->get('i18n', 'all_locales'); return (array)Config::inst()->get('i18n', 'all_locales');
} }

View File

@ -60,8 +60,8 @@ en:
ERRORNOTREC: 'That username / password isn''t recognised' ERRORNOTREC: 'That username / password isn''t recognised'
Boolean: Boolean:
ANY: Any ANY: Any
NOANSWER: No NOANSWER: 'No'
YESANSWER: Yes YESANSWER: 'Yes'
CMSLoadingScreen_ss: CMSLoadingScreen_ss:
LOADING: Loading... LOADING: Loading...
REQUIREJS: 'The CMS requires that you have JavaScript enabled.' REQUIREJS: 'The CMS requires that you have JavaScript enabled.'
@ -81,8 +81,8 @@ en:
HELLO: Hi HELLO: Hi
PASSWORD: Password PASSWORD: Password
CheckboxField: CheckboxField:
NOANSWER: No NOANSWER: 'No'
YESANSWER: Yes YESANSWER: 'Yes'
CheckboxFieldSetField: CheckboxFieldSetField:
SOURCE_VALIDATION: 'Please select a value within the list provided. {value} is not a valid option' SOURCE_VALIDATION: 'Please select a value within the list provided. {value} is not a valid option'
CMSMemberLoginForm: CMSMemberLoginForm:
@ -313,6 +313,7 @@ en:
URL: URL URL: URL
URLNOTANOEMBEDRESOURCE: 'The URL ''{url}'' could not be turned into a media resource.' URLNOTANOEMBEDRESOURCE: 'The URL ''{url}'' could not be turned into a media resource.'
UpdateMEDIA: 'Update Media' UpdateMEDIA: 'Update Media'
SUBJECT: 'Email subject'
Image: Image:
PLURALNAME: Files PLURALNAME: Files
SINGULARNAME: File SINGULARNAME: File
@ -330,7 +331,7 @@ en:
PERMAGAIN: 'You have been logged out of the CMS. If you would like to log in again, enter a username and password below.' PERMAGAIN: 'You have been logged out of the CMS. If you would like to log in again, enter a username and password below.'
PERMALREADY: 'I''m sorry, but you can''t access that part of the CMS. If you want to log in as someone else, do so below' PERMALREADY: 'I''m sorry, but you can''t access that part of the CMS. If you want to log in as someone else, do so below'
PERMDEFAULT: 'Please choose an authentication method and enter your credentials to access the CMS.' PERMDEFAULT: 'Please choose an authentication method and enter your credentials to access the CMS.'
PLEASESAVE: 'Please Save Page: This page could not be upated because it hasn''t been saved yet.' PLEASESAVE: 'Please Save Page: This page could not be updated because it hasn''t been saved yet.'
PreviewButton: Preview PreviewButton: Preview
REORGANISATIONSUCCESSFUL: 'Reorganised the site tree successfully.' REORGANISATIONSUCCESSFUL: 'Reorganised the site tree successfully.'
SAVEDUP: Saved. SAVEDUP: Saved.
@ -342,7 +343,7 @@ en:
Hello: Hi Hello: Hi
LOGOUT: 'Log out' LOGOUT: 'Log out'
ListboxField: ListboxField:
SOURCE_VALIDATION: 'Please select a value within the list provided. {value} is not a valid option' SOURCE_VALIDATION: 'Please select a value within the list provided. %s is not a valid option'
LoginAttempt: LoginAttempt:
Email: 'Email Address' Email: 'Email Address'
IP: 'IP Address' IP: 'IP Address'
@ -585,3 +586,5 @@ en:
UPLOADSINTO: 'saves into /{path}' UPLOADSINTO: 'saves into /{path}'
Versioned: Versioned:
has_many_Versions: Versions has_many_Versions: Versions
CheckboxSetField:
SOURCE_VALIDATION: 'Please select a value within the list provided. ''{value}'' is not a valid option'

View File

@ -50,10 +50,10 @@ class DB {
} }
/** /**
* @deprecated since version 3.3 Use DB::set_conn instead * @deprecated since version 4.0 Use DB::set_conn instead
*/ */
public static function setConn(SS_Database $connection, $name = 'default') { public static function setConn(SS_Database $connection, $name = 'default') {
Deprecation::notice('3.3', 'Use DB::set_conn instead'); Deprecation::notice('4.0', 'Use DB::set_conn instead');
self::set_conn($connection, $name); self::set_conn($connection, $name);
} }
@ -71,10 +71,10 @@ class DB {
} }
/** /**
* @deprecated since version 3.3 Use DB::get_conn instead * @deprecated since version 4.0 Use DB::get_conn instead
*/ */
public static function getConn($name = 'default') { public static function getConn($name = 'default') {
Deprecation::notice('3.3', 'Use DB::get_conn instead'); Deprecation::notice('4.0', 'Use DB::get_conn instead');
return self::get_conn($name); return self::get_conn($name);
} }
@ -254,10 +254,10 @@ class DB {
} }
/** /**
* @deprecated since version 3.2 DB::getConnect was never implemented and is obsolete * @deprecated since version 4.0 DB::getConnect was never implemented and is obsolete
*/ */
public static function getConnect($parameters) { public static function getConnect($parameters) {
Deprecation::notice('3.2', 'DB::getConnect was never implemented and is obsolete'); Deprecation::notice('4.0', 'DB::getConnect was never implemented and is obsolete');
} }
/** /**
@ -363,10 +363,10 @@ class DB {
} }
/** /**
* @deprecated since version 3.3 Use DB::get_generated_id instead * @deprecated since version 4.0 Use DB::get_generated_id instead
*/ */
public static function getGeneratedID($table) { public static function getGeneratedID($table) {
Deprecation::notice('3.3', 'Use DB::get_generated_id instead'); Deprecation::notice('4.0', 'Use DB::get_generated_id instead');
return self::get_generated_id($table); return self::get_generated_id($table);
} }
@ -380,10 +380,10 @@ class DB {
} }
/** /**
* @deprecated since version 3.3 Use DB::is_active instead * @deprecated since version 4.0 Use DB::is_active instead
*/ */
public static function isActive() { public static function isActive() {
Deprecation::notice('3.3', 'Use DB::is_active instead'); Deprecation::notice('4.0', 'Use DB::is_active instead');
return self::is_active(); return self::is_active();
} }
@ -400,10 +400,10 @@ class DB {
} }
/** /**
* @deprecated since version 3.3 Use DB::create_database instead * @deprecated since version 4.0 Use DB::create_database instead
*/ */
public static function createDatabase($connect, $username, $password, $database) { public static function createDatabase($connect, $username, $password, $database) {
Deprecation::notice('3.3', 'Use DB::create_database instead'); Deprecation::notice('4.0', 'Use DB::create_database instead');
return self::create_database($database); return self::create_database($database);
} }
@ -426,10 +426,10 @@ class DB {
} }
/** /**
* @deprecated since version 3.3 Use DB::create_table instead * @deprecated since version 4.0 Use DB::create_table instead
*/ */
public static function createTable($table, $fields = null, $indexes = null, $options = null) { public static function createTable($table, $fields = null, $indexes = null, $options = null) {
Deprecation::notice('3.3', 'Use DB::create_table instead'); Deprecation::notice('4.0', 'Use DB::create_table instead');
return self::create_table($table, $fields, $indexes, $options); return self::create_table($table, $fields, $indexes, $options);
} }
@ -444,10 +444,10 @@ class DB {
} }
/** /**
* @deprecated since version 3.3 Use DB::create_field instead * @deprecated since version 4.0 Use DB::create_field instead
*/ */
public static function createField($table, $field, $spec) { public static function createField($table, $field, $spec) {
Deprecation::notice('3.3', 'Use DB::create_field instead'); Deprecation::notice('4.0', 'Use DB::create_field instead');
return self::create_field($table, $field, $spec); return self::create_field($table, $field, $spec);
} }
@ -474,12 +474,12 @@ class DB {
} }
/** /**
* @deprecated since version 3.3 Use DB::require_table instead * @deprecated since version 4.0 Use DB::require_table instead
*/ */
public static function requireTable($table, $fieldSchema = null, $indexSchema = null, $hasAutoIncPK = true, public static function requireTable($table, $fieldSchema = null, $indexSchema = null, $hasAutoIncPK = true,
$options = null, $extensions = null $options = null, $extensions = null
) { ) {
Deprecation::notice('3.3', 'Use DB::require_table instead'); Deprecation::notice('4.0', 'Use DB::require_table instead');
return self::require_table($table, $fieldSchema, $indexSchema, $hasAutoIncPK, $options, $extensions); return self::require_table($table, $fieldSchema, $indexSchema, $hasAutoIncPK, $options, $extensions);
} }
@ -495,10 +495,10 @@ class DB {
} }
/** /**
* @deprecated since version 3.3 Use DB::require_field instead * @deprecated since version 4.0 Use DB::require_field instead
*/ */
public static function requireField($table, $field, $spec) { public static function requireField($table, $field, $spec) {
Deprecation::notice('3.3', 'Use DB::require_field instead'); Deprecation::notice('4.0', 'Use DB::require_field instead');
return self::require_field($table, $field, $spec); return self::require_field($table, $field, $spec);
} }
@ -514,10 +514,10 @@ class DB {
} }
/** /**
* @deprecated since version 3.3 Use DB::require_index instead * @deprecated since version 4.0 Use DB::require_index instead
*/ */
public static function requireIndex($table, $index, $spec) { public static function requireIndex($table, $index, $spec) {
Deprecation::notice('3.3', 'Use DB::require_index instead'); Deprecation::notice('4.0', 'Use DB::require_index instead');
self::require_index($table, $index, $spec); self::require_index($table, $index, $spec);
} }
@ -531,10 +531,10 @@ class DB {
} }
/** /**
* @deprecated since version 3.3 Use DB::dont_require_table instead * @deprecated since version 4.0 Use DB::dont_require_table instead
*/ */
public static function dontRequireTable($table) { public static function dontRequireTable($table) {
Deprecation::notice('3.3', 'Use DB::dont_require_table instead'); Deprecation::notice('4.0', 'Use DB::dont_require_table instead');
self::dont_require_table($table); self::dont_require_table($table);
} }
@ -549,10 +549,10 @@ class DB {
} }
/** /**
* @deprecated since version 3.3 Use DB::dont_require_field instead * @deprecated since version 4.0 Use DB::dont_require_field instead
*/ */
public static function dontRequireField($table, $fieldName) { public static function dontRequireField($table, $fieldName) {
Deprecation::notice('3.3', 'Use DB::dont_require_field instead'); Deprecation::notice('4.0', 'Use DB::dont_require_field instead');
self::dont_require_field($table, $fieldName); self::dont_require_field($table, $fieldName);
} }
@ -567,10 +567,10 @@ class DB {
} }
/** /**
* @deprecated since version 3.3 Use DB::check_and_repair_table instead * @deprecated since version 4.0 Use DB::check_and_repair_table instead
*/ */
public static function checkAndRepairTable($table) { public static function checkAndRepairTable($table) {
Deprecation::notice('3.3', 'Use DB::check_and_repair_table instead'); Deprecation::notice('4.0', 'Use DB::check_and_repair_table instead');
self::check_and_repair_table($table); self::check_and_repair_table($table);
} }
@ -584,10 +584,10 @@ class DB {
} }
/** /**
* @deprecated since version 3.3 Use DB::affected_rows instead * @deprecated since version 4.0 Use DB::affected_rows instead
*/ */
public static function affectedRows() { public static function affectedRows() {
Deprecation::notice('3.3', 'Use DB::affected_rows instead'); Deprecation::notice('4.0', 'Use DB::affected_rows instead');
return self::affected_rows(); return self::affected_rows();
} }
@ -602,10 +602,10 @@ class DB {
} }
/** /**
* @deprecated since version 3.3 Use DB::table_list instead * @deprecated since version 4.0 Use DB::table_list instead
*/ */
public static function tableList() { public static function tableList() {
Deprecation::notice('3.3', 'Use DB::table_list instead'); Deprecation::notice('4.0', 'Use DB::table_list instead');
return self::table_list(); return self::table_list();
} }
@ -621,10 +621,10 @@ class DB {
} }
/** /**
* @deprecated since version 3.3 Use DB::field_list instead * @deprecated since version 4.0 Use DB::field_list instead
*/ */
public static function fieldList($table) { public static function fieldList($table) {
Deprecation::notice('3.3', 'Use DB::field_list instead'); Deprecation::notice('4.0', 'Use DB::field_list instead');
return self::field_list($table); return self::field_list($table);
} }

View File

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

View File

@ -168,10 +168,10 @@ class Image extends File implements Flushable {
* File names are filtered through {@link FileNameFilter}, see class documentation * File names are filtered through {@link FileNameFilter}, see class documentation
* on how to influence this behaviour. * on how to influence this behaviour.
* *
* @deprecated 3.2 * @deprecated 4.0
*/ */
public function loadUploadedImage($tmpFile) { public function loadUploadedImage($tmpFile) {
Deprecation::notice('3.2', 'Use the Upload::loadIntoFile()'); Deprecation::notice('4.0', 'Use the Upload::loadIntoFile()');
if(!is_array($tmpFile)) { if(!is_array($tmpFile)) {
user_error("Image::loadUploadedImage() Not passed an array. Most likely, the form hasn't got the right" user_error("Image::loadUploadedImage() Not passed an array. Most likely, the form hasn't got the right"

View File

@ -681,70 +681,70 @@ abstract class SS_Database {
abstract public function random(); abstract public function random();
/** /**
* @deprecated since version 3.3 Use DB::get_schema()->dbDataType($type) instead * @deprecated since version 4.0 Use DB::get_schema()->dbDataType($type) instead
*/ */
public function dbDataType($type){ public function dbDataType($type){
Deprecation::notice('3.3', 'Use DB::get_schema()->dbDataType($type) instead'); Deprecation::notice('4.0', 'Use DB::get_schema()->dbDataType($type) instead');
return $this->getSchemaManager()->dbDataType($type); return $this->getSchemaManager()->dbDataType($type);
} }
/** /**
* @deprecated since version 3.3 Use selectDatabase('dbname', true) instead * @deprecated since version 4.0 Use selectDatabase('dbname', true) instead
*/ */
public function createDatabase() { public function createDatabase() {
Deprecation::notice('3.3', 'Use selectDatabase(\'dbname\',true) instead'); Deprecation::notice('4.0', 'Use selectDatabase(\'dbname\',true) instead');
$database = $this->connector->getSelectedDatabase(); $database = $this->connector->getSelectedDatabase();
$this->selectDatabase($database, true); $this->selectDatabase($database, true);
return $this->isActive(); return $this->isActive();
} }
/** /**
* @deprecated since version 3.3 SS_Database::getConnect was never implemented and is obsolete * @deprecated since version 4.0 SS_Database::getConnect was never implemented and is obsolete
*/ */
public function getConnect($parameters) { public function getConnect($parameters) {
Deprecation::notice('3.3', 'SS_Database::getConnect was never implemented and is obsolete'); Deprecation::notice('4.0', 'SS_Database::getConnect was never implemented and is obsolete');
} }
/** /**
* @deprecated since version 3.3 Use Convert::raw2sql($string, true) instead * @deprecated since version 4.0 Use Convert::raw2sql($string, true) instead
*/ */
public function prepStringForDB($string) { public function prepStringForDB($string) {
Deprecation::notice('3.3', 'Use Convert::raw2sql($string, true) instead'); Deprecation::notice('4.0', 'Use Convert::raw2sql($string, true) instead');
return $this->quoteString($string); return $this->quoteString($string);
} }
/** /**
* @deprecated since version 3.3 Use dropSelectedDatabase instead * @deprecated since version 4.0 Use dropSelectedDatabase instead
*/ */
public function dropDatabase() { public function dropDatabase() {
Deprecation::notice('3.3', 'Use dropSelectedDatabase instead'); Deprecation::notice('4.0', 'Use dropSelectedDatabase instead');
$this->dropSelectedDatabase(); $this->dropSelectedDatabase();
} }
/** /**
* @deprecated since version 3.3 Use databaseList instead * @deprecated since version 4.0 Use databaseList instead
*/ */
public function allDatabaseNames() { public function allDatabaseNames() {
Deprecation::notice('3.3', 'Use databaseList instead'); Deprecation::notice('4.0', 'Use databaseList instead');
return $this->databaseList(); return $this->databaseList();
} }
/** /**
* @deprecated since version 3.3 Use DB::create_table instead * @deprecated since version 4.0 Use DB::create_table instead
*/ */
public function createTable($table, $fields = null, $indexes = null, $options = null, $advancedOptions = null) { public function createTable($table, $fields = null, $indexes = null, $options = null, $advancedOptions = null) {
Deprecation::notice('3.3', 'Use DB::create_table instead'); Deprecation::notice('4.0', 'Use DB::create_table instead');
return $this->getSchemaManager()->createTable($table, $fields, $indexes, $options, $advancedOptions); return $this->getSchemaManager()->createTable($table, $fields, $indexes, $options, $advancedOptions);
} }
/** /**
* @deprecated since version 3.3 Use DB::get_schema()->alterTable() instead * @deprecated since version 4.0 Use DB::get_schema()->alterTable() instead
*/ */
public function alterTable($table, $newFields = null, $newIndexes = null, public function alterTable($table, $newFields = null, $newIndexes = null,
$alteredFields = null, $alteredIndexes = null, $alteredOptions = null, $alteredFields = null, $alteredIndexes = null, $alteredOptions = null,
$advancedOptions = null $advancedOptions = null
) { ) {
Deprecation::notice('3.3', 'Use DB::get_schema()->alterTable() instead'); Deprecation::notice('4.0', 'Use DB::get_schema()->alterTable() instead');
return $this->getSchemaManager()->alterTable( return $this->getSchemaManager()->alterTable(
$table, $newFields, $newIndexes, $alteredFields, $table, $newFields, $newIndexes, $alteredFields,
$alteredIndexes, $alteredOptions, $advancedOptions $alteredIndexes, $alteredOptions, $advancedOptions
@ -752,74 +752,74 @@ abstract class SS_Database {
} }
/** /**
* @deprecated since version 3.3 Use DB::get_schema()->renameTable() instead * @deprecated since version 4.0 Use DB::get_schema()->renameTable() instead
*/ */
public function renameTable($oldTableName, $newTableName) { public function renameTable($oldTableName, $newTableName) {
Deprecation::notice('3.3', 'Use DB::get_schema()->renameTable() instead'); Deprecation::notice('4.0', 'Use DB::get_schema()->renameTable() instead');
$this->getSchemaManager()->renameTable($oldTableName, $newTableName); $this->getSchemaManager()->renameTable($oldTableName, $newTableName);
} }
/** /**
* @deprecated since version 3.3 Use DB::create_field() instead * @deprecated since version 4.0 Use DB::create_field() instead
*/ */
public function createField($table, $field, $spec) { public function createField($table, $field, $spec) {
Deprecation::notice('3.3', 'Use DB::create_field() instead'); Deprecation::notice('4.0', 'Use DB::create_field() instead');
$this->getSchemaManager()->createField($table, $field, $spec); $this->getSchemaManager()->createField($table, $field, $spec);
} }
/** /**
* @deprecated since version 3.3 Use DB::get_schema()->renameField() instead * @deprecated since version 4.0 Use DB::get_schema()->renameField() instead
*/ */
public function renameField($tableName, $oldName, $newName) { public function renameField($tableName, $oldName, $newName) {
Deprecation::notice('3.3', 'Use DB::get_schema()->renameField() instead'); Deprecation::notice('4.0', 'Use DB::get_schema()->renameField() instead');
$this->getSchemaManager()->renameField($tableName, $oldName, $newName); $this->getSchemaManager()->renameField($tableName, $oldName, $newName);
} }
/** /**
* @deprecated since version 3.3 Use getSelectedDatabase instead * @deprecated since version 4.0 Use getSelectedDatabase instead
*/ */
public function currentDatabase() { public function currentDatabase() {
Deprecation::notice('3.3', 'Use getSelectedDatabase instead'); Deprecation::notice('4.0', 'Use getSelectedDatabase instead');
return $this->getSelectedDatabase(); return $this->getSelectedDatabase();
} }
/** /**
* @deprecated since version 3.3 Use DB::field_list instead * @deprecated since version 4.0 Use DB::field_list instead
*/ */
public function fieldList($table) { public function fieldList($table) {
Deprecation::notice('3.3', 'Use DB::field_list instead'); Deprecation::notice('4.0', 'Use DB::field_list instead');
return $this->getSchemaManager()->fieldList($table); return $this->getSchemaManager()->fieldList($table);
} }
/** /**
* @deprecated since version 3.3 Use DB::table_list instead * @deprecated since version 4.0 Use DB::table_list instead
*/ */
public function tableList() { public function tableList() {
Deprecation::notice('3.3', 'Use DB::table_list instead'); Deprecation::notice('4.0', 'Use DB::table_list instead');
return $this->getSchemaManager()->tableList(); return $this->getSchemaManager()->tableList();
} }
/** /**
* @deprecated since version 3.3 Use DB::get_schema()->hasTable() instead * @deprecated since version 4.0 Use DB::get_schema()->hasTable() instead
*/ */
public function hasTable($tableName) { public function hasTable($tableName) {
Deprecation::notice('3.3', 'Use DB::get_schema()->hasTable() instead'); Deprecation::notice('4.0', 'Use DB::get_schema()->hasTable() instead');
return $this->getSchemaManager()->hasTable($tableName); return $this->getSchemaManager()->hasTable($tableName);
} }
/** /**
* @deprecated since version 3.3 Use DB::get_schema()->enumValuesForField() instead * @deprecated since version 4.0 Use DB::get_schema()->enumValuesForField() instead
*/ */
public function enumValuesForField($tableName, $fieldName) { public function enumValuesForField($tableName, $fieldName) {
Deprecation::notice('3.3', 'Use DB::get_schema()->enumValuesForField() instead'); Deprecation::notice('4.0', 'Use DB::get_schema()->enumValuesForField() instead');
return $this->getSchemaManager()->enumValuesForField($tableName, $fieldName); return $this->getSchemaManager()->enumValuesForField($tableName, $fieldName);
} }
/** /**
* @deprecated since version 3.3 Use Convert::raw2sql instead * @deprecated since version 4.0 Use Convert::raw2sql instead
*/ */
public function addslashes($value) { public function addslashes($value) {
Deprecation::notice('3.3', 'Use Convert::raw2sql instead'); Deprecation::notice('4.0', 'Use Convert::raw2sql instead');
return $this->escapeString($value); return $this->escapeString($value);
} }
@ -842,134 +842,134 @@ abstract class SS_Database {
} }
/** /**
* @deprecated since version 3.3 Use DB::get_schema()->cancelSchemaUpdate instead * @deprecated since version 4.0 Use DB::get_schema()->cancelSchemaUpdate instead
*/ */
public function cancelSchemaUpdate() { public function cancelSchemaUpdate() {
Deprecation::notice('3.3', 'Use DB::get_schema()->cancelSchemaUpdate instead'); Deprecation::notice('4.0', 'Use DB::get_schema()->cancelSchemaUpdate instead');
$this->getSchemaManager()->cancelSchemaUpdate(); $this->getSchemaManager()->cancelSchemaUpdate();
} }
/** /**
* @deprecated since version 3.3 Use DB::get_schema()->isSchemaUpdating() instead * @deprecated since version 4.0 Use DB::get_schema()->isSchemaUpdating() instead
*/ */
public function isSchemaUpdating() { public function isSchemaUpdating() {
Deprecation::notice('3.3', 'Use DB::get_schema()->isSchemaUpdating() instead'); Deprecation::notice('4.0', 'Use DB::get_schema()->isSchemaUpdating() instead');
return $this->getSchemaManager()->isSchemaUpdating(); return $this->getSchemaManager()->isSchemaUpdating();
} }
/** /**
* @deprecated since version 3.3 Use DB::get_schema()->doesSchemaNeedUpdating() instead * @deprecated since version 4.0 Use DB::get_schema()->doesSchemaNeedUpdating() instead
*/ */
public function doesSchemaNeedUpdating() { public function doesSchemaNeedUpdating() {
Deprecation::notice('3.3', 'Use DB::get_schema()->doesSchemaNeedUpdating() instead'); Deprecation::notice('4.0', 'Use DB::get_schema()->doesSchemaNeedUpdating() instead');
return $this->getSchemaManager()->doesSchemaNeedUpdating(); return $this->getSchemaManager()->doesSchemaNeedUpdating();
} }
/** /**
* @deprecated since version 3.3 Use DB::get_schema()->transCreateTable() instead * @deprecated since version 4.0 Use DB::get_schema()->transCreateTable() instead
*/ */
public function transCreateTable($table, $options = null, $advanced_options = null) { public function transCreateTable($table, $options = null, $advanced_options = null) {
Deprecation::notice('3.3', 'Use DB::get_schema()->transCreateTable() instead'); Deprecation::notice('4.0', 'Use DB::get_schema()->transCreateTable() instead');
$this->getSchemaManager()->transCreateTable($table, $options, $advanced_options); $this->getSchemaManager()->transCreateTable($table, $options, $advanced_options);
} }
/** /**
* @deprecated since version 3.3 Use DB::get_schema()->transAlterTable() instead * @deprecated since version 4.0 Use DB::get_schema()->transAlterTable() instead
*/ */
public function transAlterTable($table, $options, $advanced_options) { public function transAlterTable($table, $options, $advanced_options) {
Deprecation::notice('3.3', 'Use DB::get_schema()->transAlterTable() instead'); Deprecation::notice('4.0', 'Use DB::get_schema()->transAlterTable() instead');
$this->getSchemaManager()->transAlterTable($table, $index, $schema); $this->getSchemaManager()->transAlterTable($table, $index, $schema);
} }
/** /**
* @deprecated since version 3.3 Use DB::get_schema()->transCreateField() instead * @deprecated since version 4.0 Use DB::get_schema()->transCreateField() instead
*/ */
public function transCreateField($table, $field, $schema) { public function transCreateField($table, $field, $schema) {
Deprecation::notice('3.3', 'Use DB::get_schema()->transCreateField() instead'); Deprecation::notice('4.0', 'Use DB::get_schema()->transCreateField() instead');
$this->getSchemaManager()->transCreateField($table, $index, $schema); $this->getSchemaManager()->transCreateField($table, $index, $schema);
} }
/** /**
* @deprecated since version 3.3 Use DB::get_schema()->transCreateIndex() instead * @deprecated since version 4.0 Use DB::get_schema()->transCreateIndex() instead
*/ */
public function transCreateIndex($table, $index, $schema) { public function transCreateIndex($table, $index, $schema) {
Deprecation::notice('3.3', 'Use DB::get_schema()->transCreateIndex() instead'); Deprecation::notice('4.0', 'Use DB::get_schema()->transCreateIndex() instead');
$this->getSchemaManager()->transCreateIndex($table, $index, $schema); $this->getSchemaManager()->transCreateIndex($table, $index, $schema);
} }
/** /**
* @deprecated since version 3.3 Use DB::get_schema()->transAlterField() instead * @deprecated since version 4.0 Use DB::get_schema()->transAlterField() instead
*/ */
public function transAlterField($table, $field, $schema) { public function transAlterField($table, $field, $schema) {
Deprecation::notice('3.3', 'Use DB::get_schema()->transAlterField() instead'); Deprecation::notice('4.0', 'Use DB::get_schema()->transAlterField() instead');
$this->getSchemaManager()->transAlterField($table, $index, $schema); $this->getSchemaManager()->transAlterField($table, $index, $schema);
} }
/** /**
* @deprecated since version 3.3 Use DB::get_schema()->transAlterIndex() instead * @deprecated since version 4.0 Use DB::get_schema()->transAlterIndex() instead
*/ */
public function transAlterIndex($table, $index, $schema) { public function transAlterIndex($table, $index, $schema) {
Deprecation::notice('3.3', 'Use DB::get_schema()->transAlterIndex() instead'); Deprecation::notice('4.0', 'Use DB::get_schema()->transAlterIndex() instead');
$this->getSchemaManager()->transAlterIndex($table, $index, $schema); $this->getSchemaManager()->transAlterIndex($table, $index, $schema);
} }
/** /**
* @deprecated since version 3.3 Use DB::require_table() instead * @deprecated since version 4.0 Use DB::require_table() instead
*/ */
public function requireTable($table, $fieldSchema = null, $indexSchema = null, public function requireTable($table, $fieldSchema = null, $indexSchema = null,
$hasAutoIncPK = true, $options = array(), $extensions = false $hasAutoIncPK = true, $options = array(), $extensions = false
) { ) {
Deprecation::notice('3.3', 'Use DB::require_table() instead'); Deprecation::notice('4.0', 'Use DB::require_table() instead');
return $this->getSchemaManager()->requireTable( return $this->getSchemaManager()->requireTable(
$table, $fieldSchema, $indexSchema, $hasAutoIncPK, $options, $extensions $table, $fieldSchema, $indexSchema, $hasAutoIncPK, $options, $extensions
); );
} }
/** /**
* @deprecated since version 3.3 Use DB::dont_require_table() instead * @deprecated since version 4.0 Use DB::dont_require_table() instead
*/ */
public function dontRequireTable($table) { public function dontRequireTable($table) {
Deprecation::notice('3.3', 'Use DB::dont_require_table() instead'); Deprecation::notice('4.0', 'Use DB::dont_require_table() instead');
$this->getSchemaManager()->dontRequireTable($table); $this->getSchemaManager()->dontRequireTable($table);
} }
/** /**
* @deprecated since version 3.3 Use DB::require_index() instead * @deprecated since version 4.0 Use DB::require_index() instead
*/ */
public function requireIndex($table, $index, $spec) { public function requireIndex($table, $index, $spec) {
Deprecation::notice('3.3', 'Use DB::require_index() instead'); Deprecation::notice('4.0', 'Use DB::require_index() instead');
$this->getSchemaManager()->requireIndex($table, $index, $spec); $this->getSchemaManager()->requireIndex($table, $index, $spec);
} }
/** /**
* @deprecated since version 3.3 Use DB::get_schema()->hasField() instead * @deprecated since version 4.0 Use DB::get_schema()->hasField() instead
*/ */
public function hasField($tableName, $fieldName) { public function hasField($tableName, $fieldName) {
Deprecation::notice('3.3', 'Use DB::get_schema()->hasField() instead'); Deprecation::notice('4.0', 'Use DB::get_schema()->hasField() instead');
return $this->getSchemaManager()->hasField($tableName, $fieldName); return $this->getSchemaManager()->hasField($tableName, $fieldName);
} }
/** /**
* @deprecated since version 3.3 Use DB::require_field() instead * @deprecated since version 4.0 Use DB::require_field() instead
*/ */
public function requireField($table, $field, $spec) { public function requireField($table, $field, $spec) {
Deprecation::notice('3.3', 'Use DB::require_field() instead'); Deprecation::notice('4.0', 'Use DB::require_field() instead');
$this->getSchemaManager()->requireField($table, $field, $spec); $this->getSchemaManager()->requireField($table, $field, $spec);
} }
/** /**
* @deprecated since version 3.3 Use DB::dont_require_field() instead * @deprecated since version 4.0 Use DB::dont_require_field() instead
*/ */
public function dontRequireField($table, $fieldName) { public function dontRequireField($table, $fieldName) {
Deprecation::notice('3.3', 'Use DB::dont_require_field() instead'); Deprecation::notice('4.0', 'Use DB::dont_require_field() instead');
$this->getSchemaManager()->dontRequireField($table, $fieldName); $this->getSchemaManager()->dontRequireField($table, $fieldName);
} }
/** /**
* @deprecated since version 3.3 Use DB::build_sql() instead * @deprecated since version 4.0 Use DB::build_sql() instead
*/ */
public function sqlQueryToString(SQLExpression $query, &$parameters = array()) { public function sqlQueryToString(SQLExpression $query, &$parameters = array()) {
Deprecation::notice('3.3', 'Use DB::build_sql() instead'); Deprecation::notice('4.0', 'Use DB::build_sql() instead');
return $this->getQueryBuilder()->buildSQL($query, $parameters); return $this->getQueryBuilder()->buildSQL($query, $parameters);
} }
} }

View File

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

View File

@ -246,7 +246,7 @@ abstract class SQLConditionalExpression extends SQLExpression {
public function getJoins(&$parameters = array()) { public function getJoins(&$parameters = array()) {
if(func_num_args() == 0) { if(func_num_args() == 0) {
Deprecation::notice( Deprecation::notice(
'3.2', '4.0',
'SQLConditionalExpression::getJoins() now may produce parameters which are necessary to 'SQLConditionalExpression::getJoins() now may produce parameters which are necessary to
execute this query' execute this query'
); );

View File

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

View File

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

View File

@ -202,10 +202,10 @@ class Member extends DataObject implements TemplateGlobalProvider {
private static $temp_id_lifetime = 259200; private static $temp_id_lifetime = 259200;
/** /**
* @deprecated 3.2 Use the "Member.session_regenerate_id" config setting instead * @deprecated 4.0 Use the "Member.session_regenerate_id" config setting instead
*/ */
public static function set_session_regenerate_id($bool) { public static function set_session_regenerate_id($bool) {
Deprecation::notice('3.2', 'Use the "Member.session_regenerate_id" config setting instead'); Deprecation::notice('4.0', 'Use the "Member.session_regenerate_id" config setting instead');
self::config()->session_regenerate_id = $bool; self::config()->session_regenerate_id = $bool;
} }
@ -275,11 +275,11 @@ class Member extends DataObject implements TemplateGlobalProvider {
* RewriteCond %{HTTP_COOKIE} !SS_LOGGED_IN=1 * RewriteCond %{HTTP_COOKIE} !SS_LOGGED_IN=1
* </pre> * </pre>
* *
* @deprecated 3.2 Use the "Member.login_marker_cookie" config setting instead * @deprecated 4.0 Use the "Member.login_marker_cookie" config setting instead
* @param $cookieName string The name of the cookie to set. * @param $cookieName string The name of the cookie to set.
*/ */
public static function set_login_marker_cookie($cookieName) { public static function set_login_marker_cookie($cookieName) {
Deprecation::notice('3.2', 'Use the "Member.login_marker_cookie" config setting instead'); Deprecation::notice('4.0', 'Use the "Member.login_marker_cookie" config setting instead');
self::config()->login_marker_cookie = $cookieName; self::config()->login_marker_cookie = $cookieName;
} }
@ -369,11 +369,11 @@ class Member extends DataObject implements TemplateGlobalProvider {
* Get the field used for uniquely identifying a member * Get the field used for uniquely identifying a member
* in the database. {@see Member::$unique_identifier_field} * in the database. {@see Member::$unique_identifier_field}
* *
* @deprecated 3.2 Use the "Member.unique_identifier_field" config setting instead * @deprecated 4.0 Use the "Member.unique_identifier_field" config setting instead
* @return string * @return string
*/ */
public static function get_unique_identifier_field() { public static function get_unique_identifier_field() {
Deprecation::notice('3.2', 'Use the "Member.unique_identifier_field" config setting instead'); Deprecation::notice('4.0', 'Use the "Member.unique_identifier_field" config setting instead');
return Member::config()->unique_identifier_field; return Member::config()->unique_identifier_field;
} }
@ -381,11 +381,11 @@ class Member extends DataObject implements TemplateGlobalProvider {
* Set the field used for uniquely identifying a member * Set the field used for uniquely identifying a member
* in the database. {@see Member::$unique_identifier_field} * in the database. {@see Member::$unique_identifier_field}
* *
* @deprecated 3.2 Use the "Member.unique_identifier_field" config setting instead * @deprecated 4.0 Use the "Member.unique_identifier_field" config setting instead
* @param $field The field name to set as the unique field * @param $field The field name to set as the unique field
*/ */
public static function set_unique_identifier_field($field) { public static function set_unique_identifier_field($field) {
Deprecation::notice('3.2', 'Use the "Member.unique_identifier_field" config setting instead'); Deprecation::notice('4.0', 'Use the "Member.unique_identifier_field" config setting instead');
Member::config()->unique_identifier_field = $field; Member::config()->unique_identifier_field = $field;
} }
@ -407,20 +407,20 @@ class Member extends DataObject implements TemplateGlobalProvider {
* Set the number of days that a password should be valid for. * Set the number of days that a password should be valid for.
* Set to null (the default) to have passwords never expire. * Set to null (the default) to have passwords never expire.
* *
* @deprecated 3.2 Use the "Member.password_expiry_days" config setting instead * @deprecated 4.0 Use the "Member.password_expiry_days" config setting instead
*/ */
public static function set_password_expiry($days) { public static function set_password_expiry($days) {
Deprecation::notice('3.2', 'Use the "Member.password_expiry_days" config setting instead'); Deprecation::notice('4.0', 'Use the "Member.password_expiry_days" config setting instead');
self::config()->password_expiry_days = $days; self::config()->password_expiry_days = $days;
} }
/** /**
* Configure the security system to lock users out after this many incorrect logins * Configure the security system to lock users out after this many incorrect logins
* *
* @deprecated 3.2 Use the "Member.lock_out_after_incorrect_logins" config setting instead * @deprecated 4.0 Use the "Member.lock_out_after_incorrect_logins" config setting instead
*/ */
public static function lock_out_after_incorrect_logins($numLogins) { public static function lock_out_after_incorrect_logins($numLogins) {
Deprecation::notice('3.2', 'Use the "Member.lock_out_after_incorrect_logins" config setting instead'); Deprecation::notice('4.0', 'Use the "Member.lock_out_after_incorrect_logins" config setting instead');
self::config()->lock_out_after_incorrect_logins = $numLogins; self::config()->lock_out_after_incorrect_logins = $numLogins;
} }

View File

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

View File

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

View File

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

View File

@ -189,8 +189,7 @@ class ConfigTest extends SapphireTest {
// But it won't affect subclasses - this is *uninherited* static // But it won't affect subclasses - this is *uninherited* static
$this->assertNotContains('test_2b', $this->assertNotContains('test_2b',
Config::inst()->get('ConfigStaticTest_Third', 'first', Config::UNINHERITED)); Config::inst()->get('ConfigStaticTest_Third', 'first', Config::UNINHERITED));
$this->assertNotContains('test_2b', $this->assertNull(Config::inst()->get('ConfigStaticTest_Fourth', 'first', Config::UNINHERITED));
Config::inst()->get('ConfigStaticTest_Fourth', 'first', Config::UNINHERITED));
// Subclasses that don't have the static explicitly defined should allow definition, also // Subclasses that don't have the static explicitly defined should allow definition, also
// This also checks that set can be called after the first uninherited get() // This also checks that set can be called after the first uninherited get()

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -597,20 +597,20 @@ class SSViewer implements Flushable {
* Set whether HTML comments indicating the source .SS file used to render this page should be * Set whether HTML comments indicating the source .SS file used to render this page should be
* included in the output. This is enabled by default * included in the output. This is enabled by default
* *
* @deprecated 3.2 Use the "SSViewer.source_file_comments" config setting instead * @deprecated 4.0 Use the "SSViewer.source_file_comments" config setting instead
* @param boolean $val * @param boolean $val
*/ */
public static function set_source_file_comments($val) { public static function set_source_file_comments($val) {
Deprecation::notice('3.2', 'Use the "SSViewer.source_file_comments" config setting instead'); Deprecation::notice('4.0', 'Use the "SSViewer.source_file_comments" config setting instead');
Config::inst()->update('SSViewer', 'source_file_comments', $val); Config::inst()->update('SSViewer', 'source_file_comments', $val);
} }
/** /**
* @deprecated 3.2 Use the "SSViewer.source_file_comments" config setting instead * @deprecated 4.0 Use the "SSViewer.source_file_comments" config setting instead
* @return boolean * @return boolean
*/ */
public static function get_source_file_comments() { public static function get_source_file_comments() {
Deprecation::notice('3.2', 'Use the "SSViewer.source_file_comments" config setting instead'); Deprecation::notice('4.0', 'Use the "SSViewer.source_file_comments" config setting instead');
return Config::inst()->get('SSViewer', 'source_file_comments'); return Config::inst()->get('SSViewer', 'source_file_comments');
} }
@ -684,20 +684,20 @@ class SSViewer implements Flushable {
} }
/** /**
* @deprecated 3.2 Use the "SSViewer.theme" config setting instead * @deprecated 4.0 Use the "SSViewer.theme" config setting instead
* @param string $theme The "base theme" name (without underscores). * @param string $theme The "base theme" name (without underscores).
*/ */
public static function set_theme($theme) { public static function set_theme($theme) {
Deprecation::notice('3.2', 'Use the "SSViewer.theme" config setting instead'); Deprecation::notice('4.0', 'Use the "SSViewer.theme" config setting instead');
Config::inst()->update('SSViewer', 'theme', $theme); Config::inst()->update('SSViewer', 'theme', $theme);
} }
/** /**
* @deprecated 3.2 Use the "SSViewer.theme" config setting instead * @deprecated 4.0 Use the "SSViewer.theme" config setting instead
* @return string * @return string
*/ */
public static function current_theme() { public static function current_theme() {
Deprecation::notice('3.2', 'Use the "SSViewer.theme" config setting instead'); Deprecation::notice('4.0', 'Use the "SSViewer.theme" config setting instead');
return Config::inst()->get('SSViewer', 'theme'); return Config::inst()->get('SSViewer', 'theme');
} }
@ -736,10 +736,11 @@ class SSViewer implements Flushable {
} }
/** /**
* @deprecated since version 4.0
* @return string * @return string
*/ */
public static function current_custom_theme(){ public static function current_custom_theme(){
Deprecation::notice('3.2', 'Use the "SSViewer.theme" and "SSViewer.theme_enabled" config settings instead'); Deprecation::notice('4.0', 'Use the "SSViewer.theme" and "SSViewer.theme_enabled" config settings instead');
return Config::inst()->get('SSViewer', 'theme_enabled') ? Config::inst()->get('SSViewer', 'theme') : null; return Config::inst()->get('SSViewer', 'theme_enabled') ? Config::inst()->get('SSViewer', 'theme') : null;
} }
@ -865,31 +866,31 @@ class SSViewer implements Flushable {
* links: "<?php echo $_SERVER['REQUEST_URI']; ?>". This is useful if you're generating a * links: "<?php echo $_SERVER['REQUEST_URI']; ?>". This is useful if you're generating a
* page that will be saved to a .php file and may be accessed from different URLs. * page that will be saved to a .php file and may be accessed from different URLs.
* *
* @deprecated 3.2 Use the "SSViewer.rewrite_hash_links" config setting instead * @deprecated 4.0 Use the "SSViewer.rewrite_hash_links" config setting instead
* @param string $optionName * @param string $optionName
* @param mixed $optionVal * @param mixed $optionVal
*/ */
public static function setOption($optionName, $optionVal) { public static function setOption($optionName, $optionVal) {
if($optionName == 'rewriteHashlinks') { if($optionName == 'rewriteHashlinks') {
Deprecation::notice('3.2', 'Use the "SSViewer.rewrite_hash_links" config setting instead'); Deprecation::notice('4.0', 'Use the "SSViewer.rewrite_hash_links" config setting instead');
Config::inst()->update('SSViewer', 'rewrite_hash_links', $optionVal); Config::inst()->update('SSViewer', 'rewrite_hash_links', $optionVal);
} else { } else {
Deprecation::notice('3.2', 'Use the "SSViewer.' . $optionName . '" config setting instead'); Deprecation::notice('4.0', 'Use the "SSViewer.' . $optionName . '" config setting instead');
Config::inst()->update('SSViewer', $optionName, $optionVal); Config::inst()->update('SSViewer', $optionName, $optionVal);
} }
} }
/** /**
* @deprecated 3.2 Use the "SSViewer.rewrite_hash_links" config setting instead * @deprecated 4.0 Use the "SSViewer.rewrite_hash_links" config setting instead
* @param string * @param string
* @return mixed * @return mixed
*/ */
public static function getOption($optionName) { public static function getOption($optionName) {
if($optionName == 'rewriteHashlinks') { if($optionName == 'rewriteHashlinks') {
Deprecation::notice('3.2', 'Use the "SSViewer.rewrite_hash_links" config setting instead'); Deprecation::notice('4.0', 'Use the "SSViewer.rewrite_hash_links" config setting instead');
return Config::inst()->get('SSViewer', 'rewrite_hash_links'); return Config::inst()->get('SSViewer', 'rewrite_hash_links');
} else { } else {
Deprecation::notice('3.2', 'Use the "SSViewer.' . $optionName . '" config setting instead'); Deprecation::notice('4.0', 'Use the "SSViewer.' . $optionName . '" config setting instead');
return Config::inst()->get('SSViewer', $optionName); return Config::inst()->get('SSViewer', $optionName);
} }
} }