1 module vibelog.post; 2 3 import vibe.data.bson; 4 import vibe.textfilter.markdown; 5 import vibe.textfilter.html; 6 7 import std.array; 8 import std.conv; 9 public import std.datetime; 10 11 12 class Post { 13 BsonObjectID id; 14 bool isPublic; 15 bool commentsAllowed; 16 string slug; // url entity to identify this post - generated from the header by default 17 string author; // user name 18 string category; // can be hierarchical using dotted.syntax.format 19 SysTime date; 20 string header; 21 string subHeader; 22 string content; 23 string headerImage; 24 string[] tags; 25 Comment[] comments; 26 string[] trackbacks; 27 28 this() 29 { 30 id = BsonObjectID.generate(); 31 date = Clock.currTime().toUTC(); 32 } 33 34 @property string name() const { return slug.length ? slug : id.toString(); } 35 36 static Post fromBson(Bson bson) 37 { 38 auto ret = new Post; 39 ret.id = cast(BsonObjectID)bson["_id"]; 40 ret.isPublic = cast(bool)bson["isPublic"]; 41 ret.commentsAllowed = cast(bool)bson["commentsAllowed"]; 42 ret.slug = cast(string)bson["slug"]; 43 ret.author = cast(string)bson["author"]; 44 ret.category = cast(string)bson["category"]; 45 ret.date = SysTime.fromISOExtString(cast(string)bson["date"]); 46 ret.header = cast(string)bson["header"]; 47 ret.subHeader = cast(string)bson["subHeader"]; 48 ret.headerImage = cast(string)bson["headerImage"]; 49 ret.content = cast(string)bson["content"]; 50 foreach( t; cast(Bson[])bson["tags"] ) 51 ret.tags ~= cast(string)t; 52 foreach( c; cast(Bson[])bson["comments"] ) 53 ret.comments ~= Comment.fromBson(c); 54 return ret; 55 } 56 57 Bson toBson() 58 const { 59 Bson[] btags; 60 foreach( t; tags ) 61 btags ~= Bson(t); 62 63 Bson[] bcomments; 64 foreach( c; comments ) 65 bcomments ~= c.toBson(); 66 67 Bson[string] ret; 68 ret["_id"] = Bson(id); 69 ret["isPublic"] = Bson(isPublic); 70 ret["commentsAllowed"] = Bson(commentsAllowed); 71 ret["slug"] = Bson(slug); 72 ret["author"] = Bson(author); 73 ret["category"] = Bson(category); 74 ret["date"] = Bson(date.toISOExtString()); 75 ret["header"] = Bson(header); 76 ret["subHeader"] = Bson(subHeader); 77 ret["headerImage"] = Bson(headerImage); 78 ret["content"] = Bson(content); 79 ret["tags"] = Bson(btags); 80 ret["comments"] = Bson(bcomments); 81 82 return Bson(ret); 83 } 84 85 string renderSubHeaderAsHtml() 86 const { 87 auto ret = appender!string(); 88 filterMarkdown(ret, subHeader); 89 return ret.data; 90 } 91 92 string renderContentAsHtml(string function(string)[] filters) 93 const { 94 auto html = filterMarkdown(content); 95 foreach( flt; filters ) 96 html = flt(html); 97 return html; 98 } 99 } 100 101 class Comment { 102 bool isPublic; 103 SysTime date; 104 int answerTo; 105 string authorName; 106 string authorMail; 107 string authorHomepage; 108 string header; 109 string content; 110 111 static Comment fromBson(Bson bson) 112 { 113 auto ret = new Comment; 114 ret.isPublic = cast(bool)bson["isPublic"]; 115 ret.date = SysTime.fromISOExtString(cast(string)bson["date"]); 116 ret.answerTo = cast(int)bson["answerTo"]; 117 ret.authorName = cast(string)bson["authorName"]; 118 ret.authorMail = cast(string)bson["authorMail"]; 119 ret.authorHomepage = cast(string)bson["authorHomepage"]; 120 ret.header = cast(string)bson["header"]; 121 ret.content = cast(string)bson["content"]; 122 return ret; 123 } 124 125 Bson toBson() 126 const { 127 Bson[string] ret; 128 ret["isPublic"] = Bson(isPublic); 129 ret["date"] = Bson(date.toISOExtString()); 130 ret["answerTo"] = Bson(answerTo); 131 ret["authorName"] = Bson(authorName); 132 ret["authorMail"] = Bson(authorMail); 133 ret["authorHomepage"] = Bson(authorHomepage); 134 ret["header"] = Bson(header); 135 ret["content"] = Bson(content); 136 return Bson(ret); 137 } 138 139 string renderContentAsHtml() 140 const { 141 auto ret = appender!string(); 142 filterMarkdown(ret, htmlEscape(content)); 143 return ret.data; 144 } 145 } 146 147 string makeSlugFromHeader(string header) 148 { 149 Appender!string ret; 150 foreach( dchar ch; header ){ 151 switch( ch ){ 152 default: 153 ret.put('-'); 154 break; 155 case '"', '\'', '´', '`', '.', ',', ';', '!', '?': 156 break; 157 case 'A': .. case 'Z'+1: 158 ret.put(cast(dchar)(ch - 'A' + 'a')); 159 break; 160 case 'a': .. case 'z'+1: 161 case '0': .. case '9'+1: 162 ret.put(ch); 163 break; 164 } 165 } 166 return ret.data; 167 }