<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>RoundupTracker &amp;mdash; Roundup Issue Tracker</title>
    <link>https://blog.rouilj.dynamic-dns.net/rouilj/tag:RoundupTracker</link>
    <description>A blog about developing and using the Roundup Issue Tracker. Covers web design/front end development, Python, security, user interface, customization, documentation, and community development.</description>
    <pubDate>Tue, 05 May 2026 10:28:07 -0400</pubDate>
    <item>
      <title>Copy issue reference to clipboard enhancement</title>
      <link>https://blog.rouilj.dynamic-dns.net/rouilj/copy-issue-reference-to-clipboard-enhancement</link>
      <description>&lt;![CDATA[On the Roundup Issue Tracker mailing list a user asked for a mechanism to create a reference for the current issue and copy it to the clipboard.&#xA;&#xA;The request was for the reference to look like:&#xA;  issue2345: title of issue&#xA;In Roundup, object designators like issue2345 are automatically hyperlinked to the corresponding object.&#xA;&#xA;The code I recommended used a button to trigger this operation. It also used the clipboard API. When clicking on the button (or hitting space or return while focused on the button) the reference format above will be copied to the clipboard. Triggering the clipboard API must be done by a user interaction. For example: activating a button. If the browser does not support the clipboard API, a simple alert() is shown.&#xA;&#xA;In addition to copying to the clipboard, the user gets feedback when the text of the button changes to &#34;Reference copied&#34;. Then it resets to the original message after 2 seconds. Since clicking the button multiple times is idempotent, there is no sense in disabling or debouncing the button.&#xA;!--more--&#xA;Note, this is unlikely to be a11y compliant as I don&#39;t think the change of button text is announced. If anybody has some ideas on how to make this more compliant, leave them in the comments. Maybe using aria-live=&#34;polite&#34; or &#34;assertive&#34; on the button element would do the trick?&#xA;&#xA;Here is the code I suggested:&#xA;&lt;button id=&#34;copyreference&#34; type=&#34;button&#34;&#xA;            tal:condition=&#34;context/iseditok&#34;  Copy Reference&#xA;/button&#xA;&#xA;script tal:attributes=&#34;nonce request/client/clientnonce&#34;&#xA;  (function () {&#xA;    &#34;use strict&#34;;&#xA;&#xA;    let crb = document.querySelector(&#34;#copyreference&#34;);&#xA;    if ( ! crb ) return&#xA;    crb.addEventListener(&#34;click&#34;, async (e) =  {&#xA;        e.preventDefault()&#xA;        if ( ! navigator.clipboard ) {&#xA;            alert(&#34;Clipboard is not available&#34;)&#xA;            return&#xA;        }&#xA;&#xA;        let issueDesignator = new URL(document.URL)&#xA;            .pathname&#xA;            .split(&#34;/&#34;)&#xA;            .pop()&#xA;        let issueTitleText = document.querySelector(&#34;#title&#34;).value;&#xA;&#xA;        await navigator.clipboard.writeText(${issueDesignator}: ${issueTitleText})&#xA;&#xA;        let originalCrbInnerText = crb.innerText&#xA;        crb.innerText = &#34;Reference copied&#34;&#xA;        setTimeout(() =  {&#xA;            crb.innerText = originalCrbInnerText}, 2000)&#xA;        }&#xA;    );&#xA;  })();&#xA;&#xA;The flexibility of Roundup allows the administrator to rewrite all of the HTML used in the web interface. As a result, I based my guess of CSS selectors on the HTML generated by the classic issue.item.html TAL template.&#xA;&#xA;The button could be placed anywhere on the page and the script (including the nonce required by the CSP) would be placed anywhere after the button. Probably at the end of the page so it doesn&#39;t block rendering.&#xA;&#xA;The classic structure of the issue display page for users with editing capability included an input with the id title. This is retrieved using the id and included in the string written to the clipboard.&#xA;&#xA;A user without editing capability for the issue (or without editing capability for the title attribute of an issue) does not have an input with id=&#34;title&#34;.&#xA;In these cases, the admin would have to modify the issue.item.html template to add an id of title to the enclosing element. Then the code above would be modified to replace:&#xA;&#xA;let issueTitleText = document.querySelector(&#34;#title&#34;).value;&#xA;with:&#xA; let issueTitle = document.querySelector(&#34;input#title&#34;);&#xA;   if ( ! issueTitle ) {&#xA;     issueTitle = document.querySelector(&#34;#title&#34;);&#xA;     issueTitleText = issueTitle.innerText&#xA;   } else {&#xA;     issueTitleText = issueTitle.value&#xA;   }&#xA;&#xA;In my original example, the button element is generated only if the user can edit the issue. Removing the tal:condition attribute would always display the button.&#xA;&#xA;There is nothing with an id or CSS selector that contains the object designator. I use the final element of the path of the URL to get the designator.&#xA;&#xA;I chose to use the IIFE code structure. This allows me to use the early return pattern if the button is not found. If anybody knows how to do the equivalent of an early return without an IIFE or other function in a script tag, leave your trick in the comments.&#xA;&#xA;If you use the Roundup Issue Tracker and have your own enhancements, feel free to mention them in the comments.&#xA;&lt;!--&#xA;{% embed https://www.roundup-tracker.org/?ref=devto %}&#xA;--  #WebDev, #opensource #RoundupTracker  #DevTo&#xA;]]&gt;</description>
      <content:encoded><![CDATA[<p>On the <a href="https://sourceforge.net/p/roundup/mailman/search/?q=Feature+Request+%3A+Copy+Issue+id+and+title+separated+by+%27%3B+%27+to+the+clipboard&amp;mail_list=roundup-users" rel="nofollow">Roundup Issue Tracker mailing list</a> a user asked for a mechanism to create a reference for the current issue and copy it to the clipboard.</p>

<p>The request was for the reference to look like:</p>

<pre><code>  issue2345: title of issue
</code></pre>

<p>In Roundup, object designators like <code>issue2345</code> are automatically hyperlinked to the corresponding object.</p>

<p>The code I recommended used a button to trigger this operation. It also used the <a href="https://developer.mozilla.org/en-US/docs/Web/API/Clipboard_API" rel="nofollow">clipboard API</a>. When clicking on the button (or hitting <code>space</code> or <code>return</code> while focused on the button) the reference format above will be copied to the clipboard. Triggering the clipboard API must be done by a user interaction. For example: activating a button. If the browser does not support the clipboard API, a simple <code>alert()</code> is shown.</p>

<p>In addition to copying to the clipboard, the user gets feedback when the text of the button changes to “Reference copied”. Then it resets to the original message after 2 seconds. Since clicking the button multiple times is idempotent, there is no sense in disabling or debouncing the button.

Note, this is unlikely to be a11y compliant as I don&#39;t think the change of button text is announced. If anybody has some ideas on how to make this more compliant, leave them in the comments. Maybe using <code>aria-live=&#34;polite&#34;</code> or <code>&#34;assertive&#34;</code> on the <code>button</code> element would do the trick?</p>

<p>Here is the code I suggested:</p>

<pre><code class="language-html">&lt;button id=&#34;copyreference&#34; type=&#34;button&#34;
            tal:condition=&#34;context/is_edit_ok&#34;&gt;
   Copy Reference
&lt;/button&gt;

&lt;script tal:attributes=&#34;nonce request/client/client_nonce&#34;&gt;
  (function () {
    &#34;use strict&#34;;

    let crb = document.querySelector(&#34;#copyreference&#34;);
    if ( ! crb ) return
    crb.addEventListener(&#34;click&#34;, async (e) =&gt; {
        e.preventDefault()
        if ( ! navigator.clipboard ) {
            alert(&#34;Clipboard is not available&#34;)
            return
        }

        let issueDesignator = new URL(document.URL)
            .pathname
            .split(&#34;/&#34;)
            .pop()
        let issueTitleText = document.querySelector(&#34;#title&#34;).value;

        await navigator.clipboard.writeText(`${issueDesignator}: ${issueTitleText}`)

        let originalCrbInnerText = crb.innerText
        crb.innerText = &#34;Reference copied&#34;
        setTimeout(() =&gt; {
            crb.innerText = originalCrbInnerText}, 2000)
        }
    );
  })();
</code></pre>

<p>The <a href="https://www.roundup-tracker.org/docs/features.html" rel="nofollow">flexibility of Roundup</a> allows the administrator to rewrite all of the HTML used in the web interface. As a result, I based my guess of CSS selectors on the <a href="https://sourceforge.net/p/roundup/code/ci/default/tree/share/roundup/templates/classic/html/issue.item.html" rel="nofollow">HTML generated by the classic <code>issue.item.html</code> TAL template</a>.</p>

<p>The button could be placed anywhere on the page and the script (including the nonce required by the <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP" rel="nofollow">CSP</a>) would be placed anywhere after the button. Probably at the end of the page so it doesn&#39;t block rendering.</p>

<p>The classic structure of the issue display page for users with editing capability included an input with the id <code>title</code>. This is retrieved using the id and included in the string written to the clipboard.</p>

<p>A user without editing capability for the issue (or without editing capability for the title attribute of an issue) does not have an input with <code>id=&#34;title&#34;</code>.
In these cases, the admin would have to modify the <code>issue.item.html</code> template to add an id of title to the enclosing element. Then the code above would be modified to replace:</p>

<pre><code class="language-js">let issueTitleText = document.querySelector(&#34;#title&#34;).value;
</code></pre>

<p>with:</p>

<pre><code class="language-js"> let issueTitle = document.querySelector(&#34;input#title&#34;);
   if ( ! issueTitle ) {
     issueTitle = document.querySelector(&#34;#title&#34;);
     issueTitleText = issueTitle.innerText
   } else {
     issueTitleText = issueTitle.value
   }
</code></pre>

<p>In my original example, the button element is generated only if the user can edit the issue. Removing the <code>tal:condition</code> attribute would always display the button.</p>

<p>There is nothing with an id or CSS selector that contains the object designator. I use the final element of the path of the URL to get the designator.</p>

<p>I chose to use the <a href="https://en.wikipedia.org/wiki/Immediately-invoked_function_expression" rel="nofollow">IIFE</a> code structure. This allows me to <a href="https://gomakethings.com/the-early-return-pattern-in-javascript/" rel="nofollow">use the early return pattern</a> if the button is not found. If anybody knows how to do the equivalent of an early return without an IIFE or other function in a script tag, leave your trick in the comments.</p>

<p>If you use the Roundup Issue Tracker and have your own enhancements, feel free to mention them in the comments.

<a href="/rouilj/tag:WebDev" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">WebDev</span></a>, <a href="/rouilj/tag:opensource" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">opensource</span></a> <a href="/rouilj/tag:RoundupTracker" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">RoundupTracker</span></a>  <a href="/rouilj/tag:DevTo" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">DevTo</span></a></p>
]]></content:encoded>
      <guid>https://blog.rouilj.dynamic-dns.net/rouilj/copy-issue-reference-to-clipboard-enhancement</guid>
      <pubDate>Thu, 09 Nov 2023 00:01:02 +0000</pubDate>
    </item>
    <item>
      <title>A Modern Form Update and Interior Border Hack with CSS Grid </title>
      <link>https://blog.rouilj.dynamic-dns.net/rouilj/i-am-a-developer-on-the-roundup-issue-tracker</link>
      <description>&lt;![CDATA[&#xA;I am a developer on the Roundup Issue Tracker. Roundup is a trouble ticketing system written in Python. It is very flexible. Developers, researchers, and support staff use Roundup to define and manage the lifecycle of issues.&#xA;&#xA;My background is in system administration. So I have built a tracker template designed for tracking system administration issues. Recently I redesigned the look of one of the more complex forms.&#xA;&#xA;This is the (short) story of the decisions I made and the techniques I used.&#xA;&#xA;!--more--&#xA;&#xA;The starting point&#xA;&#xA;I have limited design experience. When I set this up many years ago, it looked ok to me.&#xA;&#xA;A web form showing multiple fields (title, priority, status, etc.). Some span the width of the page, some are inside one of two columns. Fieldsets group the fields. Collapsed fieldsets show only their headers. The form has bold and bright-colored section headers. The expanded fieldsets have grooved borders.&#xA;&#xA;Looking at it today, the bold color swatches behind the fieldset legends and grooved borders make it seem cluttered and busy. It needs a more modern look.&#xA;&#xA;Step 1 - fewer borders&#xA;&#xA;First I removed the borders from the fieldsets. This brought up an issue. Tabbing through the form moves you down the columns (divs) and not across the rows. The borders and the gap between them helped re-enforce this.&#xA;&#xA;I decided to add a vertical rule between the columns. I set border-inline-end  to 2 pixels (2px) on the first column. When the first column was longer than the second this looked great. But, the fieldset panes in the first column are collapsible. When the first column was shorter than the second column, the line between the columns didn&#39;t reach the bottom of the second column.&#xA;&#xA;The solution I came up with was to add a 2px border-inline-start on the second column. Then I moved the second column back by 2px (the width of the line) using margin-inline-start. These overlapped border lines always span the entire height of the longest column.&#xA;&#xA;The layout uses the Gridiculous grid system rather than display: grid. This requires one last tweak. On narrower screens (e.g. a cell phone), the two side-by-side columns stack. Adding an @media query to remove the borders when on a narrow display completed the tweak.&#xA;&#xA;How to make a vertical rule with css grid&#xA;&#xA;I plan to use display: grid at some point for this layout. It would reduce the amount of CSS I need to serve and provide a more flexible layout. But, there is no mechanism for creating a rule in the gaps of a grid layout. I found a trick for doing that, but I haven&#39;t committed to using it yet. There is one nice advantage to using this trick. When the columns stack, grid removes the gap and also the (pseudo) vertical rule. No need for an @media cleanup.&#xA;&#xA;To create the rule, I created a div (the base div) with the background-color set to the vertical rule&#39;s color. I set the CSS for that div to  display: grid and the gap: 0 2px. I placed two divs inside the base div. They form the columns. Set their background-color to that of the rest of the page. Now the 2px column gap in the grid exposes the darker rule color of the base div. You can only emulate a solid border, but it works for my use case. Could linear gradients emulate a grooved, ridged, dotted, or dashed border?&#xA;&#xA;My eyes, my eyes&#xA;&#xA;The second change was to remove the banners of color behind the fieldset legends. With the more colorful themes, it&#39;s a bit bright on the eyes 8-). A simple underline matching the monochromatic theme replaces it. The HTML for the legend is legendspanTitle Goes Here/span/legend.  Removing the background color from legend   span cleared the banner. Since the span is set to use display: block, adding a 2px border-block-end completed the work. &#xA;&#xA;The final look&#xA;&#xA;The final redesign with  a form like the one above except with the changes described in the article.&#xA;&#xA;This is a significant improvement.&#xA;&#xA;But it&#39;s still (a) bad form...&#xA;&#xA;One question that you may ask is why tab order moves down the first/left column rather than across the form. As forms go this breaks a few rules:&#xA;&#xA;  use a single column for forms. In this case, I have&#xA;     not only two primary columns, but the basic &#xA;     fieldset has 2 subcolumns.&#xA;  put labels above their control, or right-aligned&#xA;     labels to the left of the control (for ltr&#xA;     languages). Here, I use left-aligned labels to the left&#xA;     of the controls.&#xA;&#xA;To justify my decisions, let&#39;s look at the use cases. As with most trackers, there are two types of users of this tracker. The:&#xA;&#xA;  people who need something done, and&#xA;  the people doing the work.&#xA;&#xA;There is also a third user, the person modifying the tracker for use at their site.&#xA;&#xA;The left-hand column provides information for the people who need something done. Tabbing down the column moves the page like there was only one column. Ideally, these users would scan down the left column of the page. Using left-aligned labels provides this scan line.&#xA;&#xA;Since Roundup can look at the user&#39;s role and remove/hide/collapse the second column we can get even closer to rule 1. This would make it a simpler form for these users. Still, displaying key status info above the fold requires packing a lot of fields in a small area. Following the &#34;one column&#34;, &#34;label on top&#34; guidelines would increase scrolling.&#xA;&#xA;There is one more tweak for this redesign. With the second column displayed, a skip link  will be added at the bottom of the first column. The skip link will jump to the update textarea where the user can add a change note/update. This bypasses all the tab stops in the second column.&#xA;&#xA;Adding a skip link at the top of the page to target the update  textarea area is an idea. However, a skip link already exists at the top of the page. It jumps to the main section, bypassing the left-hand navigation column (not shown in the images). Would adding a second top-of-page skip link be confusing clutter? I&#39;m not sure.&#xA;&#xA;For people doing the work, references to the schedule and linked tickets are more useful. The second column provides access to them. The &#34;All Fields&#34; tab provides even more information hidden from view. For example:&#xA;&#xA;   summaries of related tickets&#xA;   a graph of relationships among tickets&#xA;   extra fields for scheduling automatic actions.&#xA;&#xA;This brings us to the third user. I tried to set up a form that the Roundup admin could adapt for their workflows. The admin should not have to be an HTML/CSS expert. The idea of Roundup is that it is customizable for many use cases. I have tried to use clean semantic HTML and simple CSS to support that idea.&#xA;&#xA;Thanks for reading this. I hope you enjoyed it and learned something. You can find more information on Roundup using the link below.&#xA;{% embed https://www.roundup-tracker.org?ref=dev.to-modernformupdate %}&#xA;&#xA;#RoundupTracker #DevDotTo #WebDev]]&gt;</description>
      <content:encoded><![CDATA[<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s77umppmq50ebupd7xih.png" alt="" title="Banner image.">
I am a developer on the <a href="https://www.roundup-tracker.org?ref=local-modern_form_update" rel="nofollow">Roundup Issue Tracker</a>. Roundup is a trouble ticketing system written in Python. It is very flexible. Developers, researchers, and support staff use Roundup to define and manage the lifecycle of issues.</p>

<p>My background is in system administration. So I have built a tracker template designed for tracking system administration issues. Recently I redesigned the look of one of the more complex forms.</p>

<p>This is the (short) story of the decisions I made and the techniques I used.</p>



<h2 id="the-starting-point" id="the-starting-point">The starting point</h2>

<p>I have limited design experience. When I set this up many years ago, it looked ok to me.</p>

<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s77umppmq50ebupd7xih.png" alt="A web form showing multiple fields (title, priority, status, etc.). Some span the width of the page, some are inside one of two columns. Fieldsets group the fields. Collapsed fieldsets show only their headers. The form has bold and bright-colored section headers. The expanded fieldsets have grooved borders." title="Form before changes."></p>

<p>Looking at it today, the bold color swatches behind the fieldset legends and grooved borders make it seem cluttered and busy. It needs a more modern look.</p>

<h2 id="step-1-fewer-borders" id="step-1-fewer-borders">Step 1 – fewer borders</h2>

<p>First I removed the borders from the fieldsets. This brought up an issue. Tabbing through the form moves you down the columns (divs) and not across the rows. The borders and the gap between them helped re-enforce this.</p>

<p>I decided to add a vertical rule between the columns. I set <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/border-inline-end" rel="nofollow">border-inline-end</a>  to 2 pixels (2px) on the first column. When the first column was longer than the second this looked great. But, the fieldset panes in the first column are collapsible. When the first column was shorter than the second column, the line between the columns didn&#39;t reach the bottom of the second column.</p>

<p>The solution I came up with was to add a 2px border-inline-start on the second column. Then I moved the second column back by 2px (the width of the line) using <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/margin-inline-start" rel="nofollow">margin-inline-start</a>. These overlapped border lines always span the entire height of the longest column.</p>

<p>The layout uses the <a href="http://gridiculo.us/" rel="nofollow">Gridiculous</a> grid system rather than <code>display: grid</code>. This requires one last tweak. On narrower screens (e.g. a cell phone), the two side-by-side columns stack. Adding an @media query to remove the borders when on a narrow display completed the tweak.</p>

<h2 id="how-to-make-a-vertical-rule-with-css-grid" id="how-to-make-a-vertical-rule-with-css-grid">How to make a vertical rule with css grid</h2>

<p>I plan to use <code>display: grid</code> at some point for this layout. It would reduce the amount of CSS I need to serve and provide a more flexible layout. But, there is no mechanism for creating a rule in the gaps of a grid layout. I found a trick for doing that, but I haven&#39;t committed to using it yet. There is one nice advantage to using this trick. When the columns stack, grid removes the gap and also the (pseudo) vertical rule. No need for an @media cleanup.</p>

<p>To create the rule, I created a div (the base div) with the background-color set to the vertical rule&#39;s color. I set the CSS for that div to  <code>display: grid</code> and the <code>gap: 0 2px</code>. I placed two divs inside the base div. They form the columns. Set their background-color to that of the rest of the page. Now the 2px column gap in the grid exposes the darker rule color of the base div. You can only emulate a solid border, but it works for my use case. Could linear gradients emulate a grooved, ridged, dotted, or dashed border?</p>

<h2 id="my-eyes-my-eyes" id="my-eyes-my-eyes">My eyes, my eyes</h2>

<p>The second change was to remove the banners of color behind the fieldset legends. With the more colorful themes, it&#39;s a bit bright on the eyes 8-). A simple underline matching the monochromatic theme replaces it. The HTML for the legend is <code>&lt;legend&gt;&lt;span&gt;Title Goes Here&lt;/span&gt;&lt;/legend&gt;</code>.  Removing the background color from <code>legend &gt; span</code> cleared the banner. Since the span is set to use <code>display: block</code>, adding a 2px <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/border-block-end" rel="nofollow">border-block-end</a> completed the work.</p>

<h2 id="the-final-look" id="the-final-look">The final look</h2>

<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ymsg4q8cxnvs06eljw6s.png" alt="The final redesign with  a form like the one above except with the changes described in the article." title="Form after the changes in this article."></p>

<p>This is a significant improvement.</p>

<h2 id="but-it-s-still-a-bad-form" id="but-it-s-still-a-bad-form">But it&#39;s still (a) bad form...</h2>

<p>One question that you may ask is why tab order moves down the first/left column rather than across the form. As forms go this breaks a few rules:</p>
<ol><li>use a single column for forms. In this case, I have
 not only two primary columns, but the basic
 fieldset has 2 subcolumns.</li>
<li>put labels above their control, or right-aligned
 labels to the left of the control (for ltr
 languages). Here, I use left-aligned labels to the left
 of the controls.</li></ol>

<p>To justify my decisions, let&#39;s look at the use cases. As with most trackers, there are two types of users of this tracker. The:</p>
<ol><li>people who need something done, and</li>
<li>the people doing the work.</li></ol>

<p>There is also a third user, the person modifying the tracker for use at their site.</p>

<p>The left-hand column provides information for the people who need something done. Tabbing down the column moves the page like there was only one column. Ideally, these users would scan down the left column of the page. Using left-aligned labels provides this scan line.</p>

<p>Since Roundup can look at the user&#39;s role and remove/hide/collapse the second column we can get even closer to rule 1. This would make it a simpler form for these users. Still, displaying key status info above the fold requires packing a lot of fields in a small area. Following the “one column”, “label on top” guidelines would increase scrolling.</p>

<p>There is one more tweak for this redesign. With the second column displayed, a <a href="https://webaim.org/techniques/skipnav/" rel="nofollow">skip link</a>  will be added at the bottom of the first column. The skip link will jump to the update textarea where the user can add a change note/update. This bypasses all the tab stops in the second column.</p>

<p>Adding a skip link at the top of the page to target the update  textarea area is an idea. However, a skip link already exists at the top of the page. It jumps to the main section, bypassing the left-hand navigation column (not shown in the images). Would adding a second top-of-page skip link be confusing clutter? I&#39;m not sure.</p>

<p>For people doing the work, references to the schedule and linked tickets are more useful. The second column provides access to them. The “All Fields” tab provides even more information hidden from view. For example:</p>
<ol><li>summaries of related tickets</li>
<li>a graph of relationships among tickets</li>
<li>extra fields for scheduling automatic actions.</li></ol>

<p>This brings us to the third user. I tried to set up a form that the Roundup admin could adapt for their workflows. The admin should not have to be an HTML/CSS expert. The idea of Roundup is that it is customizable for many use cases. I have tried to use clean semantic HTML and simple CSS to support that idea.</p>

<p>Thanks for reading this. I hope you enjoyed it and learned something. You can find more information on Roundup using the link below.
{% embed <a href="https://www.roundup-tracker.org?ref=dev.to-modern_form_update" rel="nofollow">https://www.roundup-tracker.org?ref=dev.to-modern_form_update</a> %}</p>

<p><a href="/rouilj/tag:RoundupTracker" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">RoundupTracker</span></a> <a href="/rouilj/tag:DevDotTo" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">DevDotTo</span></a> <a href="/rouilj/tag:WebDev" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">WebDev</span></a></p>
]]></content:encoded>
      <guid>https://blog.rouilj.dynamic-dns.net/rouilj/i-am-a-developer-on-the-roundup-issue-tracker</guid>
      <pubDate>Mon, 28 Nov 2022 17:01:41 +0000</pubDate>
    </item>
    <item>
      <title>My Journey to Open-Source Development</title>
      <link>https://blog.rouilj.dynamic-dns.net/rouilj/my-journey-to-open-source-development</link>
      <description>&lt;![CDATA[&lt;!--&#xA;comment: front matter from dev.to&#xA;published: true&#xA;description: How I started as a developer of the Roundup Issue Tracker.&#xA;canonicalurl: &#xA;coverimage: https://www.roundup-tracker.org/images/indexloggedout.png?ref=dev.to1&#xA;tags: #python, #webdev, #opensource &#xA;series: Roundup Tracker - notes from an open-source developer&#xA;--  Welcome to the first article on the ongoing development of the Roundup Issue Tracker. Before we get started, a little about me.&#xA;&#xA;I have a B.S. in Physics which included some advanced computer science courses. But, my career ended up in system administration. I have taught and published a few peer-reviewed papers at conferences over the years. Trying to reduce toil and improve operations, lead to my interest in ticketing systems. Tools from LEAN and Six Sigma have proven valuable in analyzing ticketing and real-time logging data.&#xA;&#xA;Issues Need Tracking&#xA;In 2002, I ran a lab that installed a system monitoring solution on Sun computers. We needed to track requests that came to the lab. I evaluated ticketing systems across a few dimensions. The Roundup Issue Tracker scored the highest.&#xA;&#xA;Five minutes after downloading the software, I had a classic tracker running in demo mode. After entering a few tickets and adding a few keywords (tags), my co-worker and I decided it could work. It was flexible and looked like we could extend it to support our future growth. Little did I know that this was the start of a 20+ year relationship.&#xA;!--more--&#xA;Roundup Basics&#xA;Roundup tracker has two components: the Roundup core and the tracker instance. The core is written in Python. The tracker instance supports one of two HTML templating engines: (Zope Page Templates or Jinja2). It is programmed in Python.  The tracker instance controls the look/feel/functionality of the tracker. It:&#xA;&#xA;   defines the database schema/data model,&#xA;   provides the web interface (with customizable HTML and CSS),&#xA;   executes Python code to validate entered data and react to a change (e.g. by emailing notification messages),&#xA;   can hook into the core code to control how the core operates.&#xA;&#xA;One instance of the Roundup core can run many trackers. Each tracker can have a very different presentation and function.&#xA;&#xA;Getting My Feet Wet&#xA;When I started, Roundup was still very new. I started developing my customized tracker to learn Python and experiment with Roundup. My tracker implemented several ideas from Request Tracker (RT). I developed the key components, replies/comments, dependencies, and grouping (parent/child), in less than 40 hours of effort over a few weeks. Roundup&#39;s implementation and documentation made building these significant features easy.&#xA;&#xA;The other advantage was that Richard Jones, the developer, was in Australia. We set up a &#34;follow the sun&#34; development cycle. I emailed him a problem when I went to bed. By the time I got up the next morning, he would have a fix ready for me to use when I got home after work. This fast feedback is something I try to maintain in the Roundup community.&#xA;&#xA;I never deployed my custom tracker, but the classic tracker worked well for the lab. As a result, the sales engineering group requested (and got) a tracker. After I left the company in 2005, they upgraded the Roundup core and converted the database to MySQL (from a dbm key/value store).&#xA;&#xA;Back in a More Active Role&#xA;My next IT job used Request Tracker. I followed Roundup development but didn&#39;t contribute much. In 2012, there was some interest at work in setting up a customer-facing portal based on Roundup. I started development on my tracker again and re-joined the Roundup community. Over the next few years, I developed code to add:&#xA;&#xA;  a transaction source property for data changes. Makes changes from the web interface but prevents the same change if it originated from (unsigned) email.&#xA;  the filter command to the roundup-admin command line interface makes data searching easier.&#xA;  nofollow relationships to links which makes Roundup less useful to spammers.&#xA;  CSRF protection using synchronizer tokens makes Roundup more secure.&#xA;&#xA;Releasing Roundup&#xA;By 2018, Richard had stepped back, the last release was January 2016. I took over as a lead developer and release engineer. I released version 1.6.0 in July of 2018. This merged several existing patches, cleaning up Roundup&#39;s issue tracker.&#xA;&#xA;Since 2018, there were two major changes added by other developers:&#xA;&#xA;   support for both Python 2 and Python 3 (which required a 2.0.0 release)&#xA;   addition of a REST interface beside the XML-RPC interface&#xA;&#xA;On July 13th, 2022, I made my fifth annual release.&#xA;&#xA;Since Roundup deployments are on-prem, I established a yearly release cadence. Other issue trackers have more frequent releases. But, many of those releases address UI or workflow issues. In Roundup these are implemented in the (customized) tracker instance. So the user is not dependent on the fixes pushed from upstream. Each release includes upgrading instructions. These instructions can include changes for the tracker instance to bring it up to date.&#xA;&#xA;The slower release cadence has an advantage for the users. It reduces the need to update tracker instances to once a year rather than every few months. However, the yearly cadence results in less public visibility for Roundup.  &#xA;&#xA;I plan more articles on developing and maintaining an open-source application. Some future topics I am considering include (smallin no particular order/small):&#xA;&#xA;   The Customisability Trap&#xA;   Kitchen Sink Development&#xA;   Accessibility&#xA;   Responsive Design&#xA;   The Problem with Selects&#xA;   What?? No Javascript!&#xA;   Docker All the Things&#xA;   Colors and Themes&#xA;   The Bus Factor&#xA;   Evaluating Tracking Software&#xA;&#xA;The Roundup 20supth/sup anniversary article on lwn.net includes more information on Roundup.&#xA;&#xA;I hope you enjoyed this article. Thanks for reading.&#xA;&#xA;tags: #Python, #WebDev, #opensource #RoundupTracker #seriesRoundupTrackerNotesFromAnOpenSourceDeveloper&#xA;DevDotTo&#xA;]]&gt;</description>
      <content:encoded><![CDATA[

<p>Welcome to the first article on the ongoing development of the <a href="https://www.roundup-tracker.org?ref=dev.to_1" rel="nofollow">Roundup Issue Tracker</a>. Before we get started, a little about me.</p>

<p>I have a B.S. in Physics which included some advanced computer science courses. But, my career ended up in system administration. I have <a href="https://www.usenix.org/legacy/events/lisa09/training/tutonefile.html#s2" rel="nofollow">taught</a> and published a <a href="https://www.usenix.org/legacy/events/lisa04/tech/full_papers/rouillard/rouillard_html/index.html" rel="nofollow">few</a> peer-reviewed <a href="https://static.usenix.org/publications/library/proceedings/lisa94/rouillard.html" rel="nofollow">papers</a> at conferences over the years. Trying to reduce toil and improve operations, lead to my interest in ticketing systems. Tools from LEAN and Six Sigma have proven valuable in analyzing ticketing and real-time logging data.</p>

<h2 id="issues-need-tracking" id="issues-need-tracking">Issues Need Tracking</h2>

<p>In 2002, I ran a lab that installed a system monitoring solution on Sun computers. We needed to track requests that came to the lab. I evaluated ticketing systems across a few dimensions. The <a href="https://www.roundup-tracker.org?ref=dev.to_1" rel="nofollow">Roundup Issue Tracker</a> scored the highest.</p>

<p>Five minutes after downloading the software, I had a classic tracker running in demo mode. After entering a few tickets and adding a few keywords (tags), my co-worker and I decided it could work. It was flexible and looked like we could extend it to support our future growth. Little did I know that this was the start of a 20+ year relationship.
</p>

<h2 id="roundup-basics" id="roundup-basics">Roundup Basics</h2>

<p>Roundup tracker has two components: the Roundup core and the tracker instance. The core is written in Python. The tracker instance supports one of two HTML templating engines: (<a href="https://pagetemplates.readthedocs.io/en/latest/introduction.html" rel="nofollow">Zope Page Templates</a> or <a href="https://palletsprojects.com/p/jinja/" rel="nofollow">Jinja2</a>). It is programmed in Python.  The tracker instance controls the look/feel/functionality of the tracker. It:</p>
<ul><li>defines the database schema/data model,</li>
<li>provides the web interface (with customizable HTML and CSS),</li>
<li>executes Python code to <a href="https://www.roundup-tracker.org/docs/customizing.html?ref=dev.to_1#detectors-adding-behaviour-to-your-tracker" rel="nofollow">validate entered data and react</a> to a change (e.g. by emailing notification messages),</li>
<li>can <a href="https://www.roundup-tracker.org/docs/customizing.html?ref=dev.to_1#interfaces-py-hooking-into-the-core-of-roundup" rel="nofollow">hook into the core code</a> to control how the core operates.</li></ul>

<p>One instance of the Roundup core can run many trackers. Each tracker can have a very different presentation and function.</p>

<h2 id="getting-my-feet-wet" id="getting-my-feet-wet">Getting My Feet Wet</h2>

<p>When I started, Roundup was still very new. I started developing my customized tracker to learn Python and experiment with Roundup. My tracker implemented several ideas from <a href="https://bestpractical.com/request-tracker/" rel="nofollow">Request Tracker (RT)</a>. I developed the key components, <a href="https://rt-wiki.bestpractical.com/wiki/ManualUsingWebInterface#Replying_and_commenting" rel="nofollow">replies/comments</a>, <a href="https://rt-wiki.bestpractical.com/wiki/ManualUsingWebInterface#Links" rel="nofollow">dependencies, and grouping (parent/child)</a>, in less than 40 hours of effort over a few weeks. Roundup&#39;s implementation and documentation made building these significant features easy.</p>

<p>The other advantage was that Richard Jones, the developer, was in Australia. We set up a “follow the sun” development cycle. I emailed him a problem when I went to bed. By the time I got up the next morning, he would have a fix ready for me to use when I got home after work. This fast feedback is something I try to maintain in the Roundup community.</p>

<p>I never deployed my custom tracker, but the classic tracker worked well for the lab. As a result, the sales engineering group requested (and got) a tracker. After I left the company in 2005, they upgraded the Roundup core and converted the database to MySQL (from a dbm key/value store).</p>

<h2 id="back-in-a-more-active-role" id="back-in-a-more-active-role">Back in a More Active Role</h2>

<p>My next IT job used Request Tracker. I followed Roundup development but didn&#39;t contribute much. In 2012, there was some interest at work in setting up a customer-facing portal based on Roundup. I started development on my tracker again and re-joined the Roundup community. Over the next few years, I developed code to add:</p>
<ul><li>a transaction source property for data changes. Makes changes from the web interface but prevents the same change if it originated from (unsigned) email.</li>
<li>the filter command to the <code>roundup-admin</code> command line interface makes data searching easier.</li>
<li><code>nofollow</code> relationships to links which makes Roundup less useful to spammers.</li>
<li><a href="https://owasp.org/www-community/attacks/csrf" rel="nofollow">CSRF</a> protection using <a href="https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html#synchronizer-token-pattern" rel="nofollow">synchronizer tokens</a> makes Roundup more secure.</li></ul>

<h2 id="releasing-roundup" id="releasing-roundup">Releasing Roundup</h2>

<p>By 2018, Richard had stepped back, the last release was January 2016. I took over as a lead developer and release engineer. I released version 1.6.0 in July of 2018. This merged several existing patches, cleaning up <a href="https://issues.roundup-tracker.org?ref=dev.to_1" rel="nofollow">Roundup&#39;s issue tracker</a>.</p>

<p>Since 2018, there were two major changes added by other developers:</p>
<ul><li>support for both Python 2 and Python 3 (which required a 2.0.0 release)</li>
<li>addition of a <a href="https://www.roundup-tracker.org/docs/rest.html?ref=dev.to_1" rel="nofollow">REST interface</a> beside the <a href="https://www.roundup-tracker.org/docs/xmlrpc.html?ref=dev.to_1" rel="nofollow">XML-RPC</a> interface</li></ul>

<p>On July 13th, 2022, I made my <a href="https://sourceforge.net/p/roundup/mailman/roundup-users/thread/20210713043457.083CE6A0022%40pe15.cs.umb.edu/" rel="nofollow">fifth annual release</a>.</p>

<p>Since Roundup deployments are on-prem, I established a yearly release cadence. Other issue trackers have more frequent releases. But, many of those releases address UI or workflow issues. In Roundup these are implemented in the (customized) tracker instance. So the user is not dependent on the fixes pushed from upstream. Each release includes <a href="https://www.roundup-tracker.org/docs/upgrading.html?ref=dev.to_1" rel="nofollow">upgrading instructions</a>. These instructions can include changes for the tracker instance to bring it up to date.</p>

<p>The slower release cadence has an advantage for the users. It reduces the need to update tracker instances to once a year rather than every few months. However, the yearly cadence results in less public visibility for Roundup.</p>

<p>I plan more articles on developing and maintaining an open-source application. Some future topics I am considering include (<small>in no particular order</small>):</p>
<ul><li>The Customisability Trap</li>
<li>Kitchen Sink Development</li>
<li>Accessibility</li>
<li>Responsive Design</li>
<li>The Problem with Selects</li>
<li>What?? No Javascript!</li>
<li>Docker All the Things</li>
<li>Colors and Themes</li>
<li>The Bus Factor</li>
<li>Evaluating Tracking Software</li></ul>

<p>The <a href="https://lwn.net/Articles/869118/" rel="nofollow">Roundup 20<sup>th</sup> anniversary article on lwn.net</a> includes more information on Roundup.</p>

<p>I hope you enjoyed this article. Thanks for reading.</p>

<p>tags: <a href="/rouilj/tag:Python" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">Python</span></a>, <a href="/rouilj/tag:WebDev" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">WebDev</span></a>, <a href="/rouilj/tag:opensource" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">opensource</span></a> <a href="/rouilj/tag:RoundupTracker" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">RoundupTracker</span></a> <a href="/rouilj/tag:seriesRoundupTrackerNotesFromAnOpenSourceDeveloper" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">seriesRoundupTrackerNotesFromAnOpenSourceDeveloper</span></a>
<a href="/rouilj/tag:DevDotTo" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">DevDotTo</span></a></p>
]]></content:encoded>
      <guid>https://blog.rouilj.dynamic-dns.net/rouilj/my-journey-to-open-source-development</guid>
      <pubDate>Fri, 29 Jul 2022 05:49:19 +0000</pubDate>
    </item>
  </channel>
</rss>