Integrating the Tweet Text CKEditor Plugin into CoreMedia Studio

This post was inspired by the Tweetable Text CK Editor plugin by ardnet. One of my awesome colleagues, Drew Bowers, took the idea and extended the CoreMedia Studio with a similar plugin. This plugging allows readers to click on a line of text, which then launches Twitter and prepares a Tweet with the quoted text and a link back to the original article. The extension is fairly straightforward and mostly in CSS and JavaScript.

Let your readers Tweet your content easily with this plugin!

Here is a quick tutorial in getting the Tweet Text plugin up and running into your CoreMedia Studio environment as well as in the runtime (the Content Application Engine or otherwise known as the CAE):

Presumptions: Knowledge on how to add a new generic extension to studio & studio development

Setup

1. Download and Extract the Tweetable Text plugin to a temporary directory

2. Create a new Maven module within the workspace/modules/extensions directory (i.e “twitter”)

3. Inside new maven module create a new maven module with the Flash template (name: “twitter-ckeditor“)

4. Inside the flash plugin module “twitter-ckeditor

  • create src/main/joo (if not already created)
  • create package for the studio plugin mxml files (inside of src/main/joo):
    • com/coremedia/blueprint/twitterckeditor/studio
  • create sencha folder (inside of src/main/joo)
  • inside sencha folder:
    • create package “resources/ckeditor/plugins/ twitterckeditor “
    • create package “joo/resources

Example setup:

Note: this folder is built inside of the extensions directory of your CM Workspace

5. Adding the plugin files (css/js/etc)

  • inside the joo/resources folder
    • create “css”, “js”, and “images” folder
      • add the css, js, and any images from the plugin into these folders
    • inside the ckeditor/plugins/my-plugin folder
      • move plugins.js to this folder from the temporary directory (this is the main js of the plugin)

Example setup:

Updating the pom.xml to include the resource files

  • inside the “my-plugin-ckeditor” pom.xml:
    • inside of build tags
      • create a resource path to the sencha folder
      • create a plugins tag and add the css, js, and other paths as seen below
      • Make sure the additionalCssNonBundle file paths match the folder structure of the project

Example setup:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <parent>
    <groupId>com.coremedia.blueprint</groupId>
    <artifactId>twitter</artifactId>
    <version>1-SNAPSHOT</version>
  </parent>

  <artifactId>twitter-ckeditor</artifactId>
  <packaging>swc</packaging>

  <properties>
    <coremedia.project.extension.for>studio</coremedia.project.extension.for>
  </properties>

  <dependencies>
    <dependency>
      <groupId>com.coremedia.ui.toolkit</groupId>
      <artifactId>ui-components</artifactId>
      <type>swc</type>
    </dependency>
    <dependency>
      <groupId>com.coremedia.ui.sdk</groupId>
      <artifactId>editor-components</artifactId>
      <type>swc</type>
    </dependency>
    <dependency>
      <groupId>net.jangaroo</groupId>
      <artifactId>ext-as</artifactId>
      <type>swc</type>
    </dependency>
    <dependency>
      <groupId>net.jangaroo</groupId>
      <artifactId>jangaroo-runtime</artifactId>
      <type>swc</type>
    </dependency>
  </dependencies>


  <build>
    <resources>
      <resource>
        <directory>src/main/sencha</directory>
        <targetPath>../packages/${project.groupId}__${project.artifactId}</targetPath>
      </resource>
    </resources>
    <plugins>
      <plugin>
        <groupId>net.jangaroo</groupId>
        <artifactId>jangaroo-maven-plugin</artifactId>
        <version>${jangaroo.version}</version>
        <extensions>true</extensions>
        <configuration>
          <namespaces>
            <namespace>
              <uri>exml:com.coremedia.blueprint.twitterckeditor.config</uri>
            </namespace>
          </namespaces>
          <globalResourcesMap>
            <ckeditor.plugin.tweetabletext>
              ckeditor/plugins/tweetabletext/plugin.js
            </ckeditor.plugin.tweetabletext>
          </globalResourcesMap>
          <additionalCssNonBundle>
            <value>resources/joo/resources/css/tweetabletext.css</value>
            <value>resources/joo/resources/images/tweetabletext.png</value>
          </additionalCssNonBundle>
        </configuration>
      </plugin>
    </plugins>
  </build>

</project>

Development

In this step, the studio-plugins are created that load the ckeditor content

For this a driver plugin is needed that loads the menu code & ckeditor plugin

Note: The naming convention is important! Make sure that the <fx:Script> sections of each .mxml file have the proper variable & function name set (it should just be the name of the plugin without extension). CM Studio relies on these definitions to build the connection between the runtime and plugins.

1. In the package com/coremedia/blueprint/twitterckeditor /studio:

  • Create TwitterCkEditorPlugin.mxml

Use this code for that file:

<?xml version="1.0" encoding="UTF-8"?>
<ui:NestedRulesPlugin
        xmlns:exml="http://www.jangaroo.net/exml/0.8"
        xmlns="exml:ext.config"
        xmlns:ui="exml:com.coremedia.ui.config"
        xmlns:fx="http://ns.adobe.com/mxml/2009">
    <fx:Script><![CDATA[
    private var config:TwitterCkEditorPlugin;

    public native function TwitterCkEditorPlugin(config:TwitterCkEditorPlugin = null);
    ]]></fx:Script>


  <ui:rules>
    <ui:RichTextArea>
      <ui:plugins>
        <ui:AddCKEditorPluginsPlugin plugins="tweetabletext" />
        <ui:CustomizeCKEditorPlugin>
          <ui:ckConfig>
          </ui:ckConfig>
        </ui:CustomizeCKEditorPlugin>
      </ui:plugins>
    </ui:RichTextArea>
  </ui:rules>
</ui:NestedRulesPlugin>

  • Create TwitterCkEditorMenuPlugin.mxml

  • add the following toolbar code

  • Create MyPluginCkEditorStudioPlugin.mxml

<?xml version="1.0"?>
<ui:NestedRulesPlugin
        xmlns:exml="http://www.jangaroo.net/exml/0.8"
        xmlns="exml:ext.config"
        xmlns:ui="exml:com.coremedia.ui.config"
        xmlns:editor="exml:com.coremedia.cms.editor.sdk.config"
        xmlns:fx="http://ns.adobe.com/mxml/2009"
        xmlns:local="com.coremedia.blueprint.twitterckeditor.studio.*">

  <fx:Script><![CDATA[
    import com.coremedia.cms.editor.sdk.premular.fields.RichTextPropertyField;
    import com.coremedia.ui.ckeditor.RichTextAction;

    private var config:TwitterCkEditorMenuPlugin;

    public native function TwitterCkEditorMenuPlugin(config:TwitterCkEditorMenuPlugin = null);
    ]]></fx:Script>

  <ui:rules>
    <Toolbar>
      <plugins exml:mode="append">
        <ui:AddItemsPlugin>
          <ui:items>
            <ui:IconButton itemId="twitter-icon"
                           iconCls="twitter-ck-icon"
                           tooltip="Twitter Markup"
                           text="Twitter CK">
              <ui:baseAction>
                <ui:RichTextAction commandName="tweetabletext" />
              </ui:baseAction>
            </ui:IconButton>
          </ui:items>
          <ui:after>
          </ui:after>
        </ui:AddItemsPlugin>
      </plugins>
    </Toolbar>
  </ui:rules>

</ui:NestedRulesPlugin>

  • add TwitterCkEditorPlugin.mxml & TwitterCkEditorMenuPlugin.mxml

<?xml version="1.0"?>

<editor:StudioPlugin
  xmlns:fx="http://ns.adobe.com/mxml/2009"
  xmlns:local="com.coremedia.blueprint.twitterckeditor.studio.*"
  xmlns:exml="http://www.jangaroo.net/exml/0.8"
  xmlns="exml:ext.config"
  xmlns:ui="exml:com.coremedia.ui.config"
  xmlns:editor="exml:com.coremedia.cms.editor.sdk.config"
  xmlns:u="exml:untyped">
<fx:Metadata>

</fx:Metadata>
  <fx:Script><![CDATA[
    public static const xtype:String = "com.coremedia.blueprint.twitterckeditor.studio.config.twitterCkEditorStudioPlugin";

    private var config:TwitterCkEditorStudioPlugin;

    public native function TwitterCkEditorStudioPlugin(config:TwitterCkEditorStudioPlugin = null);
    ]]></fx:Script>

  <editor:rules>

    <editor:RichTextPropertyField>
      <editor:plugins>
        <local:TwitterCkEditorPlugin/>
        <local:TwitterCkEditorMenuPlugin />
      </editor:plugins>
    </editor:RichTextPropertyField>

  </editor:rules>
</editor:StudioPlugin>

  • Create manifest.xml

<?xml version="1.0"?>
<componentPackage>
  <component class="com.coremedia.blueprint.twitterckeditor.studio.TwitterCkEditorMenuPlugin"/>
  <component class="com.coremedia.blueprint.twitterckeditor.studio.TwitterCkEditorPlugin"/>
</componentPackage>

These 3 plugin files & the manifest tell the Studio runtime that a new plugin has been added to the RichTextArea (aka the CK Editor) and that it is to utilize the toolbar code plugin (MyPluginCkEditorMenuPlugin.mxml)

2. Now build the Studio Webapp via maven: mvn clean install -pl :studio-webapp -am -DskipTests

  • This step could take 7-10mins depending on the hardware

3. Run studio if the build was successful: mvn tomcat7:run inside of modules/studio/studio-webapp

Note: optionally provide the -Dinstallation.host=<url of local/remote host> to run studio against another environment. Local studio version and remote studio version need to be the same version (i.e 1901, 1904, etc)

4. Check if plugin was loaded (in this case the Twitter Icon shows on the richtext toolbar for CMArticles)

5. Once the plugin is loaded, all that’s left is to make sure that the actual javascript logic functions properly inside the studio plugin (this can be troubleshot quickly through browser tools).

Specific Customizations for Tweetable Text

As part of the integration work done for this specific plugin, the default logic of the plugin needed to be revised to better work within the CoreMedia studio. All this frontend work was done utilizing the CoreMedia frontend workflow, that easily lets developers create & modify their themes/frontend logic without having to rebuild the entire Content Application Engine. This workflow utilizes yarn to automatically push updated webpacks to the CoreMedia host server whenever changes are made to the files!

CKEDITOR.plugins.add('tweetabletext', {
  icons: 'tweetabletext',
  beforeInit: function () {

  },

  init: function(editor) {
    //editor.addCommand('tweetabletext', new CKEDITOR.dialogCommand('tweetabletextDialog'));
    editor.addCommand('tweetabletext', new CKEDITOR.command(editor, {
      exec: function(editor) {

        var selection            = editor.getSelection();
        var range                = selection.getRanges()[ 0 ];
        var html = "";
        var className = range.startContainer.$.className;
        if (className !== "tweetabletext") {
          // if text isnt already 'tweetable', mark it as tweetable and insert new HTML
          var textToWrap     = selection.getSelectedText();
          html  = '<a class="tweetabletext" _xlink:href="#">';
          html += textToWrap + '</a>';

          //html    += '<img src="' + iconPath + ' alt="twitter icon" >';
        } else {
          // set new html to be the inside text of selection with tweetable status
          html = getSelectionHtml();
        }

        // clean old tweetabletext instances
        $(".tweetabletext").each(function() {
          var value = $(this).text();
          if(value == null || value.length === 0 || value === ' ' || value === " ") {
            $(this).remove();
          }
        });

        // insert new selection
        editor.insertHtml(html);
      }
    }));

    //editor.addCommand('untweetabletext', new CKEDITOR.unlinkCommand());

    editor.ui.addButton('TweetableText', {
      label: 'Insert TweetableText',
      command: 'tweetabletext',
      toolbar: 'insert'
    });

    if (typeof editor.config.contentsCss === 'object') {
      editor.config.contentsCss.push(CKEDITOR.getUrl(this.path + 'css/tweetabletext.css'));
    }
  }
});

function getSelectionHtml() {
  var html = "";
  if (typeof window.getSelection != "undefined") {
    var sel = window.getSelection();
    if (sel.rangeCount) {
      var container = document.createElement("div");
      for (var i = 0, len = sel.rangeCount; i < len; ++i) {
        container.appendChild(sel.getRangeAt(i).cloneContents());
      }
      html = container.innerHTML;
    }
  } else if (typeof document.selection != "undefined") {
    if (document.selection.type == "Text") {
      html = document.selection.createRange().htmlText;
    }
  }
  return html;
}

For this exercise we have streamlined the main plugin.js file to contain all logic necessary to mark the text as tweetable

The code below is the js code that is invoked when the studio-user selects the twitter button

We also added appropriate front end logic (the end user site) to facilitate the opening of the Twitter link

First was to create a new ‘brick’ within the frontend workspace. In CoreMedia, bricks serve as generic frontend logic packages, usually a specific brick for each layout in the system. These bricks are typically available across all sites and e-commerce systems regardless of the theme used, so this is the best place to put this logic.

The project files to the right are located within:

<workspace>/modules/frontend/lib/bricks

The twitter-richtext.js contains all the on-click logic for the tweetable text:

//import * as logger from "@coremedia/js-logger";
import $ from "jquery";
/**
 * Displays a simple text in the console.
 *
 * @function consolePrint
 * @param {String} $text - The text to be displayed in the console.
 */
export function consolePrint($text) {
//  logger.log($text);
}

$(function() {
// abowers - added to support frontend useage of twitter extension
  $(".tweetabletext").on("click", function (e) {

    let twitterBaseUrl = 'http://twitter.com/intent/tweet?text=';
    let text = e.target.innerText + "\n\n" + window.location.href;
    text = encodeURI(text);
    twitterBaseUrl += text;
    let win = window.open(twitterBaseUrl, '_blank');
    if (win) {
      //Browser has allowed it to be opened
      win.focus();
    } else {
      //Browser has blocked it
      alert('Please allow popups for this website');
    }
  });
});

The CSS utilizes the standard CoreMedia frontend development framework of SCSS to build out the needed CSS logic in a modular format.  All the CSS for the frontend for this module is in _tweetable-text.scss. The _partials.scss file imports the tweetable text scss, and they are compiled by the yarn workflow into regular CSS.

_tweetable-text.scss:

.tweetabletext {
  background: none repeat scroll 0% 0% #FFFFFF;
  color: #555;
  text-decoration: none;
  font-weight: normal;
  border: none;
}

.tweetabletext::after {
  padding-left: 5px;

  content:url("../../img/tweetabletext.png");
}

.tweetabletext:hover {
  background-color: #E2F1F9;
}

_partials.scss: (add this line)

@import "partials/tweetable-text";

The next step is to compile the new brick with the following commands, ran inside of the brick’s folder

  1. yarn –production run
  2. yarn install

The last steps are to update the site theme to utilize the new brick code. This is done inside of the package.json inside of the theme folder. Add the line in bold to your theme dependencies block in the json:

"dependencies": {
...
"@coremedia/brick-twitter-richtext": "^1.0.0",
...
}

Finally, inside of the theme folder run the following commands to recompile & build the theme with the new brick.

  1. yarn –production run
  2. yarn install
  3. yarn start (to test theme in developer mode)

From here, if the brick and theme were updated correctly, add the final theme to the deployed CoreMedia studio, and the work is done.

In case you missed the Demo Jam video I put together, you can see this extension in action in under 3 minutes:

Advertisements

DemoJam: CoreMedia Studio Extensions!

In this quick three minute tutorial I show a CoreMedia Studio extension my team put together based on a CK Editor plugin that enables editors to tag lines of text in articles to be “tweetable” by the reader. A sample would be: this gives your readers an easy way to tweet quotes from your articles!

Stay tuned here for a tutorial for this extension with source code and be sure to subscribe to my new YouTube Channel: CoreMedia Demo Jams!

The key to iconic content management is velocity

Last week, at Salesforce Connections, I got the opportunity to listen to Brian Fortier of Deckers Brands (UGG, Hoka One One, Sanuk and Teva) tell how they used CoreMedia to deliver relevant and in the moment content, improve their speed to market, and decrease developer dependency for a race campaign they ran for their running shoes – Hoka One One. I took a few pictures of Brian telling his amazing story at the event (see below).

Imagine delivering 12 hours of live streaming on the home page and switching that home page three times in less than 12 hours – I would argue that most companies could not do this. Have six landing pages with race results and serving over 250,000 visitors.  Site performance, cache invalidation, and limiting dependecy on developers were just a few of the factors in play here.

With an implementation time of less than 60 days with over 50 unique templates, I would say Brian and his team achieved some amazing results.

Being able to achieve this velocity requires a tool that is driven by business people for the quickest results. Relying on developers to deliver this experience is a thing of the past. Businesses require speed and agility to react to real-time events, this is exactly what CoreMedia Content Cloud was designed for. Such a great story and I am glad I got to hear it!

Want to see how eCommerce business users can completely control the digital experience?

Then you need to come to the CoreMedia booth (#20) at Salesforce Connections in Chicago!  Red Hots, Wrigley Field and real Salesforce insights. That’s summer in Chicago with Salesforce Connections, taking place Jun 17-19, 2019 at McCormick Place West. I will be at the booth and would love to give you a personal demonstration of our CoreMedia Content Cloud.

VISIT US AT BOOTH #20 (CLICK HERE)

 

Let’s plan to connect so I can show you how to bring your product stories to life across a seamless customer journey with our Content Cloud platform.

Just to give you a taste for how great CoreMedia is with Salesforce, take a look at some material I posted on LinkedIn.

Screen Shot 2019-06-05 at 12.03.57 PM

If you don’t have or use LinkedIn then you can take a look at my Made Easy with CoreMedia video series on YouTube where I have a collection of videos showing how easy digital experience can be with a first class brand management system.

Webinar: Maximize your IBM Commerce Investment

Are you ready to hear how you can move forward with IBM Commerce? Do you have questions about your options for maximizing your investment? If so, join Brent Murray from CoreMedia and Rick Miller from Zilker in this 30 minute webinar and hear how your brand can be iconic with IBM Commerce, CoreMedia and Zilker!

Seize the Opportunity to Maximize your IBM Commerce Investment

Thur Jun 20, 2019 • 1-1:30 pm Eastern

Investing in IBM’s eCommerce platform was smart. But the recent divestiture of WebSphere Commerce to HCL likely raises some questions. What can you do to keep your WebSphere Commerce investment on track and moving forward?

Join CoreMedia and Zilker for this exclusive 30-minute webinar and we’ll walk you through everything you need to know to transform your customer experiences with advanced brand management and turn disruption into opportunity.

Register Today!

 

 

Microsoft Word to online article in a snap

Screen Shot 2019-05-30 at 12.52.42 PM.pngI don’t think the genie in Aladdin can do something this cool so quickly. You might be thinking, well… I have been able to save Word documents to HTML since 2000 and my answer would be “yes, but can you save it with streamlined HTML into a content management system and retain the rich text and images?”. 

One of the frustrating pieces about the HTML Microsoft Word generates is that it’s filled with all kinds of nonsense tags and CSS, it’s barely legible. Case in point is this is the HTML for the article referenced in the video that Microsoft Word generates:

<p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto;
line-height:normal;background:#FCFCFC'><b style='mso-bidi-font-weight:normal'><span
style='font-size:10.5pt;font-family:"VarelaRegular",serif;mso-fareast-font-family:
"Times New Roman";mso-bidi-font-family:"Times New Roman";color:#3E3D3D;
mso-ansi-language:EN-US;mso-fareast-language:DE'>In Speed Flying and Speed
Riding, everything happens VERY fast.<o:p></o:p></span></b></p>

<p class=MsoNormal style='mso-margin-top-alt:auto;mso-margin-bottom-alt:auto;
line-height:normal;background:#FCFCFC'><span style='font-size:10.5pt;
font-family:"VarelaRegular",serif;mso-fareast-font-family:"Times New Roman";
mso-bidi-font-family:"Times New Roman";color:#3E3D3D;mso-ansi-language:EN-US;
mso-fareast-language:DE'>It makes sense to build a solid foundation of glider
control skills in a slower, more forgiving aircraft before you move to the big
time. Even a single day lesson will teach you valuable lessons about our
incredible aircraft and you will experience the magic of flight for the first
time. To get started just visit one of the speed riding schools in Chamonix and
go fast!</span></p>

Not pretty!

That same document imported into CoreMedia has this around the same block:

Screen Shot 2019-05-30 at 3.58.29 PM.png

The other aspect is just getting the document into your CMS with all of the images properly uploaded and referenced in the article can also be a challenge.

In this video, watch how easy it is to take an MS Word document and import it into CoreMedia Studio; preserving the rich text and images and using the CoreMedia Studio to fix the various crop renderings of the teasers across the social channels.

 

The Best CMS for SAP Commerce Cloud

Yes, that is correct. I have said this for IBM WebSphere Commerce, I have said this for SalesForce, and I will say it for SAP Commerce Cloud. Our SAP Hybris integration is just as advanced as our other eCommerce integrations and to show you, I have started to build a play list dedicated to demonstrating why CoreMedia is a great fit for SAP Hybris customers.

With over 200+ Customers and a 98% Renewal Rate, we must be doing something right. link

Being able to have complete control over product and content from a single user interface (CoreMedia Studio) is key. Watch these introductory videos and then stay up to date by subscribing to the channel as I create more videos about CoreMedia and SAP Commerce Cloud!

Dynamic Product and Content Collections

One of the things talked about at SAP CX Live conference this week in Orlando was product and content collections. While CoreMedia Studio lets you define static product and content widgets, it also supports rule driven product collections – or dynamic collections. This means, based on a rule you define, you can dynamically display products mixed with content.

Continue reading

Made Easy with CoreMedia: SAP Commerce Cloud – Part 2

This second part in the SAP Commerce Cloud series I demonstrate how easily you can create a new banner for the home page and place it in the carousel. From configuring your crops to identifying teaser targets, it’s all dead simple with CoreMedia studio.

 

Discover firsthand how customer experience is the new competitive advantage at SAP CX LIVE 2019 booth #123, taking place May 7-8, 2019 in Orlando at the Orange County Convention Center West. Gain can’t-miss insights to understand your customers, transform your business model, and provide exceptional experiences while building trust and loyalty.

Made Easy with CoreMedia: SAP Commerce Cloud – Part 1

This first part in the SAP Commerce Cloud series shows our sample website and the various modules we have. From shoppable images and videos to 360 degree spinners and complete product and category integration it is all seamless to the marketer and site manager.

 

Discover firsthand how customer experience is the new competitive advantage at SAP CX LIVE 2019 booth #123, taking place May 7-8, 2019 in Orlando at the Orange County Convention Center West. Gain can’t-miss insights to understand your customers, transform your business model, and provide exceptional experiences while building trust and loyalty.