Inserting text snippets into content blocks

Permalink 1 user found helpful
Hello

I have been trying to find a solution in C5 to allow a client to define a simple text snippet in one place and access that text snippet as a common variable in the content block so anytime the snippet is changed it changes globally accross the site.

An example would be a phone number, it could be added as a snippet and inserted via a variable (%phone_number%) in any paragraph or text on the site meaning if it was changed in future only one change would be necessary.

The Global scrapbook comes close but only allows snippets to be added as blocks meaning formatting must be common to all and it cannot be included in the normal paragraph flow.

I could not find an existing solution so decided to have a go myself but I am relatively new to PHP and C5 is my first experience with OOP of any kind.

I created a package including a simple dashboard page and controller that allows users to add snippets and names which then creates a handle from the name so for example "Telephone number" to "%telephone_number%".

To enable this accross the site I put a modified controller file in root/blocks/content/
modified as follows
function translateFrom($text) {
         $db = Loader::db();
         $q = "SELECT * FROM pkSnippets";
         $r = $db->query($q);
         if ($r && $r->numRows() > 0)
         {
            while($row = $r->fetchRow())
            {
               $text = preg_replace('/'.$row['handle'].'/i', $row['snippet'], $text);
            }
         }
.....


then to enable an easy way to add these in the content editor I added a modified editor_controls.php to root/elements/

modified as follows
<?php  defined('C5_EXECUTE') or die("Access Denied."); ?> 
<div class="ccm-editor-controls">
<div class="ccm-editor-controls-right-cap">
<!-- Creates a small select element of all snippets and adds to content if selected -->
<div style="float:right;padding-top:8px;">
<select style="font-size:80%;" id="snippets" onchange="tinyMCE.activeEditor.focus();tinyMCE.activeEditor.selection.setContent(this.options[this.selectedIndex].value);">
<option value="">Snippets</option>
<?php 
   $db = Loader::db();
   $q = "SELECT * FROM pkSnippets";
   $r = $db->query($q);
   if ($r && $r->numRows() > 0)
   {
      while($row = $r->fetchRow())
      { ?>


This all works as I hoped with the added benefit that snippets can be added to the global scrapbook and accessed from the theme by referencing the scrapbook entry.
However not having much experience I was wonderring if there are any pitfalls to this method, if I am creating any potential problems etc or if there is a much simpler way to doing this.

If anyone has the time i'd be interested in your feedback

Thanks

Nat Owen
-Digirunt

1 Attachment

digirunt
 
MrNiceGaius replied on at Permalink Reply
MrNiceGaius
Hey, thanks! this is extremely cool :)

BTW, I had to add the required header items for the jquery dialogue to your single pages controllers view function in order for it to work. Kept giving an error about calling undefined "open" method.
<?php
// controllers/single_pages/snippets.php
   public function view()
   {
      $html = Loader::helper('html');
      $this->addHeaderItem($html->css('jquery.ui.css'));
      $this->addHeaderItem($html->css('ccm.dialog.css'));
      $this->addHeaderItem($html->javascript('jquery.ui.js'));
      $this->addHeaderItem($html->javascript('ccm.dialog.js'));
      if($this->listSnippets())
      {
         $this->set('snippets', $this->listSnippets());
      }
   }


I'm testing in 5.5.2.1 and am doing a few tweaks to get the styles sync'd up with the c5 bootstrap. I'll post here when it's dialed in.

The functionality is working great, this is going to be a super useful add on and love the dropdown functionality in the editor window.

For sure, going to add this to my startingPointPackage for fresh installs.
MrNiceGaius replied on at Permalink Reply
MrNiceGaius
Okay, so a little bit of a hang up when using the snippets.

The code only parses the snippet when the block is saved. If you change the snippet value afterwards the block will not update automatically.

To get the blocks to refresh their snippet values one must manually place the block into edit mode and then save it to re-parse the snippet tags.

Seems like a controller method for refreshing all content blocks needs to fire whenever new snippets are added?
jero replied on at Permalink Reply
jero
Interesting Idea.

If it were me, I would be looking to create a custom template for the content block, and have it check the content for snippets and insert them when viewed. Copy concrete/blocks/content/view.php to blocks/content/view.php and work on that. One might argue that you ought to copy the controller too, and add methods into that, but that's up to you.
JohntheFish replied on at Permalink Reply
JohntheFish
MrNiceGaius replied on at Permalink Reply
MrNiceGaius
Jeremy, that sounds great, thanks for the suggestion makes total sense. I remember seeing this technique in one of the free email obfuscater blocks, rather than change the controller out just change out the view :)

John, that block is pretty intense :) I'm going to buy it just so I can see the code haha thanks for the suggestion.
MrNiceGaius replied on at Permalink Reply
MrNiceGaius
Update:

- moved snippet parsing to custom view template for the content block ( custom block controller no longer needed )

- the snippets in the content blocks still do not automatically update, however, clearing the site's cache loads the new snippet values site wide so it's pretty decent set up. Would it make sense to pragmatically clear the site cache after saving a snippet?

Here is the custom view code for the content block:
<?php  
      defined('C5_EXECUTE') or die("Access Denied.");
      $content = $controller->getContent();
      # parse snippets
      $db = Loader::db();
           $q = "SELECT * FROM pkSnippets";
           $r = $db->query($q);
           if ($r && $r->numRows() > 0)
           {
              while($row = $r->fetchRow())
              {
                 $content = preg_replace('/'.$row['handle'].'/i', $row['snippet'], $content);
              }
           }
      print $content;
jero replied on at Permalink Reply
jero
I guess it makes as much sense as clearing the cache after making any change to any block that's cached. I have often cursed the cache after editing global scrapbook items, only for the "old" versions to persist. I guess it's up to you ;)
JohntheFish replied on at Permalink Reply
JohntheFish
You may want to guard against '/' within the match - it could be quite common if someone is writing about file paths.
MrNiceGaius replied on at Permalink Reply
MrNiceGaius
I stayed up late last night playing around with Nathanael's code. It's working pretty decently for me now.

As John points out, it's pretty easy to break the site with no input validation. Instead of filtering out all tags though I installed the HTML Purifier library and created a custom helper for c5 to purify the input as standards compliant html.

Because of this change the snippets are now limited to html ( xhtml ).
This is working awesome for inserting stored snippets of text and/or html within the content editor.

Attached is a zip file for c5.5+, to install it make sure to copy the files within blocks & elements root folders to the corresponding directories in your sites root.

'HTML Snippets'
Based on 'snippets' by Nathanael

Changes:

- created custom HTML Purifier helper for filtering the snippet inputs so user can't break site if pasting broken html into snippets

- converted dashboard pages to Concrete5.5+ UI

- added cache refresh every time a block is added, edited, deleted. This updates the snippets site wide after any changes.

- moved custom controller function into custom content block view.php

- added special tag wrapper element and assoc. css styles for visual feedback during site editing i.e. missing placeholders appear in red when in edit mode, however, when previewing / viewing site missing placeholders set to display:none.
MrNiceGaius replied on at Permalink Reply 1 Attachment
MrNiceGaius
Sorry, here is the correct attachment