Jekyll2021-06-25T16:07:25+01:00https://digital-comma.com/feed.xmlDigital CommaWhere 'digital' meets 'communications'Gary ConroyWriting human-readable JavaScript for APIs2021-06-03T00:00:00+01:002021-06-03T00:00:00+01:00https://digital-comma.com/javascript/writing-human-readable-javascript-for-apis<h2 id="use-urlsearchparams-to-ensure-your-code-is-readable">Use UrlSearchParams to ensure your code is readable</h2>
<p>With JavaScript’s <a href="https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams">UrlSearchParams</a> function, you can compose API calls that would be recognisable as a set of <a href="https://www.postman.com/">Postman</a> key/value parameters. For example:</p>
<h3 id="in-postman">In Postman</h3>
<p><img src="../assets/images/postman.png" alt="In Postman" /></p>
<h3 id="in-javascript">In JavaScript</h3>
<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kd">const</span> <span class="nx">query</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">URLSearchParams</span><span class="p">({</span>
<span class="na">client_id</span><span class="p">:</span> <span class="dl">"</span><span class="s2">SOMETHINGNOTVERYSECRET</span><span class="dl">"</span><span class="p">,</span>
<span class="na">client_secret</span><span class="p">:</span> <span class="dl">"</span><span class="s2">ASECRETTHING</span><span class="dl">"</span><span class="p">,</span>
<span class="na">v</span><span class="p">:</span> <span class="dl">"</span><span class="s2">20210201</span><span class="dl">"</span><span class="p">,</span> <span class="c1">// All Foursquare API calls need a version. See https://developer.foursquare.com/docs/places-api/versioning/</span>
<span class="na">ll</span><span class="p">:</span> <span class="nx">position</span><span class="p">.</span><span class="nx">coords</span><span class="p">.</span><span class="nx">latitude</span> <span class="o">+</span> <span class="dl">"</span><span class="s2">,</span><span class="dl">"</span> <span class="o">+</span> <span class="nx">position</span><span class="p">.</span><span class="nx">coords</span><span class="p">.</span><span class="nx">longitude</span><span class="p">,</span> <span class="c1">// User's latitude and longitude</span>
<span class="na">intent</span><span class="p">:</span> <span class="dl">"</span><span class="s2">browse</span><span class="dl">"</span><span class="p">,</span> <span class="c1">// Find all venues with the defined 'radius' of 'll'</span>
<span class="na">radius</span><span class="p">:</span> <span class="dl">"</span><span class="s2">10000</span><span class="dl">"</span><span class="p">,</span> <span class="c1">// Distance from starting point to search (metres)</span>
<span class="na">limit</span><span class="p">:</span> <span class="dl">"</span><span class="s2">10</span><span class="dl">"</span><span class="p">,</span> <span class="c1">// Maximum number of values to return (up to 50)</span>
<span class="na">categoryId</span><span class="p">:</span> <span class="dl">"</span><span class="s2">4d4b7105d754a06374d81259</span><span class="dl">"</span> <span class="c1">// Search the 'Food' category. See the list of categories at https://developer.foursquare.com/docs/build-with-foursquare/categories/</span>
<span class="p">});</span>
<span class="nx">fetch</span><span class="p">(</span><span class="nx">API_URL</span> <span class="o">+</span> <span class="nx">ENDPOINT</span> <span class="o">+</span> <span class="nx">query</span><span class="p">,</span> <span class="nx">getOptions</span><span class="p">);</span></code></pre></figure>
<h3 id="something-similar-in-an-sdk-google-places-library">Something similar in an SDK (Google Places Library)</h3>
<p>Often, a system’s <a href="https://en.wikipedia.org/wiki/Software_development_kit">SDK</a>/API library is not as intuitive or broadly understood as a standard like <a href="https://en.wikipedia.org/wiki/Open_API">Open API</a>. With SDKs, you rely a little more on the developer documentation being easy to follow and consistently constructed.</p>
<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="c1">// Find a food venue</span>
<span class="kd">function</span> <span class="nx">findRestaurant</span><span class="p">(</span><span class="nx">location</span><span class="p">,</span> <span class="nx">name</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">let</span> <span class="nx">restaurant_location</span> <span class="o">=</span> <span class="k">new</span> <span class="nb">Object</span> <span class="p">();</span>
<span class="nx">restaurant_location</span><span class="p">.</span><span class="nx">lat</span> <span class="o">=</span> <span class="nx">location</span><span class="p">.</span><span class="nx">lat</span><span class="p">;</span>
<span class="nx">restaurant_location</span><span class="p">.</span><span class="nx">lng</span> <span class="o">=</span><span class="nx">location</span><span class="p">.</span><span class="nx">lng</span><span class="p">;</span>
<span class="kd">var</span> <span class="nx">request</span> <span class="o">=</span> <span class="p">{</span>
<span class="na">location</span><span class="p">:</span> <span class="nx">restaurant_location</span><span class="p">,</span>
<span class="na">radius</span><span class="p">:</span> <span class="dl">'</span><span class="s1">500</span><span class="dl">'</span><span class="p">,</span>
<span class="na">name</span><span class="p">:</span> <span class="nx">name</span>
<span class="p">};</span>
<span class="c1">// Add food venue marker to Google map</span>
<span class="nx">service</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">google</span><span class="p">.</span><span class="nx">maps</span><span class="p">.</span><span class="nx">places</span><span class="p">.</span><span class="nx">PlacesService</span><span class="p">(</span><span class="nx">map</span><span class="p">);</span>
<span class="nx">service</span><span class="p">.</span><span class="nx">nearbySearch</span><span class="p">(</span><span class="nx">request</span><span class="p">,</span> <span class="nx">addMarker</span><span class="p">);</span>
<span class="p">}</span></code></pre></figure>
<h2 id="sdk-versus-rest-api">SDK versus REST API</h2>
<p>The advantages of using a system’s provided SDK instead of a REST API (whether that be Open API, a Postman collection, or some other standard) are that it will:</p>
<ul>
<li>Have no deployment issues (such as CORS)</li>
<li>Be faster</li>
<li>Be more conservative with its use of resources</li>
<li>Have more commands</li>
<li>Be free of any constraints imposed by a standard API specification.</li>
</ul>
<p>However, if there is no SDK for your preferred development platform, a REST API may be your only option. For REST APIs, there’s also a significant advantage in the degree of clarity that comes from a standardised approach.</p>
<h2 id="the-two-apis-combined-popular-places-for-food-in-your-area-from-foursquare-on-a-google-map">The two APIs combined: “Popular places for food in your area (from Foursquare, on a Google map)”</h2>Gary ConroyURLSearchParams allows you to compose easy-to-understand API callsOpen API 3 documentation tools for OSS projects2021-03-04T00:00:00+00:002021-03-04T00:00:00+00:00https://digital-comma.com/open%20api/open-api-3-documentation-tools-oss-projects<p>There are a few excellent options to choose from, each with individual strengths and features. Whatever your budget, I’d say that there’s no such thing as a perfect OAS3 (Open API 3 specification) documentation tool. You need to decide what’s most important to you and accept the compromises you must make.</p>
<p class="notice--info">Note that I’m only considering Open API <strong>3</strong> tools here so will not cover Open API 2 tools like <em>Dapperdox</em> and <em>Apiary</em>.</p>
<h1 id="whats-free">What’s free?</h1>
<table>
<thead>
<tr>
<th>Tool</th>
<th>OAS editor</th>
<th>Try it out</th>
<th>Layout</th>
<th>Self-host or custom domain?</th>
<th>Automated code outputs</th>
<th>Expandable schemas?</th>
<th>Git integration</th>
<th>Collaborative?</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://readme.com/">ReadMe</a></td>
<td>No</td>
<td>Yes</td>
<td>Three-column</td>
<td>Neither</td>
<td>5</td>
<td>No</td>
<td>OAS, markdown and digital file synchronisation</td>
<td>Up to 5 people</td>
</tr>
<tr>
<td><a href="https://github.com/Redocly/redoc">Redoc</a></td>
<td>No</td>
<td>No</td>
<td>Three-column</td>
<td>Self-host</td>
<td>0</td>
<td>Yes</td>
<td>No</td>
<td>No</td>
</tr>
<tr>
<td><a href="https://speca.io/">Speca</a></td>
<td>Yes</td>
<td>No</td>
<td>Three-column</td>
<td>Custom domain (not on the free tier)</td>
<td>0</td>
<td>Yes</td>
<td>OAS synchronisation</td>
<td>Not on the free tier</td>
</tr>
<tr>
<td><a href="https://swagger.io/tools/swagger-ui/">Swagger UI</a></td>
<td>Yes</td>
<td>Yes</td>
<td>One column, expandable</td>
<td>Self-host</td>
<td>1</td>
<td>Yes</td>
<td>No</td>
<td>No</td>
</tr>
<tr>
<td><a href="https://app.swaggerhub.com/">SwaggerHub</a></td>
<td>Yes</td>
<td>Yes</td>
<td>One column, expandable</td>
<td>Either (not on the free tier)</td>
<td>1</td>
<td>Yes</td>
<td>OAS synchronisation</td>
<td>Not on the free tier</td>
</tr>
</tbody>
</table>
<h2 id="key">Key</h2>
<ul>
<li><strong>OAS editor</strong> - includes its own Open API editing tool</li>
<li><strong>Try it out</strong> - allows users to test API calls within the tool?</li>
<li><strong>Layout</strong> - does this adopt the <a href="https://stripe.com/docs/api">Stripe API style</a> three-column layout or the <a href="https://petstore.swagger.io/">Swagger style</a> one column expandable layout</li>
<li><strong>Self-host or custom domain?</strong> - can you deploy this on your servers (<em>Self-host</em>) or point the hosted service to your domain (<em>Custom domain</em>)</li>
<li><strong>Automated code outputs</strong> - how many types of output can the tool generate for API requests. Note that this does not include manually-generated code injected into the Open API specification (such as Redoc’s, admittedly excellent, <em>x-codeSamples</em> extension)</li>
<li><strong>Expandable schemas</strong> - JSON structures can get complicated. Does the tool allow you to collapse and expand the schemas so you can see as much detail as you wish?</li>
<li><strong>Git integration</strong> - what is the nature of any integration with Git repositories</li>
<li><strong>Collaborative?</strong> - does this have features to allow more than one person to work on the same specification concurrently?</li>
</ul>
<h1 id="what-if-you-are-willing-to-splash-the-cash">What if you are willing to ‘splash the cash’</h1>
<p>The commercial realm often offers far more than the free options: code generation, extended documentation tools, integration with Git repositories, version control, advanced Open API editing, multi-user collaboration, mocking servers and so on. The standouts for me being:</p>
<ul>
<li><strong><a href="https://stoplight.io/welcome">Stoplight</a></strong> - for its unparalleled Open API editor and intelligent integration with Git repositories</li>
<li><strong><a href="https://app.swaggerhub.com/">SwaggerHub</a></strong> - for its integrated tooling (SmartBear/SwaggerHub is Open API central, after all)</li>
<li><strong><a href="https://readme.com/">ReadMe</a></strong> - for its complete, consistent and attractive documentation suite.</li>
</ul>
<table>
<thead>
<tr>
<th>Tool</th>
<th>OAS editor</th>
<th>Try it out</th>
<th>Layout</th>
<th>Cost (from)</th>
<th>Self-host or custom domain?</th>
<th>Automated code outputs</th>
<th>Expandable schemas?</th>
<th>Git integration</th>
<th>Collaborative</th>
<th>Other documentation tools?</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://www.apimatic.io/">Apimatic</a></td>
<td>Yes</td>
<td>Yes</td>
<td>Three-column</td>
<td>$9.90/month</td>
<td>Either (not at $9.90 tier)</td>
<td>11</td>
<td>Yes</td>
<td>Can generate SDKs</td>
<td>Yes</td>
<td>Comprehensive</td>
</tr>
<tr>
<td><a href="https://developerhub.io/">DeveloperHub.io</a></td>
<td>No</td>
<td>No</td>
<td>Three-column</td>
<td>$39/month</td>
<td>Custom domain</td>
<td>6</td>
<td>Yes</td>
<td>No</td>
<td>Yes</td>
<td>Comprehensive</td>
</tr>
<tr>
<td><a href="https://readme.com/">ReadMe</a></td>
<td>No</td>
<td>Yes</td>
<td>Three-column</td>
<td>Free (for OSS)</td>
<td>Custom domain (not on the free tier)</td>
<td>5</td>
<td>No</td>
<td>OAS, markdown and digital file synchonisation</td>
<td>Yes</td>
<td>Comprehensive</td>
</tr>
<tr>
<td><a href="https://redoc.ly/">Redocly</a></td>
<td>No</td>
<td>Yes</td>
<td>Three-column</td>
<td>$69/month</td>
<td>Custom domain (not at $69 tier)</td>
<td>0</td>
<td>Yes</td>
<td>OAS, markdown and digital file synchronisation</td>
<td>Yes</td>
<td>Some</td>
</tr>
<tr>
<td><a href="https://speca.io/">Speca</a></td>
<td>Yes</td>
<td>No</td>
<td>Three-column</td>
<td>Free</td>
<td>Custom domain (not on the free tier)</td>
<td>0</td>
<td>Yes</td>
<td>OAS synchronisation</td>
<td>Not on free tier</td>
<td>None</td>
</tr>
<tr>
<td><a href="https://stoplight.io/">Stoplight</a></td>
<td>Yes</td>
<td>Yes</td>
<td>Three-column</td>
<td>$99/month</td>
<td>Custom domain</td>
<td>33</td>
<td>Yes</td>
<td>OAS, markdown and digital file synchonisation</td>
<td>Yes</td>
<td>Some</td>
</tr>
<tr>
<td><a href="https://app.swaggerhub.com/">SwaggerHub</a></td>
<td>Yes</td>
<td>Yes</td>
<td>One column, expandable</td>
<td>Free</td>
<td>Either (not on the free tier)</td>
<td>1</td>
<td>Yes</td>
<td>OAS synchronisation</td>
<td>Not on the free tier</td>
<td>None</td>
</tr>
</tbody>
</table>
<h2 id="key-1">Key</h2>
<p>As above, plus:</p>
<ul>
<li><strong>Cost (from)</strong> - this is the cost for at least one <em>published</em> API. Quite a few of these commercial tools will let you develop an Open API specification for free but you need to pay if you want to allow public access to it</li>
<li><strong>Other documentation tools</strong> - this article is specifically looking at these systems as documentation tools. Outside of the Open API specification does the system provide other documentation tools?</li>
</ul>Gary ConroyThere are a plethora of systems that you can use but many carry a cost and, for open-source software, that's often a deal-breakerMigrating from Dropbox to Microsoft Teams and OneDrive2021-01-06T00:00:00+00:002021-01-06T00:00:00+00:00https://digital-comma.com/microsoft%20365/migrating-dropbox-microsoft-teams<h1 id="the-scenario">The scenario</h1>
<p>A team is using a single DropBox account for document sharing and collaboration even though their organisation has Microsoft 365. They are aware that they really should be using Microsoft 365 systems for this but the thought of moving all those files for all those users is daunting.</p>
<h2 id="the-solution-mover">The solution? Mover</h2>
<p>There are many ways to skin this ‘cat’ and if your file movement needs are small, this is overkill but if you have many users and/or many files,</p>
<p>This example procedure uses <strong>Mover</strong> to do the heavy lifting between DropBox and Teams/OneDrive. However, Mover can be configured to use a variety of sources and destinations:</p>
<ul>
<li><strong>Sources:</strong> Amazon S3, Azure Blob, Box, Dropbox, Egnyte, Google Suite/Drive, SharePoint, OneDrive</li>
<li><strong>Destinations:</strong> Azure Blob, Microsoft 365 (OneDrive/SharePoint/Teams), OneDrive (business and consumer).</li>
</ul>
<p><img src="https://digital-comma.com/assets/images/dropbox-migrate.png" alt=""Overview of migration"" /></p>
<h1 id="preparation">Preparation</h1>
<h2 id="dropbox-preparation">Dropbox preparation</h2>
<p class="notice--info"><strong>What’s happening here?</strong> You are ‘tidying up’ your Dropbox structure to make sure your content is in the best possible shape before migrating it.</p>
<ul>
<li>Make sure the personal files for each user are in a specific folder for that user</li>
<li>Put all the generic files in folders: create these folders for files that all users can access (for instance, you might have one called <em>Resources</em> and another called <em>Projects</em>)</li>
</ul>
<h2 id="microsoft-365-teams-preparation">Microsoft 365 Teams preparation</h2>
<p class="notice--info"><strong>What’s happening here?</strong> You are making sure that all your users have access to a specific Microsoft 365 team to replace their old Dropbox system.</p>
<ol>
<li>Create a new team (or re-use an existing one)</li>
<li>Add all the people who used to access the Dropbox system as members of this team</li>
<li>Create or choose a channel (such as <em>General</em>) to host your migrated Dropbox files, click <em>Files</em> and then create generic folders that match those created on Dropbox (for instance, <em>Resources</em> and <em>Projects</em>)</li>
</ol>
<p class="notice--primary">If there are Dropbox files that general users don’t normally see, replicate this in Teams by creating a <a href="https://docs.microsoft.com/en-us/microsoftteams/private-channels">private channel</a> for such content (but only give channel access to team owners and other administrative people).</p>
<h2 id="prepare-a-migration-spreadsheet-for-the-users-onedrive-accounts">Prepare a migration spreadsheet for the user’s OneDrive accounts</h2>
<p class="notice--info"><strong>What’s happening here?</strong> You are linking each person’s personal Dropbox files to their Microsoft 365 account so that you can subsequently copy these files across to their individual OneDrive.</p>
<ol>
<li>Create an Excel spreadsheet.</li>
<li>In the first row, first column, write <strong>Source Path</strong></li>
<li>In the first row, second column, write <strong>Destination Path</strong></li>
<li>In the <strong>second row, first column</strong>, type a <strong>/</strong> character immediately followed by the name of the Dropbox folder you are moving content from. For example, <em>/Adele Vance</em></li>
<li>In the <strong>second row, second column</strong>, type the user’s Microsoft 365 email address. For example, <code class="language-plaintext highlighter-rouge">AdeleV@digitalcomma.onmicrosoft.com</code></li>
<li><strong>Repeat steps 4 and 5</strong> for each user (adding their details to subsequent rows)</li>
<li><strong>Save the spreadsheet in CSV format</strong>. Call it something like <em>migration.csv</em></li>
</ol>
<h2 id="add-generic-files-to-migration-spreadsheet">Add generic files to migration spreadsheet</h2>
<p class="notice--info"><strong>What’s happening here?</strong> Using the same migration spreadsheet, you are adding the location of generic files for subsequent transfer to Microsoft 365 Teams.</p>
<ol>
<li>Open <em>migration.csv</em></li>
<li>Find the link for your chosen channel. To do this:
<ol>
<li>Open <strong>Teams</strong></li>
<li>Visit the new/existing team (see <em>Microsoft 365 Teams preparation</em>, above), and the channel you have chosen to host the files, perhaps <em>General</em>, and then click <strong>Files</strong></li>
<li>Click <strong>Open in SharePoint</strong></li>
<li>The link you want is everything up to, and including, <em>Shared%20Documents</em> - do not copy anything to the right of that text. For example, <code class="language-plaintext highlighter-rouge">https://digitalcomma.sharepoint.com/sites/Badger/Shared%20Documents</code></li>
</ol>
</li>
<li>Paste that link into the <strong>second column</strong> in a free row and add a <strong>/</strong> character and the name of the channel to the end of the text string. For example, <code class="language-plaintext highlighter-rouge">https://digitalcomma.sharepoint.com/sites/Badger/Shared%20Documents/General</code></li>
<li>In the corresponding <strong>first column</strong>, again write <strong>/</strong> followed by the name of one of your generic folders in Dropbox. For instance, <em>/Resources</em></li>
<li><strong>Repeat steps 3 and 4</strong> for all the generic folders.</li>
<li>Save <em>migration.csv</em></li>
</ol>
<p class="notice--primary">If you have set up a private channel or more than one channel, these will require different links (so repeat steps 2 to 4 for them).</p>
<p><img src="https://digital-comma.com/assets/images/mover-spreadsheet.png" alt=""The migration spreadsheet"" /></p>
<h1 id="actually-moving-the-stuff">Actually moving the stuff</h1>
<p class="notice--info">There will be some ‘technical admin’ required when <a href="https://docs.microsoft.com/en-us/sharepointmigration/mover-manage-connectors">setting up connectors</a> the <em>first</em> time the migration between Dropbox and Microsoft 365 is done. Configuration of this, and the move itself, requires someone with <a href="https://docs.microsoft.com/en-us/microsoft-365/admin/add-users/about-admin-roles?view=o365-worldwide">Microsoft 365 Global Administrator</a> access.</p>
<ol>
<li>Sign into <a href="https://app.mover.io/">app.mover.io</a> using a Microsoft 365 Global Administrator account.</li>
<li>Select <strong>Dropbox (Single User)</strong> as the source</li>
<li>Enter your (admin) Dropbox login details</li>
<li>Select Office 365 <strong>(OneDrive/SharePoint Admin)</strong> as the destination.</li>
<li>Click <strong>Continue migration setup</strong></li>
<li>Click <strong>Migration actions</strong> and then select <strong>Add to Migration</strong></li>
<li>Under <strong>Upload Migration CSV File</strong>, upload your <em>migration.csv</em> file</li>
<li>Click the <strong>Tags</strong> column to move the list of migrations to the top of the table. Highlight (tick) all the items you just added (they will be tagged with something like ‘csv migration.csv’)</li>
<li>Click the <strong>Scan x Users</strong> button. When the scan is complete for all rows (and this scanning can take quite some time - be prepared for hours, not minutes), click <strong>Start Migrating x Users</strong>.</li>
</ol>
<p>At the end of this process, your users should find their Dropbox files have been moved to their OneDrive accounts. All generic files will be in your Microsoft 365 team in the folders you specified.</p>
<p><img src="https://digital-comma.com/assets/images/mover.png" alt=""Mover screenshot"" /></p>Gary ConroyMover is one of Microsoft 365's lesser-known tools but can be handy for bulk migrationsLet it flow!2020-11-19T00:00:00+00:002020-11-19T00:00:00+00:00https://digital-comma.com/microsoft%20365/let-it-flow<div class="notice--info">
<h1>Benefits</h1>
<ul>
<li><strong>Snapshot</strong> One place to go for a snapshot of your approval backlog</li>
<li><strong>Approval central</strong> No missed requests and a consistent approach to sign-off (since one primary workflow and workbench handles all requests)</li>
<li><strong>Reduced emails</strong> Relief from the floods of emails generated by approval requests. No hard to track notification emails</li>
<li><strong>Stability</strong> One approval workflow instead of multiple (and multiple potential points of failure)</li>
<li><strong>More options</strong> More approval options for your authors (not just ‘approve and publish’)</li>
<li><strong>Audit</strong> An audit trail of approval actions</li>
<li><strong>Simplicity</strong> A simpler management process means quicker processing and less stalled and missed requests</li>
</ul>
</div>
<!-- Courtesy of embedresponsively.com //-->
<div class="responsive-video-container">
<iframe src="https://player.vimeo.com/video/458640313?dnt=true" frameborder="0" webkitallowfullscreen="" mozallowfullscreen="" allowfullscreen=""></iframe>
</div>
<h1 id="how-toa-summary">How to…a summary</h1>
<p><i class="fa fas fa-file-alt"></i> <strong>Make a SharePoint list.</strong> This is the core of the workbench and holds all the useful information for each approval request</p>
<p><i class="fa fas fa-birthday-cake"></i> <strong>Create ‘feeder’ workflows.</strong> These will collate the various types of request - approve, review, delete - and feed them to the primary workflow</p>
<p><i class="fa fas fa-star"></i> <strong>Create primary workflow.</strong> This handles all requests</p>
<p><i class="fa fas fa-toolbox"></i> <strong>Create workbench app.</strong> This PowerApp allows editors to approve, reject and edit all approval requests</p>
<h1 id="make-a-sharepoint-list">Make a SharePoint list</h1>
<p>First choose a site that will host your ‘workbench’ list (for instance, a hub site may be a good location).</p>
<p>Create a list and call it ‘Workbench’ with the fields:</p>
<ul>
<li><strong>Title</strong> – (the default field)</li>
<li><strong>Page</strong> – Hyperlink</li>
<li><strong>PageID</strong> – Number</li>
<li><strong>Author</strong> – Person</li>
<li><strong>Approval request</strong> – Single line of text</li>
<li><strong>Approver</strong> – Person</li>
<li><strong>Request time/date</strong> – Date and time</li>
<li><strong>Comments</strong> – Multiple lines of text</li>
<li><strong>Etag</strong> – Single line of text</li>
<li><strong>Processed</strong> – Single line of text</li>
</ul>
<p class="notice--warning"><strong>Important.</strong> Make sure that all your authors have ‘Owner’ access to the workbench list.</p>
<h1 id="create-feeder-workflows">Create ‘feeder’ workflows</h1>
<p><img src="https://digital-comma.com/assets/images/feeder.png" alt=""Create a page approval flow"" class="align-right" /> In the example given on this page, there are three types of author requests:</p>
<ul>
<li>Approve (review and publish)</li>
<li>Review (but don’t publish)</li>
<li>Delete</li>
</ul>
<p>This means that for every site’s ‘Site Pages’ library, you need to <a href="https://support.microsoft.com/en-us/office/configure-page-approval-using-power-automate-14ce6976-a0a7-427b-b4ab-d28d344a5222">configure page approval flow</a> three times. Give each approval flow a unique name (like ‘<em>Publish [sitename] site page</em>’) and an approver (you, initially, but you can edit/add as many as you like later).</p>
<h2 id="first-edit-your-publish-workflow">First, edit your ‘publish’ workflow</h2>
<ol>
<li>Open Power Automate to edit your new ‘Publish’ workflow</li>
<li><strong>Delete</strong> the <em>Send me an email notification</em> step.</li>
<li>
<p>Open (click) the <strong>Scope 2</strong> step</p>
<p><img src="https://digital-comma.com/assets/images/publish-workflow.png" alt=""Edit the PUBLISH workflow"" /></p>
</li>
<li>Copy out the <em>Body</em> text from the <strong>Send me an email notification 2</strong> step. Save this text in a file (for access later)</li>
<li>
<p>While still in <em>Scope 2</em>, <strong>delete</strong> the last three steps:</p>
<ul>
<li>Set content approval status - Pending</li>
<li>Start an approval</li>
<li>Condition.</li>
</ul>
<p>At this stage, only <em>Get file properties</em> and <em>Get file metadata</em> will remain in Scope 2.</p>
</li>
<li>Click <strong>Add an action</strong> and choose <strong>Create item (SharePoint)</strong></li>
<li>
<p>Fill in the blanks as follows (note that most of these map to the same fields in your workbench SharePoint list):</p>
<ul>
<li><strong>Site Address</strong> - from the drop-down, choose the site where your workbench list ‘lives’</li>
<li><strong>List Name</strong> - choose your workbench list</li>
<li><strong>Title</strong> is the ‘guid’ of this site’s ‘Site Pages’ library (find by going to <em>Library settings</em>, open the <em>Enterprise Metadata and Keywords Settings</em> option: the guid is the string between the curly brackets)</li>
<li><strong>Page</strong> is <em>Link to item</em> (which you will find in the ‘dynamic content’ list)</li>
<li><strong>Author Claims</strong> is <em>User name</em> (from the ‘dynamic content’ list)</li>
<li><strong>Request time</strong> is <em>Timestamp</em> (from the ‘dynamic content’ list)</li>
<li><strong>Comments</strong> is <em>Message</em> (from the ‘dynamic content’ list)</li>
<li><strong>Approval request</strong> - write <em>Publish</em></li>
<li><strong>Unique ID</strong> is <em>Id</em> (from the ‘dynamic content’ list in the ‘Get file metadata’ section)</li>
<li><strong>Site</strong> is the URL for the site you have just added the workflow to</li>
<li><strong>Etag</strong> is <em>ETag</em> (from the ‘dynamic content’ list)</li>
<li><strong>File identifier</strong> is ID (from the ‘dynamic content’ list in the ‘Get file properties’ section).</li>
</ul>
<p><img src="https://digital-comma.com/assets/images/create-item.png" alt=""Create item"" /></p>
</li>
<li><strong>Copy</strong> the Scope2 field to your clipboard (this cut-and-paste will speed things up for the other two workflows)</li>
<li>Click <strong>Save</strong>.</li>
</ol>
<h3 id="edit-the-delete-and-review-workflows">Edit the ‘delete’ and ‘review’ workflows</h3>
<ol>
<li>Open the workflow and <strong>delete</strong> the <em>Scope 2</em> and <em>Send me an email notification</em> steps</li>
<li>Click the <strong>New step</strong> button and then under <strong>My clipboard</strong>, click the <strong>Scope2</strong> step you copied from the ‘publish’ workflow</li>
<li>Edit the <em>Create item</em> step and change <strong>Approval request</strong> to either <em>Delete</em> or <em>Review</em> (as appropriate)</li>
<li>Click <strong>Save</strong>.</li>
</ol>
<h2 id="test">Test</h2>
<p>At this stage, your authors can request pages to be reviewed, deleted or published but all that happens, for now, is that their requests are recorded in the Workbench list. The ‘doing’ of these requests comes in the ‘primary workflow’ section, below.</p>
<p>However, both to ensure that your workflows are working correctly and to help you get a grasp of what is happening, I’d suggest a bit of testing. That’s as simple as creating one or more of each approval requests: Publish, Delete and Review. After each review request, check the Workbench list to ensure the right kinds of things are being recorded.</p>
<p>You do need to wait a little time for the flows to run (I’m seeing up to a minute’s delay).</p>
<h1 id="create-primary-workflow">Create primary workflow</h1>
<p>Rather than go into too much detail here, I’ve annotated the Power Automate flow to give you an idea of what’s happening. You will need that email text you copied previously for pasting into the email notifications.</p>
<p class="notice--warning"><strong>A word of caution</strong> - Power Automate seems particularly resource-intensive and crash-happy with email steps so make sure you save after you add each of yours.</p>
<p><img src="https://digital-comma.com/assets/images/primary-workflow.png" alt=""The primary workflow"" /></p>
<h2 id="a-quick-aside-clean-your-workbench-list">A quick aside: clean your workbench (list)</h2>
<p>As it stands, the workbench list isn’t very useful as a human-readable audit trail. So, to clear things up:</p>
<ul>
<li>Hide the ‘technical wiring’ fields: <em>Title, Unique ID, Etag, File identifier, Process</em></li>
<li>Put the most recent items at the top of the list (but keep pages together). To do this, sort, first by Created (date/time - a hidden field by default), and then by Page</li>
<li>Drag your column widths so that you can see what you need to see on your page</li>
<li>Save any changes so they are permanent for this view. You can create different ‘views’ if you wish; for instance, you might want to have a view for just those pages that need deleting</li>
<li>Add some conditional formatting to colour-code rows. In the example:
<ul>
<li>Initial author requests are shaded pale grey</li>
<li>Pages that must be deleted or that have been rejected are shaded red</li>
<li>Pages that have been reviewed are shaded orange</li>
<li>Other pages are shaded dark grey</li>
</ul>
</li>
</ul>
<p class="notice--success"><strong>Tip.</strong> Add workbench to your site menu for owners and members (editors and authors).</p>
<p><img src="https://digital-comma.com/assets/images/workbench.png" alt=""Tidy up the workbench list"" /></p>
<h1 id="create-workbench-app">Create workbench app</h1>
<p>Microsoft’s Power Apps have a useful direct connection to SharePoint lists which makes generating apps from such data sources much simpler. If you are new to Power Apps, check out <a href="https://youtu.be/JhomcXZaa28">Audrie Gordon’s video ‘walkthrough’</a> to get a grasp of the basics.</p>
<p>However, for your workbench app, I’d suggest adding individual fields one-by-one to a gallery (it’s a much less frustrating experience overall).</p>
<p><img src="https://digital-comma.com/assets/images/let-it-flow-app.png" alt=""The workbench app"" /></p>
<p class="notice--warning"><strong>Important.</strong> Once you’ve created your workbench app, make sure you share it with the people you want to use it (most likely your publishers and editors).</p>Gary ConroyAs a member of an editorial team approving SharePoint pages, a 'workbench' app is a handy management toolTechnical writing time travel2020-10-19T00:00:00+01:002020-10-19T00:00:00+01:00https://digital-comma.com/technical%20writing/technical-writing-time-travel<div class="feature__wrapper">
<div class="feature__item">
<div class="archive__item">
<div class="archive__item-teaser">
<img src="/assets/images/about-com/cost-benefit-analysis.jpg" alt="" />
</div>
<div class="archive__item-body">
<h2 class="archive__item-title">Cost benefit analysis</h2>
<div class="archive__item-excerpt">
<p>Comparing the costs of developing documentation against the benefits that may be expected</p>
</div>
<p><a href="https://web.archive.org/web/20000815073942/http://techwriting.about.com/careers/techwriting/library/weekly/aa090398.htm" class="btn btn--inverse">More...</a></p>
</div>
</div>
</div>
<div class="feature__item">
<div class="archive__item">
<div class="archive__item-teaser">
<img src="/assets/images/about-com/estimating.jpg" alt="" />
</div>
<div class="archive__item-body">
<h2 class="archive__item-title">Estimating - part one</h2>
<div class="archive__item-excerpt">
<p>Estimating the time required for a documentation project</p>
</div>
<p><a href="https://web.archive.org/web/20010626032544/http://techwriting.about.com/careers/techwriting/library/weekly/aa020701a.htm" class="btn btn--inverse">More...</a></p>
</div>
</div>
</div>
<div class="feature__item">
<div class="archive__item">
<div class="archive__item-teaser">
<img src="/assets/images/about-com/estimating.jpg" alt="" />
</div>
<div class="archive__item-body">
<h2 class="archive__item-title">Estimating - part two</h2>
<div class="archive__item-excerpt">
<p>A more detailed breakdown</p>
</div>
<p><a href="https://web.archive.org/web/20010709000650/http://techwriting.about.com/careers/techwriting/library/weekly/aa020701b.htm" class="btn btn--inverse">More...</a></p>
</div>
</div>
</div>
</div>
<div class="feature__wrapper">
<div class="feature__item">
<div class="archive__item">
<div class="archive__item-teaser">
<img src="/assets/images/about-com/flow-chart-of-a-technical-writers-work.jpg" alt="" />
</div>
<div class="archive__item-body">
<h2 class="archive__item-title">Flow chart of a technical writer's work</h2>
<div class="archive__item-excerpt">
<p>Covering most of the major phases and decisions</p>
</div>
<p><a href="https://web.archive.org/web/20000815074251/http://techwriting.about.com/careers/techwriting/library/weekly/aa092897.htm" class="btn btn--inverse">More...</a></p>
</div>
</div>
</div>
<div class="feature__item">
<div class="archive__item">
<div class="archive__item-teaser">
<img src="/assets/images/about-com/geting-started-in-technical-writing.jpg" alt="" />
</div>
<div class="archive__item-body">
<h2 class="archive__item-title">Getting started in technical writing</h2>
<div class="archive__item-excerpt">
<p>Do you think you might be a good fit?</p>
</div>
<p><a href="https://web.archive.org/web/20000903091949/http://techwriting.about.com/careers/techwriting/library/weekly/aa033098.htm" class="btn btn--inverse">More...</a></p>
</div>
</div>
</div>
<div class="feature__item">
<div class="archive__item">
<div class="archive__item-teaser">
<img src="/assets/images/about-com/indexing.jpg" alt="" />
</div>
<div class="archive__item-body">
<h2 class="archive__item-title">Indexing</h2>
<div class="archive__item-excerpt">
<p>A good index is a vital component in any document which is more than 20 to 30 pages long</p>
</div>
<p><a href="https://web.archive.org/web/20000818214059/http://techwriting.about.com/careers/techwriting/library/weekly/aa011998.htm" class="btn btn--inverse">More...</a></p>
</div>
</div>
</div>
</div>
<div class="feature__wrapper">
<div class="feature__item">
<div class="archive__item">
<div class="archive__item-teaser">
<img src="/assets/images/about-com/planning-a-documentation-project.jpg" alt="" />
</div>
<div class="archive__item-body">
<h2 class="archive__item-title">Planning a documentation project</h2>
<div class="archive__item-excerpt">
<p>Most writing projects consist of five major phases</p>
</div>
<p><a href="https://web.archive.org/web/20000815061715/http://techwriting.about.com/careers/techwriting/library/weekly/aa071497.htm" class="btn btn--inverse">More...</a></p>
</div>
</div>
</div>
<div class="feature__item">
<div class="archive__item">
<div class="archive__item-teaser">
<img src="/assets/images/about-com/proposal-writing.jpg" alt="" />
</div>
<div class="archive__item-body">
<h2 class="archive__item-title">Proposal writing</h2>
<div class="archive__item-excerpt">
<p>Convince the reader that the author’s objectives are well-founded and worth pursuing</p>
</div>
<p><a href="https://web.archive.org/web/20010411091656/http://techwriting.about.com/careers/techwriting/library/weekly/aa022201.htm" class="btn btn--inverse">More...</a></p>
</div>
</div>
</div>
<div class="feature__item">
<div class="archive__item">
<div class="archive__item-teaser">
<img src="/assets/images/about-com/quick-user-manuals.jpg" alt="" />
</div>
<div class="archive__item-body">
<h2 class="archive__item-title">Quick user manuals</h2>
<div class="archive__item-excerpt">
<p>Assuming that the reference guide is being written, you can create a quick user manual by referring to topics within that document</p>
</div>
<p><a href="https://web.archive.org/web/20000815061722/http://techwriting.about.com/careers/techwriting/library/weekly/aa092998.htm" class="btn btn--inverse">More...</a></p>
</div>
</div>
</div>
</div>
<div class="feature__wrapper">
<div class="feature__item">
<div class="archive__item">
<div class="archive__item-teaser">
<img src="/assets/images/about-com/report-writing.jpg" alt="" />
</div>
<div class="archive__item-body">
<h2 class="archive__item-title">Report writing</h2>
<div class="archive__item-excerpt">
<p>The components of a report (my most popular About.com article)</p>
</div>
<p><a href="https://web.archive.org/web/20000815074023/http://techwriting.about.com/careers/techwriting/library/weekly/aa033199.htm" class="btn btn--inverse">More...</a></p>
</div>
</div>
</div>
<div class="feature__item">
<div class="archive__item">
<div class="archive__item-teaser">
<img src="/assets/images/about-com/synopsis.jpg" alt="" />
</div>
<div class="archive__item-body">
<h2 class="archive__item-title">The synopsis</h2>
<div class="archive__item-excerpt">
<p>Once the research phase of a documentation project is well advanced, it is useful to plan what is going to be written in a short document (a synopsis)</p>
</div>
<p><a href="https://web.archive.org/web/20000819013135/http://techwriting.about.com/careers/techwriting/library/weekly/aa051298.htm" class="btn btn--inverse">More...</a></p>
</div>
</div>
</div>
<div class="feature__item">
<div class="archive__item">
<div class="archive__item-teaser">
<img src="/assets/images/about-com/technical-what.jpg" alt="" />
</div>
<div class="archive__item-body">
<h2 class="archive__item-title">Technical what?</h2>
<div class="archive__item-excerpt">
<p>To some, technical writing can seem an esoteric, time-consuming and unnecessary frippery</p>
</div>
<p><a href="https://web.archive.org/web/20000711040056/http://techwriting.about.com/careers/techwriting/library/weekly/aa022797.htm" class="btn btn--inverse">More...</a></p>
</div>
</div>
</div>
</div>
<div class="feature__wrapper">
<div class="feature__item">
<div class="archive__item">
<div class="archive__item-teaser">
<img src="/assets/images/about-com/translation.jpg" alt="" />
</div>
<div class="archive__item-body">
<h2 class="archive__item-title">Translation: the writer's role</h2>
<div class="archive__item-excerpt">
<p>To aid the translator in producing an accurate, and cost-effective, representation of the original</p>
</div>
<p><a href="https://web.archive.org/web/20001218044000/http://techwriting.about.com/careers/techwriting/library/weekly/aa031698.htm" class="btn btn--inverse">More...</a></p>
</div>
</div>
</div>
<div class="feature__item">
<div class="archive__item">
<div class="archive__item-teaser">
<img src="/assets/images/about-com/translation.jpg" alt="" />
</div>
<div class="archive__item-body">
<h2 class="archive__item-title">Choosing a translator</h2>
<div class="archive__item-excerpt">
<p>The criteria that should be applied to your search for a translator</p>
</div>
<p><a href="https://web.archive.org/web/20010206190758/http://techwriting.about.com/careers/techwriting/library/weekly/aa041398.htm" class="btn btn--inverse">More...</a></p>
</div>
</div>
</div>
<div class="feature__item">
<div class="archive__item">
<div class="archive__item-teaser">
<img src="/assets/images/about-com/whats-in-a-name.jpg" alt="" />
</div>
<div class="archive__item-body">
<h2 class="archive__item-title">What's in a name?</h2>
<div class="archive__item-excerpt">
<p>There are plenty of people who do technical writing but aren’t called technical writers</p>
</div>
<p><a href="https://web.archive.org/web/20010511211541/http://techwriting.about.com/careers/techwriting/library/weekly/aa083197.htm" class="btn btn--inverse">More...</a></p>
</div>
</div>
</div>
</div>
<p><img src="https://digital-comma.com/assets/images/police-call-box.jpg" alt="alt" /></p>Gary ConroyI used to work as a technical writing blogger (guide) for the sadly-defunct About.com. Courtesy of the [Internet Archive](https://archive.org/), some of those articles are still ‘floating around’.