Skip to content


Xcode and iOS apps tips & tricks for beginners continued (part 4)

How to reset the DNS cache
Okay, not really specific to development, but I’ve had issues with my WiFi (thank you Mac for not providing Ethernet…) which lead my DNS cache to mistakenly record DNS lookup failures.
The latest command (run it as su or with sudo) is killall -HUP mDNSResponder, on today’s Mac OS 10.11.2. For commands for other versions, see this help page from Apple.

How to create an unwind segue
Detailed guide there with picture. Short summary:

  • In the view that will receive the segue, crate a function -(IBAction)unwindToSomePlace:(UIStoryboardSegue *)segue {...} (Objective-C) or @IBAction func unwindToSomePlace(segue: UIStoryboardSegue) {...} (Swift)
  • In the view that will perform the segue, CTRL+click the view controller and drag it to the Exit (of the same view). Xcode will then show you a list of all unwind segues on the storyboard and you can pick unwindToSomePlace
  • Give an identifier to the segue this added to your storyboard (the easiest place to find it is probably in document outline of the view that will perform the segue), for instance name it “unwindToSomePlaceSegue”
  • To perform the segue: [self performSegueWithIdentifier:@"unwindToSomePlaceSegue" sender:self] (Objective-C) or self.performSegueWithIdentifier("unwindToSomePlaceSegue", sender:self) (Swift)

Posted in published drafts, Xcode.


How to modify date and time of old commits in Git

Sorry if this post is a bit abrupt / lacks details, it was really written as a quick memo for myself. You will find plenty more details in the sources listed at the bottom of the post (although most of them are simply what shows up first in DuckDuckGo when you search for how to edit old commits).

For this example, we will edit the last 10 commits:

git rebase --interactive HEAD~10

NB: if you’re using SourceTree, you can open a console to Git via the Menu: Actions => Open in Terminal

This will open a text editor with a listing of commits to process, like:

edit e1186e8 Oldest commit
edit 830e58c ANother commit
edit 9d41af7 Latest commit description

By default on Windows, for me the editor was that HORRIBLE shit called Vim. To start editing the text, press “Insert” key. Change the pick keyword to edit for the commits you want to modify. When you’re done, to save and quit press “Escape” (this leaves text editing mode), then type “:wq” and hit “Enter”.

Rebase will then process, telling you “You can amend the commit now”. To change the date without opening an editor (the editor would allow you to edit the commit text I think), use:

git commit --amend --date="20160101T12:12:12" --no-edit

(NB: for supported date formats, see here)

If you’re happy with your change, you can go on to the next commit with

git rebase --continue

Once you’ve run this command on the last commit, your branch will be updated (“Successfully rebased and updated refs/heads/master.”). If you screw up, git rebase --abort should revert all your changes (which seem to only get applied once you’re done with the last commit).

Last but not least, there seems to be a better way to edit the date, using something like this:

GIT_COMMITTER_DATE="20160101T12:12:12" git commit --amend

But I haven’t tried this yet.

A bunch of sources that helped (even though they all had big flaws that still made this a headache):
https://stackoverflow.com/questions/3042437/change-commit-author-at-one-specific-commit
https://stackoverflow.com/questions/9110310/update-git-commit-author-date-when-amending
https://gist.github.com/maciej/5875828
https://help.github.com/articles/about-git-rebase/
https://stackoverflow.com/questions/454734/how-can-one-change-the-timestamp-of-an-old-commit-in-git

Squashing commits with rebase

PS: a little bonus: to “uncommit” the last commit (this cancels the commit but leaves the commit + the content of the commit is still selected and ready to be committed again): reset --soft HEAD^

PS2: if you try to run rebase while still having uncommitted changes, you’ll get an error saying “Cannot pull with rebase: You have unstaged changes. Please commit or stash them.” As the message says, you can commit your changes to bypass this, but if you don’t want to commit, you can run “git stash”, then do the rebase procedure, then at the end run “git stash pop”. Cf this stackoverflow post.

Posted in programming.


Migrating from Gallery3 to Piwigo

As I posted a while back, they killed Gallery3. And finally I found the time to move (also I was getting annoyed of restraining myself from posting more pictures on the outdated software). Here are some pointers I would have liked to have for the migration :

First, in gallery3 it was okay to drop pictures in no album, in the root folder. In Piwigo, I believe it is NOT possible, and at least the migration script will ignore pictures left in the root folder. So before you make the switch, you should create a new album and put the pictures from the root folder into it. Or just take the time to finally sort those pictures 😉
While you’re there, check if you don’t have picture file names with quotes in them, as Piwigo doesn’t support that (and anyway you just shouldn’t use that kind of characters in file names). From the comments, it looks like ampersand (&) is bad voodoo too.

After that, it’s mostly straightforward:
– download Piwigo. I used the “package” but I suppose the “webinstall” works just as well
– install it following the instructions there
– once Piwigo is installed go to the administration panel, then to Plugins > Manage, then to the “Other plugins available” tab and install the plugin “Menalto2Piwigo”
– once Menalto2Piwigo is installed, go to Plugins > Menalto2Piwigo, and follow the instructions:
* step 1) Copy the content of g2data/albums (Gallery2) or var/albums (Gallery3) into [your piwigo folder]/galleries
* step 2) Go to Tools > Synchronize. At this step, you can have an error “PWG-UPDATE-1” when trying to import some files. It is caused by “forbidden” characters in file names, like spaces. Fortunately, the list of allowed characters can be configured: to do so, copy include/config_default.inc.php as local/config/config.inc.php, then locate $conf['sync_chars_regex']. It’s a regex of allowed characters, just add there the characters you need. For instance, I just needed to add spaces so my new line is $conf['sync_chars_regex'] = '/^[a-zA-Z0-9-_. ]+$/';. See this forum thread if you need more details.
* step 3) Submit the form on the page: means obviously you need to put the parameters of your gallery3 database in there (screenshot)
* step 4) Install and activate plugins Extended Description: I’m not sure how useful it is but I did it anyway and it doesn’t seem to do any harm.

And voilà, migration complete, although you might want to check for corrupted characters if your database encodings don’t match (my Gallery3 was non-UTF-8 while my Piwigo is UTF-8, I lost quite a few French characters in the move :().

A little complement now: I had some ads and stat tracking (Piwik) code on my Gallery 3, here is how I added them back. Also by default Piwigo will expose your e-mail address on your gallery, not cool so we’ll deal with that too.

I put my ad code in the header, and actually I didn’t need to edit code for these as I found the subheader was a decent place to put them. In the admin panel, go to Configuration > Options, and then in the first tab you have a “Page banner” section where you can put HTML code, like:
<iframe data-aa='348' src='//ad.a-ads.com/348' scrolling='no' style='width:320px;height:50px;border:0px;padding:0;overflow:hidden;margin-left:20px;' allowtransparency='true'></iframe>
<iframe data-aa='348' src='//ad.a-ads.com/348' scrolling='no' style='width:320px;height:50px;border:0px;padding:0;overflow:hidden' allowtransparency='true'></iframe>

If you prefer to move it around a bit, you can probably find a good fit placing your ad code directly inside themes/default/template/header.tpl.

I put my stat tracking code in the footer, and that’s where the e-mail address is too. The file to edit is themes/default/template/footer.tpl.
For the e-mail, locate the section with “if isset($CONTACT_MAIL)”, and comment the block you want to remove with {* (that’s the comment start) and *} (that’s the comment end). NB: note the comment above saying you shouldn’t remove the “Powered by Piwigo” part, so make sure you don’t comment out too much!
For the stat tracking, you know the drill: just before </body>, as usual, so you can put your code just a few lines above the end of the file.
Last note: we edited the default theme, which means if you configure your gallery to use another theme, well you need to edit that theme instead.

…and that’s all, congrats on moving! I must say I found gallery3 smoother (even though I’m definitely not a design addict), but sticking to unmaintained software is bad, so…

Update: many customizations can be done using plugins

@plegall on Twitter pointed out that most of my above customizations can be done without touching the code but by using plugins:
– to remove the e-mail in footer, plugins “Contact Form” + “Protect Notification”
– to add Piwik or *cough* Google Analytics stats, plugin “Statistics”
– to add the regex to the config file, plugin “LocalFiles Editor”, and also note that, of course, you can limit the content of your modified config.inc.php to the values that you did modify

Update (2018-07-24): where is the database configuration stored?

I’m currently migrating to a new server, and one thing that drove me crazy is that I spent an indecent amount of time looking for my Piwigo database password (I use random passwords that are unique per-application and that I don’t store anywhere, except where it’s required in the application). The Piwigo forums were not helpful, possibly because the search is, let’s say, hard to get working right 😀 So in the end I just downloaded all my Piwigo installation and opened it locally in Visual Studio Code, which provide a nice search that made it easy to find where my database name was stored, which, obviously, was kind of next to the password.

Long story short, the database credentials are stored in local/config/database.inc.php. Why the hell the obvious “config” folder was hidden away in the goddamned “local” folder is beyond my understanding. But at least now I’ll know it’s there.

Posted in multimedia, open source.


Xcode and iOS apps tips & tricks for beginners (part 4)

(the previous part 3 is there)

How to create a binary tagged “only for iPhone” (or only for iPad)
It’s used to be a pretty user-friendly Targets > Summary > Devices in the project settings, and then you could choose between iPhone, iPad or Universal (the latter meaning both iPhone and iPad).
For some reason, they changed that, and as of Xcode 7.2, this can be configured in your project main file (project.pbxproj), in Build Settings > Deployment > Targeted Device Family. That’s still easy to find, but the tricky part is that now Xcode only show the code number of the device. So, “1” means iPhone, “2” means iPad, and of course “1,2” means both/universal. In the pbxproj file, this is stored in the TARGETED_DEVICE_FAMILY variable.
Of course I could have just tried both to see in what case my binary would by for iPhone or iPad, but thanks to this page for giving away the meaning of the numbers.

How to add users to Testflight internal testing
External testing is simple enough, just add users by e-mail. Internal testing is more constrained, as you can only invite your team members to it. It’s actually pretty easy, but there’s a catch which resides in the definition of team member. It turns out there are 2 distinct teams: Apple Developer Program and iTunes Connect. The former is managed from https://developer.apple.com (then click on “People” in the menu). The latter is managed from https://itunesconnect.apple.com/WebObjects/iTunesConnect.woa/ra/ng/users_roles.
The team where you want to add people for internal Testflight testing is the iTunes Connect one, the other doesn’t matter for this. According to the Apple help, only members with some specific roles can be added as internal testers, personally I configured all of them as “Developer”, even though they aren’t really developers. The Apple help on roles is there, but as of today it doesn’t perfectly match the roles that can be assigned… Once people are added to iTunes Connect, they can be added to internal testing.

Posted in published drafts, Xcode.


More Xcode and iOS apps tips & tricks for beginners (part 3)

Well, many unclear things in Xcode, particularly to package and SIGN an app build. So in continuation of the previous posts with tips and tricks for newbies, here are some more.

How to take a screenshot
The very intuitive shortcut is Command + Shift + 3. The screenshot is then silently saved as a PNG on your desktop.

How to enable Product > Archive
The Apple help says somewhere “Archive your product for submission to iTunes Connect or for sharing with others”. They say you first need to configure archiving in your scheme, but the described way to reach the Scheme editor is a bit outdated: now (Xcode 7.2) it’s Product > Scheme > Edit scheme. Anyway, in there go to “Archive” and give a name to your archive, as they say.
The problem is, even after doing that, Product > Archive was still disabled/greyed out. As it turned out, that was because the scheme destination was a simulator instead of “Generic iOS Device”. This is configured near the “play” and “stop” buttons at the top of the screen, where you usually switch between simulators. See below:
Xcode pick scheme target Generic iOS Device
Once the target is properly set to this, Product > Archive should be enabled.

How to change the build and version numbers
The only tricky part here is to FIND where this is. They are located in the Info.plist file of your project. The version is called “Bundle versions string, short” and the build number is called “Bundle version”.

How to get Xcode to automatically fix your signing certificates
I’ll just relate my experience here, as fixing the signing certificates and provisioning profiles seems to have as many solutions as people with the problem. In my case, another developer had produced and built the initial version of the application, and then I took over the project, with the provisioning profiles but the the certificates that go with them.

After finally managing to unlock the damned “Product > Archive” menu item, I went ahead and clicked it. I ended up, at first, with a pretty confusing error message, saying: "Code Sign error: No matching provisioning profile found: Your build settings specify a provisioning profile with the UUID “8cda5448-xxxx-xxxx-xxxx-35c991b050b0”, however, no such provisioning profile was found". Searching for this, I got many “solutions” saying to copy ~/Library/MobileDevice/ (or ~/Library/MobileDevice/Provisioning \Profiles/) to various other locations, notably just /Library/MobileDevice/ (sudo required, of course). None of these worked for me, but one mentioned the fact that this error message could be caused by a missing certificate (say, if the provisioning profile is found but the corresponding certificate is missing, you still get that error message, how clear!).

By the way, you can find the location of your provisioning profiles by going to Xcode > Preferences > Accounts, then pick your team and View details, then you can “Download all” (that button apparently replaced the previous “refresh” button), and also right-click on any provisioning profile and choose “Show in Finder”. Yay! (see tip below to display the full path in Finder)

I was preparing myself to just create a hefty load of brand new provisioning profiles, but first I gave a shot to Product > Build For… > Running. And well, it got an error too, but unlike “Archive”, it explained that I should either import the previous developer’s certificate that matched the provisioning profile, or reset it, and it gave me the option to do the latter automatically. I accepted and hurray, it just worked. Then I was able to run Product > Archive, and to find said archive in Window > Organizer

In the Organizer, I tried to Validate the archive. I don’t remember it 100% (after all my struggles, I wasn’t really in the mood to take notes, sorry), but I believe it tried to sign the app too, and failed but also proposed to automatically regenerated the provisioning profiles. Note, by the way, that around this time I got an e-mail saying the prod certificate was revoked (because a new one was generated). In the previous step of building the archive, the dev certificate met the same fate. But it worked, and I ended up with a ready to upload archive, brand new certificates, and regenerated provisioning profiles. Yay 🙂 I just had one problem left, cf next “mini chapter” below.

How to solve a failed archive upload saying “Cannot verify client. (3000)”
Well in my case that was simple, I just restarted Xcode. Others have mentioned that doing a clean and rebuild (and re-archive) did the trick, I’d advise you to start with a simple restart, it’s just faster plus we just built the archive already, so I’m not convinced rebuilding it would change much, unless you do restart before rebuilding.

How to display the full path to current folder in Finder
By default, when something sends you to a folder in Finder (like, when you ask Xcode to open the folder containing your provisioning profiles), it seems impossible to find out where the said folder is as the left panel doesn’t contain a proper folder tree unlike Windows Explorer. Gladly something can be done about it, although it’s not that convenient: use View > Show Path Bar (or Alt + Cmd + P) to add a bar at the bottom of the window with the full path to the folder. You can then also use it to reach a folder among the folder’s parents (double click it).

Posted in programming, Xcode.


Some more Xcode and iOS apps tips & tricks for beginners

I posted some tips and tricks in this previous post already, here are more as I discover them on my journey to getting used to Mac OS, Xcode and Objective-C (and some day Swift probably ^^)

How to type some of those characters that are totally necessary for programming
By this I mean things such as | (pipe / vertical bar), {} (braces / curly brackets) or [] (chevrons / angle brackets). That’s right, maybe you are luckier than I, but on the MacBook Pro I use for development, Apple thought it would be a good idea not to display those characters on the keyboard. Seriously WTF…
Anyway, here are the shortcuts, although note that I have a French “AZERTY” keyboard and it might sadly be different on yours :

  • the pipe | can be obtained using Shift + Alt + L (thank god for this old blog post where I found it)
  • the backslash \ can be obtained using Shift + Alt + / (slash) (thanks same blog post again)
  • the braces {} can be obtained using Alt + ( (same key as 5) and Alt + ) (same key as °) (found it myself by pressing about every combination I could think of… reminded me how I used to push every button in Windows 3.1 as a kid, hurray for modernity!)
  • the chevrons [] can be obtained using Shift + Alt + ( and Shift + Alt + ) (ditto)
  • the tilde ~ can be obtained using Alt + N (for some crazy reason, all the “solutions” I found call the “Alt” key the “Option” key, go figure…)

How to reduce indentation of a bock of code
In Windows, reducing indentation of a code block is easy because the tab key (which is used to increase indentation) can be used to produce a reverse tab (by pressing Shift + Tab), and this reverse tab can usually be used to reduce indentation. In Mac OS I still haven’t found how to produce that reverse tab, but code indentation can be reduced the following way:
– select your block of code
– right click it
– in the contextual menu, look for Structure -> Shift Left

How to disable a specific deprecation warning with pragma
I prefer to keep all warnings on so as to be proactive about trouble, because in most cases a warning is likely to eventually turn into a nasty error. But this is not the case with every warning. Notably, you can’t get rid of deprecation warning if you want to keep supporting old iOS versions, for instance if you do something like

if(newFunctionExists) {
    doModernCode
} else {
    doDeprecatedCode
}

This is annoying because it puts an end to the quest to no warning in the project. Luckily, there’s a clean solution to this: you can disable the deprecation warning (or actually any other warning if you choose so) just around the block or line of code of interest. The code for this looks like:

#pragma clang diagnostic push 
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
[here goes your deprecated code]
#pragma clang diagnostic pop

(you may need to replace clang with GCC if you use the GCC compiler instead of clang) Pretty neat, isn’t it? 🙂
If you want to read more about pragma, I found this nice post, which is where I found out it could be used to disable warnings – after that all I had to do was find the codename of the deprecation ones.

Even neater, you can also define a macro, like this:

#define SILENCE_DEPRECATION(expr)                              \
{                                                                   \
_Pragma("clang diagnostic push")                                    \
_Pragma("clang diagnostic ignored \"-Wdeprecated-declarations\"")   \
expr;                                                               \
_Pragma("clang diagnostic pop")                                     \
}

#define SILENCE_IOS7_DEPRECATION(expr) SILENCE_DEPRECATION(expr)
#define SILENCE_IOS8_DEPRECATION(expr) SILENCE_DEPRECATION(expr)
#define SILENCE_IOS9_DEPRECATION(expr) SILENCE_DEPRECATION(expr)

And then use it like:

SILENCE_IOS7_DEPRECATION([here goes your deprecated code])

This way you can easily keep track of what code to remove when you eventually decide to drop support for very old iOS versions.

How to remove analyzer warnings from the list of warnings
As I was desperately trying to produce an archive (like this except that it doesn’t freaking work) of my app in an attempt to maybe manage to send it to other people so that they can test it (can’t believe the hell this is on iOS while it’s so trivial on Android), I “accidentally” analyzed my project. It gave me a few interesting warnings but also quite many not so relevant ones. So now my almost warning-free project is flooded with 50+ extra warning from the analyzer from Hell. Gladly, they can be purged, to do so, simply go to Product -> Clean : this will remove analyzer messages (as well as all warnings too) from the issue navigator. Then next time you build the normal warnings will come back, but not the ones from the analyzer (until you choose to run the analyzer again, that is).

Posted in programming, Xcode.


Some Xcode and iOS apps tips & tricks for beginners

How to disable word- / line-wrapping
Xcode Preferences (in the menu) -> Text Editing (in the top tabs) -> Indentation (that’s a clickable text in the top of the tab), in there you can check/uncheck Line wrapping
Source:
https://stackoverflow.com/questions/5271530/how-to-disable-word-wrap-in-xcode-4-editor

How to change iOS simulator language
You can simply use the Settings app in the simulator, just like on a normal device. Note that languages are shown in their own language, for instance “French” is “Français”, not “French”.

There is also an option that may be easier for switching back and forth between languages: you can run the app with a flag indicating the country, for instance add the flag -AppleLanguages (fr) for French.
This is explained more in details here:
http://abizern.org/2012/03/18/simple-localisation-testing/

Fixing some text getting automatically underlined in the app
This is cannot be changed by the app programmer as this is caused by a phone setting. So it’s up to the final user to disable it, in Settings -> General -> Accessibility, and then turning off Button Shapes.
Source:
https://apple.stackexchange.com/questions/124277/some-texts-automatically-underlined-in-iphone-5-with-ios-7-1

Solving error “attempt to present UIAlertController on [some UI] whose view is not in the windows hierarchy”
I got this one while replacing the more robust UIAlertView with UIAlertController. The main PITA from UIAlertController is that you need to feed it the current view as an argument, unlike UIAlertView which apparently just guessed it. This blog post pointed me to the solution for my case. Basically, UIAlertController must be called on the currently visible view, not on some other view (even if it does exist but is not on display right now).

Little bonus: here is the function I use to display a network error alert every time needed:

+(void) networkErrorAlert:(id)sender
{
     UIAlertController* alert = [UIAlertController alertControllerWithTitle: NSLocalizedString(@"Error",@"")
                                                                    message: NSLocalizedString(@"No Internet Connection",@"")
                                                             preferredStyle: UIAlertControllerStyleAlert];
     UIAlertAction* defaultAction = [UIAlertAction actionWithTitle: NSLocalizedString(@"OK",@"")
                                                             style: UIAlertActionStyleDefault
                                                           handler: ^(UIAlertAction * action) {}];
     [alert addAction:defaultAction];
     [sender presentViewController:alert animated:YES completion:nil];
}

Posted in programming, Xcode.


aToaD #17: scripts on tomeko.net + roundmypic

Online various string conversion, like base64 to hexadecimal

I’ve been having issues creating tokens for the Apple Push Notification Service, and among other thing the app would sometimes produce base64 tokens. The library I use for sending the pushes from the server requires base16 / hexadecimal tokens, and also I found that checking the validity of base64 tokens is a bit less obvious than the base16 ones although I suppose that’s more of a personal convenience. Anyhow, so I searched for a convenient online tool to convert between the 2 formats, and eventually I found those tools on tomeko.net, with a base64 to hexadecimal converter but also quite a few others (hex to base 32, ASCII, DEC, etc, etc). There it is: http://tomeko.net/online_tools/base64.php?lang=en

While I’m at it, on another topic but I don’t think it would deserve a separate post, a tool to easily round the corners of a picture (great to produce an application icon for Android) without shadow or other fancy effects (although there’s an option to add bevel effect). I found that a “Round Size” value of about 10% of the picture size looks decent (e.g. if width and height=1000px, round size=100px looks okay IMO). There it is: Round My Pic

Posted in A Tool A Day.


Apache 2.4: solving AH00125 (Request exceeded the limit of 10 subrequest…)

So I’m yet again in the middle of a painful Apache configuration, and with the major update (from version 2.2 shipped with Debian 7 to version 2.4 shipped with Debian 8) come the major annoyances as usual. When loading folders with no index.htm/.php, I noticed Apache threw errors at me instead of simply diplaying “Forbidden”. Something just like:
Internal Server Error

The server encountered an internal error or misconfiguration and was unable to complete your request.

Please contact the server administrator at [no address given] to inform them of the time this error occurred, and the actions you performed just before this error.

More information about this error may be available in the server error log.

So I checked the log and saw quite a few of these:
AH00125: Request exceeded the limit of 10 subrequest nesting levels due to probable configuration error. Use ‘LimitInternalRecursion’ to increase the limit if necessary. Use ‘LogLevel debug’ to get a backtrace.

I suppose this recursivity accident can be caused by a various amount of causes. In my case the problem came from a misconfigured DirectoryIndex (in mods-available/php5.conf) : I had added a comment at the end, which used to work without issue in Apache 2.2.

DirectoryIndex index.html index.htm index.php #(seems optional now)

But apparently # is a valid character and not a comment starter if not at the beginning of a line, so in Apache 2.4 it creates that AH00125 error when loading a folder with no DirectoryIndex. As my own comment said, this line seems optional now, so for me the fix was just to remove it. Now I’ve got another error, but it seems quite unavoidable (“AH01276: Cannot serve directory /home/xxx/: No matching DirectoryIndex (index.html, index.cgi, index.pl, index.php, index.xhtml, index.htm) found, and server-generated directory index forbidden by Options directive“). Or maybe I should just put an empty index.htm file in there.

Source where I found the solution: Mailing List Archive: Apache: Dev – How to help users diagnose invalid # in DirectoryIndex?

Posted in servers.

Tagged with .


aToaD #16: WebODF

Open source Javascript-based editor for OpenDocument files

I remember getting a request some time ago to add slideshow viewing capacity to some website. At the time I didn’t really find anything good and just messed around a bit with LibreOffice’s (very limited) capacity to export a document into HTML.

WebODF seems to offer a much more satisfying option, although I assume it will “only” deal with OpenDocument and not MS Office stuff. A list of features is available here (online demos available on the same page), but long story short it allows for editing OpenDocument files or to just view them (like, display a LibO Impress slideshow).
Something great about it is that it’s impressively compact, the main javascript file, webodf.js, is smaller than 500 KB at the moment!

Posted in A Tool A Day, LibreOffice & OpenOffice, web development.