when editing templates with CKEditor, protect blocks of perl code, #24331
authorMark Wells <mark@freeside.biz>
Thu, 29 Aug 2013 23:23:27 +0000 (16:23 -0700)
committerMark Wells <mark@freeside.biz>
Thu, 29 Aug 2013 23:23:27 +0000 (16:23 -0700)
httemplate/edit/elements/edit.html
httemplate/edit/msg_template.html
httemplate/elements/ckeditor/plugins/blockprotect/plugin.js [new file with mode: 0644]
httemplate/elements/htmlarea.html

index 0840829..ed677d7 100644 (file)
@@ -328,7 +328,7 @@ Example:
 %     qw( hashref agent_virt agent_null agent_null_right ),#*-table
 %     qw( formatted_value ),                               #fixed
 %     qw( country ),                                       #select-country
-%     qw( width height ),                                  #htmlarea
+%     qw( width height config ),                           #htmlarea
 %     qw( alt_format ),                                    #select-cust_location
 %     qw( classnum ),                                   # select-inventory_item
 %   ;
index fa375a0..06cac44 100644 (file)
@@ -67,7 +67,8 @@ if ( $curuser->access_right('Edit global templates')
       { field => 'subject',   size=>60, },
       { field => 'body',
         type  => 'htmlarea',
-        width => 763
+        width => 763,
+        config=> { extraPlugins => 'blockprotect' },
       },
   ;
 } else { #readonly
diff --git a/httemplate/elements/ckeditor/plugins/blockprotect/plugin.js b/httemplate/elements/ckeditor/plugins/blockprotect/plugin.js
new file mode 100644 (file)
index 0000000..61c993a
--- /dev/null
@@ -0,0 +1,128 @@
+/**
+ * The "blockprotect" plugin.  Adapted from the "placeholder" plugin.
+ */
+
+(function() {
+        var delim_o = '{';
+        var delim_c = '}';
+
+        var create_block = function(content) {
+          // fix nbsp's
+          content = content.replace(/&nbsp;/gi, ' ');
+          // escape the content
+          var el = document.createElement('SPAN');
+          // IE8 compat
+          if( typeof(el.textContent) != 'undefined' ) {
+            el.textContent = content;
+          } else if( typeof(el.innerText) != 'undefined' ) {
+            el.innerText = content;
+          }
+          el.setAttribute('class', 'cke_blockprotect');
+          return el.outerHTML;
+        };
+        var block_writeHtml = function( element ) {
+          // to unescape the element contents, write it out as HTML,
+          // stick that into a SPAN element, and then extract the text
+          // content of that.
+          var inner_writer = new CKEDITOR.htmlParser.basicWriter;
+          element.writeChildrenHtml(inner_writer);
+
+          var el = document.createElement('SPAN');
+          el.innerHTML = inner_writer.getHtml();
+          if( typeof(el.textContent) != 'undefined' ) {
+            return el.textContent;
+          } else if( typeof(el.innerText) != 'undefined' ) {
+            return el.innerText;
+          }
+        };
+        var to_protected_html = function(data) {
+          var depth = 0;
+          var chunk = '';
+          var out = '';
+          var p = 0; // position in the string
+          while( 1 ) {
+            // find the next delimiter of either kind
+            var i = data.indexOf(delim_o, p);
+            var j = data.indexOf(delim_c, p);
+            if (i == -1 && j == -1) {
+              // then there are no more delimiters
+              break;
+            } else if ((i < j || j == -1) && i != -1) {
+              // next delimiter is an open
+              // push everything from current position to 
+              // the delimiter
+              if ( i > p ) chunk += data.substr(p, i - p);
+              p = i + 1;
+              if ( depth == 0 ) {
+                // start of a protected block
+                out += chunk;
+                chunk = '';
+              }
+              chunk += delim_o;
+              depth++;
+            } else if ((j < i || i == -1) && j != -1) {
+              // next delimiter is a close
+              if ( j > p ) chunk += data.substr(p, j - p);
+              p = j + 1;
+              depth--;
+              chunk += delim_c
+                if ( depth == 0 ) {
+                  // end of a protected block
+                  out += create_block(chunk);
+                  chunk = '';
+                } else if ( depth < 0 ) {
+                  depth = 0;
+                }
+            } else {
+              // can't happen
+            }
+          }
+          // append any text after the last delimiter
+          if ( depth ) {
+            out += create_block(data.substr(p));
+          } else {
+            out += data.substr(p);
+          }
+          return out;
+        };
+
+       CKEDITOR.plugins.add( 'blockprotect', {
+               afterInit: function( editor ) {
+                       CKEDITOR.addCss( '.cke_blockprotect' +
+                       '{' +
+                                       'background-color: #ffff88;' +
+                                       ( CKEDITOR.env.gecko ? 'cursor: default;' : '' ) +
+                               '}'
+                        );
+                      
+                        // keep these from getting stripped out 
+                        editor.filter.allow('span(cke_blockprotect)',
+                                            'blockprotect', true);
+
+                        // add filter at the front of toHtml
+                        editor.on( 'toHtml',
+                          function( evt ) {
+                            evt.data.dataValue =
+                              to_protected_html(evt.data.dataValue);
+                            return evt;
+                          },
+                          this, null, 0
+                        );
+
+                        editor.dataProcessor.htmlFilter.addRules({
+                          elements: {
+                            span: function( element ) {
+                              if ( element.className = 'cke_blockprotect' ) {
+                                // defeat HTML escaping
+                                var content = block_writeHtml(element);
+                                element.writeHtml = function(writer, filter) {
+                                  writer.text(content);
+                                }
+                              }
+                            } // span function
+                          } // elements
+                        });
+                }
+        }); // plugins.add
+}) ();
+
index f9dcffd..c98993d 100644 (file)
@@ -6,6 +6,7 @@ Example:
             'field'      => 'fieldname',
             'curr_value' => $curr_value,
             'height'     => 800,
+            'config'     => { extraPlugins => 'blockprotect' },
          );
 
 </%doc>
@@ -19,21 +20,24 @@ Example:
 
 <SCRIPT TYPE="text/javascript">
 
-  CKEDITOR.replace('<% $opt{'field'} %>', {
-%   if ( $opt{'width'} ) {
-      width: <% $opt{'width'} %>,
-%   }
-    height: <% $opt{'height'} || 420 %>,
-    startupFocus: true,
-    toolbarCanCollapse: true,
-    basePath: '<% $p %>elements/ckeditor/',
-    enterMode: 2
-  });
+  CKEDITOR.replace('<% $opt{'field'} %>',
+                   <% encode_json($config) %>
+  );
 
 </SCRIPT>
 
 <%init>
 
 my %opt = @_;
+my $config = {
+  'height'              => ($opt{height} || 420),
+  'startupFocus'        => JSON::true,
+  'skin'                => 'kama',
+  'toolbarCanCollapse'  => JSON::true,
+  'basePath'            => $p.'elements/ckeditor/',
+  'enterMode'           => 2,
+  %{ $opt{config} || {} },
+};
+$config->{width} = $opt{width} if defined($opt{width});
 
 </%init>