added a few articles, added media
9
_site/tmp/foo/20120331-01-cms-in-viersen.md
Normal file
@ -0,0 +1,9 @@
|
||||
CMS in Viersen
|
||||
============
|
||||
Published: 2012-03-31 10:18:29am
|
||||
|
||||
<a class="news-picture" href="/media/2012-03-31/2012-03-14 19.20.14.jpg"><img src="/media/2012-03-31/2012-03-14 19.20.14_small.jpg" /></a>
|
||||
|
||||
Am 14. März haben wir im Rahmen des Projekts ["Chaos macht Schule"](https://www.ccc.de/schule) einen Vortrag über Risiken und Möglichkeiten von sozialen Netzwerken in Viersen gehalten. Die Präsentation umfasste die Geschichte des CCC, die Vorurteile über Hacker und ihre Aktivitäten, sowie die [Hackerethik](https://www.ccc.de/hackerethics). Nach einer kurzen Vorstellung des Projekts "Chaos macht Schule" folgte eine ausführliche Betrachtung der größten sozialen Netzwerke. Im Anschluss an den Vortrag gab es eine Fragerunde, in der wir (hoffentlich) alle Unklarheiten beseitigen konnten.
|
||||
|
||||
Wir bedanken uns bei der Stadtschulpflegschaft Viersen für die Einladung und gute Zusammenarbeit!
|
35
_site/tmp/foo/20120422-01-easterhegg.md
Normal file
@ -0,0 +1,35 @@
|
||||
Easterhegg 12
|
||||
=============
|
||||
Published: 2012-04-22 12:02:55pm
|
||||
|
||||
|
||||
|
||||
|
||||
<a class="news-picture" href="/media/2012-04-22/flagge.jpg"><img src="/media/2012-04-22/flagge_small.jpg" /></a>
|
||||
|
||||
Vom 6. bis 9. April fand das diesjährige Easterhegg in Basel statt. Nach einer mehrstündigen Autofahrt war das Congresscenter in Basel schnell gefunden.
|
||||
Als Unterkunft diente uns der [Bunker](http://www.rettung.bs.ch/vermietungen-dienstleistungen/vermietungen-anlagenvermietung.htm) in der
|
||||
Riehenstrasse 59, welcher sich direkt unter dem Congress Center befindet und ausreichend Platz bietet. Um in die Zivilschutzanlage zu gelangen, mussten
|
||||
wir eine Treppe und zwei lange Rampen hinabwandern, ehe uns ein großes Schild in der Anlage Willkommen hieß.
|
||||
|
||||
<a class="news-picture" href="/media/2012-04-22/zivil-schutz-bunker.jpg"><img src="/media/2012-04-22/zivil-schutz-bunker_small.jpg" /></a>
|
||||
|
||||
|
||||
Nach einer weiteren Treppe konnten wir den eigentlichen Bunker durch eine Luftschleuse betreten und unser Gepäck in einen freien
|
||||
Spind packen. Internet(tm) sowie Live-Tracking der [r0kets](http://r0ket.badge.events.ccc.de/) funktionierte in Congresscentrum und im Bunker ausgezeichnet.
|
||||
|
||||
Zum Frühstuck waren Brötchen, Heißgetränke und Obst zu den üblichen Frühstückszeiten (9:00-16:00 Uhr) reichlich vorhanden.
|
||||
|
||||
Auf dem [CMS](http://ccc.de/schule)-Vortrag wurde der Stand des Projektes in diversen Städten, unter anderem in Essen, vorgestellt.
|
||||
|
||||
Im Kontext des [ChaosVPN](http://wiki.hamburg.ccc.de/ChaosVPN)-Vortrags kam auch ins Gespräch, dass die foobar im ChaosVPN erreichbar
|
||||
sein sollte. Daran wird nach der Heimkehr (aktiv und ernsthaft) gearbeitet.
|
||||
|
||||
Die [Schräge Runde](http://schraegerunde.blogspot.de/) hat einen [Circit Bending](https://de.wikipedia.org/wiki/Circuit_bending)-Vortrag
|
||||
und -Workshop (fast durchgehend) veranstaltet, wo Musikinstrumente durch das kreative Verändern diverser Elektronik gebaut wurden. Mit Hilfe
|
||||
derartiger Musikinstrumente und eines modifizierten Videosynthesizer fand am vorletzten Abend eine Liveperformance statt.
|
||||
|
||||
Außerdem konnten wir mit der Buttonpresse der Schrägen Runde (Danke!)
|
||||
reichlich Chaospott-Buttons pressen.
|
||||
|
||||
<a class="news-picture" href="/media/2012-04-22/buttons.jpg"><img src="/media/2012-04-22/buttons_small.jpg" /></a>
|
13
_site/tmp/foo/20120910-01-gpn12.md
Normal file
@ -0,0 +1,13 @@
|
||||
GPN 12
|
||||
==========
|
||||
Published: 2012-09-10 01:33:10pm
|
||||
|
||||
Am 7. Juni ist der Chaospott der Einladung des Entropia zur Gulachprogrammiernacht gefolgt und hat sich auf die Reise nach Karsruhe begeben. Die GPN fand auch dieses Jahr im Zentrum für Kunst und Medientechnologie, einem denkmalgeschützten Industriebau, einer ehemaligen Munitionsfabrik statt.
|
||||
|
||||
<a class="news-picture" href="/media/2012-09-10/GPN12-atarifrosch-20120609-038.jpg"><img src="/media/2012-09-10//GPN12-atarifrosch-20120609-038_small.jpg" /></a>
|
||||
|
||||
Über zwei Infobeamer wurden während der Veranstaltung aktuelle Informationen, sowie der Fahrplan und das aktuelle Wetter angezeigt und im weiteren Verlauf durch Bilder, genutzte Bandbreite und diverse Webcamaufnahmen erweitert. Das Programm bot interessante Vorträge sowie Workshops und wurde durch die Projekte der Teilnehmer ergänzt.
|
||||
|
||||
<a class="news-picture" href="/media/2012-09-10/800px-595262475.jpg"><img src="/media/2012-09-10//800px-595262475_small.jpg" /></a>
|
||||
|
||||
Zum Beispiel wurde während der Veranstaltung ein alter Nadeldrucker in Betrieb genommen oder ein Höhenmodell, mit Hilfe der Kinect auf einen Sandkasten projeziert. Insgesamt hatten wir viel Spaß und freuen uns schon auf die GPN13.
|
9
_site/tmp/foo/20120910-02-dev-tal.md
Normal file
@ -0,0 +1,9 @@
|
||||
/dev/tal
|
||||
==========
|
||||
Published: 2012-09-10 01:33:45pm
|
||||
|
||||
Innen bewohnt /dev/tal einen großen Raum mit Arbeitsplätzen, Matevorrat und einem, unter den Hackerspaces der weiteren Umgebung wahrscheinlich konkurrenzlosen Ausblick in das namengebende (Wupper-)Tal.
|
||||
|
||||
<a class="news-picture" href="/media/2012-09-10/617472545.jpg"><img src="/media/2012-09-10//617472545_small.jpg" /></a>
|
||||
|
||||
Trotz großem Besucherandrang am Jubiläumstag kam kein Sardinengefühl auf, da sich das "Überlaufventil" in den benachbarten Coworking-Space bereits geöffnet hat und der alte (bis zum Umzug eine Woche zuvor noch einzige) Raum von /dev/tal zum Vortragsraum umfunktioniert wurde. Hier lief von 13:37 bis ca. 21:00 Uhr das interessante Vortragsprogramm, das abwechselnd in die Breite und in die Tiefe ging; im Coworking-Space wurde zudem das Open-Data-Projekt von /dev/tal mit Daten des Wuppertaler Stadthaushalts demonstriert. Wir finden: ein lohnender Ausflug ins Bergische.
|
14
_site/tmp/foo/20121028-01-haxogreen.md
Normal file
@ -0,0 +1,14 @@
|
||||
haxogreen 2012
|
||||
==========
|
||||
Published: 2012-10-28 09:34:17am
|
||||
|
||||
Bei bestem Wetter brachen einige Chaospottler Richtung Luxemburg auf, um an der Haxogreen 2012 in Dudelange teilzunehmen. Die Haxogreen findet zweijährig auf einem kleinen Campingplatz statt, welcher auf einem Bergrücken liegt und genug Platz für die ca. 150 Teilnehmer bietet.
|
||||
|
||||
<a class="news-picture" href="/media/2012-10-28//haxogreen_00.jpg"><img src="/media/2012-10-28//haxogreen_00_small.jpg" /></a>
|
||||
|
||||
Wie gewohnt gab es ein großes Hackcenter, das ausreichend Platz bot um gemeinsam an Projekten zu arbeiten. So entstand nach kurzer Zeit aus einer Ampel und einem Raspberry PI das Netlight, welches den aktuellen Status der Internetverbindung zeigte. Für das leibliche Wohl wurde im Barzelt gesogt, wo es neben Getränken auch Grillgut für den Gemeinschafts-Grill gab. Zwischendurch gab es noch ein starkes Unwetter, das die Zelte auf ihre Standfestigkeit prüfte.
|
||||
|
||||
<a class="news-picture" href="/media/2012-10-28//haxogreen_01.jpg"><img src="/media/2012-10-28//haxogreen_01_small.jpg" /></a>
|
||||
|
||||
Dennoch sorgte das Programm, welches z.B. einen Vortrag zum [OpenPilot](http://www.openpilot.org/) beinhaltete, sowie kleinere Projekte, wie der Wegweiser und das LightPainting, für einen gelungenen Ausflug nach Luxemburg.
|
||||
|
9
_site/tmp/foo/20121030-01-ipv6.md
Normal file
@ -0,0 +1,9 @@
|
||||
IPv6
|
||||
==========
|
||||
Published: 2012-10-30 02:26:06pm
|
||||
|
||||
Die Homepage des Chaospotts ist per IPv6 erreichbar. Falls ihr kein natives
|
||||
IPv6 von eurem Provider bekommt, schaut mal bei [SixXS](https://www.sixxs.net/)
|
||||
oder anderen Tunnelanbietern vorbei.
|
||||
|
||||
Einen interessanten Podcast zu Thema bietet [CRE 197](http://cre.fm/cre197).
|
13
_site/tmp/foo/20121105-01-datenspuren2012.md
Normal file
@ -0,0 +1,13 @@
|
||||
Datenspuren 2012
|
||||
================
|
||||
Published: 2012-11-05 02:29:14pm
|
||||
|
||||
Auch dieses Jahr war der Chaospott auf den Datenspuren in Dresden vertreten und bereicherte abermals das Programm mit einem Workshop. Insgesamt gab es an den zwei Veranstaltungstagen fünf verschiedene Workshops, die Lebensformen jeglichen Alters Spaß an der Technik vermittelten.
|
||||
|
||||
<a class="news-picture" href="/media/2012-11-05/datenspuren2012_00.png"><img src="/media/2012-11-05/datenspuren2012_00_small.png" /></a>
|
||||
|
||||
An unserem Stand gab es eine lichtsensitive Alarmanlage, welche hervoragend als Schrankwächter einsetzbar ist und der Einfachheit halber in einer Lüsterklemme aufgebaut werden kann. Neben unserer Alarmanlage konnte an mehreren Lötstationen der Pentabug, ein Mikrocontroller gesteuerter Vibrationsroboter oder ein solarbetriebenes Zwitscherding zum Leben erweckt werden.
|
||||
|
||||
<a class="news-picture" href="/media/2012-11-05/datenspuren2012_01.png"><img src="/media/2012-11-05/datenspuren2012_01_small.png" /></a>
|
||||
|
||||
Neben den hardwarelastigen Projekten konnten verschiedene Tastaturlayouts unter anderem an Schreibmaschinen ausprobiert werden, welche einen Einblick in die Historie der Tastaturentwicklung bot. Auf einem dedizierten Testsystem konnten sich die Teilnehmer verschlüsselten bzw. unverschlüsselten Mailverkehr anschauen, welcher in den meisten Fällen für Staunen sorge. In den oberen Etagen der [Scheune](http://www.scheune.org/) liefen parallel zum Bastelprogramm zwei Vortragsprogramme, die mit interessanten Themen und der traditionellen "PentaNews GameShow" gespickt waren.
|
37
_site/tmp/foo/20130118-01-cms-viersen.md
Normal file
@ -0,0 +1,37 @@
|
||||
Chaos macht Schule - Anne-Frank-Gesamtschule Viersen
|
||||
========================================
|
||||
Published: 2013-01-18 05:51:17pm
|
||||
|
||||
Im Rahmen des [Chaos macht Schule](http://ccc.de/schule/) Projektes waren wir an der [Anne-Frank-Gesamtschule](http://www.afg-vie.de) in Viersen zu Gast.
|
||||
|
||||
<a class="news-picture" href="/media/2013-01-18/kickoff.jpg"><img src="/media/2013-01-18/kickoff_small.jpg" /></a>
|
||||
|
||||
Dort hielten wir vor den ca. 180 Schülern der 7. Jahrgangsstufe Vorträge zu den Themen "[Soziale Netzwerke - Spuren im Netz](http://www.afg-vie.de/index.php?option=com_content&view=article&id=285:chaos-macht-schule&catid=43:projekte&Itemid=72)" und "Wie funktioniert das Internet". Dabei erarbeiteten wir gemeinsam mit den Jugendlichen, wie die Geschäftsmodelle sozialer Netzwerke funktionieren und wie diese das Recht auf informationelle Selbstbestimmung gefährden können. Als Beispiel diente uns hier, wie so oft, der Platzhirsch der Branche - Facebook.
|
||||
|
||||
Besonders erfreulich war, dass die meisten Jugendlichen gar nicht so sorglos mit Facebook und anderen Portalen umgingen, wie wir im Vorfeld der Veranstaltung vermutet hatten. Viele waren sich letztendlich bewusst, dass die Nutzung sozialer Netzwerke sehr wohl etwas kostet, auch wenn deren Betreiber selten Geld dafür verlangen.
|
||||
|
||||
<a class="news-picture" href="/media/2013-01-18/schueler.jpg"><img src="/media/2013-01-18/schueler_small.jpg" /></a>
|
||||
|
||||
|
||||
Die Frage, wem Texte, Fotos und Videos gehören, nachdem sie veröffentlicht wurden, sorgte jedoch für Verunsicherung. Der Umstand, dass die Unternehmen im Hintergrund private Texte, Bilder und Videos der Nutzer ungefragt kommerziell weiterverwenden können, schien niemandem wirklich zu behagen.
|
||||
|
||||
Facebooks Like-Button stieß ebenfalls auf wenig Begeisterung, als unseren Zuhörern klar wurde, wie dieser letztendlich funktioniert, und dass seine primäre Funktion nicht wie suggeriert die Empfehlung von Inhalten an Freunde darstellt, sondern die personenbezogene Protokollierung des Surfverhaltens.
|
||||
|
||||
Auch wenn unsere Ausführungen viele negative Aspekte der sozialen Medien beleuchteten, baten wir die Schüler nicht zu vergessen, dass die Netzwerke den Wunsch der Menschen nach Austausch und Teilhabe unterstützen und so eine wichtige, gesellschaftliche Funktion erfüllen.
|
||||
Da aber die Betreiber der Plattformen Wirtschaftsunternemhen sind, die rein kommerzielle Interessen verfolgen, ist es wichtig eine gesunde Balance zwischen digitaler Teilhabe und der Wahrung informationeller Selbstbestimmung zu finden. - Die Dosis macht das Gift.
|
||||
|
||||
Gegen Ende des ersten Teils hatten wir noch Gelegenheit Fragen der Schüler zu beantworten und ihnen Verhaltensregeln aufzuzeigen, die sie beim Umgang mit sozialen Medien unterstützen können:
|
||||
|
||||
1. Erst ([selbst]kritisch) nachdenken, (tief) durchatmen, dann klicken. (Im Zweifel NICHT klicken!)
|
||||
2. Wenn Du nicht dafür zahlst, bist Du nicht der Kunde, sondern die Ware die verkauft wird. (Kostenlos heisst selten, dass es umsonst ist!)
|
||||
3. Frage Dich selbst: "Würde ich es morgen wirklich noch genau so schreiben?"
|
||||
4. Frage Dich selbst: "Muss das wirklich jeder wissen, oder sollte ich soetwas lieber persönlich klären?"
|
||||
|
||||
<a class="news-picture" href="/media/2013-01-18/meldung.jpg"><img src="/media/2013-01-18/meldung_small.jpg" /></a>
|
||||
|
||||
Im weiterführenden Vortrag, der sich mit dem Aufbau des weltweiten Datennetzes beschäftigte, zeigten wir den Jugendlichen, wie Client-Server-Kommunikation im Internet funktioniert, warum diese immer als potentiell unsicher zu betrachten ist und wie Verschlüsselung diesem Umstand entgegenwirken kann.
|
||||
Zudem erklärten wir, warum kostenfreie, öffentliche Nachrichtendienste wie der Facebook-Chat und WhatsApp bewusst auf Verschlüsselungsmechanismen verzichten und welche realen Risiken der Versand unverschlüsselter Nachrichten für die Nutzer birgt.
|
||||
Hierbei gaben wir Ratschläge, wie sich private Kommunikation ohne großen Aufwand ausreichend gegen unbefugtes Mitlesen Dritter schützen lässt und wie sich durch die Nutzung des "Inkognito Modus" moderner Browser der Erfolg der Sammelwut des Like-Buttons einschränken lässt.
|
||||
|
||||
Nach Abschluss der Veranstaltung hatten wir noch Gelegenheit uns mit einigen Lehrern der Jahrgangsstufe auszutauschen, dabei bekamen wir nicht nur Lob, sondern auch hilfreiches, kritisches Feedback, das wir zur Verbesserung unserer Arbeit nutzen können.
|
||||
Wir bedanken uns an dieser Stelle herzlich bei Petra Kreiten und Thomas Rütten für die gute Zusammenarbeit und freuen uns auf weitere Vorträge an der AFG Viersen.
|
97
_site/tmp/foo/20130210-01-aachen.md
Normal file
@ -0,0 +1,97 @@
|
||||
Essen auf Rädern - Besuch in Aachen
|
||||
====================================
|
||||
Published: 2013-02-10 05:34:01pm
|
||||
|
||||
|
||||
#### Das Versprechen
|
||||
Bereits beim Regiotreffen in Göttingen haben wir zwei Vertreter des
|
||||
[CCCAC](https://aachen.ccc.de) kennen gelernt und uns auf Anhieb gut
|
||||
verstanden. Wir wurden eingeladen, ihren Hackerspace zu besuchen. Auf
|
||||
den
|
||||
[MRMCD 2012](http://mrmcd.net/wiki/) trafen wir sie wieder, ebenso
|
||||
auf dem [29C3](http://events.ccc.de/congress/2012/wiki/Main_Page).
|
||||
Jedes mal beteuerten wir, dass wir sie besuchen werden und diesmal
|
||||
haben wir ernst gemacht. 10 unserer Leute haben sich auf 3 Autos
|
||||
verteilt gen Aachen gemacht.
|
||||
|
||||
Im Pott kennt man das, es nennt sich Wegbier, speziell in unserem Fall
|
||||
ist es ein Wegmier.
|
||||
|
||||
|
||||
<a class="news-picture" href="/media/2013-02-10/EaR_Aachen_01.JPG"><img src="/media/2013-02-10//EaR_Aachen_01.JPG_small.jpg" /></a>
|
||||
|
||||
Nach etwas über einer Stunde Autofahrt sind wir angekommen.
|
||||
|
||||
#### Natürliche Selektion durch Labyrinthbau
|
||||
|
||||
<a class="news-picture" href="/media/2013-02-10/EaR_Aachen_02.JPG"><img src="/media/2013-02-10//EaR_Aachen_02.JPG_small.jpg" /></a>
|
||||
|
||||
Tja, wie kommt man jetzt hinein?
|
||||
Nach einer kleinen Odyssee haben wir dann den richtigen Eingang im Hinterhof genommen und trafen dann auf die "Offizielle CCC Aachen Klingel <strike>alpha</strike> beta" ™.
|
||||
|
||||
<a class="news-picture" href="/media/2013-02-10/EaR_Aachen_03_.JPG"><img src="/media/2013-02-10//EaR_Aachen_03_.JPG_small.jpg" /></a>
|
||||
|
||||
Als wir dieses Schild gesehen haben, da wussten wir sofort "Hier sind wir richtig!".
|
||||
|
||||
<a class="news-picture" href="/media/2013-02-10/EaR_Aachen_04_.JPG"><img src="/media/2013-02-10//EaR_Aachen_04_.JPG_small.jpg" /></a>
|
||||
|
||||
|
||||
Das Sauerstoffdiffusionsdevice ist uns sofort ins Auge gesprungen.
|
||||
|
||||
<a class="news-picture" href="/media/2013-02-10/EaR_Aachen_04.JPG"><img src="/media/2013-02-10//EaR_Aachen_04.JPG_small.jpg" /></a>
|
||||
|
||||
Aber schauen wir uns mal ein bisschen um.
|
||||
|
||||
<a class="news-picture" href="/media/2013-02-10/EaR_Aachen_03.JPG"><img src="/media/2013-02-10//EaR_Aachen_03.JPG_small.jpg" /></a>
|
||||
|
||||
<a class="news-picture" href="/media/2013-02-10/EaR_Aachen_06.JPG"><img src="/media/2013-02-10//EaR_Aachen_06.JPG_small.jpg" /></a>
|
||||
|
||||
<a class="news-picture" href="/media/2013-02-10/EaR_Aachen_07.JPG"><img src="/media/2013-02-10//EaR_Aachen_07.JPG_small.jpg" /></a>
|
||||
|
||||
<a class="news-picture" href="/media/2013-02-10/EaR_Aachen_09.JPG"><img src="/media/2013-02-10//EaR_Aachen_09.JPG_small.jpg" /></a>
|
||||
|
||||
Das gegenüberliegende Gebäude erreicht man am besten durch eine Portal Gun.
|
||||
|
||||
<a class="news-picture" href="/media/2013-02-10/EaR_Aachen_10.JPG"><img src="/media/2013-02-10//EaR_Aachen_10.JPG_small.jpg" /></a>
|
||||
|
||||
<a class="news-picture" href="/media/2013-02-10/EaR_Aachen_11.JPG"><img src="/media/2013-02-10//EaR_Aachen_11.JPG_small.jpg" /></a>
|
||||
|
||||
|
||||
#### Hunger! Grillen.
|
||||
Grillanzünder sind ja bekanntlich für Anfänger. Erschwerend kam hinzu, dass wir kein Feuerzeug hatten. Wozu auch, wenn man einen Steinkohlegrill anbekommen möchte?
|
||||
Wir versuchten also in einem Reagenzglas etwas Papier mit dem Lötkolben zum Glühen/Brennen zu bringen. Das Resultat war die sofortige Inbetriebnahme des oben genannten Sauerstoffdiffusionsdevices.
|
||||
Plötzlich zückte jemand ein Streichholz, aber ohne Verpackung, an der man es hätte entzünden können. Wozu braucht man auch das passende Gegenstück, wenn man ein einzelnes Streichholz dabei haben kann?
|
||||
Sherlock hat dann noch mal kombiniert, Lötkolben + Streichholz = Flamme. Gesagt getan. Aber wie bekommt man ein Streichholz, welches eine Brennzeit von 10 Sekunden hat, durch den Fahrstuhl, 3 Etagen tiefer in den Hof, wo der Grill stand? Wozu sollte man das Streichholz auch direkt am Grill entzünden, wo der Weg kurz genug ist? Ok, wie bekommen wir jetzt die Flamme (Bedenkzeit = Brenndauer des Streichholzes) nach unten?
|
||||
Nehmen wir doch einfach unseren mobilen Bunsenbrenner...
|
||||
Gesagt, getan.
|
||||
|
||||
<a class="news-picture" href="/media/2013-02-10/EaR_Aachen_14.JPG"><img src="/media/2013-02-10//EaR_Aachen_14.JPG_small.jpg" /></a>
|
||||
|
||||
|
||||
Neben Kartoffelsalat, Baguettes, Brötchen und den abgebildeten Würstchen, gab's auch noch jede Menge Fleisch (*grunz*).
|
||||
|
||||
<a class="news-picture" href="/media/2013-02-10/EaR_Aachen_15.JPG"><img src="/media/2013-02-10//EaR_Aachen_15.JPG_small.jpg" /></a>
|
||||
|
||||
#### Schnacken
|
||||
|
||||
Nach einer ausgiebigen Stärkung haben wir uns einige Vorträge der Aachener angehört.
|
||||
|
||||
Der Papst hat zwar mittlerweile abgedankt, aber wir sind es dank Mitgliederkarte, weiterhin! Und deshalb durften wir auch der diskordianischen Bibelstunde beiwohnen.
|
||||
|
||||
<a class="news-picture" href="/media/2013-02-10/EaR_Aachen_17.JPG"><img src="/media/2013-02-10//EaR_Aachen_17.JPG_small.jpg" /></a>
|
||||
|
||||
|
||||
Darauf folgte eine Projektbesprechung. Die Aachener stellten uns zuerst ihre Projekte vor. Unter anderem die Stromverbrauchsüberwachung durch die Abtastung mit einem Mauslaser. Die Lasermaus wurde mit Tape vor den Stromzähler geklebt und registriert, sobald die rote Markierung vorbei kommt (=1 Umdrehung). So erlebt man am Jahresende keine Überraschung, wenn eine vierstellige Nachzahlungsaufforderung ins Haus flattert. An dieser Stelle, schönen Gruß nach Hamburg :o)
|
||||
Etwas umfangreicher war die Vorstellung des eigenen CTF, welches Aachen auf dem 29C3 ausgetragen hat. Uns wurden nicht nur die Aufgabestellungen gezeigt, sondern auch die dazu gehörigen, teils extrem kreativen "Lösungsvorschläge" (Anm. d. Red.: Holy shit, Batman)
|
||||
Beendet wurde die Aachener Projektrunde mit einem PowerPoint Karaoke.
|
||||
|
||||
<a class="news-picture" href="/media/2013-02-10/EaR_Aachen_18.JPG"><img src="/media/2013-02-10//EaR_Aachen_18.JPG_small.jpg" /></a>
|
||||
|
||||
Nachdem wir uns davon erholt hatten, präsentierten wir kurz unsere Chaos macht Schule Folien. Wir besprachen auch noch weitere Projekte wie beispielsweise unser [Hacksofa](https://wiki.die-foobar.de/wiki/Hackersofa), DocPatch (internes Geheimprojekt, Link folgt, sobald nicht mehr geheim) und natürlich unser [Essen auf Rädern](https://wiki.die-foobar.de/wiki/Essen_auf_R%C3%A4dern) Projekt, wovon sie gerade Teil sind.
|
||||
|
||||
|
||||
#### Fazit
|
||||
|
||||
Kurz gesagt, wir hatten jede Menge Spaß und unser "Duisburch" wird den Tag auch noch Monate später in Erinnerung behalten, er hat nämlich den einzigen Blitzer weit und breit auf der Rückfahrt mitgenommen.
|
||||
|
||||
Hausi
|
16
_site/tmp/foo/20130217-01-hackend.md
Normal file
@ -0,0 +1,16 @@
|
||||
Spontanes Hackend im Club
|
||||
=========================
|
||||
Published: 2013-02-17 10:24:39pm
|
||||
|
||||
Die Hardware des Schließsystems lässt sich mit einer angepassten OpenWRT-Firmware bespielen und die GPIO-Pins des [Carambola](http://www.8devices.com/product/3/carambola) über das Sysfs steuern.
|
||||
|
||||
<a class="news-picture" href="/media/2013-02-17/schliesssystem.jpg"><img src="/media/2013-02-17/schliesssystem_small.jpg" /></a>
|
||||
|
||||
Auf dem Infoscreen wurde eine simple Uhr mit OpenGL ES umgesetzt und erfolgreich die Portierung des [info-beamer](http://info-beamer.org/pi/) auf das RaspberryPi getestet. Ein Platz, an dem der Infoscreen befestigt wird, ist schon gefunden.
|
||||
|
||||
<a class="news-picture" href="/media/2013-02-17/uhr.jpg"><img src="/media/2013-02-17/uhr_small.jpg" /></a>
|
||||
|
||||
Außerdem wurde der DrachenSchlumpf, eine Kamerahalterung, die unter einem Drachen befestigt werden kann, in einer ersten Version fertiggestellt. Die Kamerahalterung ist aus einer Alu-Schiene gefertig, lässt sich um die x- und y-Achse bewegen und ist mit zwei
|
||||
Servomotoren ausgestattet. Ein Servo ist so angebracht, dass automatisiert Fotos gemacht werden können. Der andere dreht das Gestell um seine y-Achse, so dass ggf. Panoramabilder erstellt werden können. Die Motoren werden mit einem atmega8 angesteuert, dessen Software mit der Arduino-IDE entwickelt wurde.
|
||||
|
||||
<a class="news-picture" href="/media/2013-02-17/drachenschlumpf.jpg"><img src="/media/2013-02-17/drachenschlumpf_small.jpg" /></a>
|
11
_site/tmp/foo/20130323-01-cccmz-besuch.md
Normal file
@ -0,0 +1,11 @@
|
||||
Essen auf Rädern - Besuch beim CCCMZ
|
||||
=====================================
|
||||
Published: 2013-03-23 09:49:30pm
|
||||
|
||||
Vom 22.-24. März haben wir den [CCCMZ](http://www.cccmz.de) in Wiesbaden besucht und das [Wochenende](http://www.cccmz.de/weekend-of-work-beim-cccmz/) genutzt, um zu sehen, welche Projekte derzeit dort verfolgt werden. Darüber hinaus haben wir Hardware-Spenden erhalten, für die wir uns an dieser Stelle herzlich bedanken!
|
||||
|
||||
Hauptsächlich wurde an der Ansteuerung eines 3D-Druckers gearbeitet, welche auf der Arduino-Plattform aufsetzt. Das Gestell des Druckers besteht aus Holz und der Tisch wird auf Schubladenschienen gelagert, um die Kosten gering zu halten.
|
||||
|
||||
<a class="news-picture" href="/media/2013-03-23/IMG_0334.JPG"><img src="/media/2013-03-23//IMG_0334.JPG_small.jpg" /></a>
|
||||
|
||||
Neben dem Drucker wurde an einem Wasserdisplay gebastelt, das über 16 Ventile gesteuert werden soll, um verschiedene Bilder zu erzeugen. Neben den Projekten gab es genug Zeit um Mate zu trinken, sich über verschiedene Ideen auszutauschen oder in der Originalausgabe der [Hackerbibel](http://de.wikipedia.org/wiki/Hackerbibel) zu stöbern.
|
20
_site/tmp/foo/20130403-01-eh13.md
Normal file
@ -0,0 +1,20 @@
|
||||
Easterhegg 13
|
||||
==========
|
||||
Published: 2013-04-03 03:47:38pm
|
||||
|
||||
Der [C3PB](https://c3pb.de) hatte zum 29. März zur viertägigen Easterhegg nach Paderborn eingeladen, der ca. 250 Hacker gefolgt sind.
|
||||
|
||||
<a class="news-picture" href="/media/2013-04-03/bild01.JPG"><img src="/media/2013-04-03/bild01.JPG_small.jpg" /></a>
|
||||
|
||||
Die Kulturwerkstatt bot ausreichend Platz für ein großes Hackcenter, Vortrags -bzw. Workshopräume und einer Bar, welche einen großen Teil des Tages ein ausgiebiges Frühstück, abends Gegrilltes, sowie kühle Getränke bereit hielt.
|
||||
|
||||
<a class="news-picture" href="/media/2013-04-03/bild02.JPG"><img src="/media/2013-04-03/bild02.JPG_small.jpg" /></a>
|
||||
|
||||
|
||||
Ein Highlight war die Besichtigung des [Heinz Nixdorf-Museums](http://www.hnf.de/), auf der zum Beispiel eine funktionsfähige Vermittlungsstelle zu bestaunen war. An mehreren Fernsprechtischapparaten ([FeTAp](http://de.wikipedia.org/wiki/Fernsprechtischapparat)) konnte die obsolete Technik ausprobiert werden, da auf den FeTaps die jeweilige Nummer zu lesen war. Für die Gesprächsvermittlung sorgt sowohl ein Wählsystem S 50 (Heb-Dreh-Wähler), als auch ein S 55, welches auf Edelmetall-Motor-Drehwählern basiert. Außerdem wurden auf insgesamt zwei Etagen viele anderen Exponate ausgestellt, unter anderem eine [ENIAC](http://de.wikipedia.org/wiki/ENIAC), eine Enigma oder alte Datenträger.
|
||||
|
||||
<a class="news-picture" href="/media/2013-04-03/bild03.JPG"><img src="/media/2013-04-03/bild03.JPG_small.jpg" /></a>
|
||||
|
||||
Zum Vortragsprogamm trugen wir einen Workshop zur Lösung des Zauberwürfels bei, zu dem sich einige Teilnehmer sogar noch einen Würfel im Souvenirgeschäft des Heinz Nixdorf-Museums erworben haben. Vorgestellt wurde die LBL-Methode, nach der die Ebenen mit sieben Algorithmen nacheinander gelöst werden.
|
||||
|
||||
Wir bedanken uns beim C3Pb und allen Beteiligten für das tolle Easterhegg!
|
9
_site/tmp/foo/20130523-01-docpatch.md
Normal file
@ -0,0 +1,9 @@
|
||||
Docpatch - Entdecke das Grundgesetz!
|
||||
====================================
|
||||
Published: 2013-05-23 05:30:12pm
|
||||
|
||||
Wir haben in den letzten Wochen fleißig an einem unserer Open Data-Projekte gebastelt, dass wir heute mit einer [Pressemitteilung des CCC](http://ccc.de/de/updates/2013/docpatch) der Öffentlichkeit vorstellen wollen: [DocPatch](http://gg.docpatch.org).
|
||||
|
||||
Zum heutigen Geburtstag des Grundgesetzes (2<sup>6</sup> Jahre) haben wir das Grundgesetz digitaler gemacht. Dank freier Software und Werkzeugen wie Versionskontrolle, Markupsprachen, Exportern und jeder Menge Web-Magie stehe der Erkundung des wichtigsten Dokuments der Republik nichts mehr im Wege! Für mehr Infos schaut bitte in die [Pressemitteilung](http://ccc.de/de/updates/2013/docpatch) oder -- noch besser! -- die [DocPatch-Seite](http://gg.docpatch.org).
|
||||
|
||||
Viel Spaß dabei!
|
9
_site/tmp/foo/20130526-01-freifunk.md
Normal file
@ -0,0 +1,9 @@
|
||||
Freifunktag im Niemandsland
|
||||
===========================
|
||||
Published: 2013-05-26 08:38:52pm
|
||||
|
||||
Am Towelday des Jahres 2013 fand im [Niemandsland](http://www.niemandsland.org/blog/) ein größeres Freifunk-Treffen statt, das vom [Freifunk Rheinland e.V](https://www.freifunk-rheinland.net/) organisiert wurde. Da wir in der Umgebung unserer Clubräume ein Freifunknetz planen, bot das Treffen eine gute Möglichkeit um grundlegende Informationen über geeignete Hard- bzw. Software zu sammeln und rechtliche Fragen zu klären, die der Betrieb eines freien Netzes aufwirft.
|
||||
|
||||
<a class="news-picture" href="/media/2013-05-26/Freifunk-Router-fixt.jpg"><img src="/media/2013-05-26//Freifunk-Router-fixt_small.jpg" /></a>
|
||||
|
||||
Während des Treffens wurden die ersten Geräte, die wir uns im Vorfeld angeschafft hatten, mit der passenden Firmware ausgestattet, so dass der Ausflug nach Düsseldorf ein voller Erfolg war. Wir werden in den nächsten Tagen weiter experimentieren und halten Euch über die Entwicklungen auf dem Laufenden.
|
31
_site/tmp/foo/20130608-01-premiumcola.md
Normal file
@ -0,0 +1,31 @@
|
||||
Wirtschaft hacken - mit Premium Cola
|
||||
====================================
|
||||
Published: 2013-06-08 04:39:12pm
|
||||
|
||||
Wir landen Uwe Lübbermann, Gründer und Kopf hinter dem Premium Systems, zu einem Vortrag über "Wirtschaft hacken - mit Premium Cola" ein.
|
||||
|
||||
Premium ist eine kleine Getränkemarke ohne Büro, die seit über elf Jahren existiert und vieles bewusster regelt als die "normale" Wirtschaft. Das Projekt wird von einem Internet-Kollektiv nach dem Prinzip der Konsensdemokratie gesteuert und bis in Details wie z.B. "Anti-Mengenrabatte", "feste Umsatzanteile in die Alkoholismusvorsorge" oder "veganer Etikettenleim" optimiert - und bis zum freien Premium-Betriebssystem entwickelt.
|
||||
|
||||
Samstag, 22.06.2013, 18:00 Uhr
|
||||
|
||||
Speakers Corner
|
||||
|
||||
am Unperfekthaus
|
||||
|
||||
Friedrich-Ebert-Str. 18
|
||||
|
||||
45127 Essen
|
||||
|
||||
Die Veranstaltung ist kostenlos und findet im Freien statt.
|
||||
|
||||
<a if="chaospott-premiumcola-video" href="http://www.youtube.com/watch?v=8eF9V0-PORM"><img src="/media/2013-06-08/preview.jpg" /></a>
|
||||
|
||||
Quelle: http://premiumcola.de
|
||||
|
||||
Weiter Quellen:
|
||||
|
||||
* [Premium in 5 Minuten in diesem der Freitag-Artikel](http://www.freitag.de/alltag/1023-die-gute-cola) (2010).
|
||||
|
||||
* [Premium in 10 Minuten im Wall Street Journal](http://www.wallstreetjournal.de/article/SB10001424127887324662404578332230968156620.html) :-) (2013).
|
||||
|
||||
<a class="news-picture" href="/media/2013-06-08/Flyer.jpg"><img src="/media/2013-06-08//Flyer_small.jpg" /></a>
|
15
_site/tmp/foo/20130615-01-gpn13.md
Normal file
@ -0,0 +1,15 @@
|
||||
Gulaschprogrammiernacht 2013
|
||||
============================
|
||||
Published: 2013-06-15 01:36:45pm
|
||||
|
||||
Die [GPN](https://entropia.de/GPN13) fand vom 30. Mai bis zum 2. Juni statt und zog in seiner 12. Iteration noch mehr technikaffine Entitäten an als in den Vorjahren. So fanden sich ca. 450 Programmierer, Elektronikbastler, Lockpicker und andere Hacker in der [HFG Karlsruhe](http://www.hfg-karlsruhe.de) ein.
|
||||
|
||||
Da bei unserer frühen Ankunft das <a href="/media/2013-06-15/DSC_0020.jpg">Netz</a> szenetypisch noch im Aufbau befand, spielten wir zuerst einige (analoge) Autorenspiele, während 3D-Drucker, Retrospieleautomaten, Schneideplotter, LED-Beleuchtung, Eismaschinen, Beamer und vieles mehr den Weg in das Hackcenter fanden.
|
||||
|
||||
<a class="news-picture" href="/media/2013-06-15/DSC_0024.jpg"><img src="/media/2013-06-15//DSC_0024_small.jpg" /></a>
|
||||
|
||||
Es gab natürlich wie jedes Jahr tägliches Frühstück, leckeres Gulasch und ein interessantes technisches Vortragsprogramm. Passend zu dem Vortragsthemen [Raketentechnik](https://entropia.de/GPN13:60_Jahre_Raketentechnik_mit_R-7_und_Союз_(Soyuz) ) und ["Was man tut wenn man morgen mal wieder in einem Raumschiff aufwacht?"](https://entropia.de/GPN13:Wie_fliegt_man_eigentlich_Raumschiffe%3F), lief über mehrere Tage das Programmierspiel [Rocket Scissor Spacegoo](https://entropia.de/GPN13:Rocket_Scissor_Spacegoo), bei dem die Mitspieler KIs für Weltraumschlachten programmiert und gegeneinander antreten ließen. Der Vortrag "Die Technik der Videospiele" bot einen Überblick über die Technik von Spieleautomaten vergangener Tage und erklärte z.B. die Funktionsweise der Pongautomaten, welche ausschließlich mit Logikgattern arbeiten.
|
||||
|
||||
Neben dem Vortragprogramm zeigte auch der Infobeamer wieder nützliche Informationen wie Abfahrzeiten des ÖPNV oder das aktuelle Wetter an, welches dieses Jahr von besonderem Interesse war, um den Heimweg trocken zu beschreiten.
|
||||
|
||||
Wir bedanken uns an dieser Stelle beim Entropia und allen Helfern für die schöne Veranstaltung und freuen uns auf die nächste GPN!
|
19
_site/tmp/foo/20130904-01-ohm2013.md
Normal file
@ -0,0 +1,19 @@
|
||||
Observe, Hack, Make
|
||||
===================
|
||||
Published: 2013-09-04 06:50:50pm
|
||||
|
||||
Vom 29. Juli bis zum 4. August schlugen wir zusammen mit dem [CCCMz](https://www.cccmz.de/) und anderen Entitäten aus Stuttgart und Wuppertal unsere Zelte in Geestmerambacht auf, um gemeinsam mit 3000 Hackern aus der ganzen Welt ein fünftägiges Outdoorfest zu feiern, die OHM.
|
||||
|
||||
<a class="news-picture" href="/media/2013-09-04/BILD1.jpg"><img src="/media/2013-09-04//BILD1_small.jpg" /></a>
|
||||
|
||||
Das Vortragsprogramm bot mit fünf parallel laufenden Vorträgen viele interessante Themen, die sich mit Hard- und Software aber auch mit Themen aus dem Bereich "Staatlicher Überwachung" beschäftigten. Neben den Vorträgen wurden im Programm auch Workshops gelistet, in denen z.B. erste Schritte beim Schweißen, Löten oder der Programmierung von Mikrocontrollern gemacht werden konnten. Darüber hinaus waren in jedem "Village" interessante Menschen und Projekte anzutreffen, deren Basteleien teilweise schon von vergangenen Veranstaltungen bekannt waren, bei denen aber die vorgenommenen Weiterentwicklungen genug Gesprächsstoff lieferten. Nachts sorgten viele Lichtbasteleien für eine schöne Atmosphäre. LED-Fackeln säumten auf dem ganzen Gelände die Wegesränder und eine Laseranlage erzeugte über dem ganzen Camp coole Lichteffekte, von denen einige Hacker immer wieder automagisch angezogen wurden.
|
||||
|
||||
<a class="news-picture" href="/media/2013-09-04/BILD2.jpg"><img src="/media/2013-09-04//BILD2_small.jpg" /></a>
|
||||
|
||||
Nicht nur bei den Lichtinstallationen wurde viel Aufwand betrieben, sondern auch bei der Versorgung mit Elektrizität und einem ausreichend dimensionierten Zugang zum weltweiten Datennetz. Die 10 Gbit-Anbindung, welche mehrere Kilometer über Äcker und Wiesen verlegt wurde, bot genug Bandbreite und wurde über die Datenklos an die Teilnehmer weitergereicht. In den Datenklos befand sich außerdem die Hardware des Telefonnetzes, die es während der OHM ermöglichte, rund 980 angemeldete Teilnehmer zu erreichen. Verschollen geglaubte Hacker konnten somit einfach erreicht und überfüllte Mülltonnen schnell zur Leerung gemeldet werden. Der elektrische Strom kam selbstverständlich in rauen Mengen aus der Steckdose.
|
||||
|
||||
<a class="news-picture" href="/media/2013-09-04/BILD3.jpg"><img src="/media/2013-09-04//BILD3_small.jpg" /></a>
|
||||
|
||||
Unser Village bestand aus dem soliden Hackcenter, welches seine Einwohner vor Wind und Wetter schützte und den darum verteilten Privatgemächern selbiger. Während der sieben Tage, an denen es höchstens nur eine halbe Stunde regnete, waren wir also ausreichend vor Wind und Wasser geschützt. Für das leibliche Wohl sorgte ein kleiner Kühlschrank und ein Gaskocher, die uns mit Mate und sonstiger Nahrung versorgten, aber ggf. zum nächsten Camp mit "mehr Power" ausgestattet werden.
|
||||
|
||||
Es war eine tolle Zeit, für die wir uns bei den Organisatoren und anwesenden Hackern bedanken möchten und freuen uns schon auf die Fortsetzung unseres "Meta-Villages" auf dem nächsten Camp.
|
11
_site/tmp/foo/20130908-01-datenspuren-2013.md
Normal file
@ -0,0 +1,11 @@
|
||||
Datenspuren 2013
|
||||
================
|
||||
Published: 2013-09-08 01:52:41pm
|
||||
|
||||
Am 7. und 8. September fanden, organisiert durch den [C3D2](http://www.c3d2.de/), die Datenspuren in Dresden statt. Wie in den Jahren zuvor haben wir den Junghackertrack mit einer lichtsensitiven Alarmanlage bereichert. Auch der Pentabug war dieses Jahr wieder vertreten, allerdings in Version 2.0, die mit einer Infrarot-Schnittstelle ausgestattet ist und die Möglichkeit bietet, zwei Erweiterungsplatinen aufzustecken. Wer also mit dem Bug fertig war und noch mehr basteln wollte, konnte entweder das "pentatonic", eine pentatonische Tonleiter oder eine Led-Matrix zusammenlöten.
|
||||
|
||||
<a class="news-picture" href="/media/2013-09-08/IMG_1010.JPG"><img src="/media/2013-09-08//IMG_1010.JPG_small.jpg" /></a>
|
||||
|
||||
|
||||
Das Vortragsprogramm bot eine Mischung aus netzpolitischen und technischen Themen. Die "Cryptobar" bot Hilfe bei der Planung und Umsetzung von digitaler Privatsphäre und war über den ganzen Tag gut besucht.
|
||||
Die Rahmenbedingungen waren also gewohnt gut und wir freuen uns schon auf die nächste Iteration der Datenspuren.
|
9
_site/tmp/foo/20130929-01-sommerfest.md
Normal file
@ -0,0 +1,9 @@
|
||||
Sommerfest 2013
|
||||
===============
|
||||
Published: 2013-09-29 12:47:13pm
|
||||
|
||||
Pünktlich zum Ende des Sommers lud der Chaospott, die foobar und der CCC-Essen zu einer kleinen Sause in die heiligen Hallen der Sibyllastraße. Der Einladung sind nicht nur einige Nachbarn, die schon immer wissen wollten, was dieser Club eigentlich so macht, sondern auch Hacker aus Düsseldorf und Aachen gefolgt, so dass die freie Fläche in unseren Räumlichkeiten gegen Null konvergiert wäre. Da sich der 21. September als niederschlagsfreier Tag präsentierte, sorgten die im Hinterhof aufgebauten Sitzgelegenheiten für ausreichend Platz und der Grillgutbeauftragte war somit auch in guter Gesellschaft.
|
||||
|
||||
<a class="news-picture" href="/media/2013-09-29/IMG_0883.JPG"><img src="/media/2013-09-29//IMG_0883.JPG_small.jpg" /></a>
|
||||
|
||||
Neben Fnords und Anekdoten vergangener Veranstaltungen wurden aktuelle Themen diskutiert, die wieder zu den Fnords führten. Neugierige Nachbarn konnten sich davon überzeugen, dass der Club kein klandestiner Verein ist, und sich die Hard- und Softwareprojekte anschauen. Erwartet wurden unzählige Computer, Monitore und in Bezug auf unseren Namen, eine Bar. Für Berge von Computern hätten wir sicherlich sorgen können, dann hätte aber die Beschallungshardware keinen Platz mehr gefunden, die vor allem bei unserem Nachwuchs für Spaß an den Reglern sorgte. Insgesamt war es eine gelungene Feier, welche wiederholungswürdig ist und zu der in ihrer nächsten Iteration gerne noch mehr benachbarte Menschen vorbei kommen dürfen. Ein herzliches Dankeschön geht an alle helfenden Hände und an unsere Gäste.
|
11
_site/tmp/foo/20131014-01-cms-bundestreffen.md
Normal file
@ -0,0 +1,11 @@
|
||||
CmS-Bundestreffen
|
||||
=================
|
||||
Published: 2013-10-14 12:57:22pm
|
||||
|
||||
Bei bestem Wetter richtete der Chaospott vom 27.-29. September das "Chaos macht Schule"-Bundestreffen aus, zu dem 15 Interessierte den Weg ins [Linuxhotel](http://www.linuxhotel.de/) fanden.
|
||||
|
||||
<a class="news-picture" href="/media/2013-10-14/linuxhotel_landschaft.jpg"><img src="/media/2013-10-14//linuxhotel_landschaft_small.jpg" /></a>
|
||||
|
||||
Im Vordergrund der Agenda stand die Vorstellung des Projekts sowie die Planung des Junghackertages auf dem 30. Chaos Communication Congress in Hamburg. Zum Einstieg wurde den Gruppen, die im Rahmen von "Chaos macht Schule" noch nicht tätig waren, ein Überblick über das Projekt geboten. Dazu stellten die Beteiligten aus Menschheim und Essen ausführlich ihre Vorträge und Hardwarebasteleien vor.
|
||||
Der zweite große Block beschäftigte sich mit der Planung des Junghackertages auf dem 30C3, da dieser im letzen Jahr bei den jüngeren Besuchern für viel Spaß und Freude sorgte. Es werden mindestens zwei Stationen angeboten, wo es nach erfolgreicher Teilnahme einen Stempel für den Junghackerpass gibt.
|
||||
An dieser Stelle möchten wir uns bei der [WHS](http://www.wauland.de/) für die Unterstützung und bei den Teilnehmern für das erfolgreiche Wochenende bedanken.
|
13
_site/tmp/foo/20131104-01-hackover.md
Normal file
@ -0,0 +1,13 @@
|
||||
Hackover 2013
|
||||
=============
|
||||
Published: 2013-11-04 05:45:01pm
|
||||
|
||||
Dieses Jahr konnten wir zum ersten Mal das [Hackover](http://www.hackover.de/) besuchen, welches vom 1.-3. November in Hannover stattfand. Die [Leitstelle 511](https://hannover.ccc.de/), ihres Zeichens der örtliche Erfa-Kreis des CCC, lud zum zweiten Mal in die Nordstädter Bürgerschule ein, um drei Tage an Hard- und Software zu basteln. Das pittoreske Backsteingebäude bot neben den Workshop- bzw. Vortragsräumen genug Platz, um sich im Hackcenter oder an der Bar über aktuelle Geschehnisse im weltweiten Datentransfernetz auszutauschen, ohne dass die Veranstaltung weitläufig wirkte.
|
||||
|
||||
<a class="news-picture" href="/media/2013-11-04/Bild1.jpg"><img src="/media/2013-11-04//Bild1_small.jpg" /></a>
|
||||
|
||||
Die elektronische Eintrittskarte sorgte mit dem Plattformspiel "Hackio" für einige rote Daumen und bot mit dem 2.4GHz-Funkmodul genug Möglichkeiten für eigene Softwareideen. Neben der Eintrittskarte gab es für jede teilnehmende Person eine Tasse und ein ganztätiges Frühstück, was den Langschläfern wohl sehr entgegen kam.
|
||||
|
||||
<a class="news-picture" href="/media/2013-11-04/Bild2.jpg"><img src="/media/2013-11-04//Bild2_small.jpg" /></a>
|
||||
|
||||
Leider verging die Zeit auf dem Hackover so schnell wie die verfügbare Bandbreite, so dass nach drei Tagen Hacken, Basteln und wenig Schlaf die Heimreise auf dem Programm stand. Unser Dank gilt den Organisatoren und Engeln und wir freuen uns auf die nächste Iteration im Jahre 2014.
|
13
_site/tmp/foo/20131111-01-openrheinruhr.md
Normal file
@ -0,0 +1,13 @@
|
||||
OpenRheinRuhr 2013
|
||||
==================
|
||||
Published: 2013-11-11 05:25:58pm
|
||||
|
||||
Auf der diesjährigen [OpenRheinRuhr](http://www.openrheinruhr.de) hat der Chaospott zusammen mit dem [Chaosdorf](http://chaosdorf.de) und dem [fNordeingang](https://fnordeingang.de) für den passenden Hackercharme gesorgt. In der Hackecke konnten technikinteressierte Menschen zusammen an Projekten arbeiten oder die Projekte anderer bestaunen. Zu sehen gab es einen Infoscreen, auf dem Zugabfahrtszeiten, Chatlogs, das Wetter und Windgeschwindigkeiten abgelesen werden konnten. Für analog Orientierte gab es den Service, den Chatlog ausgedruckt mit nach Hause zu nehmen.
|
||||
|
||||
<a class="news-picture" href="/media/2013-11-11/freifunk.jpeg"><img src="/media/2013-11-11//freifunk.jpeg_small.jpg" /></a>
|
||||
|
||||
Neben der OpenRheinRuhr Twitterwall entstand in dieser Kreativecke auch das messeweite Freifunknetz, an dem Hacker aus verschiedenen Hackspaces erfolgreich zusammenarbeiteten. Interessierte konnten mit Bausätzen und der aufgebauten Lötstation Erfahrung sammeln.
|
||||
|
||||
<a class="news-picture" href="/media/2013-11-11/loeten.jpeg"><img src="/media/2013-11-11//loeten.jpeg_small.jpg" /></a>
|
||||
|
||||
Die Kaffee-Flatrate und Premium Cola Versorgung half dabei, Koffein in SourceCode zu verwandeln. Für den intellektuellen Anspruch gab es Vorträge zu verschiedenen Themen. Das Lob gebührt den vielen freiwilligen Helfern, die diese OpenDinge{TM} Veranstaltung möglich gemacht haben.
|
25
_site/tmp/foo/20140321-01-cms-bonn.md
Normal file
@ -0,0 +1,25 @@
|
||||
Chaos macht Schule macht halt in Bonn
|
||||
=====================================
|
||||
Published: 2014-03-21 12:45:14pm
|
||||
|
||||
<img width="550px" src="/media/2014-03-21/Bild_BF_Uebergangsversion.jpg" />
|
||||
|
||||
Am 25.02.2014 waren die CMS Teams des Chaospott aus Essen und des [CCCAC](http://aachen.ccc.de/) gemeinsam an BONNS FÜNFTER zu Gast.
|
||||
|
||||
Dort sprachen wir, erstmals nach der Übernahme von WhatsApp durch Facebook, mit 40 Schülerinnen und Schülern der Jahrgangsstufe 7 über die Gefahren der Nutzung von sozialen Netzwerken anhand des Beispiels Facebook.
|
||||
|
||||
Der Fokus des Vortrags, lag auf der Sensibilisierung und Prävention bei der Nutzung und Preisgabe von privaten und intimen Informationen in sozialen Netzwerken.
|
||||
|
||||
Es kristallisierte sich heraus, dass durchaus ein Bewusstsein für informationelle Selbstbestimmung vorhanden ist. Allerdings waren sich die Schülerinnen und Schülern nicht bewusst, dass sie für ihr Recht eigenverantwortlich einzustehen haben.
|
||||
|
||||
Alles in allem gibt es eine gewisse Grundvorstellung, welchen Schutz ihrer Informationen sie sich wünschen und das sie nicht alle Informationen preisgeben möchten. Sich allerdings dem sozialen Druck ihrer Mitschülerinnen und ihrer Mitschüler unterwerfen und auch Ihre Wertevorstellung für ein paar Likes Klicks vergessen.
|
||||
|
||||
Einem großen Teil der Schüler war nicht bewusst, dass jegliche Informationen, die sie im Netz veröffentlichen, von den Plattformbetreibern auch nach der Löschung durch den Nutzer weiter vorgehalten werden und diese Daten weiterhin für interne Verwertungszwecke genutzt werden.
|
||||
|
||||
Auch, dass ihre Daten an Dritte wie Spieleplattformen oder andere im Internet verfügbare Dienste, welche auf den Facebook Log-in setzten, weitergeben werden, war den Schülerinnen und Schülern nicht bewusst.
|
||||
|
||||
Viele der Schülerinnen und Schüler zeigen sich angesichts dieser für sie neuen Informationen peinlich berührt und durch aus besorgt. Es kann die Frage, auf welche Möglichkeiten es gibt, um die Daten bei Facebook vollständig löschen lassen zu können oder aber wir uns, als CCC in die Systeme von Facebook hacken könnten, um ihre Daten löschen zu können.
|
||||
|
||||
So wohl für die Lehrerinnen und Lehrer, als auch die Schülerinnen und Schüler der Jahrgangsstufen, gab es eine Vielzahl an wichtigen und neuen Informationen, die es bei der Nutzung von Sozialen Netzwerken zukünftig zu beherzigen gilt.
|
||||
|
||||
Der Vortrag war eine gelungene Sache, alle beteiligten waren mit großer Aufmerksamkeit bei der Sache. Zum Teil konnten die Schülerrinnen und Schüler von ihren Einbringungen sogar profitieren.
|
46
_site/tmp/foo/20140401-01-30c3.md
Normal file
@ -0,0 +1,46 @@
|
||||
30. Chaos Communication Congress
|
||||
================================
|
||||
Published: 2014-04-01 06:46:35pm
|
||||
|
||||
Vom 27. bis zum 30. Dezember fand in Hamburg zum 30. Mal der Chaos Communication Congress des CCC statt. Nachdem sich das CCH letztes Jahr als Veranstaltungort bewährt hatte, wurden dieses Jahr ca. 80% des Gebäudes belegt, um den 9000 Entitäten ausreichend Platz zu bieten, damit die Projekte aus dem heimischen Hackerspace aufgebaut werden konnten. Mehrere Lastkraftwagen's und kleine Transporter fanden in den Tagen vor dem Congress den Weg in Halle H, um $Krempel für ihr Assemblies auszuladen. Ein Großteil der unteren Etage wurde durch die Assemblies in einen riesigen Hackerspace verwandelt, in dem man praktisch alles finden konnte, um zum Beispiel einen Rechner zu reparieren oder eines der zahlreichen Blinkenlightsprojekte zusammen zu löten. Für die gewohnte Beleuchtung sowohl innerhalb als auch außerhalb des Gebäudes wurde auch dieses Jahr gesorgt. Die Fassade des CCH und der Landeplatz für Raumschiffe, welchen die "Fairy Dust" in Anspruch nahm, wurden bunt beleuchtet.
|
||||
|
||||
<a class="news-picture" href="/media/2014-04-01/photo3.jpg"><img src="/media/2014-04-01//photo3_small.jpg" /></a>
|
||||
|
||||
Innerhalb des Gebäudes sorgten die abgeklebte Deckenbeleuchtung und die großen Lichtinstallationen aus München und Menschheim für eine hackerkompatible Beleuchtung, die zusammen mit den vielen Sitzgelegenheiten, dem Fahrstuhl in Steampunkoptik und größeren Displays aus LEDs oder Flipdots eine schon fast zauberhafte Atmosphäre schaffte. Viele Projekte wurden während der vier Tage modifiziert und beispielsweise mit anderen Projekten bzw. Diensten verbunden. Auf der LED-Anzeige im Eingangsbereich konnten die Vorträge in Pixeloptik geschaut werden und der Flipdot-Anzeige wurde eine Kinect spendiert, mit der es möglich wurde, die Umrisse der davor stehenden Personen anzuzeigen. Die Möglichkeit, Daten per IPv6 zu senden wurde zum Beispiel genutzt, um das "Game of Life" oder weißes Rauschen zu implementieren.
|
||||
Die Lichtwand "All colours are beautiful" bot auch eine Schnittstelle für eigene Animationen, die aber ungenutzt blieb, so dass die Engel am Infotresen ohne wildes Geflacker die Fragen der Besucher beantworten konnten.
|
||||
|
||||
|
||||
|
||||
Infrastuktur
|
||||
------------
|
||||
Über 1000 Engel meldeten sich während des 30C3 im Himmel und leisteten mehr als 8000 Stunden ehrenamtliche Arbeit, um die Infrastruktur der Veranstaltung aufzubauen, zu betreiben und nach vier Tagen wieder abzubauen. Das Network Operation Center (NOC) organisierte dieses Jahr eine Anbindung an das weltweite Datentransfernetz mit einer Bandbreite von 100 Gigabit. Im Vortrag "Infrastructure" merkte das NOC scherzhaft an, dass es mit einer maximalen Auslastung von 40% nicht zufrieden sei und sich für den nächsten Congress mehr Bits und Bytes auf der Leitung wünsche. Nach vier Tagen wurden insgesamt 267682 Gigabyte an externe Teilnehmer des Datentransfernetzes übermittelt. Der Betrag der Datenrückübermittlung fiel mit 77481 GB etwas geringer aus.
|
||||
Das Phone Operations Center (POC) registrierte 2821 Nutzer, die sich überwiegend auf das DECT- bzw. GSM-Netz aufteilten. Man munkelt, dass die 30C3-Optik der Simkarten dafür sorgte, dass am zweiten Tag das Kontingent von 1000 Karten vergriffen war. Sieben Basisstationen wurden aufgebaut, damit die große Fläche des CCH abgedeckt und genug Kanäle zum Einbuchen zur Verfügung stehen konnten. Die Basisstationen eines öffentlichen Telekommunikationsanbieters versuchten den Telefonen, die ihnen zu nahe gekommen waren, zu beweisen, dass es sie nicht gibt. Die Argumentation verläuft ungefair so. "Hier bin ich, buch mich ein", sagte das Telefon. "Aber", sagte die Basisstation, "dass du dich meldest beweist, dass es dich gibt. Da du aber nicht in meiner Datenbank stehst, gibt es dich nicht! q.e.d." "Oh mein liebes Telefon" sagte das Telefon, "daran hab ich ja gar nicht gedacht" und schaltete sich unter einem Logikwölkchen ab. "Na, das war ja einfach", sagte die Basisstation. Nachdem das Problem gelöst war, konnten betroffene Leute wieder telefonieren, Kurznachrichten verschicken oder in den großen Sälen den Vortragenden lauschen.
|
||||
Das Programm teilte sich in fünf Kategorien auf und bot insgesamt 64 Vorträge, die zum Teil in fünf Sälen parallel liefen. Die Aufzeichnungen stehen auf media.ccc.de in verschiedenen Formaten zur Verfügung und bieten eine deutsche sowie englische Tonspur. Die Streams verfolgten im Schnitt 5k und zur besten Sendezeit über 10k Menschen. Für den Betrieb der Infrastruktur und sonstigen Dingen waren 111564kWh nötig. Der genaue Mateverbrauch war nicht zu ermitteln, aber nach zwei Tagen waren die riesigen Club-Mate-Vorräte aufgebraucht. Hier und da apparierten in den folgenden Tagen die letzten taktischen Reserven, die aus dem Umland zum Congress geschafft wurden.
|
||||
|
||||
<a class="news-picture" href="/media/2014-04-01/photo1.JPG"><img src="/media/2014-04-01//photo1.JPG_small.jpg" /></a>
|
||||
|
||||
Trotz der kleinen Matecalypse gab es an den Bars und der Lounge genügend Möglichkeiten für den Erwerb von Kaltgetränken. Die Lounge wurde in einem abgetrennten Bereich der Lagerhalle aufgebaut und verdiente wegen ihrer Ausmaße und Coolness eher das Prädikat "Der coolste Club Hamburgs". Dem Drang nach Bewegung konnte in einer postrevolutionären Atmosphäre unter anderem auf einem Wasserwerfer gefrönt werden. Selbstverständlich war auch die Lounge an die Seidenstraße angebunden, über die den ganzen Tag die Rohrpost flupperte. Kapseln verschiedenster Bauart wurden blinkend durch das Gebäude verschickt. An der platzgenauen Matelieferung muss allerdings noch geforscht werden, da erste Lieferungen die Teststrecke unter Freisetzung ihrer potentiellen Lageenergie verließen. Verletzt wurde niemand, aber für einen solchen Fall wäre das Chaos-Emergency-Response-Team zur Stelle gewesen.
|
||||
|
||||
|
||||
Chaos macht Schule
|
||||
-----------------
|
||||
An Tag 3 fand zum wiederholten Mal der Junghackertag statt, der bei jungen Menschen Spaß an Technologie wecken soll und von den CmS-Gruppen verschiedener Erfas organisiert wird. Angeboten wurden zwei Bastelstationen, an denen entweder eine lichtsensitive Alarmanlage oder ein Virationsroboter namens Pentabug gebaut werden konnte und ein Vortragsprogramm. Die Vorträge informierten über das Projekt "Chaos macht Schule" als solches und Themen, die vorwiegend durch soziale Netzwerke geprägt sind.
|
||||
|
||||
|
||||
|
||||
Vorträge
|
||||
--------
|
||||
Der 30. Chaos Communication Congress wurde mit einem grandiosen [Kurzfilm](http://vimeo.com/82903406) eröffnet, welcher auf 30 Jahre Congress zurückblickte und mit einem imposanten Finale die Freude auf den 30C3 schürte. An die Eröffungsrede schlossen sich 64 Vorträge zu den Themen "Art & Beauty", "Security & Safety", "Ethics, Society & Politics", "Science & Engineering", "Hardware & Making" an. Die nachfolgende Liste beinhaltet Vorträge, von denen die Redaktion besonderes unterhalten wurde, gleichwohl die Qualität des Programms sehr hoch ist, so dass eigentlich alle Vorträge empfehlenswert sind.
|
||||
|
||||
|
||||
|
||||
|
||||
* [Überwachen und Sprache](http://media.ccc.de/browse/congress/2013/30C3_-_5377_-_de_-_saal_6_-_201312271245_-_uberwachen_und_sprache_-_josch.html) (Joachim Scharloth)
|
||||
|
||||
* [Keine Anhaltspunkte für flächendeckende Überwachung](http://media.ccc.de/browse/congress/2013/30C3_-_5281_-_de_-_saal_1_-_201312271400_-_keine_anhaltspunkte_fur_flachendeckende_uberwachung_-_martin_haase_maha_-_khamacher.html) (Martin Haase und khamacher)
|
||||
|
||||
* [Bullshit made in Germany](http://media.ccc.de/browse/congress/2013/30C3_-_5210_-_de_-_saal_g_-_201312282030_-_bullshit_made_in_germany_-_linus_neumann.html) (Linus Neumann)
|
||||
|
||||
* [Fnord News Show](http://media.ccc.de/browse/congress/2013/30C3_-_5490_-_de_-_saal_1_-_201312300000_-_fnord_news_show_-_frank_-_fefe.html) (Frank und Fefe)
|
||||
|
||||
* [Security Nightmares](http://media.ccc.de/browse/congress/2013/30C3_-_5413_-_de_-_saal_1_-_201312301715_-_security_nightmares_-_frank_-_ron.html) (Frank Rieger und Ron)
|
7
_site/tmp/foo/20140402-01-cms-essen-rwb.md
Normal file
@ -0,0 +1,7 @@
|
||||
Chaos macht Schule an dem RWB-Essen
|
||||
===================================
|
||||
Published: 2014-04-02 05:13:30pm
|
||||
|
||||
<a class="news-picture" href="/media/2014-04-02/Chaos-macht-Schule-Logo.png"><img src="/media/2014-04-02//Chaos-macht-Schule-Logo.png_small.jpg" /></a>
|
||||
|
||||
Am 24.03.14 fand im RWB-Essen eine Lehrerfortbildung statt, an der das Projekt Chaos macht Schule mit einem Vortag zum Thema "Soziale Netzwerke" teilnahm. Neben einer kurzen Einführung über die Aktivitäten des Chaos Computer Clubs und den Hacker als solchen, wendete sich der Vortrag dem eigentlichen Thema zu. Behandelt wurde das Thema am Beispiel des bekanntesten Vertreter sozialer Netzwerke, Facebook. Der erste Block lässt sich mit den Fragen "Wie sieht ein soziales Netzwerk aus?" und "Was ist für den Betrieb nötig?" zusammenfassen, die verdeutlichen, welcher Aufwand für den Betrieb nötig ist und welche Kosten dabei entstehen. Der zweite Block beschäftigte sich vorwiegend mit den Fragen "Welche Daten werden gesammelt?" und "Wie werden diese Daten erhoben?", da zum Beispiel bei der automatisierten Eingabe von Daten Aufklärungsbedarf besteht. Der dritte und letzte Block bot Zeit um Fragen zu klären, die in dem anderthalb stündigen Vortrag offen geblieben waren. Interessiertes Publikum, gerne wieder.
|
16
_site/tmp/var/www/chaospott.de/$basepath/www/.htaccess
Normal file
@ -0,0 +1,16 @@
|
||||
Options -MultiViews
|
||||
|
||||
AddOutputFilterByType DEFLATE text/html text/plain text/css text/javascript text/xml text/rss text/rss+xml
|
||||
|
||||
RewriteEngine On
|
||||
|
||||
RewriteRule ^([^\.]*)$ - [T=text/html]
|
||||
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteCond %{REQUEST_FILENAME}.html -f
|
||||
RewriteRule ^(.*)$ $1.html [QSA,L]
|
||||
|
||||
ErrorDocument 404 /404.html
|
||||
ErrorDocument 500 /500.html
|
||||
|
26
_site/tmp/var/www/chaospott.de/LICENSE
Normal file
@ -0,0 +1,26 @@
|
||||
Copyright 2012 Marco Arment. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are
|
||||
permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
of conditions and the following disclaimer in the documentation and/or other materials
|
||||
provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY MARCO ARMENT ''AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> OR
|
||||
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
The views and conclusions contained in the software and documentation are those of the
|
||||
authors and should not be interpreted as representing official policies, either expressed
|
||||
or implied, of Marco Arment.
|
||||
|
231
_site/tmp/var/www/chaospott.de/README.markdown
Normal file
@ -0,0 +1,231 @@
|
||||
Second Crack is a basic static-file blog engine using Markdown-formatted text files as input.
|
||||
|
||||
# Warning
|
||||
|
||||
Second Crack should be considered an early alpha. Even though I've run Marco.org with it for a long time, **it's still rough and unfriendly.**
|
||||
|
||||
There are also still **significant known bugs** that I haven't had time to fix yet. (Feel free!) Among them:
|
||||
|
||||
* The month archives sometimes only contain a few posts.
|
||||
* If you have a day with multiple posts, and you delete one of them in the middle, sometimes the others don't reorder themselves properly.
|
||||
* Editing slugs after publishing is unreliable.
|
||||
|
||||
These can usually be fixed temporarily by clearing the `cache/` directory's contents and running the updater for a full rebuild.
|
||||
|
||||
Other minor bugs:
|
||||
|
||||
* If you have multiple posts on the frontpage or an archive/tag page that use footnotes, the anchors will be repeated between posts and they won't work properly. Footnote anchors need some kind of unique per-post prefix.
|
||||
* If a template is edited, the changes won't propagate to the entire site automatically -- only new posts and updated pages will show the changes. To propagate a template's changes across the whole site, do a full rebuild by clearing the `cache/` directory and running the updater.
|
||||
|
||||
# Installation
|
||||
|
||||
**Second Crack requires PHP 5.3+ and has only been tested so far on Mac OS X and CentOS 5.5, with Apache httpd.** It's already fast with Apache, but it can be even faster and capable of even more burst traffic if you put a caching proxy such as Varnish in front of it.
|
||||
|
||||
Check this directory out on the server (or locally on Mac or Linux for testing, if you'd like).
|
||||
|
||||
Add a line like this to your `crontab`, replacing `{SOURCE_PATH}` (your blog source directory, see **Basics** below) and `{SECONDCRACK_PATH}` (this checkout) accordingly:
|
||||
|
||||
* * * * * /home/marco/secondcrack/engine/update.sh {SOURCE_PATH} {SECONDCRACK_PATH}
|
||||
|
||||
The updater script will append its output to `/tmp/secondcrack-update.log`.
|
||||
|
||||
I strongly suggest installing <a href="https://github.com/rvoicilas/inotify-tools/wiki">inotify-tools</a> to get `inotifywait`. With it, the Second Crack updater will respond to file changes immediately as they happen. Without it, it only updates as often as the `cron` task runs. The round-trip Dropbox updating is a **lot** faster and more awesome with `inotifywait`.
|
||||
|
||||
Your webserver should use `{SECONDCRACK_PATH}/www` as its document root. It must support `.htaccess` files and `mod_rewrite`. Second Crack will install its `engine/default.htaccess` file into the web document root as `.htaccess` if an `.htaccess` file doesn't already exist.
|
||||
|
||||
# Basics
|
||||
|
||||
Each `.md` blog-post file is structured like this:
|
||||
|
||||
This is the post title
|
||||
======================
|
||||
Tags: tag1, tag2
|
||||
Published: 2012-01-03 10:22:31pm
|
||||
Type: link
|
||||
|
||||
This is the post body, in **Markdown**.
|
||||
|
||||
It will continue for the rest of the file. See, the top is a lot
|
||||
like an HTTP response with headers, but with a title and a row of
|
||||
any number of equals signs ("===") as the first two headers.
|
||||
|
||||
You can add arbitrary fields, formatted like HTTP headers. You
|
||||
can then customize the templates to respond to them.
|
||||
|
||||
The engine expects a folder structure like this: (this is your `{SOURCE_PATH}` mentioned above)
|
||||
|
||||
(blog-source folder, name it whatever you want)/
|
||||
drafts/
|
||||
draft-post-slug.md
|
||||
another-draft-post-slug.md
|
||||
...
|
||||
media/
|
||||
(images and other files however you want to arrange them. I do it like this:)
|
||||
2010/
|
||||
2011/
|
||||
2012/
|
||||
01/
|
||||
post-slug.png
|
||||
another-day-another-post1.jpg
|
||||
another-day-another-post2.jpg
|
||||
...
|
||||
pages/
|
||||
about.md
|
||||
contact-me.md
|
||||
...
|
||||
posts/
|
||||
2010/
|
||||
2011/
|
||||
2012/
|
||||
01/
|
||||
20120102-01-post-slug.md
|
||||
20120103-01-another-day-another-post.md
|
||||
20120104-01-something-happened-today.md
|
||||
20120104-02-something-else-happened-and-its-still-today.md
|
||||
templates/
|
||||
(this can technically be anywhere but it's easiest to keep it here)
|
||||
main.php
|
||||
rss.php
|
||||
hooks/
|
||||
(optional, code that executes on every new post, see example-hooks)
|
||||
post_twitter.php
|
||||
|
||||
If you're going to use Dropbox to publish, put that top-level blog-source folder somewhere in your Dropbox folder.
|
||||
|
||||
# Set up config.php
|
||||
|
||||
Copy `{SECONDCRACK_PATH}/config.php.example` to `{SECONDCRACK_PATH}/config.php` and edit it to reflect your paths and preferences.
|
||||
|
||||
The blog-source folder (`{SOURCE_PATH}`) should be set as `Updater::$source_path`. The path to `templates/` should be assigned to `Template::$template_dir`.
|
||||
|
||||
# Posting
|
||||
|
||||
Make a file in `drafts/` named as `post-slug-you-want-to-create.md` like this:
|
||||
|
||||
My draft title
|
||||
==============
|
||||
|
||||
Draft content.
|
||||
|
||||
(Don't set a `Published:` header. The engine will add that automatically later.)
|
||||
|
||||
When the updater runs next, the engine will create a sister file in a `_previews/` directory (and a `_publish-now/` directory, if it doesn't already exist), like this:
|
||||
|
||||
drafts/
|
||||
post-slug-you-want-to-create.md
|
||||
...
|
||||
_previews/
|
||||
post-slug-you-want-to-create.html
|
||||
...
|
||||
_publish-now/
|
||||
|
||||
Whenever you want to preview your post, just save the `.md` file, run the updater (or wait for it to run itself), and open the corresponding `.html` file in your browser. When I'm writing, I just keep it open and hit Refresh to see changes.
|
||||
|
||||
To publish a post, either move it into the `_publish-now/` directory, or add a header that simply says "publish-now", like this:
|
||||
|
||||
My finished title
|
||||
=================
|
||||
Tags: whatever, optional
|
||||
publish-now
|
||||
|
||||
Here's my finished content.
|
||||
|
||||
Once that file is saved and the updater runs, it will publish the post immediately, removing it from `drafts/` (and removing its preview file) and moving its final copy into `posts/` in the appropriate year/month subdirectory.
|
||||
|
||||
To edit a post, simply edit its file in `posts/`. The updater will notice the change next time it runs and will update the published post.
|
||||
|
||||
# Posting with bookmarklets
|
||||
|
||||
Second Crack comes with a pair of convenience bookmarklets, "Draft Link" and "Draft Article", that create draft posts from the current page and any selected text.
|
||||
|
||||
To enable this, create a second web root on an alternate domain, e.g. `admin.myblog.com`, using the `api-www/` folder as its document root. Then you can navigate to, e.g.:
|
||||
|
||||
http://admin.myblog.com/add-draft.php
|
||||
|
||||
...and install the bookmarklets from there.
|
||||
|
||||
Your username and password to use these are set in `config.php` under "Meta Weblog API params". (I wanted to make a Meta Weblog interface in here too, but haven't gotten to it.)
|
||||
|
||||
# Dropbox sync (strongly recommended)
|
||||
|
||||
Here's <a href="http://www.youtube.com/watch?v=cu5uXXulnNk">a video demo</a> of why you *really* want Dropbox sync and those bookmarklets.
|
||||
|
||||
Second Crack's updater examines the blog source directory for changes, then writes HTML files into the web directory as needed.
|
||||
|
||||
If you're writing on a computer, you probably have a server hosting the blog somewhere, and need some way to update the content on the server. The best way to do this, and the setup that Second Crack is designed to operate in, is to set up Dropbox in both places: the native client for your computer, and the <a href="https://www.dropbox.com/install?os=lnx">Linux CLI client</a> on the server.
|
||||
|
||||
**NOTE:** If you don't want your server to have access to your entire Dropbox folder, you can do a dual-account trick: create a second Dropbox account, set up _that_ one on the server, and "share" the blog-source folder from your primary account to the second account on Dropbox.
|
||||
|
||||
With Dropbox set up, the publishing and previewing workflow is streamlined, and you never need to run the Second Crack updater manually:
|
||||
|
||||
* Have the preview `.html` file open in your local browser.
|
||||
* Make changes to the draft `.md` file. Hit Save in your editor.
|
||||
* Wait a few seconds. The changes will round-trip to the server and the `.html` file will update.
|
||||
* Hit Refresh in the browser and see the changes you made, rendered right in your site's layout by the server-side engine.
|
||||
|
||||
Even when you publish, just write the "publish-now" header, hit Save, and close the file. Within seconds, it will disappear from the `drafts/` folder and the post will be published.
|
||||
|
||||
It's pretty awesome.
|
||||
|
||||
And if you want to edit your blog on the iPhone or iPad, you can do it with any Dropbox-capable text editor.
|
||||
|
||||
# FAQ
|
||||
|
||||
## Don't a lot of these static-file blogging engines already exist?
|
||||
|
||||
Yes.
|
||||
|
||||
## Have you tried [existing solution]?
|
||||
|
||||
No.
|
||||
|
||||
## Isn't this reinventing the wheel?
|
||||
|
||||
Yes.
|
||||
|
||||
## Don't you have other things you could be working on?
|
||||
|
||||
Yes. (Don't we all?)
|
||||
|
||||
## This name isn't unique.
|
||||
|
||||
You're probably right. Neither the domain nor the Twitter username are available, and it's probably trademarked in an industry I've never heard of.
|
||||
|
||||
Really, it's a terrible idea to launch a major project with such an unavailable name. But this isn't a major project, and I don't intend for it to get widespread enough that those problems will ever matter.
|
||||
|
||||
I needed a name. This came to mind. It's a coffee-roasting term for the moment in the roast that the bean audibly pops for the second time, indicating development of the strongest flavors and the point that you should stop the roast because it's done.
|
||||
|
||||
## You're not entirely correct on that definition. And I prefer my roast to be [x] seconds (before|after) second crack.
|
||||
|
||||
I know. For the purposes of this FAQ, it's not really relevant.
|
||||
|
||||
## OK, back to the static-file blog engine. What have you done differently from [existing solution]?
|
||||
|
||||
A bunch of small things, probably. I don't know enough about the other solutions to really say.
|
||||
|
||||
## Why doesn't it have [feature]?
|
||||
|
||||
Because I didn't think [feature] needed to be there. Some anticipated frequent values for [feature]:
|
||||
|
||||
Comments: Use Disqus or Facebook comments. Or just go without comments. Do you really need them?
|
||||
Stats: Use Google Analytics or Mint. (Or both.)
|
||||
Widgets and dynamic page content: Use Javascript.
|
||||
Dynamic rendering for automatic mobile layouts, etc.: Use CSS.
|
||||
|
||||
## Why should I use this instead of [existing solution]?
|
||||
|
||||
I don't know. You probably shouldn't.
|
||||
|
||||
## Will this make you, me, or anyone any money?
|
||||
|
||||
I doubt it.
|
||||
|
||||
## So why did you make this?
|
||||
|
||||
Because I'm a programmer, and this is what I do.
|
||||
|
||||
Some people jog away from their house every day, only to jog back. Others walk on a treadmill, expending energy to get nowhere. In both cases, it may appear to others that they've accomplished nothing, but they've chosen to do these seemingly redundant activities on a regular basis to incrementally improve themselves. And it works.
|
||||
|
||||
## That's not a perfect analogy. Programming another version of something with lots of existing solutions is nothing like daily cardiovascular exercise.
|
||||
|
||||
I know.
|
89
_site/tmp/var/www/chaospott.de/api-www/add-draft.php
Normal file
@ -0,0 +1,89 @@
|
||||
<?php
|
||||
ob_start();
|
||||
require_once(realpath(dirname(__FILE__) . '/..') . '/config.php');
|
||||
ob_end_clean();
|
||||
|
||||
if (! isset($_SERVER['PHP_AUTH_USER']) ||
|
||||
! isset($_SERVER['PHP_AUTH_PW']) ||
|
||||
$_SERVER['PHP_AUTH_USER'] != Updater::$api_blog_username ||
|
||||
$_SERVER['PHP_AUTH_PW'] != Updater::$api_blog_password
|
||||
) {
|
||||
header('WWW-Authenticate: Basic realm="' . Post::$blog_title . '"');
|
||||
header('HTTP/1.0 401 Unauthorized');
|
||||
//sleep(3);
|
||||
exit;
|
||||
}
|
||||
|
||||
$bookmarklet_code = <<<EOF
|
||||
var d=document,w=window,e=w.getSelection,k=d.getSelection,x=d.selection,s=(e?e():(k)?k():(x?x.createRange().text:0)),l=d.location,e=encodeURIComponent;w.location.href='TARGETadd-draft.php?u='+e(l.href)+'&t='+e(d.title)+'&s='+e(s)+'&EXTRA';
|
||||
EOF;
|
||||
|
||||
$bookmarklet_code = str_replace('TARGET', (isset($_SERVER['HTTPS']) ? 'https://' : 'http://') . $_SERVER['HTTP_HOST'] . '/', trim($bookmarklet_code));
|
||||
|
||||
if (! isset($_GET['u'])) {
|
||||
?>
|
||||
<p>
|
||||
<a href="javascript:<?= rawurlencode(str_replace('EXTRA', 'is-link=1', $bookmarklet_code)) ?>">Draft Link</a> •
|
||||
<a href="javascript:<?= rawurlencode(str_replace('EXTRA', '', $bookmarklet_code)) ?>">Draft Article</a>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Draft link code:<br/>
|
||||
<textarea>javascript:<?= h(rawurlencode(str_replace('EXTRA', 'is-link=1', $bookmarklet_code))) ?></textarea>
|
||||
</p>
|
||||
<p>
|
||||
Draft article code:<br/>
|
||||
<textarea>javascript:<?= h(rawurlencode(str_replace('EXTRA', '', $bookmarklet_code))) ?></textarea>
|
||||
</p>
|
||||
<?
|
||||
exit;
|
||||
}
|
||||
|
||||
$url = substring_before(normalize_space($_GET['u']), ' ');
|
||||
$title = normalize_space($_GET['t']);
|
||||
$selection = trim($_GET['s']);
|
||||
$is_link = isset($_GET['is-link']) && intval($_GET['is-link']);
|
||||
$slug = trim(preg_replace('/[^a-z0-9-]+/ms', '-', strtolower(summarize($title, 60))), '-');
|
||||
if (! $slug) $slug = 'draft';
|
||||
|
||||
if ($selection) {
|
||||
$body = "> " . str_replace("\n", "\n> ", trim($selection)) . "\n\n";
|
||||
if (! $is_link) $body = "[$title]($url):\n\n" . $body;
|
||||
} else {
|
||||
$body = '';
|
||||
}
|
||||
|
||||
$draft_contents =
|
||||
$title . "\n" .
|
||||
str_repeat('=', max(10, min(40, strlen($title)))) . "\n" .
|
||||
($is_link ? "Link: " . $url . "\n" : '') .
|
||||
"publish-not-yet\n" .
|
||||
"\n" .
|
||||
$body
|
||||
;
|
||||
|
||||
$output_path = Updater::$source_path . '/drafts';
|
||||
if (! file_exists($output_path)) die("Drafts path doesn't exist: [$output_path]");
|
||||
if (! is_writable($output_path)) die("Drafts path isn't writable: [$output_path]");
|
||||
|
||||
$output_filename = $output_path . '/' . $slug . Updater::$post_extension;
|
||||
if (! file_put_contents($output_filename, $draft_contents)) die('File write failed');
|
||||
if (! chmod($output_filename, 0666)) die('File permission-set failed');
|
||||
|
||||
// header('Content-Type: text/plain; charset=utf-8');
|
||||
// echo "Saving to [$output_filename]:\n-----------------------\n$draft_contents\n------------------------\n";
|
||||
|
||||
?>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Refresh" content="1;url=<?= h($url) ?>">
|
||||
<meta name="viewport" content="width=320"/>
|
||||
<title>Saved draft</title>
|
||||
</head>
|
||||
<body style="font: Normal 26px 'Lucida Grande', Verdana, sans-serif; text-align:center; color:#888; margin-top:100px;">
|
||||
Saved.
|
||||
<br/>
|
||||
<br/>
|
||||
<a href="<?= h($url) ?>" style="font-size: 11px; color: #aaa;">redirecting back...</a>
|
||||
</body>
|
||||
</html>
|
1
_site/tmp/var/www/chaospott.de/cache/dir-18da8c29b82431964c4ff892e078888e
vendored
Normal file
1
_site/tmp/var/www/chaospott.de/cache/dir-9737440d081918dc9ce1752f5e84793b
vendored
Normal file
@ -0,0 +1 @@
|
||||
a:38:{s:61:"/var/www/chaospott.de/posts/2010/09/20100905-01-mrmcd1001b.md";s:17:"1348070074-3900||";s:59:"/var/www/chaospott.de/posts/2014/03/20140321-01-cms-bonn.md";s:17:"1395737838-2575||";s:56:"/var/www/chaospott.de/posts/2014/08/20140814-01-bbq14.md";s:17:"1408009993-2733||";s:64:"/var/www/chaospott.de/posts/2014/08/20140802-01-nord-open-air.md";s:17:"1407012227-1522||";s:55:"/var/www/chaospott.de/posts/2014/04/20140401-01-30c3.md";s:17:"1396370806-8013||";s:64:"/var/www/chaospott.de/posts/2014/04/20140402-01-cms-essen-rwb.md";s:17:"1396451651-1284||";s:62:"/var/www/chaospott.de/posts/2014/07/20140704-01-cryptoparty.md";s:16:"1404469842-508||";s:62:"/var/www/chaospott.de/posts/2012/02/20120218-01-hackerspace.md";s:17:"1348070093-1359||";s:66:"/var/www/chaospott.de/posts/2012/11/20121105-01-datenspuren2012.md";s:17:"1352122158-1590||";s:69:"/var/www/chaospott.de/posts/2012/03/20120302-01-essen-im-chaosdorf.md";s:16:"1348070094-903||";s:65:"/var/www/chaospott.de/posts/2012/03/20120331-01-cms-in-viersen.md";s:16:"1348070095-900||";s:86:"/var/www/chaospott.de/posts/2012/03/20120309-01-mate-sitzsaecke-und-ein-wenig-licht.md";s:17:"1348070095-1622||";s:58:"/var/www/chaospott.de/posts/2012/09/20120910-02-dev-tal.md";s:16:"1348070106-958||";s:56:"/var/www/chaospott.de/posts/2012/09/20120910-01-gpn12.md";s:17:"1348260125-1221||";s:61:"/var/www/chaospott.de/posts/2012/04/20120422-01-easterhegg.md";s:17:"1348070098-2276||";s:60:"/var/www/chaospott.de/posts/2012/10/20121028-01-haxogreen.md";s:17:"1351539853-1293||";s:55:"/var/www/chaospott.de/posts/2012/10/20121030-01-ipv6.md";s:16:"1351621573-323||";s:59:"/var/www/chaospott.de/posts/2013/05/20130526-01-freifunk.md";s:17:"1369593538-1019||";s:59:"/var/www/chaospott.de/posts/2013/05/20130523-01-docpatch.md";s:16:"1369323066-848||";s:58:"/var/www/chaospott.de/posts/2013/02/20130217-01-hackend.md";s:17:"1361136576-1467||";s:57:"/var/www/chaospott.de/posts/2013/02/20130210-01-aachen.md";s:17:"1364993970-6287||";s:64:"/var/www/chaospott.de/posts/2013/11/20131111-01-openrheinruhr.md";s:17:"1384256258-1444||";s:59:"/var/www/chaospott.de/posts/2013/11/20131104-01-hackover.md";s:17:"1383583516-1499||";s:63:"/var/www/chaospott.de/posts/2013/03/20130323-01-cccmz-besuch.md";s:17:"1364217607-1141||";s:67:"/var/www/chaospott.de/posts/2013/09/20130908-01-datenspuren-2013.md";s:17:"1378641166-1099||";s:58:"/var/www/chaospott.de/posts/2013/09/20130904-01-ohm2013.md";s:17:"1378313536-3016||";s:61:"/var/www/chaospott.de/posts/2013/09/20130929-01-sommerfest.md";s:17:"1380451644-1616||";s:55:"/var/www/chaospott.de/posts/2013/04/20130403-01-eh13.md";s:17:"1364998334-1883||";s:56:"/var/www/chaospott.de/posts/2013/06/20130615-01-gpn13.md";s:17:"1373988871-2059||";s:62:"/var/www/chaospott.de/posts/2013/06/20130608-01-premiumcola.md";s:17:"1371153590-1417||";s:62:"/var/www/chaospott.de/posts/2013/01/20130118-01-cms-viersen.md";s:17:"1364663172-4553||";s:68:"/var/www/chaospott.de/posts/2013/10/20131014-01-cms-bundestreffen.md";s:17:"1381748278-1279||";s:57:"/var/www/chaospott.de/posts/2011/08/20110808-01-cccamp.md";s:16:"1348070082-476||";s:61:"/var/www/chaospott.de/posts/2011/04/20110426-02-easterhegg.md";s:14:"1361136665-0||";s:58:"/var/www/chaospott.de/posts/2011/04/20110426-01-hackend.md";s:16:"1303849479-482||";s:78:"/var/www/chaospott.de/posts/2011/04/20110426-03-easterhegg_safe_save_232217.md";s:14:"1361139737-0||";s:62:"/var/www/chaospott.de/posts/2011/10/20111023-01-datenspuren.md";s:17:"1348070090-1161||";s:58:"/var/www/chaospott.de/posts/2014/10/20141017-01-club-v2.md";s:17:"1413572553-1875||";}
|
1
_site/tmp/var/www/chaospott.de/cache/dir-f218b0e39dfd19fb33cf1d5e30781cf0
vendored
Normal file
@ -0,0 +1 @@
|
||||
a:4:{s:46:"/var/www/chaospott.de/drafts/_previews/test.md";s:15:"1348260663-30||";s:48:"/var/www/chaospott.de/drafts/_previews/test.html";s:17:"1411309287-1376||";s:52:"/var/www/chaospott.de/drafts/_previews/docpatch.html";s:17:"1411309287-2209||";s:52:"/var/www/chaospott.de/drafts/20130523-01-docpatch.md";s:16:"1369322795-839||";}
|
1
_site/tmp/var/www/chaospott.de/cache/dir-f2f777b18aa3755673d2bb364a480db7
vendored
Normal file
@ -0,0 +1 @@
|
||||
a:3:{s:39:"/var/www/chaospott.de/templates/rss.php";s:17:"1348070070-2880||";s:41:"/var/www/chaospott.de/templates/front.php";s:17:"1411309261-2841||";s:40:"/var/www/chaospott.de/templates/main.php";s:17:"1348648910-2810||";}
|
1
_site/tmp/var/www/chaospott.de/cache/months-with-posts-
vendored
Normal file
@ -0,0 +1 @@
|
||||
a:5:{i:2010;a:1:{i:9;b:1;}i:2014;a:5:{i:3;b:1;i:8;b:1;i:4;b:1;i:7;b:1;i:10;b:1;}i:2012;a:6:{i:2;b:1;i:11;b:1;i:3;b:1;i:9;b:1;i:4;b:1;i:10;b:1;}i:2013;a:9:{i:5;b:1;i:2;b:1;i:11;b:1;i:3;b:1;i:9;b:1;i:4;b:1;i:6;b:1;i:1;b:1;i:10;b:1;}i:2011;a:3:{i:8;b:1;i:4;b:1;i:10;b:1;}}
|
1
_site/tmp/var/www/chaospott.de/cache/posts-2010-09-ea58fc35cefd2294269d1e67d6593ead
vendored
Normal file
@ -0,0 +1 @@
|
||||
a:1:{i:5;a:1:{i:0;s:61:"/var/www/chaospott.de/posts/2010/09/20100905-01-mrmcd1001b.md";}}
|
1
_site/tmp/var/www/chaospott.de/cache/posts-2011-04-ea58fc35cefd2294269d1e67d6593ead
vendored
Normal file
@ -0,0 +1 @@
|
||||
a:1:{i:26;a:3:{i:0;s:58:"/var/www/chaospott.de/posts/2011/04/20110426-01-hackend.md";i:1;s:61:"/var/www/chaospott.de/posts/2011/04/20110426-02-easterhegg.md";i:2;s:78:"/var/www/chaospott.de/posts/2011/04/20110426-03-easterhegg_safe_save_232217.md";}}
|
1
_site/tmp/var/www/chaospott.de/cache/posts-2011-08-ea58fc35cefd2294269d1e67d6593ead
vendored
Normal file
@ -0,0 +1 @@
|
||||
a:1:{i:8;a:1:{i:0;s:57:"/var/www/chaospott.de/posts/2011/08/20110808-01-cccamp.md";}}
|
1
_site/tmp/var/www/chaospott.de/cache/posts-2011-10-ea58fc35cefd2294269d1e67d6593ead
vendored
Normal file
@ -0,0 +1 @@
|
||||
a:1:{i:23;a:1:{i:0;s:62:"/var/www/chaospott.de/posts/2011/10/20111023-01-datenspuren.md";}}
|
1
_site/tmp/var/www/chaospott.de/cache/posts-2012-02-ea58fc35cefd2294269d1e67d6593ead
vendored
Normal file
@ -0,0 +1 @@
|
||||
a:1:{i:18;a:1:{i:0;s:62:"/var/www/chaospott.de/posts/2012/02/20120218-01-hackerspace.md";}}
|
1
_site/tmp/var/www/chaospott.de/cache/posts-2012-03-ea58fc35cefd2294269d1e67d6593ead
vendored
Normal file
@ -0,0 +1 @@
|
||||
a:3:{i:2;a:1:{i:0;s:69:"/var/www/chaospott.de/posts/2012/03/20120302-01-essen-im-chaosdorf.md";}i:9;a:1:{i:0;s:86:"/var/www/chaospott.de/posts/2012/03/20120309-01-mate-sitzsaecke-und-ein-wenig-licht.md";}i:31;a:1:{i:0;s:65:"/var/www/chaospott.de/posts/2012/03/20120331-01-cms-in-viersen.md";}}
|
1
_site/tmp/var/www/chaospott.de/cache/posts-2012-04-ea58fc35cefd2294269d1e67d6593ead
vendored
Normal file
@ -0,0 +1 @@
|
||||
a:1:{i:22;a:1:{i:0;s:61:"/var/www/chaospott.de/posts/2012/04/20120422-01-easterhegg.md";}}
|
1
_site/tmp/var/www/chaospott.de/cache/posts-2012-09-7215ee9c7d9dc229d2921a40e899ec5f
vendored
Normal file
@ -0,0 +1 @@
|
||||
a:1:{i:10;a:2:{i:0;s:56:"/var/www/chaospott.de/posts/2012/09/20120910-01-gpn12.md";i:1;s:58:"/var/www/chaospott.de/posts/2012/09/20120910-02-dev-tal.md";}}
|
1
_site/tmp/var/www/chaospott.de/cache/posts-2012-09-ea58fc35cefd2294269d1e67d6593ead
vendored
Normal file
@ -0,0 +1 @@
|
||||
a:1:{i:10;a:2:{i:0;s:56:"/var/www/chaospott.de/posts/2012/09/20120910-01-gpn12.md";i:1;s:58:"/var/www/chaospott.de/posts/2012/09/20120910-02-dev-tal.md";}}
|
1
_site/tmp/var/www/chaospott.de/cache/posts-2012-10-ea58fc35cefd2294269d1e67d6593ead
vendored
Normal file
@ -0,0 +1 @@
|
||||
a:2:{i:28;a:1:{i:0;s:60:"/var/www/chaospott.de/posts/2012/10/20121028-01-haxogreen.md";}i:30;a:1:{i:0;s:55:"/var/www/chaospott.de/posts/2012/10/20121030-01-ipv6.md";}}
|
1
_site/tmp/var/www/chaospott.de/cache/posts-2012-11-ea58fc35cefd2294269d1e67d6593ead
vendored
Normal file
@ -0,0 +1 @@
|
||||
a:1:{i:5;a:1:{i:0;s:66:"/var/www/chaospott.de/posts/2012/11/20121105-01-datenspuren2012.md";}}
|
1
_site/tmp/var/www/chaospott.de/cache/posts-2013-01-ea58fc35cefd2294269d1e67d6593ead
vendored
Normal file
@ -0,0 +1 @@
|
||||
a:1:{i:18;a:1:{i:0;s:62:"/var/www/chaospott.de/posts/2013/01/20130118-01-cms-viersen.md";}}
|
1
_site/tmp/var/www/chaospott.de/cache/posts-2013-02-ea58fc35cefd2294269d1e67d6593ead
vendored
Normal file
@ -0,0 +1 @@
|
||||
a:2:{i:10;a:1:{i:0;s:57:"/var/www/chaospott.de/posts/2013/02/20130210-01-aachen.md";}i:17;a:1:{i:0;s:58:"/var/www/chaospott.de/posts/2013/02/20130217-01-hackend.md";}}
|
1
_site/tmp/var/www/chaospott.de/cache/posts-2013-03-ea58fc35cefd2294269d1e67d6593ead
vendored
Normal file
@ -0,0 +1 @@
|
||||
a:1:{i:23;a:1:{i:0;s:63:"/var/www/chaospott.de/posts/2013/03/20130323-01-cccmz-besuch.md";}}
|
1
_site/tmp/var/www/chaospott.de/cache/posts-2013-04-ea58fc35cefd2294269d1e67d6593ead
vendored
Normal file
@ -0,0 +1 @@
|
||||
a:1:{i:3;a:1:{i:0;s:55:"/var/www/chaospott.de/posts/2013/04/20130403-01-eh13.md";}}
|
1
_site/tmp/var/www/chaospott.de/cache/posts-2013-05-ea58fc35cefd2294269d1e67d6593ead
vendored
Normal file
@ -0,0 +1 @@
|
||||
a:2:{i:23;a:1:{i:0;s:59:"/var/www/chaospott.de/posts/2013/05/20130523-01-docpatch.md";}i:26;a:1:{i:0;s:59:"/var/www/chaospott.de/posts/2013/05/20130526-01-freifunk.md";}}
|
1
_site/tmp/var/www/chaospott.de/cache/posts-2013-06-ea58fc35cefd2294269d1e67d6593ead
vendored
Normal file
@ -0,0 +1 @@
|
||||
a:2:{i:8;a:1:{i:0;s:62:"/var/www/chaospott.de/posts/2013/06/20130608-01-premiumcola.md";}i:15;a:1:{i:0;s:56:"/var/www/chaospott.de/posts/2013/06/20130615-01-gpn13.md";}}
|
1
_site/tmp/var/www/chaospott.de/cache/posts-2013-09-ea58fc35cefd2294269d1e67d6593ead
vendored
Normal file
@ -0,0 +1 @@
|
||||
a:3:{i:4;a:1:{i:0;s:58:"/var/www/chaospott.de/posts/2013/09/20130904-01-ohm2013.md";}i:8;a:1:{i:0;s:67:"/var/www/chaospott.de/posts/2013/09/20130908-01-datenspuren-2013.md";}i:29;a:1:{i:0;s:61:"/var/www/chaospott.de/posts/2013/09/20130929-01-sommerfest.md";}}
|
1
_site/tmp/var/www/chaospott.de/cache/posts-2013-10-ea58fc35cefd2294269d1e67d6593ead
vendored
Normal file
@ -0,0 +1 @@
|
||||
a:1:{i:14;a:1:{i:0;s:68:"/var/www/chaospott.de/posts/2013/10/20131014-01-cms-bundestreffen.md";}}
|
1
_site/tmp/var/www/chaospott.de/cache/posts-2013-11-ea58fc35cefd2294269d1e67d6593ead
vendored
Normal file
@ -0,0 +1 @@
|
||||
a:2:{i:4;a:1:{i:0;s:59:"/var/www/chaospott.de/posts/2013/11/20131104-01-hackover.md";}i:11;a:1:{i:0;s:64:"/var/www/chaospott.de/posts/2013/11/20131111-01-openrheinruhr.md";}}
|
1
_site/tmp/var/www/chaospott.de/cache/posts-2014-03-ea58fc35cefd2294269d1e67d6593ead
vendored
Normal file
@ -0,0 +1 @@
|
||||
a:1:{i:21;a:1:{i:0;s:59:"/var/www/chaospott.de/posts/2014/03/20140321-01-cms-bonn.md";}}
|
1
_site/tmp/var/www/chaospott.de/cache/posts-2014-04-ea58fc35cefd2294269d1e67d6593ead
vendored
Normal file
@ -0,0 +1 @@
|
||||
a:2:{i:1;a:1:{i:0;s:55:"/var/www/chaospott.de/posts/2014/04/20140401-01-30c3.md";}i:2;a:1:{i:0;s:64:"/var/www/chaospott.de/posts/2014/04/20140402-01-cms-essen-rwb.md";}}
|
1
_site/tmp/var/www/chaospott.de/cache/posts-2014-07-ea58fc35cefd2294269d1e67d6593ead
vendored
Normal file
@ -0,0 +1 @@
|
||||
a:1:{i:4;a:1:{i:0;s:62:"/var/www/chaospott.de/posts/2014/07/20140704-01-cryptoparty.md";}}
|
1
_site/tmp/var/www/chaospott.de/cache/posts-2014-08-ea58fc35cefd2294269d1e67d6593ead
vendored
Normal file
@ -0,0 +1 @@
|
||||
a:2:{i:2;a:1:{i:0;s:64:"/var/www/chaospott.de/posts/2014/08/20140802-01-nord-open-air.md";}i:14;a:1:{i:0;s:56:"/var/www/chaospott.de/posts/2014/08/20140814-01-bbq14.md";}}
|
1
_site/tmp/var/www/chaospott.de/cache/posts-2014-10-7215ee9c7d9dc229d2921a40e899ec5f
vendored
Normal file
@ -0,0 +1 @@
|
||||
a:0:{}
|
1
_site/tmp/var/www/chaospott.de/cache/posts-2014-10-ea58fc35cefd2294269d1e67d6593ead
vendored
Normal file
@ -0,0 +1 @@
|
||||
a:1:{i:17;a:1:{i:0;s:58:"/var/www/chaospott.de/posts/2014/10/20141017-01-club-v2.md";}}
|
21
_site/tmp/var/www/chaospott.de/config.php
Normal file
@ -0,0 +1,21 @@
|
||||
<?php
|
||||
// ----- Don't edit this -----------------
|
||||
require_once(dirname(__FILE__) . '/engine/Updater.php');
|
||||
// ---------------------------------------
|
||||
|
||||
|
||||
// ----- You can edit these settings: -----
|
||||
|
||||
//date_default_timezone_set('Europe/Berlin'); // Sometimes I hate PHP. This is one of those times.
|
||||
$basepath = "/var/www/chaospott.de";
|
||||
// Paths
|
||||
Updater::$source_path = "$basepath";
|
||||
Template::$template_dir = "$basepath/templates";
|
||||
Updater::$dest_path = "$basepath/www";
|
||||
Updater::$cache_path = "$basepath/cache";
|
||||
Updater::$post_extension = '.md';
|
||||
|
||||
// Blog metadata
|
||||
Post::$blog_title = 'Chaospott';
|
||||
Post::$blog_url = 'http://chaospott.de/';
|
||||
Post::$blog_description = 'Chaospott';
|
27
_site/tmp/var/www/chaospott.de/config.php.example
Normal file
@ -0,0 +1,27 @@
|
||||
<?php
|
||||
// ----- Don't edit this -----------------
|
||||
require_once(dirname(__FILE__) . '/engine/Updater.php');
|
||||
// ---------------------------------------
|
||||
|
||||
|
||||
// ----- You can edit these settings: -----
|
||||
|
||||
date_default_timezone_set('America/New_York'); // Sometimes I hate PHP. This is one of those times.
|
||||
|
||||
// Paths
|
||||
Updater::$source_path = '/home/marco/Dropbox/Blog';
|
||||
Template::$template_dir = '/home/marco/Dropbox/Blog/templates';
|
||||
Updater::$dest_path = '/home/marco/secondcrack/www';
|
||||
Updater::$cache_path = '/home/marco/secondcrack/cache';
|
||||
Updater::$post_extension = '.md';
|
||||
|
||||
// Blog metadata
|
||||
Post::$blog_title = 'My Blog';
|
||||
Post::$blog_url = 'http://www.mydomain.com/';
|
||||
Post::$blog_description = 'I\'m a blogger.';
|
||||
|
||||
// Meta Weblog API params
|
||||
Updater::$api_blog_id = 1; // leave this, probably
|
||||
Updater::$api_blog_username = 'make up a username';
|
||||
Updater::$api_blog_password = 'whatever password you want';
|
||||
|
@ -0,0 +1,9 @@
|
||||
Docpatch - Entdecke das Grundgesetz!
|
||||
====================================
|
||||
Published: 2013-05-23 05:24:52pm
|
||||
|
||||
Wir haben in den letzten Wochen fleißig an einem unserer Open Data-Projekte gebastelt, dass wir heute mit einer [Pressemitteilung des CCC]](http://ccc.de/de/updates/2013/docpatch) der Öffentlichkeit vorstellen wollen: [DocPatch](http://gg.docpatch.org).
|
||||
|
||||
Zum heutigen Geburtstag des Grundgesetzes (2^6 Jahre) haben wir das Grundgesetz digitaler gemacht. Dank freier Software und Werkzeugen wie Versionskontrolle, Markupsprachen, Exportern und jeder Menge Web-Magie stehe der Erkundung des wichtigsten Dokuments der Republik nichts mehr im Wege! Für mehr Infos schaut bitte in die [Pressemitteilung](http://ccc.de/de/updates/2013/docpatch) oder -- noch besser! -- die [DocPatch-Seite](http://gg.docpatch.org).
|
||||
|
||||
Viel Spaß dabei!
|
5
_site/tmp/var/www/chaospott.de/engine/Hook.php
Normal file
@ -0,0 +1,5 @@
|
||||
<?php
|
||||
abstract class Hook
|
||||
{
|
||||
abstract public function doHook(Post $post);
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
PHP Markdown & Extra
|
||||
Copyright (c) 2004-2009 Michel Fortin
|
||||
<http://michelf.com/>
|
||||
All rights reserved.
|
||||
|
||||
Based on Markdown
|
||||
Copyright (c) 2003-2006 John Gruber
|
||||
<http://daringfireball.net/>
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name "Markdown" nor the names of its contributors may
|
||||
be used to endorse or promote products derived from this software
|
||||
without specific prior written permission.
|
||||
|
||||
This software is provided by the copyright holders and contributors "as
|
||||
is" and any express or implied warranties, including, but not limited
|
||||
to, the implied warranties of merchantability and fitness for a
|
||||
particular purpose are disclaimed. In no event shall the copyright owner
|
||||
or contributors be liable for any direct, indirect, incidental, special,
|
||||
exemplary, or consequential damages (including, but not limited to,
|
||||
procurement of substitute goods or services; loss of use, data, or
|
||||
profits; or business interruption) however caused and on any theory of
|
||||
liability, whether in contract, strict liability, or tort (including
|
||||
negligence or otherwise) arising in any way out of the use of this
|
||||
software, even if advised of the possibility of such damage.
|
@ -0,0 +1,786 @@
|
||||
PHP Markdown Extra
|
||||
==================
|
||||
|
||||
Version 1.2.4 - Sat 10 Oct 2009
|
||||
|
||||
by Michel Fortin
|
||||
<http://michelf.com/>
|
||||
|
||||
based on Markdown by John Gruber
|
||||
<http://daringfireball.net/>
|
||||
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
This is a special version of PHP Markdown with extra features. See
|
||||
<http://michelf.com/projects/php-markdown/extra/> for details.
|
||||
|
||||
Markdown is a text-to-HTML conversion tool for web writers. Markdown
|
||||
allows you to write using an easy-to-read, easy-to-write plain text
|
||||
format, then convert it to structurally valid XHTML (or HTML).
|
||||
|
||||
"Markdown" is two things: a plain text markup syntax, and a software
|
||||
tool, written in Perl, that converts the plain text markup to HTML.
|
||||
PHP Markdown is a port to PHP of the original Markdown program by
|
||||
John Gruber.
|
||||
|
||||
PHP Markdown can work as a plug-in for WordPress and bBlog, as a
|
||||
modifier for the Smarty templating engine, or as a remplacement for
|
||||
textile formatting in any software that support textile.
|
||||
|
||||
Full documentation of Markdown's syntax is available on John's
|
||||
Markdown page: <http://daringfireball.net/projects/markdown/>
|
||||
|
||||
|
||||
Installation and Requirement
|
||||
----------------------------
|
||||
|
||||
PHP Markdown requires PHP version 4.0.5 or later.
|
||||
|
||||
|
||||
### WordPress ###
|
||||
|
||||
PHP Markdown works with [WordPress][wp], version 1.2 or later.
|
||||
|
||||
[wp]: http://wordpress.org/
|
||||
|
||||
1. To use PHP Markdown with WordPress, place the "makrdown.php" file
|
||||
in the "plugins" folder. This folder is located inside
|
||||
"wp-content" at the root of your site:
|
||||
|
||||
(site home)/wp-content/plugins/
|
||||
|
||||
2. Activate the plugin with the administrative interface of
|
||||
WordPress. In the "Plugins" section you will now find Markdown.
|
||||
To activate the plugin, click on the "Activate" button on the
|
||||
same line than Markdown. Your entries will now be formatted by
|
||||
PHP Markdown.
|
||||
|
||||
3. To post Markdown content, you'll first have to disable the
|
||||
"visual" editor in the User section of WordPress.
|
||||
|
||||
You can configure PHP Markdown to not apply to the comments on your
|
||||
WordPress weblog. See the "Configuration" section below.
|
||||
|
||||
It is not possible at this time to apply a different set of
|
||||
filters to different entries. All your entries will be formated by
|
||||
PHP Markdown. This is a limitation of WordPress. If your old entries
|
||||
are written in HTML (as opposed to another formatting syntax, like
|
||||
Textile), they'll probably stay fine after installing Markdown.
|
||||
|
||||
|
||||
### bBlog ###
|
||||
|
||||
PHP Markdown also works with [bBlog][bb].
|
||||
|
||||
[bb]: http://www.bblog.com/
|
||||
|
||||
To use PHP Markdown with bBlog, rename "markdown.php" to
|
||||
"modifier.markdown.php" and place the file in the "bBlog_plugins"
|
||||
folder. This folder is located inside the "bblog" directory of
|
||||
your site, like this:
|
||||
|
||||
(site home)/bblog/bBlog_plugins/modifier.markdown.php
|
||||
|
||||
Select "Markdown" as the "Entry Modifier" when you post a new
|
||||
entry. This setting will only apply to the entry you are editing.
|
||||
|
||||
|
||||
### Replacing Textile in TextPattern ###
|
||||
|
||||
[TextPattern][tp] use [Textile][tx] to format your text. You can
|
||||
replace Textile by Markdown in TextPattern without having to change
|
||||
any code by using the *Texitle Compatibility Mode*. This may work
|
||||
with other software that expect Textile too.
|
||||
|
||||
[tx]: http://www.textism.com/tools/textile/
|
||||
[tp]: http://www.textpattern.com/
|
||||
|
||||
1. Rename the "markdown.php" file to "classTextile.php". This will
|
||||
make PHP Markdown behave as if it was the actual Textile parser.
|
||||
|
||||
2. Replace the "classTextile.php" file TextPattern installed in your
|
||||
web directory. It can be found in the "lib" directory:
|
||||
|
||||
(site home)/textpattern/lib/
|
||||
|
||||
Contrary to Textile, Markdown does not convert quotes to curly ones
|
||||
and does not convert multiple hyphens (`--` and `---`) into en- and
|
||||
em-dashes. If you use PHP Markdown in Textile Compatibility Mode, you
|
||||
can solve this problem by installing the "smartypants.php" file from
|
||||
[PHP SmartyPants][psp] beside the "classTextile.php" file. The Textile
|
||||
Compatibility Mode function will use SmartyPants automatically without
|
||||
further modification.
|
||||
|
||||
[psp]: http://michelf.com/projects/php-smartypants/
|
||||
|
||||
|
||||
### In Your Own Programs ###
|
||||
|
||||
You can use PHP Markdown easily in your current PHP program. Simply
|
||||
include the file and then call the Markdown function on the text you
|
||||
want to convert:
|
||||
|
||||
include_once "markdown.php";
|
||||
$my_html = Markdown($my_text);
|
||||
|
||||
If you wish to use PHP Markdown with another text filter function
|
||||
built to parse HTML, you should filter the text *after* the Markdown
|
||||
function call. This is an example with [PHP SmartyPants][psp]:
|
||||
|
||||
$my_html = SmartyPants(Markdown($my_text));
|
||||
|
||||
|
||||
### With Smarty ###
|
||||
|
||||
If your program use the [Smarty][sm] template engine, PHP Markdown
|
||||
can now be used as a modifier for your templates. Rename "markdown.php"
|
||||
to "modifier.markdown.php" and put it in your smarty plugins folder.
|
||||
|
||||
[sm]: http://smarty.php.net/
|
||||
|
||||
If you are using MovableType 3.1 or later, the Smarty plugin folder is
|
||||
located at `(MT CGI root)/php/extlib/smarty/plugins`. This will allow
|
||||
Markdown to work on dynamic pages.
|
||||
|
||||
|
||||
### Updating Markdown in Other Programs ###
|
||||
|
||||
Many web applications now ship with PHP Markdown, or have plugins to
|
||||
perform the conversion to HTML. You can update PHP Markdown -- or
|
||||
replace it with PHP Markdown Extra -- in many of these programs by
|
||||
swapping the old "markdown.php" file for the new one.
|
||||
|
||||
Here is a short non-exhaustive list of some programs and where they
|
||||
hide the "markdown.php" file.
|
||||
|
||||
| Program | Path to Markdown
|
||||
| ------- | ----------------
|
||||
| [Pivot][] | `(site home)/pivot/includes/markdown/`
|
||||
|
||||
If you're unsure if you can do this with your application, ask the
|
||||
developer, or wait for the developer to update his application or
|
||||
plugin with the new version of PHP Markdown.
|
||||
|
||||
[Pivot]: http://pivotlog.net/
|
||||
|
||||
|
||||
Configuration
|
||||
-------------
|
||||
|
||||
By default, PHP Markdown produces XHTML output for tags with empty
|
||||
elements. E.g.:
|
||||
|
||||
<br />
|
||||
|
||||
Markdown can be configured to produce HTML-style tags; e.g.:
|
||||
|
||||
<br>
|
||||
|
||||
To do this, you must edit the "MARKDOWN_EMPTY_ELEMENT_SUFFIX"
|
||||
definition below the "Global default settings" header at the start of
|
||||
the "markdown.php" file.
|
||||
|
||||
|
||||
### WordPress-Specific Settings ###
|
||||
|
||||
By default, the Markdown plugin applies to both posts and comments on
|
||||
your WordPress weblog. To deactivate one or the other, edit the
|
||||
`MARKDOWN_WP_POSTS` or `MARKDOWN_WP_COMMENTS` definitions under the
|
||||
"WordPress settings" header at the start of the "markdown.php" file.
|
||||
|
||||
|
||||
Bugs
|
||||
----
|
||||
|
||||
To file bug reports please send email to:
|
||||
<michel.fortin@michelf.com>
|
||||
|
||||
Please include with your report: (1) the example input; (2) the output you
|
||||
expected; (3) the output PHP Markdown actually produced.
|
||||
|
||||
|
||||
Version History
|
||||
---------------
|
||||
|
||||
1.0.1n (10 Oct 2009):
|
||||
|
||||
* Enabled reference-style shortcut links. Now you can write reference-style
|
||||
links with less brakets:
|
||||
|
||||
This is [my website].
|
||||
|
||||
[my website]: http://example.com/
|
||||
|
||||
This was added in the 1.0.2 betas, but commented out in the 1.0.1 branch,
|
||||
waiting for the feature to be officialized. [But half of the other Markdown
|
||||
implementations are supporting this syntax][half], so it makes sense for
|
||||
compatibility's sake to allow it in PHP Markdown too.
|
||||
|
||||
[half]: http://babelmark.bobtfish.net/?markdown=This+is+%5Bmy+website%5D.%0D%0A%09%09%0D%0A%5Bmy+website%5D%3A+http%3A%2F%2Fexample.com%2F%0D%0A&src=1&dest=2
|
||||
|
||||
* Now accepting many valid email addresses in autolinks that were
|
||||
previously rejected, such as:
|
||||
|
||||
<abc+mailbox/department=shipping@example.com>
|
||||
<!#$%&'*+-/=?^_`.{|}~@example.com>
|
||||
<"abc@def"@example.com>
|
||||
<"Fred Bloggs"@example.com>
|
||||
<jsmith@[192.0.2.1]>
|
||||
|
||||
* Now accepting spaces in URLs for inline and reference-style links. Such
|
||||
URLs need to be surrounded by angle brakets. For instance:
|
||||
|
||||
[link text](<http://url/with space> "optional title")
|
||||
|
||||
[link text][ref]
|
||||
[ref]: <http://url/with space> "optional title"
|
||||
|
||||
There is still a quirk which may prevent this from working correctly with
|
||||
relative URLs in inline-style links however.
|
||||
|
||||
* Fix for adjacent list of different kind where the second list could
|
||||
end as a sublist of the first when not separated by an empty line.
|
||||
|
||||
* Fixed a bug where inline-style links wouldn't be recognized when the link
|
||||
definition contains a line break between the url and the title.
|
||||
|
||||
* Fixed a bug where tags where the name contains an underscore aren't parsed
|
||||
correctly.
|
||||
|
||||
* Fixed some corner-cases mixing underscore-ephasis and asterisk-emphasis.
|
||||
|
||||
|
||||
Extra 1.2.4:
|
||||
|
||||
* Fixed a problem where unterminated tags in indented code blocks could
|
||||
prevent proper escaping of characaters in the code block.
|
||||
|
||||
|
||||
Extra 1.2.3 (31 Dec 2008):
|
||||
|
||||
* In WordPress pages featuring more than one post, footnote id prefixes are
|
||||
now automatically applied with the current post ID to avoid clashes
|
||||
between footnotes belonging to different posts.
|
||||
|
||||
* Fix for a bug introduced in Extra 1.2 where block-level HTML tags where
|
||||
not detected correctly, thus the addition of erroneous `<p>` tags and
|
||||
interpretation of their content as Markdown-formatted instead of
|
||||
HTML-formatted.
|
||||
|
||||
|
||||
Extra 1.2.2 (21 Jun 2008):
|
||||
|
||||
* Fixed a problem where abbreviation definitions, footnote
|
||||
definitions and link references were stripped inside
|
||||
fenced code blocks.
|
||||
|
||||
* Fixed a bug where characters such as `"` in abbreviation
|
||||
definitions weren't properly encoded to HTML entities.
|
||||
|
||||
* Fixed a bug where double quotes `"` were not correctly encoded
|
||||
as HTML entities when used inside a footnote reference id.
|
||||
|
||||
|
||||
1.0.1m (21 Jun 2008):
|
||||
|
||||
* Lists can now have empty items.
|
||||
|
||||
* Rewrote the emphasis and strong emphasis parser to fix some issues
|
||||
with odly placed and overlong markers.
|
||||
|
||||
|
||||
Extra 1.2.1 (27 May 2008):
|
||||
|
||||
* Fixed a problem where Markdown headers and horizontal rules were
|
||||
transformed into their HTML equivalent inside fenced code blocks.
|
||||
|
||||
|
||||
Extra 1.2 (11 May 2008):
|
||||
|
||||
* Added fenced code block syntax which don't require indentation
|
||||
and can start and end with blank lines. A fenced code block
|
||||
starts with a line of consecutive tilde (~) and ends on the
|
||||
next line with the same number of consecutive tilde. Here's an
|
||||
example:
|
||||
|
||||
~~~~~~~~~~~~
|
||||
Hello World!
|
||||
~~~~~~~~~~~~
|
||||
|
||||
* Rewrote parts of the HTML block parser to better accomodate
|
||||
fenced code blocks.
|
||||
|
||||
* Footnotes may now be referenced from within another footnote.
|
||||
|
||||
* Added programatically-settable parser property `predef_attr` for
|
||||
predefined attribute definitions.
|
||||
|
||||
* Fixed an issue where an indented code block preceded by a blank
|
||||
line containing some other whitespace would confuse the HTML
|
||||
block parser into creating an HTML block when it should have
|
||||
been code.
|
||||
|
||||
|
||||
1.0.1l (11 May 2008):
|
||||
|
||||
* Now removing the UTF-8 BOM at the start of a document, if present.
|
||||
|
||||
* Now accepting capitalized URI schemes (such as HTTP:) in automatic
|
||||
links, such as `<HTTP://EXAMPLE.COM/>`.
|
||||
|
||||
* Fixed a problem where `<hr@example.com>` was seen as a horizontal
|
||||
rule instead of an automatic link.
|
||||
|
||||
* Fixed an issue where some characters in Markdown-generated HTML
|
||||
attributes weren't properly escaped with entities.
|
||||
|
||||
* Fix for code blocks as first element of a list item. Previously,
|
||||
this didn't create any code block for item 2:
|
||||
|
||||
* Item 1 (regular paragraph)
|
||||
|
||||
* Item 2 (code block)
|
||||
|
||||
* A code block starting on the second line of a document wasn't seen
|
||||
as a code block. This has been fixed.
|
||||
|
||||
* Added programatically-settable parser properties `predef_urls` and
|
||||
`predef_titles` for predefined URLs and titles for reference-style
|
||||
links. To use this, your PHP code must call the parser this way:
|
||||
|
||||
$parser = new Markdwon_Parser;
|
||||
$parser->predef_urls = array('linkref' => 'http://example.com');
|
||||
$html = $parser->transform($text);
|
||||
|
||||
You can then use the URL as a normal link reference:
|
||||
|
||||
[my link][linkref]
|
||||
[my link][linkRef]
|
||||
|
||||
Reference names in the parser properties *must* be lowercase.
|
||||
Reference names in the Markdown source may have any case.
|
||||
|
||||
* Added `setup` and `teardown` methods which can be used by subclassers
|
||||
as hook points to arrange the state of some parser variables before and
|
||||
after parsing.
|
||||
|
||||
|
||||
Extra 1.1.7 (26 Sep 2007):
|
||||
|
||||
1.0.1k (26 Sep 2007):
|
||||
|
||||
* Fixed a problem introduced in 1.0.1i where three or more identical
|
||||
uppercase letters, as well as a few other symbols, would trigger
|
||||
a horizontal line.
|
||||
|
||||
|
||||
Extra 1.1.6 (4 Sep 2007):
|
||||
|
||||
1.0.1j (4 Sep 2007):
|
||||
|
||||
* Fixed a problem introduced in 1.0.1i where the closing `code` and
|
||||
`pre` tags at the end of a code block were appearing in the wrong
|
||||
order.
|
||||
|
||||
* Overriding configuration settings by defining constants from an
|
||||
external before markdown.php is included is now possible without
|
||||
producing a PHP warning.
|
||||
|
||||
|
||||
Extra 1.1.5 (31 Aug 2007):
|
||||
|
||||
1.0.1i (31 Aug 2007):
|
||||
|
||||
* Fixed a problem where an escaped backslash before a code span
|
||||
would prevent the code span from being created. This should now
|
||||
work as expected:
|
||||
|
||||
Litteral backslash: \\`code span`
|
||||
|
||||
* Overall speed improvements, especially with long documents.
|
||||
|
||||
|
||||
Extra 1.1.4 (3 Aug 2007):
|
||||
|
||||
1.0.1h (3 Aug 2007):
|
||||
|
||||
* Added two properties (`no_markup` and `no_entities`) to the parser
|
||||
allowing HTML tags and entities to be disabled.
|
||||
|
||||
* Fix for a problem introduced in 1.0.1g where posting comments in
|
||||
WordPress would trigger PHP warnings and cause some markup to be
|
||||
incorrectly filtered by the kses filter in WordPress.
|
||||
|
||||
|
||||
Extra 1.1.3 (3 Jul 2007):
|
||||
|
||||
* Fixed a performance problem when parsing some invalid HTML as an HTML
|
||||
block which was resulting in too much recusion and a segmentation fault
|
||||
for long documents.
|
||||
|
||||
* The markdown="" attribute now accepts unquoted values.
|
||||
|
||||
* Fixed an issue where underscore-emphasis didn't work when applied on the
|
||||
first or the last word of an element having the markdown="1" or
|
||||
markdown="span" attribute set unless there was some surrounding whitespace.
|
||||
This didn't work:
|
||||
|
||||
<p markdown="1">_Hello_ _world_</p>
|
||||
|
||||
Now it does produce emphasis as expected.
|
||||
|
||||
* Fixed an issue preventing footnotes from working when the parser's
|
||||
footnote id prefix variable (fn_id_prefix) is not empty.
|
||||
|
||||
* Fixed a performance problem where the regular expression for strong
|
||||
emphasis introduced in version 1.1 could sometime be long to process,
|
||||
give slightly wrong results, and in some circumstances could remove
|
||||
entirely the content for a whole paragraph.
|
||||
|
||||
* Fixed an issue were abbreviations tags could be incorrectly added
|
||||
inside URLs and title of links.
|
||||
|
||||
* Placing footnote markers inside a link, resulting in two nested links, is
|
||||
no longer allowed.
|
||||
|
||||
|
||||
1.0.1g (3 Jul 2007):
|
||||
|
||||
* Fix for PHP 5 compiled without the mbstring module. Previous fix to
|
||||
calculate the length of UTF-8 strings in `detab` when `mb_strlen` is
|
||||
not available was only working with PHP 4.
|
||||
|
||||
* Fixed a problem with WordPress 2.x where full-content posts in RSS feeds
|
||||
were not processed correctly by Markdown.
|
||||
|
||||
* Now supports URLs containing literal parentheses for inline links
|
||||
and images, such as:
|
||||
|
||||
[WIMP](http://en.wikipedia.org/wiki/WIMP_(computing))
|
||||
|
||||
Such parentheses may be arbitrarily nested, but must be
|
||||
balanced. Unbalenced parentheses are allowed however when the URL
|
||||
when escaped or when the URL is enclosed in angle brakets `<>`.
|
||||
|
||||
* Fixed a performance problem where the regular expression for strong
|
||||
emphasis introduced in version 1.0.1d could sometime be long to process,
|
||||
give slightly wrong results, and in some circumstances could remove
|
||||
entirely the content for a whole paragraph.
|
||||
|
||||
* Some change in version 1.0.1d made possible the incorrect nesting of
|
||||
anchors within each other. This is now fixed.
|
||||
|
||||
* Fixed a rare issue where certain MD5 hashes in the content could
|
||||
be changed to their corresponding text. For instance, this:
|
||||
|
||||
The MD5 value for "+" is "26b17225b626fb9238849fd60eabdf60".
|
||||
|
||||
was incorrectly changed to this in previous versions of PHP Markdown:
|
||||
|
||||
<p>The MD5 value for "+" is "+".</p>
|
||||
|
||||
* Now convert escaped characters to their numeric character
|
||||
references equivalent.
|
||||
|
||||
This fix an integration issue with SmartyPants and backslash escapes.
|
||||
Since Markdown and SmartyPants have some escapable characters in common,
|
||||
it was sometime necessary to escape them twice. Previously, two
|
||||
backslashes were sometime required to prevent Markdown from "eating" the
|
||||
backslash before SmartyPants sees it:
|
||||
|
||||
Here are two hyphens: \\--
|
||||
|
||||
Now, only one backslash will do:
|
||||
|
||||
Here are two hyphens: \--
|
||||
|
||||
|
||||
Extra 1.1.2 (7 Feb 2007)
|
||||
|
||||
* Fixed an issue where headers preceded too closely by a paragraph
|
||||
(with no blank line separating them) where put inside the paragraph.
|
||||
|
||||
* Added the missing TextileRestricted method that was added to regular
|
||||
PHP Markdown since 1.0.1d but which I forgot to add to Extra.
|
||||
|
||||
|
||||
1.0.1f (7 Feb 2007):
|
||||
|
||||
* Fixed an issue with WordPress where manually-entered excerpts, but
|
||||
not the auto-generated ones, would contain nested paragraphs.
|
||||
|
||||
* Fixed an issue introduced in 1.0.1d where headers and blockquotes
|
||||
preceded too closely by a paragraph (not separated by a blank line)
|
||||
where incorrectly put inside the paragraph.
|
||||
|
||||
* Fixed an issue introduced in 1.0.1d in the tokenizeHTML method where
|
||||
two consecutive code spans would be merged into one when together they
|
||||
form a valid tag in a multiline paragraph.
|
||||
|
||||
* Fixed an long-prevailing issue where blank lines in code blocks would
|
||||
be doubled when the code block is in a list item.
|
||||
|
||||
This was due to the list processing functions relying on artificially
|
||||
doubled blank lines to correctly determine when list items should
|
||||
contain block-level content. The list item processing model was thus
|
||||
changed to avoid the need for double blank lines.
|
||||
|
||||
* Fixed an issue with `<% asp-style %>` instructions used as inline
|
||||
content where the opening `<` was encoded as `<`.
|
||||
|
||||
* Fixed a parse error occuring when PHP is configured to accept
|
||||
ASP-style delimiters as boundaries for PHP scripts.
|
||||
|
||||
* Fixed a bug introduced in 1.0.1d where underscores in automatic links
|
||||
got swapped with emphasis tags.
|
||||
|
||||
|
||||
Extra 1.1.1 (28 Dec 2006)
|
||||
|
||||
* Fixed a problem where whitespace at the end of the line of an atx-style
|
||||
header would cause tailing `#` to appear as part of the header's content.
|
||||
This was caused by a small error in the regex that handles the definition
|
||||
for the id attribute in PHP Markdown Extra.
|
||||
|
||||
* Fixed a problem where empty abbreviations definitions would eat the
|
||||
following line as its definition.
|
||||
|
||||
* Fixed an issue with calling the Markdown parser repetitivly with text
|
||||
containing footnotes. The footnote hashes were not reinitialized properly.
|
||||
|
||||
|
||||
1.0.1e (28 Dec 2006)
|
||||
|
||||
* Added support for internationalized domain names for email addresses in
|
||||
automatic link. Improved the speed at which email addresses are converted
|
||||
to entities. Thanks to Milian Wolff for his optimisations.
|
||||
|
||||
* Made deterministic the conversion to entities of email addresses in
|
||||
automatic links. This means that a given email address will always be
|
||||
encoded the same way.
|
||||
|
||||
* PHP Markdown will now use its own function to calculate the length of an
|
||||
UTF-8 string in `detab` when `mb_strlen` is not available instead of
|
||||
giving a fatal error.
|
||||
|
||||
|
||||
Extra 1.1 (1 Dec 2006)
|
||||
|
||||
* Added a syntax for footnotes.
|
||||
|
||||
* Added an experimental syntax to define abbreviations.
|
||||
|
||||
|
||||
1.0.1d (1 Dec 2006)
|
||||
|
||||
* Fixed a bug where inline images always had an empty title attribute. The
|
||||
title attribute is now present only when explicitly defined.
|
||||
|
||||
* Link references definitions can now have an empty title, previously if the
|
||||
title was defined but left empty the link definition was ignored. This can
|
||||
be useful if you want an empty title attribute in images to hide the
|
||||
tooltip in Internet Explorer.
|
||||
|
||||
* Made `detab` aware of UTF-8 characters. UTF-8 multi-byte sequences are now
|
||||
correctly mapped to one character instead of the number of bytes.
|
||||
|
||||
* Fixed a small bug with WordPress where WordPress' default filter `wpautop`
|
||||
was not properly deactivated on comment text, resulting in hard line breaks
|
||||
where Markdown do not prescribes them.
|
||||
|
||||
* Added a `TextileRestrited` method to the textile compatibility mode. There
|
||||
is no restriction however, as Markdown does not have a restricted mode at
|
||||
this point. This should make PHP Markdown work again in the latest
|
||||
versions of TextPattern.
|
||||
|
||||
* Converted PHP Markdown to a object-oriented design.
|
||||
|
||||
* Changed span and block gamut methods so that they loop over a
|
||||
customizable list of methods. This makes subclassing the parser a more
|
||||
interesting option for creating syntax extensions.
|
||||
|
||||
* Also added a "document" gamut loop which can be used to hook document-level
|
||||
methods (like for striping link definitions).
|
||||
|
||||
* Changed all methods which were inserting HTML code so that they now return
|
||||
a hashed representation of the code. New methods `hashSpan` and `hashBlock`
|
||||
are used to hash respectivly span- and block-level generated content. This
|
||||
has a couple of significant effects:
|
||||
|
||||
1. It prevents invalid nesting of Markdown-generated elements which
|
||||
could occur occuring with constructs like `*something [link*][1]`.
|
||||
2. It prevents problems occuring with deeply nested lists on which
|
||||
paragraphs were ill-formed.
|
||||
3. It removes the need to call `hashHTMLBlocks` twice during the the
|
||||
block gamut.
|
||||
|
||||
Hashes are turned back to HTML prior output.
|
||||
|
||||
* Made the block-level HTML parser smarter using a specially-crafted regular
|
||||
expression capable of handling nested tags.
|
||||
|
||||
* Solved backtick issues in tag attributes by rewriting the HTML tokenizer to
|
||||
be aware of code spans. All these lines should work correctly now:
|
||||
|
||||
<span attr='`ticks`'>bar</span>
|
||||
<span attr='``double ticks``'>bar</span>
|
||||
`<test a="` content of attribute `">`
|
||||
|
||||
* Changed the parsing of HTML comments to match simply from `<!--` to `-->`
|
||||
instead using of the more complicated SGML-style rule with paired `--`.
|
||||
This is how most browsers parse comments and how XML defines them too.
|
||||
|
||||
* `<address>` has been added to the list of block-level elements and is now
|
||||
treated as an HTML block instead of being wrapped within paragraph tags.
|
||||
|
||||
* Now only trim trailing newlines from code blocks, instead of trimming
|
||||
all trailing whitespace characters.
|
||||
|
||||
* Fixed bug where this:
|
||||
|
||||
[text](http://m.com "title" )
|
||||
|
||||
wasn't working as expected, because the parser wasn't allowing for spaces
|
||||
before the closing paren.
|
||||
|
||||
* Filthy hack to support markdown='1' in div tags.
|
||||
|
||||
* _DoAutoLinks() now supports the 'dict://' URL scheme.
|
||||
|
||||
* PHP- and ASP-style processor instructions are now protected as
|
||||
raw HTML blocks.
|
||||
|
||||
<? ... ?>
|
||||
<% ... %>
|
||||
|
||||
* Fix for escaped backticks still triggering code spans:
|
||||
|
||||
There are two raw backticks here: \` and here: \`, not a code span
|
||||
|
||||
|
||||
Extra 1.0 - 5 September 2005
|
||||
|
||||
* Added support for setting the id attributes for headers like this:
|
||||
|
||||
Header 1 {#header1}
|
||||
========
|
||||
|
||||
## Header 2 ## {#header2}
|
||||
|
||||
This only work only for headers for now.
|
||||
|
||||
* Tables will now work correctly as the first element of a definition
|
||||
list. For example, this input:
|
||||
|
||||
Term
|
||||
|
||||
: Header | Header
|
||||
------- | -------
|
||||
Cell | Cell
|
||||
|
||||
used to produce no definition list and a table where the first
|
||||
header was named ": Header". This is now fixed.
|
||||
|
||||
* Fix for a problem where a paragraph following a table was not
|
||||
placed between `<p>` tags.
|
||||
|
||||
|
||||
Extra 1.0b4 - 1 August 2005
|
||||
|
||||
* Fixed some issues where whitespace around HTML blocks were trigging
|
||||
empty paragraph tags.
|
||||
|
||||
* Fixed an HTML block parsing issue that would cause a block element
|
||||
following a code span or block with unmatched opening bracket to be
|
||||
placed inside a paragraph.
|
||||
|
||||
* Removed some PHP notices that could appear when parsing definition
|
||||
lists and tables with PHP notice reporting flag set.
|
||||
|
||||
|
||||
Extra 1.0b3 - 29 July 2005
|
||||
|
||||
* Definition lists now require a blank line before each term. Solves
|
||||
an ambiguity where the last line of lazy-indented definitions could
|
||||
be mistaken by PHP Markdown as a new term in the list.
|
||||
|
||||
* Definition lists now support multiple terms per definition.
|
||||
|
||||
* Some special tags were replaced in the output by their md5 hash
|
||||
key. Things such as this now work as expected:
|
||||
|
||||
## Header <?php echo $number ?> ##
|
||||
|
||||
|
||||
Extra 1.0b2 - 26 July 2005
|
||||
|
||||
* Definition lists can now take two or more definitions for one term.
|
||||
This should have been the case before, but a bug prevented this
|
||||
from working right.
|
||||
|
||||
* Fixed a problem where single column table with a pipe only at the
|
||||
end where not parsed as table. Here is such a table:
|
||||
|
||||
| header
|
||||
| ------
|
||||
| cell
|
||||
|
||||
* Fixed problems with empty cells in the first column of a table with
|
||||
no leading pipe, like this one:
|
||||
|
||||
header | header
|
||||
------ | ------
|
||||
| cell
|
||||
|
||||
* Code spans containing pipes did not within a table. This is now
|
||||
fixed by parsing code spans before splitting rows into cells.
|
||||
|
||||
* Added the pipe character to the backlash escape character lists.
|
||||
|
||||
Extra 1.0b1 (25 Jun 2005)
|
||||
|
||||
* First public release of PHP Markdown Extra.
|
||||
|
||||
|
||||
Copyright and License
|
||||
---------------------
|
||||
|
||||
PHP Markdown & Extra
|
||||
Copyright (c) 2004-2009 Michel Fortin
|
||||
<http://michelf.com/>
|
||||
All rights reserved.
|
||||
|
||||
Based on Markdown
|
||||
Copyright (c) 2003-2005 John Gruber
|
||||
<http://daringfireball.net/>
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the
|
||||
distribution.
|
||||
|
||||
* Neither the name "Markdown" nor the names of its contributors may
|
||||
be used to endorse or promote products derived from this software
|
||||
without specific prior written permission.
|
||||
|
||||
This software is provided by the copyright holders and contributors "as
|
||||
is" and any express or implied warranties, including, but not limited
|
||||
to, the implied warranties of merchantability and fitness for a
|
||||
particular purpose are disclaimed. In no event shall the copyright owner
|
||||
or contributors be liable for any direct, indirect, incidental, special,
|
||||
exemplary, or consequential damages (including, but not limited to,
|
||||
procurement of substitute goods or services; loss of use, data, or
|
||||
profits; or business interruption) however caused and on any theory of
|
||||
liability, whether in contract, strict liability, or tort (including
|
||||
negligence or otherwise) arising in any way out of the use of this
|
||||
software, even if advised of the possibility of such damage.
|
2932
_site/tmp/var/www/chaospott.de/engine/PHPMarkdownExtra/markdown.php
Normal file
@ -0,0 +1,394 @@
|
||||
PHP SmartyPants
|
||||
===============
|
||||
|
||||
Version 1.5.1e - Fri 9 Dec 2005
|
||||
|
||||
by Michel Fortin
|
||||
<http://www.michelf.com/>
|
||||
|
||||
based on work by John Gruber
|
||||
<http://daringfireball.net/>
|
||||
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
PHP SmartyPants is a port to PHP of the original SmartyPants written
|
||||
in Perl by John Gruber.
|
||||
|
||||
PHP SmartyPants is a free web publishing plug-in for WordPress and
|
||||
Smarty template engine that easily translates plain ASCII punctuation
|
||||
characters into "smart" typographic punctuation HTML entities.
|
||||
SmartyPants can also be invoked as a standalone PHP function.
|
||||
|
||||
SmartyPants can perform the following transformations:
|
||||
|
||||
* Straight quotes (`"` and `'`) into "curly" quote HTML entities
|
||||
* Backtick-style quotes (` ``like this'' `) into "curly" quote HTML
|
||||
entities
|
||||
* Dashes (`--` and `---`) into en- and em-dash entities
|
||||
* Three consecutive dots (`...`) into an ellipsis entity
|
||||
|
||||
This means you can write, edit, and save using plain old ASCII straight
|
||||
quotes, plain dashes, and plain dots, but your published posts (and
|
||||
final HTML output) will appear with smart quotes, em-dashes, and proper
|
||||
ellipses.
|
||||
|
||||
SmartyPants does not modify characters within `<pre>`, `<code>`,
|
||||
`<kbd>`, or `<script>` tag blocks. Typically, these tags are used to
|
||||
display text where smart quotes and other "smart punctuation" would not
|
||||
be appropriate, such as source code or example markup.
|
||||
|
||||
|
||||
### Backslash Escapes ###
|
||||
|
||||
If you need to use literal straight quotes (or plain hyphens and
|
||||
periods), SmartyPants accepts the following backslash escape sequences
|
||||
to force non-smart punctuation. It does so by transforming the escape
|
||||
sequence into a decimal-encoded HTML entity:
|
||||
|
||||
|
||||
Escape Value Character
|
||||
------ ----- ---------
|
||||
\\ \ \
|
||||
\" " "
|
||||
\' ' '
|
||||
\. . .
|
||||
\- - -
|
||||
\` ` `
|
||||
|
||||
|
||||
This is useful, for example, when you want to use straight quotes as
|
||||
foot and inch marks:
|
||||
|
||||
6\'2\" tall
|
||||
|
||||
translates into:
|
||||
|
||||
6'2" tall
|
||||
|
||||
in SmartyPants's HTML output. Which, when rendered by a web browser,
|
||||
looks like:
|
||||
|
||||
6'2" tall
|
||||
|
||||
|
||||
Installation and Requirement
|
||||
----------------------------
|
||||
|
||||
PHP SmartyPants require PHP version 4.0.5 or later.
|
||||
|
||||
|
||||
### WordPress ###
|
||||
|
||||
WordPress already include a filter called "Texturize" with the same
|
||||
goal as SmartyPants. You could still find some usefulness to
|
||||
PHP SmartyPants if you are not happy enough with the standard algorithm.
|
||||
|
||||
PHP SmartyPants works with [WordPress][wp], version 1.2 or later.
|
||||
|
||||
[wp]: http://wordpress.org/
|
||||
|
||||
1. To use PHP SmartyPants with WordPress, place the "smartypants.php"
|
||||
file in the "plugins" folder. This folder is hidden inside
|
||||
"wp-content" at the root of your site:
|
||||
|
||||
(site home)/wp-content/plugins/smartypants.php
|
||||
|
||||
2. Activate the plugin with the administrative interface of WordPress.
|
||||
In the "Plugins" section you will now find SmartyPants. To activate
|
||||
the plugin, click on the "Activate" button on the same line than
|
||||
SmartyPants. Your entries will now be filtered by PHP SmartyPants.
|
||||
|
||||
Note: It is not possible at this time to apply a different set of
|
||||
filters to different entries. All your entries will be filtered by
|
||||
PHP SmartyPants if the plugin is active. This is currently a limitation
|
||||
of WordPress.
|
||||
|
||||
|
||||
### Blosxom ###
|
||||
|
||||
SmartyPants works with Blosxom version 2.0 or later.
|
||||
|
||||
1. Rename the "SmartyPants.pl" plug-in to "SmartyPants" (case is
|
||||
important). Movable Type requires plug-ins to have a ".pl"
|
||||
extension; Blosxom forbids it (at least as of this writing).
|
||||
|
||||
2. Copy the "SmartyPants" plug-in file to your Blosxom plug-ins folder.
|
||||
If you're not sure where your Blosxom plug-ins folder is, see the
|
||||
Blosxom documentation for information.
|
||||
|
||||
3. That's it. The entries in your weblog should now automatically have
|
||||
SmartyPants's default transformations applied.
|
||||
|
||||
4. If you wish to configure SmartyPants's behavior, open the
|
||||
"SmartyPants" plug-in, and edit the value of the `$smartypants_attr`
|
||||
configuration variable, located near the top of the script. The
|
||||
default value is 1; see "Options", below, for the full list of
|
||||
supported values.
|
||||
|
||||
|
||||
### In your programs ###
|
||||
|
||||
You can use PHP SmartyPants easily in your current PHP program. Simply
|
||||
include the file and then call the `SmartyPants` function on the text
|
||||
you want to convert:
|
||||
|
||||
include_once "smartypants.php";
|
||||
$my_text = SmartyPants($my_text);
|
||||
|
||||
|
||||
### With Smarty ###
|
||||
|
||||
If your program use the [Smarty][sm] template engine, PHP SmartyPants
|
||||
can now be used as a modifier for your templates. Rename
|
||||
"smartypants.php" to "modifier.smartypants.php" and put it in your
|
||||
smarty plugins folder.
|
||||
|
||||
[sm]: http://smarty.php.net/
|
||||
|
||||
|
||||
Options and Configuration
|
||||
-------------------------
|
||||
|
||||
Settings are specified by editing the value of the `$smartypants_attr`
|
||||
variable in the "smartypants.php" file. For users of the Smarty template
|
||||
engine, the "smartypants" modifier also takes an optional attribute where
|
||||
you can specify configuration options, like this:
|
||||
`{$var|smartypants:1}` (where "1" is the configuration option).
|
||||
|
||||
Numeric values are the easiest way to configure SmartyPants's behavior:
|
||||
|
||||
"0"
|
||||
Suppress all transformations. (Do nothing.)
|
||||
|
||||
"1"
|
||||
Performs default SmartyPants transformations: quotes (including
|
||||
backticks-style), em-dashes, and ellipses. `--` (dash dash) is
|
||||
used to signify an em-dash; there is no support for en-dashes.
|
||||
|
||||
"2"
|
||||
Same as smarty_pants="1", except that it uses the old-school
|
||||
typewriter shorthand for dashes: `--` (dash dash) for en-dashes,
|
||||
`---` (dash dash dash) for em-dashes.
|
||||
|
||||
"3"
|
||||
Same as smarty_pants="2", but inverts the shorthand for dashes: `--`
|
||||
(dash dash) for em-dashes, and `---` (dash dash dash) for en-dashes.
|
||||
|
||||
"-1"
|
||||
Stupefy mode. Reverses the SmartyPants transformation process,
|
||||
turning the HTML entities produced by SmartyPants into their ASCII
|
||||
equivalents. E.g. `“` is turned into a simple double-quote
|
||||
(`"`), `—` is turned into two dashes, etc. This is useful if you
|
||||
wish to suppress smart punctuation in specific pages, such as
|
||||
RSS feeds.
|
||||
|
||||
The following single-character attribute values can be combined to
|
||||
toggle individual transformations from within the smarty_pants
|
||||
attribute. For example, to educate normal quotes and em-dashes, but not
|
||||
ellipses or backticks-style quotes:
|
||||
|
||||
$smartypants_attr = "qd";
|
||||
|
||||
Or inside a Smarty template:
|
||||
|
||||
{$var|smartypants:"qd"}
|
||||
|
||||
"q"
|
||||
Educates normal quote characters: (`"`) and (`'`).
|
||||
|
||||
"b"
|
||||
Educates ` ``backticks'' ` double quotes.
|
||||
|
||||
"B"
|
||||
Educates backticks-style double quotes and ` `single' ` quotes.
|
||||
|
||||
"d"
|
||||
Educates em-dashes.
|
||||
|
||||
"D"
|
||||
Educates em-dashes and en-dashes, using old-school typewriter
|
||||
shorthand: (dash dash) for en-dashes, (dash dash dash) for
|
||||
em-dashes.
|
||||
|
||||
"i"
|
||||
Educates em-dashes and en-dashes, using inverted old-school
|
||||
typewriter shorthand: (dash dash) for em-dashes, (dash dash dash)
|
||||
for en-dashes.
|
||||
|
||||
"e"
|
||||
Educates ellipses.
|
||||
|
||||
"w"
|
||||
Translates any instance of `"` into a normal double-quote
|
||||
character. This should be of no interest to most people, but of
|
||||
particular interest to anyone who writes their posts using
|
||||
Dreamweaver, as Dreamweaver inexplicably uses this entity to
|
||||
represent a literal double-quote character. SmartyPants only
|
||||
educates normal quotes, not entities (because ordinarily, entities
|
||||
are used for the explicit purpose of representing the specific
|
||||
character they represent). The "w" option must be used in
|
||||
conjunction with one (or both) of the other quote options ("q" or
|
||||
"b"). Thus, if you wish to apply all SmartyPants transformations
|
||||
(quotes, en- and em-dashes, and ellipses) and also translate
|
||||
`"` entities into regular quotes so SmartyPants can educate
|
||||
them, you should pass the following to the smarty_pants attribute:
|
||||
|
||||
$smartypants_attr = "qDew";
|
||||
|
||||
Inside a Smarty template, this will be:
|
||||
|
||||
{$var|smartypants:"qDew"}
|
||||
|
||||
|
||||
Caveats
|
||||
-------
|
||||
|
||||
### Why You Might Not Want to Use Smart Quotes in Your Weblog ###
|
||||
|
||||
For one thing, you might not care.
|
||||
|
||||
Most normal, mentally stable individuals do not take notice of proper
|
||||
typographic punctuation. Many design and typography nerds, however,
|
||||
break out in a nasty rash when they encounter, say, a restaurant sign
|
||||
that uses a straight apostrophe to spell "Joe's".
|
||||
|
||||
If you're the sort of person who just doesn't care, you might well want
|
||||
to continue not caring. Using straight quotes -- and sticking to the
|
||||
7-bit ASCII character set in general -- is certainly a simpler way to
|
||||
live.
|
||||
|
||||
Even if you *do* care about accurate typography, you still might want to
|
||||
think twice before educating the quote characters in your weblog. One
|
||||
side effect of publishing curly quote HTML entities is that it makes
|
||||
your weblog a bit harder for others to quote from using copy-and-paste.
|
||||
What happens is that when someone copies text from your blog, the copied
|
||||
text contains the 8-bit curly quote characters (as well as the 8-bit
|
||||
characters for em-dashes and ellipses, if you use these options). These
|
||||
characters are not standard across different text encoding methods,
|
||||
which is why they need to be encoded as HTML entities.
|
||||
|
||||
People copying text from your weblog, however, may not notice that
|
||||
you're using curly quotes, and they'll go ahead and paste the unencoded
|
||||
8-bit characters copied from their browser into an email message or
|
||||
their own weblog. When pasted as raw "smart quotes", these characters
|
||||
are likely to get mangled beyond recognition.
|
||||
|
||||
That said, my own opinion is that any decent text editor or email client
|
||||
makes it easy to stupefy smart quote characters into their 7-bit
|
||||
equivalents, and I don't consider it my problem if you're using an
|
||||
indecent text editor or email client.
|
||||
|
||||
### Algorithmic Shortcomings ###
|
||||
|
||||
One situation in which quotes will get curled the wrong way is when
|
||||
apostrophes are used at the start of leading contractions. For example:
|
||||
|
||||
'Twas the night before Christmas.
|
||||
|
||||
In the case above, SmartyPants will turn the apostrophe into an opening
|
||||
single-quote, when in fact it should be a closing one. I don't think
|
||||
this problem can be solved in the general case -- every word processor
|
||||
I've tried gets this wrong as well. In such cases, it's best to use the
|
||||
proper HTML entity for closing single-quotes (`’` or `’`) by
|
||||
hand.
|
||||
|
||||
|
||||
Bugs
|
||||
----
|
||||
|
||||
To file bug reports or feature requests (other than topics listed in the
|
||||
Caveats section above) please send email to:
|
||||
|
||||
<michel.fortin@michelf.com>
|
||||
|
||||
If the bug involves quotes being curled the wrong way, please send
|
||||
example text to illustrate.
|
||||
|
||||
|
||||
Version History
|
||||
---------------
|
||||
|
||||
1.5.1e (9 Dec 2005)
|
||||
|
||||
* Corrected a bug that prevented special characters from being
|
||||
escaped.
|
||||
|
||||
|
||||
1.5.1d (6 Jun 2005)
|
||||
|
||||
* Correct a small bug in `_TokenizeHTML` where a Doctype declaration
|
||||
was not seen as HTML, making curly quotes inside it.
|
||||
|
||||
|
||||
1.5.1c (13 Dec 2004)
|
||||
|
||||
* Changed a regular expression in `_TokenizeHTML` that could lead
|
||||
to a segmentation fault with PHP 4.3.8 on Linux.
|
||||
|
||||
|
||||
1.5.1b (6 Sep 2004)
|
||||
|
||||
* Corrected a problem with quotes immediately following a dash
|
||||
with no space between: `Text--"quoted text"--text.`
|
||||
|
||||
* PHP SmartyPants can now be used as a modifier by the Smarty
|
||||
template engine. Rename the file to "modifier.smartypants.php"
|
||||
and put it in your smarty plugins folder.
|
||||
|
||||
* Replaced a lot of spaces characters by tabs, saving about 4 KB.
|
||||
|
||||
|
||||
1.5.1a (30 Jun 2004)
|
||||
|
||||
* PHP Markdown and PHP Smartypants now share the same `_TokenizeHTML`
|
||||
function when loaded simultanously.
|
||||
|
||||
* Changed the internals of `_TokenizeHTML` to lower the PHP version
|
||||
requirement to PHP 4.0.5.
|
||||
|
||||
|
||||
1.5.1 (6 Jun 2004)
|
||||
|
||||
* Initial release of PHP SmartyPants, based on version 1.5.1 of the
|
||||
original SmartyPants written in Perl.
|
||||
|
||||
|
||||
Copyright and License
|
||||
---------------------
|
||||
|
||||
Copyright (c) 2005 Michel Fortin
|
||||
<http://www.michelf.com/>
|
||||
All rights reserved.
|
||||
|
||||
Copyright (c) 2003-2004 John Gruber
|
||||
<http://daringfireball.net/>
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name "SmartyPants" nor the names of its contributors may
|
||||
be used to endorse or promote products derived from this software
|
||||
without specific prior written permission.
|
||||
|
||||
This software is provided by the copyright holders and contributors "as
|
||||
is" and any express or implied warranties, including, but not limited
|
||||
to, the implied warranties of merchantability and fitness for a
|
||||
particular purpose are disclaimed. In no event shall the copyright owner
|
||||
or contributors be liable for any direct, indirect, incidental, special,
|
||||
exemplary, or consequential damages (including, but not limited to,
|
||||
procurement of substitute goods or services; loss of use, data, or
|
||||
profits; or business interruption) however caused and on any theory of
|
||||
liability, whether in contract, strict liability, or tort (including
|
||||
negligence or otherwise) arising in any way out of the use of this
|
||||
software, even if advised of the possibility of such damage.
|
@ -0,0 +1,860 @@
|
||||
<?php
|
||||
|
||||
#
|
||||
# SmartyPants - Smart punctuation for web sites
|
||||
#
|
||||
# by John Gruber
|
||||
# <http://daringfireball.net>
|
||||
#
|
||||
# PHP port by Michel Fortin
|
||||
# <http://www.michelf.com/>
|
||||
#
|
||||
# Copyright (c) 2003-2004 John Gruber
|
||||
# Copyright (c) 2004-2005 Michel Fortin
|
||||
#
|
||||
|
||||
|
||||
global $SmartyPantsPHPVersion, $SmartyPantsSyntaxVersion,
|
||||
$smartypants_attr, $sp_tags_to_skip;
|
||||
|
||||
$SmartyPantsPHPVersion = '1.5.1e'; # Fru 9 Dec 2005
|
||||
$SmartyPantsSyntaxVersion = '1.5.1'; # Fri 12 Mar 2004
|
||||
|
||||
|
||||
# Configurable variables:
|
||||
$smartypants_attr = "1"; # Change this to configure.
|
||||
# 1 => "--" for em-dashes; no en-dash support
|
||||
# 2 => "---" for em-dashes; "--" for en-dashes
|
||||
# 3 => "--" for em-dashes; "---" for en-dashes
|
||||
# See docs for more configuration options.
|
||||
|
||||
# Globals:
|
||||
$sp_tags_to_skip = '<(/?)(?:pre|code|kbd|script|math)[\s>]';
|
||||
|
||||
|
||||
# -- WordPress plugin interface -----------------------------------------------
|
||||
/*
|
||||
Plugin Name: SmartyPants
|
||||
Plugin URI: http://www.michelf.com/projects/php-smartypants/
|
||||
Description: SmartyPants is a web publishing utility that translates plain ASCII punctuation characters into “smart” typographic punctuation HTML entities. This plugin <strong>replace the default WordPress Texturize algorithm</strong> for the content and the title of your posts, the comments body and author name, and everywhere else Texturize normally apply. Based on the original Perl version by <a href="http://daringfireball.net/">John Gruber</a>.
|
||||
Version: 1.5.1e
|
||||
Author: Michel Fortin
|
||||
Author URI: http://www.michelf.com/
|
||||
*/
|
||||
if (isset($wp_version)) {
|
||||
# Remove default Texturize filter that would conflict with SmartyPants.
|
||||
remove_filter('category_description', 'wptexturize');
|
||||
remove_filter('list_cats', 'wptexturize');
|
||||
remove_filter('comment_author', 'wptexturize');
|
||||
remove_filter('comment_text', 'wptexturize');
|
||||
remove_filter('single_post_title', 'wptexturize');
|
||||
remove_filter('the_title', 'wptexturize');
|
||||
remove_filter('the_content', 'wptexturize');
|
||||
remove_filter('the_excerpt', 'wptexturize');
|
||||
# Add SmartyPants filter with priority 10 (same as Texturize).
|
||||
add_filter('category_description', 'SmartyPants', 10);
|
||||
add_filter('list_cats', 'SmartyPants', 10);
|
||||
add_filter('comment_author', 'SmartyPants', 10);
|
||||
add_filter('comment_text', 'SmartyPants', 10);
|
||||
add_filter('single_post_title', 'SmartyPants', 10);
|
||||
add_filter('the_title', 'SmartyPants', 10);
|
||||
add_filter('the_content', 'SmartyPants', 10);
|
||||
add_filter('the_excerpt', 'SmartyPants', 10);
|
||||
}
|
||||
|
||||
# -- Smarty Modifier Interface ------------------------------------------------
|
||||
function smarty_modifier_smartypants($text, $attr = NULL) {
|
||||
return SmartyPants($text, $attr);
|
||||
}
|
||||
|
||||
|
||||
|
||||
function SmartyPants($text, $attr = NULL, $ctx = NULL) {
|
||||
global $smartypants_attr, $sp_tags_to_skip;
|
||||
# Paramaters:
|
||||
$text; # text to be parsed
|
||||
$attr; # value of the smart_quotes="" attribute
|
||||
$ctx; # MT context object (unused)
|
||||
if ($attr == NULL) $attr = $smartypants_attr;
|
||||
|
||||
# Options to specify which transformations to make:
|
||||
$do_stupefy = FALSE;
|
||||
$convert_quot = 0; # should we translate " entities into normal quotes?
|
||||
|
||||
# Parse attributes:
|
||||
# 0 : do nothing
|
||||
# 1 : set all
|
||||
# 2 : set all, using old school en- and em- dash shortcuts
|
||||
# 3 : set all, using inverted old school en and em- dash shortcuts
|
||||
#
|
||||
# q : quotes
|
||||
# b : backtick quotes (``double'' only)
|
||||
# B : backtick quotes (``double'' and `single')
|
||||
# d : dashes
|
||||
# D : old school dashes
|
||||
# i : inverted old school dashes
|
||||
# e : ellipses
|
||||
# w : convert " entities to " for Dreamweaver users
|
||||
|
||||
if ($attr == "0") {
|
||||
# Do nothing.
|
||||
return $text;
|
||||
}
|
||||
else if ($attr == "1") {
|
||||
# Do everything, turn all options on.
|
||||
$do_quotes = 1;
|
||||
$do_backticks = 1;
|
||||
$do_dashes = 1;
|
||||
$do_ellipses = 1;
|
||||
}
|
||||
else if ($attr == "2") {
|
||||
# Do everything, turn all options on, use old school dash shorthand.
|
||||
$do_quotes = 1;
|
||||
$do_backticks = 1;
|
||||
$do_dashes = 2;
|
||||
$do_ellipses = 1;
|
||||
}
|
||||
else if ($attr == "3") {
|
||||
# Do everything, turn all options on, use inverted old school dash shorthand.
|
||||
$do_quotes = 1;
|
||||
$do_backticks = 1;
|
||||
$do_dashes = 3;
|
||||
$do_ellipses = 1;
|
||||
}
|
||||
else if ($attr == "-1") {
|
||||
# Special "stupefy" mode.
|
||||
$do_stupefy = 1;
|
||||
}
|
||||
else {
|
||||
$chars = preg_split('//', $attr);
|
||||
foreach ($chars as $c){
|
||||
if ($c == "q") { $do_quotes = 1; }
|
||||
else if ($c == "b") { $do_backticks = 1; }
|
||||
else if ($c == "B") { $do_backticks = 2; }
|
||||
else if ($c == "d") { $do_dashes = 1; }
|
||||
else if ($c == "D") { $do_dashes = 2; }
|
||||
else if ($c == "i") { $do_dashes = 3; }
|
||||
else if ($c == "e") { $do_ellipses = 1; }
|
||||
else if ($c == "w") { $convert_quot = 1; }
|
||||
else {
|
||||
# Unknown attribute option, ignore.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$tokens = _TokenizeHTML($text);
|
||||
$result = '';
|
||||
$in_pre = 0; # Keep track of when we're inside <pre> or <code> tags.
|
||||
|
||||
$prev_token_last_char = ""; # This is a cheat, used to get some context
|
||||
# for one-character tokens that consist of
|
||||
# just a quote char. What we do is remember
|
||||
# the last character of the previous text
|
||||
# token, to use as context to curl single-
|
||||
# character quote tokens correctly.
|
||||
|
||||
foreach ($tokens as $cur_token) {
|
||||
if ($cur_token[0] == "tag") {
|
||||
# Don't mess with quotes inside tags.
|
||||
$result .= $cur_token[1];
|
||||
if (preg_match("@$sp_tags_to_skip@", $cur_token[1], $matches)) {
|
||||
$in_pre = isset($matches[1]) && $matches[1] == '/' ? 0 : 1;
|
||||
}
|
||||
} else {
|
||||
$t = $cur_token[1];
|
||||
$last_char = substr($t, -1); # Remember last char of this token before processing.
|
||||
if (! $in_pre) {
|
||||
$t = ProcessEscapes($t);
|
||||
|
||||
if ($convert_quot) {
|
||||
$t = preg_replace('/"/', '"', $t);
|
||||
}
|
||||
|
||||
if ($do_dashes) {
|
||||
if ($do_dashes == 1) $t = EducateDashes($t);
|
||||
if ($do_dashes == 2) $t = EducateDashesOldSchool($t);
|
||||
if ($do_dashes == 3) $t = EducateDashesOldSchoolInverted($t);
|
||||
}
|
||||
|
||||
if ($do_ellipses) $t = EducateEllipses($t);
|
||||
|
||||
# Note: backticks need to be processed before quotes.
|
||||
if ($do_backticks) {
|
||||
$t = EducateBackticks($t);
|
||||
if ($do_backticks == 2) $t = EducateSingleBackticks($t);
|
||||
}
|
||||
|
||||
if ($do_quotes) {
|
||||
if ($t == "'") {
|
||||
# Special case: single-character ' token
|
||||
if (preg_match('/\S/', $prev_token_last_char)) {
|
||||
$t = "’";
|
||||
}
|
||||
else {
|
||||
$t = "‘";
|
||||
}
|
||||
}
|
||||
else if ($t == '"') {
|
||||
# Special case: single-character " token
|
||||
if (preg_match('/\S/', $prev_token_last_char)) {
|
||||
$t = "”";
|
||||
}
|
||||
else {
|
||||
$t = "“";
|
||||
}
|
||||
}
|
||||
else {
|
||||
# Normal case:
|
||||
$t = EducateQuotes($t);
|
||||
}
|
||||
}
|
||||
|
||||
if ($do_stupefy) $t = StupefyEntities($t);
|
||||
}
|
||||
$prev_token_last_char = $last_char;
|
||||
$result .= $t;
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
|
||||
function SmartQuotes($text, $attr = NULL, $ctx = NULL) {
|
||||
global $smartypants_attr, $sp_tags_to_skip;
|
||||
# Paramaters:
|
||||
$text; # text to be parsed
|
||||
$attr; # value of the smart_quotes="" attribute
|
||||
$ctx; # MT context object (unused)
|
||||
if ($attr == NULL) $attr = $smartypants_attr;
|
||||
|
||||
$do_backticks; # should we educate ``backticks'' -style quotes?
|
||||
|
||||
if ($attr == 0) {
|
||||
# do nothing;
|
||||
return $text;
|
||||
}
|
||||
else if ($attr == 2) {
|
||||
# smarten ``backticks'' -style quotes
|
||||
$do_backticks = 1;
|
||||
}
|
||||
else {
|
||||
$do_backticks = 0;
|
||||
}
|
||||
|
||||
# Special case to handle quotes at the very end of $text when preceded by
|
||||
# an HTML tag. Add a space to give the quote education algorithm a bit of
|
||||
# context, so that it can guess correctly that it's a closing quote:
|
||||
$add_extra_space = 0;
|
||||
if (preg_match("/>['\"]\\z/", $text)) {
|
||||
$add_extra_space = 1; # Remember, so we can trim the extra space later.
|
||||
$text .= " ";
|
||||
}
|
||||
|
||||
$tokens = _TokenizeHTML($text);
|
||||
$result = '';
|
||||
$in_pre = 0; # Keep track of when we're inside <pre> or <code> tags
|
||||
|
||||
$prev_token_last_char = ""; # This is a cheat, used to get some context
|
||||
# for one-character tokens that consist of
|
||||
# just a quote char. What we do is remember
|
||||
# the last character of the previous text
|
||||
# token, to use as context to curl single-
|
||||
# character quote tokens correctly.
|
||||
|
||||
foreach ($tokens as $cur_token) {
|
||||
if ($cur_token[0] == "tag") {
|
||||
# Don't mess with quotes inside tags
|
||||
$result .= $cur_token[1];
|
||||
if (preg_match("@$sp_tags_to_skip@", $cur_token[1], $matches)) {
|
||||
$in_pre = isset($matches[1]) && $matches[1] == '/' ? 0 : 1;
|
||||
}
|
||||
} else {
|
||||
$t = $cur_token[1];
|
||||
$last_char = substr($t, -1); # Remember last char of this token before processing.
|
||||
if (! $in_pre) {
|
||||
$t = ProcessEscapes($t);
|
||||
if ($do_backticks) {
|
||||
$t = EducateBackticks($t);
|
||||
}
|
||||
|
||||
if ($t == "'") {
|
||||
# Special case: single-character ' token
|
||||
if (preg_match('/\S/', $prev_token_last_char)) {
|
||||
$t = "’";
|
||||
}
|
||||
else {
|
||||
$t = "‘";
|
||||
}
|
||||
}
|
||||
else if ($t == '"') {
|
||||
# Special case: single-character " token
|
||||
if (preg_match('/\S/', $prev_token_last_char)) {
|
||||
$t = "”";
|
||||
}
|
||||
else {
|
||||
$t = "“";
|
||||
}
|
||||
}
|
||||
else {
|
||||
# Normal case:
|
||||
$t = EducateQuotes($t);
|
||||
}
|
||||
|
||||
}
|
||||
$prev_token_last_char = $last_char;
|
||||
$result .= $t;
|
||||
}
|
||||
}
|
||||
|
||||
if ($add_extra_space) {
|
||||
preg_replace('/ \z/', '', $result); # Trim trailing space if we added one earlier.
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
|
||||
function SmartDashes($text, $attr = NULL, $ctx = NULL) {
|
||||
global $smartypants_attr, $sp_tags_to_skip;
|
||||
# Paramaters:
|
||||
$text; # text to be parsed
|
||||
$attr; # value of the smart_dashes="" attribute
|
||||
$ctx; # MT context object (unused)
|
||||
if ($attr == NULL) $attr = $smartypants_attr;
|
||||
|
||||
# reference to the subroutine to use for dash education, default to EducateDashes:
|
||||
$dash_sub_ref = 'EducateDashes';
|
||||
|
||||
if ($attr == 0) {
|
||||
# do nothing;
|
||||
return $text;
|
||||
}
|
||||
else if ($attr == 2) {
|
||||
# use old smart dash shortcuts, "--" for en, "---" for em
|
||||
$dash_sub_ref = 'EducateDashesOldSchool';
|
||||
}
|
||||
else if ($attr == 3) {
|
||||
# inverse of 2, "--" for em, "---" for en
|
||||
$dash_sub_ref = 'EducateDashesOldSchoolInverted';
|
||||
}
|
||||
|
||||
$tokens;
|
||||
$tokens = _TokenizeHTML($text);
|
||||
|
||||
$result = '';
|
||||
$in_pre = 0; # Keep track of when we're inside <pre> or <code> tags
|
||||
foreach ($tokens as $cur_token) {
|
||||
if ($cur_token[0] == "tag") {
|
||||
# Don't mess with quotes inside tags
|
||||
$result .= $cur_token[1];
|
||||
if (preg_match("@$sp_tags_to_skip@", $cur_token[1], $matches)) {
|
||||
$in_pre = isset($matches[1]) && $matches[1] == '/' ? 0 : 1;
|
||||
}
|
||||
} else {
|
||||
$t = $cur_token[1];
|
||||
if (! $in_pre) {
|
||||
$t = ProcessEscapes($t);
|
||||
$t = $dash_sub_ref($t);
|
||||
}
|
||||
$result .= $t;
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
|
||||
function SmartEllipses($text, $attr = NULL, $ctx = NULL) {
|
||||
# Paramaters:
|
||||
$text; # text to be parsed
|
||||
$attr; # value of the smart_ellipses="" attribute
|
||||
$ctx; # MT context object (unused)
|
||||
if ($attr == NULL) $attr = $smartypants_attr;
|
||||
|
||||
if ($attr == 0) {
|
||||
# do nothing;
|
||||
return $text;
|
||||
}
|
||||
|
||||
$tokens;
|
||||
$tokens = _TokenizeHTML($text);
|
||||
|
||||
$result = '';
|
||||
$in_pre = 0; # Keep track of when we're inside <pre> or <code> tags
|
||||
foreach ($tokens as $cur_token) {
|
||||
if ($cur_token[0] == "tag") {
|
||||
# Don't mess with quotes inside tags
|
||||
$result .= $cur_token[1];
|
||||
if (preg_match("@$sp_tags_to_skip@", $cur_token[1], $matches)) {
|
||||
$in_pre = isset($matches[1]) && $matches[1] == '/' ? 0 : 1;
|
||||
}
|
||||
} else {
|
||||
$t = $cur_token[1];
|
||||
if (! $in_pre) {
|
||||
$t = ProcessEscapes($t);
|
||||
$t = EducateEllipses($t);
|
||||
}
|
||||
$result .= $t;
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
|
||||
function EducateQuotes($_) {
|
||||
#
|
||||
# Parameter: String.
|
||||
#
|
||||
# Returns: The string, with "educated" curly quote HTML entities.
|
||||
#
|
||||
# Example input: "Isn't this fun?"
|
||||
# Example output: “Isn’t this fun?”
|
||||
#
|
||||
# Make our own "punctuation" character class, because the POSIX-style
|
||||
# [:PUNCT:] is only available in Perl 5.6 or later:
|
||||
$punct_class = "[!\"#\\$\\%'()*+,-.\\/:;<=>?\\@\\[\\\\\]\\^_`{|}~]";
|
||||
|
||||
# Special case if the very first character is a quote
|
||||
# followed by punctuation at a non-word-break. Close the quotes by brute force:
|
||||
$_ = preg_replace(
|
||||
array("/^'(?=$punct_class\\B)/", "/^\"(?=$punct_class\\B)/"),
|
||||
array('’', '”'), $_);
|
||||
|
||||
|
||||
# Special case for double sets of quotes, e.g.:
|
||||
# <p>He said, "'Quoted' words in a larger quote."</p>
|
||||
$_ = preg_replace(
|
||||
array("/\"'(?=\w)/", "/'\"(?=\w)/"),
|
||||
array('“‘', '‘“'), $_);
|
||||
|
||||
# Special case for decade abbreviations (the '80s):
|
||||
$_ = preg_replace("/'(?=\\d{2}s)/", '’', $_);
|
||||
|
||||
$close_class = '[^\ \t\r\n\[\{\(\-]';
|
||||
$dec_dashes = '&\#8211;|&\#8212;';
|
||||
|
||||
# Get most opening single quotes:
|
||||
$_ = preg_replace("{
|
||||
(
|
||||
\\s | # a whitespace char, or
|
||||
| # a non-breaking space entity, or
|
||||
-- | # dashes, or
|
||||
&[mn]dash; | # named dash entities
|
||||
$dec_dashes | # or decimal entities
|
||||
&\\#x201[34]; # or hex
|
||||
)
|
||||
' # the quote
|
||||
(?=\\w) # followed by a word character
|
||||
}x", '\1‘', $_);
|
||||
# Single closing quotes:
|
||||
$_ = preg_replace("{
|
||||
($close_class)?
|
||||
'
|
||||
(?(1)| # If $1 captured, then do nothing;
|
||||
(?=\\s | s\\b) # otherwise, positive lookahead for a whitespace
|
||||
) # char or an 's' at a word ending position. This
|
||||
# is a special case to handle something like:
|
||||
# \"<i>Custer</i>'s Last Stand.\"
|
||||
}xi", '\1’', $_);
|
||||
|
||||
# Any remaining single quotes should be opening ones:
|
||||
$_ = str_replace("'", '‘', $_);
|
||||
|
||||
|
||||
# Get most opening double quotes:
|
||||
$_ = preg_replace("{
|
||||
(
|
||||
\\s | # a whitespace char, or
|
||||
| # a non-breaking space entity, or
|
||||
-- | # dashes, or
|
||||
&[mn]dash; | # named dash entities
|
||||
$dec_dashes | # or decimal entities
|
||||
&\\#x201[34]; # or hex
|
||||
)
|
||||
\" # the quote
|
||||
(?=\\w) # followed by a word character
|
||||
}x", '\1“', $_);
|
||||
|
||||
# Double closing quotes:
|
||||
$_ = preg_replace("{
|
||||
($close_class)?
|
||||
\"
|
||||
(?(1)|(?=\\s)) # If $1 captured, then do nothing;
|
||||
# if not, then make sure the next char is whitespace.
|
||||
}x", '\1”', $_);
|
||||
|
||||
# Any remaining quotes should be opening ones.
|
||||
$_ = str_replace('"', '“', $_);
|
||||
|
||||
return $_;
|
||||
}
|
||||
|
||||
|
||||
function EducateBackticks($_) {
|
||||
#
|
||||
# Parameter: String.
|
||||
# Returns: The string, with ``backticks'' -style double quotes
|
||||
# translated into HTML curly quote entities.
|
||||
#
|
||||
# Example input: ``Isn't this fun?''
|
||||
# Example output: “Isn't this fun?”
|
||||
#
|
||||
|
||||
$_ = str_replace(array("``", "''",),
|
||||
array('“', '”'), $_);
|
||||
return $_;
|
||||
}
|
||||
|
||||
|
||||
function EducateSingleBackticks($_) {
|
||||
#
|
||||
# Parameter: String.
|
||||
# Returns: The string, with `backticks' -style single quotes
|
||||
# translated into HTML curly quote entities.
|
||||
#
|
||||
# Example input: `Isn't this fun?'
|
||||
# Example output: ‘Isn’t this fun?’
|
||||
#
|
||||
|
||||
$_ = str_replace(array("`", "'",),
|
||||
array('‘', '’'), $_);
|
||||
return $_;
|
||||
}
|
||||
|
||||
|
||||
function EducateDashes($_) {
|
||||
#
|
||||
# Parameter: String.
|
||||
#
|
||||
# Returns: The string, with each instance of "--" translated to
|
||||
# an em-dash HTML entity.
|
||||
#
|
||||
|
||||
$_ = str_replace('--', '—', $_);
|
||||
return $_;
|
||||
}
|
||||
|
||||
|
||||
function EducateDashesOldSchool($_) {
|
||||
#
|
||||
# Parameter: String.
|
||||
#
|
||||
# Returns: The string, with each instance of "--" translated to
|
||||
# an en-dash HTML entity, and each "---" translated to
|
||||
# an em-dash HTML entity.
|
||||
#
|
||||
|
||||
# em en
|
||||
$_ = str_replace(array("---", "--",),
|
||||
array('—', '–'), $_);
|
||||
return $_;
|
||||
}
|
||||
|
||||
|
||||
function EducateDashesOldSchoolInverted($_) {
|
||||
#
|
||||
# Parameter: String.
|
||||
#
|
||||
# Returns: The string, with each instance of "--" translated to
|
||||
# an em-dash HTML entity, and each "---" translated to
|
||||
# an en-dash HTML entity. Two reasons why: First, unlike the
|
||||
# en- and em-dash syntax supported by
|
||||
# EducateDashesOldSchool(), it's compatible with existing
|
||||
# entries written before SmartyPants 1.1, back when "--" was
|
||||
# only used for em-dashes. Second, em-dashes are more
|
||||
# common than en-dashes, and so it sort of makes sense that
|
||||
# the shortcut should be shorter to type. (Thanks to Aaron
|
||||
# Swartz for the idea.)
|
||||
#
|
||||
|
||||
# en em
|
||||
$_ = str_replace(array("---", "--",),
|
||||
array('–', '—'), $_);
|
||||
return $_;
|
||||
}
|
||||
|
||||
|
||||
function EducateEllipses($_) {
|
||||
#
|
||||
# Parameter: String.
|
||||
# Returns: The string, with each instance of "..." translated to
|
||||
# an ellipsis HTML entity. Also converts the case where
|
||||
# there are spaces between the dots.
|
||||
#
|
||||
# Example input: Huh...?
|
||||
# Example output: Huh…?
|
||||
#
|
||||
|
||||
$_ = str_replace(array("...", ". . .",), '…', $_);
|
||||
return $_;
|
||||
}
|
||||
|
||||
|
||||
function StupefyEntities($_) {
|
||||
#
|
||||
# Parameter: String.
|
||||
# Returns: The string, with each SmartyPants HTML entity translated to
|
||||
# its ASCII counterpart.
|
||||
#
|
||||
# Example input: “Hello — world.”
|
||||
# Example output: "Hello -- world."
|
||||
#
|
||||
|
||||
# en-dash em-dash
|
||||
$_ = str_replace(array('–', '—'),
|
||||
array('-', '--'), $_);
|
||||
|
||||
# single quote open close
|
||||
$_ = str_replace(array('‘', '’'), "'", $_);
|
||||
|
||||
# double quote open close
|
||||
$_ = str_replace(array('“', '”'), '"', $_);
|
||||
|
||||
$_ = str_replace('…', '...', $_); # ellipsis
|
||||
|
||||
return $_;
|
||||
}
|
||||
|
||||
|
||||
function ProcessEscapes($_) {
|
||||
#
|
||||
# Parameter: String.
|
||||
# Returns: The string, with after processing the following backslash
|
||||
# escape sequences. This is useful if you want to force a "dumb"
|
||||
# quote or other character to appear.
|
||||
#
|
||||
# Escape Value
|
||||
# ------ -----
|
||||
# \\ \
|
||||
# \" "
|
||||
# \' '
|
||||
# \. .
|
||||
# \- -
|
||||
# \` `
|
||||
#
|
||||
$_ = str_replace(
|
||||
array('\\\\', '\"', "\'", '\.', '\-', '\`'),
|
||||
array('\', '"', ''', '.', '-', '`'), $_);
|
||||
|
||||
return $_;
|
||||
}
|
||||
|
||||
|
||||
# _TokenizeHTML is shared between PHP SmartyPants and PHP Markdown.
|
||||
# We only define it if it is not already defined.
|
||||
if (!function_exists('_TokenizeHTML')) :
|
||||
function _TokenizeHTML($str) {
|
||||
#
|
||||
# Parameter: String containing HTML markup.
|
||||
# Returns: An array of the tokens comprising the input
|
||||
# string. Each token is either a tag (possibly with nested,
|
||||
# tags contained therein, such as <a href="<MTFoo>">, or a
|
||||
# run of text between tags. Each element of the array is a
|
||||
# two-element array; the first is either 'tag' or 'text';
|
||||
# the second is the actual value.
|
||||
#
|
||||
#
|
||||
# Regular expression derived from the _tokenize() subroutine in
|
||||
# Brad Choate's MTRegex plugin.
|
||||
# <http://www.bradchoate.com/past/mtregex.php>
|
||||
#
|
||||
$index = 0;
|
||||
$tokens = array();
|
||||
|
||||
$match = '(?s:<!(?:--.*?--\s*)+>)|'. # comment
|
||||
'(?s:<\?.*?\?>)|'. # processing instruction
|
||||
# regular tags
|
||||
'(?:<[/!$]?[-a-zA-Z0-9:]+\b(?>[^"\'>]+|"[^"]*"|\'[^\']*\')*>)';
|
||||
|
||||
$parts = preg_split("{($match)}", $str, -1, PREG_SPLIT_DELIM_CAPTURE);
|
||||
|
||||
foreach ($parts as $part) {
|
||||
if (++$index % 2 && $part != '')
|
||||
$tokens[] = array('text', $part);
|
||||
else
|
||||
$tokens[] = array('tag', $part);
|
||||
}
|
||||
return $tokens;
|
||||
}
|
||||
endif;
|
||||
|
||||
|
||||
/*
|
||||
|
||||
PHP SmartyPants
|
||||
===============
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
This is a PHP translation of the original SmartyPants quote educator written in
|
||||
Perl by John Gruber.
|
||||
|
||||
SmartyPants is a web publishing utility that translates plain ASCII
|
||||
punctuation characters into "smart" typographic punctuation HTML
|
||||
entities. SmartyPants can perform the following transformations:
|
||||
|
||||
* Straight quotes (`"` and `'`) into "curly" quote HTML entities
|
||||
* Backticks-style quotes (` ``like this'' `) into "curly" quote HTML
|
||||
entities
|
||||
* Dashes (`--` and `---`) into en- and em-dash entities
|
||||
* Three consecutive dots (`...`) into an ellipsis entity
|
||||
|
||||
SmartyPants does not modify characters within `<pre>`, `<code>`, `<kbd>`,
|
||||
`<script>`, or `<math>` tag blocks. Typically, these tags are used to
|
||||
display text where smart quotes and other "smart punctuation" would not
|
||||
be appropriate, such as source code or example markup.
|
||||
|
||||
|
||||
### Backslash Escapes ###
|
||||
|
||||
If you need to use literal straight quotes (or plain hyphens and
|
||||
periods), SmartyPants accepts the following backslash escape sequences
|
||||
to force non-smart punctuation. It does so by transforming the escape
|
||||
sequence into a decimal-encoded HTML entity:
|
||||
|
||||
Escape Value Character
|
||||
------ ----- ---------
|
||||
\\ \ \
|
||||
\" " "
|
||||
\' ' '
|
||||
\. . .
|
||||
\- - -
|
||||
\` ` `
|
||||
|
||||
This is useful, for example, when you want to use straight quotes as
|
||||
foot and inch marks: 6'2" tall; a 17" iMac.
|
||||
|
||||
|
||||
Bugs
|
||||
----
|
||||
|
||||
To file bug reports or feature requests (other than topics listed in the
|
||||
Caveats section above) please send email to:
|
||||
|
||||
<michel.fortin@michelf.com>
|
||||
|
||||
If the bug involves quotes being curled the wrong way, please send example
|
||||
text to illustrate.
|
||||
|
||||
|
||||
### Algorithmic Shortcomings ###
|
||||
|
||||
One situation in which quotes will get curled the wrong way is when
|
||||
apostrophes are used at the start of leading contractions. For example:
|
||||
|
||||
'Twas the night before Christmas.
|
||||
|
||||
In the case above, SmartyPants will turn the apostrophe into an opening
|
||||
single-quote, when in fact it should be a closing one. I don't think
|
||||
this problem can be solved in the general case -- every word processor
|
||||
I've tried gets this wrong as well. In such cases, it's best to use the
|
||||
proper HTML entity for closing single-quotes (`’`) by hand.
|
||||
|
||||
|
||||
Version History
|
||||
---------------
|
||||
|
||||
1.5.1e (9 Dec 2005)
|
||||
|
||||
* Corrected a bug that prevented special characters from being
|
||||
escaped.
|
||||
|
||||
|
||||
1.5.1d (25 May 2005)
|
||||
|
||||
* Corrected a small bug in `_TokenizeHTML` where a Doctype declaration
|
||||
was not seen as HTML (smart quotes where applied inside).
|
||||
|
||||
|
||||
1.5.1c (13 Dec 2004)
|
||||
|
||||
* Changed a regular expression in `_TokenizeHTML` that could lead to
|
||||
a segmentation fault with PHP 4.3.8 on Linux.
|
||||
|
||||
|
||||
1.5.1b (6 Sep 2004)
|
||||
|
||||
* Corrected a problem with quotes immediately following a dash
|
||||
with no space between: `Text--"quoted text"--text.`
|
||||
|
||||
* PHP SmartyPants can now be used as a modifier by the Smarty
|
||||
template engine. Rename the file to "modifier.smartypants.php"
|
||||
and put it in your smarty plugins folder.
|
||||
|
||||
* Replaced a lot of space characters by tabs, saving about 4 KB.
|
||||
|
||||
|
||||
1.5.1a (30 Jun 2004)
|
||||
|
||||
* PHP Markdown and PHP Smartypants now share the same `_TokenizeHTML`
|
||||
function when loaded simultanously.
|
||||
|
||||
* Changed the internals of `_TokenizeHTML` to lower the PHP version
|
||||
requirement to PHP 4.0.5.
|
||||
|
||||
|
||||
1.5.1 (6 Jun 2004)
|
||||
|
||||
* Initial release of PHP SmartyPants, based on version 1.5.1 of the
|
||||
original SmartyPants written in Perl.
|
||||
|
||||
|
||||
Author
|
||||
------
|
||||
|
||||
John Gruber
|
||||
<http://daringfireball.net/>
|
||||
|
||||
Ported to PHP by Michel Fortin
|
||||
<http://www.michelf.com/>
|
||||
|
||||
|
||||
Additional Credits
|
||||
------------------
|
||||
|
||||
Portions of this plug-in are based on Brad Choate's nifty MTRegex plug-in.
|
||||
Brad Choate also contributed a few bits of source code to this plug-in.
|
||||
Brad Choate is a fine hacker indeed. (<http://bradchoate.com/>)
|
||||
|
||||
Jeremy Hedley (<http://antipixel.com/>) and Charles Wiltgen
|
||||
(<http://playbacktime.com/>) deserve mention for exemplary beta testing.
|
||||
|
||||
|
||||
Copyright and License
|
||||
---------------------
|
||||
|
||||
Copyright (c) 2003 John Gruber
|
||||
<http://daringfireball.net/>
|
||||
All rights reserved.
|
||||
|
||||
Copyright (c) 2004-2005 Michel Fortin
|
||||
<http://www.michelf.com>
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name "SmartyPants" nor the names of its contributors may
|
||||
be used to endorse or promote products derived from this software
|
||||
without specific prior written permission.
|
||||
|
||||
This software is provided by the copyright holders and contributors "as is"
|
||||
and any express or implied warranties, including, but not limited to, the
|
||||
implied warranties of merchantability and fitness for a particular purpose
|
||||
are disclaimed. In no event shall the copyright owner or contributors be
|
||||
liable for any direct, indirect, incidental, special, exemplary, or
|
||||
consequential damages (including, but not limited to, procurement of
|
||||
substitute goods or services; loss of use, data, or profits; or business
|
||||
interruption) however caused and on any theory of liability, whether in
|
||||
contract, strict liability, or tort (including negligence or otherwise)
|
||||
arising in any way out of the use of this software, even if advised of the
|
||||
possibility of such damage.
|
||||
|
||||
*/
|
||||
?>
|
326
_site/tmp/var/www/chaospott.de/engine/Post.php
Normal file
@ -0,0 +1,326 @@
|
||||
<?php
|
||||
|
||||
$fdir = dirname(__FILE__);
|
||||
require_once($fdir . '/PHPMarkdownExtra/markdown.php');
|
||||
require_once($fdir . '/PHPSmartyPants/smartypants.php');
|
||||
require_once($fdir . '/Updater.php');
|
||||
require_once($fdir . '/Template.php');
|
||||
require_once($fdir . '/Utils.php');
|
||||
|
||||
class Post
|
||||
{
|
||||
public static $blog_title = 'Untitled Blog';
|
||||
public static $blog_url = 'http://no-idea.com/';
|
||||
public static $blog_description = 'About my blog.';
|
||||
|
||||
public $source_filename = '';
|
||||
public $title = '';
|
||||
public $is_draft = false;
|
||||
public $publish_now = false;
|
||||
public $timestamp = 0;
|
||||
public $slug = '';
|
||||
public $type = '';
|
||||
public $headers = array();
|
||||
public $tags = array();
|
||||
public $body = '';
|
||||
|
||||
public $year;
|
||||
public $month;
|
||||
public $day;
|
||||
public $offset_in_day;
|
||||
|
||||
public $is_first_post_on_this_date = false; // Used for index-page rendering
|
||||
|
||||
public static function from_files(array $filenames)
|
||||
{
|
||||
$posts = array();
|
||||
$last_date = false;
|
||||
foreach ($filenames as $f) {
|
||||
if (! file_exists($f)) continue;
|
||||
$post = new Post($f);
|
||||
$date = $post->year . $post->month . $post->day;
|
||||
if ($date != $last_date) {
|
||||
$post->is_first_post_on_this_date = true;
|
||||
$last_date = $date;
|
||||
}
|
||||
$posts[] = $post;
|
||||
}
|
||||
return $posts;
|
||||
}
|
||||
|
||||
public static function next_sequence_number_for_day($filename_prefix)
|
||||
{
|
||||
$max = 0;
|
||||
foreach (glob($filename_prefix . '*' . Updater::$post_extension) as $filename) {
|
||||
$n = intval(substr(substring_after($filename, $filename_prefix), 0, 2));
|
||||
if ($n > $max) $max = $n;
|
||||
}
|
||||
return str_pad($max + 1, 2, '0', STR_PAD_LEFT);
|
||||
}
|
||||
|
||||
public function __construct($source_filename, $is_draft = -1 /* auto */)
|
||||
{
|
||||
$this->source_filename = $source_filename;
|
||||
$this->is_draft = ($is_draft === -1 ? (false !== strpos($source_filename, 'drafts/') || false !== strpos($source_filename, 'pages/')) : $is_draft);
|
||||
$this->timestamp = filemtime($source_filename);
|
||||
|
||||
$segments = preg_split( '/\R\R/', trim(file_get_contents($source_filename)), 2);
|
||||
if (! isset($segments[1])) $segments[1] = '';
|
||||
|
||||
if (count($segments) > 1) {
|
||||
// Read headers for Tag, Type values
|
||||
$headers = explode("\n", $segments[0]);
|
||||
$has_title_yet = false;
|
||||
foreach ($headers as $header) {
|
||||
if (isset($header[0]) && $header[0] == '=') {
|
||||
$has_title_yet = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (! $has_title_yet) {
|
||||
$has_title_yet = true;
|
||||
$this->title = $header;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($this->is_draft && strtolower($header) == 'publish-now') {
|
||||
$this->publish_now = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
$fields = explode(':', $header, 2);
|
||||
if (count($fields) < 2) continue;
|
||||
$fname = strtolower($fields[0]);
|
||||
$fields[1] = trim($fields[1]);
|
||||
if ($fname == 'tags') {
|
||||
$this->tags = self::parse_tag_str($fields[1]);
|
||||
} else if ($fname == 'type') {
|
||||
$this->type = str_replace('|', ' ', $fields[1]);
|
||||
} else if ($fname == 'published') {
|
||||
$this->timestamp = strtotime($fields[1]);
|
||||
} else {
|
||||
$this->headers[$fname] = $fields[1];
|
||||
}
|
||||
|
||||
if (isset($this->headers['link'])) $this->type = 'link';
|
||||
}
|
||||
array_shift($segments);
|
||||
}
|
||||
|
||||
$this->body = isset($segments[0]) ? $segments[0] : '';
|
||||
|
||||
$filename = basename($source_filename);
|
||||
$filename_datestr = substr($filename, 0, 8);
|
||||
if (is_numeric($filename_datestr)) {
|
||||
$this->year = intval(substr($filename_datestr, 0, 4));
|
||||
$this->month = intval(substr($filename_datestr, 4, 2));
|
||||
$this->day = intval(substr($filename_datestr, 6, 2));
|
||||
$this->offset_in_day = intval(substr($filename, 9, 2));
|
||||
$this->slug = ltrim(substr($filename, 11, -(strlen(Updater::$post_extension))), '-');
|
||||
} else {
|
||||
$this->year = intval(idate('Y', $this->timestamp));
|
||||
$this->month = intval(idate('m', $this->timestamp));
|
||||
$this->day = intval(idate('d', $this->timestamp));
|
||||
$posts_in_ym = Updater::posts_in_year_month($this->year, $this->month);
|
||||
$this->offset_in_day = isset($posts_in_ym[$this->day]) ? count($posts_in_ym[$this->day]) + 1 : 1;
|
||||
$this->slug = substring_before($filename, '.');
|
||||
}
|
||||
|
||||
if (! $this->is_draft && $source_filename != $this->expected_source_filename()) {
|
||||
error_log("Expected filename mismatch:\nfilename: $source_filename\nexpected: " . $this->expected_source_filename());
|
||||
}
|
||||
}
|
||||
|
||||
public function expected_source_filename($for_new_file = false)
|
||||
{
|
||||
$padded_month = str_pad($this->month, 2, '0', STR_PAD_LEFT);
|
||||
$padded_day = str_pad($this->day, 2, '0', STR_PAD_LEFT);
|
||||
|
||||
$prefix =
|
||||
Updater::$source_path . '/posts/' .
|
||||
$this->year . '/' . $padded_month . '/' .
|
||||
$this->year . $padded_month . $padded_day . '-'
|
||||
;
|
||||
|
||||
if ($for_new_file) $padded_offset = self::next_sequence_number_for_day($prefix);
|
||||
else $padded_offset = str_pad($this->offset_in_day, 2, '0', STR_PAD_LEFT);
|
||||
|
||||
if ($for_new_file) {
|
||||
$slug = trim(preg_replace('/[^a-z0-9-]+/ms', '-', strtolower($this->slug)), '-');
|
||||
if (! $slug) $slug = 'post';
|
||||
} else {
|
||||
$slug = $this->slug;
|
||||
}
|
||||
|
||||
return $prefix . $padded_offset . '-' . strtolower($slug) . Updater::$post_extension;
|
||||
}
|
||||
|
||||
public function normalized_source()
|
||||
{
|
||||
$out_headers = $this->headers;
|
||||
|
||||
if ($this->is_draft) unset($out_headers['published']);
|
||||
else $out_headers['published'] = date('Y-m-d h:i:sa', $this->timestamp);
|
||||
$source = $this->title . "\n" . str_repeat('=', max(10, min(40, strlen($this->title)))) . "\n";
|
||||
if ($this->type) $out_headers['type'] = $this->type;
|
||||
if ($this->tags) $out_headers['tags'] = implode(', ', $this->tags);
|
||||
foreach ($out_headers as $k => $v) $source .= ucfirst($k) . ': ' . $v . "\n";
|
||||
$source .= "\n" . $this->body;
|
||||
return $source;
|
||||
}
|
||||
|
||||
public function array_for_template()
|
||||
{
|
||||
$tags = array();
|
||||
foreach ($this->tags as $tag) { $tags[$tag] = array('post-tag' => $tag); }
|
||||
|
||||
// Convert relative image references to absolute so index pages work
|
||||
$base_uri = '/' . $this->year . '/' . str_pad($this->month, 2, '0', STR_PAD_LEFT) . '/' . str_pad($this->day, 2, '0', STR_PAD_LEFT);
|
||||
|
||||
return array_merge(
|
||||
$this->headers,
|
||||
array(
|
||||
'post-title' => html_entity_decode(SmartyPants($this->title), ENT_QUOTES, 'UTF-8'),
|
||||
'post-slug' => $this->slug,
|
||||
'post-timestamp' => $this->timestamp,
|
||||
'post-rss-date' => date('D, d M Y H:i:s T', $this->timestamp),
|
||||
'post-body' => SmartyPants(Markdown($this->body)),
|
||||
'post-tags' => $tags,
|
||||
'post-type' => $this->type,
|
||||
'post-permalink' => $base_uri . '/' . $this->slug,
|
||||
'post-permalink-or-link' => isset($this->headers['link']) && $this->headers['link'] ? $this->headers['link'] : $base_uri . '/' . $this->slug,
|
||||
'post-absolute-permalink' => rtrim(self::$blog_url, '/') . $base_uri . '/' . $this->slug,
|
||||
'post-absolute-permalink-or-link' => rtrim(self::$blog_url, '/') . (isset($this->headers['link']) && $this->headers['link'] ? $this->headers['link'] : $base_uri . '/' . $this->slug),
|
||||
|
||||
'post-is-first-on-date' => $this->is_first_post_on_this_date ? 'yes' : '',
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public function write_page()
|
||||
{
|
||||
$t = new Template(Updater::$page_template);
|
||||
$t->content = array(
|
||||
'page-title' => html_entity_decode(SmartyPants($this->title), ENT_QUOTES, 'UTF-8'),
|
||||
'page-body' => SmartyPants(Markdown($this->body)),
|
||||
'blog-title' => html_entity_decode(SmartyPants(self::$blog_title), ENT_QUOTES, 'UTF-8'),
|
||||
'blog-url' => self::$blog_url,
|
||||
'blog-description' => html_entity_decode(SmartyPants(self::$blog_description), ENT_QUOTES, 'UTF-8'),
|
||||
'page-type' => 'page',
|
||||
'archives' => array(),
|
||||
'previous_page_url' => false,
|
||||
'next_page_url' => false,
|
||||
);
|
||||
$output_html = $t->outputHTML();
|
||||
|
||||
if (! file_exists(Updater::$dest_path)) mkdir_as_parent_owner(Updater::$dest_path, 0755, true);
|
||||
file_put_contents_as_dir_owner(Updater::$dest_path . '/' . $this->slug, $output_html);
|
||||
}
|
||||
|
||||
public function write_permalink_page($draft = false)
|
||||
{
|
||||
$post_data = $this->array_for_template();
|
||||
$post_data['post-is-first-on-date'] = true;
|
||||
|
||||
$t = new Template(Updater::$permalink_template);
|
||||
$t->content = array_merge(
|
||||
$post_data,
|
||||
array(
|
||||
'page-title' => html_entity_decode(SmartyPants(self::$blog_title), ENT_QUOTES, 'UTF-8'),
|
||||
'blog-title' => html_entity_decode(SmartyPants(self::$blog_title), ENT_QUOTES, 'UTF-8'),
|
||||
'blog-url' => self::$blog_url,
|
||||
'blog-description' => html_entity_decode(SmartyPants(self::$blog_description), ENT_QUOTES, 'UTF-8'),
|
||||
'page-type' => 'post',
|
||||
'posts' => array($post_data),
|
||||
'post' => $post_data,
|
||||
'archives' => array(),
|
||||
'previous_page_url' => false,
|
||||
'next_page_url' => false,
|
||||
)
|
||||
);
|
||||
$output_html = $t->outputHTML();
|
||||
|
||||
if (! $draft || Updater::$write_public_drafts) {
|
||||
$output_www_path = $draft ? '/drafts' : dirname($post_data['post-permalink']);
|
||||
$output_path = Updater::$dest_path . $output_www_path;
|
||||
if (! file_exists($output_path)) mkdir_as_parent_owner($output_path, 0755, true);
|
||||
file_put_contents_as_dir_owner(Updater::$dest_path . ($draft ? '/drafts/' . $this->slug : $post_data['post-permalink']), $output_html);
|
||||
}
|
||||
|
||||
if ($draft) file_put_contents_as_dir_owner(Updater::$source_path . '/drafts/_previews/' . $this->slug . '.html', $output_html);
|
||||
}
|
||||
|
||||
public static function write_index($dest_path, $title, $type, array $posts, $template = 'main.html', $archive_array = false, $seq_count = 0)
|
||||
{
|
||||
$posts_data = array();
|
||||
foreach ($posts as $p) $posts_data[] = $p->array_for_template();
|
||||
|
||||
$t = new Template($template);
|
||||
$t->content = array(
|
||||
'page-title' => html_entity_decode(SmartyPants($title), ENT_QUOTES, 'UTF-8'),
|
||||
'blog-title' => html_entity_decode(SmartyPants(self::$blog_title), ENT_QUOTES, 'UTF-8'),
|
||||
'blog-url' => self::$blog_url,
|
||||
'blog-description' => html_entity_decode(SmartyPants(self::$blog_description), ENT_QUOTES, 'UTF-8'),
|
||||
'page-type' => $type,
|
||||
'posts' => $posts_data,
|
||||
'previous_page_url' => false,
|
||||
'next_page_url' => $seq_count > 1 ? substring_after(substring_before($dest_path, '.', true), Updater::$dest_path) . '-2' : false,
|
||||
'archives' => $archive_array ? $archive_array : array(),
|
||||
);
|
||||
$output_html = $t->outputHTML();
|
||||
|
||||
$output_path = dirname($dest_path);
|
||||
if (! file_exists($output_path)) mkdir_as_parent_owner($output_path, 0755, true);
|
||||
file_put_contents_as_dir_owner($dest_path, $output_html);
|
||||
}
|
||||
|
||||
public function write_index_sequence($dest_path, $title, $type, array $posts, $template = 'main.html', $archive_array = false, $posts_per_page = 20)
|
||||
{
|
||||
$sequence = 0;
|
||||
$new_dest_path = $dest_path;
|
||||
// $dest_uri = substring_after(substring_before($dest_path, '.', true), Updater::$dest_path);
|
||||
$dest_uri = substring_after(substring_after($dest_path, '/', true), Updater::$dest_path);
|
||||
$total_sequences = ceil(count($posts) / $posts_per_page);
|
||||
while ($posts) {
|
||||
$sequence++;
|
||||
$seq_array = array_splice($posts, 0, $posts_per_page);
|
||||
if ($sequence == 1) continue; // skip writing the redundant "-1" page
|
||||
|
||||
$new_dest_path = $dest_path . '-' . $sequence . '.html';
|
||||
|
||||
$posts_data = array();
|
||||
foreach ($seq_array as $p) $posts_data[] = $p->array_for_template();
|
||||
|
||||
$t = new Template($template);
|
||||
$t->content = array(
|
||||
'page-title' => html_entity_decode(SmartyPants($title), ENT_QUOTES, 'UTF-8'),
|
||||
'blog-title' => html_entity_decode(SmartyPants(self::$blog_title), ENT_QUOTES, 'UTF-8'),
|
||||
'blog-url' => self::$blog_url,
|
||||
'blog-description' => html_entity_decode(SmartyPants(self::$blog_description), ENT_QUOTES, 'UTF-8'),
|
||||
'page-type' => $type,
|
||||
'posts' => $posts_data,
|
||||
'previous_page_url' => $sequence != 1 ? ($sequence == 2 ? $dest_uri : $dest_uri . '-' . ($sequence - 1)) : false,
|
||||
'next_page_url' => $sequence < $total_sequences ? $dest_uri . '-' . ($sequence + 1) : false,
|
||||
'archives' => $archive_array ? $archive_array : array(),
|
||||
);
|
||||
$output_html = $t->outputHTML();
|
||||
|
||||
$output_path = dirname($new_dest_path);
|
||||
if (! file_exists($output_path)) mkdir_as_parent_owner($output_path, 0755, true);
|
||||
file_put_contents_as_dir_owner($new_dest_path, $output_html);
|
||||
}
|
||||
|
||||
return $total_sequences;
|
||||
}
|
||||
|
||||
public static function parse_tag_str($tag_str)
|
||||
{
|
||||
// Tags are comma-separated, and any spaces between multiple words in a tag will be converted to underscores for URLs
|
||||
if (! strlen($tag_str)) return array();
|
||||
$tags = array_map('trim', explode(',', strtolower($tag_str)));
|
||||
$tags = str_replace(' ', '_', $tags);
|
||||
$tags = array_unique(array_filter($tags, 'strlen'));
|
||||
sort($tags);
|
||||
return $tags;
|
||||
}
|
||||
}
|
26
_site/tmp/var/www/chaospott.de/engine/Template.php
Normal file
@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
class Template
|
||||
{
|
||||
public static $template_dir = '.';
|
||||
|
||||
public $content = array();
|
||||
|
||||
private $template_filename = false;
|
||||
private $dom;
|
||||
|
||||
public function __construct($template_name)
|
||||
{
|
||||
$this->template_filename = self::$template_dir . '/' . $template_name;
|
||||
}
|
||||
|
||||
public function outputHTML()
|
||||
{
|
||||
ob_start();
|
||||
$content = $this->content;
|
||||
include($this->template_filename);
|
||||
$contents = ob_get_contents();
|
||||
ob_end_clean();
|
||||
return $contents;
|
||||
}
|
||||
}
|
726
_site/tmp/var/www/chaospott.de/engine/Updater.php
Normal file
@ -0,0 +1,726 @@
|
||||
<?php
|
||||
|
||||
require_once(dirname(__FILE__) . '/Post.php');
|
||||
require_once(dirname(__FILE__) . '/Hook.php');
|
||||
|
||||
class Updater
|
||||
{
|
||||
public static $source_path;
|
||||
public static $dest_path;
|
||||
public static $cache_path;
|
||||
public static $post_extension = '.md';
|
||||
|
||||
// This option writes each draft preview into (web root)/drafts/whatever-slug
|
||||
// Without it, drafts only reside in the (source)/drafts/_previews folder
|
||||
// and are not accessible publicly.
|
||||
public static $write_public_drafts = false;
|
||||
|
||||
public static $frontpage_post_limit = 4;
|
||||
public static $frontpage_template = 'front.php';
|
||||
public static $frontpage_tag_filter = '!rss-only';
|
||||
public static $frontpage_type_filter = false;
|
||||
public static $frontpage_paginate = false;
|
||||
|
||||
public static $logbuch_post_limit = 20;
|
||||
public static $logbuch_template = 'main.php';
|
||||
public static $logbuch_tag_filter = '!rss-only';
|
||||
public static $logbuch_type_filter = false;
|
||||
public static $logbuch_paginate = false;
|
||||
|
||||
public static $rss_post_limit = 20;
|
||||
public static $rss_template = 'rss.php';
|
||||
public static $rss_tag_filter = '!site-only';
|
||||
public static $rss_type_filter = false;
|
||||
|
||||
public static $archive_month_template = 'main.php';
|
||||
public static $archive_year_template = 'main.php';
|
||||
public static $archive_tag_filter = '!rss-only';
|
||||
public static $archive_type_filter = '!ad';
|
||||
|
||||
public static $tag_page_post_limit = 20;
|
||||
|
||||
public static $permalink_template = 'main.php';
|
||||
public static $tag_page_template = 'main.php';
|
||||
public static $type_page_template = 'main.php';
|
||||
public static $page_template = 'main.php';
|
||||
|
||||
public static $api_blog_id = 1;
|
||||
public static $api_blog_username = '';
|
||||
public static $api_blog_password = '';
|
||||
|
||||
public static $changes_were_written = false;
|
||||
|
||||
private static $index_to_be_updated = false;
|
||||
private static $index_months_to_be_updated = array();
|
||||
private static $tags_to_be_updated = array();
|
||||
private static $types_to_be_updated = array();
|
||||
private static $posts_to_be_updated = array();
|
||||
private static $pages_to_be_updated = array();
|
||||
|
||||
public static function posts_in_year_month($year, $month, $require_tag = false, $require_type = false)
|
||||
{
|
||||
$month = str_pad($month, 2, '0', STR_PAD_LEFT);
|
||||
$cache_fname = self::$cache_path . "/posts-$year-$month-" . md5($require_tag . ' ' . $require_type);
|
||||
if (file_exists($cache_fname)) {
|
||||
$files = unserialize(file_get_contents($cache_fname));
|
||||
} else {
|
||||
$all_files = self::filelist(self::$source_path . "/posts/$year/$month", true);
|
||||
ksort($all_files);
|
||||
$in_month = false;
|
||||
$files = array();
|
||||
foreach ($all_files as $fname => $info) {
|
||||
if (substr($fname, -(strlen(self::$post_extension))) != self::$post_extension) continue;
|
||||
|
||||
// Tag/type filtering
|
||||
list($ignored_hash, $type, $tags) = explode('|', $info, 3);
|
||||
$include = true;
|
||||
if ($require_type) {
|
||||
if ($require_type[0] == '!' && $type == $require_type) $include = false;
|
||||
else if ($require_type[0] != '!' && $type != $require_type) $include = false;
|
||||
}
|
||||
if ($require_tag) {
|
||||
if ($require_tag[0] == '!' && in_array($require_tag, Post::parse_tag_str($tags))) $include = false;
|
||||
else if ($require_tag[0] != '!' && ! in_array($require_tag, Post::parse_tag_str($tags))) $include = false;
|
||||
}
|
||||
if (! $include) continue;
|
||||
|
||||
list($y, $m, $d) = array_map('intval', array_slice(explode('/', $fname), -3, 3));
|
||||
$d = intval(substr($d, 6));
|
||||
if ($year == $y && $month == $m) {
|
||||
if (isset($files[$d])) $files[$d][] = $fname;
|
||||
else $files[$d] = array($fname);
|
||||
} else {
|
||||
if ($in_month) break;
|
||||
}
|
||||
}
|
||||
|
||||
if (! file_exists(self::$cache_path)) mkdir_as_parent_owner(self::$cache_path, 0755, true);
|
||||
file_put_contents_as_dir_owner($cache_fname, serialize($files));
|
||||
}
|
||||
return $files;
|
||||
}
|
||||
|
||||
public static function post_filenames_in_year_month($year, $month, $require_tag = false, $require_type = false)
|
||||
{
|
||||
$out = array();
|
||||
foreach (self::posts_in_year_month($year, $month, $require_tag, $require_type) as $day) {
|
||||
foreach ($day as $filename) $out[] = $filename;
|
||||
}
|
||||
return $out;
|
||||
}
|
||||
|
||||
private static function resequence_post_offsets($year, $month, $day)
|
||||
{
|
||||
$month = str_pad($month, 2, '0', STR_PAD_LEFT);
|
||||
$day = str_pad($day, 2, '0', STR_PAD_LEFT);
|
||||
|
||||
error_log("Resequencing post offsets for $year-$month-$day");
|
||||
|
||||
$prefix = self::$source_path . "/posts/$year/$month/$year$month$day-";
|
||||
$files = glob($prefix . '*' . self::$post_extension);
|
||||
if (count($files) > 99) {
|
||||
error_log("Cannot resequence offsets for $year-$month-$day: too many posts (" . count($files) . ')');
|
||||
return false;
|
||||
}
|
||||
|
||||
$timestamps = array();
|
||||
foreach ($files as $filename) {
|
||||
$p = new Post($filename, false);
|
||||
if (in_array($p->timestamp, $timestamps)) {
|
||||
// Multiple posts with identical timestamp. No good way to handle this. Abort resequencing.
|
||||
error_log("Cannot resequence offsets for $year-$month-$day: multiple identical timestamps");
|
||||
return false;
|
||||
}
|
||||
$timestamps[$filename] = $p->timestamp;
|
||||
}
|
||||
asort($timestamps);
|
||||
|
||||
$offset = 0;
|
||||
$slugs = array();
|
||||
$out_files = array();
|
||||
$made_changes = false;
|
||||
foreach ($timestamps as $filename => $timestamp) {
|
||||
$offset++;
|
||||
|
||||
$slug = preg_replace('/-p[0-9]{1,2}$/ms', '', substr(basename($filename), 12, -(strlen(self::$post_extension))));
|
||||
if (! isset($slugs[$slug])) $slugs[$slug] = 1;
|
||||
else $slugs[$slug]++;
|
||||
|
||||
$correct_filename =
|
||||
$prefix .
|
||||
str_pad($offset, 2, '0', STR_PAD_LEFT) . '-' .
|
||||
$slug . ($slugs[$slug] < 2 ? '' : '-p' .$slugs[$slug]) .
|
||||
self::$post_extension
|
||||
;
|
||||
if ($filename != $correct_filename) {
|
||||
error_log("file [$filename] should be [$correct_filename]");
|
||||
if (isset($out_files[$correct_filename])) throw new Exception("Target-filename collision for [$correct_filename], this should never happen");
|
||||
$made_changes = true;
|
||||
$out_files[$correct_filename] = array(file_get_contents($filename), $timestamp);
|
||||
safe_unlink($filename);
|
||||
}
|
||||
|
||||
foreach ($out_files as $correct_filename => $a) {
|
||||
list($contents, $timestamp) = $a;
|
||||
file_put_contents_as_dir_owner($correct_filename, $contents);
|
||||
@touch($correct_filename, $timestamp);
|
||||
}
|
||||
}
|
||||
|
||||
return $made_changes;
|
||||
}
|
||||
|
||||
private static function filelist($dir, $parse_info_in_text_files = true, $use_cached_info = array(), &$deleted_files = array())
|
||||
{
|
||||
$out = array();
|
||||
if (is_dir($dir)) {
|
||||
if ($dh = opendir($dir)) {
|
||||
while ( ($file = readdir($dh) ) !== false) {
|
||||
if ($file[0] == '.') continue;
|
||||
$fullpath = $dir . '/' . $file;
|
||||
if (is_dir($fullpath)) {
|
||||
$out = array_merge($out, self::filelist($fullpath, $parse_info_in_text_files, $use_cached_info, $deleted_files));
|
||||
} else {
|
||||
if (isset($deleted_files[$fullpath])) unset($deleted_files[$fullpath]);
|
||||
|
||||
$new_stat_prefix = filemtime($fullpath) . '-' . filesize($fullpath);
|
||||
|
||||
if (isset($use_cached_info[$fullpath])) {
|
||||
$stat_prefix = substr($use_cached_info[$fullpath], 0, strpos($use_cached_info[$fullpath], '|'));
|
||||
if ($stat_prefix == $new_stat_prefix) continue;
|
||||
}
|
||||
|
||||
$tags = '';
|
||||
$type = '';
|
||||
if ($parse_info_in_text_files && substr($fullpath, -(strlen(self::$post_extension))) == self::$post_extension) {
|
||||
$post = new Post($fullpath);
|
||||
$tags = implode(',', $post->tags);
|
||||
$type = $post->type;
|
||||
$expected_fname = $post->expected_source_filename();
|
||||
if ($expected_fname != $fullpath && ! $post->is_draft) {
|
||||
rename($fullpath, $expected_fname);
|
||||
$fullpath = $expected_fname;
|
||||
}
|
||||
}
|
||||
|
||||
$out[$fullpath] = $new_stat_prefix . '|' . $type . '|' . $tags;
|
||||
}
|
||||
}
|
||||
closedir($dh);
|
||||
}
|
||||
}
|
||||
return $out;
|
||||
}
|
||||
|
||||
public static function changed_files_in_directory($directory)
|
||||
{
|
||||
$cache_fname = self::$cache_path . '/dir-' . md5($directory);
|
||||
$changed = array();
|
||||
if (file_exists($cache_fname)) {
|
||||
$fileinfo = unserialize(file_get_contents($cache_fname));
|
||||
$doesnt_exist_anymore = $fileinfo;
|
||||
foreach (self::filelist($directory, true, $fileinfo, $doesnt_exist_anymore) as $filename => $new_info) {
|
||||
if (! isset($fileinfo[$filename]) || $fileinfo[$filename] != $new_info) {
|
||||
$changed[$filename] = array(isset($fileinfo[$filename]) ? $fileinfo[$filename] : '||', $new_info);
|
||||
$fileinfo[$filename] = $new_info;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($doesnt_exist_anymore as $deleted_fname => $old_info) {
|
||||
error_log("Deleted file: $deleted_fname");
|
||||
$changed[$deleted_fname] = array($old_info, '||');
|
||||
unset($fileinfo[$deleted_fname]);
|
||||
}
|
||||
} else {
|
||||
$fileinfo = self::filelist($directory, true);
|
||||
foreach ($fileinfo as $filename => $new_info) $changed[$filename] = array('||', $new_info);
|
||||
if (! file_exists(self::$cache_path)) mkdir_as_parent_owner(self::$cache_path, 0755, true);
|
||||
}
|
||||
if ($changed) file_put_contents_as_dir_owner($cache_fname, serialize($fileinfo));
|
||||
return $changed;
|
||||
}
|
||||
|
||||
public static function most_recent_post_filenames($limit = 0, $require_tag = false, $require_type = false)
|
||||
{
|
||||
$cache_fname = self::$cache_path . '/dir-' . md5(self::$source_path . '/posts');
|
||||
if (! file_exists($cache_fname)) throw new Exception('Cache files not found, expected in [' . self::$cache_path . ']');
|
||||
$fileinfo = unserialize(file_get_contents($cache_fname));
|
||||
krsort($fileinfo); // reverse-chrono
|
||||
|
||||
$posts = array();
|
||||
foreach ($fileinfo as $filename => $info) {
|
||||
if (substr($filename, -(strlen(self::$post_extension))) != self::$post_extension) continue;
|
||||
list($ignored_hash, $type, $tags) = explode('|', $info, 3);
|
||||
$include = true;
|
||||
if ($require_type) {
|
||||
if ($require_type[0] == '!' && $type == substr($require_type, 1)) $include = false;
|
||||
else if ($require_type[0] != '!' && $type != $require_type) $include = false;
|
||||
}
|
||||
|
||||
if ($require_tag) {
|
||||
if ($require_tag[0] == '!' && in_array(substr($require_tag, 1), Post::parse_tag_str($tags))) $include = false;
|
||||
else if ($require_tag[0] != '!' && ! in_array($require_tag, Post::parse_tag_str($tags))) $include = false;
|
||||
}
|
||||
|
||||
if (! $include) continue;
|
||||
$posts[] = $filename;
|
||||
if ($limit && count($posts) >= $limit) break;
|
||||
}
|
||||
return $posts;
|
||||
}
|
||||
|
||||
public static function set_has_posts_for_month($year, $month, $scope = '')
|
||||
{
|
||||
$cache_fname = self::$cache_path . "/months-with-posts-$scope";
|
||||
if (file_exists($cache_fname)) {
|
||||
$existing = unserialize(file_get_contents($cache_fname));
|
||||
} else {
|
||||
$existing = array();
|
||||
}
|
||||
|
||||
$changed = false;
|
||||
if (! isset($existing[$year])) {
|
||||
$existing[$year] = array($month => true);
|
||||
$changed = true;
|
||||
} else if (! isset($existing[$year][$month])) {
|
||||
$existing[$year][$month] = true;
|
||||
$changed = true;
|
||||
}
|
||||
|
||||
if ($changed) {
|
||||
if (! file_exists(self::$cache_path)) mkdir_as_parent_owner(self::$cache_path, 0755, true);
|
||||
file_put_contents_as_dir_owner($cache_fname, serialize($existing));
|
||||
}
|
||||
}
|
||||
|
||||
public static function months_with_posts($scope = '')
|
||||
{
|
||||
$cache_fname = self::$cache_path . "/months-with-posts-$scope";
|
||||
if (! file_exists($cache_fname)) throw new Exception('Cache files not found, expected ' . $cache_fname);
|
||||
return unserialize(file_get_contents($cache_fname));
|
||||
}
|
||||
|
||||
public static function archive_array($scope = '')
|
||||
{
|
||||
$out = array();
|
||||
foreach (self::months_with_posts($scope) as $year => $months) {
|
||||
foreach ($months as $month => $x) {
|
||||
$ts = mktime(0, 0, 0, $month, 15, $year);
|
||||
$out[$year . str_pad($month, 2, '0', STR_PAD_LEFT)] = array(
|
||||
'archives-uri' => "/$year/" . str_pad($month, 2, '0', STR_PAD_LEFT) . '/' . $scope,
|
||||
'archives-year' => $year,
|
||||
'archives-month-number' => intval($month),
|
||||
'archives-month-name' => date('F', $ts),
|
||||
'archives-month-short-name' => date('M', $ts),
|
||||
);
|
||||
}
|
||||
}
|
||||
arsort($out);
|
||||
return $out;
|
||||
}
|
||||
|
||||
public static function update_drafts()
|
||||
{
|
||||
foreach (self::changed_files_in_directory(self::$source_path . '/drafts') as $filename => $info) {
|
||||
self::$changes_were_written = true;
|
||||
|
||||
if (contains($filename, '/_publish-now/') && file_exists($filename)) {
|
||||
$post = new Post($filename, false);
|
||||
$expected_fname = $post->expected_source_filename(true);
|
||||
error_log("Publishing draft $filename");
|
||||
$dir = dirname($expected_fname);
|
||||
if (! file_exists($dir)) mkdir_as_parent_owner($dir, 0755, true);
|
||||
if (file_put_contents_as_dir_owner($expected_fname, $post->normalized_source())) safe_unlink($filename);
|
||||
self::post_hooks($post);
|
||||
}
|
||||
|
||||
if (! file_exists($filename)) {
|
||||
if (ends_with($filename, self::$post_extension)) {
|
||||
error_log("Deleted draft $filename");
|
||||
$slug = substring_before(basename($filename), '.', true);
|
||||
$html_preview_filename = self::$source_path . '/drafts/_previews/' . $slug . '.html';
|
||||
$webroot_preview_filename = self::$dest_path . '/drafts/' . $slug;
|
||||
if (file_exists($html_preview_filename)) safe_unlink($html_preview_filename);
|
||||
if (file_exists($webroot_preview_filename)) safe_unlink($webroot_preview_filename);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (substr($filename, -(strlen(self::$post_extension))) == self::$post_extension) {
|
||||
$post = new Post($filename, true);
|
||||
if ($post->publish_now) {
|
||||
$post = new Post($filename, false);
|
||||
$expected_fname = $post->expected_source_filename(true);
|
||||
error_log("Publishing draft $filename");
|
||||
$dir = dirname($expected_fname);
|
||||
if (! file_exists($dir)) mkdir_as_parent_owner($dir, 0755, true);
|
||||
if (file_put_contents_as_dir_owner($expected_fname, $post->normalized_source())) safe_unlink($filename);
|
||||
self::post_hooks($post);
|
||||
} else {
|
||||
$post->write_permalink_page(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static function update_styles()
|
||||
{
|
||||
foreach (self::changed_files_in_directory(self::$source_path . '/templates') as $filename => $info) {
|
||||
$file_info = pathinfo($filename);
|
||||
if ($file_info['extension'] != 'css') continue;
|
||||
|
||||
error_log("Changed style file: $filename");
|
||||
$uri = substring_after($filename, self::$source_path . '/templates');
|
||||
$dest_filename = self::$dest_path . $uri;
|
||||
$output_path = dirname($dest_filename);
|
||||
if (! file_exists($output_path)) mkdir_as_parent_owner($output_path, 0755, true);
|
||||
copy($filename, $dest_filename);
|
||||
self::$changes_were_written = true;
|
||||
}
|
||||
}
|
||||
|
||||
public static function post_hooks($post)
|
||||
{
|
||||
$dir = self::$source_path . '/hooks';
|
||||
if (is_dir($dir)) {
|
||||
if ( ($dh = opendir($dir)) ) {
|
||||
while ( ($file = readdir($dh) ) !== false) {
|
||||
if ($file[0] == '.') continue;
|
||||
if (substr($file, 0, 5) == 'post_') {
|
||||
$fullpath = $dir . '/' . $file;
|
||||
require_once($fullpath);
|
||||
$className = ucfirst(substr($file, 5, strlen($file) - 9));
|
||||
$hook = new $className;
|
||||
$hook->doHook($post);
|
||||
}
|
||||
}
|
||||
closedir($dh);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static function update_pages()
|
||||
{
|
||||
foreach (self::changed_files_in_directory(self::$source_path . '/pages') as $filename => $info) {
|
||||
self::$changes_were_written = true;
|
||||
|
||||
if (! file_exists($filename)) {
|
||||
if (ends_with($filename, self::$post_extension)) {
|
||||
error_log("Deleted page $filename");
|
||||
$dest_filename = self::$dest_path . '/' . substring_before(basename($filename), '.', true);
|
||||
if (file_exists($dest_filename)) safe_unlink($dest_filename);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (substr($filename, -(strlen(self::$post_extension))) == self::$post_extension) {
|
||||
$post = new Post($filename, true);
|
||||
$post->slug = basename($filename, self::$post_extension);
|
||||
error_log("Writing page [{$post->slug}]");
|
||||
$post->write_page(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const RESEQUENCED_POSTS = -1;
|
||||
public static function update()
|
||||
{
|
||||
if (! file_exists(self::$cache_path)) {
|
||||
// Starting fresh, probably multiple resequences to do.
|
||||
// Let them all happen at once.
|
||||
$status = self::_update(false);
|
||||
}
|
||||
|
||||
do {
|
||||
$status = self::_update();
|
||||
} while ($status == self::RESEQUENCED_POSTS);
|
||||
|
||||
return $status;
|
||||
}
|
||||
|
||||
private static function _update($restart_if_resequenced = true)
|
||||
{
|
||||
if (! file_exists(self::$dest_path)) mkdir_as_parent_owner(self::$dest_path, 0755, true);
|
||||
if (! file_exists(self::$dest_path . '/.htaccess')) copy(dirname(__FILE__) . '/default.htaccess', self::$dest_path . '/.htaccess');
|
||||
|
||||
if (! file_exists(self::$source_path . '/drafts/_publish-now')) mkdir_as_parent_owner(self::$source_path . '/drafts/_publish-now', 0755, true);
|
||||
if (! file_exists(self::$source_path . '/drafts/_previews')) mkdir_as_parent_owner(self::$source_path . '/drafts/_previews', 0755, true);
|
||||
if (! file_exists(self::$source_path . '/pages')) mkdir_as_parent_owner(self::$source_path . '/pages', 0755, true);
|
||||
|
||||
self::update_pages();
|
||||
self::update_drafts();
|
||||
self::update_styles();
|
||||
|
||||
foreach (self::changed_files_in_directory(self::$source_path . '/media') as $filename => $info) {
|
||||
error_log("Changed media file: $filename");
|
||||
$uri = substring_after($filename, self::$source_path);
|
||||
$dest_filename = self::$dest_path . $uri;
|
||||
$output_path = dirname($dest_filename);
|
||||
if (! file_exists($output_path)) mkdir_as_parent_owner($output_path, 0755, true);
|
||||
copy($filename, $dest_filename);
|
||||
self::$changes_were_written = true;
|
||||
}
|
||||
|
||||
$resequence_days = array();
|
||||
|
||||
foreach (self::changed_files_in_directory(self::$source_path . '/posts') as $filename => $info) {
|
||||
self::$posts_to_be_updated[$filename] = true;
|
||||
|
||||
if (substr($filename, -(strlen(self::$post_extension))) == self::$post_extension) {
|
||||
list($old_info, $new_info) = $info;
|
||||
list($old_hash, $old_type, $old_tags) = explode('|', $old_info, 3);
|
||||
list($new_hash, $new_type, $new_tags) = explode('|', $new_info, 3);
|
||||
|
||||
if ($old_type) self::$types_to_be_updated[$old_type] = true;
|
||||
if ($new_type) self::$types_to_be_updated[$new_type] = true;
|
||||
|
||||
foreach (Post::parse_tag_str($old_tags) as $tag) self::$tags_to_be_updated[$tag] = true;
|
||||
foreach (Post::parse_tag_str($new_tags) as $tag) self::$tags_to_be_updated[$tag] = true;
|
||||
}
|
||||
|
||||
$yearpos = strpos($filename, '/posts/') + 7;
|
||||
$year = substr($filename, $yearpos, 4);
|
||||
$month = substr($filename, $yearpos + 5, 2);
|
||||
$day = substr($filename, $yearpos + 14, 2);
|
||||
$resequence_days[$year . $month . $day] = array($year, $month, $day);
|
||||
self::$index_months_to_be_updated[$year . '-' . $month] = true;
|
||||
self::$index_to_be_updated = true;
|
||||
}
|
||||
|
||||
foreach (self::$posts_to_be_updated as $filename => $x) {
|
||||
if (! file_exists($filename)) {
|
||||
// This file was deleted. Delete corresponding output file
|
||||
$filename_datestr = substr(basename($filename), 0, 8);
|
||||
if (is_numeric($filename_datestr)) {
|
||||
$year = substr($filename_datestr, 0, 4);
|
||||
$month = substr($filename_datestr, 4, 2);
|
||||
$day = substr($filename_datestr, 6, 2);
|
||||
$slug = substr(basename($filename), 12, -(strlen(self::$post_extension)));
|
||||
$target_filename = self::$dest_path . "/$year/$month/$day/$slug";
|
||||
if ($year && $month && $day && $slug && file_exists($target_filename)) {
|
||||
error_log("Deleting abandoned target file: $target_filename");
|
||||
safe_unlink($target_filename);
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
error_log("Updating post: $filename");
|
||||
self::$changes_were_written = true;
|
||||
|
||||
if (substr($filename, -(strlen(self::$post_extension))) == self::$post_extension) {
|
||||
$post = new Post($filename);
|
||||
$post->write_permalink_page();
|
||||
|
||||
self::set_has_posts_for_month($post->year, $post->month);
|
||||
foreach ($post->tags as $tag) self::set_has_posts_for_month($post->year, $post->month, 'tagged-' . $tag);
|
||||
if ($post->type) self::set_has_posts_for_month($post->year, $post->month, 'type-' . $post->type);
|
||||
|
||||
} else {
|
||||
$filename_datestr = substr(basename($filename), 0, 8);
|
||||
if (is_numeric($filename_datestr)) {
|
||||
$year = intval(substr($filename_datestr, 0, 4));
|
||||
$month = intval(substr($filename_datestr, 4, 2));
|
||||
$day = intval(substr($filename_datestr, 6, 2));
|
||||
if ($year && $month && $day) {
|
||||
$output_path = self::$dest_path . "/$year/" . str_pad($month, 2, '0', STR_PAD_LEFT) . '/' . str_pad($day, 2, '0', STR_PAD_LEFT);
|
||||
if (! file_exists($output_path)) mkdir_as_parent_owner($output_path, 0755, true);
|
||||
$output_filename = $output_path . '/' . basename($filename);
|
||||
copy($filename, $output_filename);
|
||||
$resequence_days[$year . $month . $day] = array($year, $month, $day);
|
||||
} else {
|
||||
error_log("Can't figure out where to put unrecognized numeric filename [$filename]");
|
||||
}
|
||||
} else {
|
||||
error_log("Can't figure out where to put unrecognized filename [$filename]");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
$sequence_changed = false;
|
||||
foreach ($resequence_days as $a) {
|
||||
list($year, $month, $day) = array_map('intval', $a);
|
||||
if (self::resequence_post_offsets($year, $month, $day)) $sequence_changed = true;
|
||||
}
|
||||
|
||||
if ($sequence_changed && $restart_if_resequenced) {
|
||||
self::$changes_were_written = true;
|
||||
error_log("Resequencing was performed. Restarting update...");
|
||||
return self::RESEQUENCED_POSTS;
|
||||
}
|
||||
|
||||
if (! self::$index_to_be_updated) self::$index_to_be_updated = self::$index_months_to_be_updated || self::$tags_to_be_updated || self::$types_to_be_updated || self::$posts_to_be_updated;
|
||||
|
||||
if (self::$index_to_be_updated) {
|
||||
error_log("Updating frontpage...");
|
||||
self::$changes_were_written = true;
|
||||
|
||||
$seq_count = 0;
|
||||
if (self::$frontpage_paginate) {
|
||||
$seq_count = Post::write_index_sequence(
|
||||
self::$dest_path . "/index",
|
||||
Post::$blog_title,
|
||||
'frontpage',
|
||||
Post::from_files(self::most_recent_post_filenames(0, self::$frontpage_tag_filter, self::$frontpage_type_filter)),
|
||||
self::$frontpage_template,
|
||||
self::archive_array(),
|
||||
self::$frontpage_post_limit
|
||||
);
|
||||
}
|
||||
|
||||
Post::write_index(
|
||||
self::$dest_path . "/index.html",
|
||||
Post::$blog_title,
|
||||
'frontpage',
|
||||
Post::from_files(self::most_recent_post_filenames(self::$frontpage_post_limit, self::$frontpage_tag_filter, self::$frontpage_type_filter)),
|
||||
self::$frontpage_template,
|
||||
self::archive_array(),
|
||||
$seq_count
|
||||
);
|
||||
|
||||
$seq_count = 0;
|
||||
if (self::$logbuch_paginate) {
|
||||
$seq_count = Post::write_index_sequence(
|
||||
self::$dest_path . "/logbuch",
|
||||
Post::$blog_title,
|
||||
'frontpage',
|
||||
Post::from_files(self::most_recent_post_filenames(0, self::$logbuch_tag_filter, self::$flogbuch_type_filter)),
|
||||
self::$logbuch_template,
|
||||
self::archive_array(),
|
||||
self::$logbuch_post_limit
|
||||
);
|
||||
}
|
||||
|
||||
Post::write_index(
|
||||
self::$dest_path . "/logbuch.html",
|
||||
Post::$blog_title,
|
||||
'frontpage',
|
||||
Post::from_files(self::most_recent_post_filenames(self::$logbuch_post_limit, self::$logbuch_tag_filter, self::$logbuch_type_filter)),
|
||||
self::$logbuch_template,
|
||||
self::archive_array(),
|
||||
$seq_count
|
||||
);
|
||||
|
||||
error_log("Updating RSS...");
|
||||
Post::write_index(
|
||||
self::$dest_path . "/rss.xml",
|
||||
Post::$blog_title,
|
||||
'rss',
|
||||
Post::from_files(self::most_recent_post_filenames(self::$rss_post_limit, self::$rss_tag_filter, self::$rss_type_filter)),
|
||||
self::$rss_template
|
||||
);
|
||||
}
|
||||
|
||||
foreach (self::$index_months_to_be_updated as $ym => $x) {
|
||||
error_log("Updating month index: $ym");
|
||||
self::$changes_were_written = true;
|
||||
|
||||
list($year, $month) = explode('-', $ym);
|
||||
$posts = Post::from_files(self::post_filenames_in_year_month($year, $month, self::$archive_tag_filter, self::$archive_type_filter));
|
||||
$ts = mktime(0, 0, 0, $month, 15, $year);
|
||||
Post::write_index(
|
||||
self::$dest_path . "/$year/$month/index.html",
|
||||
date('F Y', $ts),
|
||||
'archive',
|
||||
$posts,
|
||||
self::$archive_month_template,
|
||||
self::archive_array()
|
||||
);
|
||||
}
|
||||
|
||||
foreach (self::$tags_to_be_updated as $tag => $x) {
|
||||
if (! strlen($tag)) continue;
|
||||
error_log("Updating tag: $tag");
|
||||
self::$changes_were_written = true;
|
||||
|
||||
$seq_count = Post::write_index_sequence(
|
||||
self::$dest_path . "/tagged-$tag",
|
||||
Post::$blog_title,
|
||||
'tag',
|
||||
Post::from_files(self::most_recent_post_filenames(0, $tag, self::$archive_tag_filter)),
|
||||
self::$tag_page_template,
|
||||
self::archive_array(),
|
||||
self::$tag_page_post_limit
|
||||
);
|
||||
|
||||
Post::write_index(
|
||||
self::$dest_path . "/tagged-$tag.html",
|
||||
Post::$blog_title,
|
||||
'tag',
|
||||
Post::from_files(self::most_recent_post_filenames(self::$frontpage_post_limit, $tag, self::$archive_tag_filter)),
|
||||
self::$tag_page_template,
|
||||
self::archive_array('tagged-' . $tag),
|
||||
$seq_count
|
||||
);
|
||||
|
||||
Post::write_index(
|
||||
self::$dest_path . "/tagged-$tag.xml",
|
||||
Post::$blog_title,
|
||||
'tag',
|
||||
Post::from_files(self::most_recent_post_filenames(self::$rss_post_limit, $tag, self::$archive_tag_filter)),
|
||||
self::$rss_template,
|
||||
self::archive_array('tagged-' . $tag)
|
||||
);
|
||||
|
||||
$months_with_posts = self::months_with_posts('tagged-' . $tag);
|
||||
foreach (self::$index_months_to_be_updated as $ym => $x) {
|
||||
list($year, $month) = explode('-', $ym);
|
||||
if (! isset($months_with_posts[$year]) || ! isset($months_with_posts[$year][intval($month)])) continue;
|
||||
error_log("Updating month index: $ym for tag: $tag");
|
||||
$posts = Post::from_files(self::post_filenames_in_year_month($year, $month, $tag, self::$archive_type_filter));
|
||||
$ts = mktime(0, 0, 0, $month, 15, $year);
|
||||
Post::write_index(
|
||||
self::$dest_path . "/$year/$month/tagged-$tag.html",
|
||||
date('F Y', $ts),
|
||||
'tag',
|
||||
$posts,
|
||||
self::$tag_page_template,
|
||||
self::archive_array('tagged-' . $tag)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (self::$types_to_be_updated as $type => $x) {
|
||||
if (! strlen($type)) continue;
|
||||
error_log("Updating type: $type");
|
||||
self::$changes_were_written = true;
|
||||
|
||||
Post::write_index(
|
||||
self::$dest_path . "/type-$type.html",
|
||||
Post::$blog_title,
|
||||
'type',
|
||||
Post::from_files(self::most_recent_post_filenames(self::$frontpage_post_limit, self::$archive_type_filter, $type)),
|
||||
self::$type_page_template,
|
||||
self::archive_array('type-' . $type)
|
||||
);
|
||||
|
||||
Post::write_index(
|
||||
self::$dest_path . "/type-$type.xml",
|
||||
Post::$blog_title,
|
||||
'type',
|
||||
Post::from_files(self::most_recent_post_filenames(self::$rss_post_limit, self::$archive_type_filter, $type)),
|
||||
self::$rss_template,
|
||||
self::archive_array('type-' . $type)
|
||||
);
|
||||
|
||||
$months_with_posts = self::months_with_posts('type-' . $type);
|
||||
foreach (self::$index_months_to_be_updated as $ym => $x) {
|
||||
list($year, $month) = explode('-', $ym);
|
||||
if (! isset($months_with_posts[$year]) || ! isset($months_with_posts[$year][intval($month)])) continue;
|
||||
error_log("Updating month index: $ym for type: $type");
|
||||
$posts = Post::from_files(self::post_filenames_in_year_month($year, $month, self::$archive_tag_filter, $type));
|
||||
$ts = mktime(0, 0, 0, $month, 15, $year);
|
||||
Post::write_index(
|
||||
self::$dest_path . "/$year/$month/type-$type.html",
|
||||
date('F Y', $ts),
|
||||
'type',
|
||||
$posts,
|
||||
self::$type_page_template,
|
||||
self::archive_array('type-' . $type)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
201
_site/tmp/var/www/chaospott.de/engine/Utils.php
Normal file
@ -0,0 +1,201 @@
|
||||
<?php
|
||||
|
||||
function _match_owners_if_possible($target_filename, $match_from_filename)
|
||||
{
|
||||
try {
|
||||
if (false === ($intended_uid = fileowner($match_from_filename)) ) throw new Exception("fileowner failed on source");
|
||||
if (false === ($intended_gid = filegroup($match_from_filename)) ) throw new Exception("filegroup failed on source");
|
||||
|
||||
if (false === ($uid = fileowner($target_filename)) ) throw new Exception("fileowner failed on target");
|
||||
if (false === ($gid = filegroup($target_filename)) ) throw new Exception("filegroup failed on target");
|
||||
|
||||
if ($intended_uid != $uid && ! chown($target_filename, $intended_uid)) throw new Exception("chown failed on target");
|
||||
if ($intended_gid != $gid && ! chgrp($target_filename, $intended_gid)) throw new Exception("chgrp failed on target");
|
||||
} catch (Exception $e) {
|
||||
error_log("Cannot assign ownership of [$target_filename] to owner of [$match_from_filename]: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
function file_put_contents_as_dir_owner($filename, $data)
|
||||
{
|
||||
if (false !== ($ret = file_put_contents($filename, $data)) ) {
|
||||
_match_owners_if_possible($filename, dirname($filename));
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
|
||||
function mkdir_as_parent_owner($pathname, $mode = 0777, $recursive = false)
|
||||
{
|
||||
echo "$pathname\n";
|
||||
if (false !== ($ret = mkdir($pathname, $mode, $recursive)) ) {
|
||||
_match_owners_if_possible($pathname, dirname($pathname));
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
|
||||
function h($text)
|
||||
{
|
||||
$text =$text;// mb_convert_encoding($text, 'UTF-8', 'UTF-8');
|
||||
return htmlentities($text, ENT_QUOTES, 'UTF-8');
|
||||
}
|
||||
|
||||
function js_array($in_array)
|
||||
{
|
||||
if (! is_array($in_array)) return '[]';
|
||||
return '[' . implode(',', array_map('js_string', $in_array)) . ']';
|
||||
}
|
||||
|
||||
function js_string($input)
|
||||
{
|
||||
if (! strlen($input)) return "''";
|
||||
try {
|
||||
$wchars = mb_convert_encoding($input, 'UCS-2', 'UTF-8');
|
||||
$out = '';
|
||||
foreach (str_split($wchars, 2) as $wchar) {
|
||||
list($o1, $o2) = array(ord($wchar[0]), ord($wchar[1]));
|
||||
if ($o1 == 0 && (
|
||||
$o2 <= 0x1F ||
|
||||
$wchar[1] == '<' || $wchar[1] == '>' || $wchar[1] == '"' || $wchar[1] == '&'
|
||||
)) {
|
||||
$out .= '\x' . sprintf('%02x', $o2);
|
||||
} else if ($o1 == 0 && $wchar[1] == "\\") {
|
||||
$out .= "\\\\";
|
||||
} else if ($o1 == 0 && $wchar[1] == "'") {
|
||||
$out .= "\'";
|
||||
} else if ($o1 == 0 && $o2 <= 0x7F) {
|
||||
$out .= $wchar[1];
|
||||
} else {
|
||||
$out .= '\u' . sprintf('%02x%02x', $o1, $o2);
|
||||
}
|
||||
}
|
||||
return "'$out'";
|
||||
} catch (Exception $e) {
|
||||
error_log('js_string(): ' . $e->getMessage() . ' [input:[' . $input . ']]');
|
||||
|
||||
$str = str_replace(array('\\', "'"), array("\\\\", "\\'"), $input);
|
||||
$str = preg_replace('#([\x00-\x1F])#e', '"\x" . sprintf("%02x", ord("\1"))', $str);
|
||||
return "'{$str}'";
|
||||
}
|
||||
}
|
||||
|
||||
function starts_with($haystack, $needle)
|
||||
{
|
||||
return (substr($haystack, 0, strlen($needle)) == $needle);
|
||||
}
|
||||
|
||||
function ends_with($haystack, $needle)
|
||||
{
|
||||
return (substr($haystack, 0 - strlen($needle)) == $needle);
|
||||
}
|
||||
|
||||
function contains($haystack, $needle)
|
||||
{
|
||||
return (strpos($haystack, $needle) !== false);
|
||||
}
|
||||
|
||||
function contains_any($haystack, array $needles)
|
||||
{
|
||||
foreach ($needles as $needle) {
|
||||
if (strpos($haystack, $needle) !== false) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function contains_all($haystack, array $needles)
|
||||
{
|
||||
foreach ($needles as $needle) {
|
||||
if (strpos($haystack, $needle) === false) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function substring_before($haystack, $needle, $from_end = false)
|
||||
{
|
||||
if ($from_end) {
|
||||
if ( ($p = strrpos($haystack, $needle)) === false) return $haystack;
|
||||
} else {
|
||||
if ( ($p = strpos($haystack, $needle)) === false) return $haystack;
|
||||
}
|
||||
return substr($haystack, 0, $p);
|
||||
}
|
||||
|
||||
function substring_after($haystack, $needle, $from_end = false)
|
||||
{
|
||||
if ($from_end) {
|
||||
if ( ($p = strrpos($haystack, $needle)) === false) return $haystack;
|
||||
} else {
|
||||
if ( ($p = strpos($haystack, $needle)) === false) return $haystack;
|
||||
}
|
||||
return substr($haystack, $p + strlen($needle));
|
||||
}
|
||||
|
||||
function substring_between($haystack, $left_needle, $right_needle, $from_end = false)
|
||||
{
|
||||
return substring_before(
|
||||
substring_after($haystack, $left_needle), $right_needle, $from_end
|
||||
);
|
||||
}
|
||||
|
||||
function normalize_space($str, $trim_beginning_and_end = true)
|
||||
{
|
||||
if ($trim_beginning_and_end) $str = trim($str);
|
||||
$str = str_replace("\xC2\xA0", ' ', $str);
|
||||
return preg_replace('/\s\s+/ms', ' ', $str);
|
||||
}
|
||||
|
||||
function pluralize($number, $noun, $nouns = false)
|
||||
{
|
||||
if (! $nouns) $nouns = $noun . 's';
|
||||
return number_format($number) . ' ' . ($number == 1 ? $noun : $nouns);
|
||||
}
|
||||
|
||||
function array_to_verbal_list(array $a, $and_or_or = 'and')
|
||||
{
|
||||
$c = count($a);
|
||||
if (! $c) return '';
|
||||
if ($c == 1) return $a[0];
|
||||
if ($c == 2) return $a[0] . ' ' . $and_or_or . ' ' . $a[1];
|
||||
$last = array_pop($a);
|
||||
return implode(', ', $a) . ', ' . $and_or_or . ' ' . $last;
|
||||
}
|
||||
|
||||
function strip_tags_preserve_space($str, $allowable_tags = false)
|
||||
{
|
||||
$str = preg_replace('#([^ ])<(p|br|ul|ol|li)/?>#msi', '\\1 <\\2>', $str);
|
||||
return (
|
||||
$allowable_tags ? strip_tags($str, $allowable_tags) : strip_tags($str)
|
||||
);
|
||||
}
|
||||
|
||||
function summarize(
|
||||
$text, $length, $ellipsis = '...', $encoding = 'UTF-8',
|
||||
$strict_cut = false, $strict_sentences = false
|
||||
)
|
||||
{
|
||||
if ($length == 0) return $text;
|
||||
|
||||
if ($strict_sentences) {
|
||||
$split_char = '.';
|
||||
$ellipsis = '.' . $ellipsis;
|
||||
} else {
|
||||
$split_char = ' ';
|
||||
}
|
||||
|
||||
if (mb_strlen($text, $encoding) <= $length) return $text;
|
||||
$cut_str = mb_substr($text, 0, $length + 1, $encoding);
|
||||
$split_pos = (
|
||||
$cut_str ? mb_strrpos($cut_str, $split_char, $encoding) : false
|
||||
);
|
||||
|
||||
if ($split_pos) {
|
||||
return mb_substr($text, 0, $split_pos, $encoding) . $ellipsis;
|
||||
} else {
|
||||
return ($strict_cut ? $ellipsis : $cut_str . $ellipsis);
|
||||
}
|
||||
}
|
||||
|
||||
function safe_unlink($file)
|
||||
{
|
||||
try { @unlink($file); } catch (Exception $e) { }
|
||||
}
|
||||
|
16
_site/tmp/var/www/chaospott.de/engine/default.htaccess
Normal file
@ -0,0 +1,16 @@
|
||||
Options -MultiViews
|
||||
|
||||
AddOutputFilterByType DEFLATE text/html text/plain text/css text/javascript text/xml text/rss text/rss+xml
|
||||
|
||||
RewriteEngine On
|
||||
|
||||
RewriteRule ^([^\.]*)$ - [T=text/html]
|
||||
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteCond %{REQUEST_FILENAME}.html -f
|
||||
RewriteRule ^(.*)$ $1.html [QSA,L]
|
||||
|
||||
ErrorDocument 404 /404.html
|
||||
ErrorDocument 500 /500.html
|
||||
|
37
_site/tmp/var/www/chaospott.de/engine/update.php
Normal file
@ -0,0 +1,37 @@
|
||||
<?php
|
||||
define('LOCK_FILE', isset($_SERVER['argv'][1]) ? $_SERVER['argv'][1] : '/tmp/secondcrack-updater.pid');
|
||||
|
||||
// Ensure that no other instances are running
|
||||
if (file_exists(LOCK_FILE) &&
|
||||
($pid = intval(trim(file_get_contents(LOCK_FILE)))) &&
|
||||
posix_kill($pid, 0)
|
||||
) {
|
||||
fwrite(STDERR, "Already running [pid $pid]\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (file_put_contents(LOCK_FILE, posix_getpid())) {
|
||||
register_shutdown_function(
|
||||
function() {
|
||||
try { unlink(LOCK_FILE); } catch (Exception $e) {
|
||||
fwrite(STDERR, "Cannot remove lock file [" . LOCK_FILE . "]: " . $e->getMessage() . "\n");
|
||||
}
|
||||
}
|
||||
);
|
||||
} else {
|
||||
fwrite(STDERR, "Cannot write lock file: " . LOCK_FILE . "\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$fdir = dirname(__FILE__);
|
||||
require_once($fdir . '/Post.php');
|
||||
|
||||
$config_file = realpath(dirname(__FILE__) . '/..') . '/config.php';
|
||||
if (! file_exists($config_file)) {
|
||||
fwrite(STDERR, "Missing config file [$config_file]\nsee [$config_file.default] for an example\n");
|
||||
exit(1);
|
||||
}
|
||||
require_once($config_file);
|
||||
|
||||
Updater::update();
|
||||
exit(Updater::$changes_were_written ? 2 : 0);
|
47
_site/tmp/var/www/chaospott.de/engine/update.sh
Executable file
@ -0,0 +1,47 @@
|
||||
#!/bin/bash
|
||||
|
||||
if [ "$1" == "" -o "$2" == "" ] ; then
|
||||
echo ""
|
||||
echo "Usage: update.sh SOURCE_PATH SECONDCRACK_PATH"
|
||||
echo " where SOURCE_PATH contains /posts, /templates, ..."
|
||||
echo " and SECONDCRACK_PATH contains /cache, /engine, ..."
|
||||
echo ""
|
||||
exit 1
|
||||
fi
|
||||
|
||||
SOURCE_PATH="$1"
|
||||
SECONDCRACK_PATH="$2"
|
||||
FORCE_CHECK_EVERY_SECONDS=30
|
||||
UPDATE_LOG=/tmp/secondcrack-update.log
|
||||
|
||||
SCRIPT_LOCK_FILE="${SECONDCRACK_PATH}/engine/secondcrack-updater.pid"
|
||||
BASH_LOCK_DIR="${SECONDCRACK_PATH}/engine/secondcrack-updater.sh.lock"
|
||||
|
||||
if mkdir "$BASH_LOCK_DIR" ; then
|
||||
trap "rmdir '$BASH_LOCK_DIR' 2>/dev/null ; exit" INT TERM EXIT
|
||||
|
||||
echo "`date` -- updating secondcrack" >> $UPDATE_LOG
|
||||
php -f "${SECONDCRACK_PATH}/engine/update.php" "$SCRIPT_LOCK_FILE"
|
||||
|
||||
if [ "`which inotifywait`" != "" ] ; then
|
||||
while true ; do
|
||||
inotifywait -q -q -r -t $FORCE_CHECK_EVERY_SECONDS -e close_write -e create -e delete -e moved_from "$SOURCE_PATH"
|
||||
if [ $? -eq 0 ] ; then
|
||||
echo "`date` -- updating secondcrack, a source file changed" >> $UPDATE_LOG
|
||||
else
|
||||
echo "`date` -- updating secondcrack, $FORCE_CHECK_EVERY_SECONDS seconds elapsed" >> $UPDATE_LOG
|
||||
fi
|
||||
|
||||
php -f "${SECONDCRACK_PATH}/engine/update.php" "$SCRIPT_LOCK_FILE"
|
||||
while [ $? -eq 2 ] ; do
|
||||
echo "`date` -- updating secondcrack, last run performed writes" >> $UPDATE_LOG
|
||||
php -f "${SECONDCRACK_PATH}/engine/update.php" "$SCRIPT_LOCK_FILE"
|
||||
done
|
||||
done
|
||||
fi
|
||||
|
||||
rmdir "$BASH_LOCK_DIR" 2>/dev/null
|
||||
trap - INT TERM EXIT
|
||||
else
|
||||
echo "Already running"
|
||||
fi
|
@ -0,0 +1,8 @@
|
||||
<?php
|
||||
class Dummy extends Hook
|
||||
{
|
||||
public function doHook(Post $post)
|
||||
{
|
||||
error_log('Hooked ' . $post->title);
|
||||
}
|
||||
}
|
70
_site/tmp/var/www/chaospott.de/example-hooks/post_fb.php
Normal file
@ -0,0 +1,70 @@
|
||||
<?php
|
||||
// Auto-posting requires Facebook's PHP API. It can be found on GitHub.
|
||||
// https://github.com/facebook/facebook-php-sdk
|
||||
|
||||
require_once(dirname(__FILE__) . '/facebook-php-sdk/src/facebook.php');
|
||||
|
||||
// Obtaining proper credentials will take a few steps, listed below.
|
||||
// More here: http://jeremygibbs.com/2012/02/11/how-to-autopost-facebook
|
||||
//
|
||||
// 1.) Create Facebook App
|
||||
// Go to Facebook app page: https://developers.facebook.com/apps
|
||||
// and click "Create New App" button in top right corner. Choose a
|
||||
// name (you can leave namespace empty) and continue. Next, you will
|
||||
// customize the app. Write down App ID and App Secret located in header.
|
||||
// In basic info section, enter email/domain from which you will be
|
||||
// posting. Finally, declare how the app integrates with Facebook. You
|
||||
// will likely want the website option. Enter your site's URL and save.
|
||||
//
|
||||
// 2.) Obtain Credentials
|
||||
// You now must authorize your app to post content on your behalf.
|
||||
// Normally, once an app is authorized, the user must be logged in to
|
||||
// verify permissions. You can avoid this by requesting a long-term
|
||||
// token. To do this, you will create a simple php webpage - grab the
|
||||
// example here: https://gist.github.com/2114528. Visit the webpage in
|
||||
// your app, which will forward you to Facebook. You will grant
|
||||
// permissions and be redirected back to the webpage. That page will
|
||||
// will display your offline access token. Save this token, and put
|
||||
// it with your App ID and App Secret. Now you are good to go.
|
||||
|
||||
class FacebookCredentials
|
||||
{
|
||||
public static $app_id = 'YOUR APP ID';
|
||||
public static $app_secret = 'YOUR APP SECRET';
|
||||
public static $access_token = 'YOUR ACCESS TOKEN';
|
||||
}
|
||||
|
||||
function construct_post_text(array $post)
|
||||
{
|
||||
$post_txt = $post['post-title'];
|
||||
$post_url = $post['post-absolute-permalink'];
|
||||
$post_txt .= ' - ' . $post_url;
|
||||
|
||||
if (isset($post['link'])) $post_txt = "\xE2\x86\x92 " . $post_txt;
|
||||
return $post_txt;
|
||||
}
|
||||
|
||||
function post_facebook_link_to_post(array $post)
|
||||
{
|
||||
$post_text = construct_post_text($post);
|
||||
|
||||
$facebook = new Facebook(array(
|
||||
'appId' => FacebookCredentials::$app_id,
|
||||
'secret' => FacebookCredentials::$app_secret,
|
||||
'cookie' => true));
|
||||
|
||||
$req = array(
|
||||
'access_token' => FacebookCredentials::$access_token,
|
||||
'message' => $post_text);
|
||||
|
||||
$res = $facebook->api('/me/feed', 'POST', $req);
|
||||
}
|
||||
|
||||
class fb extends Hook
|
||||
{
|
||||
public function doHook(Post $post)
|
||||
{
|
||||
post_facebook_link_to_post($post->array_for_template());
|
||||
}
|
||||
}
|
||||
?>
|
158
_site/tmp/var/www/chaospott.de/example-hooks/post_tumblr.php
Normal file
@ -0,0 +1,158 @@
|
||||
<?php
|
||||
|
||||
// Paste in your values for these strings:
|
||||
class TumblrPostHookCredentials
|
||||
{
|
||||
public static $account_email = '';
|
||||
public static $account_password = '';
|
||||
|
||||
// The part that goes here: (blog name).tumblr.com
|
||||
public static $blog_name = '';
|
||||
}
|
||||
|
||||
// Test your credentials by executing this file alone from the command line:
|
||||
// php -f post_tumblr.php
|
||||
|
||||
// Edit this function to customize the generated Tumblr posts.
|
||||
// The $post array is just like the $content['post'] arrays in the templates.
|
||||
// This function MUST return an associative array of Tumblr post variables documented here:
|
||||
// http://www.tumblr.com/docs/en/api/v1#api_write
|
||||
//
|
||||
// Or return false to skip posting this to Tumblr.
|
||||
//
|
||||
function tumblr_post_for_post(array $post)
|
||||
{
|
||||
if (isset($post['link'])) {
|
||||
return false;
|
||||
} else {
|
||||
return array(
|
||||
'type' => 'link',
|
||||
'name' => $post['post-title'],
|
||||
'url' => $post['post-absolute-permalink'],
|
||||
'description' => isset($post['sharing-summary']) ? $post['sharing-summary'] : ''
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// ======== You probably don't want to edit anything below this line =========
|
||||
|
||||
function tumblr_http_build_query_raw(array $arr)
|
||||
{
|
||||
$out = array();
|
||||
foreach ($arr as $name => $val) $out[] = rawurlencode($name) . '=' . rawurlencode($val);
|
||||
return implode('&', $out);
|
||||
}
|
||||
|
||||
function post_tumblr_link_to_post(array $post_array_for_template)
|
||||
{
|
||||
$tumblr_post = tumblr_post_for_post($post_array_for_template);
|
||||
if (! $tumblr_post) {
|
||||
error_log("Not posting this to Tumblr");
|
||||
return;
|
||||
}
|
||||
|
||||
$request_data = tumblr_http_build_query_raw(
|
||||
array_merge(
|
||||
array(
|
||||
'email' => TumblrPostHookCredentials::$account_email,
|
||||
'password' => TumblrPostHookCredentials::$account_password,
|
||||
'group' => TumblrPostHookCredentials::$blog_name . '.tumblr.com',
|
||||
'generator' => 'Second Crack'
|
||||
),
|
||||
$tumblr_post
|
||||
)
|
||||
);
|
||||
|
||||
$c = curl_init('https://www.tumblr.com/api/write');
|
||||
curl_setopt($c, CURLOPT_CONNECTTIMEOUT, 5);
|
||||
curl_setopt($c, CURLOPT_TIMEOUT, 10);
|
||||
curl_setopt($c, CURLOPT_SSL_VERIFYPEER, false);
|
||||
curl_setopt($c, CURLOPT_FAILONERROR, 0);
|
||||
curl_setopt($c, CURLOPT_POST, true);
|
||||
curl_setopt($c, CURLOPT_POSTFIELDS, $request_data);
|
||||
curl_setopt($c, CURLOPT_RETURNTRANSFER, true);
|
||||
$result = curl_exec($c);
|
||||
$status = curl_getinfo($c, CURLINFO_HTTP_CODE);
|
||||
curl_close($c);
|
||||
|
||||
// Check for success
|
||||
if ($status == 201) {
|
||||
error_log("Posted to Tumblr: http://" . TumblrPostHookCredentials::$blog_name . ".tumblr.com/post/$result");
|
||||
} else {
|
||||
error_log("Posting to Tumblr failed (HTTP $status): $response");
|
||||
}
|
||||
}
|
||||
|
||||
$command_line_test_mode = isset($_SERVER['argv'][0]) && substr($_SERVER['argv'][0], -15) == 'post_tumblr.php';
|
||||
if ($command_line_test_mode) {
|
||||
// test auth
|
||||
|
||||
$c = curl_init('https://www.tumblr.com/api/authenticate');
|
||||
curl_setopt($c, CURLOPT_CONNECTTIMEOUT, 5);
|
||||
curl_setopt($c, CURLOPT_TIMEOUT, 10);
|
||||
curl_setopt($c, CURLOPT_SSL_VERIFYPEER, false);
|
||||
curl_setopt($c, CURLOPT_FAILONERROR, 0);
|
||||
curl_setopt($c, CURLOPT_POST, true);
|
||||
curl_setopt($c, CURLOPT_POSTFIELDS,
|
||||
tumblr_http_build_query_raw(array(
|
||||
'email' => TumblrPostHookCredentials::$account_email,
|
||||
'password' => TumblrPostHookCredentials::$account_password,
|
||||
))
|
||||
);
|
||||
curl_setopt($c, CURLOPT_RETURNTRANSFER, true);
|
||||
$result = curl_exec($c);
|
||||
$status = curl_getinfo($c, CURLINFO_HTTP_CODE);
|
||||
curl_close($c);
|
||||
|
||||
// Check for success
|
||||
libxml_use_internal_errors(true);
|
||||
if ($status == 200 &&
|
||||
($dom = new DOMDocument()) &&
|
||||
$dom->loadXML($result) &&
|
||||
($tumblelogs = $dom->getElementsByTagName('tumblelog'))
|
||||
) {
|
||||
$permitted = false;
|
||||
$names = array();
|
||||
foreach ($tumblelogs as $tumblelog) {
|
||||
if (! ($name = $tumblelog->getAttribute('name')) ) continue;
|
||||
if ($name == TumblrPostHookCredentials::$blog_name) {
|
||||
$permitted = true;
|
||||
break;
|
||||
}
|
||||
$names[] = $name;
|
||||
}
|
||||
|
||||
if ($permitted) {
|
||||
echo "\nSuccessfully authenticated for " . TumblrPostHookCredentials::$blog_name . "\n";
|
||||
} else {
|
||||
echo "\nTumblrPostHookCredentials::\$blog_name is not set to one of your blog names.\nBlog names available on your account:\n\n\t" . implode("\n\t", $names) . "\n\n";
|
||||
exit(1);
|
||||
}
|
||||
} else if ($status == 403) {
|
||||
echo "\nInvalid Tumblr account email address or password.\n\n";
|
||||
exit(1);
|
||||
} else {
|
||||
echo "\nTumblr returned an error:\n$result\n\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (isset($_SERVER['argv'][1]) && $_SERVER['argv'][1] == 'post') {
|
||||
post_tumblr_link_to_post(array(
|
||||
'post-title' => 'Test post title',
|
||||
'post-absolute-permalink' => 'http://www.tumblr.com/',
|
||||
'sharing-summary' => 'This is a post about nothing.',
|
||||
));
|
||||
echo "\n";
|
||||
} else {
|
||||
echo "Re-run with argument 'post' to create a test post on Tumblr.\n\n";
|
||||
}
|
||||
} else {
|
||||
class Tumblr extends Hook
|
||||
{
|
||||
public function doHook(Post $post)
|
||||
{
|
||||
post_tumblr_link_to_post($post->array_for_template());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
233
_site/tmp/var/www/chaospott.de/example-hooks/post_twitter.php
Normal file
@ -0,0 +1,233 @@
|
||||
<?php
|
||||
|
||||
// Create an "app" from the Twitter account you want to post to:
|
||||
// https://dev.twitter.com/
|
||||
//
|
||||
// From your app's Details page, request an Access Token (bottom).
|
||||
// Reload periodically until you have it.
|
||||
// Paste in your values for these strings:
|
||||
|
||||
class TwitterPostHookCredentials
|
||||
{
|
||||
public static $consumer_key = '';
|
||||
public static $consumer_secret = '';
|
||||
public static $access_token = '';
|
||||
public static $access_token_secret = '';
|
||||
}
|
||||
|
||||
// Test your credentials by executing this file alone from the command line:
|
||||
// php -f post_twitter.php
|
||||
|
||||
// Edit this function to customize the formatting of the posted tweets.
|
||||
// The $post array is just like the $content['post'] arrays in the templates.
|
||||
// You can even change the posted URL by overwriting $url_to_use.
|
||||
//
|
||||
// The string returned by this function will have a space and URL appended.
|
||||
// $max_characters accounts for this, and is the most UTF-8 characters YOU can
|
||||
// return from this function.
|
||||
//
|
||||
// The twitter_summarize($text, $max_characters) function is available. It
|
||||
// will trim a string at the nearest word boundary and append an ellipsis to
|
||||
// fit within the number of characters if it's too long.
|
||||
//
|
||||
function tweet_text_before_url_for_post(array $post, $max_characters, &$url_to_use)
|
||||
{
|
||||
$title = $post['post-title'];
|
||||
if (isset($post['link'])) $title = "\xE2\x86\x92 " . $title; // right arrow
|
||||
return twitter_summarize($title, $max_characters);
|
||||
}
|
||||
|
||||
|
||||
// ======== You probably don't want to edit anything below this line =========
|
||||
|
||||
class ExtremelyBasicSelfContainedOAuthConsumer
|
||||
{
|
||||
public $consumer_key;
|
||||
public $consumer_secret;
|
||||
public $token = '';
|
||||
public $token_secret = '';
|
||||
|
||||
public $request_timeout_seconds = 10;
|
||||
|
||||
public static function hmac_sha1($data, $key)
|
||||
{
|
||||
if (strlen($key) > 64) $key = pack('H40', sha1($key));
|
||||
if (strlen($key) < 64) $key = str_pad($key, 64, chr(0));
|
||||
$ipad = (substr($key, 0, 64) ^ str_repeat(chr(0x36), 64));
|
||||
$opad = (substr($key, 0, 64) ^ str_repeat(chr(0x5C), 64));
|
||||
return sha1($opad . pack('H40', sha1($ipad . $data)), true);
|
||||
}
|
||||
|
||||
public function signature($request_method, $target_url, $post_params = array())
|
||||
{
|
||||
if ( ($qs = @parse_url($target_url, PHP_URL_QUERY)) ) {
|
||||
parse_str($qs, $get_params);
|
||||
$post_params = array_merge($get_params, $post_params);
|
||||
}
|
||||
if (isset($post_params['oauth_signature'])) unset($post_params['oauth_signature']);
|
||||
ksort($post_params);
|
||||
|
||||
if (false !== ($cutpos = strpos($target_url, '?')) ) $target_url = substr($target_url, 0, $cutpos);
|
||||
$parts = array($request_method, rawurlencode($target_url));
|
||||
$vars = array();
|
||||
foreach ($post_params as $name => $val) $vars[] = rawurlencode($name) . '=' . rawurlencode($val);
|
||||
$parts[] = rawurlencode(implode('&', $vars));
|
||||
$signature_base_string = implode('&', $parts);
|
||||
return base64_encode(self::hmac_sha1($signature_base_string, $this->consumer_secret . '&' . ($this->token_secret ? $this->token_secret : '')));
|
||||
}
|
||||
|
||||
public function request_parameters($request_method, $target_url, $post_params = array())
|
||||
{
|
||||
$params = array(
|
||||
'oauth_consumer_key' => $this->consumer_key,
|
||||
'oauth_signature_method' => 'HMAC-SHA1',
|
||||
'oauth_timestamp' => time(),
|
||||
'oauth_nonce' => sha1($this->consumer_secret . microtime(true) . $this->token_secret),
|
||||
'oauth_version' => '1.0'
|
||||
);
|
||||
if (strlen($this->token)) $params['oauth_token'] = $this->token;
|
||||
|
||||
$post_params = array_merge($post_params, $params);
|
||||
$sig = $this->signature($request_method, $target_url, $post_params);
|
||||
$params['oauth_signature'] = $sig;
|
||||
return $params;
|
||||
}
|
||||
|
||||
public function authorization_header_value($oauth_request_parameters)
|
||||
{
|
||||
$value = 'OAuth ';
|
||||
foreach ($oauth_request_parameters as $key => $val) {
|
||||
$value .= $key . '="' . rawurlencode($val) . '", ';
|
||||
}
|
||||
$value = rtrim($value, ', ');
|
||||
return $value;
|
||||
}
|
||||
|
||||
public function http_build_query_raw(array $arr)
|
||||
{
|
||||
$out = array();
|
||||
foreach ($arr as $name => $val) $out[] = rawurlencode($name) . '=' . rawurlencode($val);
|
||||
return implode('&', $out);
|
||||
}
|
||||
|
||||
public function request($method, $endpoint, $params = array())
|
||||
{
|
||||
$oauth_parameters = $this->request_parameters($method, $endpoint, $params);
|
||||
$auth = $this->authorization_header_value($oauth_parameters);
|
||||
|
||||
$curl = curl_init($endpoint);
|
||||
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, $this->request_timeout_seconds);
|
||||
curl_setopt($curl, CURLOPT_TIMEOUT, $this->request_timeout_seconds);
|
||||
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
|
||||
curl_setopt($curl, CURLOPT_FAILONERROR, 0);
|
||||
curl_setopt($curl, CURLOPT_HTTPHEADER, array('Authorization: ' . $auth, 'Expect:'));
|
||||
if ($method == 'POST') {
|
||||
curl_setopt($curl, CURLOPT_POST, true);
|
||||
curl_setopt($curl, CURLOPT_POSTFIELDS, self::http_build_query_raw($params));
|
||||
}
|
||||
|
||||
$data = curl_exec($curl);
|
||||
curl_close($curl);
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
||||
function twitter_summarize($text, $length, $ellipsis = "\xE2\x80\xA6" /* UTF-8 … */)
|
||||
{
|
||||
if ($length == 0) return $text;
|
||||
if (mb_strlen($text, 'UTF-8') <= $length) return $text;
|
||||
|
||||
$word_delimiter = ' ';
|
||||
$word_delimiter_mblen = 1;
|
||||
|
||||
$length -= mb_strlen($ellipsis, 'UTF-8');
|
||||
$cut_str = mb_substr($text, 0, $length + $word_delimiter_mblen, 'UTF-8');
|
||||
if (mb_substr($cut_str, 0 - $word_delimiter_mblen) != $word_delimiter) {
|
||||
$cut_str = mb_substr($cut_str, 0, 0 - $word_delimiter_mblen);
|
||||
}
|
||||
|
||||
$split_pos = $cut_str ? mb_strrpos($cut_str, $word_delimiter, 'UTF-8') : false;
|
||||
if ($split_pos) {
|
||||
return mb_substr($text, 0, $split_pos, 'UTF-8') . $ellipsis;
|
||||
} else {
|
||||
return $cut_str . $ellipsis;
|
||||
}
|
||||
}
|
||||
|
||||
function tweet_link_to_post(array $post_array_for_template)
|
||||
{
|
||||
$oauth = new ExtremelyBasicSelfContainedOAuthConsumer();
|
||||
$oauth->consumer_key = TwitterPostHookCredentials::$consumer_key;
|
||||
$oauth->consumer_secret = TwitterPostHookCredentials::$consumer_secret;
|
||||
$oauth->token = TwitterPostHookCredentials::$access_token;
|
||||
$oauth->token_secret = TwitterPostHookCredentials::$access_token_secret;
|
||||
|
||||
$short_url_length = 24; // Big, safe default (at time of writing, Twitter's value is actually 20)
|
||||
try {
|
||||
$config = json_decode($oauth->request('GET', 'http://api.twitter.com/1/help/configuration.json'), true);
|
||||
if (isset($config['short_url_length'])) $short_url_length = intval($config['short_url_length']);
|
||||
if ($short_url_length < 16) $short_url_length = 24; // sanity check
|
||||
} catch (Exception $e) { }
|
||||
|
||||
$url_to_use = $post_array_for_template['post-absolute-permalink'];
|
||||
$tweet_text = tweet_text_before_url_for_post(
|
||||
$post_array_for_template,
|
||||
140 - ($short_url_length + 1 /* the space before the URL */),
|
||||
$url_to_use
|
||||
);
|
||||
|
||||
$tweet_text .= ' ' . $url_to_use;
|
||||
$response = $oauth->request(
|
||||
'POST', 'https://api.twitter.com/1/statuses/update.json',
|
||||
array('trim_user' => 'true', 'status' => $tweet_text)
|
||||
);
|
||||
|
||||
$post = @json_decode($response, true);
|
||||
if ($post && isset($post['id_str']) && isset($post['text'])) {
|
||||
error_log("Posted to Twitter: [{$post['text']}]");
|
||||
} else {
|
||||
error_log("Posting to Twitter failed: $response");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$command_line_test_mode = isset($_SERVER['argv'][0]) && substr($_SERVER['argv'][0], -16) == 'post_twitter.php';
|
||||
if ($command_line_test_mode) {
|
||||
$oauth = new ExtremelyBasicSelfContainedOAuthConsumer();
|
||||
$oauth->consumer_key = TwitterPostHookCredentials::$consumer_key;
|
||||
$oauth->consumer_secret = TwitterPostHookCredentials::$consumer_secret;
|
||||
$oauth->token = TwitterPostHookCredentials::$access_token;
|
||||
$oauth->token_secret = TwitterPostHookCredentials::$access_token_secret;
|
||||
|
||||
$response = $oauth->request('GET', 'https://api.twitter.com/1/account/verify_credentials.json');
|
||||
$user = @json_decode($response, true);
|
||||
|
||||
if ($user && isset($user['error'])) {
|
||||
echo "\nTwitter returned an error: {$user['error']}\n\n";
|
||||
exit(1);
|
||||
} else if ($user && isset($user['name']) && isset($user['screen_name'])) {
|
||||
echo "\nSuccessfully authenticated as @{$user['screen_name']} ({$user['name']})\n";
|
||||
} else {
|
||||
echo "\nGot unrecognized response from Twitter:\n$response\n\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (isset($_SERVER['argv'][1]) && $_SERVER['argv'][1] == 'post') {
|
||||
tweet_link_to_post(array(
|
||||
'post-title' => 'Test post title',
|
||||
'post-absolute-permalink' => 'http://twitter.com/',
|
||||
));
|
||||
echo "\n";
|
||||
} else {
|
||||
echo "Re-run with argument 'post' to create a test post on Twitter.\n\n";
|
||||
}
|
||||
} else {
|
||||
class Twitter extends Hook
|
||||
{
|
||||
public function doHook(Post $post)
|
||||
{
|
||||
tweet_link_to_post($post->array_for_template());
|
||||
}
|
||||
}
|
||||
}
|
89
_site/tmp/var/www/chaospott.de/example-templates/main.php
Normal file
@ -0,0 +1,89 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="alternate" type="application/rss+xml" title="RSS" href="/rss.xml" />
|
||||
<meta name="viewport" content="width=device-width" />
|
||||
<link rel="apple-touch-icon-precomposed" href="/apple-touch-icon.png"/>
|
||||
<title><?=
|
||||
(isset($content['post']) ? h($content['post']['post-title']) . ' – ' : '') .
|
||||
($content['page-title'] != $content['blog-title'] && (
|
||||
$content['page-type'] == 'page' || $content['page-type'] == 'archive' || $content['page-type'] == 'tag' || $content['page-type'] == 'type'
|
||||
) ? h($content['page-title']) . ' – ' : '') .
|
||||
h($content['blog-title'])
|
||||
?></title>
|
||||
|
||||
<? if ($content['page-type'] != 'frontpage' && $content['page-type'] != 'page' && $content['page-type'] != 'post') { ?>
|
||||
<meta name="robots" content="noindex"/>
|
||||
<? } ?>
|
||||
</head>
|
||||
<body>
|
||||
<div id="mastheadbackground"> </div>
|
||||
|
||||
<section id="posts">
|
||||
|
||||
<div id="masthead">
|
||||
<h1><a href="/">My Site</a></h1>
|
||||
|
||||
<p id="description">Who am I?</p>
|
||||
</div>
|
||||
|
||||
<? if ($content['page-type'] == 'page') { ?>
|
||||
<article>
|
||||
<header>
|
||||
<h2><?= h($content['page-title']) ?></h2>
|
||||
</header>
|
||||
<?= $content['page-body'] ?>
|
||||
</article>
|
||||
<? } else { ?>
|
||||
<? if (isset($content['posts'])) foreach ($content['posts'] as $post) { ?>
|
||||
<article<?= $post['post-type'] == 'link' ? ' class="link"' : '' ?>>
|
||||
<header>
|
||||
<h2>
|
||||
<a href="<?= h($post['post-permalink-or-link']) ?>"><?= h($post['post-title']) ?></a>
|
||||
<?= $post['post-type'] == 'link' ? '<span class="linkarrow">→</span>' : '' ?>
|
||||
</h2>
|
||||
|
||||
<p>
|
||||
<time datetime="<?= h(date('c', $post['post-timestamp'])) ?>" pubdate="pubdate"><?= date('F j, Y', $post['post-timestamp']) ?></time>
|
||||
•
|
||||
<a class="permalink" title="Permalink" href="<?= h($post['post-permalink']) ?>">∞</a>
|
||||
</p>
|
||||
</header>
|
||||
|
||||
<?= $post['post-body'] ?>
|
||||
</article>
|
||||
<? } ?>
|
||||
<? } ?>
|
||||
|
||||
<? if (isset($content['archives'])) { ?>
|
||||
<nav id="archives">
|
||||
<h3>Archives</h3>
|
||||
<div style="clear: both; font-size: 1px; line-height: 1px;"> </div>
|
||||
<div style="float: left; width: 90px; text-align: right; padding-bottom: 2em;">
|
||||
<? $so_far = 0; $per_column = ceil(count($content['archives']) / 5); ?>
|
||||
<? foreach ($content['archives'] as $archive) { ?>
|
||||
<? if (++$so_far > $per_column) { ?>
|
||||
<? $so_far = 1; ?>
|
||||
</div>
|
||||
<div style="float: left; width: 90px; text-align: right;">
|
||||
<? } ?>
|
||||
<a href="<?= h($archive['archives-uri']) ?>"><?= $archive['archives-month-short-name'] ?> <?= $archive['archives-year'] ?></a>
|
||||
<br/>
|
||||
<? } ?>
|
||||
</div>
|
||||
<div style="clear: both; font-size: 1px; line-height: 1px;"> </div>
|
||||
</nav>
|
||||
<? } ?>
|
||||
|
||||
<footer>
|
||||
<p>© 2006-2012 Marco Arment. All rights reserved.</p>
|
||||
<p>
|
||||
<a href="/rss.xml">RSS feed</a>.
|
||||
Powered by <a href="http://www.marco.org/secondcrack">Second Crack</a>.
|
||||
</p>
|
||||
|
||||
</footer>
|
||||
</section>
|
||||
</body>
|
||||
</html>
|
80
_site/tmp/var/www/chaospott.de/example-templates/rss.php
Normal file
@ -0,0 +1,80 @@
|
||||
<?
|
||||
if (! function_exists('title_for_post_callback')) {
|
||||
|
||||
// Feel free to edit these -------------------------------------------------
|
||||
|
||||
function title_for_post_callback($post)
|
||||
{
|
||||
if ($post['post-type'] == 'link') {
|
||||
// For link posts, prepend a Unicode right-pointing-arrow (→)
|
||||
return "\xE2\x86\x92 " . $post['post-title'];
|
||||
} else {
|
||||
return $post['post-title'];
|
||||
}
|
||||
}
|
||||
|
||||
function url_for_post_callback($post) { return $post['post-permalink-or-link']; }
|
||||
|
||||
function body_for_post_callback($post)
|
||||
{
|
||||
if ($post['post-type'] == 'link') {
|
||||
// Insert little permalink infinity-symbol into RSS body for link posts
|
||||
return $post['post-body'] . "\n\n<p><a href=\"http://www.YOURDOMAINHERE.com" . $post['post-permalink'] . "\">∞ Permalink</a></p>";
|
||||
} else {
|
||||
return $post['post-body'];
|
||||
}
|
||||
}
|
||||
|
||||
// You should probably stop editing here -----------------------------------
|
||||
}
|
||||
|
||||
$dom = new DOMDocument('1.0', 'UTF-8');
|
||||
$root = $dom->createElement('rss');
|
||||
$root->setAttribute('version', '2.0');
|
||||
$channel = $dom->createElement('channel');
|
||||
|
||||
$title_node = $dom->createElement('title');
|
||||
$title_node->appendChild($dom->createTextNode($content['blog-title']));
|
||||
$channel->appendChild($title_node);
|
||||
|
||||
$link_node = $dom->createElement('link');
|
||||
$link_node->appendChild($dom->createTextNode($content['blog-url']));
|
||||
$channel->appendChild($link_node);
|
||||
|
||||
$desc_node = $dom->createElement('description');
|
||||
$desc_node->appendChild($dom->createTextNode($content['blog-description']));
|
||||
$channel->appendChild($desc_node);
|
||||
|
||||
foreach ($content['posts'] as $post) {
|
||||
$item_node = $dom->createElement('item');
|
||||
|
||||
$title_node = $dom->createElement('title');
|
||||
$title_node->appendChild($dom->createTextNode(title_for_post_callback($post)));
|
||||
$item_node->appendChild($title_node);
|
||||
|
||||
$link_node = $dom->createElement('link');
|
||||
$link_url = url_for_post_callback($post);
|
||||
if (! isset($link_url[0]) || $link_url[0] == '/') $link_url = rtrim($content['blog-url'], '/') . $link_url;
|
||||
$link_node->appendChild($dom->createTextNode($link_url));
|
||||
$item_node->appendChild($link_node);
|
||||
|
||||
$guid = $dom->createElement('guid');
|
||||
$guid->setAttribute('isPermaLink', 'false');
|
||||
$guid->appendChild($dom->createTextNode($post['post-permalink-or-link']));
|
||||
$item_node->appendChild($guid);
|
||||
|
||||
$date_node = $dom->createElement('pubDate');
|
||||
$date_node->appendChild($dom->createTextNode($post['post-rss-date']));
|
||||
$item_node->appendChild($date_node);
|
||||
|
||||
$desc = $dom->createElement('description');
|
||||
$desc->appendChild($dom->createTextNode(body_for_post_callback($post)));
|
||||
$item_node->appendChild($desc);
|
||||
|
||||
$channel->appendChild($item_node);
|
||||
}
|
||||
|
||||
$root->appendChild($channel);
|
||||
$dom->appendChild($root);
|
||||
echo $dom->saveXML();
|
||||
|
BIN
_site/tmp/var/www/chaospott.de/media/2012-03-02/P1020474.JPG
Normal file
After Width: | Height: | Size: 4.3 MiB |
After Width: | Height: | Size: 1.2 MiB |
After Width: | Height: | Size: 58 KiB |
After Width: | Height: | Size: 6.4 MiB |
After Width: | Height: | Size: 184 KiB |
After Width: | Height: | Size: 5.4 MiB |
After Width: | Height: | Size: 157 KiB |
After Width: | Height: | Size: 2.1 MiB |
After Width: | Height: | Size: 2.9 MiB |
After Width: | Height: | Size: 2.7 MiB |
After Width: | Height: | Size: 2.4 MiB |
After Width: | Height: | Size: 1.6 MiB |
After Width: | Height: | Size: 2.9 MiB |
After Width: | Height: | Size: 2.2 MiB |
After Width: | Height: | Size: 2.7 MiB |
After Width: | Height: | Size: 1.8 MiB |
After Width: | Height: | Size: 2.4 MiB |
After Width: | Height: | Size: 2.8 MiB |