Feed

January 2024

How to start a call from the iOS calendar.app

Wed, 24 Jan 2024 06:58:53 +0100

To save a telephone appointment in the iOS calendar so that you can call immediately when you receive a notification, simply enter the telephone number in the format tel:+4912341234567 in the Location field.

Perhaps this also works with other x-callback-url parameters, but I haven't tried that yet.

Reply via e-mail

What is currently making me despair

Sun, 14 Jan 2024 00:19:23 +0100

This is an RSS-only post. It's a secret! Read more about RSS Club.

I live in a German federal state where history is very likely to repeat itself in this year's state elections. And I don't mean the good things. A far-right party called AfD is currently polling at over 30% in the voter surveys here and I don't know what I'll do if they actually win the election. I also don't know what I could do to mobilise against this party. All this Nazi shit here in Germany makes me feel powerless and my inability to act drives my depression to new heights

Reply via e-mail

Add daily notes and capture to do's with two iOS shortcuts for Obsidian

Thu, 11 Jan 2024 22:18:14 +0100

A few days ago, Evan[1] showcased his lock screen with a shortcut to start writing in Drafts. I was so impressed that I decided to create something similar for Obsidian[2]using Advanced URI[3] and Locknload[4].

Then, yesterday, it dawned on me that Obsidian is essentially composed of files and folders. So, I quickly abandoned the previous approach and built a regular iOS shortcut. Okay, I created two. One to generate a daily note or add text to an existing one, and another to update my to-do list.

Add-Daily-Note.shortcut

This simple shortcut appends content to your Daily Note or creates one if it doesn't exist yet. You only need to customize the path to your Daily Notes and adjust the format of the 'Current Date' to match your preferences.

Shortcut settings - Adjust '00 - INBOX' to your needs

Capture-to-do.shortcut

Mijndert[5] wrote today

Now I have a folder in iCloud Drive called 'to-dos' with a sub-folder per year. I create a single note per week in which I write down my to-dos for that particular week. Since the notes are just text files I can open them in any editor (currently Vim or BBedit). If any items didn't get done I move them over to the next note.

Unlike him, I prefer capturing my to-dos in a single Markdown file within Obsidian. I synchronize my vault via iCloud, ensuring that the to-do list is readily accessible from anywhere. To conveniently add new items on the go from my mobile phone, I’ve installed Folder Notes[5] and set up this shortcut.

Shortcut settings - Adjust '__ToDo' and file path to your needs

You can download both shortcuts via the following links:

I'll leave the shortcuts as they are for now and will be happy if they inspire you and you develop them further. There is no question that they can be optimised.

Reply via e-mail

Indieweb: A great idea messed up

Tue, 09 Jan 2024 14:13:09 +0100

I like the basic idea of the Indieweb - Your content is yours, You are better connected, You are in control - and have already tried to integrate my small site into the indiewebring, which failed. And to be honest, after 2 days of trying I didn't feel like it anymore.

Then yesterday I read over at Colin's about the uproar that Giles started and I can only agree. Everything has already been said about WordPress, but I would like to point out once again that the onboarding in Indieweb itself is hard to beat in terms of inconvenience. I mean, do you know their wiki? I have no idea what's still up to date and what's not, which services still work and which don't.

But again, I really like the basic idea and find it worthwhile. The rest should be reconsidered. Thoroughly!

Reply via e-mail

Welcome 2024!

Mon, 01 Jan 2024 00:00:01 +0100

I wish us all a year 2024 in which conflicts are resolved peacefully and it's more about the "we" again and not just everyone thinking about themselves.

Reply via e-mail

December 2023

In Memoriam Lemmy Kilmister

Thu, 28 Dec 2023 19:08:06 +0100

Eight years ago today, I woke up and saw the news of Lemmy's death on my mobile phone. That was the celebrity death that shook me the most up until then and ever since, even if it was foreseeable. So today I'm raising a glass to Ian Fraser Kilmister. Cheers Lem! 🥃

Reply via e-mail

My 2023 in short (or what I especially remember)

Tue, 26 Dec 2023 21:45:04 +0100

In a few days, the year 2023 will come to an end, and I'd like to take a moment to reflect on it from a personal perspective..

As has been the tradition for over 20 years, 2023 commenced musically with U2's “New Year's Day” and everything else unfolded in a familiar fashion. However, it was also the first New Year's Day shadowed by the Russian war in Ukraine, which brought a geopolitical shift close to the European border. On a positive note, COVID was less pervasive than in the two preceding years.

In March, I celebrated my birthday with my wife in Leipzig, where we enjoyed a performance by Henry Rollins at the Felsenkeller. Prior to the event, we had the pleasure of meeting up with a friend from my early days. I thoroughly enjoyed that.

In June, the highlight was meant to be a small open-air festival in my hometown featuring bands like Coogans Bluff and Rotor. However, on the same day, we experienced a car accident. Fortunately, no one was injured, but it left me with some emotional scars, and the car was totaled. The subsequent weeks were consumed by the search for a replacement that could comfortably accommodate a family of four, with a trunk spacious enough for all our vacation luggage. Additionally, it had to accommodate someone almost 2 meters tall. While the new used car meets all these requirements, the loss of its predecessor still lingers in my thoughts. Nevertheless, I enjoyed the concert, which, given the circumstances, may have been thanks to a beer or two 😉.

After having created numerous websites using Datenstrom's Yellow for over a year, I decided in August to start a blog again after almost two decades — and I don't regret it. On one hand, finding content for it is now easier for me; on the other hand, I'm thoroughly enjoying its development by exploring the possibilities of the CMS and experimenting with extensions. I've mentioned before that the fantastic Yellow Community provides invaluable support and serves as a great motivator. 😄

At the end of September, we embarked on our long-awaited family vacation to Denmark. Even after 10 years, it remains an incredibly relaxing and rejuvenating experience due to its serene ambiance. This tranquility is especially pronounced along the North Sea coast of Midtjylland, our declared favorite autumn travel destination for a few years now. Unfortunately, despite, or perhaps because of, the peace and quiet, the “black dog”[1] of depression emerged in its full size and has been a constant companion ever since. Consequently, I’ve been on sick leave since the beginning of October and am slowly but persistently working my way back to a normal life.

Starting the dark season with mental health issues is not particularly pleasant, so November was rather uneventful for me, aside from the occasional walk with friends. Strangely enough, my mood lifted in December to the extent that I accepted a friend's request to try my hand at DJing (with records, of course) for the first time in my life — and it went well. The audience was happy, and, more importantly, my friend was satisfied. However, it also showed me that it's not that easy to entertain people for an entire evening. This experience was somehow beneficial for me. December has brought even more uplifting moments, such as the accidental pen pal story with Colin[2] that unfolded when I came across his hyblog, or the fact that my Anchor Extension is now featured on the official Yellow Extension site[3]. Even Christmas failed to have a negative impact on my mood. Now, one of the last events left is New Year's Eve, which we plan to spend with friends. I look forward to it on one hand, but on the other, I might prefer spending it with the cats because there tends to be quite a lot of noise and shooting here.

Reply via e-mail

Funkypop Yourself

Sun, 24 Dec 2023 22:53:43 +0100

Frank and Colin wrote today about "Funkopop Yourself" and of course I'm always up for such nonsense. 😄

Me as a Funkypop figure

(Looks like the AI isn’t clever enough to simply accept the name entered. 🙄)

Reply via e-mail

I wish you a merry Lemmy Day

Fri, 22 Dec 2023 15:29:54 +0100

Voices - Sometimes they make the difference

Wed, 20 Dec 2023 23:37:09 +0100

You can think what you like about the influence and importance of Soundgarden, but Chris Cornell's voice is unrivalled. I currently have to turn up the volume every time his goosebump-inducing version of Billy Jean appears in my cover playlist on Spotify.

Reply via e-mail

Microblog posts moved to their own page

Tue, 19 Dec 2023 16:55:14 +0100

I've banned the microblog posts from the homepage again and given them a page. There they can do what they want and don't disrupt the flow I wanted to break up with them.

Well, so be it. I have, however, given them a copy-to-clipboard feature on their page, with which you can copy the text and the short URL to your clipboard to share it wherever you like.

Reply via e-mail

1702934854

Mon, 18 Dec 2023 22:27:34 +0100

Have you ever noticed that "Lycos" is phonetically the same as "Social" spoken backwards?

Reply via e-mail

A night at the turntables

Sat, 16 Dec 2023 09:50:39 +0100

Short URLs with YOURLS for Yellow

Thu, 14 Dec 2023 14:39:56 +0100

As you've probably noticed, I thought I'd break up the flow of this blog a bit and put posts in the style of a microblog here in case I just want to get something off my chest that doesn't need a headline. During my research on blogs in general and microblogs in particular, I kept coming across IndieWeb. Among other things, they also recommend short URLs[1] so that there are enough characters left for texts for your posts if they (should) be shared on services such as Bluesky, Mastodon or Foursquare.

And as luck would have it, with kxe.one (KxE means Kurt & Edgar, which are the names of my sons) I have a very short URL behind which I have installed YOURLS to generate short URLs. So far only via the Short Menu[2] app for iOS. So I thought I'd build a YOURLS extension for my CMS and create short URLs for the microblog posts.

But I have to say that I haven't implemented it. It works but no, it has to wait until the microblog posts fit in here visually. So if you know a universal logo for short URLs, please let me know! I'm still not sure how I should display them. 🙄

YOURLS?

YOURLS[3] is short for "Your Own URL Shortener" and that basically says it all. And hell yes, Ozh deserves a prize for this homophonic abbreviation. It’s probably the only usable URL shortener for self-hosting in our solar system. The backend looks a bit old-fashioned, but let's be honest: a) nobody sees it except you and b) you hardly spend any time there. I've been using it for a while now and am completely satisfied, as features that are missing at first can be added via a plugin. If you're interested in the topic, give it a try. It's open source and free.

Shit, I'm raving here as if I'm involved in the project in some way, which of course I'm not. Anyone who knows me can confirm that I am a software user and not a developer.

So what was my approach?

I'm slowly warming up to creating extensions for Yellow and so it was clear to me that I would use the onParseContentShortcut() function to output the short URL to the post. This gives me the best opportunity to place the link in the layout file and also gives me complete freedom with the link name. Depending on which template I use it in (in case I want to extend the short URLs to post categories other than the microblog).

So my extension begins as follows:

<?php
// ShortUrl extension, https://github.com/pftnhr/yellow-shorturl

class YellowShorturl {
    const VERSION = "0.8.19";
    public $yellow;         // access to API

    public function onLoad($yellow) {
        $this->yellow = $yellow;
        $this->yellow->system->setDefault("ShorturlApi", "<your-yourls-host/yourls-api.php");
        $this->yellow->system->setDefault("ShorturlSecret", "<your secret signature from the Tools page of your YOURLS install");
    }

    public function onParseContentShortcut($page, $name, $text, $type) {
        $output = null;
        if ($name=="shorturl" && ($type=="block" || $type=="inline")) {
            list($shorturlLong, $shorturlKeyword, $shorturlTitle) = $this->yellow->toolbox->getTextArguments($text);
            $shorturlLong    = $this->yellow->lookup->normaliseUrl(
                                    $this->yellow->system->get("coreServerScheme"),
                                    $this->yellow->system->get("coreServerAddress"),
                                    $this->yellow->system->get("coreServerBase"),
                                    $page->location);
            $shorturlKeyword = $page->get("keyword");
            $shorturlTitle   = $page->get("title");
            $shorturlApi     = $this->yellow->system->get("ShorturlApi");
            $shorturlSecret  = $this->yellow->system->get("ShorturlSecret");

            $output .= $this->shortenWithYourls($shorturlLong, $shorturlKeyword, $shorturlTitle, $shorturlApi, $shorturlSecret);
        }
        return $output;
    }

The value for $shorturlLong ensures that the URL is passed without the /edit slug, which would appear because you are logged in to Yellow when you write a new article. The function onLoad($yellow) writes the URL to your YOURLS-API and your secret signature token into the yellow-system.ini (you only have to enter the correct values there).

To give the short URLs a custom name, I can set a keyword in the page settings. For example, you can create "https://sho.rt/CustomKey" with the following settings.

---
Title: Post title
Published: @datetime
Author: @username
Keyword: CustomKey
Layout: blog
Tag: Example
---
This is a new blog page.

So far, so simple. The next one took me a while and only worked in the end thanks to the plugin yourls-api-edit-url[4]:

    public function shortenWithYourls($shorturlLong, $shorturlKeyword, $shorturlTitle, $shorturlApi, $shorturlSecret) {
       $shortUrl = null;
       /*
        * YOURLS : sample file showing how to use the API
        * This shows how to tap into your YOURLS install API from *ANOTHER* server
        * not from a file hosted on the same server. It's just a bit dumb to make a
        * remote HTTP request to the server the request originates from.
        *
        * Rename to .php
        *
        */

       // EDIT THIS: your auth parameters
       $signature = $shorturlSecret;

       // EDIT THIS: the query parameters
       $url     = $shorturlLong;            // URL to shrink
       $keyword = $shorturlKeyword;         // custom Keyword (optional)
       $title   = $shorturlTitle;           // title (optional)
       $format  = 'json';                   // output format: 'json', 'xml' or 'simple'

       // EDIT THIS: the URL of the API file
       $api_url = $shorturlApi;


       // Init the CURL session
       $ch = curl_init();
       curl_setopt( $ch, CURLOPT_URL, $api_url );
       curl_setopt( $ch, CURLOPT_HEADER, 0 );            // No header in the result
       curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true ); // Return, do not echo result
       curl_setopt( $ch, CURLOPT_POST, 1 );              // This is a POST request
       curl_setopt( $ch, CURLOPT_POSTFIELDS, array(      // Data to POST
               'url'      => $url,
               'keyword'  => $keyword,
               'title'  => $title,
               'format'   => $format,
               'action'   => 'shorturl',
               'signature' => $signature
           ) );

       // Fetch and return content
       $data = curl_exec($ch);
       curl_close($ch);

       $data = json_decode( $data );

       // Do something with the result. Here, we just echo it.
       if ($data == null) {
            $chGet = curl_init();
            curl_setopt( $chGet, CURLOPT_URL, $api_url );
            curl_setopt( $chGet, CURLOPT_HEADER, 0 );            // No header in the result
            curl_setopt( $chGet, CURLOPT_RETURNTRANSFER, true ); // Return, do not echo result
            curl_setopt( $chGet, CURLOPT_POST, 1 );              // This is a POST request
            curl_setopt( $chGet, CURLOPT_POSTFIELDS, array(      // Data to POST
                  'url'      => $url,
                  'keyword'  => $keyword,
                  'title'  => $title,
                  'format'   => $format,
                  'action'   => 'geturl',
                  'signature' => $signature
              ) );

            // Fetch and return content
            $dataGet = curl_exec($chGet);
            curl_close($chGet);

            $dataGet = json_decode( $dataGet );
            $shortUrl = 'https://kxe.one/'.$dataGet->keyword;

       } else {

           $shortUrl = $data->shorturl;
       }

       return $shortUrl;
    }

The content is mainly based on the sample-Remote-api-call.txt, which is shipped with YOURLS. I adapted it to the passed variables and inserted another cURL session if the JSON array of the first cURL session is empty, which happens if $shorturlLong already exists in the YOURLS database. So the value 'action' => 'geturl' in the second cURL session only works if the plugin yourls-api-edit-url is installed! If anyone has a solution for this without a plugin, please send me an email.

I don't want to go into detail about what didn't work in the meantime because I simply can't put it into words. As it is now, it works and that makes me extremely happy because, as I mentioned at the beginning, I am not a developer.

You can find the complete code as a Yellow Extension on GitHub[5].

Reply via e-mail

2023-12-13 09:34:22

Wed, 13 Dec 2023 09:34:22 +0100

Friday will be my first ever appearance as a DJ. I've narrowed down the genres to stoner, krautrock and punk. Okay, and a bit of Motörhead and Fun Lovin' Criminals.

Reply via e-mail

A simple approach to categories in Yellow

Tue, 12 Dec 2023 09:08:13 +0100

TL;DR:

Originally, I used the Author: setting in Yellow for categories, but it wasn't ideal. When seeking a way to integrate a microblog into my site, I considered using a second CMS. However, a simpler solution struck me: creating a Category: setting to filter and display articles chronologically on the start page with different layouts. I defined layouts for "blog," "image," and "micro" categories.

What's it about?

I had missed categories in Yellow some time ago and used the page setting Author: to help me. That wasn't ideal, of course, but it was enough at the time. Now I have a different approach, which somehow seems too simple to be true. But first things first.

My intention

I was looking for a way to integrate a kind of micro blog into my blog. Nothing dramatic, just a different formatting of the articles. In my head, it started with thinking about how I could integrate a second, simpler CMS into Yellow so that the articles would appear chronologically in a timeline. Then, as happens quickly with ADHD people, I spent a lot of time trying to find a suitable second CMS, while my brain continued to research alternatives to this approach in the back of my mind.

The spark

And suddenly this one thought came. At first it was only vague, but it became increasingly clear until it hit me with a certain force that made me grin: I simply give the articles another page setting Category:, according to which I can easily filter in blog-start.html via if ($page->get("Category") == "categoryName") and then give the different categories their individual layout on the start page.

It currently looks like this in my blog-start.html:

<?php if ($page->get("Category") == "blog") { ?>

<article class="<?php echo $page->getHtml("entryClass") ?>">
    <header>
        <h2><?php echo $page->getHtml("title") ?></h2>
        <p>
            <?php echo $page->getDateHtml("published") ?>            
            <?php echo " &middot; " . $page->parseContentShortcut("readingtime", "", "inline") . " min"; ?>
            <?php if ($page->isExisting("tag")): ?>
            <?php echo " &middot; Filed under: " ?>
            <?php $tagCounter = 0; foreach (preg_split("/\s*,\s*/", $page->get("tag")) as $tag) { if (++$tagCounter>1) echo ", "; echo "<a href=\"".$this->yellow->page->getLocation(true).$this->yellow->toolbox->normaliseArguments("tag:$tag")."\" class=\"tag\" title=\"Tag: ".htmlspecialchars($tag)."\" aria-label=\"Tag: ".htmlspecialchars($tag)."\">".htmlspecialchars($tag)."</a>"; } ?>
            <?php endif ?></p>
    </header>
    <p>
        <?php echo $this->yellow->toolbox->createTextDescription($page->getContentHtml(), 350, true, "<!--more-->", " ") ?>
    </p>
    <a class="entry-link" title="<?php echo $page->getHtml("title") ?>" aria-label="<?php echo $page->getHtml("title") ?>" href="<?php echo $page->getLocation(true) ?>"> </a>
</article>

<?php } elseif ($page->get("Category") == "image") { 
    $page->set("entryClass", "post-image") ?>
    <article class="<?php echo $page->getHtml("entryClass") ?>">
        <header>
            <h2><?php echo $page->getHtml("title") ?></h2>
            <p>
                <?php echo $page->getDateHtml("published") ?>            
                <?php echo " &middot; " . $page->parseContentShortcut("readingtime", "", "inline") . " min"; ?>
                <?php if ($page->isExisting("tag")): ?>
                <?php echo " &middot; Filed under: " ?>
                <?php $tagCounter = 0; foreach (preg_split("/\s*,\s*/", $page->get("tag")) as $tag) { if (++$tagCounter>1) echo ", "; echo "<a href=\"".$this->yellow->page->getLocation(true).$this->yellow->toolbox->normaliseArguments("tag:$tag")."\" class=\"tag\" title=\"Tag: ".htmlspecialchars($tag)."\" aria-label=\"Tag: ".htmlspecialchars($tag)."\">".htmlspecialchars($tag)."</a>"; } ?>
                <?php endif ?></p>
        </header>
        <p>
            <?php echo $page->getContentHtml() ?>
        </p>
        <a class="entry-link" title="<?php echo $page->getHtml("title") ?>" aria-label="<?php echo $page->getHtml("title") ?>" href="<?php echo $page->getLocation(true) ?>"> </a>
    </article>

<?php } elseif ($page->get("Category") == "micro") { 
    $page->set("entryClass", "post-micro") ?>
    <div class="<?php echo $page->getHtml("entryClass") ?>">
        <?php echo $page->getContentHtml() ?>

        <p class="entryMeta">
            <a title="Permalink to this post" aria-label="<?php echo $page->getHtml("title") ?>" href="<?php echo $page->getUrl(true) ?>">#</a> <?php echo $page->getDateHtml("published") ?>  
        </p>
    </div>
<?php } ?>

Firstly, there is my "blog" category. It is output as <article> with <header> and displays the content as an excerpt, limited to 350 characters. The "image " category differs from "blog " only in that it does not output an excerpt, but the unfiltered content of $page->getContentHtml, as I only post a photo with caption in this category. And, last but not least, the category "micro ". Articles with this category are not <article> and therefore have a completely different appearance. (As written here, I am still looking for a design for the micro posts and am open to all suggestions.)

Conclusion

That's it. It's often so easy to achieve what you want. It just seems important not to close your mind to alternatives and to give your brain space to come up with these alternatives.

Reply via e-mail

Blessing in disguise

Tue, 12 Dec 2023 06:36:30 +0100

I recently implemented a simple category solution in the development clone of this blog. I wanted to use it to create a kind of micro blog and, surprisingly, it worked very well. Anyway, I've had another idea for the last few days that I want to implement and now guess what I completely forgot and deleted yesterday... 🙄

But luckily, due to another idiotic mistake on my part, I haven't emptied the recycle bin yet.

PS:. Incidentally, I'm struggling with the design of the micro blog articles here. If you have an idea, drop me a line.

PPS.: See what I did in the next post.

PPPS.: This is the HTML structure of the micro blog posts

<div class="post-micro">
    <p>This is a micro blog example</p>

    <p class="entryMeta">
        <a title="Permalink to this post" aria-label="Micro blog post from 2023-12-11" href="https://path/to/post">#</a> 2023-12-11
    </p>
</div>

Reply via e-mail

Holiday memory

Mon, 11 Dec 2023 14:08:07 +0100

A weird story about how sometimes one thing leads to another

Wed, 06 Dec 2023 11:20:09 +0100

Short Introduction

Since August, I've been back to blogging and this time, it feels much better than my first attempt a decade ago. Back then, I delved into blogging during my studies in the early/mid-2000s, just before the WordPress boom. My journey began with WordPress Version 1.2[1], marking my entry into the community. I joined WordPress Deutschland (WPDE)[2], won a book in a theme contest, became a WPDE forum moderator, co-founded Inpsyde[3], and eventually burned out.

Transition

A significant hiatus followed, mentioned on the start page. I withdrew from web development, focused on myself, met my wife, and ended up in a job that partly relied on my experience with HTML, CSS, PHP & Co. The websites I semi-editorially oversee run on Typo3[4] (expressing my strong dislike for Typo3 is challenging). Hence, I offered to create a presentation website. That was fun, especially collaborating with a designer. I had nothing to do with the design, so I finally got to let loose with JavaScript and some wild CSS stuff. In short, I'm back in the game. I created this blog based on Yellow, shamelessly borrowing the design from the PaperMod[5] theme for Hugo, and have been tinkering with it since August. Phew, I feel like the beginning is longer than the crux of the matter...

Transition Part 2

With frittiert.es[6], newsfeeds came back into focus (recently reported[7]). These charming, somewhat nerdy things alert subscribers (like you?[8]) to updates on the subscribed website, not as intrusive as modern push notifications. I'd like to call newsfeeds “almost analog,” but maybe another time. Back to the topic: Reading Evan Travers'[9] feed led me to a “secret” circle, and the App Defaults[10] party dumped its OPML into my reader. From that illustrious group, I subscribed to a few, including a certain Kev Quirk, who mentioned hyblog by Colin Walker[11] in a post[12]. Ha! That's exactly where I wanted my rambling to lead. 😁

Conscience Interjection

"...he said, took a deep breath, and the next torrent prepared itself in his mind."

– The voice in my head

The Crux of the Matter

For something diary-like meant to be non-public online, I was looking for something even leaner than Yellow, and hyblog[13] seemed like the right fit. It even has on-page editing. I installed and tried it. Unfortunately, I immediately encountered a bug. I reported an issue[14] to Colin via GitHub, and he promptly responded. Excitement spread. Unfortunately, shortly after, another problem popped up. So, Colin and I exchanged messages on GitHub for a while before I chose a somewhat unorthodox approach: I asked ChatGPT, after narrowing down the code causing the issue, where the problem might lie. Well, now it's running as intended, and Colin and I have accidentally become pen pals. (You can read about the pen pal thing at Kev's post[15].)

Epilogue

You might wonder why you had to read the detailed backstory, and the ending came so suddenly. Well, first, I'm currently dealing with depression, and I'm a bit proud that I took the time to write all this. Also, I suspect ADHD plays a role for me. In any case, thank you for making it this far, and I'd appreciate tips on how to improve the next post or the one after.

Reply via e-mail

Introducing my Hello page

Tue, 05 Dec 2023 09:48:30 +0100

Some of you have probably already seen them on one website or another: The Hello page.

Alastair Johnston had the simple idea of creating a page on which your favourite contact options are listed.

A kind of 'rules of engagement'. That way, anyone could get in touch with me, and vice versa, without needing to rely on algorithms, or guesswork as to which platform would be best.

— Alastair Johnston[1]

As I really like the idea, I decided to create a Hello page myself. I'm not sure yet whether I'll keep all the linked icons on the start page or whether I'll just leave the ones that don't allow direct communication there. How would you handle this? Feel free to hit the "Reply via email" link below.

Edit: After I had set up the hello page, I realised that I basically already had it on the start page. So I adjusted the text there and removed the hello page again. Redundancy is sometimes not a bad thing, but it's completely unnecessary here.

Reply via e-mail

How to stop your website from overscrolling

Sun, 03 Dec 2023 00:01:46 +0100

Have you ever been annoyed by the fact that your website scrolls up and down beyond the viewport and bounces back? Put the following code in your stylesheet and you‘ll be satisfied.

:root {
    overscroll-behavior: none;
}

But be sure you have no pull-to-refresh action.

Reply via e-mail

November 2023

Comment on „Cookie Permissions 101“

Thu, 30 Nov 2023 16:22:53 +0100

This is an RSS-only post. It's a secret! Read more about RSS Club.

I stumbled upon an article on nngroup.com, which states in its conclusion:

Cookie-permission overlays shouldn’t be only a compliance requirement. They are an opportunity to build trust […]

However, the website itself does not give the user the option of rejecting cookies...

How glad I am that I don't have to offer you such options.

Reply via e-mail

Custom newsfeed with web scraping

Tue, 28 Nov 2023 09:55:13 +0100

TL;DR

To address limitations in my flat-file CMS (Yellow), I implemented web scraping to customize the RSS feed on my website. The CMS, saving content in Markdown, excluded layout-added details from the feed. A lengthy feed URL also posed aesthetic concerns. To overcome these, I created a dedicated page layout for feed articles, designed to match RSS 2.0 data. Leveraging cURL and DOMDocument, I fetched and parsed HTML, extracting key elements. The script then transformed the data into an XML RSS feed. Caching was employed for efficiency, reducing the frequency of web scraping. This approach provides a personalized and aesthetically pleasing RSS feed, showcasing web scraping's efficacy for content customization and optimization.

The problem

My content management system (Yellow) is a flat-file CMS and as such it saves the content in Markdown files, from which it then also creates the newsfeed. Unfortunately, this also means that content that I add using the layout files (e.g. the "comment by e-mail" link or the date of the last edit) is not included in the feed. Apart from that, I could not reconcile the feed URL (/feed/page:feed.xml), which in my eyes is far too long, with my aesthetic conscience. Since these things are important to me, I had to find another solution.

The solution

As I'm not an experienced programmer, I quickly discarded the attempt to "hack" into the markdown files and remembered an older project of mine in which I created a newsfeed for a site without RSS support using web scraping.

The preparation

Yellow creates a page[^1] with the yellow-feed extension on which all articles for the feed are displayed. By default, only the titles with a link are listed there in an <ul>. I then had to design this page in such a way that all the data required for an RSS 2.0 feed was displayed. I added classes to this data, as this makes subsequent web scraping much easier. Now that the data was available, it was time to turn the HTML output into rss.xml, which is then accepted by common feed readers.

The implementation

So I wrote a PHP script, with which I perform web scraping on the "https://frittiert.es/feed/" page to generate the custom RSS feed for my website, focusing on the content within certain HTML elements.

Fetching and Parsing HTML

I use cURL to fetch the content from the specified feed URL and then utilize the DOMDocument and DOMXPath classes to extract relevant information.

$feedUrl = "https://frittiert.es/feed/";

$ch = curl_init();
curl_setopt($ch, CURLOPT_AUTOREFERER, TRUE);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_URL, $feedUrl);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, TRUE);
curl_setopt($ch, CURLOPT_PROXY, "");
$data = curl_exec($ch);
curl_close($ch);

$dom = new DOMDocument();
$loadSuccess = @$dom->loadHTML($data);

// Handle errors if HTML loading fails
if (!$loadSuccess) {
    // ... Handle errors ...
    die("Error loading HTML document");
}

XPath Queries

XPath queries are employed to select specific elements from the HTML structure, such as article dates, links, titles, and content.

$newsfeedDate  = $xpath->query("//article/p[@class='feedDate']");
$newsfeedLink  = $xpath->query("//article/h2[@class='feedTitle']/a[@class='feedLink']/@href"); 
$newsfeedTitle = $xpath->query("//article/h2[@class='feedTitle']");
$newsfeedContent = $xpath->query("//article/div[@class='articleContent']");

Creating the RSS Feed

The script then constructs an XML document representing an RSS feed using DOMDocument.

$xml = new DOMDocument("1.0", "utf-8");
$xml->formatOutput = true;

$domStyle = $xml->createProcessingInstruction("xml-stylesheet", "href='/pretty-feed-v3.xsl' type='text/xsl'");
$xml->appendChild($domStyle);

$rss = $xml->createElement("rss");
$rss->setAttribute("version", "2.0");
$rss->setAttribute("xmlns:atom", "http://www.w3.org/2005/Atom");
$rss->setAttribute("xmlns:dc", "http://purl.org/dc/elements/1.1/");
$xml->appendChild($rss);

$channel = $xml->createElement("channel");
$rss->appendChild($channel); 

// Head des Feeds    
$head = $xml->createElement("title", "frittiert.es");
$channel->appendChild($head);

$head = $xml->createElement("description", "It's all about the web here, at least that's what I'm trying to do. From opinions and practical guides to development projects and web technologies.");
$channel->appendChild($head);

$head = $xml->createElement("language", "en");
$channel->appendChild($head);

$head = $xml->createElement("atom:link");
$head->setAttribute("href", "https://frittiert.es/rss.xml");
$head->setAttribute("rel", "self");
$head->setAttribute("type", "application/rss+xml");
$channel->appendChild($head);

$head = $xml->createElement("link", "https://frittiert.es");
$channel->appendChild($head);

//Create RFC822 Date format to comply with RFC822
$date_f = date("D, d M Y H:i:s O", time());
$build_date = gmdate(DATE_RFC2822, strtotime($date_f));

$head = $xml->createElement("lastBuildDate", $build_date);
$channel->appendChild($head); 

The fetched data is looped through, and individual items (posts) are added to the RSS feed.

for ($i = 0; $i < count($newsfeedTitle); $i++) {
    $item = $xml->createElement('item');
    $channel->appendChild($item);

    $data = $xml->createElement('title', $newsfeedTitle[$i]->nodeValue);
    $item->appendChild($data);

    $data = $xml->createElement('description');
    $cdata = $xml->createCDATASection($newsfeedContentHtml[$i]);
    $data->appendChild($cdata);
    $item->appendChild($data);  

    $data = $xml->createElement('link', $newsfeedLink[$i]->nodeValue);
    $item->appendChild($data);

    $data = $xml->createElement('pubDate', $newsfeedDate[$i]->nodeValue);
    $item->appendChild($data);

    $data = $xml->createElement('guid', $newsfeedLink[$i]->nodeValue);
    $item->appendChild($data);
}

Caching

To improve performance, I implement caching. If a cached XML file exists and is not expired, I send it directly to the client.

$cacheFile = 'rss.xml';
$cacheTime = 1200; // Cache for 20 minutes

if (file_exists($cacheFile) && time() - filemtime($cacheFile) < $cacheTime) {
    header('Content-Type: application/rss+xml');
    readfile($cacheFile);
    exit();
}

If the cache is not valid or does not exist, the script proceeds with the web scraping process, generates a new XML file, and saves it for future use.

// Save the newly generated XML file in the cache
$xml->save($cacheFile);

// Send the XML file to the client
header('Content-Type: application/rss+xml');
readfile($cacheFile);

By employing caching, the script reduces the frequency of web scraping, improving the overall efficiency of the RSS feed generation.

The summary

In this PHP script, I leverage web scraping on "https://frittiert.es/feed/" to tailor the RSS feed representation to my preferences. By employing cURL, DOMDocument, and DOMXPath, I extract specific content from the HTML structure. This allows me to customize the feed's appearance by selecting and organizing elements such as dates, links, titles, and content. Additionally, I implement an efficient caching mechanism to optimize performance, reducing the need for frequent web scraping. This approach not only ensures a personalized presentation of the RSS feed on my website but also highlights the effectiveness of web scraping as a valuable tool for content customization and optimization.

Reply via e-mail

Feed now works the way I want it to

Fri, 24 Nov 2023 17:55:24 +0100

This is an RSS-only post. It's a secret! Read more about RSS Club.

Fixed a feed issue so that it now contains everything I consider important. The feed now has a new URL: https://frittiert.es/rss.xml but I've set up a redirect 301 that takes care of that so you don't necessarily have to update the URL.

Reply via e-mail

Just a little test

Mon, 20 Nov 2023 12:34:25 +0100

This is an RSS-only post. It's a secret! Read more about RSS Club.

I have written an extension that should automatically add the "RSS Only" text to the post content and now I want to test if it works.

Edit: Apparently it only works in the browser, but not in the newsfeed. I'm working on a solution!

Reply via e-mail

Crafting the Web: Where CMS and SSG unite

Sat, 18 Nov 2023 20:47:27 +0100

Do you remember the early 2010s, just before WordPress skyrocketed? Back then, Movable Type[1] was undisputedly the number one blogging system. A position it carelessly lost in 2004 with the introduction of a paid license in version 3.0, driving users towards the open-source competitor, WordPress. Okay, it probably has something to do with Movable Type being written in Perl while most web hosts support PHP.

Why am I putting this in writing? Because Movable Type wasn’t just a pure content management system but also a static site generator[2]. And this is where it gets interesting because static site generators are currently very popular. Examples include Hugo[3], Jekyll[4] or Eleventy[5], to name a few. Unlike Movable Type, they are purely static site generators that don’t function as standalone CMS.

And here comes my small yet exquisite favorite CMS into play: Yellow[6]. Yellow’s zipped installation files are not even 400 kB in size. Yet, they enable the installation of blogs, wikis, or a small website (whatever you choose; blog and wiki are also available as extensions for later installation), allowing you to get started right away. If you want to use Yellow as a static site generator, you can install the 5 kB yellow-generate[7] extension and immediately

You can generate a static website at the command line.

And for this, your server only needs PHP. No nodeJS, Docker, or golang, just PHP. Let’s be honest, dear community: It’s refreshing to not need a framework, not depend on software whose instructions to fix security issues are almost as long as the Wikipedia article describing them, or have to learn the x-th C/C++ clone.

But that’s not the end of the (repeated) praise. No, the developer community, including the maintainers, is incredibly approachable, respectful, and open to improvements. While the documentation could include more examples, if the only drawback is having to occasionally ask a question in GitHub Discussions[8], I gladly accept that.

As I’ve done before, I won’t delve into details, thinking that, even though I might be a nerd who can barely program, the provided links are sufficient to address emerging questions. I’d probably hate myself for this move, but I just wrote and published this entire thing in the bathtub.

Reply via e-mail

Mental illness is not a personal failure

Thu, 16 Nov 2023 21:58:06 +0100

This is an RSS-only post. It's a secret! Read more about RSS Club.

I'm struggling with mental health problems. It has built up over the last few years and five weeks ago I pulled the ripcord and went to the doctor, who put me on sick leave straight away.

Today was the first good day I've had since then. I went for a long walk with a friend and then my mum told me that I had practically grown up without a father, although he was always there. This sentence made me suddenly realise that I am a better father than I had previously thought.

The doubts aren't completely gone, but I'm confident that I'll get it together. Also because I have an appointment with my psychiatrist in about a month.

I will continue to try to go for a walk at least once a week for a few kilometres as I notice that this does me good in the long term. I'm also really enjoying working on the website. Probably because I have no pressure from outside.

I hope this doesn't bore you, because it's also good for me to write it out.

Reply via e-mail

Tracking-free since '23

Mon, 13 Nov 2023 21:59:05 +0100

Dear Sir or Madam, I am pleased to inform you that there are no cookies, trackers or anything like that on this website.

That's it, thank you for your attention and have a nice day.

Reply via e-mail

App Defaults

Mon, 13 Nov 2023 11:06:43 +0100

The year is drawing to a close and a not so uninteresting list is making the rounds among bloggers: App Defaults. The whole thing was triggered by Episode 097 of Hemispheric Views, Duel of the Defaults! An these are mine:

  • Mail Client: Mail.app, Spark
  • Mail Server: Gmail
  • Notes: Obsidian, Drafts
  • To-Do: Reminder.app
  • iPhone Photo Shooting: Camera.app
  • Photo Management: Apple Photos
  • Calendar: Google Calendar via Calendar.app
  • Cloud File Storage: iCloud, Nextcloud (selfhosted)
  • RSS service: FreshRSS (selfhosted)
  • RSS client: Reeder
  • Contacts: Contacts.app
  • Browser: Safari, Chrome
  • Chat: WhatsApp, Telegram, Signal, Discord
  • Bookmarks: LinkAce (selfhosted)
  • Read It Later: Reeder
  • Word Processing: Word
  • Spreadsheets: Excel
  • Presentations: Powerpoint
  • Shopping Lists: Bring!
  • Meal Planning: Not needed, the children only eat pasta with ketchup
  • Budgeting and Personal Finance: Banking App
  • News: RND
  • Music: Spotify
  • Podcasts: Podcasts.app, Spotify
  • Password Management: iCloud Keychain, 1Password

Additional categories:

Reply via e-mail

Readme - The easy way

Tue, 07 Nov 2023 11:12:23 +0100

Who doesn't know it? You've created a tool, programme or a cool code snippet and want to make it available to the general public and then platforms like GitHub and the users want a README... Fortunately, I recently stumbled across readme.so.

Reply via e-mail

Back to top