diff --git a/composer.json b/composer.json index 17a8be381..a24d16400 100644 --- a/composer.json +++ b/composer.json @@ -20,7 +20,7 @@ "composer/installers": "*" }, "require-dev": { - "phpunit/PHPUnit": "~3.7" + "phpunit/PHPUnit": "~3.7@stable" }, "extra": { "branch-alias": { diff --git a/control/Director.php b/control/Director.php index d67b276fb..eb20ad0fd 100644 --- a/control/Director.php +++ b/control/Director.php @@ -165,13 +165,13 @@ class Director implements TemplateGlobalProvider { DataModel::inst() ); } else { - $response = new SS_HTTPResponse(); + $response = new SS_HTTPResponse(); $response->redirect($url); - $res = Injector::inst()->get('RequestProcessor')->postRequest($req, $response, $model); + $res = Injector::inst()->get('RequestProcessor')->postRequest($req, $response, $model); - if ($res !== false) { - $response->output(); - } + if ($res !== false) { + $response->output(); + } } // Handle a controller } else if($result) { diff --git a/control/RequestFilter.php b/control/RequestFilter.php index f0cb0d823..c1c5b6439 100644 --- a/control/RequestFilter.php +++ b/control/RequestFilter.php @@ -11,16 +11,24 @@ * @subpackage control */ interface RequestFilter { + /** * Filter executed before a request processes * - * @return boolean (optional) - * Whether to continue processing other filters + * @param SS_HTTPRequest $request Request container object + * @param Session $session Request session + * @param DataModel $model Current DataModel + * @return boolean Whether to continue processing other filters. Null or true will continue processing (optional) */ public function preRequest(SS_HTTPRequest $request, Session $session, DataModel $model); /** * Filter executed AFTER a request + * + * @param SS_HTTPRequest $request Request container object + * @param SS_HTTPResponse $response Response output object + * @param DataModel $model Current DataModel + * @return boolean Whether to continue processing other filters. Null or true will continue processing (optional) */ public function postRequest(SS_HTTPRequest $request, SS_HTTPResponse $response, DataModel $model); -} \ No newline at end of file +} diff --git a/control/RequestProcessor.php b/control/RequestProcessor.php index a160fe423..e391be0a5 100644 --- a/control/RequestProcessor.php +++ b/control/RequestProcessor.php @@ -1,17 +1,29 @@ filters = $filters; } + /** + * Assign a list of request filters + * + * @param array $filters + */ public function setFilters($filters) { $this->filters = $filters; } @@ -25,9 +37,6 @@ class RequestProcessor { } } - /** - * Filter executed AFTER a request - */ public function postRequest(SS_HTTPRequest $request, SS_HTTPResponse $response, DataModel $model) { foreach ($this->filters as $filter) { $res = $filter->postRequest($request, $response, $model); diff --git a/control/VersionedRequestFilter.php b/control/VersionedRequestFilter.php index c6158cc4f..7072a18c2 100644 --- a/control/VersionedRequestFilter.php +++ b/control/VersionedRequestFilter.php @@ -5,13 +5,15 @@ * @package framework * @subpackage control */ -class VersionedRequestFilter { +class VersionedRequestFilter implements RequestFilter { - public function preRequest() { - Versioned::choose_site_stage(); + public function preRequest(SS_HTTPRequest $request, Session $session, DataModel $model) { + Versioned::choose_site_stage($session); + return true; } - public function postRequest() { + public function postRequest(SS_HTTPRequest $request, SS_HTTPResponse $response, DataModel $model) { + return true; } } diff --git a/control/injector/Injector.php b/control/injector/Injector.php index af0552a4a..1888ce838 100644 --- a/control/injector/Injector.php +++ b/control/injector/Injector.php @@ -210,6 +210,13 @@ class Injector { } } + /** + * The injector instance this one was copied from when Injector::nest() was called. + * + * @var Injector + */ + protected $nestedFrom = null; + /** * If a user wants to use the injector as a static reference * @@ -227,9 +234,38 @@ class Injector { * Sets the default global injector instance. * * @param Injector $instance + * @return Injector Reference to new active Injector instance */ public static function set_inst(Injector $instance) { - self::$instance = $instance; + return self::$instance = $instance; + } + + /** + * Make the newly active {@link Injector} be a copy of the current active + * {@link Injector} instance. + * + * You can then make changes to the injector with methods such as + * {@link Injector::inst()->registerService()} which will be discarded + * upon a subsequent call to {@link Injector::unnest()} + * + * @return Injector Reference to new active Injector instance + */ + public static function nest() { + $current = self::$instance; + + $new = clone $current; + $new->nestedFrom = $current; + return self::set_inst($new); + } + + /** + * Change the active Injector back to the Injector instance the current active + * Injector object was copied from. + * + * @return Injector Reference to restored active Injector instance + */ + public static function unnest() { + return self::set_inst(self::$instance->nestedFrom); } /** diff --git a/core/Config.php b/core/Config.php index 752e73c27..f048d0fa1 100644 --- a/core/Config.php +++ b/core/Config.php @@ -201,13 +201,15 @@ class Config { * A use case for replacing the active configuration set would be for * creating an isolated environment for unit tests. * - * @return Config + * @param Config $instance New instance of Config to assign + * @return Config Reference to new active Config instance */ public static function set_instance($instance) { self::$instance = $instance; global $_SINGLETONS; $_SINGLETONS['Config'] = $instance; + return $instance; } /** @@ -215,23 +217,27 @@ class Config { * {@link Config} instance. * * You can then make changes to the configuration by calling update and - * remove on the new value returned by Config::inst(), and then discard + * remove on the new value returned by {@link Config::inst()}, and then discard * those changes later by calling unnest. + * + * @return Config Reference to new active Config instance */ public static function nest() { $current = self::$instance; $new = clone $current; $new->nestedFrom = $current; - self::set_instance($new); + return self::set_instance($new); } /** * Change the active Config back to the Config instance the current active * Config object was copied from. + * + * @return Config Reference to new active Config instance */ public static function unnest() { - self::set_instance(self::$instance->nestedFrom); + return self::set_instance(self::$instance->nestedFrom); } /** diff --git a/core/manifest/ConfigStaticManifest.php b/core/manifest/ConfigStaticManifest.php index e0beb5576..fc1c54feb 100644 --- a/core/manifest/ConfigStaticManifest.php +++ b/core/manifest/ConfigStaticManifest.php @@ -186,7 +186,7 @@ class SS_ConfigStaticManifest_Parser { * Get the next token to process, incrementing the pointer * * @param bool $ignoreWhitespace - if true will skip any whitespace tokens & only return non-whitespace ones - * @return null | int - Either the next token or null if there isn't one + * @return null | mixed - Either the next token or null if there isn't one */ protected function next($ignoreWhitespace = true) { do { @@ -198,6 +198,40 @@ class SS_ConfigStaticManifest_Parser { return $next; } + /** + * Get the next set of tokens that form a string to process, + * incrementing the pointer + * + * @param bool $ignoreWhitespace - if true will skip any whitespace tokens + * & only return non-whitespace ones + * @return null|string - Either the next string or null if there isn't one + */ + protected function nextString($ignoreWhitespace = true) { + static $stop = array('{', '}', '(', ')', '[', ']'); + + $string = ''; + while ($this->pos < $this->length) { + $next = $this->tokens[$this->pos]; + if (is_string($next)) { + if (!in_array($next, $stop)) { + $string .= $next; + } else { + break; + } + } else if ($next[0] == T_STRING) { + $string .= $next[1]; + } else if ($next[0] != T_WHITESPACE || !$ignoreWhitespace) { + break; + } + $this->pos++; + } + if ($string === '') { + return null; + } else { + return $string; + } + } + /** * Parse the given file to find the static variables declared in it, along with their access & values */ @@ -208,12 +242,12 @@ class SS_ConfigStaticManifest_Parser { $type = is_array($token) ? $token[0] : $token; if($type == T_CLASS) { - $next = $this->next(); - if($next[0] != T_STRING) { + $next = $this->nextString(); + if($next === null) { user_error("Couldn\'t parse {$this->path} when building config static manifest", E_USER_ERROR); } - $class = $next[1]; + $class = $next; } else if($type == T_NAMESPACE) { $namespace = ''; @@ -227,11 +261,11 @@ class SS_ConfigStaticManifest_Parser { $next = $this->next(); } - if($next[0] != T_STRING) { + if(!is_string($next) && $next[0] != T_STRING) { user_error("Couldn\'t parse {$this->path} when building config static manifest", E_USER_ERROR); } - $namespace .= $next[1]; + $namespace .= is_string($next) ? $next : $next[1]; } } else if($type == '{' || $type == T_CURLY_OPEN || $type == T_DOLLAR_OPEN_CURLY_BRACES){ diff --git a/dev/DevelopmentAdmin.php b/dev/DevelopmentAdmin.php index fff4db8a8..6533aea10 100644 --- a/dev/DevelopmentAdmin.php +++ b/dev/DevelopmentAdmin.php @@ -181,25 +181,15 @@ class DevelopmentAdmin extends Controller { public function generatesecuretoken() { $generator = Injector::inst()->create('RandomGenerator'); $token = $generator->randomToken('sha1'); + $body = <<request->getVar('path'); - if($path) { - if(file_exists(BASE_PATH . '/' . $path)) { - echo sprintf( - "Configuration file '%s' exists, can't merge. Please choose a new file.\n", - BASE_PATH . '/' . $path - ); - exit(1); - } - $yml = "Security:\n token: $token"; - Filesystem::makeFolder(dirname(BASE_PATH . '/' . $path)); - file_put_contents(BASE_PATH . '/' . $path, $yml); - echo "Configured token in $path\n"; - } else { - echo "Generated new token. Please add the following code to your YAML configuration:\n\n"; - echo "Security:\n"; - echo " token: $token\n"; - } +Security: + token: $token + +TXT; + $response = new SS_HTTPResponse($body); + return $response->addHeader('Content-Type', 'text/plain'); } public function errors() { diff --git a/docs/en/changelogs/3.1.5.md b/docs/en/changelogs/3.1.5.md index 54647c1e8..f4c63b141 100644 --- a/docs/en/changelogs/3.1.5.md +++ b/docs/en/changelogs/3.1.5.md @@ -7,3 +7,55 @@ user login name between sessions, and disable browser auto-completion on the username field. Note that users of certain browsers who have previously autofilled and saved login credentials will need to clear their password autofill history before this setting is properly respected. + * Test cases that rely on updating and restoring `[api:Injector]` services may now take advantage + of the new `Injector::nest()` and `Injector::unnest()` methods to sandbox their alterations. + * If errors could potentially be raised by any `[api:RequestHandler]` class such as a `[api:Form]` or + `[api:Controller]`, you may now add the new `[api:ErrorPageControllerExtension]` to this class to + transform plain text error messages into `ErrorPage` rendered HTML errors. In the past this + behaviour was limited to subclasses of `[api:ContentController]`. By default this extension is now + added to the `Security` controller, and if this is not desirable then it should be removed + explicitly via the Config system. + +## Security + + * 2014-04-16 [bde16f0](https://github.com/silverstripe/sapphire/commit/bde16f0) Potential DoS exploit in TinyMCE - See [announcement SS-2014-009](http://www.silverstripe.org/ss-2014-009-potential-dos-exploit-in-tinymce/) + * 2014-05-05 [d9bc352](https://github.com/silverstripe/silverstripe-framework/commit/d9bc352) Injection / Filesystem vulnerability in generatesecuretoken - See [announcement SS-2014-010](http://www.silverstripe.org/ss-2014-010-injection-filesystem-vulnerability-in-generatesecuretoken/) + * 2014-05-02 [8e841cc](https://github.com/silverstripe/sapphire/commit/8e841cc) Folder filename injection - See [announcement SS-2014-011](http://www.silverstripe.org/ss-2014-011-folder-filename-injection/) + * 2014-05-05 [df28ccb](https://github.com/silverstripe/sapphire/commit/df28ccb) Upload fileexists vulnerability - See [announcement SS-2014-013](http://www.silverstripe.org/ss-2014-013-upload-fileexists-vulnerability/) + +### API Changes + + * 2014-05-02 [f9cb880](https://github.com/silverstripe/silverstripe-cms/commit/f9cb880) Error page support for Security controller errors (Damian Mooyman) + * 2014-05-01 [3162d0e](https://github.com/silverstripe/silverstripe-cms/commit/3162d0e) Update ErrorPage to respect new HTTP Error codes (Damian Mooyman) + * 2014-04-28 [0285322](https://github.com/silverstripe/silverstripe-cms/commit/0285322) Ability to configure paging for assets / pages (Damian Mooyman) + * 2014-04-22 [d06d5c1](https://github.com/silverstripe/sapphire/commit/d06d5c1) Injector supports nesting BUG Resolve issue with DirectorTest breaking RequestProcessor Injector::nest and Injector::unnest are introduced to better support sandboxing of testings. Injector and Config ::nest and ::unnest support chaining Test cases for both Injector::nest and Config::nest (Damian Mooyman) + * 2014-04-17 [a6017a0](https://github.com/silverstripe/sapphire/commit/a6017a0) HTTP 429 Allowed for use with rate limiting methods (Damian Mooyman) + * 2014-04-11 [892b440](https://github.com/silverstripe/sapphire/commit/892b440) Make default gridfield paging configurable Documentation improved (Damian Mooyman) + * 2014-04-09 [997077a](https://github.com/silverstripe/sapphire/commit/997077a) Security.remember_username to disable login form autocompletion (Damian Mooyman) + +### Features and Enhancements + + * 2014-03-28 [a502c9d](https://github.com/silverstripe/silverstripe-cms/commit/a502c9d) Fixes #966. Ability to filter pages on page status. - New filters for statuses normally found through SiteTree::getStatusFlags(). - Refactored menu sorting. Now alphabetical, as it wasn't previously. (Russell Michell) + * 2014-04-11 [3765030](https://github.com/silverstripe/silverstripe-cms/commit/3765030) Filter by date created for files Added test cases Do not merge before https://github.com/silverstripe-labs/silverstripe-behat-extension/pull/32 (Damian Mooyman) + +### Bugfixes + + * 2014-05-05 [c5d5d10](https://github.com/silverstripe/silverstripe-cms/commit/c5d5d10) Behat now uses explicit radio button behaviour (Damian Mooyman) + * 2014-05-01 [bd5abb6](https://github.com/silverstripe/sapphire/commit/bd5abb6) parent::init is not called first (Michael Parkhill) + * 2014-05-01 [4fd3015](https://github.com/silverstripe/sapphire/commit/4fd3015) corrected link to CMS Alternating Button Page (James Pluck) + * 2014-04-29 [8673b11](https://github.com/silverstripe/sapphire/commit/8673b11) Fix ImageTest Image test would erroneously reset the Image::$backend to null if the test was skipped, breaking subsequent test cases (Damian Mooyman) + * 2014-04-29 [89fbae2](https://github.com/silverstripe/silverstripe-cms/commit/89fbae2) Fix encoding of SiteTree.MetaTags (Damian Mooyman) + * 2014-04-25 [ff5f607](https://github.com/silverstripe/sapphire/commit/ff5f607) Docs for DataList::filter() (Daniel Hensby) + * 2014-04-24 [5e9ae57](https://github.com/silverstripe/sapphire/commit/5e9ae57) Fix edge case IE8 / dev / ssl / download file crash Prevents issue at http://support.microsoft.com/kb/323308 appearing on dev (Damian Mooyman) + * 2014-04-17 [bec8927](https://github.com/silverstripe/sapphire/commit/bec8927) Allow PHPUnit installation with composer / Fix travis (Will Morgan) + * 2014-04-16 [396fd9a](https://github.com/silverstripe/silverstripe-cms/commit/396fd9a) Broken file link tracking (fixes #996) (Loz Calver) + * 2014-04-14 [0b4f62d](https://github.com/silverstripe/sapphire/commit/0b4f62d) Fix jstree when duplicating subtrees (Damian Mooyman) + * 2014-04-11 [a261f22](https://github.com/silverstripe/sapphire/commit/a261f22) Delete Character \x01 (Stevie Mayhew) + * 2014-04-09 [91034d1](https://github.com/silverstripe/sapphire/commit/91034d1) HTMLText whitelist considers text nodes Minor improvement to #2853. If a list of whitelisted elements are specified, text nodes no longer evade the whitelist (Damian Mooyman) + * 2014-04-09 [a3c8a59](https://github.com/silverstripe/sapphire/commit/a3c8a59) Fix data query not always joining necessary tables Fixes #2846 (Damian Mooyman) + * 2014-04-08 [a060784](https://github.com/silverstripe/sapphire/commit/a060784) - missing link url for composer (camfindlay) + * 2014-04-07 [3204ab5](https://github.com/silverstripe/silverstripe-cms/commit/3204ab5) Fix orphaned pages reporting they can be viewed (Damian Mooyman) + * 2014-04-01 [84d8022](https://github.com/silverstripe/sapphire/commit/84d8022) Fix Date and SS_DateTime::FormatFromSettings This issue is caused by the odd default behaviour of Zend_Date, which attempts to parse yyyy-mm-dd format date and times as though they were yyyy-dd-mm. (Damian Mooyman) + * 2014-03-12 [b4a1aa4](https://github.com/silverstripe/silverstripe-cms/commit/b4a1aa4) Fixes #965. Allow user date-settings to show on GridField Page admin (Russell Michell) + * 2014-03-04 [ae573f8](https://github.com/silverstripe/sapphire/commit/ae573f8) Fix Versioned stage not persisting in Session. Fixes #962 BUG Disabled disruptive test case in DirectorTest API RequestProcessor and VersionedRequestFilter now both correctly implement RequestFilter Better PHPDoc on RequestFilter and implementations (Damian Mooyman) + * 2013-06-20 [f2c4a62](https://github.com/silverstripe/sapphire/commit/f2c4a62) ConfirmedPasswordField used to expose existing hash (Hamish Friedlander) diff --git a/docs/en/changelogs/index.md b/docs/en/changelogs/index.md index 4c2575c5c..b98fa8b42 100644 --- a/docs/en/changelogs/index.md +++ b/docs/en/changelogs/index.md @@ -9,8 +9,10 @@ For information on how to upgrade to newer versions consult the [upgrading](/ins ## Stable Releases + * [3.1.4](3.1.4) - 8 April 2014 * [3.1.0](3.1.0) - 1 October 2013 + * [3.0.10](3.0.10) - 8 April 2014 * [3.0.5](3.0.5) - 20 February 2013 * [3.0.4](3.0.4) - 19 February 2013 * [3.0.3](3.0.3) - 26 November 2012 @@ -75,6 +77,9 @@ For information on how to upgrade to newer versions consult the [upgrading](/ins ## Alpha/beta/release candidate ## + * [3.1.5-rc1](rc/3.1.5-rc1) - 7 May 2014 + * [3.1.4-rc1](rc/3.1.4-rc1) - 1 April 2014 + * [3.0.10-rc1](rc/3.0.10-rc1) - 1 April 2014 * [3.0.6-rc1](rc/3.0.6-rc1) - 2013-08-08 * [3.0.3-rc1](rc/3.0.3-rc1) - 6 November 2012 * [3.0.2-rc2](rc/3.0.2-rc2) - 12 September 2012 diff --git a/docs/en/changelogs/rc/3.1.5-rc1.md b/docs/en/changelogs/rc/3.1.5-rc1.md new file mode 100644 index 000000000..08e0d8c89 --- /dev/null +++ b/docs/en/changelogs/rc/3.1.5-rc1.md @@ -0,0 +1,61 @@ +# 3.1.5-rc1 + +## Upgrading + + * If running an application in an environment where user security is critical, it may be necessary to + assign the config value `Security.remember_username` to false. This will disable persistence of + user login name between sessions, and disable browser auto-completion on the username field. + Note that users of certain browsers who have previously autofilled and saved login credentials + will need to clear their password autofill history before this setting is properly respected. + * Test cases that rely on updating and restoring `[api:Injector]` services may now take advantage + of the new `Injector::nest()` and `Injector::unnest()` methods to sandbox their alterations. + * If errors could potentially be raised by any `[api:RequestHandler]` class such as a `[api:Form]` or + `[api:Controller]`, you may now add the new `[api:ErrorPageControllerExtension]` to this class to + transform plain text error messages into `ErrorPage` rendered HTML errors. In the past this + behaviour was limited to subclasses of `[api:ContentController]`. By default this extension is now + added to the `Security` controller, and if this is not desirable then it should be removed + explicitly via the Config system. + +## Security + + * 2014-04-16 [bde16f0](https://github.com/silverstripe/sapphire/commit/bde16f0) Potential DoS exploit in TinyMCE - See [announcement SS-2014-009](http://www.silverstripe.org/ss-2014-009-potential-dos-exploit-in-tinymce/) + * 2014-05-05 [d9bc352](https://github.com/silverstripe/silverstripe-framework/commit/d9bc352) Injection / Filesystem vulnerability in generatesecuretoken - See [announcement SS-2014-010](http://www.silverstripe.org/ss-2014-010-injection-filesystem-vulnerability-in-generatesecuretoken/) + * 2014-05-02 [8e841cc](https://github.com/silverstripe/sapphire/commit/8e841cc) Folder filename injection - See [announcement SS-2014-011](http://www.silverstripe.org/ss-2014-011-folder-filename-injection/) + * 2014-05-05 [df28ccb](https://github.com/silverstripe/sapphire/commit/df28ccb) Upload fileexists vulnerability - See [announcement SS-2014-013](http://www.silverstripe.org/ss-2014-013-upload-fileexists-vulnerability/) + +### API Changes + + * 2014-05-02 [f9cb880](https://github.com/silverstripe/silverstripe-cms/commit/f9cb880) Error page support for Security controller errors (Damian Mooyman) + * 2014-05-01 [3162d0e](https://github.com/silverstripe/silverstripe-cms/commit/3162d0e) Update ErrorPage to respect new HTTP Error codes (Damian Mooyman) + * 2014-04-28 [0285322](https://github.com/silverstripe/silverstripe-cms/commit/0285322) Ability to configure paging for assets / pages (Damian Mooyman) + * 2014-04-22 [d06d5c1](https://github.com/silverstripe/sapphire/commit/d06d5c1) Injector supports nesting BUG Resolve issue with DirectorTest breaking RequestProcessor Injector::nest and Injector::unnest are introduced to better support sandboxing of testings. Injector and Config ::nest and ::unnest support chaining Test cases for both Injector::nest and Config::nest (Damian Mooyman) + * 2014-04-17 [a6017a0](https://github.com/silverstripe/sapphire/commit/a6017a0) HTTP 429 Allowed for use with rate limiting methods (Damian Mooyman) + * 2014-04-11 [892b440](https://github.com/silverstripe/sapphire/commit/892b440) Make default gridfield paging configurable Documentation improved (Damian Mooyman) + * 2014-04-09 [997077a](https://github.com/silverstripe/sapphire/commit/997077a) Security.remember_username to disable login form autocompletion (Damian Mooyman) + +### Features and Enhancements + + * 2014-03-28 [a502c9d](https://github.com/silverstripe/silverstripe-cms/commit/a502c9d) Fixes #966. Ability to filter pages on page status. - New filters for statuses normally found through SiteTree::getStatusFlags(). - Refactored menu sorting. Now alphabetical, as it wasn't previously. (Russell Michell) + * 2014-04-11 [3765030](https://github.com/silverstripe/silverstripe-cms/commit/3765030) Filter by date created for files Added test cases Do not merge before https://github.com/silverstripe-labs/silverstripe-behat-extension/pull/32 (Damian Mooyman) + +### Bugfixes + + * 2014-05-05 [c5d5d10](https://github.com/silverstripe/silverstripe-cms/commit/c5d5d10) Behat now uses explicit radio button behaviour (Damian Mooyman) + * 2014-05-01 [bd5abb6](https://github.com/silverstripe/sapphire/commit/bd5abb6) parent::init is not called first (Michael Parkhill) + * 2014-05-01 [4fd3015](https://github.com/silverstripe/sapphire/commit/4fd3015) corrected link to CMS Alternating Button Page (James Pluck) + * 2014-04-29 [8673b11](https://github.com/silverstripe/sapphire/commit/8673b11) Fix ImageTest Image test would erroneously reset the Image::$backend to null if the test was skipped, breaking subsequent test cases (Damian Mooyman) + * 2014-04-29 [89fbae2](https://github.com/silverstripe/silverstripe-cms/commit/89fbae2) Fix encoding of SiteTree.MetaTags (Damian Mooyman) + * 2014-04-25 [ff5f607](https://github.com/silverstripe/sapphire/commit/ff5f607) Docs for DataList::filter() (Daniel Hensby) + * 2014-04-24 [5e9ae57](https://github.com/silverstripe/sapphire/commit/5e9ae57) Fix edge case IE8 / dev / ssl / download file crash Prevents issue at http://support.microsoft.com/kb/323308 appearing on dev (Damian Mooyman) + * 2014-04-17 [bec8927](https://github.com/silverstripe/sapphire/commit/bec8927) Allow PHPUnit installation with composer / Fix travis (Will Morgan) + * 2014-04-16 [396fd9a](https://github.com/silverstripe/silverstripe-cms/commit/396fd9a) Broken file link tracking (fixes #996) (Loz Calver) + * 2014-04-14 [0b4f62d](https://github.com/silverstripe/sapphire/commit/0b4f62d) Fix jstree when duplicating subtrees (Damian Mooyman) + * 2014-04-11 [a261f22](https://github.com/silverstripe/sapphire/commit/a261f22) Delete Character \x01 (Stevie Mayhew) + * 2014-04-09 [91034d1](https://github.com/silverstripe/sapphire/commit/91034d1) HTMLText whitelist considers text nodes Minor improvement to #2853. If a list of whitelisted elements are specified, text nodes no longer evade the whitelist (Damian Mooyman) + * 2014-04-09 [a3c8a59](https://github.com/silverstripe/sapphire/commit/a3c8a59) Fix data query not always joining necessary tables Fixes #2846 (Damian Mooyman) + * 2014-04-08 [a060784](https://github.com/silverstripe/sapphire/commit/a060784) - missing link url for composer (camfindlay) + * 2014-04-07 [3204ab5](https://github.com/silverstripe/silverstripe-cms/commit/3204ab5) Fix orphaned pages reporting they can be viewed (Damian Mooyman) + * 2014-04-01 [84d8022](https://github.com/silverstripe/sapphire/commit/84d8022) Fix Date and SS_DateTime::FormatFromSettings This issue is caused by the odd default behaviour of Zend_Date, which attempts to parse yyyy-mm-dd format date and times as though they were yyyy-dd-mm. (Damian Mooyman) + * 2014-03-12 [b4a1aa4](https://github.com/silverstripe/silverstripe-cms/commit/b4a1aa4) Fixes #965. Allow user date-settings to show on GridField Page admin (Russell Michell) + * 2014-03-04 [ae573f8](https://github.com/silverstripe/sapphire/commit/ae573f8) Fix Versioned stage not persisting in Session. Fixes #962 BUG Disabled disruptive test case in DirectorTest API RequestProcessor and VersionedRequestFilter now both correctly implement RequestFilter Better PHPDoc on RequestFilter and implementations (Damian Mooyman) + * 2013-06-20 [f2c4a62](https://github.com/silverstripe/sapphire/commit/f2c4a62) ConfirmedPasswordField used to expose existing hash (Hamish Friedlander) diff --git a/docs/en/howto/pagination.md b/docs/en/howto/pagination.md index 457c2e38d..e30e1146b 100644 --- a/docs/en/howto/pagination.md +++ b/docs/en/howto/pagination.md @@ -87,7 +87,7 @@ To set the limit of items displayed in a paginated page use the `[api:PaginatedL public function PaginatedPagesLimit() { $paginatedItems = new PaginatedList(Page::get(), $this->request); $paginatedItems->setPageLength(4); - return $pagination; + return $paginatedItems; } ## Related diff --git a/docs/en/reference/injector.md b/docs/en/reference/injector.md index 20b8b792b..42df40321 100644 --- a/docs/en/reference/injector.md +++ b/docs/en/reference/injector.md @@ -192,6 +192,30 @@ would * Create a MySQLDatabase class, passing dbusername and dbpassword as the parameters to the constructor +### Testing with Injector in a sandbox environment + +In situations where injector states must be temporarily overridden, it is possible +to create nested Injector instances which may be later discarded, reverting the +application to the original state. + +This is useful when writing test cases, as certain services may be necessary to +override for a single method call. + +For instance, a temporary service can be registered and unregistered as below: + + :::php + // Setup default service + Injector::inst()->registerService(new LiveService(), 'ServiceName'); + + // Test substitute service temporarily + Injector::nest(); + Injector::inst()->registerService(new TestingService(), 'ServiceName'); + $service = Injector::inst()->get('ServiceName'); + // ... do something with $service + Injector::unnest(); + + // ... future requests for 'ServiceName' will return the LiveService instance + ### What are Services? diff --git a/forms/UploadField.php b/forms/UploadField.php index 73bf334c5..f6c6a2513 100644 --- a/forms/UploadField.php +++ b/forms/UploadField.php @@ -1286,16 +1286,16 @@ class UploadField extends FileField { } /** - * Determines if a specified file exists + * Check if file exists, both checking filtered filename and exact filename * - * @param SS_HTTPRequest $request + * @param string $originalFile Filename + * @return bool */ - public function fileexists(SS_HTTPRequest $request) { + protected function checkFileExists($originalFile) { // Check both original and safely filtered filename - $originalFile = $request->requestVar('filename'); $nameFilter = FileNameFilter::create(); - $filteredFile = basename($nameFilter->filter($originalFile)); + $filteredFile = $nameFilter->filter($originalFile); // Resolve expected folder name $folderName = $this->getFolderName(); @@ -1305,17 +1305,32 @@ class UploadField extends FileField { : ASSETS_PATH."/"; // check if either file exists - $exists = false; - foreach(array($originalFile, $filteredFile) as $file) { - if(file_exists($parentPath.$file)) { - $exists = true; - break; - } + return file_exists($parentPath.$originalFile) + || file_exists($parentPath.$filteredFile); + } + + /** + * Determines if a specified file exists + * + * @param SS_HTTPRequest $request + */ + public function fileexists(SS_HTTPRequest $request) { + // Assert that requested filename doesn't attempt to escape the directory + $originalFile = $request->requestVar('filename'); + if($originalFile !== basename($originalFile)) { + $return = array( + 'error' => _t('File.NOVALIDUPLOAD', 'File is not a valid upload') + ); + } else { + $return = array( + 'exists' => $this->checkFileExists($originalFile) + ); } // Encode and present response - $response = new SS_HTTPResponse(Convert::raw2json(array('exists' => $exists))); + $response = new SS_HTTPResponse(Convert::raw2json($return)); $response->addHeader('Content-Type', 'application/json'); + if (!empty($return['error'])) $response->setStatusCode(400); return $response; } diff --git a/i18n/i18n.php b/i18n/i18n.php index bac113d21..4b85ffec7 100644 --- a/i18n/i18n.php +++ b/i18n/i18n.php @@ -2512,8 +2512,7 @@ class i18n extends Object implements TemplateGlobalProvider { */ public static function include_by_locale($locale, $clean = false) { if($clean) { - $cache = Zend_Translate::getCache(); - if($cache) $cache->clean(Zend_Cache::CLEANING_MODE_ALL); + Zend_Translate::clearCache(); } // Get list of module => path pairs, and then just the names diff --git a/javascript/lang/nb.js b/javascript/lang/nb.js new file mode 100644 index 000000000..282e94de1 --- /dev/null +++ b/javascript/lang/nb.js @@ -0,0 +1,47 @@ +// This file was generated by GenerateJavaScriptI18nTask from javascript/lang/src/nb.js. +// See https://github.com/silverstripe/silverstripe-buildtools for details +if(typeof(ss) == 'undefined' || typeof(ss.i18n) == 'undefined') { + if(typeof(console) != 'undefined') console.error('Class ss.i18n not defined'); +} else { + ss.i18n.addDictionary('nb', { + "VALIDATOR.FIELDREQUIRED": "Vennligst fyll ut det påkrevde feltet \"%s\"", + "HASMANYFILEFIELD.UPLOADING": "Laster opp ... %s", + "TABLEFIELD.DELETECONFIRMMESSAGE": "Er du sikker på at du vil slette denne oppføringen?", + "LOADING": "laster ...", + "UNIQUEFIELD.SUGGESTED": "Endret verdien til '%s': %s", + "UNIQUEFIELD.ENTERNEWVALUE": "Du må skrive inn en ny verdi for dette feltet", + "UNIQUEFIELD.CANNOTLEAVEEMPTY": "Dette feltet kan ikke stå tomt", + "RESTRICTEDTEXTFIELD.CHARCANTBEUSED": "Tegnet '%s' kan ikke brukes i dette feltet", + "UPDATEURL.CONFIRM": "Ønsker du å endre adressen til:\n\n%s/\n\nTrykk Ok for å endre adressen, trykk Avbryt for å beholde den som:\n\n%s", + "UPDATEURL.CONFIRMURLCHANGED": "Adressen har blitt endret til\n'%s'", + "FILEIFRAMEFIELD.DELETEFILE": "Slett fil", + "FILEIFRAMEFIELD.UNATTACHFILE": "Fjern vedlagt fil", + "FILEIFRAMEFIELD.DELETEIMAGE": "Slett bilde", + "FILEIFRAMEFIELD.CONFIRMDELETE": "Er du sikker på du vil slette denne filen?", + "LeftAndMain.IncompatBrowserWarning": "Nettleseren din er ikke kompatibel med publiseringsgrensesnittet. Vennligst bruk Internet Explorer 7+, Google Chrome 10+ eller Mozilla Firefox 3.5+.", + "GRIDFIELD.ERRORINTRANSACTION": "En feil oppstod ved lesing fra serveren\nVennligst prøv på nytt senere.", + "HtmlEditorField.SelectAnchor": "Velg et anker", + "UploadField.ConfirmDelete": "Er du sikker på at du vil fjerne denne filen fra serverens filsystem?", + "UploadField.PHP_MAXFILESIZE": "Filen er større enn upload_max_filesize (innstilling i php.ini)", + "UploadField.HTML_MAXFILESIZE": "Filen er større enn MAX_FILE_SIZE (HTML-direktiv)", + "UploadField.ONLYPARTIALUPLOADED": "Filen ble bare delvis lastet opp", + "UploadField.NOFILEUPLOADED": "Ingen filer ble lastet opp", + "UploadField.NOTMPFOLDER": "Mangler en midlertidig mappe", + "UploadField.WRITEFAILED": "Klarte ikke å lagre filen på harddisken", + "UploadField.STOPEDBYEXTENSION": "Filopplastingen ble stoppet av en utvidelse", + "UploadField.TOOLARGE": "Filen er for stor", + "UploadField.TOOSMALL": "Filen er for liten", + "UploadField.INVALIDEXTENSION": "Filtypen er ikke tillatt", + "UploadField.MAXNUMBEROFFILESSIMPLE": "For mange filer", + "UploadField.UPLOADEDBYTES": "Lastet opp flere bytes enn filstørrelsen tilsier", + "UploadField.EMPTYRESULT": "Filopplastingen ga et tomt resultat", + "UploadField.LOADING": "Laster ...", + "UploadField.Editing": "Redigerer ...", + "UploadField.Uploaded": "Lastet opp", + "UploadField.OVERWRITEWARNING": "En fil med samme navn eksisterer allerede", + "TreeDropdownField.ENTERTOSEARCH": "Trykk enter for å søke", + "TreeDropdownField.OpenLink": "Åpne", + "TreeDropdownField.FieldTitle": "Velg", + "TreeDropdownField.SearchFieldTitle": "Velg eller søk" +}); +} \ No newline at end of file diff --git a/lang/af.yml b/lang/af.yml index d02dd4803..be4d19f85 100644 --- a/lang/af.yml +++ b/lang/af.yml @@ -233,7 +233,6 @@ af: PERMAGAIN: 'Jy is uit die IBS uitgeteken. As jy weer wil inteken, moet jy ''n gebruikersnaam en wagwoord onder in tik' PERMALREADY: 'Ek is jammer, maar jy het nie toestemming om dié gedeelte van die IBS te besugtig nie. As jy as iemand anders wil inteken doen so hieronder' PERMDEFAULT: 'Kies asseblief ''n kontroleer metode en sleutel jou sekuriteit''s besonderhede in' - PLEASESAVE: 'Stoor asseblief die bladsy: Die bladsy kon nie opgedateer word nie omdat dit nog nie gestoor is nie' PreviewButton: Beskou REORGANISATIONSUCCESSFUL: 'Die ''site tree'' is suksesvol geheorganiseer' SAVEDUP: Gestoor diff --git a/lang/ar.yml b/lang/ar.yml index 8612ac439..17aa296b2 100644 --- a/lang/ar.yml +++ b/lang/ar.yml @@ -302,7 +302,6 @@ ar: PERMAGAIN: 'تم خروجك من النظام بنجاح. للدخول مرة أخرى أدحل البريد الإلكتروني و الرقم السري بالأسفل' PERMALREADY: 'عذراً , لكن لا يمكنك الوصول لهذا القسم من النظام. يتوجب عليك الدخول بصلاحية أخرى' PERMDEFAULT: 'أدخل البريد الإلكتروني و الرقم السري للوصول إلى نظام إدارة المحتوى' - PLEASESAVE: 'فضلاً احفظ الصفحة: هذه الصفحة لا يمكن تحديثها لأنها لم تحفظ بعد' PreviewButton: استعراض REORGANISATIONSUCCESSFUL: 'تم إعادة تنظيم خريطة الموقع بنجاح' SAVEDUP: تم الحفظ. diff --git a/lang/bg.yml b/lang/bg.yml index bd3bc2d0d..620c3e9fb 100644 --- a/lang/bg.yml +++ b/lang/bg.yml @@ -220,7 +220,6 @@ bg: PERMAGAIN: 'Вие излязохте от CMS. Ако искате да влезете отново, моля, въведете потребителско име и парола.' PERMALREADY: 'Съжалявам, но нямате достъп до тази част от CMS. Ако искате да влезете с друго потребителско име, моля, направете го по-долу' PERMDEFAULT: 'Въведете имейл адреса и паролата си, за да влезете в CMS.' - PLEASESAVE: 'Съхрани страницата: Тази страница не може да бъде обновена, защото още не е записана.' PreviewButton: Преглед REORGANISATIONSUCCESSFUL: 'Реорганизацията на дървото на сайта беше успешна.' SAVEDUP: Записано diff --git a/lang/bs.yml b/lang/bs.yml index c091e3bf7..0364642a0 100644 --- a/lang/bs.yml +++ b/lang/bs.yml @@ -104,7 +104,6 @@ bs: PERMAGAIN: 'Odjavljeni ste sa CMS-a. Ukoliko se želite ponovo prijaviti, unesite korisničko ime i šifru ispod.' PERMALREADY: 'Žao nam je ali ne možete pristupiti ovom dijelu CMS-a. Ako se želite prijaviti sa drugim korisnikom uradite to ispod' PERMDEFAULT: 'Unesite vašu e-mail adresu i šifru kako biste pristupili CMS-u.' - PLEASESAVE: 'Molimo snimite stranicu: Ova stranica ne može biti ažurirana ako nije prethodno snimljena.' Member: BUTTONCHANGEPASSWORD: 'Promijeni šifru' BUTTONLOGIN: 'Prijava' diff --git a/lang/ca.yml b/lang/ca.yml index ff0b6042b..a13b261cc 100644 --- a/lang/ca.yml +++ b/lang/ca.yml @@ -123,7 +123,6 @@ ca: PERMAGAIN: 'Heu estat desconnectat del SGC. Si voleu entrar de nou, introduïu un nom d''usuari i contrasenya a sota' PERMALREADY: 'Lamentant-ho molt, no podeu accedir a aquesta part del SGC. Si voleu entrar com a algú altre, feu-ho a sota' PERMDEFAULT: 'Introduïu la vostra adreça de correu electrònic i la contrasenya per a entrar al SGC.' - PLEASESAVE: 'Si us plau, deseu la pàgina: aquesta pàgina no s''ha pogut actualitzar perquè encara no s''ha desat.' LoginAttempt: Email: 'Adreça de correu' IP: 'Adreça IP' diff --git a/lang/cs.yml b/lang/cs.yml index e03f3cdb8..984a0e4a5 100644 --- a/lang/cs.yml +++ b/lang/cs.yml @@ -307,7 +307,7 @@ cs: PERMAGAIN: 'Byli jste odhlášeni z CMS. Pokud se chcete znovu přihlásit, zadejte níže své uživatelské jméno a heslo.' PERMALREADY: 'Je nám líto, ale nemůžete vstoupit do této části CMS. Pokud se chcete přihlásit jako někdo jiný, udělejte tak níže' PERMDEFAULT: 'Pro přístup do CMS zadejte Vaši e-mailovou adresu a heslo.' - PLEASESAVE: 'Prosím uložte stránku: Tato stránka nemohla být aktualizována, protože ještě nebyla uložena.' + PLEASESAVE: 'Uložte stránku, prosím. Tato stránka nemůže být aktualizována, protože ještě nebyla uložena.' PreviewButton: Náhled REORGANISATIONSUCCESSFUL: 'Strom webu reorganizován úspěšně.' SAVEDUP: Uloženo. diff --git a/lang/da.yml b/lang/da.yml index ea485b3e6..d30af63b6 100644 --- a/lang/da.yml +++ b/lang/da.yml @@ -20,7 +20,6 @@ da: PERMAGAIN: 'Du er blevet logget ud af CMS, hvis du vil logge ind igen, indtast brugernavn og kodeord nedenfor.' PERMALREADY: 'Beklager, men du kan ikke få adgang til denne del af CMS, hvis du vil logge ind som en anden, kan du gøre det nedenfor' PERMDEFAULT: 'Indtast din email adresse og kodeord for at få adgang til CMS systemet' - PLEASESAVE: 'Gem siden: Denne side kunne ikke blive opdateret, fordi den endnu ikke er gemt.' ModelAdmin: DELETE: Slet IMPORT: 'Importer fra CSV' diff --git a/lang/de.yml b/lang/de.yml index 5e5caf79b..23997ad9d 100644 --- a/lang/de.yml +++ b/lang/de.yml @@ -309,7 +309,6 @@ de: PERMAGAIN: 'Sie wurden aus dem System ausgeloggt. Falls Sie sich wieder einloggen möchten, geben Sie bitte Benutzernamen und Passwort im untenstehenden Formular an.' PERMALREADY: 'Leider dürfen Sie diesen Teil des CMS nicht aufrufen. Wenn Sie sich als jemand anderes einloggen wollen, benutzen Sie bitte das nachstehende Formular.' PERMDEFAULT: 'Bitte wählen Sie eine Authentifizierungsmethode und geben Sie Ihre Benutzerdaten für den Zugang zum CMS ein.' - PLEASESAVE: 'Diese Seite konnte nicht aktualisiert werden weil sie noch nicht gespeichert wurde - bitte speichern.' PreviewButton: Vorschau REORGANISATIONSUCCESSFUL: 'Der Seitenbaum wurde erfolgreich sortiert.' SAVEDUP: Gespeichert. diff --git a/lang/en_GB.yml b/lang/en_GB.yml index a43972ee5..8518f4957 100644 --- a/lang/en_GB.yml +++ b/lang/en_GB.yml @@ -18,7 +18,6 @@ en_GB: PAGETYPE: 'Page type:' 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' - PLEASESAVE: 'Please Save Page: This page could not be updated because it hasn''t been saved yet.' Member: ERRORNEWPASSWORD: 'Your have entered your new password differently, try again' Security: diff --git a/lang/eo.yml b/lang/eo.yml index 6109924f8..9485873a8 100644 --- a/lang/eo.yml +++ b/lang/eo.yml @@ -137,7 +137,6 @@ eo: PERMAGAIN: 'Vin adiaŭis la CMS. Se vi volas denove saluti, enigu salutnomon kaj pasvorton malsupre.' PERMALREADY: 'Bedaŭrinde vi ne povas aliri tiun parton de la CMS. Se vi volas saluti kiel iu alia, tiel faru sube' PERMDEFAULT: 'Enigi vian retadreson kaj pasvorton por aliri al la CMS.' - PLEASESAVE: 'Bonvolu konservi paĝon: Ne eblis ĝisdatigi ĉi tiun paĝon ĉar ĝi ankoraŭ ne estas konservita.' LoginAttempt: Email: 'Retadreso' IP: 'IP-Adreso' diff --git a/lang/es.yml b/lang/es.yml index ec8220374..5e7d045b0 100644 --- a/lang/es.yml +++ b/lang/es.yml @@ -171,6 +171,7 @@ es: TEXT2: 'enlace para restablecer contraseña' TEXT3: para Form: + CSRF_FAILED_MESSAGE: "Parece que hay un problema técnico. Por favor presionar el botón volver \n\n⇥⇥⇥⇥⇥refresca tu navegador e intenta nuevamente" FIELDISREQUIRED: 'Se requiere este campo' SubmitBtnLabel: Ir VALIDATIONCREDITNUMBER: 'Por favor, asegúrese de que ha introducido el número de tarjeta de crédito correctamente {number}' @@ -180,6 +181,7 @@ es: VALIDATIONSTRONGPASSWORD: 'Las contraseñas deben tener al menos un dígito y un carácter alfanumérico' VALIDATOR: Validador VALIDCURRENCY: 'Por favor, introduzca una moneda válida.' + CSRF_EXPIRED_MESSAGE: 'Tu sesión ha expirado. Por favor re envíe el formulario' FormField: Example: 'Ejemplo' NONE: ninguna @@ -305,7 +307,7 @@ es: PERMAGAIN: 'Ha sido desconectado del CMS. Si quiere volver a entrar, introduzca su nombre de usuario y contraseña a continuación.' PERMALREADY: 'Lamentablemente no puede acceder a esta parte del CMS. Si quiere entrar como alguien distinto, hágalo a continuación' PERMDEFAULT: 'Introduzca su correo electrónico y su contraseña para acceder al CMS.' - PLEASESAVE: 'Por favor Guarde la Página: Esta página no se ha podido actualizar porque aún no ha sido salvada.' + PLEASESAVE: 'Por favor guardar la página: Esta página no puede ser actualizada porque no ha sido guardada aún.' PreviewButton: Vista previa REORGANISATIONSUCCESSFUL: 'Reorganizado el árbol del sitio con éxito.' SAVEDUP: Guardado @@ -473,6 +475,7 @@ es: ERRORPASSWORDPERMISSION: 'Debe iniciar una sesión para poder cambiar su contraseña!' LOGGEDOUT: 'Ha terminado su sesión. Si desea iniciar sesión nuevamente, introduzca sus datos de acreditación a continuación.' LOGIN: 'Entrar' + LOSTPASSWORDHEADER: '¿Contraseña Perdida?' NOTEPAGESECURED: 'Esa página está protegida. Introduzca sus datos de acreditación a continuación y lo enviaremos a ella en un momento.' NOTERESETLINKINVALID: '

El enlace para restablecer la contraseña es inválido o ha expirado.

Usted puede solicitar uno nuevo aqui o cambiar su contraseña después de que se haya conectado.

' NOTERESETPASSWORD: 'Introduzca su dirección de e-mail, y le enviaremos un enlace, con el cual podrá restaurar su contraseña' diff --git a/lang/es_AR.yml b/lang/es_AR.yml index 098e1c0b9..672d2ff4e 100644 --- a/lang/es_AR.yml +++ b/lang/es_AR.yml @@ -136,7 +136,6 @@ es_AR: PERMAGAIN: 'Haz sido desconectado del CMS. Si quieres volver a entrar, a continuación introduce tu nombre de usuario y contraseña.' PERMALREADY: 'Lamentablemente no puedes ingresar a esta parte del CMS. Si quieres entrar como alguien distinto, haz eso a continuación' PERMDEFAULT: 'Por favor elegir un método de autenticación e ingresar sus credenciales para acceder al CMS.' - PLEASESAVE: 'Por favor Guarda la Página: No se puede actualizar esta página porque aún no se ha guardado.' LoginAttempt: Email: 'Dirección Email' IP: 'Dirección IP' diff --git a/lang/es_MX.yml b/lang/es_MX.yml index ed575e0ae..58dcaa250 100644 --- a/lang/es_MX.yml +++ b/lang/es_MX.yml @@ -187,7 +187,6 @@ es_MX: PERMAGAIN: 'Usted ha sido desconectado del CMS. Si quiere volver a entrar, introduzca su nombre de usuario y contraseña.' PERMALREADY: 'Lamentablemente no puedes ingresar a esta parte del CMS. Si quieres entrar como alguien distinto, hazlo a continuación' PERMDEFAULT: 'Por favor, elija un método de autenticación e introduzca sus credenciales para acceder al CMS.' - PLEASESAVE: 'Por favor Guarda la Página: No se puede actualizar esta página porque aún no se ha guardado.' VersionUnknown: desconocido LoginAttempt: Email: 'Dirección de Correo Electrónico' diff --git a/lang/et_EE.yml b/lang/et_EE.yml index 0b65badd2..6ffe2dd51 100644 --- a/lang/et_EE.yml +++ b/lang/et_EE.yml @@ -279,7 +279,6 @@ et_EE: PERMAGAIN: 'Oled Sisuhaldusest välja logitud. Kui soovite uuesti sisse logida sisestage kasutajanimi ja parool.' PERMALREADY: 'Vabandust, aga sul pole lubatud sisuhaldussüsteemi selle osa juurde pääseda. Kui soovid kellegi teisena sisse logida, tee seda allpool.' PERMDEFAULT: 'Sisesta oma e-posti aadress ja parool sisuhaldussüsteemi ligipääsemiseks.' - PLEASESAVE: 'Palun Salvesta Lehekülg: Antud lehekülge ei uuendatud, kuna seda ei ole veel salvestatud.' PreviewButton: Eelvaade REORGANISATIONSUCCESSFUL: 'Saidipuu korraldati edukalt ümber.' SAVEDUP: Salvestatud. diff --git a/lang/fa_IR.yml b/lang/fa_IR.yml index a9842c599..1d2a6806f 100644 --- a/lang/fa_IR.yml +++ b/lang/fa_IR.yml @@ -99,7 +99,6 @@ fa_IR: PAGETYPE: 'نوع صفحه' PERMAGAIN: 'شما از سیستم مدیریت محتوا خارج شده اید.اگر میخواهید دوباره وارد شوید نام کاربری و رمز عبور خود را در قسمت زیر وارد کنید' PERMALREADY: 'من متاسفم، شما نمی توانید به آن قسمت از سیستم مدیریت محتوا دسترسی پیدا کنید. اگر میخواهید به عنوان شخص دیگری وارد شوید از قسمت زیر تلاش کنید' - PLEASESAVE: 'لطفاً صفحه را ذخیره کنید : این صفحه نمی تواند بروز شود چراکه هنوز ذخیره نشده است.' LoginAttempt: Email: 'آدرس های ایمیل' Member: diff --git a/lang/fi.yml b/lang/fi.yml index 0ff383afa..574a51fc1 100644 --- a/lang/fi.yml +++ b/lang/fi.yml @@ -309,7 +309,6 @@ fi: PERMAGAIN: 'Olet kirjautunut ulos CMS:stä. Jos haluat kirjautua uudelleen sisään, syötä käyttäjätunnuksesi ja salasanasi alla.' PERMALREADY: 'Paihoittelut, mutta et pääse tähän osaan CMS:ää. Jos haluat kirjautua jonain muuna, voit tehdä sen alla.' PERMDEFAULT: 'Valitse tunnistustapa ja syötä tunnistetietosi CMS:ään.' - PLEASESAVE: 'Tätä sivua ei voitu päivittää, koska sitä ei ole vielä tallennettu. Tallenna sivu.' PreviewButton: Esikatselu REORGANISATIONSUCCESSFUL: 'Hakemistopuu järjestettiin uudelleen onnistuneesti.' SAVEDUP: Tallennettu. diff --git a/lang/fo.yml b/lang/fo.yml index c18fd85c6..bbb0a2ef2 100644 --- a/lang/fo.yml +++ b/lang/fo.yml @@ -92,7 +92,6 @@ fo: PERMAGAIN: 'Tú ert blivin útritaður av CMS skipanini. Um tú ynskir at innrita aftur, inntøppa so títt brúkaranavn og loyniorð niðanfyri:' PERMALREADY: 'Tíanverri, tú hevur ikki atgongd til handan partin av CMS skipanini. Um tú ynskir at innrita sum onkur annar, so kann tú gera tað niðanfyri.' PERMDEFAULT: 'Inntøppa tygara teldupost og loyniorð fyri at fáa atgongd til CMS skipanina.' - PLEASESAVE: 'Vinarliga goym síðuna: Hendan síðan kundi ikki blíva dagført, tí at hon er ikki goymd enn.' LoginAttempt: Email: 'Teldupostur' IP: 'IP adressa' diff --git a/lang/fr.yml b/lang/fr.yml index 01e9c31d7..2b791fc0c 100644 --- a/lang/fr.yml +++ b/lang/fr.yml @@ -301,7 +301,6 @@ fr: PERMAGAIN: 'Vous avez été déconnecté du CMS. Si vous voulez vous reconnecter, entrez un nom d''utilisateur et un mot de passe ci-dessous.' PERMALREADY: 'Désolé, mais vous ne pouvez pas accéder à cette partie du CMS. Si vous voulez changer d''identité, faites le ci-dessous' PERMDEFAULT: 'Saisissez votre adresse de courriel et votre mot de passe pour accéder au CMS.' - PLEASESAVE: 'Enregistrez la page s’il vous plaît : elle ne pouvait pas être mise à jour car elle n’avait pas encore été sauvegardée.' PreviewButton: Aperçu REORGANISATIONSUCCESSFUL: 'L’arbre du site a été bien réorganisé.' SAVEDUP: Enregistré. diff --git a/lang/gl_ES.yml b/lang/gl_ES.yml index 1ed449b9c..b61335747 100644 --- a/lang/gl_ES.yml +++ b/lang/gl_ES.yml @@ -162,7 +162,6 @@ gl_ES: PERMAGAIN: 'Non tes unha sesión válida no CMS. Se queres volver entrar, insire o nome de usuario e contrasinal a continuación.' PERMALREADY: 'Sintoo, pero non podes acceder a esta parte do CMS. Se queres iniciar sesión con outras credenciais, faino a continuación' PERMDEFAULT: 'Escolle un método de autenticación e insire as túas credenciais para acceder o CMS.' - PLEASESAVE: 'Por favor Garda Páxina: Esta páxina podería non ser actualizada porque inda non foi gardada.' VersionUnknown: descoñecido LoginAttempt: Email: 'Enderezo Correo-e' diff --git a/lang/he_IL.yml b/lang/he_IL.yml index d1a558ca1..447d9e7ab 100644 --- a/lang/he_IL.yml +++ b/lang/he_IL.yml @@ -67,7 +67,6 @@ he_IL: PERMAGAIN: 'התנתקת מהמערכת. לחיבור מחדש נא להזין שם וסיסמה' PERMALREADY: 'צר לנו, אך לא תוכל לגשת לחלק זה של מערכת ניהול התוכן. אם ברצונך להתחבר למערכת בתור משתמש אחר נא להשתמש בתיבה בעמוד זה' PERMDEFAULT: 'נא לבחור בשיטת וידוא והזן פרטיך למערכת' - PLEASESAVE: 'נא לשמור עמוד זה. העמוד לא עודכן מכיוון ולא עודכן.' Member: BUTTONCHANGEPASSWORD: 'שנה סיסמא' BUTTONLOGIN: 'התחבר' diff --git a/lang/hr.yml b/lang/hr.yml index cfd588b8b..e557487b5 100644 --- a/lang/hr.yml +++ b/lang/hr.yml @@ -75,7 +75,6 @@ hr: PERMAGAIN: 'Odjavili ste se sa CMS-a. Želite li se ponovno prijaviti, upišite korisničko ime i lozinku' PERMALREADY: 'Nažalost, ne možete pristupiti tom dijelu CMS-a. Želite li se prijaviti kao netko drugi, učinite to ispod' PERMDEFAULT: 'Odaberite metodu autorizacije te upišite svoje podatke za pristup CMS-u.' - PLEASESAVE: 'Molim spremite stranicu: Nemože biti ažurirano dok nije spremljeno.' Member: BUTTONCHANGEPASSWORD: 'Promjeni lozinku' BUTTONLOGIN: 'Prijavi' diff --git a/lang/hu.yml b/lang/hu.yml index a250aa38a..d703622e0 100644 --- a/lang/hu.yml +++ b/lang/hu.yml @@ -71,7 +71,6 @@ hu: PERMAGAIN: 'Kiléptetésre kerültél a CMS-ből. Ha újra be szeretnél lépni, add meg alább a felhasználóneved és jelszavad.' PERMALREADY: 'Nincs jogosultságod a CMS ezen részének megtekintéséhez. Ha be szeretnél jelentkezni más felhasználóként, lejjebb megteheted.' PERMDEFAULT: 'A CMS- be való belépéshez, kérünk válassz egy azonosítási módot, és írd be az azonosítási infomációkat.' - PLEASESAVE: 'Kérjük, mentsd el az oldalt: az oldalt nem lehetett frissíteni, mivel még nem került elmentésre.' Member: BUTTONCHANGEPASSWORD: 'Jelszó megváltoztatása' BUTTONLOGIN: 'Bejelentkezés' diff --git a/lang/it.yml b/lang/it.yml index e5717f488..e9512838e 100644 --- a/lang/it.yml +++ b/lang/it.yml @@ -297,7 +297,6 @@ it: PERMAGAIN: 'Sei stato disconnesso dal CMS. Se desideri autenticarti nuovamente, inserisci qui sotto nome utente e password.' PERMALREADY: 'Siamo spiacenti, ma non puoi accedere a questa sezione del CMS. Se desideri autenticarti come qualcun altro, fallo qui sotto.' PERMDEFAULT: 'Inserisci il tuo indirizzo email e password per accedere al CMS.' - PLEASESAVE: 'Per favore salva la pagina: La stessa potrebbe non venire aggiornata se non si provvede quanto prima a salvarla.' PreviewButton: Anteprima REORGANISATIONSUCCESSFUL: 'Albero del sito riorganizzato con successo.' SAVEDUP: Salvato. diff --git a/lang/ja.yml b/lang/ja.yml index 5bfdafea2..43b9104b7 100644 --- a/lang/ja.yml +++ b/lang/ja.yml @@ -301,7 +301,6 @@ ja: PERMAGAIN: 'ログアウトしました。再度ログインする場合は下にユーザー名とパスワードを入力してください。' PERMALREADY: '申し訳ございません。ご指定になられたCMSの箇所にはアクセスいただけません。別ユーザーとしてログインをされたい場合は、下記より行えます。' PERMDEFAULT: '認証方法を選択し、CMSにアクセスするために利用する認証情報を入力してください。' - PLEASESAVE: '保存してください: 保存してないため更新できません。' PreviewButton: プレビュー REORGANISATIONSUCCESSFUL: 'サイトツリーの再編集に成功しました。' SAVEDUP: 保存済み diff --git a/lang/mi.yml b/lang/mi.yml index 3ae9f9cc9..cd4052176 100644 --- a/lang/mi.yml +++ b/lang/mi.yml @@ -303,7 +303,6 @@ mi: PERMAGAIN: 'Kua takiputaina atu koe i te CMS. Ki te pīrangi koe ki te takiuru atu anō, tāurutia tētahi ingoa kaiwhakamahi me te kupuhipa i raro.' PERMALREADY: 'Aroha mai, kāore e taea te whakauru i tērā wāhanga o te CMS. Ki te pīrangi koe ki te takiuru atu mā tētahi atu ingoa, whakamahia ki raro nei.' PERMDEFAULT: 'Whiriwhiria tētahi aratuka motuhēhēnga me te tāuru i ō taipitopito tuakiri ki te uru ki te CMS.' - PLEASESAVE: 'Tiaki Whārangi: Kāore i taea tēnei whārangi te whakahōu nā te mea kāore anō kia tiakina.' PreviewButton: Arokite REORGANISATIONSUCCESSFUL: 'Kua momoho te whakaraupapa anō i te rākau pae' SAVEDUP: Kua Tiakina diff --git a/lang/nb.yml b/lang/nb.yml index 91935a3b0..ad128078d 100644 --- a/lang/nb.yml +++ b/lang/nb.yml @@ -123,6 +123,7 @@ nb: INVALID_REQUEST: 'Ugyldig forespørsel' DropdownField: CHOOSE: (Velg) + CHOOSESEARCH: '(Velg eller søk)' EmailField: VALIDATION: 'Vennligst skriv inn en epostadresse' Enum: @@ -170,6 +171,7 @@ nb: TEXT2: 'lenke for nullstilling av passord' TEXT3: for Form: + CSRF_FAILED_MESSAGE: "Det ser ut til å ha oppstått et teknisk problem. Vennligst trykk på tilbakeknappen, oppdater nettsiden og prøv på nytt." FIELDISREQUIRED: '{name} er påkrevet' SubmitBtnLabel: Utfør VALIDATIONCREDITNUMBER: 'Vennligst sjekk at du har skrevet inn {number} korrekt kortnummer' @@ -192,7 +194,7 @@ nb: FilterBy: 'Filtrer på ' Find: Finn LEVELUP: 'Opp ett nivå' - LinkExisting: 'Eksisterende lenke' + LinkExisting: 'Knytt til eksisterende' NewRecord: 'Ny %s' NoItemsFound: 'Ingen elementer ble funnet' PRINTEDAT: 'Skrevet ut ved' @@ -264,6 +266,8 @@ nb: FROMWEB: 'Fra internett' FindInFolder: 'Finn i mappe' IMAGEALT: 'Alternativ tekst (alt)' + IMAGEALTTEXT: 'Alternativ tekst (alt) - blir brukt hvis bildet ikke kan vises' + IMAGEALTTEXTDESC: 'Blir vist til skjermlesere eller hvis bildet ikke kan vises' IMAGEDIMENSIONS: Dimensjoner IMAGEHEIGHTPX: Høyde IMAGETITLE: 'Titteltekst (tooltip) - for tilleggsinformasjon om bildet' @@ -465,6 +469,7 @@ nb: ERRORPASSWORDPERMISSION: 'Du må logge inn for å bytte passord.' LOGGEDOUT: 'Du har blitt logget ut. Hvis du vil logge inn igjen, så gjør du det under.' LOGIN: 'Logg inn' + LOSTPASSWORDHEADER: 'Mistet passord' NOTEPAGESECURED: 'Den siden er sikret. Skriv inn gyldig innloggingsinfo så kommer du inn.' NOTERESETLINKINVALID: '

Lenken for å nullstille passordet er ugyldig eller utgått.

Du kan kreve en ny her eller endre passordet etter at du har logget inn.

' NOTERESETPASSWORD: 'Skriv inn epostadressen din og vi vil sende deg en lenke som nullstiller passordet.' diff --git a/lang/nl.yml b/lang/nl.yml index a427ab7c6..3f6e59cb1 100644 --- a/lang/nl.yml +++ b/lang/nl.yml @@ -307,7 +307,6 @@ nl: PERMAGAIN: 'U bent uitgelogd uit het CMS. Als U weer wilt inloggen vul dan uw gebruikersnaam en wachtwoord hier beneden in.' PERMALREADY: 'Helaas, dat deel van het CMS is niet toegankelijk voor U. Hieronder kunt U als iemand anders inloggen.' PERMDEFAULT: 'Geef uw e-mailadres en wachtwoord voor toegang tot het CMS.' - PLEASESAVE: 'Deze pagina kon niet bijgewerkt worden, omdat deze nog niet is bewaard.' PreviewButton: Voorbeeld REORGANISATIONSUCCESSFUL: 'Menu-indeling is aangepast' SAVEDUP: Opgeslagen diff --git a/lang/pl.yml b/lang/pl.yml index 6be67298d..70080b910 100644 --- a/lang/pl.yml +++ b/lang/pl.yml @@ -305,7 +305,6 @@ pl: PERMAGAIN: 'Zostałeś wylogowany z CMSa. Jeśli chcesz zalogować się ponownie, wpisz login i hasło poniżej.' PERMALREADY: 'Niestety nie masz dostępu do tej części CMS. Jeśli chcesz zalogować się jako ktoś inny, zrób to poniżej' PERMDEFAULT: 'Proszę wybrać metodę identyfikacji i wpisać swoje dane, aby uruchomić CMSa.' - PLEASESAVE: 'Proszę zapisać stronę. Ta strona nie mogła zostać uaktualniona, ponieważ nie została jeszcze zapisana.' PreviewButton: Podgląd REORGANISATIONSUCCESSFUL: 'Pomyślnie zreorganizowano drzewo serwisu.' SAVEDUP: Zapisano. diff --git a/lang/pt_BR.yml b/lang/pt_BR.yml index 01eeb1274..3f46cc4ae 100644 --- a/lang/pt_BR.yml +++ b/lang/pt_BR.yml @@ -123,7 +123,6 @@ pt_BR: PERMAGAIN: 'Você foi desconectado do CMS. Se você quiser entrar novamente, digite um nome de usuário e senha abaixo.' PERMALREADY: 'Sinto muito, mas você não pode acessar essa parte do CMS. Se você quiser entrar como outra pessoa, faça-o abaixo.' PERMDEFAULT: 'Por favor, entre com seu e-mail e senha para entrar no sistema.' - PLEASESAVE: 'Por favor salve a página: Esta página não pode ser atulizada porque ainda não foi salva.' LoginAttempt: Email: 'Endereço de E-mail' IP: 'Endereço IP' diff --git a/lang/ru.yml b/lang/ru.yml index 476f21082..81ebed4e5 100644 --- a/lang/ru.yml +++ b/lang/ru.yml @@ -306,7 +306,6 @@ ru: PERMAGAIN: 'Вы вышли из Системы Управления Сайтом. Если Вы хотите войти снова, введите внизу имя пользователя и пароль.' PERMALREADY: 'Извините, у вас нет доступа к этому разделу Системы Управления. Если Вы хотите войти под другой учетной записью, сделайте это ниже' PERMDEFAULT: 'Введите ваши адрес электр. почты и пароль для доступа к системе.' - PLEASESAVE: 'Пожалуйста, сохраните страницу: ее нельзя обновить, т.к. она еще не была сохранена.' PreviewButton: Просмотр REORGANISATIONSUCCESSFUL: 'Древесная структура сайта успешно реорганизована.' SAVEDUP: Сохранено. diff --git a/lang/si.yml b/lang/si.yml index 89652f2c3..92559be01 100644 --- a/lang/si.yml +++ b/lang/si.yml @@ -82,7 +82,6 @@ si: PERMAGAIN: 'ඹබ CMS ඵකෙන් ඉවත් වී ඇත. නැවත ඇතුල් වීමට නම හා මුරපදය යොදන්න' PERMALREADY: 'සමාවන්න ඔබට මෙම කොටස පරිශීලනය කල නොහැක. පහතින් වෙනත් නමකින් ඇතුල් වන්න' PERMDEFAULT: 'හදුනාගැනීමේ ක්රමයක් තෝරා ඹබගේ දත්ත ඇතුල් කරන්න' - PLEASESAVE: 'පිටුව සේව් කරන්න, නැතිනම් මෙම පිටුව යාවත්කාලීන කල නොහැක' Member: BUTTONCHANGEPASSWORD: 'මුර පදය අලුත් කරන්න' BUTTONLOGIN: 'ඇතුල්වන්න' diff --git a/lang/sk.yml b/lang/sk.yml index eefabb1ac..fd9463166 100644 --- a/lang/sk.yml +++ b/lang/sk.yml @@ -307,7 +307,7 @@ sk: PERMAGAIN: 'Boli ste odhlásený' PERMALREADY: 'Je mi ľúto, ale nemáte prístup k tejto časti CMS. Ak sa chcete prihlásiť ako niekto iný, urobte tak nižšie' PERMDEFAULT: 'Vyberte si prosím metódu autentifikácie a zadajte svoje prístupové údaje k CMS.' - PLEASESAVE: 'Prosím uložte stránku: Táto stránka nemôže byť aktualizovaná, lebo ešte nebola uložená.' + PLEASESAVE: 'Uložte stránku, prosím. Táto stránka nemôže byť aktualizovaná, pretože eště nebola uložená.' PreviewButton: Náhľad REORGANISATIONSUCCESSFUL: 'Strom webu bol reorganizovaný úspešne.' SAVEDUP: Uložené. diff --git a/lang/sl.yml b/lang/sl.yml index 8148f10cd..5841b3123 100644 --- a/lang/sl.yml +++ b/lang/sl.yml @@ -238,7 +238,6 @@ sl: PERMAGAIN: 'Odjavili ste se iz CMS-vmesnika. Če se želite ponovno prijaviti, vpišite uporabniško ime in geslo.' PERMALREADY: 'Do tega dela CMS-vmesnika nimate dostopa. Če se želite vpisati z drugim uporabniškim imenom, lahko to storite spodaj' PERMDEFAULT: 'Izberite način avtentikacije in vpišite svoje podatke za dostop do CMS-vmesnika.' - PLEASESAVE: 'Shranite stran: te strani ne morete posodobiti, ker še ni bila shranjena.' PreviewButton: Predogled REORGANISATIONSUCCESSFUL: 'Struktura spletnega mesta je bila uspešno spremenjena.' SAVEDUP: Shranjeno. diff --git a/lang/sr.yml b/lang/sr.yml index 525ba05f0..bc0e81380 100644 --- a/lang/sr.yml +++ b/lang/sr.yml @@ -305,7 +305,6 @@ sr: PERMAGAIN: 'Одјављени сте са CMS-а. Уколико желите да се поново пријавите, унесите корисничко име и лозинку.' PERMALREADY: 'Не можете да приступите овом делу CMS-а. Ако желите да се пријавите као неко други, урадите то испод' PERMDEFAULT: 'Изаберите методу аутентификације и унесите податке за приступ CMS-у.' - PLEASESAVE: 'Сачувајте страну: ова страна не може да буде ажурирана јер још увек није сачувана.' PreviewButton: Претходни преглед REORGANISATIONSUCCESSFUL: 'Стабло сајта је успешно реорганизовано.' SAVEDUP: Сачувано. diff --git a/lang/sr_RS.yml b/lang/sr_RS.yml index 0d3fddca4..c469a5e0e 100644 --- a/lang/sr_RS.yml +++ b/lang/sr_RS.yml @@ -305,7 +305,6 @@ sr_RS: PERMAGAIN: 'Одјављени сте са CMS-а. Уколико желите да се поново пријавите, унесите корисничко име и лозинку.' PERMALREADY: 'Не можете да приступите овом делу CMS-а. Ако желите да се пријавите као неко други, урадите то испод' PERMDEFAULT: 'Изаберите методу аутентификације и унесите податке за приступ CMS-у.' - PLEASESAVE: 'Сачувајте страну: ова страна не може да буде ажурирана јер још увек није сачувана.' PreviewButton: Претходни преглед REORGANISATIONSUCCESSFUL: 'Стабло сајта је успешно реорганизовано.' SAVEDUP: Сачувано. diff --git a/lang/sv.yml b/lang/sv.yml index 1d67a8909..2a4063b83 100644 --- a/lang/sv.yml +++ b/lang/sv.yml @@ -259,7 +259,6 @@ sv: PERMAGAIN: 'Du har blivit utloggad. Om du vill logga in igen anger du dina uppgifter nedan.' PERMALREADY: 'Tyvärr så har du inte tillträde till den delen av CMSet. Om du vill logga in med en annan användare kan du göra det nedan' PERMDEFAULT: 'Var god välj en inloggningsmetod och fyll i dina uppgifter för att logga in i CMSet.' - PLEASESAVE: 'Var god spara sidan. Den kan inte uppdateras för att den har inte sparats ännu.' PreviewButton: Förhandsgranska REORGANISATIONSUCCESSFUL: 'Omorganisationen av sidträdet luyckades.' SAVEDUP: Sparad. diff --git a/lang/th.yml b/lang/th.yml index 5c873f81b..38b5322b5 100644 --- a/lang/th.yml +++ b/lang/th.yml @@ -209,7 +209,6 @@ th: PERMAGAIN: 'คุณได้ออกจากระบบของ CMS แล้ว หากคุณต้องการเข้าสู่ระบบอีกครั้ง กรุณากรอกชื่อผู้ใช้งานและรหัสผ่านของคุณด้านล่าง' PERMALREADY: 'ขออภัย, คุณไม่สามารถเข้าใช้งานในส่วนนี้ของ CMS ได้ หากคุณต้องการเข้าสู่ระบบในชื่ออื่นได้จากด้านล่าง' PERMDEFAULT: 'กรุณาเลือกวิธีการยืนยันตัวบุคคลและกรอกข้อมูลประจำตัวเพื่อเข้าใช้งาน CMS' - PLEASESAVE: 'กรุณาบันทึกหน้าเว็บ หน้าเว็บนี้ยังไม่สามรถอัพเดทข้อมูลได้ เนื่องจากยังไม่ได้ถูกบันทึกข้อมูล' LeftAndMain_Menu_ss: Hello: สวัสดีค่ะ LOGOUT: 'ออกจากระบบ' diff --git a/lang/tr.yml b/lang/tr.yml index 0b6af1398..c28b3d584 100644 --- a/lang/tr.yml +++ b/lang/tr.yml @@ -137,7 +137,6 @@ tr: PERMAGAIN: 'İYS yönetiminden çıkış yaptınız. Eğer tekrar giriş yapmak isterseniz, aşağıya kullanıcı adı ve şifrenizi giriniz.' PERMALREADY: 'Üzgünüm ama İYS''nin bu bölümüne erişim hakkınız yok. Başka bir kullanıcı olarak giriş yapmak istiyorsanız aşağıdan bunu yapabilirsiniz' PERMDEFAULT: 'İYS erişimi için eposta adresinizi ve parolanızı giriniz.e kolaylık sağlama' - PLEASESAVE: 'Lütfen Sayfayı Kaydedin: Bu sayfa henüz kaydedilmediği için güncellenemedi.' PreviewButton: Önizleme SAVEDUP: Kaydedilmiş. LoginAttempt: diff --git a/lang/uk.yml b/lang/uk.yml index 8c8acea57..cf86cf1e1 100644 --- a/lang/uk.yml +++ b/lang/uk.yml @@ -142,7 +142,6 @@ uk: PERMAGAIN: 'Ви вийшли з системи. Якщо Ви хочете повторно ідентифікуватися, введіть дані нижче.' PERMALREADY: 'Вибачте, та Ви не маєте доступу до цієї чатини системи. Якщо Ви хочете ідентифікуватися як хтось інший, зробіть це нижче ' PERMDEFAULT: 'Будь ласка, оберіть метод ідентифікації та введіть дані доступу до системи.' - PLEASESAVE: 'Будь ласка, збережіть сторінку: Ця сторінка не може бути оновлена, бо вона ще не була збережена.' LeftAndMain_Menu_ss: Hello: Привіт LOGOUT: 'Вилогуватися' diff --git a/lang/zh.yml b/lang/zh.yml index d86a43708..9015ddb0f 100644 --- a/lang/zh.yml +++ b/lang/zh.yml @@ -305,7 +305,6 @@ zh: PERMAGAIN: '您已经退出 CMS。如果您想再次登录,请在下面输入用户名和密码。' PERMALREADY: '抱歉,您不能访问 CMS 的这一部分。如果您想以不同的身份登录,请在下面进行操作' PERMDEFAULT: '请选择一种认证方法并输入您的凭据以访问 CMS。' - PLEASESAVE: '请保存页面:不能更新该页面因为它还没有被保存。' PreviewButton: 预览 REORGANISATIONSUCCESSFUL: '重新组织网站地图已成功' SAVEDUP: 已保存。 diff --git a/lang/zh_CN.yml b/lang/zh_CN.yml index 95a94aaf7..eed70ea26 100644 --- a/lang/zh_CN.yml +++ b/lang/zh_CN.yml @@ -73,7 +73,6 @@ zh_CN: PERMAGAIN: '您于CMS的登录已被注销,请在下面输入用户名和密码重新登录。' PERMALREADY: '对不起,您无权登录CMS的这一部分。如果您要用另外的帐号,请在下面登录。' PERMDEFAULT: '请先选择一种验证方法并输入您的权限信息,以登录CMS。' - PLEASESAVE: '请先保存:因为该网页还未保存,所以该页无法更新。' Member: BUTTONCHANGEPASSWORD: '更改密码' BUTTONLOGIN: '登录' diff --git a/lang/zh_TW.yml b/lang/zh_TW.yml index 95b59e25d..2225446a9 100644 --- a/lang/zh_TW.yml +++ b/lang/zh_TW.yml @@ -59,7 +59,6 @@ zh_TW: PERMAGAIN: '您已被登出,請在下面重新登入。' PERMALREADY: '抱歉,您沒有權力使用這個部分。您可以用別的帳號登入。' PERMDEFAULT: '請選擇一個認證方法並登入。' - PLEASESAVE: '請儲存:這個網頁沒有被更新因為尚未被儲存。' Member: BUTTONCHANGEPASSWORD: '更改密碼' BUTTONLOGIN: '登入' diff --git a/model/DataQuery.php b/model/DataQuery.php index 07179f45c..089f275ca 100644 --- a/model/DataQuery.php +++ b/model/DataQuery.php @@ -149,11 +149,13 @@ class DataQuery { } $query = clone $this->query; + $ancestorTables = ClassInfo::ancestry($this->dataClass, true); - // Generate the list of tables to iterate over and the list of columns required by any existing where clauses. - // This second step is skipped if we're fetching the whole dataobject as any required columns will get selected - // regardless. + // Generate the list of tables to iterate over and the list of columns required + // by any existing where clauses. This second step is skipped if we're fetching + // the whole dataobject as any required columns will get selected regardless. if($queriedColumns) { + // Specifying certain columns allows joining of child tables $tableClasses = ClassInfo::dataClassesFor($this->dataClass); foreach ($query->getWhere() as $where) { @@ -163,8 +165,9 @@ class DataQuery { if (!in_array($matches[1], $queriedColumns)) $queriedColumns[] = $matches[1]; } } + } else { + $tableClasses = $ancestorTables; } - else $tableClasses = ClassInfo::ancestry($this->dataClass, true); $tableNames = array_keys($tableClasses); $baseClass = $tableNames[0]; @@ -172,30 +175,26 @@ class DataQuery { // Iterate over the tables and check what we need to select from them. If any selects are made (or the table is // required for a select) foreach($tableClasses as $tableClass) { - $joinTable = false; - // If queriedColumns is set, then check if any of the fields are in this table. + // Determine explicit columns to select + $selectColumns = null; if ($queriedColumns) { + // Restrict queried columns to that on the selected table $tableFields = DataObject::database_fields($tableClass); - $selectColumns = array(); - // Look through columns specifically requested in query (or where clause) - foreach ($queriedColumns as $queriedColumn) { - if (array_key_exists($queriedColumn, $tableFields)) { - $selectColumns[] = $queriedColumn; - } - } - + $selectColumns = array_intersect($queriedColumns, array_keys($tableFields)); + } + + // If this is a subclass without any explicitly requested columns, omit this from the query + if(!in_array($tableClass, $ancestorTables) && empty($selectColumns)) continue; + + // Select necessary columns (unless an explicitly empty array) + if($selectColumns !== array()) { $this->selectColumnsFromTable($query, $tableClass, $selectColumns); - if ($selectColumns && $tableClass != $baseClass) { - $joinTable = true; - } - } else { - $this->selectColumnsFromTable($query, $tableClass); - if ($tableClass != $baseClass) $joinTable = true; } - if ($joinTable) { - $query->addLeftJoin($tableClass, "\"$tableClass\".\"ID\" = \"$baseClass\".\"ID\"", $tableClass, 10) ; + // Join if not the base table + if($tableClass !== $baseClass) { + $query->addLeftJoin($tableClass, "\"$tableClass\".\"ID\" = \"$baseClass\".\"ID\"", $tableClass, 10); } } @@ -687,7 +686,8 @@ class DataQuery { /** * Query the given field column from the database and return as an array. * - * @param String $field See {@link expressionForField()}. + * @param string $field See {@link expressionForField()}. + * @return array List of column values for the specified column */ public function column($field = 'ID') { $fieldExpression = $this->expressionForField($field); diff --git a/model/Versioned.php b/model/Versioned.php index 4dd192d1d..ad01ffc57 100644 --- a/model/Versioned.php +++ b/model/Versioned.php @@ -954,20 +954,24 @@ class Versioned extends DataExtension implements TemplateGlobalProvider { * * If neither of these are set, it checks the session, otherwise the stage * is set to 'Live'. + * + * @param Session $session Optional session within which to store the resulting stage */ - public static function choose_site_stage() { + public static function choose_site_stage($session = null) { + if(!$session) $session = Session::current_session(); + if(isset($_GET['stage'])) { $stage = ucfirst(strtolower($_GET['stage'])); if(!in_array($stage, array('Stage', 'Live'))) $stage = 'Live'; - Session::set('readingMode', 'Stage.' . $stage); + $session->inst_set('readingMode', 'Stage.' . $stage); } if(isset($_GET['archiveDate']) && strtotime($_GET['archiveDate'])) { - Session::set('readingMode', 'Archive.' . $_GET['archiveDate']); + $session->inst_set('readingMode', 'Archive.' . $_GET['archiveDate']); } - if($mode = Session::get('readingMode')) { + if($mode = $session->inst_get('readingMode')) { Versioned::set_reading_mode($mode); } else { Versioned::reading_stage("Live"); diff --git a/tests/control/DirectorTest.php b/tests/control/DirectorTest.php index b99e00971..e071b3785 100644 --- a/tests/control/DirectorTest.php +++ b/tests/control/DirectorTest.php @@ -13,6 +13,9 @@ class DirectorTest extends SapphireTest { public function setUp() { parent::setUp(); + + // Required for testRequestFilterInDirectorTest + Injector::nest(); // Hold the original request URI once so it doesn't get overwritten if(!self::$originalRequestURI) { @@ -52,6 +55,8 @@ class DirectorTest extends SapphireTest { $_SERVER[$header] = $value; } } + + Injector::unnest(); parent::tearDown(); } @@ -404,8 +409,6 @@ class DirectorTest extends SapphireTest { $processor = new RequestProcessor(array($filter)); - $currentProcessor = Injector::inst()->get('RequestProcessor'); - Injector::inst()->registerService($processor, 'RequestProcessor'); $response = Director::test('some-dummy-url'); @@ -430,9 +433,6 @@ class DirectorTest extends SapphireTest { // preCall 'false' will trigger an exception and prevent post call execution $this->assertEquals(2, $filter->postCalls); - - // swap back otherwise our wrapping test execution request may fail in the post processing later - Injector::inst()->registerService($currentProcessor, 'RequestProcessor'); } } diff --git a/tests/core/ConfigTest.php b/tests/core/ConfigTest.php index 6a0edebdc..21ae3bf43 100644 --- a/tests/core/ConfigTest.php +++ b/tests/core/ConfigTest.php @@ -1,6 +1,6 @@ assertEquals(3, Config::inst()->get('ConfigTest_TestNest', 'foo')); + $this->assertEquals(5, Config::inst()->get('ConfigTest_TestNest', 'bar')); + + // Test nest copies data + Config::nest(); + $this->assertEquals(3, Config::inst()->get('ConfigTest_TestNest', 'foo')); + $this->assertEquals(5, Config::inst()->get('ConfigTest_TestNest', 'bar')); + + // Test nested data can be updated + Config::inst()->update('ConfigTest_TestNest', 'foo', 4); + $this->assertEquals(4, Config::inst()->get('ConfigTest_TestNest', 'foo')); + $this->assertEquals(5, Config::inst()->get('ConfigTest_TestNest', 'bar')); + + // Test unnest restores data + Config::unnest(); + $this->assertEquals(3, Config::inst()->get('ConfigTest_TestNest', 'foo')); + $this->assertEquals(5, Config::inst()->get('ConfigTest_TestNest', 'bar')); + } public function testUpdateStatic() { $this->assertEquals(Config::inst()->get('ConfigStaticTest_First', 'first', Config::FIRST_SET), @@ -270,7 +299,7 @@ class ConfigTest extends SapphireTest { } } -class ConfigTest_Config_LRU extends Config_LRU { +class ConfigTest_Config_LRU extends Config_LRU implements TestOnly { public $cache; public $indexing; diff --git a/tests/core/manifest/ConfigStaticManifestTest.php b/tests/core/manifest/ConfigStaticManifestTest.php index 5ee09660e..3709bd384 100644 --- a/tests/core/manifest/ConfigStaticManifestTest.php +++ b/tests/core/manifest/ConfigStaticManifestTest.php @@ -200,4 +200,70 @@ DOC; $this->assertEquals($expectedValue, $statics['config\staticmanifest\NamespaceTest']['db']['value']); } + + public function testParsingMultyStringClass() { + static $tokens = array( + array(T_OPEN_TAG, "parse(); + + $statics = $parser->getStatics(); + + $expected = array( + 'test' => array( + 'access' => T_PRIVATE, + 'value' => array(3) + ) + ); + + $this->assertEquals($expected, $statics[':ss:test2']); + } +} + +class ConfigStaticManifestTest_Parser extends SS_ConfigStaticManifest_Parser implements TestOnly { + public function __construct($tokens) { + $this->path = __FILE__; + $this->tokens = $tokens; + $this->length = count($this->tokens); + $this->pos = 0; + } } diff --git a/tests/forms/uploadfield/UploadFieldTest.php b/tests/forms/uploadfield/UploadFieldTest.php index 058483267..f0c59b77b 100644 --- a/tests/forms/uploadfield/UploadFieldTest.php +++ b/tests/forms/uploadfield/UploadFieldTest.php @@ -730,6 +730,30 @@ class UploadFieldTest extends FunctionalTest { $responseExistsData = json_decode($responseExists->getBody()); $this->assertFalse($responseExists->isError()); $this->assertTrue($responseExistsData->exists); + + // Test that files with invalid characters are rewritten safely and both report exists + // Check that uploaded files can be detected in the root + $tmpFileName = '_test___Upload___Bad.txt'; + $tmpFileNameExpected = 'test-Upload-Bad.txt'; + $response = $this->mockFileUpload('NoRelationField', $tmpFileName); + $this->assertFalse($response->isError()); + $this->assertFileExists(ASSETS_PATH . "/UploadFieldTest/$tmpFileNameExpected"); + // With original file + $responseExists = $this->mockFileExists('NoRelationField', $tmpFileName); + $responseExistsData = json_decode($responseExists->getBody()); + $this->assertFalse($responseExists->isError()); + $this->assertTrue($responseExistsData->exists); + // With rewritten file + $responseExists = $this->mockFileExists('NoRelationField', $tmpFileNameExpected); + $responseExistsData = json_decode($responseExists->getBody()); + $this->assertFalse($responseExists->isError()); + $this->assertTrue($responseExistsData->exists); + + // Test that attempts to navigate outside of the directory return false + $responseExists = $this->mockFileExists('NoRelationField', "../../../../var/private/$tmpFileName"); + $responseExistsData = json_decode($responseExists->getBody()); + $this->assertTrue($responseExists->isError()); + $this->assertContains('File is not a valid upload', $responseExists->getBody()); } protected function getMockForm() { diff --git a/tests/injector/InjectorTest.php b/tests/injector/InjectorTest.php index 06099779c..d5b626b88 100644 --- a/tests/injector/InjectorTest.php +++ b/tests/injector/InjectorTest.php @@ -12,6 +12,24 @@ define('TEST_SERVICES', dirname(__FILE__) . '/testservices'); */ class InjectorTest extends SapphireTest { + protected $nestingLevel = 0; + + public function setUp() { + parent::setUp(); + + $this->nestingLevel = 0; + } + + public function tearDown() { + + while($this->nestingLevel > 0) { + $this->nestingLevel--; + Config::unnest(); + } + + parent::tearDown(); + } + public function testCorrectlyInitialised() { $injector = Injector::inst(); $this->assertTrue($injector->getConfigLocator() instanceof SilverStripeServiceConfigurationLocator, @@ -562,6 +580,64 @@ class InjectorTest extends SapphireTest { $this->assertInstanceOf('TestObject', $injector->get('service')); } + + /** + * Test nesting of injector + */ + public function testNest() { + + // Outer nest to avoid interference with other + Injector::nest(); + $this->nestingLevel++; + + // Test services + $config = array( + 'NewRequirementsBackend', + ); + Injector::inst()->load($config); + $si = Injector::inst()->get('TestStaticInjections'); + $this->assertInstanceOf('TestStaticInjections', $si); + $this->assertInstanceOf('NewRequirementsBackend', $si->backend); + $this->assertInstanceOf('MyParentClass', Injector::inst()->get('MyParentClass')); + $this->assertInstanceOf('MyChildClass', Injector::inst()->get('MyChildClass')); + + // Test that nested injector values can be overridden + Injector::nest(); + $this->nestingLevel++; + Injector::inst()->unregisterAllObjects(); + $newsi = Injector::inst()->get('TestStaticInjections'); + $newsi->backend = new OriginalRequirementsBackend(); + Injector::inst()->registerService($newsi, 'TestStaticInjections'); + Injector::inst()->registerService(new MyChildClass(), 'MyParentClass'); + + // Check that these overridden values are retrievable + $si = Injector::inst()->get('TestStaticInjections'); + $this->assertInstanceOf('TestStaticInjections', $si); + $this->assertInstanceOf('OriginalRequirementsBackend', $si->backend); + $this->assertInstanceOf('MyParentClass', Injector::inst()->get('MyParentClass')); + $this->assertInstanceOf('MyParentClass', Injector::inst()->get('MyChildClass')); + + // Test that unnesting restores expected behaviour + Injector::unnest(); + $this->nestingLevel--; + $si = Injector::inst()->get('TestStaticInjections'); + $this->assertInstanceOf('TestStaticInjections', $si); + $this->assertInstanceOf('NewRequirementsBackend', $si->backend); + $this->assertInstanceOf('MyParentClass', Injector::inst()->get('MyParentClass')); + $this->assertInstanceOf('MyChildClass', Injector::inst()->get('MyChildClass')); + + // Test reset of cache + Injector::inst()->unregisterAllObjects(); + $si = Injector::inst()->get('TestStaticInjections'); + $this->assertInstanceOf('TestStaticInjections', $si); + $this->assertInstanceOf('NewRequirementsBackend', $si->backend); + $this->assertInstanceOf('MyParentClass', Injector::inst()->get('MyParentClass')); + $this->assertInstanceOf('MyChildClass', Injector::inst()->get('MyChildClass')); + + // Return to nestingLevel 0 + Injector::unnest(); + $this->nestingLevel--; + } } diff --git a/tests/model/DataObjectLazyLoadingTest.php b/tests/model/DataObjectLazyLoadingTest.php index a5d4e4691..83bc8263b 100644 --- a/tests/model/DataObjectLazyLoadingTest.php +++ b/tests/model/DataObjectLazyLoadingTest.php @@ -35,6 +35,7 @@ class DataObjectLazyLoadingTest extends SapphireTest { '"DataObjectTest_Team"."ClassName" IS NOT NULL THEN "DataObjectTest_Team"."ClassName" ELSE ' . $db->prepStringForDB('DataObjectTest_Team').' END AS "RecordClassName", "DataObjectTest_Team"."Title" '. 'FROM "DataObjectTest_Team" ' . + 'LEFT JOIN "DataObjectTest_SubTeam" ON "DataObjectTest_SubTeam"."ID" = "DataObjectTest_Team"."ID" ' . 'WHERE ("DataObjectTest_Team"."ClassName" IN ('.$db->prepStringForDB('DataObjectTest_SubTeam').'))' . ' ORDER BY "DataObjectTest_Team"."Title" ASC'; $this->assertEquals($expected, $playerList->sql()); @@ -62,7 +63,8 @@ class DataObjectLazyLoadingTest extends SapphireTest { $expected = 'SELECT DISTINCT "DataObjectTest_Team"."ClassName", "DataObjectTest_Team"."Created", ' . '"DataObjectTest_Team"."LastEdited", "DataObjectTest_Team"."Title", "DataObjectTest_Team"."ID", ' . 'CASE WHEN "DataObjectTest_Team"."ClassName" IS NOT NULL THEN "DataObjectTest_Team"."ClassName" ELSE ' . - $db->prepStringForDB('DataObjectTest_Team').' END AS "RecordClassName" FROM "DataObjectTest_Team" WHERE ' . + $db->prepStringForDB('DataObjectTest_Team').' END AS "RecordClassName" FROM "DataObjectTest_Team" ' . + 'LEFT JOIN "DataObjectTest_SubTeam" ON "DataObjectTest_SubTeam"."ID" = "DataObjectTest_Team"."ID" WHERE ' . '("DataObjectTest_Team"."ClassName" IN ('.$db->prepStringForDB('DataObjectTest_SubTeam').')) ' . 'ORDER BY "DataObjectTest_Team"."Title" ASC'; $this->assertEquals($expected, $playerList->sql()); diff --git a/tests/model/DataQueryTest.php b/tests/model/DataQueryTest.php index 0475cb8a5..09454c044 100644 --- a/tests/model/DataQueryTest.php +++ b/tests/model/DataQueryTest.php @@ -1,11 +1,15 @@ sql() ); } + + public function testDefaultSort() { + $query = new DataQuery('DataQueryTest_E'); + $result = $query->column('Title'); + $this->assertEquals(array('First', 'Second', 'Last'), $result); + } } @@ -148,6 +158,10 @@ class DataQueryTest_B extends DataQueryTest_A { } class DataQueryTest_C extends DataObject implements TestOnly { + + private static $db = array( + 'Title' => 'Varchar' + ); private static $has_one = array( 'TestA' => 'DataQueryTest_A', @@ -171,3 +185,12 @@ class DataQueryTest_D extends DataObject implements TestOnly { 'Relation' => 'DataQueryTest_B', ); } + +class DataQueryTest_E extends DataQueryTest_C implements TestOnly { + + private static $db = array( + 'SortOrder' => 'Int' + ); + + private static $default_sort = '"DataQueryTest_E"."SortOrder" ASC'; +} diff --git a/tests/model/DataQueryTest.yml b/tests/model/DataQueryTest.yml new file mode 100644 index 000000000..70e4b653a --- /dev/null +++ b/tests/model/DataQueryTest.yml @@ -0,0 +1,10 @@ +DataQueryTest_E: + query1: + Title: 'Last' + SortOrder: 3 + query2: + Title: 'First' + SortOrder: 1 + query3: + Title: 'Second' + SortOrder: 2 diff --git a/tests/model/VersionedTest.php b/tests/model/VersionedTest.php index 7b64a56c5..bb8d67b04 100644 --- a/tests/model/VersionedTest.php +++ b/tests/model/VersionedTest.php @@ -590,6 +590,42 @@ class VersionedTest extends SapphireTest { Versioned::set_reading_mode($originalMode); } + + /** + * Tests that reading mode persists between requests + */ + public function testReadingPersistent() { + $session = new Session(array()); + + // Set to stage + Director::test('/?stage=Stage', null, $session); + $this->assertEquals( + 'Stage.Stage', + $session->inst_get('readingMode'), + 'Check querystring changes reading mode to Stage' + ); + Director::test('/', null, $session); + $this->assertEquals( + 'Stage.Stage', + $session->inst_get('readingMode'), + 'Check that subsequent requests in the same session remain in Stage mode' + ); + + // Test live persists + Director::test('/?stage=Live', null, $session); + $this->assertEquals( + 'Stage.Live', + $session->inst_get('readingMode'), + 'Check querystring changes reading mode to Live' + ); + Director::test('/', null, $session); + $this->assertEquals( + 'Stage.Live', + $session->inst_get('readingMode'), + 'Check that subsequent requests in the same session remain in Live mode' + ); + + } }