Congratulations!

[Valid RSS] This is a valid RSS feed.

Recommendations

This feed is valid, but interoperability with the widest range of feed readers could be improved by implementing the following recommendations.

Source: http://camerongregor.com/category/software-development/feed/

  1. <?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
  2. xmlns:content="http://purl.org/rss/1.0/modules/content/"
  3. xmlns:wfw="http://wellformedweb.org/CommentAPI/"
  4. xmlns:dc="http://purl.org/dc/elements/1.1/"
  5. xmlns:atom="http://www.w3.org/2005/Atom"
  6. xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
  7. xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
  8. >
  9.  
  10. <channel>
  11. <title>Software Development &#8211; Cameron Gregor</title>
  12. <atom:link href="http://camerongregor.com/category/software-development/feed/" rel="self" type="application/rss+xml" />
  13. <link>http://camerongregor.com</link>
  14. <description>Sharing Tips, Tools and Techniques for XPages Developers</description>
  15. <lastBuildDate>Fri, 21 Jul 2017 13:56:45 +0000</lastBuildDate>
  16. <language>en-AU</language>
  17. <sy:updatePeriod>hourly</sy:updatePeriod>
  18. <sy:updateFrequency>1</sy:updateFrequency>
  19. <generator>https://wordpress.org/?v=4.8</generator>
  20. <item>
  21. <title>Swiper Official Version 2 Release</title>
  22. <link>http://camerongregor.com/2017/07/21/swiper-official-version-2-release/</link>
  23. <comments>http://camerongregor.com/2017/07/21/swiper-official-version-2-release/#comments</comments>
  24. <pubDate>Fri, 21 Jul 2017 13:52:52 +0000</pubDate>
  25. <dc:creator><![CDATA[camerongregor]]></dc:creator>
  26. <category><![CDATA[Software Development]]></category>
  27. <category><![CDATA[designer]]></category>
  28. <category><![CDATA[sourcecontrol]]></category>
  29. <category><![CDATA[swiper]]></category>
  30.  
  31. <guid isPermaLink="false">http://camerongregor.com/?p=495</guid>
  32. <description><![CDATA[So I have finally posted Swiper version 2 to OpenNTF! (and Github of course) Swiper OpenNTF Project Page Swiper Github Releases The latest version is 2.0.1,  and is the same as 2.0.0beta but with&#46;&#46;&#46;]]></description>
  33. <content:encoded><![CDATA[<p>So I have finally posted Swiper version 2 to OpenNTF! (and Github of course)</p>
  34. <ul>
  35. <li><a href="https://www.openntf.org/main.nsf/project.xsp?r=project/Swiper/summary" target="_blank" rel="noopener">Swiper OpenNTF Project Page</a></li>
  36. <li><a href="https://github.com/camac/Swiper/releases" target="_blank" rel="noopener">Swiper Github Releases</a></li>
  37. </ul>
  38. <p>The latest version is 2.0.1,  and is the same as 2.0.0beta but with a bug fix for the toolbar buttons.</p>
  39. <p>Swiper 2.0.0 beta has been available on the Github project site for a few months, and the core functionality of it works as planned. There was however a bug within the shortcut buttons that I added to the menu bar, and this is a bit of a nasty bug that can cause deletion of design elements.</p>
  40. <p>Why has it taken me so long to fix this bug and prepare the final release? to be perfectly honest with you, I just couldn&#8217;t muster up the motivation to get this done. My development process for Swiper involves launching Domino Designer from my eclipse IDE.  After I upgraded my IBM Notes to 9.0.1 FP8 my designer launch settings no longer worked. By this time frankly my tank was empty. I knew re-configuring the launch settings was destined to be a fiddly encounter, and entire nights can be wasted with no fruitful results. All my extra-curricular projects are done in my spare-time, and this usually means between 8:30pm-11:30pm, and after a full day of problem-solving at work, I just couldn&#8217;t muster up the courage to face a task that had a high-probability of failure. I was satisfied that the beta version was available, and the project would have to wait until I felt the motivation again.</p>
  41. <p>Well this week, I noticed a tweet from Per Henrik Lausten about &#8216;Open Source Friday&#8217;.</p>
  42. <blockquote class="twitter-tweet" data-lang="en">
  43. <p dir="ltr" lang="en">Contribute to open source on <a href="https://twitter.com/hashtag/OpenSourceFriday?src=hash">#OpenSourceFriday</a> (by <a href="https://twitter.com/github">@github</a>):<a href="https://t.co/EeHpi9UphG">https://t.co/EeHpi9UphG</a></p>
  44. <p>— Per Henrik Lausten (@perlausten) <a href="https://twitter.com/perlausten/status/887551759731871744">July 19, 2017</a></p></blockquote>
  45. <p><script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script></p>
  46. <p>It made me think a little about Swiper, and I thought I would give it another go. I also felt validated for taking my little break from the project when I read the &#8216;<a href="https://opensource.guide/best-practices/#its-okay-to-hit-pause" target="_blank" rel="noopener">It&#8217;s ok to hit pause</a>&#8216; section of the Open Source best practices for Maintainers <img src="https://s.w.org/images/core/emoji/2.3/72x72/1f642.png" alt="🙂" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
  47. <p>So I needed to fix my Designer Launch settings, I really don&#8217;t know how to do this by myself and have relied on others for this information. Previously it was Mikkel Heisterberg, and then for the past few years I turned to <a href="https://twitter.com/RalfMPetter" target="_blank" rel="noopener">Ralf M Petter</a>. I was very sad to see recent news that Ralf passed away from a cancer related illness. I would just like to express my sympathy to Ralf&#8217;s family for their loss. Ralf&#8217;s blog posts on setting up Eclipse to launch Notes and Designer were extremely helpful to me whilst developing Swiper and other plugins that I have made for Notes and Designer. Ralf was always quick to respond to my questions and his expertise will be missed.</p>
  48. <p>A couple of months ago I had asked Ralf if he knew how to launch Designer FP8 from eclipse and surely enough within a day he had provided me with updated instructions, I had also asked Gary Marjoram @ IBM for some advice and armed with all this advice I was able to get FP8 launching again after a couple of hours tweaking.</p>
  49. <p>Tonight I was able to debug the toolbar buttons again and therefore I was able to release the official version 2.</p>
  50. <p>Let me know if you have any problems or suggestions!</p>
  51. ]]></content:encoded>
  52. <wfw:commentRss>http://camerongregor.com/2017/07/21/swiper-official-version-2-release/feed/</wfw:commentRss>
  53. <slash:comments>5</slash:comments>
  54. </item>
  55. <item>
  56. <title>Auto-width Bootstrap Column XPages Controls</title>
  57. <link>http://camerongregor.com/2017/03/22/auto-width-bootstrap-column-xpages-controls/</link>
  58. <comments>http://camerongregor.com/2017/03/22/auto-width-bootstrap-column-xpages-controls/#respond</comments>
  59. <pubDate>Tue, 21 Mar 2017 13:13:18 +0000</pubDate>
  60. <dc:creator><![CDATA[camerongregor]]></dc:creator>
  61. <category><![CDATA[Software Development]]></category>
  62. <category><![CDATA[bootstrap]]></category>
  63. <category><![CDATA[control]]></category>
  64. <category><![CDATA[xpages]]></category>
  65.  
  66. <guid isPermaLink="false">http://camerongregor.com/?p=479</guid>
  67. <description><![CDATA[I&#8217;ve been stuck working with OneUI Version 3 for the past couple of years, due to a regretful decision made at the beginning of my major project. OneUI was better than nothing but very frustrating&#46;&#46;&#46;]]></description>
  68. <content:encoded><![CDATA[<p>I&#8217;ve been stuck working with <a href="http://infolib.lotus.com/resources/oneui/3.0/docPublic/examples.htm?content=2colGrid.htm" target="_blank">OneUI Version 3</a> for the past couple of years, due to a regretful decision made at the beginning of my major project. OneUI was better than nothing but very frustrating at times.<br />
  69. Finally, I have moved on to my next project and I am now using bootstrap (version 3)</p>
  70. <p>A common task when laying out a page using bootstrap is to divide sections up into rows and columns, and use the appropriate css styles to do so. I&#8217;m going to assume you are familiar with bootstrap&#8217;s grid system, and if not I recommend you <a href="http://getbootstrap.com/css/#grid" target="_blank">hearing it from the horse&#8217;s mouth</a> instead of me trying to explain it again.</p>
  71. <p>I like creating XspLibrary controls, so I decided I would go ahead and create row and column controls. It could be seen as a little overkill and you can easily live without it, but I see the <a href="http://showcase.bootsfaces.net/layout/basic.jsf" target="_blank">Bootfaces project</a> did it, so I took inspiration from their container, row and column controls.</p>
  72. <p>I then also added the ability for the columns to have their widths automatically distributed. Auto-widths is a feature that is <a href="https://v4-alpha.getbootstrap.com/layout/grid/#auto-layout-columns" target="_blank">coming with bootstrap 4</a>,  but for now we need to do it ourselves!</p>
  73. <h2>Example XPages Markup</h2>
  74. <p>The following examples end up looking like this: (<strong>Note:</strong> the blue border is just added for demo purposes to show the outline of the columns)</p>
  75. <p><img class="aligncenter size-full wp-image-481" src="http://camerongregor.com/wp-content/uploads/2017/03/BootstrapDemoPage.jpg" alt="" width="981" height="140" srcset="http://camerongregor.com/wp-content/uploads/2017/03/BootstrapDemoPage.jpg 981w, http://camerongregor.com/wp-content/uploads/2017/03/BootstrapDemoPage-300x43.jpg 300w, http://camerongregor.com/wp-content/uploads/2017/03/BootstrapDemoPage-768x110.jpg 768w" sizes="(max-width: 981px) 100vw, 981px" /></p>
  76. <p>Here is a simple example of 2-column layout with equal width columns</p><pre class="crayon-plain-tag">&lt;gb:row id="row1"&gt;
  77.    &lt;gb:column id="column1"&gt;These two columns&lt;/gb:column&gt;
  78.    &lt;gb:column id="column2"&gt;are Equal Width&lt;/gb:column&gt;
  79. &lt;/gb:row&gt;</pre><p>Notice above how I haven&#8217;t specified any column widths? The columns will all become equal width, so in the example above, they will both be 6 columns wide (12 divided by 2 columns).</p>
  80. <p>If there have 3 columns, then they would each automatically be 4 columns wide (12 divided by 3 columns).</p><pre class="crayon-plain-tag">&lt;gb:row id="row5"&gt;
  81.    &lt;gb:column id="column3"&gt;These three &lt;/gb:column&gt;
  82.    &lt;gb:column id="column10"&gt;columns are also&lt;/gb:column&gt;
  83.    &lt;gb:column id="column11"&gt;Equal Width&lt;/gb:column&gt;
  84. &lt;/gb:row&gt;</pre><p></p>
  85. <h3>Specifying a column width</h3>
  86. <p>If you want a column to be a specifc width, you use the span property to specify the width for that column, the remaining columns will receive their fair share of whatever is left over.</p><pre class="crayon-plain-tag">&lt;gb:row id="row2"&gt;
  87.    &lt;gb:column id="column4"&gt;These columns have&lt;/gb:column&gt;
  88.    &lt;gb:column id="column5" span="6"&gt;the middle with span="6"&lt;/gb:column&gt;
  89.    &lt;gb:column id="column6"&gt;and the outer columns are auto width&lt;/gb:column&gt;
  90. &lt;/gb:row&gt;</pre><p></p>
  91. <h3>Specifying alignment</h3>
  92. <p>I&#8217;ve also added an align property so I can quickly align the contents.</p><pre class="crayon-plain-tag">&lt;gb:row id="row4"&gt;
  93.    &lt;gb:column id="column7" align="left"&gt;This is align left (default)&lt;/gb:column&gt;
  94.    &lt;gb:column id="column8" align="center"&gt;This is align middle&lt;/gb:column&gt;
  95.    &lt;gb:column id="column9" align="right"&gt;This is align left&lt;/gb:column&gt;
  96. &lt;/gb:row&gt;</pre><p></p>
  97. <h3>Full Row column</h3>
  98. <p>If you want to use the entire row, then you can just leave the column definition out entirely and the contents will automatically be wrapped in a col-md-12 column</p><pre class="crayon-plain-tag">&lt;gb:row id="row3"&gt;
  99. If you want a Full Row, then just don't include columns
  100. &lt;/gb:row&gt;</pre><p></p>
  101. <h3>Using style and styleClass</h3>
  102. <p>You can still use style and styleClass properties as you normally would.</p>
  103. <h3>Container Control</h3>
  104. <p>If you are using one of the extension library bootstrap applicationLayouts, then you won&#8217;t need to use the container control because it is already covered by that. But, If you are rolling your own you can use the container layout which has a fluid property to control whether is is a bootstrap fluid layout or not.</p><pre class="crayon-plain-tag">&lt;gb:container id="container1" fluid="true"&gt;
  105.  
  106.    &lt;!-- rows, columns, your content --&gt;
  107.  
  108. &lt;/gb:container&gt;</pre><p></p>
  109. <h2>Overview of creating the controls</h2>
  110. <p>Here is what we are going to do:</p>
  111. <ol>
  112. <li>Create the Container UIComponent</li>
  113. <li>Create the Column UIComponent</li>
  114. <li>Create the Row UIComponent</li>
  115. <li>Provide Control Definitions via an xsp-config file so that designer knows how to use them</li>
  116. <li>Program in the auto-width algorithm</li>
  117. </ol>
  118. <p>Notice we are not going to create a renderer. We will be extending the panel control, and the existing panel renderer will be used automatically.</p>
  119. <p>For each component we are just going to <em>extend </em>the existing <strong>&lt;xp:panel&gt;</strong> control to re-use all the existing functionality, and just add a few extra properties.</p>
  120. <h3>Creating the Container Component</h3>
  121. <p>The container component is simply an extension of the panel control (UIPanelEx) with an additional property &#8216;fluid&#8217;. We add the getters and setters for the fluid property, and make sure it is included in the restore and save state operations.</p>
  122. <p>We also override the getStyleClass method so that it will always concatenate &#8216;container&#8217; or &#8216;container-fluid&#8217; depending on the fluid property.</p><pre class="crayon-plain-tag">package com.gregorbyte.xsp.component.bootstrap;
  123.  
  124. import javax.faces.context.FacesContext;
  125. import javax.faces.el.ValueBinding;
  126.  
  127. import com.gregorbyte.xsp.util.GregorbyteUtil;
  128. import com.ibm.xsp.component.UIPanelEx;
  129.  
  130. public class UIContainer extends UIPanelEx {
  131.  
  132. public Boolean fluid;
  133.  
  134. public UIContainer() {
  135.  
  136. }
  137.  
  138. @Override
  139. public String getStyleClass() {
  140.  
  141. String parent = super.getStyleClass();
  142.  
  143. if (isFluid()) {
  144. return GregorbyteUtil.concatStyleClasses("container-fluid", parent);
  145. } else {
  146. return GregorbyteUtil.concatStyleClasses("container", parent);
  147. }
  148.  
  149. }
  150.  
  151. public Boolean isFluid() {
  152.  
  153. if (this.fluid != null) {
  154. return this.fluid;
  155. }
  156.  
  157. ValueBinding vb = getValueBinding("fluid");
  158.  
  159. if (vb != null) {
  160. return (Boolean) vb.getValue(getFacesContext());
  161. }
  162.  
  163. return false;
  164.  
  165. }
  166.  
  167. public void setFluid(Boolean fluid) {
  168. this.fluid = fluid;
  169. }
  170.  
  171. @Override
  172. public void restoreState(FacesContext context, Object state) {
  173.  
  174. Object[] values = (Object[]) state;
  175.  
  176. super.restoreState(context, values[0]);
  177. fluid = (Boolean) values[1];
  178.  
  179. }
  180.  
  181. @Override
  182. public Object saveState(FacesContext context) {
  183.  
  184. Object[] values = new Object[2];
  185.  
  186. values[0] = super.saveState(context);
  187. values[1] = fluid;
  188.  
  189. return values;
  190.  
  191. }
  192.  
  193. }</pre><p></p>
  194. <h3>Creating the Column Component</h3>
  195. <p>The column component is a little more complex. We are also going to extend the UIPanelEx control but we will also add some control properties</p>
  196. <ul>
  197. <li><strong>span</strong> &#8211; This will be an integer that can be set to specify the column span of the bootstrap grid</li>
  198. <li><strong>align</strong> &#8211; This will be a String which can be &#8216;left&#8217; (Default), &#8216;center&#8217; or &#8216;right&#8217;. This will then add the necessary bootstrap css class to perform aligning of the contents</li>
  199. </ul>
  200. <p>I am also going to add a property <strong>guessSpan </strong>which is not exposed to domino designer and will only be used to store our &#8216;guesstimate&#8217; column span when calculating the auto widths.</p>
  201. <p>Then, I create a private method &#8216;getEffectiveSpan&#8217; which will check if a column span has been explicitly set with the <strong>span</strong> property and if so it will use that. If a span was not set, it will use the &#8216;guessSpan&#8217; that was guessed via the algorithm</p>
  202. <p>Then we override the &#8216;getStyleClass&#8217; method, so that when the renderer comes along and asks for the styleClasses, we can inject some styleClasses of our own.</p>
  203. <p>We are using the very handy method from Extension Library&#8217;s ExtLibUtil, which allows us to easily concatenate styles. I have copied the method into my own Util class so that there will be no dependency on the Extension Library.</p>
  204. <p>First we ask for any styles that have been explicitly set. Then we concatenate our column style from the bootstrap grid system e.g. &#8220;col-md-8&#8221;, and then we concatenate an alignment style if one was set on the column.</p><pre class="crayon-plain-tag">package com.gregorbyte.xsp.component.bootstrap;
  205.  
  206. import javax.faces.context.FacesContext;
  207. import javax.faces.el.ValueBinding;
  208.  
  209. import com.gregorbyte.xsp.util.GregorbyteUtil;
  210. import com.ibm.commons.util.StringUtil;
  211. import com.ibm.xsp.component.UIPanelEx;
  212.  
  213. public class UIColumn extends UIPanelEx {
  214.  
  215. private Integer guessSpan = null;
  216.  
  217. // Defaults for Medium Columns, use styleClass for specific layouts
  218. private Integer span = null;
  219.  
  220. private String align = null;
  221.  
  222. public UIColumn() {
  223. super();
  224. }
  225.  
  226. private Integer getEffectiveSpan() {
  227. if (getSpan() != null) {
  228. return getSpan();
  229. } else if (getGuessSpan() != null) {
  230. return getGuessSpan();
  231. }
  232.  
  233. return 12;
  234. }
  235.  
  236. @Override
  237. public String getStyleClass() {
  238.  
  239. String styleClass = super.getStyleClass();
  240.  
  241. styleClass = GregorbyteUtil.concatStyleClasses(styleClass, "col-md-" + getEffectiveSpan());
  242.  
  243. String align = getAlign();
  244.  
  245. if (StringUtil.equals(align, "left")) {
  246. styleClass = GregorbyteUtil.concatStyleClasses(styleClass, "text-left");
  247. } else if (StringUtil.equals(align, "right")) {
  248. styleClass = GregorbyteUtil.concatStyleClasses(styleClass, "text-right");
  249. } else if (StringUtil.equals(align, "center")) {
  250. styleClass = GregorbyteUtil.concatStyleClasses(styleClass, "text-center");
  251. }
  252.  
  253. return styleClass;
  254.  
  255. }
  256.  
  257. public Integer getGuessSpan() {
  258. return guessSpan;
  259. }
  260.  
  261. public void setGuessSpan(Integer guessSpan) {
  262. this.guessSpan = guessSpan;
  263. }
  264.  
  265. public Integer getSpan() {
  266.  
  267. if (this.span != null) {
  268. return this.span;
  269. }
  270.  
  271. ValueBinding vb = getValueBinding("span");
  272.  
  273. if (vb != null) {
  274. return (Integer) vb.getValue(getFacesContext());
  275. }
  276.  
  277. return null;
  278.  
  279. }
  280.  
  281. public void setSpan(Integer span) {
  282. this.span = span;
  283. }
  284.  
  285. public String getAlign() {
  286.  
  287. if (this.align != null) {
  288. return this.align;
  289. }
  290.  
  291. ValueBinding vb = getValueBinding("align");
  292.  
  293. if (vb != null) {
  294. return (String) vb.getValue(getFacesContext());
  295. }
  296.  
  297. return null;
  298.  
  299. }
  300.  
  301. public void setAlign(String align) {
  302. this.align = align;
  303. }
  304.  
  305. @Override
  306. public void restoreState(FacesContext context, Object state) {
  307.  
  308. Object[] values = (Object[]) state;
  309.  
  310. super.restoreState(context, values[0]);
  311.  
  312. guessSpan = (Integer) values[1];
  313. span = (Integer) values[2];
  314. align = (String) values[3];
  315.  
  316. }
  317.  
  318. @Override
  319. public Object saveState(FacesContext context) {
  320.  
  321. Object[] values = new Object[4];
  322.  
  323. values[0] = super.saveState(context);
  324. values[1] = guessSpan;
  325. values[2] = span;
  326. values[3] = align;
  327.  
  328. return values;
  329.  
  330. }
  331.  
  332. }</pre><p></p>
  333. <h3>Creating the Row Component</h3>
  334. <p>The row component is also an extension of the panel control. But we are going to add some extra behaviour to it. Just before the Row&#8217;s children (the columns) are Rendered we are going to calculate the necessary spans for any columns that did not have a span. If we can&#8217;t find any columns as children, then we will assume that we are supposed to wrap all the contents into a single column and therefore we will render a start and end tag for a &lt;div class=&#8221;col-md-12&#8243;&gt;</p>
  335. <h3>Programming the auto-width algorithm</h3>
  336. <p>The auto-width algorithm does the following</p>
  337. <ol>
  338. <li>Finds the total span of columns that have been explicitly allocated using the <strong>span</strong> property</li>
  339. <li>Figures out the remaining span available (e.g. 12 &#8211; allocated)</li>
  340. <li>Figures out the average span to allocate to remaining columns without a span</li>
  341. <li>Allocates the &#8216;guessed&#8217; span to the remaining columns</li>
  342. </ol>
  343. <p></p><pre class="crayon-plain-tag">package com.gregorbyte.xsp.component.bootstrap;
  344.  
  345. import java.io.IOException;
  346. import java.util.ArrayList;
  347. import java.util.List;
  348.  
  349. import javax.faces.FacesException;
  350. import javax.faces.component.UIComponent;
  351. import javax.faces.context.FacesContext;
  352.  
  353. import com.gregorbyte.xsp.util.GregorbyteUtil;
  354. import com.ibm.xsp.component.UIPanelEx;
  355. import com.ibm.xsp.util.TypedUtil;
  356.  
  357. public class UIRow extends UIPanelEx {
  358.  
  359. private Boolean autoColumn = false;
  360.  
  361. public UIRow() {
  362. super();
  363.  
  364. }
  365.  
  366. @Override
  367. public String getStyleClass() {
  368.  
  369. String parent = super.getStyleClass();
  370.  
  371. return GregorbyteUtil.concatStyleClasses("row", parent);
  372.  
  373. }
  374.  
  375. @Override
  376. public void encodeBegin(FacesContext context) throws IOException {
  377.  
  378. super.encodeBegin(context);
  379.  
  380. calculateColumnSpans(context);
  381. if (autoColumn) {
  382. context.getResponseWriter().startElement("div", null);
  383. context.getResponseWriter().writeAttribute("class", "col-md-12", null);
  384. }
  385.  
  386. }
  387.  
  388. @Override
  389. public void encodeEnd(FacesContext context) throws IOException {
  390.  
  391. if (autoColumn) {
  392. context.getResponseWriter().endElement("div");
  393. }
  394.  
  395. super.encodeEnd(context);
  396. }
  397.  
  398. public void calculateColumnSpans(FacesContext context) throws FacesException {
  399.  
  400. List&lt;UIComponent&gt; kids = TypedUtil.getChildren(this);
  401.  
  402. List&lt;UIColumn&gt; cols = new ArrayList&lt;UIColumn&gt;();
  403.  
  404. int totalMediumSpecified = 0;
  405.  
  406. for (UIComponent comp : kids) {
  407. if (comp instanceof UIColumn) {
  408.  
  409. UIColumn col = (UIColumn) comp;
  410.  
  411. if (col.getSpan() != null) {
  412. totalMediumSpecified += col.getSpan();
  413. } else {
  414. cols.add(col);
  415. }
  416.  
  417. }
  418. }
  419.  
  420. // If No Columns we will render
  421. autoColumn = (cols.size() == 0);
  422.  
  423. if (autoColumn)
  424. return;
  425.  
  426. int remaining = 12 - totalMediumSpecified;
  427.  
  428. Integer colGuess = remaining / cols.size();
  429.  
  430. if (colGuess != null) {
  431. for (UIColumn uiColumn : cols) {
  432. uiColumn.setGuessSpan(colGuess);
  433. }
  434. }
  435.  
  436. }
  437.  
  438. @Override
  439. public void restoreState(FacesContext context, Object state) {
  440.  
  441. Object[] values = (Object[]) state;
  442.  
  443. super.restoreState(context, values[0]);
  444. autoColumn = (Boolean) values[1];
  445.  
  446. }
  447.  
  448. @Override
  449. public Object saveState(FacesContext context) {
  450.  
  451. Object[] values = new Object[2];
  452.  
  453. values[0] = super.saveState(context);
  454. values[1] = autoColumn;
  455.  
  456. return values;
  457.  
  458. }
  459.  
  460. }</pre><p></p>
  461. <h2>Provide Component Definition via xsp-config file</h2>
  462. <p>The xsp-config file tells Domino Designer everything it needs to know to include it on an XPage so we need to specify the properties that can be set.</p>
  463. <p>The file starts with namespace definition to say &#8216;all component definitions in this file are under this namespace&#8217; and then it specifies the 2 components.</p>
  464. <p>We provide the description, tagname, display name, corresponding java class (so it knows how to instantiate it) a component type (usually just the same as the class).</p>
  465. <p>Then we specify the properties that can be set. You can see for the row there is no extra properties, but for the column we have added the <strong>span</strong> and <strong>align </strong>properties and have provided combo options Left, Center and Right for the <strong>align</strong> property. For the container component, we have specified the <strong>fluid</strong> property.</p><pre class="crayon-plain-tag">&lt;?xml version="1.0" encoding="UTF-8"?&gt;
  466. &lt;faces-config&gt;
  467.  
  468. &lt;faces-config-extension&gt;
  469. &lt;namespace-uri&gt;http://www.gregorbyte.com/xsp/&lt;/namespace-uri&gt;
  470. &lt;default-prefix&gt;gb&lt;/default-prefix&gt;
  471. &lt;/faces-config-extension&gt;
  472.  
  473. &lt;component&gt;
  474.  
  475. &lt;description&gt;Row Container for Bootstrap Columns
  476. &lt;/description&gt;
  477. &lt;display-name&gt;Bootstrap Row&lt;/display-name&gt;
  478. &lt;component-type&gt;com.gregorbyte.xsp.bootstrap.Row
  479. &lt;/component-type&gt;
  480. &lt;component-class&gt;com.gregorbyte.xsp.component.bootstrap.UIRow
  481. &lt;/component-class&gt;
  482.  
  483. &lt;component-extension&gt;
  484.  
  485. &lt;base-component-type&gt;com.ibm.xsp.Panel
  486. &lt;/base-component-type&gt;
  487.  
  488. &lt;component-family&gt;javax.faces.Panel
  489. &lt;/component-family&gt;
  490.  
  491. &lt;tag-name&gt;row&lt;/tag-name&gt;
  492.  
  493. &lt;designer-extension&gt;
  494. &lt;in-palette&gt;true&lt;/in-palette&gt;
  495. &lt;category&gt;Gregorbyte Library&lt;/category&gt;
  496. &lt;/designer-extension&gt;
  497.  
  498. &lt;/component-extension&gt;
  499.  
  500. &lt;/component&gt;
  501.  
  502. &lt;component&gt;
  503.  
  504. &lt;description&gt;Bootstrap Columns
  505. &lt;/description&gt;
  506. &lt;display-name&gt;Bootstrap Column&lt;/display-name&gt;
  507. &lt;component-type&gt;com.gregorbyte.xsp.bootstrap.Column
  508. &lt;/component-type&gt;
  509. &lt;component-class&gt;com.gregorbyte.xsp.component.bootstrap.UIColumn
  510. &lt;/component-class&gt;
  511.  
  512. &lt;property&gt;
  513. &lt;description&gt;Adds an Alignment class to the column &lt;/description&gt;
  514. &lt;display-name&gt;Alignment&lt;/display-name&gt;
  515. &lt;property-name&gt;align&lt;/property-name&gt;
  516. &lt;property-class&gt;java.lang.String&lt;/property-class&gt;
  517. &lt;property-extension&gt;
  518. &lt;designer-extension&gt;
  519. &lt;editor&gt;com.ibm.workplace.designer.property.editors.comboParameterEditor&lt;/editor&gt;
  520. &lt;editor-parameter&gt;
  521. left
  522. center
  523. right
  524. &lt;/editor-parameter&gt;
  525. &lt;category&gt;basics&lt;/category&gt;
  526. &lt;/designer-extension&gt;
  527. &lt;/property-extension&gt;
  528. &lt;/property&gt;
  529.  
  530. &lt;property&gt;
  531. &lt;description&gt;Number of Columns to Span (Medium Screen size). Leave blank for auto calculation&lt;/description&gt;
  532. &lt;display-name&gt;Columns to Span&lt;/display-name&gt;
  533. &lt;property-name&gt;span&lt;/property-name&gt;
  534. &lt;property-class&gt;java.lang.Integer&lt;/property-class&gt;
  535. &lt;property-extension&gt;
  536. &lt;allow-run-time-binding&gt;true&lt;/allow-run-time-binding&gt;
  537. &lt;designer-extension&gt;
  538. &lt;category&gt;basics&lt;/category&gt;
  539. &lt;/designer-extension&gt;
  540. &lt;/property-extension&gt;
  541. &lt;/property&gt;
  542.  
  543. &lt;component-extension&gt;
  544.  
  545. &lt;base-component-type&gt;com.ibm.xsp.Panel
  546. &lt;/base-component-type&gt;
  547.  
  548. &lt;component-family&gt;javax.faces.Panel
  549. &lt;/component-family&gt;
  550.  
  551. &lt;tag-name&gt;column&lt;/tag-name&gt;
  552.  
  553. &lt;designer-extension&gt;
  554. &lt;in-palette&gt;true&lt;/in-palette&gt;
  555. &lt;category&gt;Gregorbyte Library&lt;/category&gt;
  556. &lt;/designer-extension&gt;
  557.  
  558. &lt;/component-extension&gt;
  559.  
  560. &lt;/component&gt;
  561.  
  562. &lt;component&gt;
  563.  
  564. &lt;description&gt;Container for Bootstrap Layout
  565. &lt;/description&gt;
  566. &lt;display-name&gt;Bootstrap Container&lt;/display-name&gt;
  567. &lt;component-type&gt;com.gregorbyte.xsp.bootstrap.Container
  568. &lt;/component-type&gt;
  569. &lt;component-class&gt;com.gregorbyte.xsp.component.bootstrap.UIContainer
  570. &lt;/component-class&gt;
  571.  
  572. &lt;property&gt;
  573. &lt;description&gt;Use Fluid Layout
  574. &lt;/description&gt;
  575. &lt;display-name&gt;Use Fluid Layout&lt;/display-name&gt;
  576. &lt;property-name&gt;fluid&lt;/property-name&gt;
  577. &lt;property-class&gt;java.lang.Boolean&lt;/property-class&gt;
  578. &lt;property-extension&gt;
  579. &lt;allow-run-time-binding&gt;true&lt;/allow-run-time-binding&gt;
  580. &lt;designer-extension&gt;
  581. &lt;category&gt;display&lt;/category&gt;
  582. &lt;/designer-extension&gt;
  583. &lt;/property-extension&gt;
  584. &lt;/property&gt;
  585.  
  586. &lt;component-extension&gt;
  587.  
  588. &lt;base-component-type&gt;com.ibm.xsp.Panel
  589. &lt;/base-component-type&gt;
  590.  
  591. &lt;component-family&gt;javax.faces.Panel
  592. &lt;/component-family&gt;
  593.  
  594. &lt;tag-name&gt;container&lt;/tag-name&gt;
  595.  
  596. &lt;designer-extension&gt;
  597. &lt;in-palette&gt;true&lt;/in-palette&gt;
  598. &lt;category&gt;Gregorbyte Library&lt;/category&gt;
  599. &lt;/designer-extension&gt;
  600.  
  601. &lt;/component-extension&gt;
  602.  
  603. &lt;/component&gt;
  604.  
  605. &lt;/faces-config&gt;</pre><p></p>
  606. <h2>Installing</h2>
  607. <p>If you would like to use these controls, you have 2 choices!</p>
  608. <ol>
  609. <li>Install the <a href="https://github.com/camac/GregorbyteXspLibrary/releases" target="_blank">latest version of my GregorbyteXspLibrary</a> to your Designer Client and Domino Server using the same method as Extension Library Installation</li>
  610. <li>Scavenge the necessary files from my Github repository and include it your own NSF</li>
  611. </ol>
  612. <p>To Scavenge the necessary files from my <a href="https://github.com/camac/GregorbyteXspLibrary" target="_blank">Github Repo</a>:</p>
  613. <ul>
  614. <li>You will need to place these java files in your java design elements:
  615. <ul>
  616. <li>/com.gregorbyte.xsp.controls/src/com/gregorbyte/xsp/component/bootstrap/UIColumn.java</li>
  617. <li>/com.gregorbyte.xsp.controls/src/com/gregorbyte/xsp/component/bootstrap/UIRow.java</li>
  618. <li>/com.gregorbyte.xsp.controls/src/com/gregorbyte/xsp/component/bootstrap/UIContainer.java</li>
  619. <li>/com.gregorbyte.xsp.core/src/com/gregorbyte/xsp/util/GregorbyteUtil.java</li>
  620. </ul>
  621. </li>
  622. <li>You will need to place this xsp-config file in the WEB-INF directory
  623. <ul>
  624. <li>/com.gregorbyte.xsp.controls/src/com/gregorbyte/xsp/config/gregorbyte-bootstrap.xsp-config</li>
  625. </ul>
  626. </li>
  627. </ul>
  628. <h1>Conclusion</h1>
  629. <p>You can easily get the job done without these controls, but so far I have really enjoyed having them in my palette.</p>
  630. <p>I&#8217;ve only just started using these controls, so I expect to improve on them. Maybe by adding the &#8216;small&#8217;, &#8216;medium&#8217;, &#8216;large&#8217;, &#8216;extraLarge&#8217; properties to the columns (but for now I can use styleClass=&#8221;col-sm-4&#8243; etc.)</p>
  631. <p>I&#8217;m also keen to take a look at other parts of the bootfaces project and see if any of the ideas might be effective in Xpages.</p>
  632. <p>If you end up using these please let me know! I&#8217;d love to hear any suggestions and as always if any problems then please leave a comment or post an issue on my GregorbyteXspLibrary project.</p>
  633. <p>&nbsp;</p>
  634. <h3></h3>
  635. ]]></content:encoded>
  636. <wfw:commentRss>http://camerongregor.com/2017/03/22/auto-width-bootstrap-column-xpages-controls/feed/</wfw:commentRss>
  637. <slash:comments>0</slash:comments>
  638. </item>
  639. <item>
  640. <title>Swiper FP8 Version Beta Release</title>
  641. <link>http://camerongregor.com/2017/03/16/swiper-fp8-version-beta-release/</link>
  642. <comments>http://camerongregor.com/2017/03/16/swiper-fp8-version-beta-release/#comments</comments>
  643. <pubDate>Thu, 16 Mar 2017 12:50:18 +0000</pubDate>
  644. <dc:creator><![CDATA[camerongregor]]></dc:creator>
  645. <category><![CDATA[Software Development]]></category>
  646. <category><![CDATA[designer]]></category>
  647. <category><![CDATA[sourcecontrol]]></category>
  648. <category><![CDATA[swiper]]></category>
  649.  
  650. <guid isPermaLink="false">http://camerongregor.com/?p=469</guid>
  651. <description><![CDATA[Last week I released the &#8216;alpha&#8217; version of Swiper which was untested on FP8 but presumed to be ok. So far I have only had good reports from the pioneers who have gone ahead&#46;&#46;&#46;]]></description>
  652. <content:encoded><![CDATA[<p>Last week <a href="http://camerongregor.com/2017/03/08/swiper-fp8-rollout/" target="_blank">I released the &#8216;alpha&#8217; version of Swiper</a> which was untested on FP8 but presumed to be ok. So far I have only had good reports from the pioneers who have gone ahead and installed FP8 + the alpha version.</p>
  653. <p>I have since managed to ugrade my home office setup to FP8 which unfortunately has broken my ability to launch designer from eclipse but I am seeking some advice on fixing this up.</p>
  654. <p>In the meantime I have to test the slow way of building plugins, import plugins, restart &#8230; ugh.</p>
  655. <p>Anyway, I persist because I want to give the people what they want! I have prepared another release with some requests received last week. This will most likely be the final 2.0.0 release but I am just releasing as beta for now. You can <a href="https://github.com/camac/Swiper/releases/tag/v2.0.0-beta" target="_blank">download the v2.0.0 Beta release from the github repo right now</a>! I&#8217;d love to hear if the requested features are also useful for you.</p>
  656. <p>Note: for the toolbar buttons to enable, you must click on the Project Header in the left pane</p>
  657. <h3><strong>&#8216;Refresh ODP and then Sync&#8217; button</strong></h3>
  658. <p>requested by Tinus Riyanto</p>
  659. <p><img class="aligncenter size-full wp-image-470" src="http://camerongregor.com/wp-content/uploads/2017/03/SwiperRefreshThenSyncButton.jpg" alt="" width="434" height="143" srcset="http://camerongregor.com/wp-content/uploads/2017/03/SwiperRefreshThenSyncButton.jpg 434w, http://camerongregor.com/wp-content/uploads/2017/03/SwiperRefreshThenSyncButton-300x99.jpg 300w" sizes="(max-width: 434px) 100vw, 434px" /></p>
  660. <p>Tinus&#8217; setup is to have Build Automatically off, Refresh Automatically off (I assume) and also &#8216;Auto Import&#8217; off.<br />
  661. This means that to for sync, he would need to find the ODP and right-click refresh, and then run a sync</p>
  662. <p>Instead, he can now just click &#8216;Refresh ODP and then Sync&#8217; button!</p>
  663. <p>I spent about 20 minutes trying to figure out what Icon could represent &#8216;Refresh and Sync&#8217; without confusing it with the other icons. I gave up and chose the 8-ball :p</p>
  664. <p>For bonus points I threw in a &#8216;Refresh ODP&#8217; button which just refreshes the ODP.</p>
  665. <p><img class="aligncenter size-full wp-image-471" src="http://camerongregor.com/wp-content/uploads/2017/03/SwiperRefreshODPButton.jpg" alt="" width="434" height="144" srcset="http://camerongregor.com/wp-content/uploads/2017/03/SwiperRefreshODPButton.jpg 434w, http://camerongregor.com/wp-content/uploads/2017/03/SwiperRefreshODPButton-300x100.jpg 300w" sizes="(max-width: 434px) 100vw, 434px" /></p>
  666. <h3><strong>&#8216;Launch ODP Folder in System Explorer&#8217;</strong></h3>
  667. <p>requested by me!</p>
  668. <p>A few times I just want to open the ODP in the System Explorer (windows Explorer) so I added this button to quickly launch it without having to navigate to package explorer etc.</p>
  669. <p><img class="aligncenter size-full wp-image-472" src="http://camerongregor.com/wp-content/uploads/2017/03/SwiperLaunchODPButton.jpg" alt="" width="434" height="144" srcset="http://camerongregor.com/wp-content/uploads/2017/03/SwiperLaunchODPButton.jpg 434w, http://camerongregor.com/wp-content/uploads/2017/03/SwiperLaunchODPButton-300x100.jpg 300w" sizes="(max-width: 434px) 100vw, 434px" /></p>
  670. <p>&nbsp;</p>
  671. <h3>&#8216;Enable Swiper for All projects&#8217; preference option</h3>
  672. <p>Requested by Patrick Kwinten</p>
  673. <p><img class="aligncenter size-full wp-image-474" src="http://camerongregor.com/wp-content/uploads/2017/03/SwiperEnableForAll-1.jpg" alt="" width="518" height="220" srcset="http://camerongregor.com/wp-content/uploads/2017/03/SwiperEnableForAll-1.jpg 518w, http://camerongregor.com/wp-content/uploads/2017/03/SwiperEnableForAll-1-300x127.jpg 300w" sizes="(max-width: 518px) 100vw, 518px" /></p>
  674. <p>Patrick wants to &#8216;Fire and Forget&#8217; on a Headless Build setup so that all projects will definitely have swiper enabled when the Headless build runs.</p>
  675. <h2>Next step</h2>
  676. <p>As long as I hear everything is good, I will publish the final version 2.0.0 to OpenNTF next week.</p>
  677. <p>As always happy to hear any further suggestions that may make your life with source-control in Domino Designer easier.</p>
  678. ]]></content:encoded>
  679. <wfw:commentRss>http://camerongregor.com/2017/03/16/swiper-fp8-version-beta-release/feed/</wfw:commentRss>
  680. <slash:comments>6</slash:comments>
  681. </item>
  682. <item>
  683. <title>Swiper FP8 Integration Rollout</title>
  684. <link>http://camerongregor.com/2017/03/08/swiper-fp8-rollout/</link>
  685. <comments>http://camerongregor.com/2017/03/08/swiper-fp8-rollout/#comments</comments>
  686. <pubDate>Wed, 08 Mar 2017 12:43:10 +0000</pubDate>
  687. <dc:creator><![CDATA[camerongregor]]></dc:creator>
  688. <category><![CDATA[Software Development]]></category>
  689. <category><![CDATA[designer]]></category>
  690. <category><![CDATA[sourcecontrol]]></category>
  691. <category><![CDATA[swiper]]></category>
  692.  
  693. <guid isPermaLink="false">http://camerongregor.com/?p=461</guid>
  694. <description><![CDATA[Notes Domino 9.0.1 FP8 is finally here and as far as I know (I have yet to download it) it includes the necessary changes which will allow Swiper to swipe whatever it wants, whenever it wants,&#46;&#46;&#46;]]></description>
  695. <content:encoded><![CDATA[<p>Notes Domino 9.0.1 FP8 is finally here and as far as I know (I have yet to download it) it includes the necessary changes which will allow Swiper to swipe whatever it wants, whenever it wants, which is good news for people who don&#8217;t like to have &#8216;Build Automatically&#8217; turned on.</p>
  696. <p>&nbsp;</p>
  697. <h3><strong>Plan of Attack for release of Swiper version 2.0.0</strong></h3>
  698. <p>So, I haven&#8217;t actually downloaded FP8 yet,  so I can&#8217;t say for sure that the updated version works perfectly. Here is a bit of background, I had been working with IBM team to make sure that the necessary changes would be sufficient. I received an updated version of the &#8216;team&#8217; plugin and have tested my POC successfully against that updated plugin, but I haven&#8217;t tested it fully against the full new FP8 because, as previously mentioned, I haven&#8217;t downloaded it yet.</p>
  699. <p>So the plan is: I am putting a <a href="https://github.com/camac/Swiper/releases/tag/v2.0.0-alpha">pre-release of Swiper v2.0.0</a> up on the <a href="https://github.com/camac/Swiper">Swiper Github repository</a>.</p>
  700. <blockquote><p>If you have updated to FP8 and would like to test the new version I would very much appreciated it!</p></blockquote>
  701. <p>Here are the steps to install and ugrade:</p>
  702. <ul>
  703. <li>Download <a href="https://github.com/camac/Swiper/releases/tag/v2.0.0-alpha">Swiper v2.0.0-alpha release</a> from Github and install to designer</li>
  704. <li>&#8216;Remove Swiper&#8217; and then &#8216;Add Swiper&#8217; from any projects currently using swiper<br />
  705. Basically, the &#8216;remove&#8217; will remove the old builders that are no longer needed, then &#8216;add&#8217; will re-enable the new version.</li>
  706. <li>Check that it is swiping your metadata whenever you sync!</li>
  707. </ul>
  708. <p>I will also upgrade my Notes over the next week and test. Once I have heard back from a few people that it is working well, then I will prepare the final release and update the OpenNTF Project page.</p>
  709. <h3><strong>Bonus Feature!</strong></h3>
  710. <p>I added a new feature: there is a &#8216;Sync&#8217; toolbar button to perform a sync on the currently active project. Give it a try and let me know if useful.</p>
  711. <p><img class="aligncenter size-full wp-image-462" src="http://camerongregor.com/wp-content/uploads/2017/03/SwiperSyncButton.jpg" alt="" width="146" height="59" /></p>
  712. <h3><strong>Background of the Reason for new Integration point</strong></h3>
  713. <p>If you are curious about why there was a change necessary to allow Swiper to perform more efficiently, here is a summary:</p>
  714. <p>Swiper version 1 worked like this:</p>
  715. <ul>
  716. <li>Designer has a &#8216;Builder&#8217; which triggers the sync process of Design Elements to Disk</li>
  717. <li>Swiper version 1 sandwiched this Builder with 2 builders of it&#8217;s own, a Pre and Post Sync builder.</li>
  718. <li>The Pre sync builder saved some info about files that might be exported</li>
  719. <li>The Post sync builder used Pre Sync builder to figure out if files were exported, if they were then it filtered them</li>
  720. </ul>
  721. <p>The trouble with this, is that there is more than one way for the Sync process to be triggered. If it was triggered on &#8216;project open&#8217; event or manually with the right-click menu, then the Swiper builders would not run.</p>
  722. <p>IBM have now made a modification to provide a &#8216;SyncListener&#8217; extension point, so that a class can be registered to listen to import / export / rename / delete events, and has the opportunity to run some code when those events fire.</p>
  723. <p>This allows swiper to run whenever there is a sync operation, regardless of when it was called.</p>
  724. <p>So Swiper version 2 simply listens for the events and then acts accordingly whenever they happen.</p>
  725. <h4><strong>Other uses for SyncListener</strong></h4>
  726. <p>I am also curious if there are other options to utilise this feature to correct DXL Import export errors. One example is to do with agent scheduling for all-day agents. A custom Scheduling metadata file could be created and used to save the agent scheduling info on export, and then configure the agent after import.</p>
  727. <h3><strong>Feedback welcome</strong></h3>
  728. <p>Let me know how you go, you can report bugs either on <a href="https://www.openntf.org/main.nsf/project.xsp?r=project/Swiper/summary">OpenNTF Swiper Project</a> or the <a href="https://github.com/camac/Swiper/issues">Github Repository Issues</a></p>
  729. <p>Or maybe you just want to say it is all working well! It is nice to hear when people say thanks so if it is helping you please don&#8217;t be shy! I do get some nice comments from time to time and it is a great ego boost.</p>
  730. <p>When I first learned of Source Control Enablement in Designer, I was excited and then disappointed.</p>
  731. <p>It was clearly not a workable solution if you needed to do any sort of branching and merging, which is core to good source control.</p>
  732. <p>I was determined to get it working property. The initial DORA solution, took me about 2-3 months in my spare time to problem solve, develop and prepare so it was robust enough to share with others. It was workable but still a bit too fiddly. Later I decided that it would be cleaner to have it integrated into Designer, and a further month or two of development to get it ready. It was much more robust to install, and almost perfect.</p>
  733. <p>This latest version should complete the puzzle, and hopefully there will be no more major developments needed!</p>
  734. <p>I cannot imagine being able to manage the projects I have been working on over the past few years if I did not have source control, so I hope that my efforts have also enable you to work collaboratively with others, and also manage your own local branches as well!</p>
  735. ]]></content:encoded>
  736. <wfw:commentRss>http://camerongregor.com/2017/03/08/swiper-fp8-rollout/feed/</wfw:commentRss>
  737. <slash:comments>13</slash:comments>
  738. </item>
  739. <item>
  740. <title>Markdown XPages UIControl</title>
  741. <link>http://camerongregor.com/2017/03/05/markdown-xpages-uicontrol/</link>
  742. <comments>http://camerongregor.com/2017/03/05/markdown-xpages-uicontrol/#comments</comments>
  743. <pubDate>Sun, 05 Mar 2017 11:44:24 +0000</pubDate>
  744. <dc:creator><![CDATA[camerongregor]]></dc:creator>
  745. <category><![CDATA[Software Development]]></category>
  746. <category><![CDATA[control]]></category>
  747. <category><![CDATA[markdown]]></category>
  748. <category><![CDATA[xpages]]></category>
  749.  
  750. <guid isPermaLink="false">http://camerongregor.com/?p=444</guid>
  751. <description><![CDATA[Often when I&#8217;m designing an xpage, there might be a section of the page in which I want to explain some instructions to the user. Some options here are to: write the Instructions using html&#46;&#46;&#46;]]></description>
  752. <content:encoded><![CDATA[<p>Often when I&#8217;m designing an xpage, there might be a section of the page in which I want to explain some instructions to the user. Some options here are to:</p>
  753. <ol>
  754. <li>write the Instructions using html and embed directly in the xpage markup</li>
  755. <li>write the Instructions directly in the design pane and format using designer&#8217;s ui e.g. bold, color, size etc</li>
  756. <li>use some native xpage controls to achieve the desired result.</li>
  757. <li>Write the instructions in a richtext field on a notes document that is loaded to display the info.</li>
  758. </ol>
  759. <p>Option 1 works good but is annoying to type html opening and closing all those tags. Option 2 is quick to use but creates ugly html with hardcoded styles / colours etc. Option 3 is doable but still a little clunky, Option 4 is not a great move because it is very hard to include the instructions within source control.</p>
  760. <h3>A better option needed</h3>
  761. <p><a href="https://en.wikipedia.org/wiki/Lightweight_markup_language">Lightweight markup languages</a> are a great way to let formatting &#8216;get out of the way&#8217; and just let you focus on writing the content. There are many lightweight languages to choose from. <a href="http://daringfireball.net/projects/markdown/">Markdown </a>is a popular one so I thought that would be great to be able to quickly write some markdown and let the formatting take care of itself.</p>
  762. <p>If you aren&#8217;t familiar with the concept, it is basically a system of writing some plaintext content that can automatically be converted to html. For example the following text is plain text but easy to see the intended structure:</p><pre class="crayon-plain-tag">This is a heading
  763. =================
  764.  
  765. This is a paragraph
  766.  
  767. * this is a list item
  768. * this is another item</pre><p>This will processed by Markdown into the following html</p><pre class="crayon-plain-tag">&lt;h1&gt;This is a Heading&lt;/h1&gt;
  769. &lt;p&gt;This is a paragraph&lt;/p&gt;
  770. &lt;ul&gt;
  771. &lt;li&gt;this is a list item&lt;/li&gt;
  772. &lt;li&gt;this is another item&lt;/li&gt;
  773. &lt;/ul&gt;</pre><p></p>
  774. <h3>Side note: Asciidoctor is good too</h3>
  775. <p>I have also created a similar control for AsciiDoctor which is another lightweight format. I actually prefer to use Asciidoctor now, however since Markdown seems to be more common I thought I would share the markdown control first.</p>
  776. <p>If you are interested in AsciiDoctor control let me know. It is a bit more intense though because it starts up a JRuby instance and maybe that is not something you&#8217;d like to do (because AsciiDoctor is ruby based).</p>
  777. <h2>Overview</h2>
  778. <p>A few years ago <a href="https://www.ovalbusinesssolutions.co.uk/single-post/2015/04/29/Practical-text-formatting-with-markdown">Martin Rolph from Oval UK shared a solution</a> of a Markdown Custom Control. You can find the project on github at <a href="https://github.com/OvalUK/XPagesDemos">OvalUK/XPagesDemos</a>. The solution used <a href="https://code.google.com/archive/p/markdown4j/">markdown4j </a>which is a java-based markdown processor. I decided to build on Martin&#8217;s work and convert it from a custom control into an XspLibrary Control with a few extra options of where to retrieve the markdown from.</p>
  779. <p>The markdown control allows you bind to a dynamic data source such as a NotesDocument / scoped variable etc. but also provides some options to load the source text from some static locations. You can have the source text:</p>
  780. <ul>
  781. <li>Bound to a String Field in a Datasource or Scope variable</li>
  782. <li>Supplied from a text file in the WEB-INF directory (read only)</li>
  783. <li>Supplied from a text file from within a plugin (read only)</li>
  784. <li>Supplied from a File in the &#8216;Resources&#8217; Design Element (read only)</li>
  785. <li>Written directly within the control&#8217;s start and end tags (passthrough text) but this has some gotchas. (read only)</li>
  786. </ul>
  787. <p>The control is an extension of the normal &#8216;inputTextArea&#8217; control. When the control is in edit mode it just works exactly like a plain text area.</p>
  788. <p>In Read-only mode, the supplied plaintext will be converted to html for display using the Markdown Processor.</p>
  789. <h3>Simple Example</h3>
  790. <p>In this simple example, I am binding the text to a viewScope variable &#8216;sampleText&#8217;<br />
  791. I have 2 markdown controls, one on the left in edit mode, and one on the right in read mode, showing a live preview of the markup using the onKeyUp event to refresh the preview control. Here is the markup for the page</p><pre class="crayon-plain-tag">&lt;h2&gt;Live Preview example&lt;/h2&gt;
  792.  
  793. &lt;xp:table id="tablePreview" style="width:800px;margin-bottom:50.0px;margin-top:10.0px"&gt;
  794. &lt;xp:tr&gt;
  795. &lt;xp:td style="width:50%" valign="top"&gt;
  796. &lt;gb:markdown id="markdown2" value="#{viewScope.sampleText}"&gt;
  797. &lt;xp:eventHandler event="onkeyup" submit="true" refreshMode="partial" refreshId="markdown3" execMode="partial" execId="tablePreview"
  798. disableValidators="true"&gt;
  799. &lt;/xp:eventHandler&gt;
  800. &lt;/gb:markdown&gt;
  801. &lt;/xp:td&gt;
  802. &lt;xp:td valign="top"&gt;
  803. &lt;gb:markdown id="markdown3" value="#{viewScope.sampleText}" readonly="true"&gt;&lt;/gb:markdown&gt;
  804. &lt;/xp:td&gt;
  805. &lt;/xp:tr&gt;
  806. &lt;/xp:table&gt;</pre><p>And here is an example of how it looks</p>
  807. <p><img class="aligncenter size-full wp-image-446" src="http://camerongregor.com/wp-content/uploads/2017/03/MarkdownLivePreview.jpg" alt="" width="693" height="234" srcset="http://camerongregor.com/wp-content/uploads/2017/03/MarkdownLivePreview.jpg 693w, http://camerongregor.com/wp-content/uploads/2017/03/MarkdownLivePreview-300x101.jpg 300w" sizes="(max-width: 693px) 100vw, 693px" /></p>
  808. <h2>Examples of the different methods of supplying the text</h2>
  809. <p>You should be able to find the control in the Controls palette under Gregorbyte Library. Drag it onto the page.</p>
  810. <p><img class="aligncenter size-full wp-image-457" src="http://camerongregor.com/wp-content/uploads/2017/03/MarkdownPalette.jpg" alt="" width="213" height="52" /></p>
  811. <h3>Bound to a Datasource / Managed Bean</h3>
  812. <p>You can bind it to any text field just like you would normally. In this example it is bound to a document and in edit mode acts just like a normal text area.</p><pre class="crayon-plain-tag">&lt;?xml version="1.0" encoding="UTF-8"?&gt;
  813. &lt;xp:view xmlns:xp="http://www.ibm.com/xsp/core" xmlns:gb="http://www.gregorbyte.com/xsp/"&gt;
  814.  
  815. &lt;xp:this.data&gt;
  816. &lt;xp:dominoDocument var="document1" formName="Markdown"&gt;&lt;/xp:dominoDocument&gt;
  817. &lt;/xp:this.data&gt;
  818.  
  819. &lt;xp:button value="Save" id="button1"&gt;
  820. &lt;xp:eventHandler event="onclick" submit="true" refreshMode="complete"&gt;
  821. &lt;xp:this.action&gt;
  822. &lt;xp:saveDocument&gt;&lt;/xp:saveDocument&gt;
  823. &lt;/xp:this.action&gt;
  824. &lt;/xp:eventHandler&gt;
  825. &lt;/xp:button&gt;
  826.  
  827. &lt;gb:markdown id="markdown1" value="#{document1.markdownText}"&gt;&lt;/gb:markdown&gt;
  828.  
  829. &lt;/xp:view&gt;</pre><p>Then in edit mode:</p>
  830. <p><img class="aligncenter size-full wp-image-452" src="http://camerongregor.com/wp-content/uploads/2017/03/MarkdownDSEntry.jpg" alt="" width="381" height="259" srcset="http://camerongregor.com/wp-content/uploads/2017/03/MarkdownDSEntry.jpg 381w, http://camerongregor.com/wp-content/uploads/2017/03/MarkdownDSEntry-300x204.jpg 300w" sizes="(max-width: 381px) 100vw, 381px" /></p>
  831. <p>In Read only mode:</p>
  832. <p><img class="aligncenter size-full wp-image-453" src="http://camerongregor.com/wp-content/uploads/2017/03/MarkdownDSRead.jpg" alt="" width="305" height="124" srcset="http://camerongregor.com/wp-content/uploads/2017/03/MarkdownDSRead.jpg 305w, http://camerongregor.com/wp-content/uploads/2017/03/MarkdownDSRead-300x122.jpg 300w" sizes="(max-width: 305px) 100vw, 305px" /></p>
  833. <h3>Supplied from WEB-INF</h3>
  834. <p>To read a file from WEB-INF the &#8216;value&#8217; should be the path to the file</p>
  835. <ul>
  836. <li>It must start with &#8216;/WEB-INF&#8217;</li>
  837. <li>It must finish with &#8216;.md&#8217;</li>
  838. <li>readonly property should be &#8216;true&#8217;</li>
  839. </ul>
  840. <p>e.g.</p><pre class="crayon-plain-tag">&lt;!-- Read from WEB-INF --&gt;
  841. &lt;gb:markdown id="markdown4" readonly="true" value="/WEB-INF/markdown/Sample.md"&gt;&lt;/gb:markdown&gt;</pre><p><img class="aligncenter size-full wp-image-447" src="http://camerongregor.com/wp-content/uploads/2017/03/MarkdownSampleWebInf.jpg" alt="" width="265" height="245" /></p>
  842. <h3>Supplied from a File in the Resources Design element</h3>
  843. <p>To supply the text from a File in the Resources Design element, give your file should the .md extension, and then supply &#8216;value&#8217; as &#8216;/&lt;yourfilename&gt;.md&#8217;, also set readonly=&#8221;true&#8221; e.g.</p><pre class="crayon-plain-tag">&lt;!-- Read from the File Resources --&gt;
  844. &lt;gb:markdown id="markdown1" readonly="true" value="/FileResource.md"&gt;&lt;/gb:markdown&gt;</pre><p><img class="aligncenter size-full wp-image-448" src="http://camerongregor.com/wp-content/uploads/2017/03/MarkdownSampleFileResource.jpg" alt="" width="505" height="269" srcset="http://camerongregor.com/wp-content/uploads/2017/03/MarkdownSampleFileResource.jpg 505w, http://camerongregor.com/wp-content/uploads/2017/03/MarkdownSampleFileResource-300x160.jpg 300w" sizes="(max-width: 505px) 100vw, 505px" /></p>
  845. <h3>Supplied from a TextFile in a plugin</h3>
  846. <p>To supply the text from a file in a plugin, make sure your file has .md extension. Right click on the file in eclipse and &#8216;Copy Qualified Name&#8217;. Paste this as the &#8216;value&#8217; and set readonly = true.<br />
  847. You may run into some trouble if your file is not included in the build process, or it is built to a different location than it is in the source code, but basically the format should be &#8216;/com.your.plugin/your/classpath/to/resource/file.md&#8217;</p><pre class="crayon-plain-tag">&lt;!-- Read from A plugin --&gt;
  848. &lt;gb:markdown id="markdown6" readonly="true" value="/com.gregorbyte.xsp/resources/sample/Markdown.md"&gt;&lt;/gb:markdown&gt;</pre><p><img class="aligncenter size-full wp-image-449" src="http://camerongregor.com/wp-content/uploads/2017/03/MarkdownSamplePluginSource.jpg" alt="" width="774" height="287" srcset="http://camerongregor.com/wp-content/uploads/2017/03/MarkdownSamplePluginSource.jpg 774w, http://camerongregor.com/wp-content/uploads/2017/03/MarkdownSamplePluginSource-300x111.jpg 300w, http://camerongregor.com/wp-content/uploads/2017/03/MarkdownSamplePluginSource-768x285.jpg 768w" sizes="(max-width: 774px) 100vw, 774px" /></p>
  849. <h3>Written directly within start and end tag</h3>
  850. <p>This is a quick and dirty method but it is a bit buggy. If you sometimes format your xsp markup with Ctrl + Shift + F then it will ruin your markdown with indents etc. The markdown text must be placed flush with the margin of the code editor</p><pre class="crayon-plain-tag">&lt;gb:markdown id="markdown5" readonly="true"&gt;
  851. This text is directly within the tags
  852. =====================================
  853.  
  854. It has some drawbacks though
  855.  
  856. * The text must be lined up directly against the margin (No indents)
  857. * If you hit 'Ctrl + Shift + F' to format your xsp markup, it will indent the text and ruin everything
  858. * You cannot embed html tags in the text
  859.  
  860. These bugs could be fixed but I just haven't got to it yet
  861. &lt;/gb:markdown&gt;</pre><p></p>
  862. <h2>Installation</h2>
  863. <p>To install you do one of the following:</p>
  864. <ul>
  865. <li>install my GregorbyteXspLibrary to your designer + server</li>
  866. <li>Scavenge necessary files from the Github repo</li>
  867. </ul>
  868. <h3>Method 1: Install my library</h3>
  869. <p>Go to the <a href="https://github.com/camac/GregorbyteXspLibrary/releases">latest releases of camac/GregorbyteXspLibrary</a>, download the zip file and install the update site using your favourite methods (same method as you would install IBM Extension Library)</p>
  870. <h3>Method 2: Scavenge Necessary files from github repo</h3>
  871. <p>You will need These java files</p>
  872. <ul>
  873. <li>/com.gregorbyte.xsp.controls/src/com/gregorbyte/xsp/component/markdown/UIMarkdown.java</li>
  874. <li>/com.gregorbyte.xsp.controls/src/com/gregorbyte/xsp/renderkit/markdown/MarkdownRenderer.java</li>
  875. </ul>
  876. <p>You will need this JAR</p>
  877. <ul>
  878. <li>/com.gregorbyte.lib.markdown4j/lib/markdown4j-2.2.jar</li>
  879. </ul>
  880. <p>You will need this xsp-config (place in your WEB-INF</p>
  881. <ul>
  882. <li>/com.gregorbyte.xsp.controls/src/com/gregorbyte/xsp/config/gregorbyte-markdown.xsp-config</li>
  883. </ul>
  884. <p>You will need to include the contents of this file in your WEB-INF/faces-config.xml file</p>
  885. <ul>
  886. <li>/com.gregorbyte.xsp.controls/src/com/gregorbyte/xsp/config/gregorbyte-markdown-faces-config.xml</li>
  887. </ul>
  888. <p>I am pretty sure that is all that is needed, let me know if you have any trouble.</p>
  889. <h2>Conclusion</h2>
  890. <p>Once again thanks to Martin Rolph for providing the markdown custom control in the first place, I have simply added a few more ways to supply the source and wrapped it in a control.</p>
  891. <p>I&#8217;d love to hear what you think if this is useful to you or not. Personally I have been using it mainly to supply markdown from within a plugin. Mainly help/instructions/code examples in our demo database.</p>
  892. ]]></content:encoded>
  893. <wfw:commentRss>http://camerongregor.com/2017/03/05/markdown-xpages-uicontrol/feed/</wfw:commentRss>
  894. <slash:comments>2</slash:comments>
  895. </item>
  896. <item>
  897. <title>TextDiff XPages control &#8211; For visual comparison of text</title>
  898. <link>http://camerongregor.com/2017/02/28/textdiff-xpages-control-for-visual-comparison-of-text/</link>
  899. <comments>http://camerongregor.com/2017/02/28/textdiff-xpages-control-for-visual-comparison-of-text/#comments</comments>
  900. <pubDate>Tue, 28 Feb 2017 12:00:19 +0000</pubDate>
  901. <dc:creator><![CDATA[camerongregor]]></dc:creator>
  902. <category><![CDATA[Software Development]]></category>
  903. <category><![CDATA[control]]></category>
  904. <category><![CDATA[xpages]]></category>
  905.  
  906. <guid isPermaLink="false">http://camerongregor.com/2017/02/28/textdiff-xpages-control-for-visual-comparison-of-text/</guid>
  907. <description><![CDATA[A few years back I stumbled across Google&#8217;s diff-match-patch project which provides some handy algorithms for text manipulation. At the time of discovery I was doing &#8216;classic&#8217; notes development. Although I probably could have&#46;&#46;&#46;]]></description>
  908. <content:encoded><![CDATA[<p>A few years back I stumbled across <a href="https://code.google.com/p/google-diff-match-patch/" target="_blank">Google&#8217;s diff-match-patch project</a> which provides some handy algorithms for text manipulation. At the time of discovery I was doing &#8216;classic&#8217; notes development. Although I probably could have implemented something that worked in lotuscript with RichText or Mime, it wasn&#8217;t a priority at the time and I never bothered.</p>
  909. <p>Since then, I have been doing mainly XPages, and now that I have been also doing a bit of XPages Control development. I was mainly interested in the &#8216;diff&#8217; algorithm so I decided the time was right to turn the diff algorithm into a useful XPages control, which takes 2 input strings and displays a visual difference between them.</p>
  910. <p>The diff-match-patch algorithm / project is licenced under Apache Licence 2.0 so it is the same licence as most OpenNTF projects.</p>
  911. <h3>Overview</h3>
  912. <p>Basically the control takes a &#8216;from&#8217; and &#8216;to&#8217; input, and displays the visual diff. I guess it is best to show the example!</p>
  913. <p>Here is some From and To Input that we will use for our examples. I have bound these textareas to viewScope &#8216;fromText&#8217; and &#8216;toText&#8217; but they could be bound to whatever text field you like</p>
  914. <p><img class="aligncenter size-full wp-image-435" src="http://camerongregor.com/wp-content/uploads/2017/02/TextDiffExampleInput.jpg" alt="" width="585" height="114" srcset="http://camerongregor.com/wp-content/uploads/2017/02/TextDiffExampleInput.jpg 585w, http://camerongregor.com/wp-content/uploads/2017/02/TextDiffExampleInput-300x58.jpg 300w" sizes="(max-width: 585px) 100vw, 585px" /></p>
  915. <p>So then we will use the text diff like so:</p><pre class="crayon-plain-tag">&lt;gb:textDiff id="textDiff1" from="#{viewScope.fromText}" to="#{viewScope.toText}" disableTheme="true"&gt;
  916. &lt;/gb:textDiff&gt;</pre><p>Note the following picture is from my demo page so the actual output from the textdiff control is only what is inside the black box.</p>
  917. <p><img class="aligncenter size-full wp-image-436" src="http://camerongregor.com/wp-content/uploads/2017/02/TextDiffDefault.jpg" alt="" width="524" height="103" srcset="http://camerongregor.com/wp-content/uploads/2017/02/TextDiffDefault.jpg 524w, http://camerongregor.com/wp-content/uploads/2017/02/TextDiffDefault-300x59.jpg 300w" sizes="(max-width: 524px) 100vw, 524px" /></p>
  918. <h3>Algorithm parameters</h3>
  919. <p>For a good explanation of the algorithm parameters, I suggest you have a look at the google diff-match-patch webpage. But here is my best explanation!</p>
  920. <ul>
  921. <li><strong>cleanup</strong> &#8211; whether to perform some sort of cleanup to produce some more human readable results. I find &#8216;semantic&#8217; is a good option</li>
  922. <li><strong>edit cost</strong> &#8211; only used for &#8216;efficiency&#8217; cleanup</li>
  923. <li><strong>Timeout</strong> &#8211; don&#8217;t spend longer than this amount of seconds</li>
  924. </ul>
  925. <p>Here is an example of output after changing the &#8216;cleanup&#8217; option to &#8216;semantic&#8217;. Note that it has lengthened the change from single words to a longer run of words</p>
  926. <p><img class="aligncenter size-full wp-image-437" src="http://camerongregor.com/wp-content/uploads/2017/02/TextDiffOptions.jpg" alt="" width="628" height="134" srcset="http://camerongregor.com/wp-content/uploads/2017/02/TextDiffOptions.jpg 628w, http://camerongregor.com/wp-content/uploads/2017/02/TextDiffOptions-300x64.jpg 300w" sizes="(max-width: 628px) 100vw, 628px" /></p>
  927. <h3>Styling</h3>
  928. <p>The control has style and styleClass properties for the insert, delete and equals operations. Basically the control outputs a bunch of &#8216;&lt;span&gt;&#8217;s for each chunk of the diff operation. These outputs are either an insertion, a deletion or &#8216;equals&#8217;, and they have associated &#8216;insertStyle&#8217; and &#8216;insertStyleClass&#8217; properties.</p>
  929. <h3>Themable</h3>
  930. <p>All the above properties are themable. You can use the &#8216;Text.Diff&#8217; control type to set properties for all textdiff controls or you can come up with a new ThemeId that can be applied as needed.</p>
  931. <p>Here is an example using the following theme properties with a custom ThemeId</p><pre class="crayon-plain-tag">&lt;control&gt;
  932. &lt;name&gt;Text.Diff.Funky&lt;/name&gt;
  933. &lt;property&gt;
  934. &lt;name&gt;style&lt;/name&gt;
  935. &lt;value&gt;background-color: black;&lt;/value&gt;
  936. &lt;/property&gt;
  937. &lt;property&gt;
  938. &lt;name&gt;insertStyle&lt;/name&gt;
  939. &lt;value&gt;color: aqua;&lt;/value&gt;
  940. &lt;/property&gt;
  941. &lt;property&gt;
  942. &lt;name&gt;equalStyle&lt;/name&gt;
  943. &lt;value&gt;color: lime;&lt;/value&gt;
  944. &lt;/property&gt;
  945. &lt;property&gt;
  946. &lt;name&gt;deleteStyle&lt;/name&gt;
  947. &lt;value&gt;color: pink;&lt;/value&gt;
  948. &lt;/property&gt;
  949. &lt;/control&gt;</pre><p><img class="aligncenter size-full wp-image-438" src="http://camerongregor.com/wp-content/uploads/2017/02/TextDiffTheme.jpg" alt="" width="697" height="65" srcset="http://camerongregor.com/wp-content/uploads/2017/02/TextDiffTheme.jpg 697w, http://camerongregor.com/wp-content/uploads/2017/02/TextDiffTheme-300x28.jpg 300w" sizes="(max-width: 697px) 100vw, 697px" /></p>
  950. <h3>Installation</h3>
  951. <p>You have 2 choices</p>
  952. <ul>
  953. <li>install my &#8216;Gregorbyte&#8217; extension library</li>
  954. <li>scavenge the necessary files from my github repo and install to your nsf</li>
  955. </ul>
  956. <h4>Install my Gregorbyte Extension Library</h4>
  957. <p>Go and get the <a href="https://github.com/camac/GregorbyteXspLibrary/releases" target="_blank">latest release</a> which should be an update site zip file.</p>
  958. <p>Install this to your domino designer, and also to your domino server using your favourite update site method.</p>
  959. <h4>To Scavenge the necessary files from my <a href="https://github.com/camac/GregorbyteXspLibrary" target="_blank">github repo</a></h4>
  960. <p>You&#8217;ll need to put these java files in your Java section of your NSF:</p>
  961. <ul>
  962. <li>/com.gregorbyte.xsp.controls/src/com/gregorbyte/xsp/component/UITextDiff.java</li>
  963. <li>/com.gregorbyte.xsp.controls/src/com/gregorbyte/xsp/renderkit/TextDiffRenderer.java</li>
  964. <li>/com.gregorbyte.xsp.controls/src/name/fraser/neil/plaintext/DiffMatchPatch.java</li>
  965. </ul>
  966. <p>And this xsp-config (place in your WebContent/WEB-INF directory in you NSF using package explorer)</p>
  967. <ul>
  968. <li>/com.gregorbyte.xsp.controls/src/com/gregorbyte/xsp/config/gregorbyte-textdiff.xsp-config</li>
  969. </ul>
  970. <p>and add this entry to your faces-config.xml</p><pre class="crayon-plain-tag">&lt;render-kit&gt;
  971. &lt;renderer&gt;
  972. &lt;component-family&gt;javax.faces.Output&lt;/component-family&gt;
  973. &lt;renderer-type&gt;com.gregorbyte.TextDiff&lt;/renderer-type&gt;
  974. &lt;renderer-class&gt;com.gregorbyte.xsp.renderkit.TextDiffRenderer&lt;/renderer-class&gt;
  975. &lt;/renderer&gt;
  976. &lt;/render-kit&gt;</pre><p>Let me know if it works or doesn&#8217;t work in case I forgot something. I haven&#8217;t used this control heavily in production yet so there may be a bug here and there, please report it if so!</p>
  977. <p>&nbsp;</p>
  978. ]]></content:encoded>
  979. <wfw:commentRss>http://camerongregor.com/2017/02/28/textdiff-xpages-control-for-visual-comparison-of-text/feed/</wfw:commentRss>
  980. <slash:comments>2</slash:comments>
  981. </item>
  982. <item>
  983. <title>XPages webmail &#8211; Using Mime Inspector to debug Mime</title>
  984. <link>http://camerongregor.com/2017/02/14/xpages-webmail-using-mime-inspector-to-debug-mime/</link>
  985. <comments>http://camerongregor.com/2017/02/14/xpages-webmail-using-mime-inspector-to-debug-mime/#comments</comments>
  986. <pubDate>Tue, 14 Feb 2017 11:12:21 +0000</pubDate>
  987. <dc:creator><![CDATA[camerongregor]]></dc:creator>
  988. <category><![CDATA[Software Development]]></category>
  989.  
  990. <guid isPermaLink="false">http://camerongregor.com/?p=320</guid>
  991. <description><![CDATA[In a previous post in this series I did a bit of an overview on how MIME works. We also did a little bit about how MIME works in XPages + Domino land. With&#46;&#46;&#46;]]></description>
  992. <content:encoded><![CDATA[<p>In a previous post in this series I did a bit of an overview on <a href="http://camerongregor.com/2016/04/21/webmail-ui-you-must-learn-about-mime/">how MIME works</a>. We also did a little bit about how MIME works in XPages + Domino land. With this knowledge in hand we can now start to analyse the different ways a &#8216;Pretty words, pictures and attachments&#8217; can be stored in the document.</p>
  993. <p>During development of the &#8216;XPages Webmail&#8217; interface, I encountered many problems which could only be solved by investigating the MIME content in detail.</p>
  994. <p>To help me do this, I developed a simple control (Mime Inspector) that I could use during debugging, which allowed me to see details of Mime Entities and the relation between them.</p>
  995. <p><strong>Overview</strong></p>
  996. <p>To use the MIME Inspector, I just bind it to a &#8220;rich text/Mime&#8221; field, and then view the output in the XPage. There are 2 control properties that can be control the level of detail</p>
  997. <ul>
  998. <li>showHeaders &#8211; will output the MimeHeader entries of each MimeEntity</li>
  999. <li>showContent &#8211; will output the actual content of each MimeEntity</li>
  1000. </ul>
  1001. <p>The output starts with a summary of the status of the DominoRichTextItem (which is a wrapper for the RichTextItem. This is less useful but can still be helpful to know in some situations.</p>
  1002. <ul>
  1003. <li>Are there Embedded Images that have not been saved yet</li>
  1004. <li>are there attachments that are not saved yet</li>
  1005. <li>is the wrapped field a MIME field (or otherwise still rich text)</li>
  1006. <li>Is the backend item discarded</li>
  1007. </ul>
  1008. <p>Then the control will output the visual representation of the Mime Structure.</p>
  1009. <p>It does this in a &#8216;Nested List&#8217; of MIMEEntities so that you can see sibling / parent / child relationship (which is very important when debugging MIME problems)</p>
  1010. <p>Each Mime Entity will output:</p>
  1011. <ul>
  1012. <li>content type e.g. text/html</li>
  1013. <li>encoding details</li>
  1014. <li>mime headers (if set to do so with showHeaders)</li>
  1015. <li>content (if set to do so with showContent)</li>
  1016. </ul>
  1017. <h3>Demonstration Video</h3>
  1018. <p>Here is as short demonstration video of the control in action</p>
  1019. <div class="video-container"><iframe width="500" height="281" src="https://www.youtube.com/embed/-WPqKAl3Y-E?feature=oembed&#038;wmode=opaque" frameborder="0" allowfullscreen></iframe></div>
  1020. <h3></h3>
  1021. <h3>Installation / usage</h3>
  1022. <p>You can either install the GregorbyteXspLibrary and use from that, or you can rip out the necessary files and use within an NSF</p>
  1023. <p>To do it via installing the library:</p>
  1024. <ul>
  1025. <li>Download the GregorbyteXspLibrary from the <a href="https://github.com/camac/GregorbyteXspLibrary/releases">project&#8217;s github releases page</a></li>
  1026. <li>Install the library to your Domino Designer (same method as IBM ExtLib)</li>
  1027. <li>Install the library to your Domino Server (same method as IBM ExtLib)</li>
  1028. <li>Enable the library in your NSF (Xsp Properties page, &#8216;Page Generation&#8217; section choose &#8216;com.gregorbyte.xsp.library&#8217;</li>
  1029. <li>Drag the &#8216;Mime Inspector&#8217; onto your page</li>
  1030. </ul>
  1031. <p>To do it the &#8216;non-library&#8217; way:</p>
  1032. <ul>
  1033. <li>Copy the <a href="https://github.com/camac/GregorbyteXspLibrary/blob/master/com.gregorbyte.xsp.core/src/com/gregorbyte/xsp/util/MimeUtil.java">MimeUtil </a>class and paste it in your java elements section (you will need to removeh/comment out all statements referencing the GregorbyteLogger</li>
  1034. <li>Copy the <a href="https://github.com/camac/GregorbyteXspLibrary/blob/master/com.gregorbyte.xsp.controls/src/com/gregorbyte/xsp/component/UIMimeInspector.java">UIMimeInspector </a>class and paste it in your java elements section</li>
  1035. <li>Copy the <a href="https://github.com/camac/GregorbyteXspLibrary/blob/master/com.gregorbyte.xsp.controls/src/com/gregorbyte/xsp/config/gregorbyte-mimeinspector.xsp-config">gregorbyte-mimeinspector.xsp-config</a> file and put it in your WebContent\WEB-INF directory in your NSF</li>
  1036. <li>There is no renderer because the control renders itself, so no need for renderer or faces-conig.xml entries</li>
  1037. </ul>
  1038. <p>Here is an example of the code in the XPages Markup:</p><pre class="crayon-plain-tag">&lt;gb:mimeInspector id="mimeInspector1" value="#{document1.Body}"
  1039. showHeaders="true"
  1040. showContent="true"&gt;&lt;/gb:mimeInspector&gt;</pre><p>Here is an example of some output with &#8216;showHeaders&#8217; and &#8216;showContent&#8217;. In this example the mime being looked at is just a single text/html MimeEntity.</p>
  1041. <p><img class="aligncenter size-full wp-image-411" src="http://camerongregor.com/wp-content/uploads/2017/02/exampleMimeInspectorOutput2.jpg" alt="" width="564" height="584" srcset="http://camerongregor.com/wp-content/uploads/2017/02/exampleMimeInspectorOutput2.jpg 564w, http://camerongregor.com/wp-content/uploads/2017/02/exampleMimeInspectorOutput2-290x300.jpg 290w" sizes="(max-width: 564px) 100vw, 564px" /></p>
  1042. <p>Let me know if you have any trouble, I hope this helps somebody!</p>
  1043. ]]></content:encoded>
  1044. <wfw:commentRss>http://camerongregor.com/2017/02/14/xpages-webmail-using-mime-inspector-to-debug-mime/feed/</wfw:commentRss>
  1045. <slash:comments>3</slash:comments>
  1046. </item>
  1047. <item>
  1048. <title>Pasting Images into XPages CKEditor</title>
  1049. <link>http://camerongregor.com/2017/02/13/pasting-images-into-xpages-ckeditor/</link>
  1050. <comments>http://camerongregor.com/2017/02/13/pasting-images-into-xpages-ckeditor/#comments</comments>
  1051. <pubDate>Sun, 12 Feb 2017 22:00:21 +0000</pubDate>
  1052. <dc:creator><![CDATA[camerongregor]]></dc:creator>
  1053. <category><![CDATA[Software Development]]></category>
  1054. <category><![CDATA[ckeditor]]></category>
  1055. <category><![CDATA[xpages]]></category>
  1056.  
  1057. <guid isPermaLink="false">http://camerongregor.com/?p=392</guid>
  1058. <description><![CDATA[Programs like &#8216;Snipping Tool&#8217; on Windows, are super useful for users to make a quick snapshot, do some quick markup on the image, paste into chat/email and send. Unfortunately when using the default configuration&#46;&#46;&#46;]]></description>
  1059. <content:encoded><![CDATA[<p>Programs like &#8216;Snipping Tool&#8217; on Windows, are super useful for users to make a quick snapshot, do some quick markup on the image, paste into chat/email and send.</p>
  1060. <p>Unfortunately when using the default configuration of CKEditor in XPages (the inputRichText control), support for pasting images is not available for all browsers, and even for the ones that do support it, the images are only pasted as a PNG data URI. I have explained data URI images in <a href="http://camerongregor.com/2016/11/14/preventing-pasting-of-images-in-ckeditor/">a previous post</a>, so check that out if you are unfamiliar with it.</p>
  1061. <p>This also means the pasted images will <a href="http://stackoverflow.com/questions/40540890/configure-ckeditor-to-encode-pasted-images-as-base-64-jpg-not.png" data-rel="lightbox-0" title="">not display properly in Some versions of IBM Notes client</a>, and also if you are using the content of the inputRichText for sending email content, the pasted images will also not display in the majority of webmail / email clients. This will result in a lot of replies from recipients along the lines of &#8220;I can&#8217;t see any images&#8217;.</p>
  1062. <p>My major project recently has been a webmail interface for XPages, and accordingly I needed to solve this problem to make the users happy!</p>
  1063. <p><strong>From first solution to Current solution</strong></p>
  1064. <p>The solution to this problem, was to intercept the image paste event and instead of creating the data URI image, it uploads the image via the same mechanism that is used for the standard &#8216;image button&#8217; on the CKEditor toolbar. This involves posting the image data to a URL and interpreting the &#8216;success&#8217; response, and then inserting the corresponding &#8216;&lt;img&gt;&#8217; tag into the editor content.</p>
  1065. <p>The first solution I came up was a modification of the free <a href="http://ckeditor.com/addon/imagepaste">imagepaste CKEditor plugin</a> by Alfonso Martinez de Lizarrondo. Unfortunately this solution only worked for Firefox. So it was a good start but not completely useful because some users preferred to use Chrome.</p>
  1066. <p>Alfonso had also released a paid plugin called <a href="https://alfonsoml.blogspot.com.au/p/simpleuploads-plugin-for-ckeditor.html">SimpleUploads</a>, which included support for other browsers such as Chrome and Internet Explorer. So I purchased this plugin and ripped the parts out of it that were needed to support chrome and internet explorer, and modified my existing solution.</p>
  1067. <p>This was great for me! Now my users could paste from Firefox, Chrome and Internet Explorer. However I could not share this solution on my blog because it included source code from the plugin that I purchased. The plugin is not licensed as Open Source, so this would not be within my rights to do so.</p>
  1068. <p>So when a stackoverflow user had the same problem,  I decided to amend the solution so that I could share it whilst respecting the licensing conditions of the original plugin author.</p>
  1069. <p>Alfonso had actually already included some callback functions which allowed customisation of the simpleuploads plugin. It turns out I could actually reconfigure my solution to be an &#8216;extension&#8217; of the simpleuploads plugin.</p>
  1070. <p>I modified the solution to use the callbacks and it was almost perfect, but I just needed Alfonso to make 1 minor change to the SimpleUploads plugin. He agreed to make the change and his latest version of SimpleUploads includes the modification to allow my solution to work.</p>
  1071. <h3><strong>The Solution</strong></h3>
  1072. <h4>Obtain SimpleUploads</h4>
  1073. <p>So as mentioned above, you will need to purchase the SimpleUploads plugin and make sure you have the latest version or at least verson 4.4.4</p>
  1074. <p>It starts at 10 Euros for a single site licence.</p>
  1075. <ul>
  1076. <li>If you are a new customer you will automatically receive the latest version.</li>
  1077. <li>If you had previously bought SimpleUploads you may need to request the latest version from Alfonso. Alfonso has not pushed the new version out to his customer mailing list yet as the change is not significant for other users.</li>
  1078. </ul>
  1079. <h4>Install SimpleUploads</h4>
  1080. <p>Once you have obtained the simple uploads plugin, put it in your WEB-INF directory of the NSF</p>
  1081. <p><img class="aligncenter size-full wp-image-401" src="http://camerongregor.com/wp-content/uploads/2017/02/simpleuploadswebinf.jpg" alt="" width="657" height="332" srcset="http://camerongregor.com/wp-content/uploads/2017/02/simpleuploadswebinf.jpg 657w, http://camerongregor.com/wp-content/uploads/2017/02/simpleuploadswebinf-300x152.jpg 300w" sizes="(max-width: 657px) 100vw, 657px" /></p>
  1082. <h4>Create a new Javascript library in your nsf</h4>
  1083. <p>This library will configure CKEditor to know the whereabouts of simpleuploads plugin, and also include the necessary extensions to simpleuploads to allow it to work with XPages.</p>
  1084. <p>I have called it &#8216;xspSimpleUploads&#8217;</p>
  1085. <p><img class="size-full wp-image-402 aligncenter" src="http://camerongregor.com/wp-content/uploads/2017/02/xspsimpleuploads.jpg" alt="" width="223" height="91" /></p>
  1086. <p>Here are the contents of the library:</p><pre class="crayon-plain-tag">// Get the Base Url of the nsf so we can figure out the location of simpleuploads plugin
  1087. var urlBase = document.URL.substr(0,document.URL.lastIndexOf('.nsf') + 4);
  1088.  
  1089. // Tell CKEditor not to include a random timestamp that forces cache (because Domino gets upset if it is there)
  1090. CKEDITOR.timestamp = '';
  1091.  
  1092. // Tell CKEditor to include simpleuploads as an available plugin
  1093. CKEDITOR.plugins.addExternal('simpleuploads', urlBase + '/simpleuploads/', 'plugin.js');
  1094.  
  1095. // Disable the file upload part of SimpleUploads
  1096. CKEDITOR.config.filebrowserUploadUrl = 'dontuploadfiles';
  1097. CKEDITOR.on('instanceReady', function(e) {
  1098.  
  1099. var currPageUrl = document.location.href;
  1100.  
  1101. e.editor.config.filebrowserUploadUrl = false;
  1102.  
  1103. if (currPageUrl.indexOf('?') === -1) {
  1104. e.editor.config.filebrowserImageUploadUrl = currPageUrl;
  1105. } else {
  1106. e.editor.config.filebrowserImageUploadUrl = currPageUrl.substring(0, currPageUrl.indexOf('?'));
  1107. }
  1108.  
  1109.    // Listen for Start Upload
  1110.    e.editor.on( 'simpleuploads.startUpload' , function(ev) {
  1111.  
  1112.        var data = ev.data;
  1113.  
  1114.        var id = ev.editor.element.$.id;
  1115.        
  1116.        if (ev.data.url.indexOf('?') !== -1) {
  1117.         ev.data.url = ev.data.url.substring(0, currPageUrl.indexOf('?'));
  1118.        }        
  1119.        
  1120.        // Add the xpage viewid and the client id of the inputRichText
  1121.        ev.data.url += '?$$axtarget=' + id + '&amp;$$viewid=' + XSP.findForm(id)['$$viewid'].value;        
  1122.  
  1123.        var extraFields = ev.data.extraFields || {};
  1124.  
  1125.        var imgid = CKEDITOR.tools.getNextId();
  1126.        var fileName = imgid;
  1127.  
  1128.        ev.data.name = fileName + '.png';
  1129.  
  1130.        CKEDITOR.tools.extend(extraFields, {
  1131.            'fileName': fileName + '.png',
  1132.            'actionType': 'embedded-image'
  1133.        });
  1134.  
  1135.        ev.data.extraFields = extraFields;
  1136.  
  1137.        // Tell the Request to expect a html document in response
  1138.        ev.data.xhr = new XMLHttpRequest();
  1139.        ev.data.xhr.responseType = 'document';
  1140.    
  1141.    });
  1142.    
  1143.    e.editor.on( 'simpleuploads.serverResponse' , function(ev) {
  1144.      
  1145.        var respXml = ev.data.xhr.responseXML;
  1146.        var jsonText; // = respXml.documentElement.childNodes[1].firstChild.innerHTML;;
  1147.        
  1148.        if (!respXml) {
  1149.         /*
  1150. I am aware that parsing html with regex is not recommended
  1151. however the simple upload plugin does not allow for setting
  1152. the responseType to document, and the response should be the same
  1153.         */
  1154.         var re = /.*&lt;textarea&gt;(.*)&lt;\/textarea&gt;.*/;
  1155.         var jsonText = ev.data.xhr.responseText.replace(re, "$1");
  1156.      
  1157.        } else {
  1158.         jsonText = respXml.documentElement.childNodes[1].firstChild.innerHTML;
  1159.        }
  1160.  
  1161. var embeddedImage = XSP.fromJson(jsonText);
  1162.  
  1163. if (null != embeddedImage &amp;&amp; embeddedImage.statusMessage == "SUCCESS") {
  1164.  
  1165. var imageElement = ev.editor.document.createElement(embeddedImage.tag);
  1166. imageElement.setAttribute("src", embeddedImage.src);
  1167. imageElement.setAttribute("data-cke-saved-src", embeddedImage.dataSrc);
  1168. imageElement.setAttribute("alt", ev.data.altText);
  1169.  
  1170. ev.editor.insertElement(imageElement);
  1171. } else {
  1172. if (null != embeddedImage) {
  1173. XSP.alert(embeddedImage.statusMessage);
  1174. } else {
  1175. console.log("Error:IbmImage:ibmxspimage.js:load");
  1176. }
  1177. }
  1178. var el = ev.editor.document.getById( ev.data.data.id );
  1179. if (!el) {
  1180. } else {
  1181. if (ev.data.data.originalNode)
  1182. el.$.parentNode.replaceChild(ev.data.data.originalNode, el.$);
  1183. else
  1184. el.remove();
  1185. }
  1186. ev.editor.fire("updateSnapshot");
  1187. ev.cancel();        
  1188.        
  1189.    });    
  1190.    
  1191. });</pre><p><strong>Configure your XPage and Ckeditor</strong></p>
  1192. <p>You must include the Script Library on your xpage, and you must tell your CKeditor to use simpleuploads via the extraPlugins dojoAttribute</p>
  1193. <p>Here is an example page that I am using with the key points highlighted.</p><pre class="crayon-plain-tag">&lt;?xml version="1.0" encoding="UTF-8"?&gt;
  1194. &lt;xp:view xmlns:xp="http://www.ibm.com/xsp/core"&gt;
  1195.  
  1196. &lt;xp:this.resources&gt;
  1197. &lt;xp:script src="/xspSimpleUploads.js" clientSide="true"&gt;&lt;/xp:script&gt;
  1198. &lt;/xp:this.resources&gt;
  1199.  
  1200. &lt;xp:this.data&gt;
  1201. &lt;xp:dominoDocument var="document1" formName="Post"&gt;&lt;/xp:dominoDocument&gt;
  1202. &lt;/xp:this.data&gt;
  1203. &lt;xp:table&gt;
  1204. &lt;xp:tr&gt;
  1205. &lt;xp:td&gt;
  1206. &lt;xp:label value="Subject:" id="subject_Label1" for="subject1"&gt;&lt;/xp:label&gt;
  1207. &lt;/xp:td&gt;
  1208. &lt;xp:td&gt;
  1209. &lt;xp:inputText value="#{document1.Subject}" id="subject1"&gt;&lt;/xp:inputText&gt;
  1210. &lt;/xp:td&gt;
  1211. &lt;/xp:tr&gt;
  1212. &lt;/xp:table&gt;
  1213. &lt;xp:inputRichText id="inputRichText1" value="#{document1.Body}"&gt;
  1214. &lt;xp:this.dojoAttributes&gt;
  1215. &lt;xp:dojoAttribute name="extraPlugins" value="simpleuploads"&gt;&lt;/xp:dojoAttribute&gt;
  1216. &lt;/xp:this.dojoAttributes&gt;
  1217. &lt;/xp:inputRichText&gt;
  1218. &lt;xp:br&gt;&lt;/xp:br&gt;
  1219. &lt;xp:button value="Save" id="button1"&gt;
  1220. &lt;xp:eventHandler event="onclick" submit="true" refreshMode="complete"&gt;
  1221. &lt;xp:this.action&gt;
  1222. &lt;xp:saveDocument var="document1"&gt;&lt;/xp:saveDocument&gt;
  1223. &lt;/xp:this.action&gt;
  1224. &lt;/xp:eventHandler&gt;
  1225. &lt;/xp:button&gt;
  1226.  
  1227. &lt;xp:viewPanel rows="30" id="viewPanel1" pageName="/Home.xsp"&gt;
  1228. &lt;xp:this.facets&gt;
  1229. &lt;xp:pager partialRefresh="true" layout="Previous Group Next" xp:key="headerPager" id="pager1"&gt;&lt;/xp:pager&gt;
  1230. &lt;/xp:this.facets&gt;
  1231. &lt;xp:this.data&gt;
  1232. &lt;xp:dominoView var="view1" viewName="Posts"&gt;&lt;/xp:dominoView&gt;
  1233. &lt;/xp:this.data&gt;
  1234. &lt;xp:viewColumn columnName="Subject" id="viewColumn1" displayAs="link" openDocAsReadonly="true"&gt;
  1235. &lt;xp:viewColumnHeader value="Subject" id="viewColumnHeader1"&gt;&lt;/xp:viewColumnHeader&gt;
  1236. &lt;/xp:viewColumn&gt;
  1237. &lt;/xp:viewPanel&gt;
  1238.  
  1239. &lt;/xp:view&gt;</pre><p></p>
  1240. <h3>Result:</h3>
  1241. <p>Your users should be able to paste images with no worries!</p>
  1242. <p>As usual, I am sure I haven&#8217;t thought of everything so if you have any problems with the above, then please let me know!</p>
  1243. <h3>Demonstration</h3>
  1244. <p>As requested by Timothy Briley, here is a demonstration video which demonstrates the problem and shows the steps to implement the solution.</p>
  1245. <div class="video-container"><iframe width="500" height="281" src="https://www.youtube.com/embed/LYQpJ3qqwsQ?feature=oembed&#038;wmode=opaque" frameborder="0" allowfullscreen></iframe></div>
  1246. ]]></content:encoded>
  1247. <wfw:commentRss>http://camerongregor.com/2017/02/13/pasting-images-into-xpages-ckeditor/feed/</wfw:commentRss>
  1248. <slash:comments>5</slash:comments>
  1249. </item>
  1250. <item>
  1251. <title>Preventing pasting of remotely hosted images in CKEditor</title>
  1252. <link>http://camerongregor.com/2016/11/15/preventing-pasting-of-remotely-hosted-images-in-ckeditor/</link>
  1253. <comments>http://camerongregor.com/2016/11/15/preventing-pasting-of-remotely-hosted-images-in-ckeditor/#comments</comments>
  1254. <pubDate>Mon, 14 Nov 2016 23:21:59 +0000</pubDate>
  1255. <dc:creator><![CDATA[camerongregor]]></dc:creator>
  1256. <category><![CDATA[Software Development]]></category>
  1257. <category><![CDATA[ckeditor]]></category>
  1258. <category><![CDATA[webmail]]></category>
  1259. <category><![CDATA[xpages]]></category>
  1260.  
  1261. <guid isPermaLink="false">http://camerongregor.com/?p=384</guid>
  1262. <description><![CDATA[In the previous post, I showed how to prevent a user from pasting Images from the Clipboard into CKEditor. This post is of a similar nature but is designed to ensure that users don&#8217;t&#46;&#46;&#46;]]></description>
  1263. <content:encoded><![CDATA[<p>In the previous post, I showed <a href="http://camerongregor.com/2016/11/14/preventing-pasting-of-images-in-ckeditor/">how to prevent a user from pasting Images from the Clipboard into CKEditor</a>. This post is of a similar nature but is designed to ensure that users don&#8217;t paste images with URLs to external / internal applications.</p>
  1264. <p>This post is part of my <a href="http://camerongregor.com/2016/04/19/tips-for-creating-a-webmail-ui-with-xpages/">XPages webmail tips series</a>, and addresses a problem where, a user copies and pastes some HTML that includes images, from a webpage and pastes it into CKEditor for a message that is then sent via email. The recipient is then unable to see the image due to the fact they don&#8217;t have the same access as the author of the email.</p>
  1265. <p>The cause of problem is, when the image is pasted it is pasted as an img tag with a link to the location of the image on a server.<br />
  1266. There is no guarantee that the email recipient can access the server that the image is located on. The server is possibly behind a firewall, OR if the HTML was copied from an internal system, then it is possible an external email recipient does not have access to that internal server.</p>
  1267. <p>Additionally even for Internal emails, if the html was copied from an XPages application and the copied image is located inside a Notes Document, the URL that is used for that image is only temporarily available by the Xpages Persistence service, and is only available to the user that copied the HTML.</p>
  1268. <p>The result of all of this is more complaints of &#8220;I can&#8217;t see any image&#8221;</p>
  1269. <p><strong>Another CKEditor Plugin!</strong></p>
  1270. <p>The solution is just a modified version of the CKEditor plugin in previous post. The plugin listens for pasted content, and strips out any remotely hosted Images.</p><pre class="crayon-plain-tag">CKEDITOR.plugins.add('blockpasteimagelink', {
  1271.  
  1272. init : function(editor) {
  1273.  
  1274. function replaceImgText(html) {
  1275.  
  1276. var replacedSomething = false;
  1277.  
  1278. var ret = html.replace(/&lt;img[^&gt;]*src="http.*?"[^&gt;]*&gt;/gi, function(img) {
  1279. replacedSomething = true;
  1280. return '';
  1281. });
  1282.  
  1283. if (replacedSomething) {
  1284. alert('The Image you have attempted to paste is hosted on a remote server and may not be visible to others. Pasting these images is not currently supported, please upload an image file using the Image button in the toolbar.');
  1285. }
  1286.  
  1287. return ret;
  1288. }
  1289.  
  1290. function chkImg() {
  1291.  
  1292. // don't execute code if the editor is readOnly
  1293. if (editor.readOnly)
  1294. return;
  1295.  
  1296. setTimeout(function() {
  1297. editor.document.$.body.innerHTML = replaceImgText(editor.document.$.body.innerHTML);
  1298. }, 100);
  1299. }
  1300.  
  1301. editor.on('contentDom', function() {
  1302. // For Firefox
  1303. editor.document.on('drop', chkImg);
  1304. // For IE
  1305. editor.document.getBody().on('drop', chkImg);
  1306. });
  1307.  
  1308. editor.on('paste', function(e) {
  1309.  
  1310. var html = e.data.dataValue;
  1311. if (!html) {
  1312. return;
  1313. }
  1314.  
  1315. e.data.dataValue = replaceImgText(html);
  1316.  
  1317. });
  1318.  
  1319. }
  1320.  
  1321. });</pre><p>Save the above javascript as a script library in your nsf, called &#8216;blockpasteimagelink&#8217;</p>
  1322. <p>Make sure to include the script library on your XPage, and then tell your InputRichText to use the plugin using the extraPlugins dojoAttribute. Here is a sample XPage:</p><pre class="crayon-plain-tag">&lt;?xml version="1.0" encoding="UTF-8"?&gt;
  1323. &lt;xp:view xmlns:xp="http://www.ibm.com/xsp/core"&gt;
  1324.  
  1325. &lt;xp:this.data&gt;
  1326. &lt;xp:dominoDocument var="document1"&gt;&lt;/xp:dominoDocument&gt;
  1327. &lt;/xp:this.data&gt;
  1328.  
  1329. &lt;xp:this.resources&gt;
  1330. &lt;xp:script src="/blockpasteimagelink.js" clientSide="true"&gt;&lt;/xp:script&gt;
  1331. &lt;/xp:this.resources&gt;
  1332. &lt;xp:inputRichText id="inputRichText1" value="#{document1.body}"&gt;
  1333. &lt;xp:this.dojoAttributes&gt;
  1334. &lt;xp:dojoAttribute name="extraPlugins" value="blockpasteimagelink"&gt;&lt;/xp:dojoAttribute&gt;
  1335. &lt;/xp:this.dojoAttributes&gt;
  1336. &lt;/xp:inputRichText&gt;
  1337. &lt;/xp:view&gt;</pre><p>Now let&#8217;s test it out, I would like everyone to know about the mythical Jackalope, so I will copy some info from Wikipedia!</p>
  1338. <p><img class="aligncenter size-full wp-image-385" src="http://camerongregor.com/wp-content/uploads/2016/11/PreventPasteCopyJackalope.jpg" alt="preventpastecopyjackalope" width="368" height="400" srcset="http://camerongregor.com/wp-content/uploads/2016/11/PreventPasteCopyJackalope.jpg 368w, http://camerongregor.com/wp-content/uploads/2016/11/PreventPasteCopyJackalope-276x300.jpg 276w" sizes="(max-width: 368px) 100vw, 368px" /></p>
  1339. <p>Then I will paste it into my CKEditor, where I will receive a warning&#8230;</p>
  1340. <p><img class="aligncenter size-full wp-image-386" src="http://camerongregor.com/wp-content/uploads/2016/11/PreventPasteWarning.jpg" alt="preventpastewarning" width="638" height="262" srcset="http://camerongregor.com/wp-content/uploads/2016/11/PreventPasteWarning.jpg 638w, http://camerongregor.com/wp-content/uploads/2016/11/PreventPasteWarning-300x123.jpg 300w" sizes="(max-width: 638px) 100vw, 638px" /></p>
  1341. <p>And after clicking ok, I can see that everything except the image has been pasted&#8230;</p>
  1342. <p><img class="aligncenter size-full wp-image-387" src="http://camerongregor.com/wp-content/uploads/2016/11/PreventPasteAfter.jpg" alt="preventpasteafter" width="514" height="269" srcset="http://camerongregor.com/wp-content/uploads/2016/11/PreventPasteAfter.jpg 514w, http://camerongregor.com/wp-content/uploads/2016/11/PreventPasteAfter-300x157.jpg 300w" sizes="(max-width: 514px) 100vw, 514px" /></p>
  1343. <p><strong>Summary</strong></p>
  1344. <p>So we have now prevented some more cases whereby the recipient of an email will have trouble viewing images, it can be a little frustating for a user, but probably less frustrating that having to re-send an email, so I call that a win.</p>
  1345. <p>I had also intended on looking into the possibility of modifying the plugin so that upon pasting a remote image, the browser would try to download that image and then upload to the XPages server to be attached as an embedded image, however I haven&#8217;t looked into that yet!</p>
  1346. <p>In our system we have another CKEditor plugin which will allows users to paste image data from the clipboard, this is a nicer solution and I will cover that in the next post.</p>
  1347. ]]></content:encoded>
  1348. <wfw:commentRss>http://camerongregor.com/2016/11/15/preventing-pasting-of-remotely-hosted-images-in-ckeditor/feed/</wfw:commentRss>
  1349. <slash:comments>1</slash:comments>
  1350. </item>
  1351. <item>
  1352. <title>Preventing Pasting of Images in CKEditor</title>
  1353. <link>http://camerongregor.com/2016/11/14/preventing-pasting-of-images-in-ckeditor/</link>
  1354. <comments>http://camerongregor.com/2016/11/14/preventing-pasting-of-images-in-ckeditor/#comments</comments>
  1355. <pubDate>Mon, 14 Nov 2016 00:43:29 +0000</pubDate>
  1356. <dc:creator><![CDATA[camerongregor]]></dc:creator>
  1357. <category><![CDATA[Software Development]]></category>
  1358. <category><![CDATA[ckeditor]]></category>
  1359. <category><![CDATA[webmail]]></category>
  1360. <category><![CDATA[xpages]]></category>
  1361.  
  1362. <guid isPermaLink="false">http://camerongregor.com/?p=374</guid>
  1363. <description><![CDATA[In the process of developing our XPages &#8216;Webmail&#8217; interface, we discovered that many recipients were unable to view embedded images in the emails. After investigating, it was caused by the images being embedded using&#46;&#46;&#46;]]></description>
  1364. <content:encoded><![CDATA[<p>In the process of developing our XPages &#8216;Webmail&#8217; interface, we discovered that many recipients were unable to view embedded images in the emails.</p>
  1365. <p>After investigating, it was caused by the images being embedded using Data URIs. <a href="http://stackoverflow.com/questions/6070196/what-is-data-uri-support-like-in-major-email-client-software">Support for Data URI Images is not universal</a>, and because it is supported in IBM Notes, everything looked like it was working ok, but a quick test viewing an email in Gmail confirmed a problem when images could not be seen.</p>
  1366. <p><strong>What is a Data URI?</strong></p>
  1367. <p>You are most likely familar with an image being specified by a URL to Resource, in this cause all the binary data for the image is requested from some location</p><pre class="crayon-plain-tag">&lt;img src="reddot.png" alt="Red dot" /&gt;</pre><p>With a data URI, all the binary data for the image is contained directly in the src=&#8221;&#8221; attribute, and does not need to be requested from anywhere, here is an example <a href="https://en.wikipedia.org/wiki/Data_URI_scheme#HTML">I lifted from wikipedia</a>:</p><pre class="crayon-plain-tag">&lt;img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUA
  1368. AAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO
  1369. 9TXL0Y4OHwAAAABJRU5ErkJggg==" alt="Red dot" /&gt;</pre><p>When sending emails however, it is a much better idea to embed the image as a separate mime entity, and refer to it using a &#8216;Content Identifier&#8217; (cid).</p><pre class="crayon-plain-tag">&lt;img alt="" src=cid:reddot_1479074021464&gt;&lt;/img&gt;</pre><p>The image data is then retrieved from the related mime entity which specifies that Content-ID as a MimeHeader</p><pre class="crayon-plain-tag">--==IFJRGLKFGIR32748UHRUHIHD
  1370. Content-Type: image/jpeg
  1371. Content-Disposition: inline;
  1372. filename="reddot_1479074021464.JPG"
  1373. Content-ID: &lt;reddot_1479074021464&gt;
  1374. Content-Transfer-Encoding: base64
  1375.  
  1376. /9j/4AAQSkZJRgABAQEAYABgAAD/4RD4RXhpZgAATU0AKgAAAAgABAE7AAIAAAAPAAAISodpAAQA
  1377. ...more binary data...
  1378. iigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigD/9k=
  1379.  
  1380.  
  1381. --==IFJRGLKFGIR32748UHRUHIHD--</pre><p><strong>Sending Embedded Images in Emails<br />
  1382. </strong></p>
  1383. <p>The best format for sending emails is the &#8216;content-id&#8217; method, and the Xpages CKEditor provides a toolbar button which will perform the image upload and attach as  separate mime entity.</p>
  1384. <p>On the other hand, an easy way for users to insert images is to use some screen capture software such as Snipping Tool and then copy and paste into the CKEditor. When this method is used, the image is inserted as a Data URI.</p>
  1385. <p>By default, some browsers don&#8217;t support the pasting of images anyway, but Firefox and probably some others do, and it must be stopped!</p>
  1386. <p><strong>Preventing Pasting of Images using a CKEditor plugin</strong></p>
  1387. <p>After a bit of googling I found that <a href="http://www.isummation.com/blog/block-drag-drop-image-or-direct-image-paste-into-ckeditor-using-firefox/">someone else had already come up with a plugin which prevents pasted images,</a> and it worked without modification for XPages</p>
  1388. <p>I will repost the plugin here, the only modification I have made is the alert message to explain to the User to use the image upload toolbar button</p><pre class="crayon-plain-tag">CKEDITOR.plugins.add('blockimagepaste', {
  1389.  
  1390. init : function(editor) {
  1391.  
  1392. function replaceImgText(html) {
  1393.  
  1394. var ret = html.replace(/&lt;img[^&gt;]*src="data:image\/(bmp|dds|gif|jpg|jpeg|png|psd|pspimage|tga|thm|tif|tiff|yuv|ai|eps|ps|svg);base64,.*?"[^&gt;]*&gt;/gi, function(img) {
  1395. alert("Pasting Image Data is not allowed. Please use the Image Upload button (if available).");
  1396. return '';
  1397. });
  1398.  
  1399. return ret;
  1400. }
  1401.  
  1402. function chkImg() {
  1403. // don't execute code if the editor is readOnly
  1404. if (editor.readOnly)
  1405. return;
  1406.  
  1407. setTimeout(function() {
  1408. editor.document.$.body.innerHTML = replaceImgText(editor.document.$.body.innerHTML);
  1409. }, 100);
  1410. }
  1411.  
  1412. editor.on('contentDom', function() {
  1413. // For Firefox
  1414. editor.document.on('drop', chkImg);
  1415. // For IE
  1416. editor.document.getBody().on('drop', chkImg);
  1417. });
  1418.  
  1419. editor.on('paste', function(e) {
  1420.  
  1421. var html = e.data.dataValue;
  1422. if (!html) {
  1423. return;
  1424. }
  1425.  
  1426. e.data.dataValue = replaceImgText(html);
  1427. });
  1428.  
  1429. } // Init
  1430. });</pre><p>To use the plugin, copy the above code into a Client Side JavaScript Library &#8216;blockimagepaste&#8217;.</p>
  1431. <p><img class="aligncenter size-full wp-image-378" src="http://camerongregor.com/wp-content/uploads/2016/11/preventpasteScriptLib-1.jpg" alt="preventpastescriptlib" width="699" height="309" srcset="http://camerongregor.com/wp-content/uploads/2016/11/preventpasteScriptLib-1.jpg 699w, http://camerongregor.com/wp-content/uploads/2016/11/preventpasteScriptLib-1-300x133.jpg 300w" sizes="(max-width: 699px) 100vw, 699px" /></p>
  1432. <p>&nbsp;</p>
  1433. <p>Then, make sure to include the ScriptLibrary as a resource on your page. Also add the dojoAttribute &#8216;extraPlugins&#8217; with the name of the plugin (from line 1 above &#8216;blockimagepaste&#8217;.</p><pre class="crayon-plain-tag">&lt;?xml version="1.0" encoding="UTF-8"?&gt;
  1434. &lt;xp:view xmlns:xp="http://www.ibm.com/xsp/core"&gt;
  1435.  
  1436. &lt;xp:this.data&gt;
  1437. &lt;xp:dominoDocument var="document1"&gt;&lt;/xp:dominoDocument&gt;
  1438. &lt;/xp:this.data&gt;
  1439.  
  1440. &lt;xp:this.resources&gt;
  1441. &lt;xp:script src="/blockimagepaste.js" clientSide="true"&gt;&lt;/xp:script&gt;
  1442. &lt;/xp:this.resources&gt;
  1443. &lt;xp:inputRichText id="inputRichText1" value="#{document1.body}"&gt;
  1444. &lt;xp:this.dojoAttributes&gt;
  1445. &lt;xp:dojoAttribute name="extraPlugins" value="blockimagepaste"&gt;&lt;/xp:dojoAttribute&gt;
  1446. &lt;/xp:this.dojoAttributes&gt;
  1447. &lt;/xp:inputRichText&gt;
  1448. &lt;/xp:view&gt;</pre><p>So now when a user attempts to paste an image it will not be completed, and they will be given the alert</p>
  1449. <p><img class="aligncenter size-full wp-image-376" src="http://camerongregor.com/wp-content/uploads/2016/11/preventpastealert.jpg" alt="preventpastealert" width="734" height="309" srcset="http://camerongregor.com/wp-content/uploads/2016/11/preventpastealert.jpg 734w, http://camerongregor.com/wp-content/uploads/2016/11/preventpastealert-300x126.jpg 300w" sizes="(max-width: 734px) 100vw, 734px" /></p>
  1450. <p><strong>Summary and Next Steps</strong></p>
  1451. <p>So in this post we added a CKEditor plugin to our application, and configured our InputRichText to use it.</p>
  1452. <p>The plugin prevents the pasting of data URI images, however you could modify this to prevent any html you like, in a future post we will show a modified version of this plugin to prevent images pasted using URLs. This can be troublesome for emails, as users may copy and paste html from internal applications, which is then not available to external email recipients.</p>
  1453. <p>Also, copying and pasting images is actually quite a useful feature don&#8217;t you think? our users did too, so we also implemented another plugin which intercepts the pasted image, and uploads it using the normal &#8216;Content-ID&#8217; method instead, I will share that solution as well in a future post.</p>
  1454. <p>&nbsp;</p>
  1455. ]]></content:encoded>
  1456. <wfw:commentRss>http://camerongregor.com/2016/11/14/preventing-pasting-of-images-in-ckeditor/feed/</wfw:commentRss>
  1457. <slash:comments>3</slash:comments>
  1458. </item>
  1459. </channel>
  1460. </rss>
  1461.  

If you would like to create a banner that links to this page (i.e. this validation result), do the following:

  1. Download the "valid RSS" banner.

  2. Upload the image to your own server. (This step is important. Please do not link directly to the image on this server.)

  3. Add this HTML to your page (change the image src attribute if necessary):

If you would like to create a text link instead, here is the URL you can use:

http://www.feedvalidator.org/check.cgi?url=http%3A//camerongregor.com/category/software-development/feed/

Copyright © 2002-9 Sam Ruby, Mark Pilgrim, Joseph Walton, and Phil Ringnalda