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
Hi,

As most of Couch's users would know, tag <cms:field> (used from within <cms:config_form_view> tag block) offers a powerful way to customize the looks of fields as they appear in the admin-panel while editing a page (viewtopic.php?f=5&t=10241).

As mentioned in the documentation post linked above, the content enclosed within the <cms:field> tag-pair is dynamic i.e. although it is defined on the front-end, it executes in the context of the admin-panel.
An example of which is the following taken from the same documentation -
Code: Select all
<cms:field 'my_message' >
    <cms:repeat '3' startcount='1'>
        <h<cms:show k_count />>Hello!</h<cms:show k_count />>
    </cms:repeat>
</cms:field>

As seen above, the content indeed is dynamic and can be changed depending on the context of the admin-panel.
However, the definition of <cms:field>, per se, is set in stone to the front-end context and will not react to the dynamics of the admin-panel.

A little example will make this point clearer -
Suppose, for some reason, you wish to add a particular field in the edit screen of the admin-panel only when the page has been saved at least once (i.e. is not new).
Placing the required check as follows will not work as you might expect -
Code: Select all
<cms:if k_page_id ne '-1'>
    <cms:field 'my_message' >
        <cms:repeat '3' startcount='1'>
            <h<cms:show k_count />>Hello!</h<cms:show k_count />>
        </cms:repeat>
    </cms:field>
</cms:if>

The reason being that <cms:if k_page_id ne '-1'> (a new page has an id of -1) was executed on the front-end (while the 'visit as super-admin' part of making changes to a template was done).
A page on the front-end will never be new so the check will succeed and the <cms:field> definition will be executed causing the field to be added.
In the edit screen of the admin-panel, the check will never be made again and the field will always be present in the form.

Now, one can argue that, since the contents of the field will nevertheless be executed every time the edit screen is accessed, we could place the check within the contents e.g. as follows
Code: Select all
<cms:field 'my_message' >
    <cms:if k_page_id ne '-1'>
        <cms:repeat '3' startcount='1'>
            <h<cms:show k_count />>Hello!</h<cms:show k_count />>
        </cms:repeat>
    </cms:if>
</cms:field>

Yes, that would cause the contents to appear only for a saved page but the field itself (with its label etc.) will continue to appear in new pages (and you might have to fall back on CSS to hide it).

Anyway, if you happen to run into a use-case, as I did recently, where dynamically controlling the very presence of fields in the admin-panel is a requirement, following is a little addon that I coded to help with the situation.
Please copy the full code given at the end of this post to your 'couch/addons/kfunctions.php' file (if a file by this name does not exist, please rename the 'kfunctions.example.php' found there to 'kfunctions.php').

With this code incorporated, you can use a tag named <cms:jit_fields> (that is 'Just In Time' fields) within your <cms:config_form_view> block.
Now, instead of directly within your <cms:config_form_view> block, you should define the <cms:field> tags within the <cms:jit_fields> block e.g. as follows -
Code: Select all
<cms:config_form_view>
    <cms:jit_fields>
   
        .. place <cms:field> tags here

    </cms:jit_fields>
</cms:config_form_view>


The <cms:jit_fields> tag block serves to provide a dynamic context to all enclosed <cms:field> tags and so the following amendment of the failed code we tried above will work as expected
Code: Select all
<cms:jit_fields>
    <cms:if k_page_id ne '-1'>
        <cms:field 'my_message' >
            <cms:repeat '3' startcount='1'>
                <h<cms:show k_count />>Hello!</h<cms:show k_count />>
            </cms:repeat>
        </cms:field>
    </cms:if>
</cms:jit_fields>


Hope this helps.

CODE:
Code: Select all
// JIT fields
{
    // Tag <cms:jit_fields>
    $FUNCS->register_tag( 'jit_fields', function($params, $node){
        global $CTX, $FUNCS, $AUTH;

        if( $AUTH->user->access_level < K_ACCESS_LEVEL_SUPER_ADMIN ){ return; }

        // get the 'config' object supplied by 'cms:config_form_view' tag
        $arr_config = &$CTX->get_object( '__config', 'config_form_view' );
        if( !is_array($arr_config) ){ return; }

        if( count($node->children) ){
            $content = $node->children;
        }
        $arr_config['jit_fields'] = $content;
    });

    if( defined('K_ADMIN') ){
        $FUNCS->add_event_listener( 'alter_pages_form_default_fields', function(&$arr_default_fields, &$obj){
            global $PAGE, $FUNCS, $CTX, $DB;

            if( !(is_array($obj->arr_config) && array_key_exists('jit_fields', $obj->arr_config) && is_array($obj->arr_config['jit_fields'])) ){ return; }

            // replace cms:config_form_view tag
            $arr_config = array( 'arr_fields'=>array(), 'js'=>'', 'css'=>'', 'html'=>'', 'params'=>'' );
            $listener_config_form_view = function($tag_name, &$params, &$node, &$html) use(&$arr_config){
                global $FUNCS, $CTX;

                $CTX->set_object( '__config', $arr_config );

                // invoke child tags
                foreach( $node->children as $child ){
                    $child->get_HTML();
                }
                return 1; // skip original tag code
            };
            $FUNCS->add_event_listener( 'alter_tag_config_form_view_execute', $listener_config_form_view );

            $html = '<cms:config_form_view></cms:config_form_view>';
            $parser = new KParser( $html );
            $dom = &$parser->get_DOM();
            $dom->children[1]->children = $obj->arr_config['jit_fields'];
            foreach( $dom->children as $child ){
                $child->get_HTML();
            }

            $FUNCS->remove_event_listener( 'alter_tag_config_form_view_execute', $listener_config_form_view );

            // set fields if any
            if( is_array($arr_config['arr_fields']) && count($arr_config['arr_fields']) ){
                if( !is_array($obj->arr_config) ){ $obj->arr_config = array(); }
                if( !is_array($obj->arr_config['arr_fields']) ){ $obj->arr_config['arr_fields'] = array(); }

                foreach( $arr_config['arr_fields'] as $k=>$v ){
                    $obj->arr_config['arr_fields'][$k] = $v;
                }
            }
        });
    }
}// end JIT fields
Could be useful :)

I once created this working piece of code that helped me show a couple of rows of fields on the "New" page and add the rest rows of fields on "Edit" i.e. existing (saved once) pages. Here is an abstract of that code which went into modified theme snippet (usually a template-name_content_form.html snippet) —

Code: Select all

        <!-- advance settings dropdown -->
        <cms:render 'group_advanced_settings' />

        <!-- Editable regions -->
        <cms:set bufferedFields = '[]' is_json='1' scope='global' />
        <cms:admin_form_fields>
            <cms:if k_field_group eq '_system_fields_'><cms:render 'form_row' /></cms:if>
            <cms:if k_field_group eq '_custom_fields_'>
                <cms:put
                    var="bufferedFields.<cms:show k_field_name />"
                    value="<cms:render 'form_row' />"
                    scope='parent'
                  />
            </cms:if>
            <cms:set has_custom_fields = '1' scope='parent' />
        </cms:admin_form_fields>

        <cms:if k_cur_form_mode eq 'create'>
            <!-- show first rows to begin with -->
            <cms:show bufferedFields.row00_title_type />
            <cms:show bufferedFields.row01_handle />

        <cms:else_if k_cur_form_mode eq 'edit' />

            <!-- show first rows to begin with -->
            <cms:show bufferedFields.row00_title_type />
            <cms:show bufferedFields.row01_handle />

            <!-- unset rendered -->
            <cms:unset 'bufferedFields.row00_title_type' />
            <cms:unset 'bufferedFields.row01_handle' />

            <cms:if page_type eq '1'>

              <!-- Just need group inputs -->
              <cms:show bufferedFields.row02_parent_group />
              <cms:unset 'bufferedFields.row02_parent_group' />
              <cms:unset 'bufferedFields.row02_parent_param' />

            <cms:else_if page_type eq '20' />

              <!-- Just need parameter inputs -->
              <cms:show bufferedFields.row02_parent_param />
              <cms:unset 'bufferedFields.row02_parent_param' />
              <cms:unset 'bufferedFields.row02_parent_group' />

              <cms:each bufferedFields as='render' >
                  <cms:show render />
              </cms:each>

            <cms:else />

              <!-- show first rows to begin with? -->
              <cms:show bufferedFields.row00_title_type />
              <cms:show bufferedFields.row01_handle />

            </cms:if>
        </cms:if>
        <!-- /End of custom fields-->



I am sorry to not providing more context, but thats of course possible if needed. Here is a similar example which helps "rearrange" some rows when needed and insert information to the desired place —

Code: Select all

        <cms:if k_cur_form_mode eq 'create'>

            <!-- show first row to begin with -->
            <cms:show bufferedFields.row0 />

            <!-- Infomatics -->
            <div id="row-info" class='row field'>
              <div class="col-md-12">
                <div class="alert alert-success alert-icon"><cms:show_icon 'copywriting' />
                   <b>Demo</b><br> A demo message
                 </div>
              </div>
            </div>

            <cms:show bufferedFields.group0 />
        </cms:if>

        <cms:if k_cur_form_mode eq 'edit'>

            <!-- custom fields-->
            <cms:each bufferedFields as='render' >
                <cms:if key eq 'row_pk' || key eq 'group0' || key eq 'MySuperAdminCode'><cms:continue /></cms:if>
                <cms:show render />
            </cms:each>
            <!-- /End of custom fields-->

            <!-- show "row_pk" group only now -->
            <cms:show bufferedFields.row_pk />


            <!-- Infomatics -->
            <div id="row-info" class='row field'>
              <div class="col-md-12">
                <div class="alert alert-info alert-icon"><cms:show_icon 'copywriting' />
                   <b>Msg</b><br> Here is a msg
                 </div>
              </div>
            </div>

            <!-- Prepare func that will "create" fields by executing prepared code -->
            <cms:func '_execute' html=''>
                <cms:set text = "<cms:swap val='%cms:' with='cms:' text=html />" scope='local'/>
                <cms:embed code=text /><cms:test ignore='1'><cms:log text /></cms:test>
            </cms:func>

            <!-- show group "group0" -->
            <cms:show bufferedFields.group0 />

            <!-- some custom links -->
            <cms:show bufferedFields.MySuperAdminCode />

        </cms:if>
        <!-- /End of editable regions -->



If this JIT fields addon was available earlier it could save the hassle, I presume. Nice work!
@trendoman, shows it was a problem with no easy workarounds :)
Hope the new tag simplifies matters.
Thank you both very much for the time and effort put in to this solution. I can think of many uses for this.

The <cms:jit_fields> pair will need an opening and a closing tag. The examples above show 2 opening tags sandwiching the fields.
@scratz
The <cms:jit_fields> pair will need an opening and a closing tag. The examples above show 2 opening tags sandwiching the fields.

Thanks for the heads-up. I have fixed the typo.
5 posts Page 1 of 1
cron