push my changes to new repo

This commit is contained in:
N-Upchurch
2023-07-05 11:06:31 -05:00
parent 4f0a64d781
commit 5e0eb0cdcf
118 changed files with 1696 additions and 358 deletions

View File

@ -1,19 +1,5 @@
---
layout: layouts/home.njk
layout: layouts/404.njk
permalink: 404.html
eleventyExcludeFromCollections: true
---
# Content not found.
Go <a href="/">home</a>.
<!--
Read more: https://www.11ty.dev/docs/quicktips/not-found/
This will work for both GitHub pages and Netlify:
* https://help.github.com/articles/creating-a-custom-404-page-for-your-github-pages-site/
* https://www.netlify.com/docs/redirects/#custom-404
-->

19
content/404.md.backup Normal file
View File

@ -0,0 +1,19 @@
---
layout: layouts/home.njk
permalink: 404.html
eleventyExcludeFromCollections: true
---
# Content not found.
Go <a href="/">home</a>.
<!--
Read more: https://www.11ty.dev/docs/quicktips/not-found/
This will work for both GitHub pages and Netlify:
* https://help.github.com/articles/creating-a-custom-404-page-for-your-github-pages-site/
* https://www.netlify.com/docs/redirects/#custom-404
-->

View File

@ -1,9 +1,8 @@
---
layout: layouts/base.njk
eleventyNavigation:
key: About Me
key: About
order: 3
---
# About Me
I am a person that writes stuff.
# About the author, Nathan Upchurch.
<p class="page-block nodropcap">I'm a prolific vegan home cook, classical trombonist, a <abbr title="Free/Libre Open Source Software">FLOSS</abbr> enthusiast, daily GNU/Linux user and unabashed <a href="https://kde.org/">KDE</a> stan, speaker of subpar elementary Spanish, incense enthusiast, writer, electronics hobbyist, designer, programmer, music producer, print lover, and human with too many interests and too little time. This is my personal website and blog. <br><br>Because this is my <em>personal</em> website, I'm not here to talk about work, but I will link my professional bio here when I've gotten around to making it. If you would like to say something nice, you can find me on <a href="https://mastodon.social/@nathanu">Mastodon</a>.<br><br>This website is made with <a href="https://www.11ty.dev/">11ty</a>, <a href="https://www.gent.media/manrope">Manrope</a>, <a href="https://github.com/clauseggers/Playfair">Playfair Display</a>, and plain-old HTML & CSS. I don't collect any of your data, full-stop. No analytics, no "anonymized data," nothing. All webfonts, icons, and images are hosted locally (Instead of by Google, for instance, or any other company which might<a href="https://www.firstpost.com/world/how-google-uses-fonts-to-track-what-users-do-online-and-sell-data-to-advertisers-12496552.html"> use them to track you</a>).<br><hr>Blogs are the soul of the web! To find more, visit <a href="https://blogroll.org">blogroll.org</a>, or <a href="https://ooh.directory/">ooh.directory</a>.</p>

View File

@ -1,10 +1,14 @@
---
layout: layouts/home.njk
layout: layouts/base.njk
eleventyNavigation:
key: Archive
key: Blog
order: 2
---
<h1>Archive</h1>
<h1>Nathan Upchurchs Personal Blog: Latest Posts.</h1>
<p class="page-block nodropcap">
Looking for something in particular? Have a look at <a href="/tags/">this convenient list of post categories</a> to filter results by topic.
</p>
{% set postslist = collections.posts %}
{% include "postslist.njk" %}

View File

@ -0,0 +1,451 @@
---
title: Build an SVG Circle Grid with p5.js
description: Make a configurable SVG graphic of a grid of circles in random colors and sizes with p5.js.
date: 2023-06-18
tags:
- Processing
- p5.js
- Code Tutorial
- SVG
synopsis: In this tutorial, we'll learn how to make a configurable SVG graphic of a grid of circles in random colors and sizes with p5.js.
imageURL: /img/terminal.svg
imageAlt: A stylized illustration of a terminal prompt.
---
Processing is a fantastic language for creative programming and learning how to code, allowing programmers of all skill levels to quickly and simply create complex graphics, data visualizations, and generative art. Its Javascript implementation, p5.js, is perfect for those already familiar with Javascript, or who want to use processing to make graphics for the web without the complexity of SVG, or the insanity of using CSS for complex graphics. Today we're going to build [a simple but pretty graphic using P5](#et-voila).
## Our goal
First, let's define exactly what we're going to be making. We want to make a grid of circles that:
1. Fills the viewport
2. Randomly assigns a size to each circle
3. Randomly assigns a color to each circle
4. Is flexible and avoids hardcoded values, allowing us to get a variety of different looks depending on our parameters
5. Allows us to download our generated image as an SVG
With that nailed down, let's go ahead and get set up.
## Setup
Processing has a handy <abbr title="Integrated Development Environment">IDE</abbr>, much like Arduino, that we can use to get started quickly. Processing IDE is available as a Flatpak, so it should be simple to install no matter what distro you're running. If, sadly, you're on Windows or MacOS, I'm sure there is also a simple way to install it on your machine that can be worked out with a quick search on the internet.
Once you've installed and launched the IDE, at the top right you'll notice a dropdown that says "Java." This is the Processing IDE's mode selector. Click the dropdown, and choose "Manage Modes." In the new window, install "p5.js Mode," and switch to p5.js using the mode selector.
## Getting started
If you've done everything correctly up to now, you should see two tabs in your IDE: a .js file, and index.html. Our .js file should look like this:
``` javascript
function setup() {
}
function draw() {
}
```
and our index.html should look like this:
``` html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- PLEASE NO CHANGES BELOW THIS LINE (UNTIL I SAY SO) -->
<script language="javascript" type="text/javascript" src="libraries/p5.min.js"></script>
<script language="javascript" type="text/javascript" src="sketch_230615e.js"></script>
<!-- OK, YOU CAN MAKE CHANGES BELOW THIS LINE AGAIN -->
<style>
body {
padding: 0;
margin: 0;
}
</style>
</head>
<body>
</body>
</html>
```
In any processing sketch, there are two main functions, as you can see by looking at our .js file. `setup()` will run once when the sketch is loaded, and `draw()` will loop repeatedly. For our purposes, we don't need `draw()`, so we'll just leave it empty.
Now that we have our boilerplate, the next thing we need to do is set up the canvas for our sketch. As you may recall, we want our sketch to fill the viewport, so let's set up a canvas inside of our `setup()` function like so:
``` javascript
function setup() {
createCanvas(window.innerWidth, window.innerHeight);
background(0);
noStroke();
}
```
Now if we click the play icon at the top left of the Processing IDE, a browser window should open and load a page showing our sketch so far. We ought to see a viewport entirely covered with one large black canvas.
[![The LibreWolf web browser opened to localhost. The viewport is entirely black.](../../img/posts/build-an-svg-circle-grid-with-p5js/circle-grid-canvas.webp)](../../img/posts/build-an-svg-circle-grid-with-p5js/circle-grid-canvas.webp)
## Random results
To meet conditions two and three of our goal, we'll need a way to get random numbers. Let's write a quick function inside `setup()` to provide us with random integers:
``` javascript
const getRandomInt = (min, max) => {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min) + min);
}
```
## Let's make some circles
To make a circle in p5, we need to define three parameters: x position, y position, and its diameter. With that in mind, we can define a circle like so:
``` javascript
circle(10,10,10);
```
Add one to the bottom of `setup()` and refresh your browser tab if you'd like to test it out. Be sure and delete it afterwards.
We're going to be making a grid of circles, however, so we are going to need a little more information. First, let's start by generating a line of circles. Although we only need to generate a line of circles on one axis for the purpose of making a grid, a row or a column, for the sake of future flexibility, we'll assume that our line might be horizontal, vertical, or diagonal. To do this, we'll need the following information:
* Line start position x
* Line start position y
* Distance between circles on x
* Distance between circles on y
* Minimum circle diameter
* Maximum circle diameter
* Quantity of circles
* The axis upon which our line extends
* An array of colors for our circles
You'll notice that we have min-max values for circle diameter, and an array for our circle colors. This is so that a size and color can be chosen randomly by our program.
With that out of the way, let's start writing a function to generate our line of circles inside `setup()`:
``` javascript
const generateCircleLine = (startX, startY, distX, distY, minD, maxD, qty, axis, fillArr) => {
}
```
We are going to need to increment our starting coordinates, so let's assign two variables to those parameters:
``` javascript
let x = startX;
let y = startY;
```
And we can write our loop inside of `generateCircleLine()`:
``` javascript
for (let i = 0; i < qty; i++) {
const diameter = getRandomInt(minD-1, maxD);
if (!i) {
fill(...fillArr[getRandomInt(0, fillArr.length)]);
circle(x,y,diameter);
continue;
}
switch (axis) {
case 'x':
x = !distX ? x + startX : x + distX;
break;
case 'y':
y = !distY ? y + startY : y + distY;
break;
case 'xy':
x = !distX ? x + startX : x + distX;
y = !distY ? y + startY : y + distY;
break;
}
fill(...fillArr[getRandomInt(0, fillArr.length)]);
circle(x,y,diameter);
}
```
In the loop above:
* We start by assigning `diameter` to a random integer between `minD` and `maxD`
* Then, if `i` is false / 0, indicating that we're on our first iteration, we randomly choose a fill color value from `fillArr`, draw our first circle, and continue on to our next iteration.
* Now that we're on iteration 1 / circle 2, we need to figure out what axis or axes our line is on, and calculate the new starting coordinates for the circle about to be drawn. For this, we use a switch statement, so if we are operating on the x asis, only the x starting coordinate will be incremented, et cetera. We have used the ternary operator here to indicate that if a distance is not specified along any given axis, the program should increment by a distance equal to its starting coordinate.
* As we now have our coordinates, we choose a random fill color from fillArr, draw our next circle, and repeat until `i < qty`.
That was a lot, so let's look at our code and make sure everything is alright. This is what our code should look like at the moment:
``` javascript
function setup() {
createCanvas(window.innerWidth, window.innerHeight);
background(0);
noStroke();
const getRandomInt = (min, max) => {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min) + min);
}
const generateCircleLine = (startX, startY, distX, distY, minD, maxD, qty, axis, fillArr) => {
let x = startX;
let y = startY;
for (let i = 0; i < qty; i++) {
const diameter = getRandomInt(minD-1, maxD);
if (!i) {
fill(...fillArr[getRandomInt(0, fillArr.length)]);
circle(x,y,diameter);
continue;
}
switch (axis) {
case 'x':
x = !distX ? x + startX : x + distX;
break;
case 'y':
y = !distY ? y + startY : y + distY;
break;
case 'xy':
x = !distX ? x + startX : x + distX;
y = !distY ? y + startY : y + distY;
break;
}
fill(...fillArr[getRandomInt(0, fillArr.length)]);
circle(x,y,diameter);
}
}
}
function draw() {
}
```
We can check that everything is working by calling our new function at the bottom of `setup()`:
``` javascript
generateCircleLine(10, 10, 20, 0, 5, 10, 10, 'x', [[255,255,255]]);
```
[![The LibreWolf web browser opened to localhost. The viewport is black, with a single row of ten white circles in varying sizes in the top-left corner.](../../img/posts/build-an-svg-circle-grid-with-p5js/circle-grid-line.webp)](../../img/posts/build-an-svg-circle-grid-with-p5js/circle-grid-line.webp)
And look at that! We have a line!
## Repeating rows
As we now have a function built to generate lines of circles, all we need to do is repeat the process and we'll have a grid. In `setup()`, let's go ahead and create `generateCircleGrid()` to do just that. Because we'll be passing our parameter values from `generateCircleGrid()` to `generateCircleLine()`, we'll need all of the same information, plus a few new parameters:
``` javascript
const generateCircleGrid = (rowStartX, rowStartY, rowDistX, minD, maxD, rowCircleQty, numRows, rowSpacing, fillArr) => {
}
```
Inside our new function, all we need to do is call `generateCircleLine()` in a loop and we'll have ourselves a grid:
``` javascript
for (let i = 0; i < numRows; i++) {
rowYPosition = !i ? rowStartY : rowStartY + rowSpacing*i;
generateCircleLine(rowStartX, rowYPosition, rowDistX, 0, minD, maxD, rowCircleQty, 'x', fillArr);
}
```
In the first line of our for loop, we're checking whether we're in the first iteration (`i == 0`). If so, `rowYPosition` is assigned to rowStartY; otherwise, `rowYPosition` is assigned to `rowStartY + rowSpacing*i`. That means that on our second+ iterations, our row starting coordinate is incremented by our row spacing value multiplied by the current iteration number, ensuring that each iteration draws a row at the correct y coordinate.
We've hardcoded 'x' in the `generateCircleLine()` parameter, so that we only have to worry about incrementing one axis as we generate our grid: `generateCircleLine()` will create columns, and `generateCircleGrid()` loops them to generate rows. To this end, we're also passing 0 to the `distY` in generateCircleLine()`.
## The perfect fit
So, we can create lines of circles, and use those to make a grid, but we have a problem. Right now, our sketch needs to be explicitly told how many circles to draw in each row. We need a way to determine how many circles ought to be drawn in order to fill the canvas on a given axis.
Fortunately, this is an easy fix. If write a function inside `setup()` to subtract our starting coordinate from the window dimension for a given axis, divide the result by the distance between circles, and return the result, we should wind up with just the right nimber of circles to fit within that axis. We'll also give our function a parameter called `axis`. Using this parameter, we'll specify whether the operation should use `window.innerWidth` to return a result for the x axis, or `window.innerHeight` to return a result for the y axis.
``` javascript
const getMaxCircles = (distance, axis, startDistance)=> {
return Math.floor(axis == 'x' ? (window.innerWidth - startDistance) / distance :
(window.innerHeight - startDistance) / distance);
}
```
## Specifying the grid
We're now all set up to begin actually generating grids, so let's make one! To keep things neat, let's specify our grid in an object called `myGrid` that contains all the parameters we'll need. Here's what mine looks like:
``` javascript
const myGrid = {
rowStartX: 20,
rowStartY: 20,
rowDistX: 20,
minDiameter: 5,
maxDiameter: 15,
rowSpacing: 20,
rowCircleQty: function() {return getMaxCircles(this.rowDistX, 'x', this.rowStartX)},
numRows: function() {return getMaxCircles(this.rowSpacing, 'y', this.rowStartY)},
fillArray: [
[100, 50, 255],
[100, 100, 255],
[100, 150, 255],
[100, 200, 255],
[100, 250, 255]
]
}
```
You may notice a couple of things:
* I've taken advantage of the `getMaxCircles()` function we wrote earlier to calculate ideal values for `myGrid.rowCircleQty`, and `myGrid.numRows`.
* `myGrid.fillArray` is using RGB values. Check the [p5.js documentation](https://p5js.org/reference/#/p5/fill) for other color values you can use.
Feel free to customize `myGrid` to make your ideal circle grid, and call `generateCircleGrid()` to see the result:
``` javascript
generateCircleGrid(
myGrid.rowStartX,
myGrid.rowStartY,
myGrid.rowDistX,
myGrid.minDiameter,
myGrid.maxDiameter,
myGrid.rowCircleQty(),
myGrid.numRows(),
myGrid.rowSpacing,
myGrid.fillArray
);
```
[![The LibreWolf web browser opened to localhost. The viewport is filled with circles of varying sizes in various shades of blue.](../../img/posts/build-an-svg-circle-grid-with-p5js/circle-grid-grid.webp)](../../img/posts/build-an-svg-circle-grid-with-p5js/circle-grid-grid.webp)
## SVG export
Looking back on our goals, we've accomplished all but one: SVG export. Happily, there's [a library](https://github.com/zenozeng/p5.js-svg) that makes this trivial. All we need to do is link the script in the `<head>` of index.html,
``` html
<script language="javascript" type="text/javascript" src="https://unpkg.com/p5.js-svg@1.5.1"></script>
```
add a button in `<body>`,
``` html
<button onclick="save()">Save SVG</button>
```
style it in `<style>`,
``` css
button {
position: fixed;
bottom: 3rem;
right: 3rem;
width: 7rem;
height: 3rem;
}
```
and make one small tweak in our Javascript file. We simply need to modify our `createCanvas()` function like so:
``` javascript
createCanvas(window.innerWidth, window.innerHeight, SVG);
```
## Our finished code
Here's what everything should look like at this point:
### *.js
``` javascript
function setup() {
createCanvas(window.innerWidth, window.innerHeight, SVG);
background(0);
noStroke();
const getRandomInt = (min, max) => {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min) + min);
}
const getMaxCircles = (distance, axis, startDistance)=> {
return Math.floor(axis == 'x' ? (window.innerWidth - startDistance) / distance :
(window.innerHeight - startDistance) / distance);
}
const generateCircleLine = (startX, startY, distX, distY, minD, maxD, qty, axis, fillArr) => {
let x = startX;
let y = startY;
for (let i = 0; i < qty; i++) {
const diameter = getRandomInt(minD-1, maxD);
if (!i) {
fill(...fillArr[getRandomInt(0, fillArr.length)]);
circle(x,y,diameter);
continue;
}
switch (axis) {
case 'x':
x = !distX ? x + startX : x + distX;
break;
case 'y':
y = !distY ? y + startY : y + distY;
break;
case 'xy':
x = !distX ? x + startX : x + distX;
y = !distY ? y + startY : y + distY;
break;
}
fill(...fillArr[getRandomInt(0, fillArr.length)]);
circle(x,y,diameter);
}
}
const generateCircleGrid = (rowStartX, rowStartY, rowDistX, minD, maxD, rowCircleQty, numRows, rowSpacing, fillArr) => {
for (let i = 0; i < numRows; i++) {
rowYPosition = !i ? rowStartY : rowStartY + rowSpacing*i;
generateCircleLine(rowStartX, rowYPosition, rowDistX, 0, minD, maxD, rowCircleQty, 'x', fillArr);
}
}
const myGrid = {
rowStartX: 20,
rowStartY: 20,
rowDistX: 20,
minDiameter: 5,
maxDiameter: 15,
rowSpacing: 20,
rowCircleQty: function() {return getMaxCircles(this.rowDistX, 'x', this.rowStartX)},
numRows: function() {return getMaxCircles(this.rowSpacing, 'y', this.rowStartY)},
fillArray: [
[100, 50, 255],
[100, 100, 255],
[100, 150, 255],
[100, 200, 255],
[100, 250, 255]
]
}
generateCircleGrid(
myGrid.rowStartX,
myGrid.rowStartY,
myGrid.rowDistX,
myGrid.minDiameter,
myGrid.maxDiameter,
myGrid.rowCircleQty(),
myGrid.numRows(),
myGrid.rowSpacing,
myGrid.fillArray
);
}
function draw() {
}
```
### index.html
``` html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- PLEASE NO CHANGES BELOW THIS LINE (UNTIL I SAY SO) -->
<script language="javascript" type="text/javascript" src="libraries/p5.min.js"></script>
<script language="javascript" type="text/javascript" src="sketch_230615e.js"></script>
<script language="javascript" type="text/javascript" src="https://unpkg.com/p5.js-svg@1.5.1"></script>
<!-- OK, YOU CAN MAKE CHANGES BELOW THIS LINE AGAIN -->
<style>
body {
padding: 0;
margin: 0;
}
button {
position: fixed;
bottom: 3rem;
right: 3rem;
width: 7rem;
height: 3rem;
}
</style>
</head>
<body>
<button onclick="save()">Save SVG</button>
</body>
</html>
```
## Et voilà
And we're done! Now you can tweak the parameters and make grids with all sorts of colors and size variations, and even export and edit them in Inkscape!
[![The LibreWolf web browser opened to localhost. The viewport is filled with circles of varying sizes in various shades of blue. There is a button reading "Save SVG" in the bottom right corner.](../../img/posts/build-an-svg-circle-grid-with-p5js/circle-grid-complete.webp)](../../img/posts/build-an-svg-circle-grid-with-p5js/circle-grid-complete.webp)
If you'd like to make this project even better, maybe consider implementing a GUI to adjust your grid paramaters, or adding some interactivity.

View File

@ -1,6 +0,0 @@
---
title: This is a fifth post (draft)
date: 2023-01-23
draft: true
---
This is a draft post

View File

@ -1,26 +0,0 @@
---
title: This is my first post.
description: This is a post on My Blog about agile frameworks.
date: 2018-05-01
tags:
- another tag
---
Leverage agile frameworks to provide a robust synopsis for high level overviews. Iterative approaches to corporate strategy foster collaborative thinking to further the overall value proposition. Organically grow the holistic world view of disruptive innovation via workplace diversity and empowerment.
Bring to the table win-win survival strategies to ensure proactive domination. At the end of the day, going forward, a new normal that has evolved from generation X is on the runway heading towards a streamlined cloud solution. User generated content in real-time will have multiple touchpoints for offshoring.
## Section Header
Capitalize on low hanging fruit to identify a ballpark value added activity to beta test. Override the digital divide with additional clickthroughs from DevOps. Nanotechnology immersion along the information highway will close the loop on focusing solely on the bottom line.
```diff-js
// this is a command
function myCommand() {
+ let counter = 0;
- let counter = 1;
counter++;
}
// Test with a line break above this line.
console.log('Test');
```

View File

@ -0,0 +1,28 @@
---
title: Free Software is the Future for SMEs and Small Nonprofits
description: Tech giants aren't meeting the needs of SMEs and nonprofits. Combined with digital privacy concerns, a perfect storm is brewing for increased adoption of free and open source software for these organizations.
date: 2022-10-28
tags:
- FOSS/FLOSS
- Digital Privacy
synopsis: Tech giants aren't meeting the needs of SMEs and nonprofits. Combined with digital privacy concerns, a perfect storm is brewing for increased adoption of free and open source software for these organizations.
imageURL: /img/posts/floss-and-smes/pedro-lastra-Nyvq2juw4_o-unsplash.webp
imageAlt: A stylized illustration of a terminal prompt.
---
If you're a cottage industry solopreneur, a cricut hobbyist, or a makerspace regular, you've probably heard of Inkscape. Often dismissed by design professionals as a poor substitute for fully fleshed out design tools or simply "not industry standard," this 19 year old vector graphics powerhouse allows artists, designers, scrapbookers and makers alike to create high quality graphics and illustrations that can be used anywhere, from the browser to printed collateral and laser-etched goods. Inkscape, like many other free and open source software (FOSS) projects, has been taking great strides in recent years to match, if not surpass, commercial alternatives in terms of features, output, and usability. FOSS users have long been privy to a a world of software all but unknown to those who have never ventured from the comfort of Microsoft, Adobe, Alphabet, and Oracle, or the shimmering walled-garden of the Apple ecosystem. It hasn't always been easy, pretty, or even stable, but these days free and open source software projects are more organized, better funded, and more accountable than ever before, and people are starting to take notice.
## FOSS offers a level of flexibility and transparency that proprietary mainstays can't or won't provide.
As free and open source software becomes increasingly attractive to small businesses and nonprofits due to expanding feature-sets and improved user-interfaces, the principles at the core of its creation, perhaps counterintuitively, render projects such as Inkscape ideal for a business environment. While those unfamiliar with the concept of FOSS projects may interpret "free" to reflect its pricing structure, developers can and do charge for access to their products. Instead, the "F" in FOSS represents the freedom to run the program however you want, to study how it works, to change it, and to redistribute copies. FOSS software, in addition, must also publish its source code publicly. This radically transparent approach, beyond the obvious implication that FOSS software is vastly more flexible than proprietary mainstays, can have a tremendous effect on security and privacy, both key with software present on devices containing sensitive employee information, intellectual property, and credentials. While the dialogue surrounding digital privacy has almost exclusively focused on individual users, as we're now aware of the ability of marketers, campaigns, ([or even John Oliver](https://www.youtube.com/watch?v=wqn3gR1WTcA)) to purchase highly targeted, fine-grained data for any purpose, it's only a matter of time before more organizations begin to consider the implications of the potential for proprietary software to indiscriminately collect employee web-searches, map routes, downloads, and discussions to turn into data-points available for purchase by anyone with the budget, such as competitors, marketers, journalists, or government and regulatory bodies, domestic and abroad.
This is where FOSS starts to make a lot more sense from a privacy perspective; because human-readable source code is generally transformed, or compiled, into a more efficient language that can only be read by computers before software is distributed to users, FOSS software authors publish their human-readable source code publicly, enabling anyone to review it. In contrast, companies behind proprietary software do not publicly publish their source code, making it almost impossible to understand precisely how their software works, or what it's doing at any given time. We already know that just about all big names in tech collect private data; we either shrug our shoulders and make peace with it, or we suffer through innumerable privacy policies, settings, sliders, and toggles, and still itch a little afterwards. But what about the tools we use every day that don't have a privacy policy? What about your picture viewer, your file browser, or even your calculator app? The stringent efforts of even the most militant IT departments to block users from installing unapproved software are moot in the face of the fact that we have no way of truly knowing what even the most fundamental software tools from trusted brands are actually doing on the devices that we use to do our work each day. By enabling public access to its source code, free and *open source* software can see have dozens, hundreds or even thousands of tech-savvy users pointing out security flaws to be fixed, alerting developers to bugs, and raising alarm bells about potential privacy issues.
## Individual users and small organizations can directly influence development of the FOSS tools they use every day.
The focus on transparency in FOSS software engenders a culture of collaboration all but alien to organizations accustomed to proprietary solutions. Successful projects build communities of developers and other contributors, who are each free to work in their own way. Some contributors are individuals who stop by to implement a feature they wish a project had, while others are teams paid by large enterprises to improve open source projects that form a part of their digital infrastructure. Some individuals simply work on FOSS projects as a hobby, and others, like [Inkscape's Martin Owens](https://www.youtube.com/channel/UCPxxdsRV92DZGE-RcRsw_gw), are freelance developers who collect donations to work on bug fixes and features using donor feedback to prioritize their tasks and steer their work. Martin regularly uploads videos on his [YouTube channel](https://www.youtube.com/channel/UCPxxdsRV92DZGE-RcRsw_gw) and [Patreon](https://www.patreon.com/doctormo) to fill in donors on the latest Inkscape updates and development work, and uses polls to allow them to vote on which features should be prioritized for the next release. Donors can message him directly, for technical support, questions, or suggestions. Developers on Inkscape and other FOSS projects can be reached via public mailing lists and chat channels, and software users often interact directly with them via bug reports and feature requests. This makes each user a potential collaborator in the development of the tools they use.
Only a few weeks ago, I filed [a feature request](https://bugs.kde.org/show_bug.cgi?id=454674) for [Kasts](https://apps.kde.org/kasts/), a desktop app that syncs and plays podcasts. After a discussion with one of the developers, they eventually sent me a link to test the feature, which is now in the latest release. This culture of collaboration represents a unique opportunity for SMEs and nonprofits that may not have the budget for custom enterprise solutions. So long as organizations understand and respect the culture, a reasonable donation budget and a willingness to collaborate can open avenues for smaller organizations to work with developers, freelancers, designers, and others within FOSS projects to work towards better experiences and expanded feature sets. Whether this takes the shape of donating a sum in support of the project, and participating by filing competent bug reports and feature requests, or directly commissioning a developer on the project to design and implement a needed feature, needed funds are usually a far cry away from big-budget proprietary-software customizations, and having the option to even speak to a developer at all is a breath of fresh air when compared to the usual experience dealing with sales representatives and account managers. Of course, commercial users of free and open source software must temper their expectations; as with proprietary software, there are no guarantees that the project will go in the direction you'd like, and FOSS projects have no obligation to provide support or develop suggested features no matter how much you donate.
## Many FOSS projects have long been ready for professional and enterprise use.
Beyond privacy, security, and collaboration potential, FOSS alternatives are getting better by the day. Many open source projects are being developed at a breakneck pace while receiving major corporate support, and projects strictly manned by volunteers are more organized than ever before. An uptick in donations sees some projects considering diverting a portion of donated funds to outsourcing some of the more arduous development tasks less likely to be quickly tackled by volunteers. While FOSS projects such as GNU/Linux based operating systems and the software written for them have long played a critical role in the world's digital infrastructure, often even serving as the base upon which commercial products are built, free software is no longer resigned to the server room, with consumer products such as Valve's Steam Deck being based on projects such as the Arch Linux desktop operating system and KDE's Plasma desktop environment, a computer graphical user interface that looks good enough to compete with large commercial players like Microsoft's Windows Shell and Apple's Aqua, and flogs both of them when it comes to flexibility and customization. It is not uncommon for free-of-charge FOSS projects such as Inkscape to now boast a feature-set comparable, or as in the case of Inkscape, even exceeding that of their paid competitors, and large communities such as those of GNOME and KDE have developed extensive collections of software that can take the place of hundreds of proprietary software tools at zero cost.
## As organizations begin to see FOSS benefits, big tech is likely to begin losing market share.
While big names in tech continue to erode user privacy and ownership of their software via expanding data collection and subscription models that see users unable to use a PDF reader without connecting to the internet, knowledge of FOSS projects is becoming less and less of the domain of the hyper tech-literate. Our world has already become one in which feature-rich, fast, and well designed free and open source software can replace most black-box proprietary solutions, and for SMEs and nonprofits in the know, the flexibility, privacy expectations, and price-point makes FOSS an extremely attractive proposition. Tech giants won't lose sluggish enterprise clients any time soon, but the writing is on the wall for the many smaller organizations without the budgets for multi-million dollar IT contracts, unless they do something fast.

View File

@ -1,16 +0,0 @@
---
title: This is my fourth post.
description: This is a post on My Blog about touchpoints and circling wagons.
date: 2018-09-30
tags: second tag
---
Leverage agile frameworks to provide a robust synopsis for high level overviews. Iterative approaches to corporate strategy foster collaborative thinking to further the overall value proposition. Organically grow the holistic world view of disruptive innovation via workplace diversity and empowerment.
Bring to the table win-win survival strategies to ensure proactive domination. At the end of the day, going forward, a new normal that has evolved from generation X is on the runway heading towards a streamlined cloud solution. User generated content in real-time will have multiple touchpoints for offshoring.
{% image "./possum.png", "A possum parent and two possum kids hanging from the iconic red balloon" %}
## Section Header
Capitalize on low hanging fruit to identify a ballpark value added activity to beta test. Override the digital divide with additional clickthroughs from DevOps. Nanotechnology immersion along the information highway will close the loop on focusing solely on the bottom line.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 127 KiB

View File

@ -0,0 +1,50 @@
---
title: How to Transfer Files Securely with the “scp” Command
description: How to transfer files to and from a remote Server with the scp / secure copy command
synopsis: How to transfer files to and from a remote Server with the scp / secure copy command
date: 2023-06-12
tags:
- GNU/Linux
- Tutorial
- CLI Tools
imageURL: /img/terminal.svg
imageAlt: A stylized illustration of a terminal prompt.
---
Copying files to and from remote servers is a common chore that can initially seem uncommonly tricky. Thankfully, we have the `scp` command, also known as the secure copy command, as a simple way to initiate secure transfers to and from your remote boxes.
## First, make sure your permissions are in order
Just like when you `ssh` into your server, when using `scp`, you need to ensure that the user you intend to use to log in to the remote server has access to the files and directories you'll need to access on your remote machine. For instance, if you want to copy a file from a remote server that only the root user has access to, you will need to either:
1. Specify the root user in your `scp` command;
2. `ssh` into the remote server as root and edit the file's permissions and / or move it somewhere accessible to the user you will specify in your `scp` command;
3. `ssh` into the remote server as a user with sudo permissions and edit the file's permissions and / or move it somewhere accessible to the user you will specify in your scp command;
Hopefully, you have disabled remote root login to your server via `ssh` for security reasons, so you will use option three, if necessary, prior to executing your `scp` command.
Using `scp` is very similar to using the `ssh` command. Both tools share some flags, and their commands look similar, the key difference being that `scp` is not interactive; every action you wish to take on your remote machine must be encapsulated in your `scp` command, which is why you must first ensure that the user specified in your `scp` command has access to any files or directories you want to transfer to or from.
## Examples
### Copying from a remote server
Just like you might when logging in via the `ssh` command, if you've changed your default `ssh` port to something other than 22, you'll need to specify it with the `-P` flag; if you're using key-based authentication, you'll also need to specify your key with the `-i` flag:
``` bash
scp -P [port] -i [keyPath] [remoteUser]@[remoteServerNameOrIP]:[filePath] [downloadPath]
```
### Copying to a remote server
When uploading, your command should look like this, with the local file path before your username and server address:
``` bash
scp -P [port] -i [keyPath] [filePath] [remoteUser]@[remoteServerNameOrIP]:[uploadPath]
```
### Other options
Copy the contents of a directory recursively using the `-r` flag:
``` bash
scp -r [remoteUser]@[remoteServerNameOrIP]:[filePath] [downloadPath]
```
Copy a file between two remote hosts directly:
``` bash
scp [remoteUser1]@[remoteServerNameOrIP1]:[filePath] [remoteUser2]@[remoteServerNameOrIP2]:[filePath]
Copy a file between two remote hosts, routing through your local machine:
``` bash
scp -3 [remoteUser1]@[remoteServerNameOrIP1]:[filePath] [remoteUser2]@[remoteServerNameOrIP2]:[filePath]
```
### That's all there is to it.
And there you have it: one simple command to transfer files to and from your remote servers. So long as your remote users have the prerequisite permissions to access the correct remote files and directories, and there's nothing wrong with your `ssh` setup, transferring files with `scp` is a breeze.

View File

@ -1,17 +0,0 @@
---
title: This is my second post with a much longer title.
description: This is a post on My Blog about leveraging agile frameworks.
date: 2018-07-04
tags:
- number 2
---
Leverage agile frameworks to provide a robust synopsis for high level overviews. Iterative approaches to corporate strategy foster collaborative thinking to further the overall value proposition. Organically grow the holistic world view of disruptive innovation via workplace diversity and empowerment.
## Section Header
<a href="/blog/firstpost/">First post</a>
<a href="/blog/thirdpost/">Third post</a>
Bring to the table win-win survival strategies to ensure proactive domination. At the end of the day, going forward, a new normal that has evolved from generation X is on the runway heading towards a streamlined cloud solution. User generated content in real-time will have multiple touchpoints for offshoring.
Capitalize on low hanging fruit to identify a ballpark value added activity to beta test. Override the digital divide with additional clickthroughs from DevOps. Nanotechnology immersion along the information highway will close the loop on focusing solely on the bottom line.

View File

@ -1,45 +0,0 @@
---
title: This is my third post.
description: This is a post on My Blog about win-win survival strategies.
date: 2018-08-24
tags:
- second tag
- posts with two tags
---
Leverage agile frameworks to provide a robust synopsis for high level overviews. Iterative approaches to corporate strategy foster collaborative thinking to further the overall value proposition. Organically grow the holistic world view of disruptive innovation via workplace diversity and empowerment.
## Code
### Styled (with Syntax)
Bring to the table win-win survival strategies to ensure proactive domination. At the end of the day, going forward, a new normal that has evolved from generation X is on the runway heading towards a streamlined cloud solution. User generated content in real-time will have multiple touchpoints for offshoring.
```js
// this is a command
function myCommand() {
let counter = 0;
counter++;
}
// Test with a line break above this line.
console.log('Test');
```
### Unstyled
Bring to the table win-win survival strategies to ensure proactive domination. At the end of the day, going forward, a new normal that has evolved from generation X is on the runway heading towards a streamlined cloud solution. User generated content in real-time will have multiple touchpoints for offshoring.
```
// this is a command
function myCommand() {
let counter = 0;
counter++;
}
// Test with a line break above this line.
console.log('Test');
```
## Section Header
Capitalize on low hanging fruit to identify a ballpark value added activity to beta test. Override the digital divide with additional clickthroughs from DevOps. Nanotechnology immersion along the information highway will close the loop on focusing solely on the bottom line.

View File

@ -16,11 +16,18 @@ permalink: /feed/feed.xml
</author>
{%- for post in collections.posts | reverse %}
{% set absolutePostUrl %}{{ post.url | htmlBaseUrl(metadata.url) }}{% endset %}
{% if post.image-url %}{% set imageURL %}{{ post.image-url | htmlBaseUrl(metadata.url) }}{% endset %}{% endif %}
{% set defaultImageURL %}{{ metadata.defaultPostImageURL | htmlBaseUrl(metadata.url) }}{% endset %}
<entry>
<title>{{ post.data.title }}</title>
<link href="{{ absolutePostUrl }}"/>
<updated>{{ post.date | dateToRfc3339 }}</updated>
<id>{{ absolutePostUrl }}</id>
<image>
<url>{% if post.image-url %}{{ imageURL }}{% else %}{{ defaultImageURL }}{% endif %}</url>
<title>{% if post.image-alt %}{{ post.image-alt }}{% else %}{{ metadata.defaultPostImageAlt }}{% endif %}</title>
<link href="{{ absolutePostUrl }}"/>
</image>
<content type="html">{{ post.templateContent | transformWithHtmlBase(absolutePostUrl, post.url) }}</content>
</entry>
{%- endfor %}

View File

@ -7,15 +7,13 @@ numberOfLatestPostsToShow: 3
---
{% set postsCount = collections.posts | length %}
{% set latestPostsCount = postsCount | min(numberOfLatestPostsToShow) %}
<h1>Latest {{ latestPostsCount }} Post{% if latestPostsCount != 1 %}s{% endif %}</h1>
{% set postslist = collections.posts | head(-1 * numberOfLatestPostsToShow) %}
{% set postslistCounter = postsCount %}
{% include "postslist.njk" %}
{% set morePosts = postsCount - numberOfLatestPostsToShow %}
{% if morePosts > 0 %}
<p>{{ morePosts }} more post{% if morePosts != 1 %}s{% endif %} can be found in <a href="/blog/">the archive</a>.</p>
<p>See {{ morePosts }} more post{% if morePosts != 1 %}s{% endif %} in <a href="/blog/">the blog</a>.</p>
{% endif %}
{# List every content page in the project #}

View File

@ -1,10 +1,14 @@
---
permalink: /tags/
layout: layouts/home.njk
layout: layouts/base.njk
---
<h1>Tags</h1>
<h1>Here are some things I like to talk about.</h1>
<ul>
<p class="page-block nodropcap">
Click on a tag to see all posts on the topic.
</p>
<ul class="taglist">
{% for tag in collections.all | getAllTags | filterTagList %}
{% set tagUrl %}/tags/{{ tag | slugify }}/{% endset %}
<li><a href="{{ tagUrl }}" class="post-tag">{{ tag }}</a></li>

View File

@ -9,14 +9,16 @@ pagination:
- posts
- tagList
addAllPagesToCollections: true
layout: layouts/home.njk
layout: layouts/base.njk
eleventyComputed:
title: Tagged “{{ tag }}”
permalink: /tags/{{ tag | slugify }}/
---
<h1>Tagged “{{ tag }}”</h1>
<h1>More posts about “{{ tag }}.”</h1>
<p class="page-block nodropcap">Here's everything I've posted about {{ tag }}:</p>
{% set postslist = collections[ tag ] %}
{% include "postslist.njk" %}
<p>See <a href="/tags/">all tags</a>.</p>
<p class="nodropcap">See <a href="/tags/">all tags</a>.</p>