- mastodon publish added besides "ordinary" fed.brid.gy (curiosity, for now)

- micropub moved to local from zapier - end of zapier-as-service for micropub experiment; it's too restricted for this purpose, mainly because timeouts on other remote services, such as bookmarks -> wallabag
- added unlinked worldmap view for of posts as early experiment
- added fediverse "stats"
- inlined JSON-LD - the sidecar json file is useless the way it was
- u-photo/u-featured is now out of e-content
- fixed accidentally swapp lon-lat to lat-lon
- added rel-licence to figures for encapsulated licence (future use)
- re-added twitter, added mastodon in footer
This commit is contained in:
Peter Molnar 2019-10-25 09:43:50 +01:00
parent 70a775b6d5
commit 79af56af9b
13 changed files with 543 additions and 255 deletions

View file

@ -4,15 +4,15 @@ verify_ssl = true
name = "pypi"
[packages]
wand = "*"
Wand = "*"
unicode-slugify = "*"
requests = "*"
python-frontmatter = "*"
langdetect = "*"
jinja2 = "*"
Jinja2 = "*"
feedgen = "*"
filetype = "*"
arrow = "==0.14.2"
arrow = "*"
[dev-packages]

118
Pipfile.lock generated
View file

@ -1,7 +1,7 @@
{
"_meta": {
"hash": {
"sha256": "51fd7fc20bd922d7404b46cdf8aa2e1c7119900b44c6e2ba639cff49633632e3"
"sha256": "8c4e0b49bc91f0a4ffa5031290053dfa5fcca8957e3f7378577137e3a8cda540"
},
"pipfile-spec": 6,
"requires": {
@ -18,18 +18,18 @@
"default": {
"arrow": {
"hashes": [
"sha256:03404b624e89ac5e4fc19c52045fa0f3203419fd4dd64f6e8958c522580a574a",
"sha256:41be7ea4c53c2cf57bf30f2d614f60c411160133f7a0a8c49111c30fb7e725b5"
"sha256:10257c5daba1a88db34afa284823382f4963feca7733b9107956bed041aff24f",
"sha256:c2325911fcd79972cf493cfd957072f9644af8ad25456201ae1ede3316576eb4"
],
"index": "pypi",
"version": "==0.14.2"
"version": "==0.15.2"
},
"certifi": {
"hashes": [
"sha256:046832c04d4e752f37383b628bc601a7ea7211496b4638f6514d0e5b9acc4939",
"sha256:945e3ba63a0b9f577b1395204e13c3a231f9bc0223888be653286534e5873695"
"sha256:e4f3620cfea4f83eedc95b24abd9cd56f3c4b146dd0177e83a21b4eb49e21e50",
"sha256:fd7c7c74727ddcf00e9acd26bba8da604ffec95bf1c2144e67aff7a8b50e6cef"
],
"version": "==2019.6.16"
"version": "==2019.9.11"
},
"chardet": {
"hashes": [
@ -40,10 +40,10 @@
},
"feedgen": {
"hashes": [
"sha256:82c9e29884e137c3e3e7959a02f142d1f7a46cd387d572e9e40150112a27604f"
"sha256:26557304284007a3c2d772c376a48c0a8ec8e24371c4471455ea89cf568fa114"
],
"index": "pypi",
"version": "==0.7.0"
"version": "==0.8.0"
},
"filetype": {
"hashes": [
@ -53,14 +53,6 @@
"index": "pypi",
"version": "==1.0.5"
},
"gumbo": {
"hashes": [
"sha256:48e9dac2a872a9e2677b63c0bb7a39a76051c703abf30fff4df60664dfda52f4",
"sha256:e8c5504479a535ae7683c6bfba787ea37aa2fe77ae3e40943595a8adebb4d508"
],
"index": "pypi",
"version": "==0.10.0"
},
"idna": {
"hashes": [
"sha256:c357b3f628cf53ae2c4c05627ecc484553142ca23264e593d327bcde5e9c3407",
@ -70,11 +62,11 @@
},
"jinja2": {
"hashes": [
"sha256:065c4f02ebe7f7cf559e49ee5a95fb800a9e4528727aec6f24402a5374c65013",
"sha256:14dd6caf1527abb21f08f86c784eac40853ba93edb79552aa1e4b8aef1b61c7b"
"sha256:74320bb91f31270f9551d46522e33af46a80c3d619f4a4bf42b3164d30b5911f",
"sha256:9fe95f19286cfefaa917656583d020be14e7859c6b0252588391e47db34527de"
],
"index": "pypi",
"version": "==2.10.1"
"version": "==2.10.3"
},
"langdetect": {
"hashes": [
@ -85,32 +77,30 @@
},
"lxml": {
"hashes": [
"sha256:06c7616601430aa140a69f97e3116308fffe0848f543b639a5ec2e8920ae72fd",
"sha256:177202792f9842374a8077735c69c41a4282183f7851443d2beb8ee310720819",
"sha256:19317ad721ceb9e39847d11131903931e2794e447d4751ebb0d9236f1b349ff2",
"sha256:36d206e62f3e5dbaafd4ec692b67157e271f5da7fd925fda8515da675eace50d",
"sha256:387115b066c797c85f9861a9613abf50046a15aac16759bc92d04f94acfad082",
"sha256:3ce1c49d4b4a7bc75fb12acb3a6247bb7a91fe420542e6d671ba9187d12a12c2",
"sha256:4d2a5a7d6b0dbb8c37dab66a8ce09a8761409c044017721c21718659fa3365a1",
"sha256:58d0a1b33364d1253a88d18df6c0b2676a1746d27c969dc9e32d143a3701dda5",
"sha256:62a651c618b846b88fdcae0533ec23f185bb322d6c1845733f3123e8980c1d1b",
"sha256:69ff21064e7debc9b1b1e2eee8c2d686d042d4257186d70b338206a80c5bc5ea",
"sha256:7060453eba9ba59d821625c6af6a266bd68277dce6577f754d1eb9116c094266",
"sha256:7d26b36a9c4bce53b9cfe42e67849ae3c5c23558bc08363e53ffd6d94f4ff4d2",
"sha256:83b427ad2bfa0b9705e02a83d8d607d2c2f01889eb138168e462a3a052c42368",
"sha256:923d03c84534078386cf50193057aae98fa94cace8ea7580b74754493fda73ad",
"sha256:b773715609649a1a180025213f67ffdeb5a4878c784293ada300ee95a1f3257b",
"sha256:baff149c174e9108d4a2fee192c496711be85534eab63adb122f93e70aa35431",
"sha256:bca9d118b1014b4c2d19319b10a3ebed508ff649396ce1855e1c96528d9b2fa9",
"sha256:ce580c28845581535dc6000fc7c35fdadf8bea7ccb57d6321b044508e9ba0685",
"sha256:d34923a569e70224d88e6682490e24c842907ba2c948c5fd26185413cbe0cd96",
"sha256:dd9f0e531a049d8b35ec5e6c68a37f1ba6ec3a591415e6804cbdf652793d15d7",
"sha256:ecb805cbfe9102f3fd3d2ef16dfe5ae9e2d7a7dfbba92f4ff1e16ac9784dbfb0",
"sha256:ede9aad2197a0202caff35d417b671f5f91a3631477441076082a17c94edd846",
"sha256:ef2d1fc370400e0aa755aab0b20cf4f1d0e934e7fd5244f3dd4869078e4942b9",
"sha256:f2fec194a49bfaef42a548ee657362af5c7a640da757f6f452a35da7dd9f923c"
"sha256:02ca7bf899da57084041bb0f6095333e4d239948ad3169443f454add9f4e9cb4",
"sha256:096b82c5e0ea27ce9138bcbb205313343ee66a6e132f25c5ed67e2c8d960a1bc",
"sha256:0a920ff98cf1aac310470c644bc23b326402d3ef667ddafecb024e1713d485f1",
"sha256:17cae1730a782858a6e2758fd20dd0ef7567916c47757b694a06ffafdec20046",
"sha256:17e3950add54c882e032527795c625929613adbd2ce5162b94667334458b5a36",
"sha256:1f4f214337f6ee5825bf90a65d04d70aab05526c08191ab888cb5149501923c5",
"sha256:2e8f77db25b0a96af679e64ff9bf9dddb27d379c9900c3272f3041c4d1327c9d",
"sha256:4dffd405390a45ecb95ab5ab1c1b847553c18b0ef8ed01e10c1c8b1a76452916",
"sha256:6b899931a5648862c7b88c795eddff7588fb585e81cecce20f8d9da16eff96e0",
"sha256:726c17f3e0d7a7200718c9a890ccfeab391c9133e363a577a44717c85c71db27",
"sha256:760c12276fee05c36f95f8040180abc7fbebb9e5011447a97cdc289b5d6ab6fc",
"sha256:796685d3969815a633827c818863ee199440696b0961e200b011d79b9394bbe7",
"sha256:891fe897b49abb7db470c55664b198b1095e4943b9f82b7dcab317a19116cd38",
"sha256:a471628e20f03dcdfde00770eeaf9c77811f0c331c8805219ca7b87ac17576c5",
"sha256:a63b4fd3e2cabdcc9d918ed280bdde3e8e9641e04f3c59a2a3109644a07b9832",
"sha256:b0b84408d4eabc6de9dd1e1e0bc63e7731e890c0b378a62443e5741cfd0ae90a",
"sha256:be78485e5d5f3684e875dab60f40cddace2f5b2a8f7fede412358ab3214c3a6f",
"sha256:c27eaed872185f047bb7f7da2d21a7d8913457678c9a100a50db6da890bc28b9",
"sha256:c81cb40bff373ab7a7446d6bbca0190bccc5be3448b47b51d729e37799bb5692",
"sha256:d11874b3c33ee441059464711cd365b89fa1a9cf19ae75b0c189b01fbf735b84",
"sha256:e9c028b5897901361d81a4718d1db217b716424a0283afe9d6735fe0caf70f79",
"sha256:fe489d486cd00b739be826e8c1be188ddb74c7a1ca784d93d06fda882a6a1681"
],
"version": "==4.3.4"
"version": "==4.4.1"
},
"markupsafe": {
"hashes": [
@ -162,19 +152,21 @@
},
"pyyaml": {
"hashes": [
"sha256:57acc1d8533cbe51f6662a55434f0dbecfa2b9eaf115bede8f6fd00115a0c0d3",
"sha256:588c94b3d16b76cfed8e0be54932e5729cc185caffaa5a451e7ad2f7ed8b4043",
"sha256:68c8dd247f29f9a0d09375c9c6b8fdc64b60810ebf07ba4cdd64ceee3a58c7b7",
"sha256:70d9818f1c9cd5c48bb87804f2efc8692f1023dac7f1a1a5c61d454043c1d265",
"sha256:86a93cccd50f8c125286e637328ff4eef108400dd7089b46a7be3445eecfa391",
"sha256:a0f329125a926876f647c9fa0ef32801587a12328b4a3c741270464e3e4fa778",
"sha256:a3c252ab0fa1bb0d5a3f6449a4826732f3eb6c0270925548cac342bc9b22c225",
"sha256:b4bb4d3f5e232425e25dda21c070ce05168a786ac9eda43768ab7f3ac2770955",
"sha256:cd0618c5ba5bda5f4039b9398bb7fb6a317bb8298218c3de25c47c4740e4b95e",
"sha256:ceacb9e5f8474dcf45b940578591c7f3d960e82f926c707788a570b51ba59190",
"sha256:fe6a88094b64132c4bb3b631412e90032e8cfe9745a58370462240b8cb7553cd"
"sha256:0113bc0ec2ad727182326b61326afa3d1d8280ae1122493553fd6f4397f33df9",
"sha256:01adf0b6c6f61bd11af6e10ca52b7d4057dd0be0343eb9283c878cf3af56aee4",
"sha256:5124373960b0b3f4aa7df1707e63e9f109b5263eca5976c66e08b1c552d4eaf8",
"sha256:5ca4f10adbddae56d824b2c09668e91219bb178a1eee1faa56af6f99f11bf696",
"sha256:7907be34ffa3c5a32b60b95f4d95ea25361c951383a894fec31be7252b2b6f34",
"sha256:7ec9b2a4ed5cad025c2278a1e6a19c011c80a3caaac804fd2d329e9cc2c287c9",
"sha256:87ae4c829bb25b9fe99cf71fbb2140c448f534e24c998cc60f39ae4f94396a73",
"sha256:9de9919becc9cc2ff03637872a440195ac4241c80536632fffeb6a1e25a74299",
"sha256:a5a85b10e450c66b49f98846937e8cfca1db3127a9d5d1e31ca45c3d0bef4c5b",
"sha256:b0997827b4f6a7c286c01c5f60384d218dca4ed7d9efa945c3e1aa623d5709ae",
"sha256:b631ef96d3222e62861443cc89d6563ba3eeb816eeb96b2629345ab795e53681",
"sha256:bf47c0607522fdbca6c9e817a6e81b08491de50f3766a7a0e6a5be7905961b41",
"sha256:f81025eddd0327c7d4cfe9b62cf33190e1e736cc6e97502b3ec425f574b3e7a8"
],
"version": "==5.1.1"
"version": "==5.1.2"
},
"requests": {
"hashes": [
@ -207,18 +199,18 @@
},
"urllib3": {
"hashes": [
"sha256:b246607a25ac80bedac05c6f282e3cdaf3afb65420fd024ac94435cabe6e18d1",
"sha256:dbe59173209418ae49d485b87d1681aefa36252ee85884c31346debd19463232"
"sha256:3de946ffbed6e6746608990594d08faac602528ac7015ac28d33cee6a45b7398",
"sha256:9a107b99a5393caf59c7aa3c1249c16e6879447533d0887f4336dde834c7be86"
],
"version": "==1.25.3"
"version": "==1.25.6"
},
"wand": {
"hashes": [
"sha256:c52d647a34205f9b3948baae739db461a7379a04818548fe8042b5ce751ea6ea",
"sha256:e2e08e19a37c61e85eaa307fe319889af46fe4cac6c23e3ae668b96be3e497ff"
"sha256:13a96818a2f89e7684704ba3bfc20bdb21a15e08736c3fdf74035eeaeefd7873",
"sha256:8cfa30a71a3c65efd1d90678790297fec287300715ebcdf17e119fe075148dd0"
],
"index": "pypi",
"version": "==0.5.4"
"version": "==0.5.7"
}
},
"develop": {}

View file

@ -0,0 +1,5 @@
<!-- Generated by IcoMoon.io -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<title>mastodon</title>
<path fill="#3088d4" d="M15.462 5.253c0-3.471-2.274-4.488-2.274-4.488-1.147-0.527-3.116-0.748-5.161-0.765h-0.051c-2.045 0.017-4.013 0.238-5.16 0.765 0 0-2.274 1.017-2.274 4.488 0 0.795-0.015 1.745 0.010 2.753 0.083 3.395 0.623 6.739 3.761 7.57 1.447 0.383 2.689 0.463 3.69 0.408 1.815-0.1 2.833-0.648 2.833-0.648l-0.060-1.317s-1.297 0.409-2.753 0.359c-1.443-0.049-2.966-0.155-3.199-1.927-0.020-0.145-0.032-0.313-0.032-0.483 0-0.005 0-0.009 0-0.014v0.001s1.417 0.347 3.211 0.429c1.097 0.050 2.127-0.065 3.172-0.189 2.005-0.239 3.75-1.475 3.969-2.603 0.345-1.777 0.317-4.338 0.317-4.338zM12.779 9.725h-1.665v-4.079c0-0.86-0.362-1.296-1.085-1.296-0.8 0-1.201 0.517-1.201 1.541v2.233h-1.655v-2.233c0-1.024-0.401-1.541-1.201-1.541-0.723 0-1.085 0.437-1.085 1.296v4.079h-1.665v-4.202c0-0.859 0.219-1.542 0.658-2.047 0.453-0.505 1.046-0.764 1.783-0.764 0.852 0 1.497 0.327 1.924 0.983l0.414 0.695 0.415-0.695c0.427-0.655 1.072-0.983 1.924-0.983 0.736 0 1.329 0.259 1.783 0.764 0.439 0.505 0.657 1.187 0.657 2.047v4.203z"></path>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

202
nasg.py
View file

@ -252,6 +252,71 @@ class Gone(object):
)
class FediverseStats(object):
def __init__(self, postcount=0, commentcount=0):
self.postcount = postcount
self.commentcount = commentcount
self.nodeinfo_json = "nodeinfo.json"
async def render(self):
writepath(
os.path.join(
settings.paths.build, ".well-known", "nodeinfo"
),
json.dumps(self.metanodeinfo, indent=4, ensure_ascii=False),
)
writepath(
os.path.join(
settings.paths.build, ".well-known", self.nodeinfo_json
),
json.dumps(self.nodeinfo, indent=4, ensure_ascii=False),
)
@property
def metanodeinfo(self):
return {
"links": [
{
"href": f"{settings.site.url}/.well-known/{self.nodeinfo_json}",
"rel": "http://nodeinfo.diaspora.software/ns/schema/2.0",
}
]
}
@property
def nodeinfo(self):
return {
"metadata": {
"nodeName": settings.site.name,
"software": {
"homepage": "https://github.com/petermolnar/nasg",
"github": "https://github.com/petermolnar/nasg",
"follow": f"{settings.site.url}",
},
},
"openRegistrations": False,
"protocols": ["webmention", "activitypub"],
"services": {
"inbound": [],
"outbound": ["github", "flickr"],
},
"software": {
"name": "nasg",
"version": "6.6"
},
"usage": {
"localPosts": self.postcount,
"localComments": self.commentcount,
"users": {
"total": 1,
"activeHalfyear": 1,
"activeMonth": 1,
},
},
"version": "2.0",
}
class Redirect(Gone):
"""
Redirect object for entries that moved
@ -328,7 +393,10 @@ class MarkdownDoc(object):
c = self.content
if hasattr(self, "images") and len(self.images):
for match, img in self.images.items():
c = c.replace(match, str(img))
if self.is_photo:
c = c.replace(match, "")
else:
c = c.replace(match, str(img))
c = str(PandocMD2HTML(c))
c = RE_PRECODE.sub(
'<pre><code lang="\g<1>" class="language-\g<1>">', c
@ -453,17 +521,19 @@ class WebImage(object):
"caption": self.caption,
"headline": self.title,
"representativeOfPage": False,
# "text": str(self)
}
for k, v in self.exif.items():
r["exifData"].append(
{"@type": "PropertyValue", "name": k, "value": v}
)
if self.is_photo:
licence = settings.licence["_default"]
r.update(
{
"creator": settings.author,
"copyrightHolder": settings.author,
"license": settings.licence["_default"],
"license": f"https://spdx.org/licenses/{licence}.html",
}
)
if self.is_mainimg:
@ -483,12 +553,12 @@ class WebImage(object):
{
"@context": "http://schema.org",
"@type": "GeoCoordinates",
"latitude": self.exif[
"GPSLatitude"
],
"longitude": self.exif[
"GPSLongitude"
],
"latitude": round(
self.exif["GPSLatitude"], 4
),
"longitude": round(
self.exif["GPSLongitude"], 4
),
}
),
}
@ -1000,6 +1070,7 @@ class Singular(MarkdownDoc):
urls = self.meta.get("syndicate", [])
if not self.is_page:
urls.append("https://fed.brid.gy/")
urls.append("https://brid.gy/publish/mastodon")
if self.is_photo:
urls.append("https://brid.gy/publish/flickr")
return urls
@ -1134,7 +1205,9 @@ class Singular(MarkdownDoc):
if len(self.images):
r["image"] = []
for img in list(self.images.values()):
r["image"].append(img.jsonld)
imgld = img.jsonld
imgld["text"] = str(img)
r["image"].append(imgld)
if self.is_reply:
r.update(
@ -1158,7 +1231,7 @@ class Singular(MarkdownDoc):
{
"@context": "http://schema.org",
"@type": "InteractAction",
"url": url,
"target": url,
}
)
@ -1300,14 +1373,6 @@ class Singular(MarkdownDoc):
)
del g
j = settings.site.copy()
j.update({"mainEntity": self.jsonld})
writepath(
os.path.join(self.renderdir, settings.filenames.json),
json.dumps(j, indent=4, ensure_ascii=False),
)
del j
class Home(Singular):
def __init__(self, fpath):
@ -1578,6 +1643,89 @@ class IndexPHP(PHPFile):
writepath(self.renderfile, r)
class Micropub(PHPFile):
@property
def renderfile(self):
return os.path.join(settings.paths.get("build"), "micropub.php")
@property
def templatefile(self):
return "%s.j2.php" % (self.__class__.__name__)
async def _render(self):
r = J2.get_template(self.templatefile).render(
{
"wallabag": keys.wallabag,
"site": settings.site,
}
)
writepath(self.renderfile, r)
class WorldMap(object):
def __init__(self):
self.data = {}
self.mtime = 0
def add(self, post):
if not post.is_photo:
return
if not post.photo.jsonld.locationCreated:
return
lat = post.photo.jsonld.locationCreated.geo.latitude
lon = post.photo.jsonld.locationCreated.geo.longitude
k = (lat, lon)
content = f'<p><a href="{post.url}"><img src="{post.photo.src}" style="width: 150px; height: auto" /><br />{post.title}</a></p>'
# d = {"latitude": nlat, "longitude": nlon, "popup": content}
if k in self.data:
self.data[k].append(content)
else:
self.data[k] = [content]
self.mtime = max(post.dt.timestamp, self.mtime)
@property
def exists(self):
if settings.args.get("force"):
return False
if not os.path.exists(self.renderfile):
return False
if mtime(self.renderfile) >= self.mtime:
return True
return False
@property
def renderfile(self):
return os.path.join(
settings.paths.get("build"), settings.filenames.worldmap
)
@property
def template(self):
return "%s.j2.html" % (self.__class__.__name__)
@property
def tmplvars(self):
return {
"token": keys.mapbox,
"site": settings.site,
"menu": settings.menu,
"meta": settings.meta,
"site": settings.site,
"geo": self.data,
}
async def render(self):
if self.exists:
return
logger.info(
"rendering %s to %s", self.__class__, self.renderfile
)
writepath(
self.renderfile,
J2.get_template(self.template).render(self.tmplvars),
)
class Category(dict):
def __init__(self, name=""):
self.name = name
@ -1863,7 +2011,8 @@ class Category(dict):
fe.link(
href=post.url, rel="alternate", type="text/html"
)
fe.content(src=post.url, type="text/html")
# fe.content(src=post.url, type="text/html")
fe.content(post.html_content, type="html")
if len(post.summary):
fe.summary(post.summary)
fg.add_entry(fe)
@ -2343,7 +2492,6 @@ class WebmentionIO(object):
webmention.get("source"),
)
fdir = glob.glob(
os.path.join(settings.paths.get("content"), "*", slug)
)
@ -2371,8 +2519,7 @@ class WebmentionIO(object):
author = webmention.get("data", {}).get("author", None)
if not author:
logger.error(
"missing author info on webmention: %s",
webmention,
"missing author info on webmention: %s", webmention
)
return
meta = {
@ -2434,6 +2581,9 @@ def make():
categories = {}
frontposts = Category()
home = Home(settings.paths.get("home"))
worldmap = WorldMap()
postcount = 0
commentcount = 0
for e in glob.glob(os.path.join(content, "*", "*.url")):
post = Redirect(e)
@ -2446,6 +2596,9 @@ def make():
):
post = Singular(e)
if not post.is_future:
postcount = postcount + 1
commentcount = commentcount + len(post.comments)
worldmap.add(post)
for i in post.to_ping:
outbox.append(i)
if not (
@ -2518,6 +2671,11 @@ def make():
queue.put(frontposts.render_feeds())
queue.put(home.render())
queue.put(worldmap.render())
fediversemockery = FediverseStats(postcount, commentcount)
queue.put(fediversemockery.render())
micropub = Micropub()
queue.put(micropub.render())
queue.run()
# copy static files

View file

@ -72,6 +72,7 @@ site = nameddict(
"https://wa.me/447592011721",
"https://t.me/petermolnar",
"https://twitter.com/petermolnar",
"https://mastodon.social/@petermolnar"
],
"follows": "https://petermolnar.net/following.opml",
},
@ -93,29 +94,32 @@ site = nameddict(
"@type": "SearchAction",
"target": "https://petermolnar.net/search.php?q={q}",
"query-input": "required name=q",
"url": "https://petermolnar.net/search.php",
},
{
"@context": "http://schema.org",
"@type": "FollowAction",
"url": "https://petermolnar.net/follow/",
"name": "follow",
"target": "https://petermolnar.net/follow/",
},
{
"@context": "http://schema.org",
"@type": "DonateAction",
"description": "Monzo",
"name": "monzo",
"url": "https://monzo.me/petermolnar/",
"recipient": author,
"target": "https://monzo.me/petermolnar/",
"price": "3",
"priceCurrency": "GBP"
},
{
"@context": "http://schema.org",
"@type": "DonateAction",
"description": "Paypal",
"name": "paypal",
"url": "https://paypal.me/petermolnar/",
"recipient": author,
"target": "https://paypal.me/petermolnar/",
"price": "3",
"priceCurrency": "GBP"
},
],
}
@ -147,12 +151,12 @@ menu = nameddict(
meta = nameddict(
{
"webmention": "https://webmention.io/petermolnar.net/webmention",
#"pingback": "https://webmention.io/petermolnar.net/xmlrpc",
"pingback": "https://webmention.io/petermolnar.net/xmlrpc",
"hub": "https://petermolnar.superfeedr.com/",
"authorization_endpoint": "https://indieauth.com/auth",
"token_endpoint": "https://tokens.indieauth.com/token",
"micropub": "https://hooks.zapier.com/hooks/catch/3982452/o3hpw1x/",
#'microsub': 'https://aperture.p3k.io/microsub/83'
"micropub": "https://petermolnar.net/micropub.php",
"microsub": "https://aperture.p3k.io/microsub/83"
}
)
@ -184,6 +188,7 @@ filenames = nameddict(
"html": "index.html",
"gopher": "gophermap",
"sitemap": "sitemap.txt",
"worldmap": "map.html"
}
)

181
templates/Micropub.j2.php Normal file
View file

@ -0,0 +1,181 @@
<?php
function _syslog($msg) {
$trace = debug_backtrace();
$caller = $trace[1];
$parent = $caller['function'];
if (isset($caller['class'])) {
$parent = $caller['class'] . '::' . $parent;
}
return error_log( "{$parent}: {$msg}" );
}
function notimplemented() {
header('HTTP/1.1 501 Not Implemented');
die("This functionality is yet to be implemented");
}
function unauthorized($text) {
header('HTTP/1.1 401 Unauthorized');
_syslog("unauth:" . $text);
die($text);
}
function badrequest($text) {
header('HTTP/1.1 400 Bad Request');
_syslog("badreq:" . $text);
die($text);
}
function remoteerror($text) {
header('HTTP/1.1 421 Misdirected Request');
_syslog("remote_err:" . $text);
die($text);
}
function httpok($text) {
header('HTTP/1.1 200 OK');
_syslog("ok:" . $text);
echo($text);
exit(0);
}
function accepted() {
header('HTTP/1.1 202 Accepted');
_syslog("accepted:");
exit(0);
}
function verify_token($token) {
$request = curl_init();
curl_setopt($request, CURLOPT_URL, 'https://tokens.indieauth.com/token');
curl_setopt($request, CURLOPT_HTTPHEADER, array(
'Content-Type: application/x-www-form-urlencoded',
"Authorization: Bearer {$token}"
));
curl_setopt($request, CURLOPT_RETURNTRANSFER, 1);
$response = curl_exec($request);
curl_close($request);
parse_str(urldecode($response), $verification);
if (! isset($verification['scope']) ) {
unauthorized('missing "scope"');
}
if (! isset($verification['me']) ) {
unauthorized('missing "me"');
}
if ( ! stristr($verification['me'], '{{ site.name }}') ) {
unauthorized('wrong domain');
}
}
function save_to_wallabag($url) {
$wallabag_url = "{{ wallabag["url"] }}";
$data = array(
"client_id" => "{{ wallabag["client_id"] }}",
"client_secret" => "{{ wallabag["client_secret"] }}",
"username" => "{{ wallabag["username"] }}",
"password" => "{{ wallabag["password"] }}",
"grant_type" => "password"
);
$request = curl_init();
curl_setopt($request, CURLOPT_URL, "{$wallabag_url}/oauth/v2/token");
curl_setopt($request, CURLOPT_POST, 1);
curl_setopt($request, CURLOPT_POSTFIELDS,http_build_query($data));
curl_setopt($request, CURLOPT_RETURNTRANSFER, 1);
$response = curl_exec($request);
curl_close($request);
try {
$wallabag_token = json_decode($response, true);
} catch (Exception $e) {
remoteerror("failed to parse response from wallabag: " . $response);
}
if (! isset($wallabag_token['access_token']) ) {
remoteerror("failed to obtain access token from wallabag: " . $response);
}
$data = array(
"url" => $url,
"archive" => 1
);
$headers = array(
'Content-Type: application/x-www-form-urlencoded',
"Authorization: Bearer ". $wallabag_token["access_token"]
);
$request = curl_init();
curl_setopt($request, CURLOPT_URL, "{$wallabag_url}/api/entries");
curl_setopt($request, CURLOPT_POST, 1);
curl_setopt($request, CURLOPT_POSTFIELDS,http_build_query($data));
curl_setopt($request, CURLOPT_HTTPHEADER, $headers);
curl_setopt($request, CURLOPT_RETURNTRANSFER, 1);
$response = curl_exec($request);
curl_close($request);
try {
$is_saved = json_decode($response, true);
accepted();
} catch (Exception $e) {
remoteerror("failed to parse response to save from wallabag: " . $response);
}
}
if (!empty($_GET)) {
if ( ! isset($_GET['q']) ) {
badrequest('please POST a micropub request');
}
if ( isset($_GET['q']['config']) ) {
httpok(json_encode(array('tags' => array())));
}
if(isset($_GET['q']['syndicate-to'])) {
httpok(json_encode(array('syndicate-to' => array())));
}
badrequest('please POST a micropub request');
}
$raw = file_get_contents("php://input");
try {
$decoded = json_decode($raw, true);
} catch (Exception $e) {
_syslog('failed to decode JSON, trying decoding form data');
try {
parse_str($raw, $decoded);
}
catch (Exception $e) {
_syslog('failed to decoding form data as well');
badrequest('invalid POST contents');
}
}
$token = '';
if (isset($decoded['access_token'])) {
$token = $decoded['access_token'];
unset($decoded['access_token']);
}
elseif (isset($_SERVER['HTTP_AUTHORIZATION'])) {
$token = trim(str_replace('Bearer', '', $_SERVER['HTTP_AUTHORIZATION']));
}
if (empty($token)) {
unauthorized('missing token');
}
else {
verify_token($token);
}
$source_url = '';
if(isset($decoded["properties"]) && isset($decoded["properties"]["like-of"])) {
$source_url = $decoded["properties"]["like-of"];
}
elseif(isset($decoded["like-of"])) {
$source_url = $decoded["like-of"];
}
/* deal with like: forward it to wallabag */
if(!empty($source_url)) {
save_to_wallabag($source_url);
}
notimplemented();

View file

@ -7,7 +7,6 @@
{% block meta %}
<meta name="description" content="{{ post.description|striptags|e }}" />
<link rel="canonical" href="{{ post.url }}" />
<link rel="alternate" type="application/ld+json" href="{{ post.url }}{{ fnames.json }}" />
<link rel="alternate" type="text/plain" href="{{ post.url }}{{ fnames.txt }}" />
<meta property="og:title" content="{{ post.headline }}" />
<meta property="og:type" content="article" />
@ -33,7 +32,9 @@
<meta NAME="DC.Creator" content="{{ post.author.name }}" />
<meta NAME="DC.Date" content="{{ post.datePublished }}" />
<meta NAME="DC.Description" content="{{ post.description|striptags|e }}" />
<script type="application/ld+json">
{{ post|tojson(indent=4) }}
</script>
{% if post['@type'] == 'TechArticle' %}
<style media="all">
{% include('prism.css') %}
@ -67,14 +68,14 @@
<span id="syndication">
this post on other sites:
{% for url in post.sameAs %}
<a class="u-syndication" href="{{ url }}"><svg width="16" height="16" aria-label="{{ url|extractdomain }}"><use xlink:href="#icon-{{ url|extractdomain }}"</svg></a>
<a class="u-syndication" href="{{ url }}"><svg width="16" height="16" aria-label="{{ url|extractdomain }}"><use xlink:href="#icon-{{ url|extractdomain }}" /></svg></a>
{% endfor %}
</span>
{% endif %}
{% if 'WebPage' != post['@type'] %}
{% for action in post.potentialAction %}
{% if 'InteractAction' == action['@type'] %}
<a href="{{ action.url }}"></a>
<a href="{{ action.target }}"></a>
{% endif %}
{% endfor %}
{% endif %}
@ -127,8 +128,18 @@
</section>
{% endif %}
<section class="e-content entry-content">
{{ post.text|relurl(baseurl) }}
<section>
{% if post.image|length %}
{% for image in post.image %}
{% if image.representativeOfPage %}
{{ image.text|relurl(baseurl) }}
{% endif %}
{% endfor %}
{% endif %}
<div class="e-content entry-content">
{{ post.text|relurl(baseurl) }}
</div>
</section>
{% if 'WebPage' != post['@type'] %}
@ -205,8 +216,8 @@
{% if post.image[0].locationCreated %}
from the location
<a class="h-geo" href="https://www.openstreetmap.org/#map=14/{{ post.image[0].locationCreated.geo.longitude }}/{{ post.image[0].locationCreated.geo.latitude }}">
<span class="p-longitude">{{ post.image[0].locationCreated.geo.longitude }}</span>,
<span class="p-latitude">{{ post.image[0].locationCreated.geo.latitude }}</span>
<span class="p-latitude">{{ post.image[0].locationCreated.geo.latitude }}</span>,
<span class="p-longitude">{{ post.image[0].locationCreated.geo.longitude }}</span>
</a>
{% endif %}
{% endif %}

View file

@ -1,6 +1,7 @@
<figure class="photo">
<link rel="license" href="{{ license }}" />
{% if url != thumbnail.url %}
<a href="{{ url }}"{% if representativeOfPage %} class="u-featured"{% endif %}>
<a href="{{ url }}"{% if representativeOfPage %} class="u-photo u-featured"{% endif %}>
{% endif %}
<img src="{{ thumbnail.url }}" title="{{ headline }}" alt="{{ headline }}" width="{{ thumbnail.width }}" height="{{ thumbnail.height }}" />
{% if url != thumbnail.url %}

View file

@ -0,0 +1,25 @@
{% extends "base.j2.html" %}
{% block title %}Posts World Map - {{ site.name }}{% endblock %}
{% block meta %}
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.5.1/dist/leaflet.css" integrity="sha512-xwE/Az9zrjBIphAcBb3F6JVqxf46+CDLwfLMHloNu6KEQCAWi6HcDUbeOfBIptF7tcCzusKFjFw2yuvEpDL9wQ==" crossorigin=""/>
<script src="https://unpkg.com/leaflet@1.5.1/dist/leaflet.js" integrity="sha512-GffPMF3RvMeYyc1LWMHtK8EbPv0iNZ8/oTtHPx9/cc2ILxQ+u905qIwdpULaqDkyBKgOaB57QTMg7ztg8Jm2Og==" crossorigin=""></script>
{% endblock %}
{% block content %}
<div id="map" style="margin: 2em auto; width: 90vw; height: 80vh;"></div>
<script>
var mymap = L.map('map').setView([48, 58], 4);
L.tileLayer('https://api.tiles.mapbox.com/v4/{id}/{z}/{x}/{y}.png?access_token={{ token }}', {
maxZoom: 18,
attribution: '© <a href="https://www.mapbox.com/feedback/">Mapbox</a> © <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>',
id: 'mapbox.streets'
}).addTo(mymap);
{% for (lat, lon), content in geo.items() %}
L.marker([{{ lat }},{{ lon }}]).addTo(mymap).bindPopup("{{ content|join|replace('"', '\\\"') }}", {maxHeight: 240});
{% endfor %}
</script>
{% endblock %}

View file

@ -13,8 +13,9 @@
{% for key, value in meta.items() %}
<link rel="{{ key }}" href="{{ value }}" />
{% endfor %}
{% block meta %}{% endblock %}
<script type="application/ld+json">
{{ site|tojson(indent=4) }}
</script>
<style media="all">
{% include('style.css') %}
</style>
@ -27,7 +28,7 @@
<style media="print">
{% include('style-print.css') %}
</style>
{% block meta %}{% endblock %}
</head>
<body>
@ -58,7 +59,7 @@
<div id="header-forms">
{% for action in site.potentialAction %}
{% if 'SearchAction' == action['@type'] %}
<form id="search" role="search" method="get" action="{{ action.url|relurl(baseurl) }}">
<form id="search" role="search" method="get" action="{{ action.target|relurl(baseurl) }}">
<label for="qsub">
<input type="submit" value="search" id="qsub" name="qsub" />
<svg width="16" height="16">
@ -97,7 +98,8 @@
{% for action in site.potentialAction %}
{% if 'FollowAction' == action['@type'] %}
<li>
<a href="{{ action.url }}">
<input type="hidden" value="" name="{{ action['name'] }}" id="{{ action['name'] }}" />
<a href="{{ action.target }}">
<svg width="16" height="16"><use xlink:href="#icon-{{ action['@type'] }}" /></svg>
{{ action.name }}
</a>
@ -125,6 +127,20 @@
github
</a>
</li>
{% elif 'twitter' in url %}
<li>
<a rel="me" href="{{ url }}">
<svg width="16" height="16"><use xlink:href="#icon-twitter" /></svg>
twitter
</a>
</li>
{% elif 'mastodon' in url %}
<li>
<a rel="me" href="{{ url }}">
<svg width="16" height="16"><use xlink:href="#icon-mastodon.social" /></svg>
mastodon
</a>
</li>
{% endif %}
{% endfor %}
</ul>
@ -139,7 +155,8 @@
{% for action in site.potentialAction %}
{% if 'DonateAction' == action['@type'] %}
<li>
<a href="{{ action.url }}">
<input type="hidden" value="{{ action['price'] }}" name="{{ action['name'] }}" id="{{ action['name'] }}" />
<a href="{{ action.target }}">
<svg width="16" height="16"><use xlink:href="#icon-{{ action['name'] }}" /></svg>
{{ action.description }}
</a>

View file

@ -40,8 +40,18 @@
</span>
</section>
{% else %}
<section class="e-content entry-content">
<section>
{% if post.image|length %}
{% for image in post.image %}
{% if image.representativeOfPage %}
{{ image.text|relurl(baseurl) }}
{% endif %}
{% endfor %}
{% endif %}
<div class="e-content entry-content">
{{ post.text|relurl(baseurl) }}
</div>
</section>
{% endif %}

View file

@ -366,6 +366,26 @@ li p {
text-align: right;
}
#fediversefollow label {
font-weight: bold;
}
#fediversefollow input {
display: block;
width: 30em;
max-width: 90%;
font-size:1em;
padding: 0.6em;
background-color: transparent;
border: 2px solid #f90;
}
#fediversefollow input[type="submit"] {
cursor: pointer;
margin-top: 0.3em;
width: auto;
font-weight: bold;
}
@media all and (max-width: 51em) {
#header > * {
display: block;
@ -428,4 +448,4 @@ li p {
object-fit:contain;
}
*/
}
}

View file

@ -77,148 +77,6 @@
<symbol id="icon-star" viewBox="0 0 16 16">
<path d="M16 6.204l-5.528-0.803-2.472-5.009-2.472 5.009-5.528 0.803 4 3.899-0.944 5.505 4.944-2.599 4.944 2.599-0.944-5.505 4-3.899z"></path>
</symbol>
<symbol id="button-indieweb" viewBox="0 0 80 15">
<rect width="80" height="15" fill="#666"/>
<rect x="1" y="1" width="78" height="13" fill="#fff"/>
<path d="m3 4v2h7v-2h-7zm0 3v4h7v-4h-7z" fill="#fc0d1b"/>
<path d="m11 4v2h1v-2h-1zm1 2v3h1v-3h-1zm1 0h1v3h1v2h2v-2h1v-3h1v-2h-6v2zm1 3h-1v2h1v-2z" fill="#fc5d20"/>
<polygon points="21 4 25 4 25 5 26 5 26 7 22 7 22 8 26 8 26 10 25 10 25 11 21 11 21 10 20 10 20 8 19 8 19 7 20 7 20 5 21 5" fill="#fdb02a"/>
<rect x="29" y="2" width="49" height="1" fill="#fda829"/>
<rect x="29" y="3" width="49" height="1" fill="#fd9c27"/>
<rect x="29" y="4" width="49" height="1" fill="#fd9025"/>
<rect x="29" y="5" width="49" height="1" fill="#fd8124"/>
<rect x="29" y="6" width="49" height="1" fill="#fd7222"/>
<rect x="29" y="7" width="49" height="1" fill="#fd6420"/>
<rect x="29" y="8" width="49" height="1" fill="#fc561f"/>
<rect x="29" y="9" width="49" height="1" fill="#fc481e"/>
<rect x="29" y="10" width="49" height="1" fill="#fc371d"/>
<rect x="29" y="11" width="49" height="1" fill="#fc291c"/>
<rect x="29" y="12" width="49" height="1" fill="#fc1c1c"/>
<g fill="#fff">
<path d="m34 5h1v5h-1z"/>
<path d="m37 5h1v1h1v1h1v1h1v-3h1v5h-1v-1h-1v-1h-1v-1h-1v3h-1z"/>
<path d="m44 5v5h3v-1h-2v-3h2v-1h-3zm3 1v3h1v-3h-1z"/>
<path d="m50 5h1v5h-1z"/>
<path d="m53 5h3v1h-2v1h2v1h-2v1h2v1h-3z"/>
<path d="m58 5h1v4h1v-3h1v3h1v-4h1v4h-1v1h-1v-1h-1v1h-1v-1h-1z"/>
<path d="m65 5h3v1h-2v1h2v1h-2v1h2v1h-3z"/>
<path d="m70 5v5h3v-1h-2v-1h2v-1h-2v-1h2v-1h-3zm3 1v1h1v-1h-1zm0 2v1h1v-1h-1z"/>
</g>
</symbol>
<symbol id="button-webmention" viewBox="0 0 80 15">
<rect width="80" height="15" fill="#666"/>
<rect x="1" y="1" width="78" height="13" fill="#fff" style="paint-order:markers fill stroke"/>
<path d="m13 1v1h-1v1h-1v1h1 1v1h-1v2h-1v2h-1v-3h-1v-2h-1v2h-1v3h-1v-2h-1v-3h-1v-2h-1-1v2h1v3h1v3h1v3h1 1v-3h1v-2h1v2h1v3h1 1v-3h1v-3h1v-3h1 1v-1h-1v-1h-1v-1h-1z" fill="#610371"/>
<rect x="19" y="2" width="59" height="11" fill="#850e9a"/>
<g fill="#fff">
<path d="m33 5v5h3v-1h-2v-1h2v-1h-2v-1h2v-1h-3zm3 1v1h1v-1h-1zm0 2v1h1v-1h-1z"/>
<path d="m28 5h3v1h-2v1h2v1h-2v1h2v1h-3z"/>
<path d="m21 5h1v4h1v-3h1v3h1v-4h1v4h-1v1h-1v-1h-1v1h-1v-1h-1z"/>
<path d="m39 5h1v1h1v1h1v-1h1v-1h1v5h-1v-3h-1v1h-1v-1h-1v3h-1z"/>
<path d="m51 5h1v1h1v1h1v1h1v-3h1v5h-1v-1h-1v-1h-1v-1h-1v3h-1z"/>
<path d="m58 5h3v1h-1v4h-1v-4h-1"/>
<path d="m63 5h1v5h-1z"/>
<path d="m67 5v1h2v-1zm2 1v3h1v-3zm0 3h-2v1h2zm-2 0v-3h-1v3z"/>
<path d="m71 5h1v1h1v1h1v1h1v-3h1v5h-1v-1h-1v-1h-1v-1h-1v3h-1z"/>
<path d="m46 5h3v1h-2v1h2v1h-2v1h2v1h-3z"/>
</g>
</symbol>
<symbol id="button-microformats" viewBox="0 0 80 15">
<rect width="80" height="15" fill="#666"/>
<rect x="1" y="1" width="78" height="13" fill="#fff"/>
<rect x="18" y="2" width="2" height="11" fill="#5d8f17"/>
<rect x="20" y="2" width="2" height="11" fill="#609218"/>
<rect x="22" y="2" width="2" height="11" fill="#639519"/>
<rect x="24" y="2" width="2" height="11" fill="#66981a"/>
<rect x="26" y="2" width="2" height="11" fill="#699b1b"/>
<rect x="28" y="2" width="2" height="11" fill="#6c9e1b"/>
<rect x="30" y="2" width="2" height="11" fill="#6fa11c"/>
<rect x="32" y="2" width="2" height="11" fill="#72a51d"/>
<rect x="34" y="2" width="2" height="11" fill="#76a81e"/>
<rect x="36" y="2" width="2" height="11" fill="#78aa1f"/>
<rect x="38" y="2" width="2" height="11" fill="#7cae20"/>
<rect x="40" y="2" width="2" height="11" fill="#7eb120"/>
<rect x="42" y="2" width="2" height="11" fill="#82b421"/>
<rect x="44" y="2" width="2" height="11" fill="#85b722"/>
<rect x="46" y="2" width="2" height="11" fill="#88ba23"/>
<rect x="48" y="2" width="2" height="11" fill="#8bbd24"/>
<rect x="50" y="2" width="2" height="11" fill="#8ec125"/>
<rect x="52" y="2" width="2" height="11" fill="#91c325"/>
<rect x="54" y="2" width="2" height="11" fill="#94c626"/>
<rect x="56" y="2" width="2" height="11" fill="#97ca27"/>
<rect x="58" y="2" width="2" height="11" fill="#9acd28"/>
<rect x="60" y="2" width="2" height="11" fill="#9dd029"/>
<rect x="62" y="2" width="2" height="11" fill="#a0d32a"/>
<rect x="64" y="2" width="2" height="11" fill="#a4d62b"/>
<rect x="66" y="2" width="2" height="11" fill="#a6d92b"/>
<rect x="68" y="2" width="2" height="11" fill="#a8db2c"/>
<rect x="70" y="2" width="2" height="11" fill="#acdf2d"/>
<rect x="72" y="2" width="2" height="11" fill="#b0e32e"/>
<rect x="74" y="2" width="2" height="11" fill="#b3e62f"/>
<rect x="76" y="2" width="2" height="11" fill="#b6e92f"/>
<polygon points="4 4 6 4 6 9 7 9 7 10 12 10 12 12 11 12 11 13 4 13 4 12 3 12 3 5 4 5" fill="#5c8d17"/>
<polygon points="7 3 9 3 9 6 10 6 10 7 13 7 13 9 8 9 8 8 7 8" fill="#8dc024"/>
<polygon points="10 2 13 2 13 3 14 3 14 6 11 6 11 5 10 5" fill="#a5d82b"/>
<g fill="#fff">
<path d="m20 5h1v1h1v1h1v-1h1v-1h1v5h-1v-3h-1v1h-1v-1h-1v3h-1z"/>
<path d="m26 5h1v5h-1z"/>
<path d="m29 5h2v1h1v1h-1v-1h-2v3h2v-1h1v1h-1v1h-2v-1h-1v-3h1z"/>
<path d="m33 5v5h1v-2h1v1h1v-2h-2v-1h2v-1h-3zm3 1v1h1v-1h-1zm0 3v1h1v-1h-1z"/>
<path d="m39 5v1h2v-1h-2zm2 1v1 1 1h1v-1-1-1h-1zm0 3h-2v1h2v-1zm-2 0v-3h-1v3h1z"/>
<path d="m43 5h3v1h-2v1h2v1h-2v2h-1z"/>
<path d="m48 5v1h2v-1h-2zm2 1v1 1 1h1v-1-1-1h-1zm0 3h-2v1h2v-1zm-2 0v-3h-1v3h1z"/>
<path d="m52 5v5h1v-2h1v1h1v-2h-2v-1h2v-1h-3zm3 1v1h1v-1h-1zm0 3v1h1v-1h-1z"/>
<path d="m57 5h1v1h1v1h1v-1h1v-1h1v5h-1v-3h-1v1h-1v-1h-1v3h-1z"/>
<path d="m64 5v1h2v-1h-2zm2 1v1h-2v-1h-1v4h1v-2h2v2h1v-4h-1z"/>
<path d="m68 5h3v1h-1v4h-1v-4h-1"/>
<path d="m73 5h3v1h-3v1h2v1h1v1h-1v1h-3v-1h3v-1h-2v-1h-1v-1h1"/>
</g>
</symbol>
<symbol id="button-cc" viewBox="0 0 80 15">
<rect y="0" width="80" height="15" fill="#000"/>
<rect x="1" y="1" width="78" height="13" fill="#fff"/>
<rect x="2" y="2" width="76" height="11" fill="#000"/>
<g fill="#fff">
<path d="m42 5h1v1h1v1h1v-1h1v-1h1v5h-1v-3h-1v1h-1v-1h-1v3h-1z"/>
<path d="m31 5h2v1h1v1h-1v-1h-2v3h2v-1h1v1h-1v1h-2v-1h-1v-3h1z"/>
<path d="m37 5v1h2v-1zm2 1v3h1v-3zm0 3h-2v1h2zm-2 0v-3h-1v3z"/>
<path d="m57 5v1h2v-1zm2 1v3h1v-3zm0 3h-2v1h2zm-2 0v-3h-1v3z"/>
<path d="m49 5h1v1h1v1h1v-1h1v-1h1v5h-1v-3h-1v1h-1v-1h-1v3h-1z"/>
<path d="m70 5h3v1h-3v1h2v1h1v1h-1v1h-3v-1h3v-1h-2v-1h-1v-1h1"/>
<path d="m62 5h1v1h1v1h1v1h1v-3h1v5h-1v-1h-1v-1h-1v-1h-1v3h-1z"/>
</g>
<path d="m2 2v11h19.891a8.5136 8.5 0 0 0 2.1088-5.5898 8.5136 8.5 0 0 0-1.9563-5.4102h-13.119z" fill="#aaa"/>
<path d="m6.0195 2a8.5 8.5 0 0 0-2.0195 5.5 8.5 8.5 0 0 0 2.0312 5.5h12.949a8.5 8.5 0 0 0 2.0195-5.5 8.5 8.5 0 0 0-2.0312-5.5z" fill="#000"/>
<circle cx="12.5" cy="7.5" r="6.5" fill="#fff"/>
<path d="m9 5h2v1h1v1h-1v-1h-2v3h2v-1h1v1h-1v1h-2v-1h-1v-3h1z" fill="#000"/>
<path d="m14 5h2v1h1v1h-1v-1h-2v3h2v-1h1v1h-1v1h-2v-1h-1v-3h1z" fill="#000"/>
</symbol>
<symbol id="button-nojs" viewBox="0 0 80 15">
<rect width="80" height="15" fill="#666"/>
<rect x="1" y="1" width="78" height="13" fill="#fff"/>
<rect x="16" y="2" width="62" height="11" fill="#666"/>
<g fill="#fff">
<path d="m27 5v1h2v-1zm2 1v3h1v-3zm0 3h-2v1h2zm-2 0v-3h-1v3z"/>
<path d="m20 5h1v1h1v1h1v1h1v-3h1v5h-1v-1h-1v-1h-1v-1h-1v3h-1z"/>
<rect x="-1" y="259.03" width="78" height="13"/>
<path d="m70 5v5h3v-1h-2v-3h2v-1zm3 1v3h1v-3z"/>
<path d="m66 5h3v1h-2v1h2v1h-2v1h2v1h-3z"/>
<circle cx="7.5" cy="7.5" r="5.5" stroke="#666"/>
</g>
<g fill="#666">
<rect transform="rotate(-45)" x="-6" y="10" width="11" height="1"/>
<path d="m9 5h3v1h-3v1h2v1h1v1h-1v1h-3v-1h3v-1h-2v-1h-1v-1h1"/>
<path d="m6 5v4h1v-4zm0 4h-2v1h2zm-2 0v-1h-1v1z"/>
</g>
<g fill="#fff">
<path d="m40 5h3v1h-3v1h2v1h1v1h-1v1h-3v-1h3v-1h-2v-1h-1v-1h1"/>
<path d="m37 5v4h1v-4zm0 4h-2v1h2zm-2 0v-1h-1v1z"/>
<path d="m57 5h3v1h-2v1h2v1h-2v1h2v1h-3z"/>
<path d="m61 5v5h3v-1h-2v-3h2v-1zm3 1v3h1v-3z"/>
<path d="m53 5h3v1h-2v1h2v1h-2v1h2v1h-3z"/>
<path d="m47 5h1v1h1v1h1v1h1v-3h1v5h-1v-1h-1v-1h-1v-1h-1v3h-1z"/>
</g>
</symbol>
<symbol id="icon-www.flickr.com" viewBox="0 0 16 16">
<circle cx="3.75" cy="8.25" r="3.75" fill="#0063dc"/>
<circle cx="12.25" cy="8.25" r="3.75" fill="#ff0084"/>
@ -227,5 +85,10 @@
<path d="M16 15v-1h-1v-6h1v-1h-3v1h1v6h-3v-6h1v-1h-3v1h1v6h-3v-6h1v-1h-3v1h1v6h-3v-6h1v-1h-3v1h1v6h-1v1h-1v1h17v-1h-1z"></path>
<path d="M8 0h1l8 5v1h-17v-1l8-5z"></path>
</symbol>
<symbol id="icon-mastodon.social" viewBox="0 0 16 16">
<path d="M15.462 5.253c0-3.471-2.274-4.488-2.274-4.488-1.147-0.527-3.116-0.748-5.161-0.765h-0.051c-2.045 0.017-4.013 0.238-5.16 0.765 0 0-2.274 1.017-2.274 4.488 0 0.795-0.015 1.745 0.010 2.753 0.083 3.395 0.623 6.739 3.761 7.57 1.447 0.383 2.689 0.463 3.69 0.408 1.815-0.1 2.833-0.648 2.833-0.648l-0.060-1.317s-1.297 0.409-2.753 0.359c-1.443-0.049-2.966-0.155-3.199-1.927-0.020-0.145-0.032-0.313-0.032-0.483 0-0.005 0-0.009 0-0.014v0.001s1.417 0.347 3.211 0.429c1.097 0.050 2.127-0.065 3.172-0.189 2.005-0.239 3.75-1.475 3.969-2.603 0.345-1.777 0.317-4.338 0.317-4.338zM12.779 9.725h-1.665v-4.079c0-0.86-0.362-1.296-1.085-1.296-0.8 0-1.201 0.517-1.201 1.541v2.233h-1.655v-2.233c0-1.024-0.401-1.541-1.201-1.541-0.723 0-1.085 0.437-1.085 1.296v4.079h-1.665v-4.202c0-0.859 0.219-1.542 0.658-2.047 0.453-0.505 1.046-0.764 1.783-0.764 0.852 0 1.497 0.327 1.924 0.983l0.414 0.695 0.415-0.695c0.427-0.655 1.072-0.983 1.924-0.983 0.736 0 1.329 0.259 1.783 0.764 0.439 0.505 0.657 1.187 0.657 2.047v4.203z"></path>
</symbol>
<symbol id="icon-twitter" viewBox="0 0 16 16">
<path d="M15.969 3.046c-0.59 0.259-1.22 0.436-1.883 0.517 0.676-0.407 1.196-1.049 1.442-1.815-0.634 0.37-1.337 0.639-2.085 0.789-0.597-0.639-1.449-1.039-2.394-1.039-1.811 0-3.28 1.469-3.28 3.278 0 0.26 0.030 0.51 0.085 0.749-2.727-0.129-5.143-1.438-6.761-3.417-0.285 0.481-0.444 1.041-0.444 1.65 0 1.14 0.58 2.142 1.459 2.731-0.538-0.017-1.044-0.165-1.485-0.411v0.041c0 1.59 1.129 2.916 2.631 3.218-0.275 0.074-0.566 0.114-0.864 0.114-0.209 0-0.41-0.020-0.611-0.057 0.421 1.302 1.63 2.251 3.069 2.278-1.12 0.879-2.539 1.403-4.068 1.403-0.26 0-0.519-0.015-0.78-0.045 1.459 0.929 3.179 1.473 5.038 1.473 6.036 0 9.333-4.997 9.333-9.324 0-0.139 0-0.28-0.010-0.42 0.641-0.459 1.2-1.040 1.64-1.699l-0.031-0.013z"></path>
</symbol>
</svg>

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 15 KiB