Textpattern CMS support forum
You are not logged in. Register | Login | Help
- Topics: Active | Unanswered
Pages: 1
Callback for saving own new fields
I have more or less successfull managed to generate a Plugin that gives me complete set of new fields for the edit article page.
the idea is to have multiple checkboxes and fields customized for a real estate Website to maintain properties. glz_custom fields is not enough in that case because I really wanted to create my own kind of backend template for setting up the fields etc.
That all works great. The only little issue is, that when I save it it does not show the green notice “article saved” (but actualy does save) and when I save twice I always get the error message that meanwhile the admin changed the article and nothing was saved.
I don´t want to post my whole plugin script now but I narrowed it down to the callbacks and to my save function.
I use those callbacks:
register_callback('acf_custom_fields', 'article_ui','excerpt');
register_callback('acf_custom_fields_save', 'article_saved');
register_callback('acf_custom_fields_save', 'article_posted');
It seems that only using both _save callbacks successfully change the data of my new fields.
My save function contains that:
safe_update(
'textpattern',
"$name = '" . doSlash($value) . "'",
"ID = " . intval($articleid)
);
Has anybody ever created custom fields (not the txp custom fields) through a plugin and managed to save them correctly?
The way things are saved in txp seem very confusing.
Would be great if someone had an idea what causes the error messag on every second save-attempt.
Could that be due to the lastmod entry not beeing in sync?
Offline
Re: Callback for saving own new fields
The devs are more qualified to answer this than me, so it’s wiser to wait to hear from the experts, but …
a) Attaching your callback function to both article_posted and article_saved ensures it is called on first posting of a new article and on editing an existing article.
b) As far as I remember (sketchy here, sorry), you can either have your plugin insert new fields where you want them and then update them when you save asychronously (see how bot_wtc does it by hooking into the txpAsyncForm.success callback; glz_cf does something similar) or you can extend the existing array of $partials that exist on the article pane and make a function for each. Then textpattern takes care of the refreshing (right devs?).
TXP Builders – finely-crafted code, design and txp
Offline
Re: Callback for saving own new fields
Thanks jacob, it brought me on the right track.
However my problems originated somewhere where I did not expected them. It seems to be ok to have those two callbacks.
I was also looking into glz_cf to check how they do it. But it is a little different here. They actually do not “create” any new fields that would need to be “declared” in a way.
But my problem was that I tried to write into “Excerpt” twice. Once in the original process and once again by adding a new field that should simply overwrite the original field. But that does not work like that.
I overcome this problem by having second safe_update.
$excerpt_value = trim(ps('cp_reference'));
safe_update(
'textpattern',
"Excerpt = '" . doSlash($excerpt_value) . "'",
"ID = " . intval($articleid)
);
And another problem was, that I had so many fields that I over saw that one field was actually missing in the database. Both mistakes created the same problem that I originaly posted. This made it hard to debug.
Offline
Re: Callback for saving own new fields
demoncleaner wrote #341502:
I was also looking into glz_cf to check how they do it. But it is a little different here. They actually do not “create” any new fields that would need to be “declared” in a way.
Yes, glz_cf extends the existing custom_field mechanism, but it does create new columns for each field.
my problem was that I tried to write into “Excerpt” twice. Once in the original process and once again by adding a new field that should simply overwrite the original field. But that does not work like that.
I overcome this problem by having second safe_update.
I have a mini self-made plugin that adds a few extra custom textile helpers and it first retrieves the body_html field from the currently posted/saved item, then does its changes and resaves the field.
if (txpinterface === 'admin') {
register_callback('jcr_textile_helpers', 'article_posted');
register_callback('jcr_textile_helpers', 'article_saved');
}
function jcr_textile_helpers($event, $step, $rs)
{
// get 'Body_html' field from currently saved/posted article or exit if unsuccessful
if (!($row = safe_row('Body_html', 'textpattern', 'ID='.$rs['ID']))) return;
// turn retrieved field(s) into variables named after the field
extract($row);
// put your replacement stuff for $Body_html here …
// re-save escaped 'Body_html'
$Body_html_postprocessed = doSlash($Body_html_postprocessed);
safe_update('textpattern', "Body_html = '$Body_html_postprocessed'", 'ID = '.$rs['ID']);
}
but I think you can also hook in earlier in the process and modify a field before it gets posted. See the callback reference. Bloke or Oleg can probably explain that better.
This made it hard to debug.
You probably know these already, but you can stick a ,1 to the end of safe_functions to get debug output of the queries it produces. And dmp($your_variable_or_array); shows you output at certain points.
Two other tricks for actions that don’t happen on a page, and so don’t get reported on the page:
- Open up the webinspector when making your submission and check for another existence of
index.phpin the sources/network panel. It’s only spawned for the submission but can contain an error message. Also useful for debugging com_connect submissions. - If you add the following to your config.php:
define('txpdmpfile', 'debug.txt');
it diverts error output (including your dmp) to /path/to/your/tmpdir/debug.txt. Note: you don’t get errors in your pane anymore, though.
I had so many fields that I over saw that one field was actually missing in the database.
Are you making your own front-end tags to output these, then? Or using etc_query / smd_query to output these new fields.
TXP Builders – finely-crafted code, design and txp
Offline
Re: Callback for saving own new fields
demoncleaner wrote #341493:
The only little issue is, that when I save it it does not show the green notice “article saved” (but actualy does save) …
It is often caused by warnings in debug/test mode, you might want to inspect the server response. Async requests are expecting a valid js response, but warnings make it invalid.
jakob wrote #341506:
I think you can also hook in earlier in the process and modify a field before it gets posted.
Yes, txp 4.9 has ('article_submit', 'post|save', '1|0') callbacks that one can use to alter article fields before textile/save. Dunno if it helps here, but see around l.190 of txp_article.php.
Offline
Re: Callback for saving own new fields
etc wrote #341516:
It is often caused by warnings in debug/test mode, you might want to inspect the server response. Async requests are expecting a valid js response, but warnings make it invalid.
Yes, txp 4.9 has
('article_submit', 'post|save', '1|0')callbacks that one can use to alter article fields before textile/save. Dunno if it helps here, but see around l.190 oftxp_article.php.
Interesting (link to code). So that means in the above example I could probably save both queries by using article_submit and the 0 option – because I want to add some stuff to the already textiled output – and do my modifications to $incoming['Body_html'] and then return $incoming at the end of the function. Textpattern then goes ahead and does the rest, including saving to the DB.
And demoncleaner could potentially also use article_submit but with the 1 option to switch out $incoming['Excerpt'] with his own field’s content before letting Textpattern textile the content [if he’s using txp 4.9].
TXP Builders – finely-crafted code, design and txp
Offline
Re: Callback for saving own new fields
^ Correct. The pre/post flag is one of the things that a lot of the callbacks don’t sadly employ in a uniform manner.
Come the revolution, all callbacks will be (at least) in pairs. One pre-Txp involvement and one after it’s done its bit. Some panels have additional ‘during’ style callbacks so you can alter the actual content of the page (e.g. extend tables or add custom steps for your own handlers).
The notion of ‘pre’ and ‘post’ has been subtly enhanced in 4.9.0 so it’s not just limited to pre=1 and post=0. You can actually pass any string to the pre/post value when you register a callback, allowing plugins and themes to intercept at any stage. This sort of ‘sub-step’ allows us to wire up better callbacks, e.g. :
| Current event.step.pre | Potential future event.step.pre |
|---|---|
| article_saved | article.save |
| article_posted | article.save.new |
| image_deleted.image | image.delete.1 (pre) / image.delete.0 (success) / image.delete.fail |
…
I have a branch (somewhere) knocking around where I started work on building a HUGE table of current -> future callback mappings. I didn’t get past about 4 panels before I got sidetracked.
The smd plugin menagerie — for when you need one more gribble of power from Textpattern. Bleeding-edge code available on GitHub.
Hire Txp Builders – finely-crafted code, design and Txp
Offline
Re: Callback for saving own new fields
Thank you so much guys. So many valuable information. A bit like xmas…
For the record: I am on the latest (I guess) 4.9.0 and yes I have my own frontend tags.
And as always I am pretty stoked with what can be achieved with textpattern as a base.
Last edited by demoncleaner (2025-12-05 10:50:29)
Offline
Re: Callback for saving own new fields
Coming back to this, I wanted to make a tiny plugin that prefixes the article_url_title with the date string specified in a custom field for articles in a specific section, as a way of avoiding all those duplicate url_title messages.
This is my current code but it’s not working:
<?php
if (txpinterface === 'admin') {
register_callback('jcr_date_url', 'article_submit', 'post', 0);
register_callback('jcr_date_url', 'article_submit', 'save', 0);
}
function jcr_date_url($event, $step, $incoming)
{
$prefix_from = 'custom_1';
$section = 'kalender';
$separator = '-';
// if section matches and specified custom field contains content
if (($incoming['Section'] === $section) && !empty($incoming[$prefix_from])) {
// chop any pre-existing date prefix in the url_title
if (preg_match('/^\d{4}-\d{2}-\d{2}-/', $incoming['url_title'])) {
// remove any existing YYYY-MM-DD- = first 11 characters
$incoming['url_title'] = substr($incoming['url_title'], 11);
}
// prefix with date
$incoming['url_title'] = ($incoming[$prefix_from] . $separator . $incoming['url_title'];
}
return $incoming;
}
I had hoped this would hook in during article save or post, and if the article is in Section ‘kalender’, it should add the event start date, which is stored as YYYY-MM-DD in event_start_day which is custom_1. All other articles it should leave alone.
As far as I can tell from dmp-ing, $incoming is passed to the function, and the replacement $incoming array at the end of the function is correctly modified and intact, but it’s not being picked up by textpattern for saving and updating on the write pane.
I’m probably going about it wrong. All tips gratefully received.
EDIT: manifest.json as "order": 5, "type": 4;
TXP Builders – finely-crafted code, design and txp
Offline
Re: Callback for saving own new fields
Just bumping this gently 😬. How do I get Textpattern to carry on working with the modified $incoming array using this new method?
The plugin is doing it thing correctly but I’m obviously not passing it back properly because txp_article.php doesn’t use it.
dmp-ing $incoming at the start of the function shows all the fields being saved. If you do it with post-processing, you get the additional textiled fields as well.
dmp-ing $incoming at the end of the function before the return statement shows the replaced url_title correctly.
dmp-ing $incoming in txp_article.php immediately after the callback shows the original, unmodified state.
Looking at the callback reference in the docs, it seems some functions have a four input attributes, $event, $step, $data, and $rs, but all those examples refer to modifying ui output with $data, or processing database requests with $rs, and I’m doing neither of those here.
TXP Builders – finely-crafted code, design and txp
Offline
Re: Callback for saving own new fields
Sorry, I meant to reply sooner. I did exactly this recently (though I was appending not prefixing) and the approach I took was almost identical to yours. I checked the previous url_title and skipped the append if it was already applied.
The crucial differences are the callback point is after Txp has done its stuff, and so I didn’t just return the new value, I called safe_update() to store (overwrite) it.
Here’s my working attempt if it helps (it uses the class-based pattern, hence $this but you can adapt that if you’re using the regular global pattern) :
register_callback(array($this, 'appendStockCode'), 'article_saved');
register_callback(array($this, 'appendStockCode'), 'article_posted');
...
...
public function appendStockCode($evt, $stp, $data = array())
{
$sec = $data['Section'];
$stockID = $data['custom_14'];
$urlTitle = $data['url_title'];
$articleID = (int)$data['ID'];
if ($sec === 'whatever') {
$newStockID = $stockID ? '-'.$stockID : '';
if (preg_match('/(\-p[0-9]{1,7})$/', $urlTitle, $matches)) {
// Stock ID is already in the URL: replace it.
$currStockID = $matches[1];
$urlTitle = str_replace($currStockID, $newStockID, $urlTitle);
} else {
// Stock ID isn't already in the URL: append it.
$urlTitle .= $newStockID;
}
safe_update('textpattern', "url_title = '" . doSlash($urlTitle) . "'", "ID = " . $articleID);
}
}
P.S. with your original version, you might have more luck if you register your involvement pre by using 1 in the register_callback() but I’m pretty sure I tried something similar and gave up. So I cheated and did it after.
Last edited by Bloke (Yesterday 23:29:46)
The smd plugin menagerie — for when you need one more gribble of power from Textpattern. Bleeding-edge code available on GitHub.
Hire Txp Builders – finely-crafted code, design and Txp
Offline
Re: Callback for saving own new fields
Thanks! But isn’t that the ‘old way’ we’ve been doing up to now? I have something similar in an earlier post in this thread where I alter the body_html and then re-save it.
What attracted me here, was to just modify the form output before it is written by Textpattern to the DB. From what I can tell from the $incoming array with pre (i.e. 1) is shorter, and with post (i.e. 0), the array then also includes body_html and excerpt_html and an empty title_html but is otherwise identical.
TXP Builders – finely-crafted code, design and txp
Offline
#13 Today 00:26:57
Re: Callback for saving own new fields
Yeah. The general rule of thumb in Txp is if you intercept something pre, then you can alter it and leave Txp to then use what you’ve given it.
The only reason those extra fields appear post is because they’re not submitted from the form fields; they’re generated by running the input through Textile. So if your plugin changed the Title field pre, Txp would use your modified Title to generate Title_html. If you modified Title in post, you’d have to update the row AND regenerate it to keep both input and Textiled content in sync.
The only reason I can see why your existing code wouldn’t work is because your callback is post and Txp has thus already committed the content to the DB.
The smd plugin menagerie — for when you need one more gribble of power from Textpattern. Bleeding-edge code available on GitHub.
Hire Txp Builders – finely-crafted code, design and Txp
Offline
#14 Today 00:30:09
Re: Callback for saving own new fields
Oh you could also try this function signature, although I’m not sure if Txp uses call by reference in this callback:
function jcr_date_url($event, $step, &$incoming) ...
It might make no difference.
The smd plugin menagerie — for when you need one more gribble of power from Textpattern. Bleeding-edge code available on GitHub.
Hire Txp Builders – finely-crafted code, design and Txp
Offline
Pages: 1