Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Initial revision

svn path=/trunk/lb/; revision=18986
  • Loading branch information...
commit 59a9375740ddbf1ff1315aa8e1b5f34fbb6085ea 0 parents
@migueldeicaza migueldeicaza authored
37 README
@@ -0,0 +1,37 @@
+This is Lame Blog.
+
+ A small script that I wrote to maintain my blog on the web. I
+ did not use another blog system, because I had different
+ requirements than other people have.
+
+ I wanted:
+
+ * To support off-line editing. Since for long periods
+ of time I might not have an internet connection.
+
+ * To be able to edit my blog entries with my choice
+ editor, not using some integrated tool.
+
+ * To keep my old .txt file format for my entries.
+
+ * To do some minimal processing on the input.
+
+ The script has plenty of data hardcoded, I should make this
+ configurable some day.
+
+ It looks for blog entries in:
+
+ ~/activity/YYYY/mmm-dd.{html|txt}
+
+ Ie, for example:
+
+ ~/activity/2003/oct-01.html
+
+ For historic reasons, it generates an all.html (compatibility
+ with old permalinks) and also now it generates per-day
+ permalink entries in the archive/ directory.
+
+ The result is then pushed to a server with rsync.
+
+Enjoy,
+Miguel de Icaza (miguel@gnu.org)
379 lb.cs
@@ -0,0 +1,379 @@
+//
+// Lame Blog 1.0
+//
+// Features:
+// Per-day entries
+// HTML and .txt files supported (pulls header from the file).
+// Include text support
+//
+//
+// Template macros:
+//
+// @BLOG_ENTRIES@
+// The blob entries rendered
+//
+//
+
+using System;
+using System.IO;
+using System.Text;
+using System.Text.RegularExpressions;
+using System.Collections;
+using System.Globalization;
+using System.Web;
+using Rss;
+
+class DayEntry : IComparable {
+ public DateTime Date;
+ public string Body;
+ public string Caption;
+ Blog blog;
+
+ public const string blog_base = "http://primates.ximian.com/~miguel/";
+ const string code_style = "style=\"border-style: solid; background: #ddddff; border-width: 1px; padding: 2pt;\"";
+ const string shell_style = "style=\"border-style: solid; background: #000000; color: #777777; border-width: 1px; padding: 2pt;\"";
+
+ public DayEntry (Blog blog, string file)
+ {
+ this.blog = blog;
+ ParseDate (file);
+
+ using (FileStream i = File.OpenRead (file)){
+ using (StreamReader s = new StreamReader (i, Encoding.GetEncoding (28591))){
+ if (file.EndsWith (".html"))
+ Load (s, true);
+ else if (file.EndsWith (".txt"))
+ Load (s, false);
+ }
+ }
+ }
+
+ void ParseDate (string file)
+ {
+ int p = file.LastIndexOf ("/");
+ int month;
+
+ Match match = Regex.Match (file, "(200[0-9])/([a-z]+)-0*([0-9]+)");
+
+ int year = Int32.Parse (file.Substring (match.Groups [1].Index, match.Groups [1].Length));
+ int day = Int32.Parse (file.Substring (match.Groups [3].Index, match.Groups [3].Length));
+ string month_name = file.Substring (match.Groups [2].Index, match.Groups [2].Length);
+
+ switch (month_name){
+ case "jan":
+ month = 1; break;
+ case "feb":
+ month = 2; break;
+ case "mar":
+ month = 3; break;
+ case "apr":
+ month = 4; break;
+ case "may":
+ month = 5; break;
+ case "jun":
+ month = 6; break;
+ case "jul":
+ month = 7; break;
+ case "aug":
+ month = 8; break;
+ case "sep":
+ month = 9; break;
+ case "oct":
+ month = 10; break;
+ case "nov":
+ month = 11; break;
+ case "dec":
+ month = 12; break;
+ default:
+ throw new Exception ("Unknown month: " + month_name + " from: " + file);
+ }
+
+ Date = new DateTime (year, month, day, 12, 0, 0);
+ Caption = String.Format ("{0:dd} {0:MMM} {0:yyyy}", Date);
+ }
+
+ void Load (StreamReader i, bool is_html)
+ {
+ bool caption_found = false;
+ StringBuilder sb = new StringBuilder ();
+ string s;
+
+ while ((s = i.ReadLine ()) != null){
+ if (!caption_found){
+ if (is_html){
+ if (s.StartsWith ("<h1>")){
+ Caption = Caption + ": " + s.Replace ("<h1>", "").Replace ("</h1>", "");
+ caption_found = true;
+ continue;
+ } else if (s.StartsWith ("#include")){
+ sb.Append (Include (s.Substring (9), out Caption));
+ caption_found = true;
+ continue;
+ }
+ } else {
+ if (s.StartsWith ("@") && !caption_found){
+ Caption = Caption + ": " + s.Substring (1);
+ caption_found = true;
+ continue;
+ }
+ }
+ }
+ if (!is_html){
+ if (s == "")
+ sb.Append ("<p>");
+ else if (s.StartsWith ("@"))
+ sb.Append (String.Format ("<h1>{0}</h1>", s.Substring (1)));
+ else
+ sb.Append (s);
+ } else {
+ if (s.StartsWith ("#include")){
+ string c;
+ sb.Append (Include (s.Substring (9), out c));
+ continue;
+ }
+ sb.Append (s);
+ }
+ sb.Append ("\n");
+ }
+ Body = sb.ToString ();
+ }
+
+ public int CompareTo (object o)
+ {
+ return Date.CompareTo (((DayEntry) o).Date);
+ }
+
+ string Include (string file, out string caption)
+ {
+ if (file.StartsWith ("~/")){
+ file = Environment.GetEnvironmentVariable ("HOME") + "/" + file.Substring (2);
+ }
+
+ string article_file = "./texts/" + Path.GetFileName (file);
+ File.Copy (file, article_file, true);
+ article_file = article_file.Substring (1);
+
+ //
+ // Remove header stuff, and include inline, stick a copy
+ //
+ StringBuilder r = new StringBuilder ();
+
+ caption = "";
+
+ using (FileStream i = File.OpenRead (file)){
+ StreamReader s = new StreamReader (i, Encoding.GetEncoding (28591));
+ string line;
+ bool output = false;
+
+ while ((line = s.ReadLine ()) != null){
+ Match m = Regex.Match (line, "<title>(.*)</title>");
+ if (m.Groups.Count > 1){
+ caption = line.Substring (m.Groups [1].Index, m.Groups [1].Length);
+ blog.AddArticle (blog_base + article_file, caption);
+ continue;
+ }
+ if (!output){
+ if (line == "<!--start--!>"){
+ output = true;
+ r.Append (String.Format ("<h3>{0}: (<a href=\"{2}{1}\">Article Permalink</a>)</h3>", caption, article_file, blog_base));
+ }
+ continue;
+ }
+ line = Regex.Replace (line, "id=\"code\"", code_style);
+ line = Regex.Replace (line, "id=\"shell\"", shell_style);
+ r.Append (line);
+ r.Append ("\n");
+ }
+ }
+ return r.ToString ();
+ }
+
+ public string PermaLink {
+ get {
+ return String.Format ("archive/{0:yyyy}/{0:MMM}-{0:dd}.html", Date);
+ }
+ }
+}
+
+class Blog {
+ ArrayList entries = new ArrayList ();
+
+ public int Entries {
+ get {
+ return entries.Count;
+ }
+ }
+
+ public Blog ()
+ {
+ string [] years = Directory.GetDirectories ("/home/miguel/activity");
+
+ foreach (string year in years){
+ string [] days = Directory.GetFiles (year);
+
+ foreach (string file in days){
+ if (!(file.EndsWith (".html") || file.EndsWith (".txt")))
+ continue;
+
+ entries.Add (new DayEntry (this, file));
+ }
+ }
+
+ Console.WriteLine ("Loaded: {0} days", entries.Count);
+
+ entries.Sort ();
+ }
+
+ void Render (StreamWriter o, int idx, string blog_base)
+ {
+ DayEntry d = (DayEntry) entries [idx];
+
+ string anchor = HttpUtility.UrlEncode (d.Date.ToString ());
+ o.WriteLine (String.Format ("<a name=\"{0}\"></a>", anchor));
+ o.WriteLine ("<h2><a href=\"{2}{0}\" class=\"entryTitle\">{1}</a> <font size=-2>(<a href=\"{2}{0}\">Permalink</a>)</font></h2>",
+ d.PermaLink, d.Caption, blog_base);
+ o.WriteLine (d.Body);
+ }
+
+ void Render (StreamWriter o, int start, int end, string blog_base)
+ {
+ for (int i = start; i < end; i++){
+ int idx = entries.Count - i - 1;
+ if (idx < 0)
+ return;
+
+ Render (o, idx, blog_base);
+ }
+ }
+
+ void RenderArticleList (StreamWriter o)
+ {
+ foreach (Article a in articles){
+ o.WriteLine ("<a href=\"{0}\">{1}</a><p>", a.url, a.caption);
+ }
+ }
+
+ public void RenderHtml (string template, string output, int start, int end, string blog_base)
+ {
+
+ using (FileStream i = File.OpenRead (template), o = File.Create (output)){
+ StreamReader s = new StreamReader (i, Encoding.GetEncoding (28591));
+ StreamWriter w = new StreamWriter (o, Encoding.GetEncoding (28591));
+ string line;
+
+ while ((line = s.ReadLine ()) != null){
+ switch (line){
+ case "@BLOG_ENTRIES@":
+ Render (w, start, end, blog_base);
+ break;
+ case "@BLOG_ARTICLES@":
+ RenderArticleList (w);
+ break;
+
+ default:
+ line = line.Replace ("@BASEDIR@", blog_base);
+ w.WriteLine (line);
+ break;
+ }
+
+ }
+ w.Flush ();
+ }
+ }
+
+ public void RenderArchive (string template)
+ {
+ for (int i = 0; i < Entries; i++){
+ DayEntry d = (DayEntry) entries [i];
+
+ RenderHtml (template, d.PermaLink, Entries - i - 1, Entries - i, "../../");
+ }
+ }
+
+ RssChannel MakeChannel ()
+ {
+ RssChannel c = new RssChannel ();
+
+ c.Title = "Miguel de Icaza";
+ c.Link = new Uri ("http://primates.ximian.com/~miguel/activity-log.php");
+ c.Description = "Miguel de Icaza's web log";
+ c.Copyright = "Miguel de Icaza";
+ c.Generator = "lb#";
+ c.ManagingEditor = "miguel@ximian.com";
+ c.PubDate = System.DateTime.Now;
+
+ return c;
+ }
+
+ public void RenderRSS (RssVersion version, string output, int start, int end)
+ {
+ RssChannel channel = MakeChannel ();
+
+ for (int i = start; i < end; i++){
+ int idx = entries.Count - i - 1;
+ if (idx < 0)
+ continue;
+
+ DayEntry d = (DayEntry) entries [idx];
+
+ RssItem item = new RssItem ();
+ item.Author = "Miguel de Icaza (miguel@ximian.com)";
+ item.Description = d.Body;
+ item.Guid = new RssGuid ();
+ item.Guid.Name = "http://primates.ximian.com/~miguel/all.html#" + HttpUtility.UrlEncode (d.Date.ToString ());
+ item.Link = new Uri (item.Guid.Name);
+ item.Guid.PermaLink = DBBool.True;
+ item.PubDate = d.Date;
+ item.Title = d.Caption;
+
+ channel.Items.Add (item);
+ }
+
+ FileStream o = File.Create (output);
+ RssWriter w = new RssWriter (o, new UTF8Encoding (false));
+
+ w.Version = version;
+
+ w.Write (channel);
+ w.Close ();
+ }
+
+ public class Article {
+ public string url, caption;
+
+ public Article (string u, string c)
+ {
+ url = u;
+ caption = c;
+ }
+ }
+
+ ArrayList articles = new ArrayList ();
+
+ public void AddArticle (string url, string caption)
+ {
+ articles.Add (new Article (url, caption));
+ }
+
+ public void RenderRSS (string output, int start, int end)
+ {
+ RenderRSS (RssVersion.RSS20, output + ".rss2", start, end);
+ }
+
+}
+
+class LB {
+
+ static void Main ()
+ {
+ Blog b = new Blog ();
+
+ b.RenderHtml ("template", "activity-log.php", 0, 30, "");
+ b.RenderHtml ("template", "all.html", 0, b.Entries, "");
+ b.RenderArchive ("template");
+
+ b.RenderRSS ("miguel", 0, 30);
+
+ File.Copy ("log-style.css", "texts/log-style.css", true);
+ }
+}
39 log-style.css
@@ -0,0 +1,39 @@
+
+ div#entries {
+ font-size: 12pt;
+ margin-left: 2em;
+ margin-right: 35%;
+ float: left;
+ }
+
+ div#entries h2 {
+ background-color: #ddddff;
+ }
+
+ #sidebar {
+ position: absolute;
+ right:10px;
+ left: 70%;
+ top: 6em;
+ }
+
+ #title {
+ background-color: #ccccff;
+ }
+
+ .code { border-style: solid; background: #ddddff;
+ border-width: 1px; padding: 2pt; }
+
+ .shell { border-style: solid; background: #000000; color: #bbbbbb;
+ #777777; border-width:
+ 1px; padding: 2pt; }
+
+A.entryTitle:link {
+text-decoration : none;
+color : 003382;
+}
+
+A.entryTitle:visited {
+text-decoration : none;
+color: black;
+}
10 makefile
@@ -0,0 +1,10 @@
+
+lb.exe: lb.cs
+ mcs -g lb.cs -out:lb.exe -r:RSS.NET -r:System.Web
+
+b: lb.exe
+ mono --debug lb.exe
+
+push: b
+ chmod 644 archive/*/*.html
+ rsync -pr -v --rsh=ssh texts archive log-style.css miguel.rss2 activity-log.php all.html primates.ximian.com:public_html
62 template
@@ -0,0 +1,62 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html>
+<head>
+<title>Miguel de Icaza's Activity Log</title>
+<link rel="stylesheet" href="@BASEDIR@log-style.css" type="text/css">
+<link rel="alternate" type="application/rss+xml" title="RSS" href="@BASEDIR@miguel.rss2">
+</head>
+<body>
+<div id="title"><h1>Miguel de Icaza's web log</h1></div>
+
+<div id="entries">
+@BLOG_ENTRIES@
+
+
+<p>
+<a href="all.html">All entries</a>
+</div>
+
+<div id="sidebar">
+<img src="http://www.go-mono.com/images/mono-contributor-static.gif">
+<p>
+<a href="miguel.rss2"><img align="center" src="@BASEDIR@xml.gif"></a> RSS feed<br>
+
+<p>Email: <a href="mailto:miguel@ximian.com">miguel@ximian.com</a>
+
+<p><b>Docs:</b></p>
+@BLOG_ARTICLES@
+
+<p><b>Mono Blogs:</b></p>
+
+ <a href="http://codeblogs.ximian.com/blogs/benm/index.rdf"><img align="center" src="@BASEDIR@xml.gif"></a>
+ <a href="http://codeblogs.ximian.com/blogs/benm/">Ben Maurer</a>
+ <br>
+ <a href="http://ada.fciencias.unam.mx/~olopez/jscript/cesar.rss2"><img align="center" src="@BASEDIR@xml.gif"></a>
+ <a href="http://ada.fciencias.unam.mx/~olopez/jscript/">Cesar Nataren</a>
+ <br>
+ <a href="http://primates.ximian.com/~duncan/blog/index.rdf"><img align="center" src="@BASEDIR@xml.gif"></a>
+ <a href="http://primates.ximian.com/~duncan/blog">Duncan Mak</a>
+ <br>
+ <a href="http://www.jacksonh.net/jackson/blog/jackson.rss2"><img align="center" src="@BASEDIR@xml.gif"></a>
+ <a href="http://www.jacksonh.net/jackson/blog/">Jackson Harper</a>
+ <br>
+ <a href="http://primates.ximian.com/~lluis/blog/index.rdf"><img align="center" src="@BASEDIR@xml.gif"></a>
+ <a href="http://primates.ximian.com/~lluis/blog/">Lluis Sanchez</a>
+ <br>
+ <a href="http://primates.ximian.com/~martin/blog/index.rdf"><img align="center" src="@BASEDIR@xml.gif"></a>
+ <a href="http://primates.ximian.com/~martin/blog/">Martin Baulig</a>
+ <br>
+ <a href="http://primates.ximian.com/~mkestner/blog/index.rdf"><img align="center" src="@BASEDIR@xml.gif"></a>
+ <a href="http://primates.ximian.com/~mkestner/blog/">Mike Kestner</a>
+ <br>
+ <a href="http://pages.infinit.net/ctech/poupou.rss"><img align="center" src="@BASEDIR@xml.gif"></a>
+ <a href="http://pages.infinit.net/ctech/poupou.html">Sebastien Pouliot</a>
+
+
+<p>
+<a href="http://msdn.microsoft.com/events/pdc"><img src="http://weblog.ikvm.net/pdc2003.gif"></a>
+</div>
+
+
+</body>
+</html>
BIN  xml.gif
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 59a9375

Please sign in to comment.
Something went wrong with that request. Please try again.