- 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
@@ -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 @@ "version": "==3.0.4"
}, "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 @@ "version": "==2.8"
}, "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 @@ "version": "==1.0.7"
}, "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 @@ "version": "==0.4.5"
}, "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 @@ "version": "==1.1.1"
}, "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": {}
@@ -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>
@@ -252,6 +252,71 @@ self.renderfile, J2.get_template(self.template).render()
) +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 @@
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 @@ "exifData": [],
"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 @@ "geo": settings.nameddict(
{ "@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 @@ def to_syndicate(self):
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 @@ r.update({"@type": "WebPage"})
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 @@ r["potentialAction"].append(
{ "@context": "http://schema.org", "@type": "InteractAction", - "url": url, + "target": url, } )@@ -1299,14 +1372,6 @@ writepath(
self.txtfile, J2.get_template(self.txttemplate).render(g) ) 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):@@ -1578,6 +1643,89 @@ )
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 @@ fe = self.init_entry(post)
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 @@ "couldn't find post for incoming webmention: %s",
webmention.get("source"), ) - fdir = glob.glob( os.path.join(settings.paths.get("content"), "*", slug) )@@ -2371,8 +2519,7 @@
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 @@ search = Search()
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 @@ )
): 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 @@ queue.put(category.render())
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
@@ -72,6 +72,7 @@ "xmpp:mail@petermolnar.net",
"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 @@ "@context": "http://schema.org",
"@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 @@
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 @@ "txt": "index.txt",
"html": "index.html", "gopher": "gophermap", "sitemap": "sitemap.txt", + "worldmap": "map.html" } )
@@ -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();
@@ -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.Rights" content="{{ post.licence }}" />
<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 @@ {% if post.sameAs|length %}
<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 @@ {{ post.description|relurl(baseurl) }}
</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['@type'] == 'Photograph' %}
{% 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 %}
@@ -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 %}
@@ -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 %}
@@ -13,8 +13,9 @@ <link rel="icon" href="{{ site.image }}" />
{% 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>
<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 @@ <ul>
{% 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 @@ <svg width="16" height="16"><use xlink:href="#icon-github" /></svg>
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 @@ <ul>
{% 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>
@@ -40,8 +40,18 @@ </a>
</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 %}
@@ -366,6 +366,26 @@ display: block;
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 @@ min-width: 100%;
object-fit:contain; } */ -} +}
@@ -77,148 +77,6 @@ </symbol>
<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 @@ <symbol id="icon-web.archive.org" viewBox="0 0 17 16">
<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>