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 string[] trackbacks; 26 27 this() 28 { 29 id = BsonObjectID.generate(); 30 date = Clock.currTime().toUTC(); 31 } 32 33 @property string name() const { return slug.length ? slug : id.toString(); } 34 35 static Post fromBson(Bson bson) 36 { 37 auto ret = new Post; 38 ret.id = cast(BsonObjectID)bson["_id"]; 39 ret.isPublic = cast(bool)bson["isPublic"]; 40 ret.commentsAllowed = cast(bool)bson["commentsAllowed"]; 41 ret.slug = cast(string)bson["slug"]; 42 ret.author = cast(string)bson["author"]; 43 ret.category = cast(string)bson["category"]; 44 ret.date = SysTime.fromISOExtString(cast(string)bson["date"]); 45 ret.header = cast(string)bson["header"]; 46 ret.subHeader = cast(string)bson["subHeader"]; 47 ret.headerImage = cast(string)bson["headerImage"]; 48 ret.content = cast(string)bson["content"]; 49 foreach( t; cast(Bson[])bson["tags"] ) 50 ret.tags ~= cast(string)t; 51 return ret; 52 } 53 54 Bson toBson() 55 const { 56 Bson[] btags; 57 foreach( t; tags ) 58 btags ~= Bson(t); 59 60 Bson[string] ret; 61 ret["_id"] = Bson(id); 62 ret["isPublic"] = Bson(isPublic); 63 ret["commentsAllowed"] = Bson(commentsAllowed); 64 ret["slug"] = Bson(slug); 65 ret["author"] = Bson(author); 66 ret["category"] = Bson(category); 67 ret["date"] = Bson(date.toISOExtString()); 68 ret["header"] = Bson(header); 69 ret["subHeader"] = Bson(subHeader); 70 ret["headerImage"] = Bson(headerImage); 71 ret["content"] = Bson(content); 72 ret["tags"] = Bson(btags); 73 74 return Bson(ret); 75 } 76 77 string renderSubHeaderAsHtml() 78 const { 79 auto ret = appender!string(); 80 filterMarkdown(ret, subHeader); 81 return ret.data; 82 } 83 84 string renderContentAsHtml(string function(string)[] filters) 85 const { 86 auto html = filterMarkdown(content); 87 foreach( flt; filters ) 88 html = flt(html); 89 return html; 90 } 91 } 92 93 class Comment { 94 BsonObjectID id; 95 BsonObjectID postId; 96 bool isPublic; 97 SysTime date; 98 int answerTo; 99 string authorName; 100 string authorMail; 101 string authorHomepage; 102 string authorIP; 103 string header; 104 string content; 105 106 static Comment fromBson(Bson bson) 107 { 108 auto ret = new Comment; 109 ret.id = cast(BsonObjectID)bson["_id"]; 110 ret.postId = cast(BsonObjectID)bson["postId"]; 111 ret.isPublic = cast(bool)bson["isPublic"]; 112 ret.date = SysTime.fromISOExtString(cast(string)bson["date"]); 113 ret.answerTo = cast(int)bson["answerTo"]; 114 ret.authorName = cast(string)bson["authorName"]; 115 ret.authorMail = cast(string)bson["authorMail"]; 116 ret.authorHomepage = cast(string)bson["authorHomepage"]; 117 ret.authorIP = bson["authorIP"].opt!string(); 118 ret.header = cast(string)bson["header"]; 119 ret.content = cast(string)bson["content"]; 120 return ret; 121 } 122 123 Bson toBson() 124 const { 125 Bson[string] ret; 126 ret["_id"] = Bson(id); 127 ret["postId"] = Bson(postId); 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["authorIP"] = Bson(authorIP); 135 ret["header"] = Bson(header); 136 ret["content"] = Bson(content); 137 return Bson(ret); 138 } 139 140 string renderContentAsHtml() 141 const { 142 auto ret = appender!string(); 143 filterMarkdown(ret, htmlEscape(content)); 144 return ret.data; 145 } 146 } 147 148 string makeSlugFromHeader(string header) 149 { 150 Appender!string ret; 151 foreach( dchar ch; header ){ 152 switch( ch ){ 153 default: 154 ret.put('-'); 155 break; 156 case '"', '\'', '´', '`', '.', ',', ';', '!', '?': 157 break; 158 case 'A': .. case 'Z'+1: 159 ret.put(cast(dchar)(ch - 'A' + 'a')); 160 break; 161 case 'a': .. case 'z'+1: 162 case '0': .. case '9'+1: 163 ret.put(ch); 164 break; 165 } 166 } 167 return ret.data; 168 }