Conversation
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## 3.x #16930 +/- ##
============================================
+ Coverage 21.67% 22.20% +0.52%
- Complexity 10786 10827 +41
============================================
Files 566 567 +1
Lines 33149 33300 +151
============================================
+ Hits 7186 7394 +208
+ Misses 25963 25906 -57 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
|
@pbowyer Hey Peter - Absolutely agree the timestamp additions should be done. However, for consistency with the current data structure, I'd much rather see them implemented as unix timestamps. For next-gen MODX (presumable 4.0) the optimal date/time storage type can be debated and, if changed, implemented across the whole codebase. Note that in the context of web apps, the 32-bit limitation is really a non-issue as, from my research, you're pretty much only going to find legacy non 64-bit hardware in industrial settings, especially 12 years from now. |
5c4b80b to
b2d4469
Compare
|
Thanks for your review @smg6511. I disagree with you (I would improve the column type now for new additions, so there's less to migrate in MODX v4) but I've changed them to be Unix timestamps as requested. |
smg6511
left a comment
There was a problem hiding this comment.
@pbowyer - Hey Peter, thanks for being flexible and adjusting to UTs for this PR. I've confirmed that, for the most part, timestamps are recorded as expected. I have a handful of initial review questions and requests to share ahead of a more final review:
- I'm unsure about the value of adding these cols to the association tables, especially the TVRs one. I'd like to get your take on how it'd be useful. @opengeek @Mark-H and others: It'd be great to get your opinions on this as well.
- Same as above goes for the Plugin Event table.
- With Snippets and Plugins, after the first save something seems to trigger the save again (or maybe it's just the reloading of the page) — resulting in
editedonbeing recorded as well (a second or two later thancreatedon). It'd be ideal to find a way for this to not happen. - Nuance 1: I think logic should also be added for the Duplicate processors, adding a
beforeSavemethod to reset the duplicated element'screatedonto the currenttime()andeditedonto empty. - Nuance 2: This may or may not be a job for another PR but I noted that when a static element's content is externally edited you have to at least revisit/reload the element's editing page for
editedonto be updated. I was wondering if clearing cache could also trigger this update. This may be too fine a point, but just wanted to at least bring it up.
|
Two I did because I have a use for them: for plugin events it's helpful to see when new events were added to a plugin. The same to a lesser extent for The other two (TVRs and TVTpls) I included for consistency, not because I currently would use that field.
I cannot reproduce this. Do you perhaps still have the
Good catch, added in 97b73d4.
I purposefully kept this out of scope of this PR because it's what stopped the former PR (#13857 (comment) onwards). Addressing it in a follow-on PR will keep the discussion focused and allow the side-discussions static elements seem to generate(!) to happen without affecting this change. |
|
@pbowyer - Hello again! Ok, as I continue to look this over, I realize I failed to specify that the effect I was seeing with Snippets and Plugins seemingly double-saving had to do with static ones. That's probably why you couldn't replicate what I was describing (?). Thanks for making the changes for the Duplicate processor, that's working as expected. On the association tables, I'm not saying “no” but just want you to consider whether these changes have broader appeal. If it's pretty niche or for mostly a personal need, then it might be the job of a custom component/plugin or and Extra (thinking of auditing). |
|
Hi @smg6511 and thanks for the bug report. I was able to reproduce and fix the problem, it was happening because static Snippet/Plugin files retained a leading newline after stripping Tangentially the same extra newline has caused us problems when using Gitify, as it leads to unexpected changes after exporting an imported snippet saved via the Manager. I'd never investigated, but this explains why it happens and the fix will sort that too.
I don't think that's for me to consider. I've presented a PR and @opengeek needs to make a call on their inclusion or not as he's steering the direction of the project. |
|
@pbowyer - Ok, so let's work on moving this forward without the addition to the association tables. I talked with Jason a while back and, while he didn't have a strong opinion on this, he said that it was a bit much adding them there. I personally think that dates should only be persisted in the db for the objects we create and tracking activity re peripheral tables' data might better be left to an auditing Extra/CMP. |
…hemas Add nullable datetime columns for createdon and editedon to the XML schema and PHP map files for: modCategory, modChunk, modPlugin, modPluginEvent, modSnippet, modTemplate, modTemplateVar, modTemplateVarResource, modTemplateVarResourceGroup, and modTemplateVarTemplate. editedon uses ON UPDATE CURRENT_TIMESTAMP so MySQL automatically tracks modifications. Both fields default to NULL for existing records.
Set createdon to the current datetime in modElement::save() and modCategory::save() when creating new objects, matching the convention used by modUser. Add upgrade script for 3.2.1 that adds the createdon and editedon columns to all 10 affected tables during setup.
Verify that createdon is set on new chunks and categories, that editedon remains null after creation, and that createdon is preserved after updates.
Duplicate processors copy the source element's fields via fromArray(), which carried over createdon/editedon. Since modElement::save() only sets createdon when empty, duplicates inherited the original's timestamps. Add a beforeSave() to the abstract Element\Duplicate processor that gives the new element a fresh createdon and an empty editedon. This covers all element types (Chunk, Plugin, Snippet, Template, TemplateVar); PropertySet extends DuplicateProcessor directly and has no timestamp fields.
createdon/editedon are dbtype=int phptype=timestamp (matching modResource), so xPDO's get() returns a formatted 'Y-m-d H:i:s' string, not a raw Unix integer. The existing assertions used is_numeric()/(int) casts and failed. Parse the values with strtotime() and use assertNotEmpty/assertEmpty for the present/absent checks instead.
5493c7c to
e599db2
Compare
|
Hi @smg6511 I've removed the association timestamps and rebased the branch too. |
|
@pbowyer - Hey Peter, thanks for the update. @opengeek @JoshuaLuckers @Mark-H - and others who were/are involved in this and the original solution: The small issue of |
opengeek
left a comment
There was a problem hiding this comment.
Thank you for your work on this, Peter. The model and processor changes look good and the maps match a regeneration from the schema. One significant issue with the setup side, though, was found while running a build + upgrade against a snapshot.
Blocking: the upgrade script never runs
setup/includes/upgrades/common/3.2.1-element-timestamps.php is not included by anything. modInstallVersion::_getUpgradeScripts() scans upgrades/mysql/, and each mysql/X.Y.Z-pl.php there includes the relevant common/ scripts. Without a wrapper the columns are never added.
After running the upgrade, any element or category save fails, and since xPDO selects columns explicitly, element grids fail:
Unknown column 'modSnippet.createdon' in 'SELECT'
Unknown column 'createdon' in 'INSERT INTO'
Separately, the 3.2.1 prefix is wrong for the release this ships in. version_compare('3.2.2-pl', '3.2.1-element-timestamps', '<') is false, thus installs on 3.2.1 or 3.2.2 would skip it even if it were wired up.
Fix (verified locally: columns added to all six tables, saves work, createdon set on create, editedon set on update):
- Rename to
common/3.3.0-element-timestamps.php - Add
setup/includes/upgrades/mysql/3.3.0-pl.php:
<?php
/**
* Specific upgrades for Revolution 3.3.0-pl
*
* @var modX $modx
* @package setup
* @subpackage upgrades
*/
/* run upgrades common to all db platforms */
include dirname(__DIR__) . '/common/3.3.0-element-timestamps.php';Cleanup Requests/Considerations
- PR description is out of date. It describes
datetimecolumns withON UPDATE CURRENT_TIMESTAMP,NULLfor existing rows, and association tables. The implementation is int Unix timestamps set in PHP with default0, and association tables were dropped. Please freshen this in order to maintain accurate merge history. editedonis stamped on every non-newsave(), including category moves via the Sort processor, elements orphaned by a category removal, static file resyncs ingetContent(), and package installs. That fits the "last time the row changed" semantic you chose, but it differs frommodResource, where processors set it. I wanted to note this to ensure this behavior is a deliberate decision.modScript::getFileContent()trim change: Good fix for the leading newline issue. One-time side effect after upgrade: existing static scripts that stored the newline will be re-saved once on first load and get aneditedonstamp. I think this is fine, but worth a line in the description.- Whitespace-only changes remain on
modPluginEvent,modTemplateVarResource*,modTemplateVarTemplatemaps and a blank line inPasswordResetToken.phpand its test, left over from the reverted association table commit. Harmless, but could be dropped to keep this clean.
Setup only includes scripts from upgrades/mysql/, so the common script never ran. Rename it to 3.3.0 so installs on 3.2.x pick it up, and add the mysql/3.3.0-pl.php wrapper. Restore the generator's trailing whitespace in the model maps.
5920fdb to
80f6563
Compare
|
Hi @opengeek, Thanks for running the build and upgrade against a snapshot, and for the fix. I've renamed the script to On I've also rewritten the description to match the implementation, and dropped the leftover whitespace changes from the maps, |
What does it do?
MODX tracks when resources, users, and settings were created and last modified, but not elements. There's no way to tell when a snippet was last changed or which templates haven't been touched in years.
As a developer, this information is useful. This PR adds
createdonandeditedoncolumns to chunks, snippets, plugins, templates, TVs, and categories.How does it work?
Both columns are
intUnix timestamps with a default of0, matchingmodResource. They're set in PHP rather than by MySQL:createdonis set inmodElement::save()andmodCategory::save()when the row is new, unless a value was already supplied.editedonis set on every save of an existing row.createdonand resetseditedonto0.Existing rows get
0for both after the upgrade.editedonmeans "the last time this row was saved", which differs frommodResource, where processors set it. I chose this deliberately, so it also changes when MODX saves an element or category outside the update processors: moving a category with the Sort processor, orphaning elements when their category is removed, resyncing a static element's file content, and package installs. For static elements, it reflects when MODX last synced the file to the database, which may differ from when the file itself changed.To stop static scripts being re-saved on every load,
modScript::getFileContent()now trims newlines as well as spaces. Before, the newline after<?phpin the file survived the trim, so the file content didn't match the stored content andgetContent()saved the element on every load. After upgrading, static snippets and plugins whose stored content picked up that newline will be re-saved once on first load and get aneditedonstamp.The upgrade script is
setup/includes/upgrades/common/3.3.0-element-timestamps.php, included fromsetup/includes/upgrades/mysql/3.3.0-pl.php.How to test
createdonandeditedoncolumnscreatedonis seteditedonis set andcreatedonis unchangedcreatedonandeditedonof0The test suite covers
createdonbeing set on create for chunks and categories,createdonbeing preserved on update, duplicates resetting both timestamps, and unchanged static snippets and plugins not being re-saved on load.Related issue(s)/PR(s)
#13857 was a previous PR to do this. It added
createdbyandeditedbyas well, and as it went the scope expanded. I have kept my PR focused because I am not using the other features. It ran into problems with static elements, and I think I have taken a pragmatic approach to solving that.#13770 #13626 #13641 #2027