Commit 5b1d5cb0 authored by Oliver Smith's avatar Oliver Smith

Merge branch '2018-12-post' into 'master'

January 2019 blog post

See merge request postmarketOS/postmarketos.org!70
parents 71bc4ce0 6e51d721
......@@ -6,7 +6,7 @@
Python 3.4+ is supported. Install all requirements, preferably within a virtualenv:
```bash
```shell
$ python -m venv .venv
$ source .venv/bin/activate
(venv)$ pip install -r requirements.txt
......@@ -16,7 +16,7 @@ $ source .venv/bin/activate
Blog content is written in markdown format with metadata in the file header. Filename syntax is `yyyy-mm-dd-slug.md`.
```bash
```shell
$ cat >content/blog/2017-12-31-happy-new-year.md << EOF
> ---
> title: Happy New Year!
......@@ -26,33 +26,53 @@ $ cat >content/blog/2017-12-31-happy-new-year.md << EOF
> EOF
```
### Dev Server
### Writing responsive content
Run the dev server during local development, changes are auto reloaded:
Use the following custom tags to create responsive sections in the blog posts:
* `[#grid side#]`
* `[#grid text#]`
* `[#grid bottom#]`
* `[#grid end#]`
```bash
(venv)$ FLASK_DEBUG=1 FLASK_APP=app.py flask run
```
Using any tag except for "end" will encapsulate the following markdown code in the grid area of the same name. If the grid was not opened yet, it will be opened with the first tag. The "end" tag closes the grid.
### CSS
The grid layout looks like this, in desktop mode:
Not much CSS is used due to heavy usage of [Tachyons](http://tachyons.io/) toolkit classes. Please use CSS judiciously only when needed.
|text|(20px free space)|side|
|bottom|(20px free space)|side|
Any CSS used should be compiled via `lessc`:
...and with a lower screen width (mobile phones etc.):
|side|
|text|
|bottom|
Use the responsive mode tools of your browser to check if it works as expected. For usage examples, look at the existing blog posts and the grid-related code in `static/css/page.css`. This feature is implemented in `page.py`.
### Dev Server
```bash
$ npm install -g less
$ lessc static/css/main.less static/css/main.css
Run the dev server during local development, changes are auto reloaded:
```shell
(venv)$ FLASK_DEBUG=1 FLASK_APP=app.py flask run
```
### Build
To run a static site build, run:
```bash
```shell
(venv)$ python freeze.py
```
This will generate a static version in `docs/`. Any manual changes to the `docs/` directory will be overridden in the next build.
Note that the `docs/` directory is ignored and not versioned.
### Upgrading requirements.txt
```shell
(venv)$ pip install pip-upgrader
(venv)$ pip-upgrade
```
import collections
import logo
import markdown
import os
......@@ -9,6 +10,9 @@ from flask import Flask, render_template, url_for, Response, request, send_file
from werkzeug.contrib.atom import AtomFeed
from os import listdir
# current dir
import page
app = Flask(__name__)
......@@ -66,6 +70,11 @@ def logo_svg():
return Response(response=logo.create(phone=False), mimetype="image/svg+xml")
def parse_post(post, external_links=False, create_html=True):
""" :returns: a parsed post, something like this:
{"html": "<parsedhtmlcode...",
"url": "url/to/the/post",
"reading_time": "10 min",
"year": 2019} """
with open(os.path.join(BLOG_CONTENT_DIR, post), encoding="utf-8") as handle:
raw = handle.read()
frontmatter, content = REGEX_SPLIT_FRONTMATTER.split(raw, 2)
......@@ -79,21 +88,35 @@ def parse_post(post, external_links=False, create_html=True):
'markdown.extensions.extra',
'markdown.extensions.codehilite',
'markdown.extensions.toc'
])
], extension_configs={"markdown.extensions.toc": {"anchorlink": True}})
data['html'] = page.replace(data['html'])
data['url'] = url_for('blog_post', y=y, m=m, d=d, slug=slug,
_external=external_links)
data['reading_time'] = reading_time(content)
data['year'] = y
return data
def get_posts(**kwargs):
""" :returns: posts categorized by year, looks like:
{2019: [post1, post2, ...], 2018: [...], ...}
post1, post2 are the posts as returned by parse_post() above.
"""
posts = sorted(listdir(BLOG_CONTENT_DIR), reverse=True)
return (parse_post(post, **kwargs) for post in posts)
ret = collections.OrderedDict()
for post in posts:
parsed = parse_post(post, **kwargs)
year = parsed['year']
if not year in ret:
ret[year] = []
ret[year].append(parsed)
return ret
@app.route('/blog/')
def blog():
return render_template('blog.html', posts=get_posts(create_html=False))
return render_template('blog.html',
year_posts=get_posts(create_html=False))
@app.route('/blog/feed.atom')
def atom():
......@@ -103,13 +126,15 @@ def atom():
title='postmarketOS Blog',
url=url_for('blog', _external=True))
for post in get_posts(external_links=True):
feed.add(content=post['html'],
content_type='html',
title=post['title'],
url=post['url'],
# midnight
updated=datetime.combine(post['date'], datetime.min.time()))
for year, posts in get_posts(external_links=True).items():
for post in posts:
feed.add(content=post['html'],
content_type='html',
title=post['title'],
url=post['url'],
# midnight
updated=datetime.combine(post['date'],
datetime.min.time()))
return feed.get_response()
@app.route('/blog/<y>/<m>/<d>/<slug>/')
......@@ -128,7 +153,7 @@ def static_page(page):
'markdown.extensions.extra',
'markdown.extensions.codehilite',
'markdown.extensions.toc'
])
], extension_configs={"markdown.extensions.toc": {"anchorlink": True}})
return render_template('page.html', **data)
......
......@@ -2,16 +2,11 @@ title: "Aiming for a 10 year life-cycle for smartphones"
date: 2017-05-26
---
[![Samsung Galaxy SII (i9100) running postmarketOS](/static/img/2017-05-26/i9100-filled-thumb.jpg)](/static/img/2017-05-26/i9100-filled.jpg)
[![Samsung Galaxy SII (i9100) running postmarketOS](/static/img/2017-05-26/i9100-filled-thumb.jpg){: class="border"}](/static/img/2017-05-26/i9100-filled.jpg)
_Introduction post to [postmarketOS](https://github.com/postmarketOS/), a touch-optimized, pre-configured [Alpine Linux](https://alpinelinux.org/) with own packages, that can be installed on smartphones. (Not usable for most people yet!)_
## Index
[TOC]
## Minimalistic Linux distributions run fine on ten year old PCs.
It is 2017\. Pick an average PC from 2007 and install a minimal Linux based operating system. You will be able to do basic computing tasks (eg. surfing the web, reading E-Mails, listening to music, chatting) just like on an _expensive_ modern PC. You will even get security updates, so your old computer is protected, just like as a new one.
## Why are Android/Linux phones different?
......@@ -30,13 +25,13 @@ Here is the solution: Bend an existing Linux distribution to run on smartphones.
Of course we are not the only ones, that came to this conclusion - especially in the last few weeks with the [Halium](https://halium.org/) project rising _(greetings!)_. We are all-in for working together — sharing udev rules, merging Android kernels together, whatever makes sense!
[![Unlocking encrypted rootfs via telnet](https://ollieparanoid.github.io/img/2017-05-26/i9100/telnet-thumb.jpg "Unlocking encrypted rootfs via telnet")](https://ollieparanoid.github.io/img/2017-05-26/i9100/telnet.jpg)
[![Unlocking encrypted rootfs via telnet](https://ollieparanoid.github.io/img/2017-05-26/i9100/telnet-thumb.jpg "Unlocking encrypted rootfs via telnet"){: class="border"}](https://ollieparanoid.github.io/img/2017-05-26/i9100/telnet.jpg)
## postmarketOS architecture
We're working on an Alpine Linux based distribution called postmarketOS where each phone will have **only one [unique](https://github.com/postmarketOS/pmbootstrap/tree/master/aports/device/device-samsung-i9100/) [package](https://github.com/postmarketOS/pmbootstrap/tree/master/aports/device/device-lg-mako)** — all other packages are shared among all devices.
These `device-$vendor-$name` packages contain a so-called `/etc/deviceinfo` file, which [describes](https://github.com/postmarketOS/pmbootstrap/blob/master/aports/device/device-samsung-i9100/deviceinfo) [what](https://github.com/postmarketOS/pmbootstrap/blob/master/aports/device/device-lg-mako/deviceinfo) makes the device special: SD card availability, which flash software to use and other information. The file format is not stable yet, and once we have common kernels for multiple devices, I'd like to include the required modules and `dtb` name.
These `device-$vendor-$name` packages contain a so-called `/etc/deviceinfo` file, which [describes](https://github.com/postmarketOS/pmbootstrap/blob/master/aports/device/device-samsung-i9100/deviceinfo) [what](https://github.com/postmarketOS/pmbootstrap/blob/master/aports/device/device-lg-mako/deviceinfo) makes the device special: SD card availability, which flash software to use and other information. The file format is not stable yet, and once we have common kernels for multiple devices, We'd like to include the required modules and `dtb` name.
And just to make it clear, postmarketOS does not fit the Halium model, as it avoids the Android build system entirely and does _not_ run any part of the Android userspace next to its more or less typical Linux userspace. _(At least not in the regular install, but it could come at some point in the future as optional compatibility layer for Android applications if someone wants to work on it.)_
......@@ -125,12 +120,11 @@ Great care has been taken to make pmbootstrap safe, as it will run on productive
### Photos
[![postmarketOS demos menu](/static/img/2017-05-26/i9100-demos-thumb.jpg){: class="cl fl mr3 mb3" }](/static/img/2017-05-26/i9100-demos.jpg)
[![Firefox running in XWayland](/static/img/2017-05-26/i9100-firefox-thumb.jpg){: class="fl mr3 mb3" }](/static/img/2017-05-26/i9100-firefox.jpg)
[![htop in two weston-terminals](/static/img/2017-05-26/i9100-htop-thumb.jpg){: class="fl mr3 mb3" }](/static/img/2017-05-26/i9100-htop.jpg)
[![Weston smoke demo](/static/img/2017-05-26/i9100-smoke-thumb.jpg){: class="fl mr3 mb3" }](/static/img/2017-05-26/i9100-smoke.jpg)
[![lg-mako: colors are red, but weston-editor works due to the bigger screen size](/static/img/2017-05-26/mako-editor-thumb.jpg){: class="fl mr3 mb3" }](/static/img/2017-05-26/mako-editor.jpg)
[#grid bottom#]
[![postmarketOS demos menu](/static/img/2017-05-26/i9100-demos-thumb.jpg)](/static/img/2017-05-26/i9100-demos.jpg)
[![Firefox running in XWayland](/static/img/2017-05-26/i9100-firefox-thumb.jpg)](/static/img/2017-05-26/i9100-firefox.jpg)
[![htop in two weston-terminals](/static/img/2017-05-26/i9100-htop-thumb.jpg)](/static/img/2017-05-26/i9100-htop.jpg)
[![Weston smoke demo](/static/img/2017-05-26/i9100-smoke-thumb.jpg)](/static/img/2017-05-26/i9100-smoke.jpg)
[![lg-mako: colors are red, but weston-editor works due to the bigger screen size](/static/img/2017-05-26/mako-editor-thumb.jpg)](/static/img/2017-05-26/mako-editor.jpg)
[#grid end#]
......@@ -2,8 +2,6 @@ title: Why supporting the Librem Phone crowdfunding campaign helps postmarketOS
date: 2017-09-24
---
[TOC]
## Proprietary components make smartphones insecure for the masses
Whenever you buy *any* smartphone, you get a device full of proprietary components. These are integrated so deeply with each other that they can access everything on your phone, such as the camera, microphone, browser history and chat messages. Since these components are proprietary, they've been designed to make it **impossible for anyone but the vendor to modify**, and they can only be **understood by others through immense efforts**.
......
This diff is collapsed.
This diff is collapsed.
......@@ -2,7 +2,7 @@ title: "postmarketOS is #movingtogitlab"
date: 2018-06-27
---
[![Broken Sony Xperia Z2 Tablet](/static/img/2018-06/broken-castor-thumb.jpg)](/static/img/2018-06/broken-castor.jpg)
[![Broken Sony Xperia Z2 Tablet](/static/img/2018-06/broken-castor-thumb.jpg){: class="wfull border" }](/static/img/2018-06/broken-castor.jpg)
For a lot of people, learning that [Microsoft will buy GitHub at the end of 2018](https://www.bloomberg.com/news/articles/2018-06-04/microsoft-agrees-to-buy-coding-site-github-for-7-5-billion) [shattered trust in GitHub](https://jacquesmattheij.com/what-is-wrong-with-microsoft-buying-github) like the glass of [@opendata26](https://gitlab.com/opendata26)'s [Sony Xperia Z2 Tablet](https://wiki.postmarketos.org/wiki/Sony_Xperia_Z2_Tablet_(sony-castor-windy)). Beyond that, GitHub has always employed vendor lock-in: the user's issues and pull requests are hidden behind a rate limited API instead of being available through a proper export feature. And even if you managed to export it through that API, you cannot host your own GitHub instance and modify it as you like because, there is not even a partially open source version of it.
......
......@@ -12,7 +12,7 @@ Read the [Plasma Mobile's blog post](https://www.plasma-mobile.org/2018/08/23/Pl
### UPDATE: The [video recordings](https://cdn.files.kde.org/akademy/2018/videos/) of the talks are online!
[![](/static/img/2018-08/pmos-plamo-akademy2018.jpg)](/static/img/2018-08/pmos-plamo-akademy2018.jpg)
[![](/static/img/2018-08/pmos-plamo-akademy2018.jpg){: class="wfull border" }](/static/img/2018-08/pmos-plamo-akademy2018.jpg)
From left to right:
[@ata2001](https://gitlab.com/ata2001),
......
This diff is collapsed.
# Copyright 2018 Oliver Smith
# SPDX-License-Identifier: GPL-3.0-or-later
def grid(html):
""" Replace the following markers with appropriate <div class="..."> and
</div> tags. See README.md and static/code/blog-post.css for more
information.
- "[#grid side#]"
- "[#grid text#]"
- "[#grid bottom#]"
- "[#grid end#]"
:param html: blog post code (already converted from markdown to HTML)
:returns: html with all markers replaced """
sections = ["side", "text", "bottom"]
ret = ""
in_grid = False
for word in html.split("[#grid "):
# Continue or start grid
if in_grid:
# Avoid "<p></div>"
if ret[-3:] == "<p>":
ret = ret[:-3]
ret += "</div>"
# New grid section
tag_found = ''
for section in sections:
tag = section + "#]"
if word.startswith(tag):
tag_found = tag
if not in_grid:
ret += '<div class="grid">'
in_grid = True
ret += '<div class="grid-' + section + '">'
break
# End grid
tag = "end#]"
if word.startswith(tag):
if not in_grid:
raise ValueError("[#grid end#] found before it was opened!")
tag_found = tag
ret += "</div>"
in_grid = False
# Remove tag from word
word = word[len(tag_found):]
# Avoid "<div class=...></p>"
if word[:4] == "</p>":
word = word[4:]
ret += word
# Check for grids without end tag
if in_grid:
raise ValueError("Missing [#grid end#]!")
return ret
def replace(html):
""" Various replacements for blog posts, to make them responsive etc.
:param html: blog post code (already converted from markdown to HTML)
:returns: html with replacements made """
ret = grid(html)
return ret
click==6.7
Flask==0.12.2
click==7.0
Flask==1.0.2
Frozen-Flask==0.15
itsdangerous==0.24
Jinja2==2.9.6
Markdown==2.6.8
MarkupSafe==1.0
Pygments==2.2.0
PyYAML==3.12
Werkzeug==0.12.2
itsdangerous==1.1.0
Jinja2==2.10
Markdown==3.0.1
MarkupSafe==1.1.0
Pygments==2.3.1
PyYAML==3.13
Werkzeug==0.14.1
/* Copyright 2018 Oliver Smith
SPDX-License-Identifier: GPL-3.0-or-later */
/* LINKS */
a {
color: black;
}
a:hover {
color: #0d8000;
}
/* GRID: OUTER */
body {
display: grid;
grid-template-areas: ". header ."
". main ."
". footer .";
grid-template-columns: auto minmax(360px, 1024px) auto;
margin: 0px;
background: #f5f5f5;
font-family: sans-serif;
line-height: 2;
font-size: 13pt;
}
/* MAIN */
main {
grid-area: main;
background: white;
}
h1 {
font-size: 25pt;
}
.content {
padding: 10px 30px 40px;
}
.content.alt {
background: #eee;
}
.codehilite pre {
font-size: 10pt;
overflow: hidden;
text-overflow: ellipsis;
}
.separator {
background-image: url(/static/img/nexus5-bg-bottom.jpg);
height: 10px;
}
/* GRID: HEADER */
.header {
grid-area: header;
display: grid;
grid-template-areas: "logo . nav"
". . nav"
"slogan . ."
"slogan . disclaimer";
grid-template-columns: auto 1fr auto;
background-image: url(/static/img/nexus5-bg.jpg);
background-repeat: no-repeat;
background-position: left bottom;
}
/* SLOGAN AND DISCLAIMER */
.slogan {
grid-area: slogan;
padding: 30px;
color: white;
font-size: 25pt;
font-weight: bold;
line-height: 35pt;
}
.slogan small {
font-size: 15pt;
line-height: 1;
}
.disclaimer {
grid-area: disclaimer;
margin: 15px 30px;
padding: 3px 7px;
background: black;
color: white;
font-weight: bold;
font-size: 10pt;
}
/* GRID: LOGO */
.logo {
grid-area: logo;
display: grid;
align-items: center;
grid-template-areas: "logo-img logo-span";
grid-template-columns: 90px 1fr;
padding: 10px 0px 0px;
margin-left: 30px;
min-height: 90px;
text-decoration: none;
}
.logo img {
grid-area: logo-img;
width: 75px;
height: 75px;
filter: drop-shadow(0 0 7px rgba(0,0,0,0.3));
}
.logo span {
grid-area: logo-span;
font-size: 25pt;
text-shadow: 0px 0px 8px rgba(0, 0, 0, 0.5);
color: white;
}
/* GRID: NAVIGATION */
.nav {
grid-area: nav;
display: grid;
grid-template-areas: "nav-row1"
"."
"nav-row2"
"."
"nav-row3";
grid-template-rows: auto
6px
auto
6px
1fr;
text-align: right;
margin: 15px 30px;
}
.nav-row1 {
grid-area: nav-row1;
}
.nav-row2 {
grid-area: nav-row2;
}
.nav-row3 {
grid-area: nav-row3;
}
.nav a {
font-size: 10pt;
background: white;
padding: 5px 4px;
margin-left: 2px;
font-weight: bold;
}
.nav-row1 a {
font-size: 15pt;
}
/* GRID: FOOTER */
footer {
grid-area: footer;
display: grid;
grid-template-areas: "contribute . nav-bottom"
"contribute . copyright";
grid-template-columns: auto 1fr auto;
align-items: center;
background-image: url(/static/img/nexus5-bg-bottom.jpg);
}
.nav-bottom {
grid-area: nav-bottom;
margin: 15px 30px 0px;
text-align: right;
}
/* GRID: ICON BUTTON */
.icon-button {
display: inline-grid;
grid-template-areas: "icon-button-icon icon-button-label";
grid-template-columns: auto auto;
padding: 0px 10px;
align-items: center;
background: white;
text-decoration: none;
margin-left: 2px;
}
.icon-button img {
grid-area: icon-button-icon;
width: 16px;
height: 16px;
margin-right: 5px;
}
.icon-button span {
grid-area: icon-button-label;
text-decoration: underline;
}
.icon-button:hover img {
/* Obviously this changes the color to green */
filter: sepia(1) saturate(5) hue-rotate(80deg);
}
.contribute {
grid-area: contribute;
font-size: 20pt;
padding: 10px 20px;
margin: 0px 30px;
}
.contribute span {
text-decoration: none;
}
.contribute img {
width: 48px;
height: 48px;
margin-right: 10px;
}
.copyright {
grid-area: copyright;
color: white;
margin: 5px 30px 15px;
font-size: 11pt;
text-align: right;
}
/* RESPONSIVENESS */
@media screen and (max-width: 800px) {
/* HEADER */
.header {
grid-template-areas: "logo nav nav"
". nav nav"
"slogan slogan slogan"
"disclaimer disclaimer disclaimer";
}
.logo span {
font-size: 20pt;
}
.slogan {
/* Hide the slogan in mobile view everywhere, except on the main page.
We override this in index.css. */
display: none;
}
/* MAIN */
.codehilite pre {
font-size: 8pt;
}
/* FOOTER */
footer {
grid-template-areas: "contribute"
"nav-bottom"
"copyright";
grid-template-columns: 100%;
}
.nav-bottom {
text-align: center;
}
.nav-bottom .icon-button {
margin: 0px 2px 10px;
}
.nav-bottom .icon-button img {
margin-right: 10px;
}
.contribute {
margin: 15px auto 0px;
}
.copyright {
text-align: center;
}
}
@media screen and (max-width: 600px) {
/* HEADER */
.header {
grid-template-areas: "logo"
"nav"
"slogan"
"disclaimer";
grid-template-columns: 100%;
}
.logo {
margin-left: 0px;
padding-left: 30px;
}
.nav {
text-align: left;
margin-left: 30px;
}
.nav a {
margin-left: 0px;
margin-right: 5px;
}
.disclaimer {
margin: 10px 30px 15px;
font-size: 8pt;
}
}
/* Copyright 2018 Oliver Smith
SPDX-License-Identifier: GPL-3.0-or-later
This file is used for the listing of all blog posts (/blog).
See page.css for the file used in single blog posts. */
.post {
display: grid;
grid-template-areas: ". . title"
"date . title"
"reading_time . title"
". . title";
grid-template-columns: min-content 20px 1fr;
grid-template-rows: 1fr
auto
auto
1fr;
margin: 10px 0px;
}
.title {
grid-area: title;
font-size: 14pt;
}
.date {
grid-area: date;
white-space: nowrap;
font-size: 12pt;
}
.reading_time {
grid-area: reading_time;
color: gray;
font-size: 9pt;
}
/* RESPONSIVENESS */
@media screen and (max-width: 600px) {
.post {
grid-template-areas: "date . reading_time"
"title title title";
grid-template-columns: min-content 10px 1fr;
grid-template-rows: auto
auto;
margin-bottom: 20px;
}
.reading_time {
font-size: 12pt;
}
}
/* Copyright 2018 Oliver Smith
SPDX-License-Identifier: GPL-3.0-or-later */