<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>DevTo &amp;mdash; Roundup Issue Tracker</title>
    <link>https://blog.rouilj.dynamic-dns.net/rouilj/tag:DevTo</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:26:07 -0400</pubDate>
    <item>
      <title>How does your customer help you?</title>
      <link>https://blog.rouilj.dynamic-dns.net/rouilj/how-does-your-customer-help-you</link>
      <description>&lt;![CDATA[Many companies are cutting back and delaying new projects after the recent election. As a new solution provider, how do you create a win-win situation for customers who might worry about:&#xA;&#xA;  quality of the solution&#xA;  satisfaction with the result&#xA;  cost&#xA;&#xA;How can you add value for the customer by reducing risk or cost?&#xA;&#xA;I propose 4 types of customers:&#xA;&#xA;  Regular customer: straight work for hire.&#xA;  Reference customer: provides a private reference for my work to a potential customer.&#xA;  Sponsor customer: the company is publically promoted as a user of the product.&#xA;  Partner customer: actively promotes the product within their market.&#xA;&#xA;Regular customer&#xA;&#xA;The customer receives a proposal for their project. Engages in discussions regarding the scope of work, pricing, and other details. Once the contract concludes, there is no continued relationship with the client.&#xA;&#xA;!--more--&#xA;&#xA;Reference Customer&#xA;&#xA;A reference customer starts as a regular customer. As part of the contract, they agree to talk with potential clients to discuss:&#xA;&#xA;  the project scope,&#xA;  the benefits of the solution,&#xA;  and the overall business impact of the work performed.&#xA;&#xA;Contracts are protected by a non-disclosure agreement to safeguard the confidentiality of references. This allows prospective clients to better understand the value proposition. But it prohibits them from sharing any proposal-related information outside their organization.&#xA;&#xA;In exchange for serving as a reference, these customers receive discounted rates on their contracts.&#xA;&#xA;Sponsor Customer&#xA;&#xA;A sponsor customer is a reference customer who has consented to publicly associate their company name with the solution/product. This association includes prominent visibility on websites, emails, and various marketing materials.&#xA;&#xA;Sponsor customers receive a greater discount on their contracts compared to reference customers. They may enjoy other benefits, such as enhanced support agreements.&#xA;&#xA;Partner Customer&#xA;&#xA;A partner customer can be in any of the other customer categories. Unlike the other customers, who take a passive approach to supporting the product, these customers actively advocate for the product.&#xA;&#xA;They share their positive experiences with others in the same industry. This strategy is particularly effective when the product offered is not a primary focus or distinguishing factor for the customer&#39;s business.&#xA;&#xA;Rather than receiving discounts on contracts, these customers benefit from additional revenue when a client they refer decides to purchase the solution.&#xA;&#xA;Conclusion&#xA;&#xA;Offering customers an ongoing role in the success of your business can reduce their concerns about your solution. This win-win approach to negotiating contracts can also reach new clients and help answer the concerns of new clients.&#xA;&#xA;What are your thoughts about these customer categories?&#xA;What tools/techniques do you use to help customers get&#xA;to yes?&#xA;&#xA;(A good reference on negotiating is https://www.amazon.com/Getting-Yes-Negotiating-Agreement-Without/dp/0143118757.)&#xA;&#xA;#opensource, #Business,  #DevTo]]&gt;</description>
      <content:encoded><![CDATA[<p>Many companies are cutting back and delaying new projects after the recent election. As a new solution provider, how do you create a win-win situation for customers who might worry about:</p>
<ul><li>quality of the solution</li>
<li>satisfaction with the result</li>
<li>cost</li></ul>

<p>How can you add value for the customer by reducing risk or cost?</p>

<p>I propose 4 types of customers:</p>
<ol><li>Regular customer: straight work for hire.</li>
<li>Reference customer: provides a private reference for my work to a potential customer.</li>
<li>Sponsor customer: the company is publically promoted as a user of the product.</li>
<li>Partner customer: actively promotes the product within their market.</li></ol>

<h2 id="regular-customer" id="regular-customer">Regular customer</h2>

<p>The customer receives a proposal for their project. Engages in discussions regarding the scope of work, pricing, and other details. Once the contract concludes, there is no continued relationship with the client.</p>



<h2 id="reference-customer" id="reference-customer">Reference Customer</h2>

<p>A reference customer starts as a regular customer. As part of the contract, they agree to talk with potential clients to discuss:</p>
<ul><li>the project scope,</li>
<li>the benefits of the solution,</li>
<li>and the overall business impact of the work performed.</li></ul>

<p>Contracts are protected by a non-disclosure agreement to safeguard the confidentiality of references. This allows prospective clients to better understand the value proposition. But it prohibits them from sharing any proposal-related information outside their organization.</p>

<p>In exchange for serving as a reference, these customers receive discounted rates on their contracts.</p>

<h2 id="sponsor-customer" id="sponsor-customer">Sponsor Customer</h2>

<p>A sponsor customer is a reference customer who has consented to publicly associate their company name with the solution/product. This association includes prominent visibility on websites, emails, and various marketing materials.</p>

<p>Sponsor customers receive a greater discount on their contracts compared to reference customers. They may enjoy other benefits, such as enhanced support agreements.</p>

<h2 id="partner-customer" id="partner-customer">Partner Customer</h2>

<p>A partner customer can be in any of the other customer categories. Unlike the other customers, who take a passive approach to supporting the product, these customers actively advocate for the product.</p>

<p>They share their positive experiences with others in the same industry. This strategy is particularly effective when the product offered is not a primary focus or distinguishing factor for the customer&#39;s business.</p>

<p>Rather than receiving discounts on contracts, these customers benefit from additional revenue when a client they refer decides to purchase the solution.</p>

<h2 id="conclusion" id="conclusion">Conclusion</h2>

<p>Offering customers an ongoing role in the success of your business can reduce their concerns about your solution. This win-win approach to negotiating contracts can also reach new clients and help answer the concerns of new clients.</p>

<p>What are your thoughts about these customer categories?
What tools/techniques do you use to help customers get
to yes?</p>

<p>(A good reference on negotiating is <a href="https://www.amazon.com/Getting-Yes-Negotiating-Agreement-Without/dp/0143118757." rel="nofollow">https://www.amazon.com/Getting-Yes-Negotiating-Agreement-Without/dp/0143118757.</a>)</p>

<p><a href="/rouilj/tag:opensource" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">opensource</span></a>, <a href="/rouilj/tag:Business" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">Business</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/how-does-your-customer-help-you</guid>
      <pubDate>Thu, 14 Nov 2024 01:28:38 +0000</pubDate>
    </item>
    <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>
  </channel>
</rss>