Ohh ok, I think this was silently erring - trying the hidden field now like you posted above.
the same approach will also work for a hidden field as date storage:
createdOn:
type: hidden
default: "{{ date('Y-m-d h:i')}}"
But note that you still need the hooks for the modification fields.
Edit: Turns out this doesnāt work after allā¦
Cool, thanks for the heads up!
Sorry for all the replies, haha, slowing down now, Iāll focus on just the creation stuff, no hooks.
Hereās what I have:
createdOn:
type: hidden
default: "{{ date('D. M j, Y ā g:ia')}}"
createdInfo:
type: info
label: false
text: Page created by {{ page.createdBy.toUser.username }} on {{ page.createdOn.time.toDate('D. M j, Y ā g:ia') }}
The username is pulling in correctly, however, the date is not (itās empty). I tried just {{ page.createdOn }}
but that was also empty / broken.
Because this code is wrong: page.createdOn.time.toDate('D. M j, Y ā g:ia')
Should be page.createdOn.toDate('D. M j, Y ā g:ia')
, without the time
bit in between.
And keep in mind that default values only work at page creation, not if the page was already created and you add the fields afterwards.
Thanks! I tried this and it still came up empty / brokenā¦
And yes, I deleted the page, refreshed from the main panel and went back in creating a brand new pageā¦
Ok, looks like I was mistaken regarding the hidden field as date.
Alternatives:
- Use a standard disabled date field and hide via custom stylesheet
- Use a page model to store the date
- Use a hook to store the date
Edit: I wouldnāt store this format: date('D. M j, Y ā g:ia')
but keep it basic or only store a timestamp.
Page model example:
<?php
class NotePage extends Page
{
public static function create(array $props): Page
{
$props['content']['createdon'] = date('Y-m-d h:i');
return parent::create($props);
}
}
NotePage
is the model for a page with the note
blueprint, so you would have to change this.
Using a model is useful if you only have one page type that needs this method. If you want to apply this to multiple page types, you either need as many models (or a based model that you extend) or a hook is the better option.
There is something strange with your setup, @something-strange.
The timezone which is shown from your format (here UTC
) is the default timezone, when the date.timezone
value in your php.ini
is NOT set. Please double check this. Note, that you might have two configuration files named php.ini
, one for PHP which will be invoked by your webserver and one for PHP which will be invoked on the command line. Take care to define the correct setting in both files.
This is also independent from executing this on your localhost (either via CLI or Webserver) or on a remote server, however the timezone setting could be different in both installations.
The PHP time()
function - which is used by my plugin - will ALWAYS return the epoch as an offset from Jan 1st, 1970 0:00 GMT (yes, but we can consider this as equivalent to UTC here), regardless of the timezone setting in php.ini
. Thus you will have an universally valid timestamp, which then will be rendered for the timezone which is set for PHP.
All of this however will always be server side, independently from where a client will look onto it. In order to show the same date/time converted to the timezone of the clients, you will need to do some client-side conversions via JavaScript or any other client-side scripting in your website. But for this it makes sense to have a universal time, or lets say āUTCā(!), on the server side as a source, which will lead us back to the epoch, this universally valid timestamp which is written by the time()
function.
To illustrate this further run this script (in comments the output on my machine):
<?php
function showTime() {
$myTZ = date_default_timezone_get();
echo "Currently active timezone: $myTZ\n";
$Now = time();
echo "Now it is $Now seconds from epoch, which is ",date('D, j M Y H:i:s T',$Now)," for humans.\n";
}
$TZ = ini_get('date.timezone');
echo "Default timezone (from ini): $TZ\n";
/* Default timezone (from ini): Europe/Berlin */
showTime();
/* Currently active timezone: Europe/Berlin
Now it is 1595665226 seconds from epoch, which is Sat, 25 Jul 2020 10:20:26 CEST for humans. */
date_default_timezone_set('UTC');
showTime();
/* Currently active timezone: UTC
Now it is 1595665226 seconds from epoch, which is Sat, 25 Jul 2020 08:20:26 UTC for humans. */
date_default_timezone_set('America/Denver');
showTime();
/* Currently active timezone: America/Denver
Now it is 1595665226 seconds from epoch, which is Sat, 25 Jul 2020 02:20:26 MDT for humans. */
?>
Hey @texnixe, @pixelijn and @Adspectus - thank you all for these examples and detailed notes!
Iām going to play around with a bit of all of these, didnāt realize there were so many options, haha. So, Iām not sure which comment to mark āSolvedā but I think itās safe to say by now this overall topic is solved.
@Adspectus āsomething strangeā with my setup - haha I see what you did thereā¦ I think this will resolve on my server, I have some weird stuff running locally with Kirby / Nuxt php servers and stuff (non-MAMP). Keep you posted on this!
Iāll share my code snippet(s) this afternoon once I decide which options to go with.
Thanks again!!
Hey all,
Hereās a 99% complete solution I came up with, consists of 3 parts!
(1) Plugin - based off of @Adspectusās very nice Date Extended Plugin (I modified plugin name / simplified it a bit for my own understanding of how plugins work - as itās my first time with all of this).
kirby/site/plugins/ssk3-page-log/index.php
<?php
Kirby::plugin('ssk3-page-log/page-info', [
'fieldMethods' => [
'time2date' => function ($field) {
$format = 'D. M j, Y ā g:ia';
return date($format, $field->value());
}
],
'hooks' => [
'page.create:after' => function ($page) {
$page->update([
'createdBy' => kirby()->user(),
'dateCreatedTime' => time()
]);
},
'page.update:after' => function ($newPage, $oldPage) {
$newPage->update([
'updatedText' => 'Page was updated by',
'updatedUser' => kirby()->user(),
'dateModifiedTime' => time()
]);
},
'page.changeTitle:after' => function ($newPage, $oldPage) {
$newPage->update([
'updatedText' => 'Page title was changed by',
'updatedUser' => kirby()->user(),
'dateModifiedTime' => time()
]);
}
]
]);
(2) Blueprint - here are the fields I set up - using hooks / no empty or hidden fields
kirby/site/blueprints/pages/project.yml
pageInfoColumn:
type: fields
fields:
pageInfoHeadline:
type: headline
label: Page Info
numbered: false
createdInfo:
type: info
label: false
text: "{{ page.createdBy.toUser.avatar }} Created by {{ page.createdBy.toUser.username }} on {{ page.dateCreatedTime.time2date }}"
updatedInfo:
type: info
label: false
text: "{{ page.updatedUser.toUser.avatar }} {{ page.updatedText }} {{ page.updatedUser.toUser.username }} on {{ page.dateModifiedTime.time2date }}"
(3) CSS - hereās some custom css, entirely optional, shows avatar next to info text:
kirby/assets/css/custom-panel.css
.k-section-name-pageInfoColumn .k-box[data-theme=info] {
border: 0;
padding: 0;
border: none;
background: none;
font-style: italic;
opacity: 1;
}
.k-section-name-pageInfoColumn .k-headline:not(.k-headline-field) {
display: none;
}
.k-section-name-pageInfoColumn .k-box[data-theme=info] .k-text {
width: 100%;
line-height: 1.25em;
flex-wrap: nowrap;
align-items: center;
display: flex;
}
.k-section-name-pageInfoColumn .k-box[data-theme=info] .k-text img {
width: 2em;
height: 2em;
min-width: 2em;
margin-right: 10px;
background-color: #000;
border-radius: 50%;
overflow: hidden;
display: block;
}
RESULTS:
- Now for my final question (I hope haha)ā¦ Page created and page updated updates the user name, but it isnāt updating the
{{ page.updatedText }}
in the blueprint for thepage.changeTitle:after
hook. So, after a user changes the title (and after a page refresh) the value just says āPage was updated byā¦ā which is thepage.update:after
hook. Itās like page update stomps out / overrides the page changeTitle hookā¦ yet the user and time update perfectly on both hooks.
Any thoughts?
As always, thank you all for your time and efforts!!
That is not really surprising, since your changeTtitle
hook updates the page and and the update action triggers the update hook again. Better use save
instead of update
in the page.changeTitle
hook.
On a side note it would nevertheless make sense to define those fields in the blueprint.
Sweet! Thanks, using save
did the trick! Applied this to page.changeSlug:after
and page.changeStatus:after
as well, so now the {{ page.updatedText }}
updates accordingly.
I was only imaging showing one row being the user that created the page and then one row of the latest update / change by whichever user, what did you mean by:
Just to be on the safe side, in case non-defined fields get deleted in the future (there are discussions about this), or that you have a way to clean up unused fields yourself.
Ah very cool, thanks for sharing!
@Adspectus - I did a push to a server and confirmed my php.ini is working correctly with date.timezone = "America/Denver"
It was just a localhost issue, but your example is still very helpful!
Again, not sure which comment(s) to mark āSolvedā haha, but I think this thread is solved in 9 different ways if you want to mark this done @texnixe
Very simple solution here if somebody is interested: Simple changelog hook for Kirby