Coded something up in Couch in an interesting way? Have a snippet or shortcode to share? Post it here for the community to benefit.
5 posts Page 1 of 1
Dear Couchmembers,

I've just recently learned how to take actions while persisting data in the admin panel. For instance, upon creating a blog post, I may want the site to send an email, persist some change to a related template, or log an update event.

Be warned, it is a hacky way of doing it :mrgreen:, but it does work! If anyone happens to know a better way, I would love to hear it! Until then, here's what I have so far. Maybe someone has something more elegant they can share. :)

- - - - - - -

So, <cms:persist /> seems to be *strictly* a self-closing tag, unlike <cms:db_persist /> and <cms:db_persist_form /> which you can wrap if you want (<cms:db_persist> // do things here </cms:db_persist>).

However, It turns out we can still sneak some logic in as an argument to a field parameter! Just use a fake one so you don't overwrite anything real.

Code: Select all
// Log an update event
<cms:persist
  field_that_does_not_exist = "
    <cms:log 'logs/admin-updates.txt' value='Updated the record' />
  "
/>

You can also take actions only under certain conditions.

Code: Select all
// Conditional actions
<cms:editable
  name='tracking_number'
  label='Tracking number'
  type='text'
/>
<cms:persist
  zzz = "
    <cms:if (frm_tracking_number && frm_tracking_number != tracking_number)>
      <cms:log 'logs/tracking.txt' value='Tracking number updated' />
    </cms:if>
  "
/>

Because we're cramming the logic into double quotes, it is difficult to work with variables but not impossible.

Code: Select all
// Custom variables aren't recognized outside of <cms:persist /> tag

// This won't work
<cms:set my_custom_var='Tracking info updated' scope='global' />
<cms:persist
  zzz = "
    <cms:if (frm_tracking_number && frm_tracking_number != tracking_number)>
      <cms:log 'logs/tracking.txt' value=my_custom_var />
    </cms:if>
  "
/>

// Instead, you need to set it *inside* the fake field parameter. This does work.
<cms:persist
  zzz = "
    <cms:if (frm_tracking_number && frm_tracking_number != tracking_number)>
      <cms:set my_message='Tracking info updated' />
      <cms:log 'logs/tracking.txt' value=my_message />
    </cms:if>
  "
/>

To combine variables, use <cms:capture>

Code: Select all
// Combine variables
<cms:persist
  zzz = "
    <cms:if (frm_tracking_number && frm_tracking_number != tracking_number)>
      <cms:capture into='my_message'>Tracking info updated: <cms:show frm_tracking_number /></cms:capture>
      <cms:log 'logs/tracking.txt' value=my_message />
    </cms:if>
  "
/>

- - - - - - -

Like I said, this is really hacky, but being able to take action upon <cms:persist /> seems powerful. I used <cms:log /> in the examples above, but you could use a number of other couch tags like <cms:send_mail> or <cms:db_persist> to send emails, or persist data to a related template, etc.

Maybe there's a better way. Would love to hear thoughts or feedback :)
That is an interesting hack, Matt :)

I usually find just overriding the snippet rendering the admin form easier.
For example, if the template in question is 'invoice.php', I copy snippet 'content_form.html' from '/couch/theme/_system', rename it to 'content_form_invoices-php.html' and place it in '/couch/theme/sample',

Assuming theme-override is already active (viewtopic.php?f=5&t=10241) Couch, while rendering the admin edit-form for invoice.php, will display the renamed snippet we used above.

The snippet is a normal DataBound Form and can be tweaked in whatever way you wish.
The naming of the snippet to match the template is important (essentially the dots are to be replaced with dashes); more info can be found here - viewtopic.php?f=2&t=10438&p=25693#p25696

Hope this helps.
I will add some bits from my experience. :)

@KK, we had a convo (on February 13th, 2019) about doing something useful on page edit/create -
KK wrote: As for how to do that, I think instead of extending cms:persist, the issue is complex enough to warrant overriding the form screen (content_form.html) and tweaking the success condition of the form within - this way we can also make use of cms:db_commit or cms:db_rollback if need be.

Using custom validators is one of the ways we can try and synchronize changes - another would be using events (e.g. as extended-users does). Latch on to save event of the template(s) in question and then save values in multiple templates.

To summarize, possible placements where code can monitor changes to the page being processed –

  • Tag <cms:persist> in <cms:config_form_view>
  • Custom validator which runs on modified value of a field
  • Overridden form via custom admin theme
  • Hooks

A function cms:func is also a way to encapsulate long and heavy code, then invoke it via posted cms:persist hack. The function must be visible - perhaps, pre-loaded globally and early for admin panel. Below I post one such example - setting a random title to a page.

Antother talking point when we mention passing code via cms:persist is populating a repeatable region. Here viewtopic.php?f=8&t=11400&p=33502#p33525 we already discussed that and uncovered a solution. In short, I wanted to paste json code into a textarea and upon page save overwrite a repeatable region. Very useful to import-export of many rows. I will only use my old snippet as another example of a code used within cms:persist, just to add to already good example by OP -
Code: Select all
<?php require_once( 'couch/cms.php' ); ?>
<cms:template clonable='0' title='Import RR from Adm.Panel via JSON'>

    <cms:repeatable name='my_repeatable'>
        <cms:editable name='my_text' type='text' />
        <cms:editable name='my_text2' type='text' />
        <cms:editable name='my_checkbox' type='checkbox' opt_values='=1' />
    </cms:repeatable>
    <cms:editable name='my_import' type='textarea' ><cms:show_repeatable 'my_repeatable' as_json='1' /></cms:editable>

    <cms:config_form_view>
        <cms:persist
             dummy = "
              <cms:php>
                  $repeatable_name = 'my_repeatable';
                  $json_sourcefield = 'my_import';
                  //---------------
                  unset($_POST['_f_'.$repeatable_name.'_sortorder']);
                  unset($_POST['f_'.$repeatable_name]);
                  global $CTX; $CTX->set('json', json_decode($_POST['f_'.$json_sourcefield], true), 'global');
              </cms:php>"
             my_repeatable = json
             remember = 'Don`t Try This At Home'
         />
    </cms:config_form_view>

</cms:template>
<?php COUCH::invoke(); ?>


Creating randomly titled pages is another great example -

Code: Select all
<cms:persist k_page_title="<cms:get 'frm_k_page_title' default="<cms:call 'random_name' count='8' />" />" />

Code effectively persists a user-defined title or sets a random one. I found it useful when titles are forgotten, page does not generate error on save.

Couch Cart's example -
Code: Select all
        <cms:config_form_view>

            <cms:persist
                pp_options="
Size[<cms:each frm_pp_option_size as='option' sep=','><cms:show option /><cms:if k_last_item='0'> | </cms:if></cms:each>]
Color[<cms:each frm_pp_option_color as='option' sep=','><cms:show option /><cms:if k_last_item='0'> | </cms:if></cms:each>]
                "
            />


            <cms:field name='pp_options' hide='0' />
            <cms:field name='explain_options' hide='1' />

        </cms:config_form_view>



Edit Couch Configuration file from backend? Again, cms:persist helps with a simple custom function `read_file`. Note how we can use k_submitted system variable -

Code: Select all
<cms:editable type='textarea' name='my_config' no_xss_check='1' searchable='0'/>

<cms:config_form_view><cms:ignore>

    // Couch Config file is read dynamically into textarea
    // After editing, write updated _config.php to disk + save bound input

    </cms:ignore>
    <cms:persist
            my_config = "<cms:if k_submitted>
                            <cms:write '_config.php' add_newline='1' truncate='1'><cms:show frm_my_config /></cms:write>
                            <cms:show frm_my_config />
                        </cms:if>"
            />



    <cms:field 'my_config' label='CouchCMS config' desc='Edit here'>
        <cms:hide>
            <cms:input type='bound' name=k_field_input_name trust_mode='1'/>
        </cms:hide>
        <textarea
                  id="f_<cms:show k_field_input_name />"
                  name="f_<cms:show k_field_input_name />"
                  rows="12" cols="79" class="textarea "
                  style="width: 100%; height: 60vh"><cms:call 'read_file' file='_config.php' />
        </textarea>
        <cms:unset 'my_config' />
    </cms:field>

</cms:config_form_view>


Hope it de-fogs the topic even further.
@trendoman, this definitely deserves a "WOW"!

You have demoed almost all the possible ways of handling this use-case at a single place.
Thanks.
Beautiful @KK and @trendoman; topic is de-fogging nicely! "Don't try this at home" :lol:

+1 seeing 'default' parameter in cms:get tag, by the way! Wasn't aware of that and am looking forward to using it
5 posts Page 1 of 1