diff --git a/docs/en/02_Developer_Guides/05_Extending/How_Tos/03_Track_member_logins.md b/docs/en/02_Developer_Guides/05_Extending/How_Tos/03_Track_member_logins.md index cf2eaa688..c3a00d601 100644 --- a/docs/en/02_Developer_Guides/05_Extending/How_Tos/03_Track_member_logins.md +++ b/docs/en/02_Developer_Guides/05_Extending/How_Tos/03_Track_member_logins.md @@ -37,7 +37,7 @@ explicitly logging in or by invoking the "remember me" functionality. DB::query(sprintf( 'UPDATE "Member" SET "LastVisited" = %s, "NumVisit" = "NumVisit" + 1 WHERE "ID" = %d', - DB::getConn()->now(), + DB::get_conn()->now(), $this->owner->ID )); } diff --git a/docs/en/02_Developer_Guides/09_Security/04_Secure_Coding.md b/docs/en/02_Developer_Guides/09_Security/04_Secure_Coding.md index 29bb57bd5..974791c93 100644 --- a/docs/en/02_Developer_Guides/09_Security/04_Secure_Coding.md +++ b/docs/en/02_Developer_Guides/09_Security/04_Secure_Coding.md @@ -27,7 +27,7 @@ come from user input. Example: :::php - $records = DB::preparedQuery('SELECT * FROM "MyClass" WHERE "ID" = ?', array(3)); + $records = DB::prepared_query('SELECT * FROM "MyClass" WHERE "ID" = ?', array(3)); $records = MyClass::get()->where(array('"ID" = ?' => 3)); $records = MyClass::get()->where(array('"ID"' => 3)); $records = DataObject::get_by_id('MyClass', 3); @@ -48,7 +48,7 @@ Parameterised updates and inserts are also supported, but the syntax is a little )) ->assignSQL('"Created"', 'NOW()') ->execute(); - DB::preparedQuery( + DB::prepared_query( 'INSERT INTO "MyClass" ("Name", "Position", "Age", "Created") VALUES(?, ?, GREATEST(0,?,?), NOW())' array('Daniel', 'Accountant', 24, 28) ); @@ -100,7 +100,7 @@ and [datamodel](/developer_guides/model) for ways to parameterise, cast, and con * `SQLQuery` * `DB::query()` -* `DB::preparedQuery()` +* `DB::prepared_query()` * `Director::urlParams()` * `Controller->requestParams`, `Controller->urlParams` * `SS_HTTPRequest` data diff --git a/docs/en/04_Changelogs/3.2.0.md b/docs/en/04_Changelogs/3.2.0.md index 1ff719cfd..93daa8943 100644 --- a/docs/en/04_Changelogs/3.2.0.md +++ b/docs/en/04_Changelogs/3.2.0.md @@ -1,60 +1,200 @@ -# 3.2.0 (unreleased) +# 3.2.0 -## Overview +## Contents -### Framework +* [Major Changes](#major-changes) +* [Removed API](#deprecated-classesmethods-removed) +* [New API](#new-and-changed-api) +* [Bugfixes](#bugfixes) +* [Upgrading Notes](#upgrading-notes) - * Minimum PHP version raised to 5.3.3 - * `DataObject::validate()` method visibility changed to public - * `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 `
` 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` - * Implementation of new "Archive" concept for page removal, which supercedes "delete". Where deletion removed - pages only from draft, archiving removes from both draft and live simultaneously. - * Support for multiple HtmlEditorConfigs on the same page. - * Most of the `Image` manipulation methods have been renamed - * New `Image` methods `CropWidth` and `CropHeight` added - * 'Max' versions of `Image` methods introduced to prevent up-sampling +## Major changes -#### Deprecated classes/methods removed +* 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 - * `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. +## 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()`, +* `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 +* `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. +* `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. - * Object::singleton() method for better type-friendly singleton generation +* 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 -### CMS +## New and changed API - * `SearchForm::getSearchQuery` no longer pre-escapes search keywords and must be cast in your template +* Implementation of a parameterised query framework eliminating the need to manually escape variables for + use in SQL queries. This has been integrated into nearly every level of the database ORM. +* Refactor of database connectivity classes into separate components linked together through dependency injection +* Refactor of `SQLQuery` into separate objects for each query type: `SQLQuery`, `SQLDelete`, `SQLUpdate` and `SQLInsert` +* PDO is now a standard connector, and is available for all database interfaces +* `DataObject::validate()` method visibility changed to public +* `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 `
` 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` -## Changelog +## Bugfixes -### CMS +* 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. + +## Upgrading Notes ### DataObject::validate() method visibility changed to public @@ -63,6 +203,7 @@ The visibility of `DataObject::validate()` has been changed from `protected` to Any existing classes that currently set this as `protected` should be changed like in this example: + ::php class MyDataClass extends DataObject { ... @@ -72,13 +213,17 @@ this example: ... } + ### 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` @@ -88,6 +233,7 @@ To display a warning before overwriting a file: Via config: + ::yaml Upload: # Replace an existing file rather than renaming the new one. @@ -96,12 +242,15 @@ Via config: # 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 @@ -130,35 +279,160 @@ 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()`. -### Bugfixes - * Migration of code to use new parameterised framework +### API: Removed URL routing by controller name -### Framework +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. - * Implementation of a parameterised query framework eliminating the need to manually escape variables for - use in SQL queries. This has been integrated into nearly every level of the database ORM. - * Refactor of database connectivity classes into separate components linked together through dependency injection - * Refactor of `SQLQuery` into separate objects for each query type: `SQLQuery`, `SQLDelete`, `SQLUpdate` and `SQLInsert` - * Rename of API methods to conform to coding conventions - * PDO is now a standard connector, and is available for all database interfaces - * Additional database and query generation tools +Please access any custom controllers exclusively through self-defined +[routes](/reference/director). For controllers extending `Page_Controller`, +simply use the provided page URLs. -## 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. + :::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 +
+ + +Now: + + + :::html + +
+ + + +#### Namespaced FormField ID's + +Form Field ID values will now be namespaced with the parent form ID. + +Before: + + + :::html +
+ + +Now: + + + :::html +
+ + +#### 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 +
+ + + +After: + + + :::html +
+ + +#### 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 -## Upgrading ### Update code that uses SQLQuery @@ -177,6 +451,7 @@ although now a new SQLDelete object should be created from the original SQLQuery Before: + :::php setDelete(true); $query->execute(); + After: + :::php setWhere(array('"SiteTree"."ShowInMenus"' => 0)); $query->execute(); + Alternatively: + :::php toDelete(); $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 @@ -225,121 +505,214 @@ As a result of this upgrade there are now very few cases where `Convert::raw2sql Examples of areas where queries should be upgraded are below: -1. #### Querying the database directly through DB, including non-SELECT queries +#### 1. Querying the database directly through DB, including non-SELECT queries - Before: +Before: - :::php - where('"Name" = \''.Convert::raw2sql($name).'\''); - $list = DataList::create('Banner')->where(array( - '"ParentID" IS NOT NULL', - '"Title" = \'' . Convert::raw2sql($title) . '\'' - ); - After: +#### 2. Querying the database through `DataList`, `DataQuery`, `SQLQuery`, and `DataObject` - :::php - $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 + :::php + where('"Name" = \''.Convert::raw2sql($name).'\''); + $list = DataList::create('Banner')->where(array( + '"ParentID" IS NOT NULL', + '"Title" = \'' . Convert::raw2sql($title) . '\'' + ); - User code that assumes parameterless queries will likely fail, and need to be - updated to handle this case properly. - Before: +After: - :::php - setFrom('"SiteTree"') - ->setWhere(array("\"SiteTree\".\"Title\" LIKE '" . Convert::raw2sql($argument) . "'")); + :::php + sql(); - $sql = preg_replace('/LIKE \'(.+)\'/', 'LIKE \'%${1}%\'', $sql); // Adds %% around the argument + $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 + ); - // Pass new query to database connector - DB::query($sql); - After: +#### 3. Interaction with `DataList::sql()`, `DataQuery::sql()`, `SQLQuery::sql()`, or `SQLQuery::getJoins()` methods - :::php - setFrom('"SiteTree"') - ->setWhere(array('"SiteTree"."Title" LIKE ?' => $argument)); +User code that assumes parameterless queries will likely fail, and need to be +updated to handle this case properly. - // Inspect elements of the query - $sql = $query->sql($parameters); - foreach($parameters as $key => $value) { - // Adds %% around arguments - $parameters[$key] = "%{$value}%"; +Before: + + + :::php + 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 + 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 + 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 `SQLQuery::getWhere()` method + +As all where conditions are now parameterised, the format of the results returned by `SQLQuery::getWhere()` +will not always equate with that in FrameWork 3.1. Once this would be a list of strings, what will +now be returned is a list of conditions, each of which is an associative array mapping the +condition string to a list of parameters provided. + +Before: + + + :::php + getWhere(); + $new = array(); + foreach($conditions as $condition) { + if(preg_match('/\"Count\" = (?\d+)/', $condition, $matches)) { + $condition = '"Count" = '.($matches['count'] + 1); } + $new[] = $condition; + } + $query->setWhere($new); - // 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). +After: - E.g. - :::php - 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); - $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. ### Update code that interacts with the DB schema +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 consistent with 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. @@ -350,6 +723,7 @@ interact with it via `DB::get_schema` instead of `DB::get_conn` (previously name Before: + :::php endSchemaUpdate(); + After: + :::php `Fit` - * `CroppedImage` -> `Fill` - * `PaddedImage` -> `Pad` - * `SetSize` -> `Pad` - * `SetWidth` -> `ScaleWidth` - * `SetHeight` -> `ScaleHeight` - -### Other - - * 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` -> `createField` - - `createTable` -> `createTable` - - `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. diff --git a/docs/en/04_Changelogs/rc/3.2.0.md b/docs/en/04_Changelogs/rc/3.2.0.md deleted file mode 100644 index 2f6f57909..000000000 --- a/docs/en/04_Changelogs/rc/3.2.0.md +++ /dev/null @@ -1,146 +0,0 @@ -# 3.2.0 (unreleased) - -## Overview - -### CMS - - * 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) - -### Framework - - * 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. - -## Details - -### 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: - - - Now: - -
- -#### Namespaced FormField ID's - -Form Field ID values will now be namespaced with the parent form ID. - - Before: -
- - Now: -
- -#### 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: -
- - - After: -
- -#### 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. - - :::yaml - # mysite/config/_config.yml - - Injector: - FormTemplateHelper: - class: FormTemplateHelper_Pre32 - -### Further Changes - - * Removed `Member.LastVisited` and `Member.NumVisits` properties, see [Howto: Track Member Logins](doc.silverstripe.org/framework/en/trunk/howto/track-member-logins) to restore functionality as custom code \ No newline at end of file