DataObject::validate() is currently set to protected, but this means
you can't call validate() from outside the context of itself unless
you overload the method to use a public visibility and then call
parent::validate()
As it would turn out, most classes that overload this method already
set the visibility to public, so it would make sense the parent matches
that as well.
If we want DataObject->validate() to be used instead of
the form layer, we should allow for validation errors
to be passed through unchanged to the controller layer
so we can present them to the user. The context of
which class is written should be apparent from the stacktrace
of the exception.
- Document the format for descriptor arrays
- Implement the behaviour that developers have come to expect for
string descriptors of indexes
- Add test for handling of unique indexes (MySQL & sqlite3)
- Resolve#2403
Versioned needs to convert unique indexes to non-unique for its suffixed
tables, such as Foo_Live and Foo_versions. Because DataObject accepts
string descriptors such as array('UniqIDX' => 'unique (Uniq)') as well
as array-based descriptors, Versioned needs to recognize string
descriptors. This patch accomplishes that. Before, Versioned would fail
to convert string-described indexes to non-unique, resulting in run-time
errors when creating a new version of an object.
This ensures that the correct stage is selected, even if the request
does not come through the model as controller system. This fixes an
issue where custom controllers would always be on the "Stage" stage.
If more than two $from were added through SQLQuery->addFrom(),
the getOrderedJoins() comparison kicks in. It assumes all $from
parts are in array notation, which isn't always the case.
For legacy reasons, and because we don't have full API support,
you can still add literal joins through addFrom('INNER JOIN ...').
On PHP 5.3, the ordering comparison still works because it
allows array access in strings, with string rather than numeric indexes.
Thankfully that's no longer supported in PHP 5.4.
DataQuery::initialiseQuery() will add a default sort to a query,
and when calling up an aggregate it will make a query like this
which doesn't make sense:
SELECT MAX("LastEdited") FROM "Member" ORDER BY "ID"
In this case there is no need to add the ORDER BY, and it will
break databases like MSSQL in cases such as
GenericTemplateGlobalProvider
which provides a default List() function for adding aggregates
into SSViewer template cacheblocks.
If we add a limit, however, then it does make sense:
SELECT MAX("LastEdited") FROM "Member" ORDER BY "ID" LIMIT 10
This fixes SQLQuery::aggregate() to NOT add an ORDER BY to an
aggregate call if there is no limit.
If multiple image manipulations are performend the resulting cached image is stored in assets/_resampled because the cached version of the image has no ParentID, which cacheFilename needs to set the correct path.
Not a security issue as such, since the user input is sanitized
before being used in Versioned->augmentSQL(). But it shouldn't
reach the session state either, since that's commonly assumed
to be sanitized data, and it leaves unnecessary room for error.
strtotime() has fairly loose validation rules around dates,
but its a good "first line of defence".
Doesn't have much effect in practice, because charset and collation
are already hardcoded on an ALTER TABLE level (field definitions),
which take priority. Since most MySQL installs will still default
to a latin1 encoding, this propagates to the table though,
confusing devs and in some cases causing wrong data.
Example: A MSSQL->MySQL DB migration tool used the table metadata
to determine the charset, creating encoding issues.
In terms of hardcoding, we don't really support anything other than UTF8,
and the field-level settings are already hardcoded.
We should probably remove the field-specific settings and rely
on the DB defaults, but that's a sensitive API change
(need to set on existing DBs during upgrade).
Allow DataList::limit() to take a null value to remove the limit.
Added tests for limit(). Note the one failure, currently the ORM doesn't support unlimited values with an offset.
The function "first" on ArrayList uses the PHP function "reset", which
returns false if there aren't any elements in the array. Two functions
inside ArrayList use this function, "canFilterBy" and "byID". I've
changed these functions to catch the possibility of a false return from
first().
Commit 964b3f2 fixed an issue where dbObject was returning casting helpers for
fields that were not actually DB objects, but had something in $casting config.
However, because dbObject was no longer calling DataObject->castingHelper, this
exposed a bug that the underlying function db($fieldName) was not returning
field specs for the base fields that are created by SS automatically on all
DataObjects (i.e. Created, LastEdited, etc).
This commit fixes the underlying issue that DataObject->db($fieldName) should
return the field specs for *all* DB fields like its documentation says it will,
including those base fields that are automatically created and do not appear in
$db.
Since ViewableData was returning a casting helper for Link, but DataObject was
only using $this->$fieldname to set values on that casting helper, you could
not use <% if Link %> (or <% if $Link %>) in your templates because Link is not
a field, and thus had no value to be set on the casting helper, causing
hasValue to think that there was no value. Since DataObject->dbObject says that
"it only matches fields and not methods", it seems safe to have it call db(..)
to get the field spec, and not call ViewableData->castingHelper at all.
SQLQuery->setLimit(0, 99) should result in "SELECT ... LIMIT 0 OFFSET 1".
In fact it does "SELECT ..." without a LIMIT clause at all,
which is unexpected. This is regardless of the $offset value.
In large sites this can take a very long time, drastically slowing down the CMS
admin. Even though the versions will then need to be queried individually,
this is still significantly faster than loading hundreds of thousands of
version numbers in one query and populating the cache array.
This caused problems when duplicate() was used in the CMS UI
to duplicate a SiteTree object. Since every object of this type
has a ParentID relation, it copied this empty relation into
new "ghost page".
See https://github.com/silverstripe/silverstripe-cms/issues/689
This references silverstripe/silverstripe-translatable#113
For that issue, we needed to have the DataQuery as the second parameter to
DataQuery's augmentSQL call. Fortunately, DataQuery was already passing this
argument. However, where the function was defined in DataExtension, the
argument was not present. Thus, subclasses of DataExtension could not add the
parameter to their function signature if they were running in PHP strict mode
because PHP will complain that the signatures don't match.
This resulted in Object extensions not working for it, and methods not existing
where they should have. It also resulted in poor error messages appearing when
thrown from Object since $this->class was empty since the constructor was never
called in Object.
Cleanup of framework's use of @package and @subpackage labels and additional of labels for classes missing packages.
Moved all GridField related components to the one name.
Countless spelling fixes, grammar for other comments.
Link ClassName references in file headers.
When DataObject::update() is run with relation fields and the relationship
is new the relationship ID was not set on the DataObject. This patch fixes
this. Fixes issue 6195 in open.silverstripe.org.
With a many to many relation, e.g. SiteTree_MyRelation, and listing
them in your template then adding ?archiveDate=x in the URL, a SQL
error is shown because Versioned::augmentSQL() tries to query the
non-existent table "SiteTree_MyRelation_versions" assuming there's
versioning setup, but there isn't.
$forceWrite was being ignored because it was tested in a part of the
code that is reachable if and only if there are changes to the object.
This patch adds an additional test to correct that logic error.
Also, refrain from needlessly checking for changes when $forceWrite is
true.
Fixes#1687
API: CompositeDBField::setValue() may be passed an object as its second argument, in addition to array.
These changes provide a 15% - 20% performance improvement, and as such justify an small API change in the 3.0 branch. It will likely affect anyone who has created their own composite fields, which is fortunately not all that common.
Rendering potentially 1000s of nodes can exceed the CPU and memory constraints
of a normal PHP process, as well as the rendering capabilities of browsers.
Set a hard maximum for the renderable nodes, deferring to a "show as list" action
in the main CMS tree. For TreeDropdownField, we don't have the list fallback option,
so ask the user to search for the node title instead.
Also makes both the "node_threshold_total" and "node_threshold_leaf" values configurable
This breaks databases like MSSQL which don't allow an ORDER BY with
a subquery at the same time. DELETE queries don't need to be ordered,
so we can safely remove the default.
Otherwise aggregate queries through DataQuery->column() fail unless the
passed in field is specifically quoted already. This fixes ManyManyListTest->testRemoveAll()
This should allow custom meta tags to be saved in the admin panel.
Also, if you use html5 video or audio the source sub tag
would have been stripped two these are all the tags that may be empty
and are valid.
- Renamed $minNodeCount to more accurate $nodeCountThreshold
- The $minNodeCount attribute wasn't properly respected
during actual querying, so SilverStripe would always traverse
the entire tree (and load all objects into memory),
before then marking nodes as "unexpanded", which prevents
them from actually being rendered.
- Fixes nodes on search results to be expanded by default
- Fixes nodes on search results to correctly ajax-expand
This means that you dont have to worry about casting it
as HTMLText again when using the result in a template or other context
However in some situations code might be assuming it can
check with is_string, in which case you now need to use instanceof HTMLText
Extracted common code out to SS_HTMLValue and made abstract, then
put HTML 4 specific code in SS_HTML4Value. Its now possible to
replace HTMLValue with one designed for HTML 5 or XHTML
Requires a code change from new SS_HTMLValue to
Injector::inst()->create(HTMLValue)
This is to fix a bug that caused CheckboxSetFields to throw an error
when trying to call this function when editing a new DataObject. This
occurred when using the advancedworkflow module.
Thanks to simonwelsh for the majority of the work on this fix.
Lazy loading no longer loads fields from the versions table when querying. This could lead to incorrect data being displayed if the data on the object and the version it pointed to did not match.
API methods to allow setting of the context of the query that generated the DataObject on that object (used by the lazy loading mechanism to correctly query the Stage, Live, or Versions tables)
See https://github.com/silverstripe/sapphire/pull/1178 for context.
Lazy loading no longer loads fields from the versions table when querying. This could lead to incorrect data being displayed if the data on the object and the version it pointed to did not match.
API methods to allow setting of the context of the query that generated the DataObject on that object (used by the lazy loading mechanism to correctly query the Stage, Live, or Versions tables)
See https://github.com/silverstripe/sapphire/pull/1178 for context.
Same fix as be97535b for 3.1. Makes the method signature
more consistent with other DataExtension methods,
and comply with its subclass implementation in
Hierarchy->validate(). See accbd7f1e2 for more comments.
Before this was only possible for some specific ones, like onBeforeWrite.
This excludes any callbacks with augment*() or update*() naming,
since these are assumed to be on extension only, with a corresponding
base method available on the class itself (e.g. "updateCMSFields()"
vs "getCMSFields()").
This allows a developer to programatically access the size of the DB Varchar field. This allows us to be a bit more DRY and to define the size in one place and limit TextFields to the same value
This reverts commit 1960df8bc3.
Revert "FIX: validate doesn't take var by reference"
This reverts commit 866bb0713b.
@ajshort has changed the method signatures in 1f6f7f08. While it wasn't explicitly noted in the commit message, I think its a good change - objects like a FieldList are always passed by reference in PHP, no need to declare that behaviour.
PHP is throwing strict error warnings when overriding the
updateCMSFields and other functions in custom DataExtensions due to
the fact that the abstract class doesn't declare the variables should
be passed by reference
small change in DataList::filterByCallback. new ArrayList; changed to new ArrayList(); and added @return as this is particular important for this method (unexpected return type).
Needed for introspection for many_many relationships
without knowing the name of the relationship,
meaning we can't use DataObject->many_many_extraFields().
Does not actually change behaviour, but ensures that the hyphen
is not interpreted as a range identifier should it be placed
between two characters which PCRE regards as "rangeable".
This causes a 'Fatal error: Call to a member function hasMethod() on a non-object'.
This can happen when displaying a field in a gridfield on a belongs_to relationship.
+ has a special meaning in the URLs so overall it's a good idea to
strip them out. Otherwise they would need to appear in their ugly url
encoded form "%2B".
Refer: http://open.silverstripe.org/ticket/7929
BUG Issue with deleted records not being queried properly.
API DataQuery::expressionForField no longer requires a second parameter. Rather the query object is inferred from the DataQuery itself. This should improve consistency of use of this function.
In 3.0 there was some confusion about whether DataLists and ArrayLists
were mutable or not. If DataLists were immutable, they'd return the result, and your code
would look like
$list = $list->filter(....);
If DataLists were mutable, they'd operate on themselves, returning nothing, and your code
would look like
$list->filter(....);
This makes all DataLists and ArrayList immutable for all _searching_ operations.
Operations on DataList that modify the underlying SQL data store remain mutating.
- These functions no longer mutate the existing object, and if you do not capture the value
returned by them will have no effect:
ArrayList#reverse
ArrayList#sort
ArrayList#filter
ArrayList#exclude
DataList#dataQuery (use DataList#alterDataQuery to modify dataQuery in a safe manner)
DataList#where
DataList#limit
DataList#sort
DataList#addFilter
DataList#applyFilterContext
DataList#innerJoin
DataList#leftJoin
DataList#find
DataList#byIDs
DataList#reverse
- DataList#setDataQueryParam has been added as syntactic sugar around the most common
cause of accessing the dataQuery directly - setting query parameters
- RelationList#setForeignID has been removed. Always use RelationList#forForeignID
when querying, and overload RelationList#foreignIDList when subclassing.
- Relatedly,the protected variable RelationList->foreignID has been removed, as the ID is
now stored on a query parameter. Use RelationList#getForeignID to read it.
Session is not initialized by the time we need to use
the setting in DB::connect(). Cookie values get initialized
automatically for each request.
Tightened name format validation to ensure it can only
be used for temporary databases, rather than switching
the browser session to a different production database.
Encrypting token for secure cookie usage.
Added dev/generatesecuretoken to generate this token.
Not storing in YML config directly because of web access issues.
This bug will surface when using the ORM and adding an join to DataList
where a DataObject inherits another DataObject.
If you for example want to restrict the number of pages that only have a
related Staff object:
$list = DataList::create('Page')
->InnerJoin('Staff', '"Staff"."ID" = "Page"."StaffID");
This will create a SQL query where the INNER JOIN is before the
LEFT JOIN of Page and SiteTree in the resulting SQL string. In MySQL
and PostgreSQL this will create an invalid query.
This patch solves the problem by sorting the joins.
Avoid PHPUnit throwing "test didn't run any assertions"
notices in PHP. If nothing else, it keeps test output
looking less broken by default, making it more likely
that actual errors do get noticed.
The original intention of this method was to be a rewrite
check by the installer, but it doesn't belong in
DatabaseAdmin, nor is it used by the installer anymore,
since the rewrite test is done by the installer directly.
Quoted table / column names to make test cases work in postgres
BUG Fixed issue with SQLQuery::lastRow crashing on empty set. Added test cases for lastRow and firstRow.
Quoted table / column names to make test cases work in postgres
Merge branch '3.0-sqlquery-lastrow-fix' of github.com:tractorcow/sapphire into 3.0-sqlquery-lastrow-fix
In a usual CMS request, DataObject::db() is called potentially
thousands of times, calling Config::get() constantly for the same
uninherited statics, which is slow. This improves performance
by caching those into DataObject::$_cache_db
On sites with lots of modules, and pages with plenty of database
queries, DataObject::custom_database_fields() can be called
thousands of times, and slow down page render times. This fixes
it so the fields are cached by class in a static variable, and
are cleared when reset() is called on the DataObject.
SS_HTMLValue::getContent() uses urldecode() on the content returned
by saveHTML() -- this was done to fix encoded HTML entities like
square brackets, which are used by shortcodes. Unfortunately, this
also removes valid characters like "+" from the content.
This fixes it so square bracket entities are decoded *only*, and
leaves everything else as-is.
The specific situation where this is useful is where populateDefaults on
DataObjects needs to query the database. This will break the dev/build
when it tries to create the object via singleton - the query will not be
able to be executed if the table is not there or its schema has changed.
For an example of such use case see Translatable::populateDefaults.
If formatOutput is set to TRUE, then the regexes in getContent()
will not match the newlines, and the output will include html, body
and meta tags. Introduce a few new tests to ensure the output is
correct, and fix the regex.
Setting session directly through $_SESSION relies on
session_autostart which might not be set on every environment,
and isn't consistent with other framework use.