1 module uim.html.apps.app; 2 3 import uim.html; 4 5 class DH5App { 6 this() { init; } 7 this(string aName) { this().name(aName); } 8 this(string aName, string aRootPath) { this().name(aName).rootPath(aRootPath); } 9 10 void init() { 11 _layout = new DH5AppLayout; 12 this.index(new DH5AppPage); 13 this.error(new DH5AppPage); 14 } 15 16 /// Id of app 17 mixin(OProperty!("string", "id")); 18 unittest { 19 assert(H5App.id("aId").id == "aId"); 20 assert(H5App.id("aId").id("otherId").id == "otherId"); 21 } 22 23 /// Name of app 24 mixin(OProperty!("string", "name")); 25 unittest { 26 assert(H5App.name("aName").name == "aName"); 27 assert(H5App.name("aName").name("otherName").name == "otherName"); 28 } 29 30 /// Language of app 31 string _lang = "en"; 32 /// Get language of app 33 string lang() { return _lang; } 34 string language() { return _lang; } 35 /// Change language of app 36 O lang(this O)(string newLang) { _lang = newLang; return cast(O)this; } 37 O language(this O)(string newLang) { _lang = newLang; return cast(O)this; } 38 unittest { 39 assert(H5App.lang("aLanguage").lang == "aLanguage"); 40 assert(H5App.lang("aLanguage").lang("otherLanguage").lang == "otherLanguage"); 41 assert(H5App.language("aLanguage").language == "aLanguage"); 42 assert(H5App.language("aLanguage").language("otherLanguage").language == "otherLanguage"); 43 } 44 45 46 /// Page parameters - will be used to communicate between components 47 mixin(XStringAA!"parameters"); 48 unittest { 49 assert(H5App.parameters == null); 50 assert(H5App.parameters(["x":"y"]).parameters == ["x":"y"]); 51 assert(H5App.parameters("x", "y").parameters == ["x":"y"]); 52 } 53 unittest { 54 /// TODO 55 } 56 57 /// central layout for page 58 DH5AppLayout _layout; 59 auto layout() { if (_layout) return _layout; return null; } 60 O layout(this O)(DH5AppLayout newlayout) { _layout = newlayout.app(this); return cast(O)this; } 61 unittest { 62 /// TODO 63 } 64 65 /// Rootpath of app 66 string _rootPath = "/"; 67 auto rootPath() { return _rootPath; } 68 O rootPath(this O)(string newPath) { _rootPath = newPath; if (!uim.core.datatypes.string_.endsWith(_rootPath, "/")) _rootPath ~= "/"; return cast(O)this; } 69 unittest { 70 assert(H5App.rootPath("/test/path").rootPath == "/test/path/"); 71 assert(H5App.rootPath("/test/path/").rootPath == "/test/path/"); 72 } 73 auto path() { return _rootPath~"index"; } 74 75 76 /// Index startpage 77 DH5AppPage _index; 78 auto index() { return _index; } 79 O index(this O)(string newContent) { this.index(H5AppPage.content(newContent)); return cast(O)this; } 80 O index(this O)(DH5AppPage newPage) { _index = newPage; this.pages("index", newPage); return cast(O)this; } 81 unittest { 82 auto page = H5AppPage; 83 assert(H5App.index(page).index == page); 84 } 85 86 /// Index startpage 87 DH5AppPage _error; 88 auto error() { return _error; } 89 O error(this O)(string newContent) { this.error(H5AppPage.content(newContent)); return cast(O)this; } 90 O error(this O)(DH5AppPage newPage) { _error = newPage; this.pages("error", newPage); return cast(O)this; } 91 unittest { 92 auto page = H5AppPage; 93 assert(H5App.error(page).error == page); 94 } 95 96 mixin(XStringAA!"use"); 97 unittest { 98 /// TODO 99 } 100 101 // ---------- 102 // Manage the objs of an app. Everything is an obj 103 DH5AppObj[string] _objs; 104 105 auto objs() { return _objs; } 106 O objs(this O)(DH5AppObj[string] newObjects) { foreach(objName, objNew; newObjects) this.obj(objName, objNew); return cast(O)this; } 107 O objs(this O)(DH5AppObj[] newObjects...) { foreach(objNew; newObjects) this.obj(objNew); return cast(O)this; } 108 109 O obj(this O)(DH5AppObj newObject) { this.obj(newObject.name, newObject); return cast(O)this; } 110 O obj(this O)(string objName, DH5AppObj newObject) { newObject.app(this); _objs[objName] = newObject; return cast(O)this; } 111 112 DH5AppObj opIndex(string name) { if (name in _objs) return _objs[name]; return null; } 113 114 O remove(this O)(string[] names...) { foreach(objName; names) _objs.remove(objName); return cast(O)this; } 115 O remove(this O)(string[] names) { foreach(objName; names) _objs.remove(objName); return cast(O)this; } 116 O clear(this O)() { _objs = null; return cast(O)this; } 117 unittest { 118 /// TODO 119 } 120 121 protected DH5Meta[] _metas; 122 DH5Meta[] metas() { return _metas; } 123 O metas(this O)(string[string] value, string[string][] addMetas...) { this.metas([value]~addMetas); return cast(O)this;} 124 O metas(this O)(string[string][] addMetas) { foreach(meta; addMetas) this.metas(H5Meta(meta)); return cast(O)this; } 125 126 O metas(this O)(DH5Meta[] addMetas...) { this.metas(addMetas); return cast(O)this; } 127 O metas(this O)(DH5Meta[] addMetas) { _metas ~= addMetas; return cast(O)this;} 128 129 O clearMetas(this O)() { _metas = null; return cast(O)this; } 130 unittest { 131 assert(uim.html.elements.meta.toString(H5AppLayout.metas(["a":"b"]).metas) == `<meta a="b">`); 132 assert(uim.html.elements.meta.toString(H5AppLayout.metas([["a":"b"]]).metas) == `<meta a="b">`); 133 assert(uim.html.elements.meta.toString(H5AppLayout.metas(H5Meta(["a":"b"])).metas) == `<meta a="b">`); 134 assert(uim.html.elements.meta.toString(H5AppLayout.metas([H5Meta(["a":"b"])]).metas) == `<meta a="b">`); 135 } 136 137 DH5Style[] _styles; 138 DH5Style[] styles() { return _styles; } 139 O styles(this O)(string content, string[] contents...) { this.styles([content]~contents); return cast(O)this; } // <style>...</style> 140 O styles(this O)(string[] links) { foreach(link; links) _styles ~= H5Style(content); return cast(O)this;} 141 142 O styles(this O)(string[string] link, string[string][] links...) { this.links([link]~links); return cast(O)this;} 143 O styles(this O)(string[string][] links) { foreach(link; links) _links ~= H5Link(link); return cast(O)this;} 144 145 O styles(this O)(DH5Style[] styles...) { this.styles(styles); return cast(O)this;} 146 O styles(this O)(DH5Style[] styles) { _styles ~= styles; return cast(O)this;} 147 O styles(this O)(DH5Link[] links...) { this.styles(links); return cast(O)this;} 148 O styles(this O)(DH5Link[] links) { _styles ~= links; return cast(O)this;} 149 150 O clearStyles(this O)() { _styles = null; return cast(O)this; } 151 unittest { 152 /// TODO 153 } 154 155 DH5Script[] _scripts; 156 DH5Script[] scripts() { return _scripts; } 157 O scripts(this O)(string lib, string[] libs...) { this.scripts([lib]~libs); return cast(O)this;} 158 O scripts(this O)(string[] libs) { foreach(lib; libs) _scripts ~= H5Script(lib); return cast(O)this;} 159 160 O scripts(this O)(string[string] lib, string[string][] libs...) { this.scripts([lib]~libs); return cast(O)this;} 161 O scripts(this O)(string[string][] libs) { foreach(lib; libs) _scripts ~= H5Script(lib); return cast(O)this;} 162 163 O scripts(this O)(DH5Script[] libs...) { this.scripts(libs); return cast(O)this;} 164 O scripts(this O)(DH5Script[] libs) { _scripts ~= libs; return cast(O)this;} 165 166 O clearScripts(this O)() { _scripts = null; return cast(O)this; } 167 unittest { 168 // assert(H5AppLayout.) 169 } 170 171 auto data() { 172 DH5AppData[string] results; 173 foreach(name, obj; _objs) if (auto result = cast(DH5AppData)obj) results[name] = result; 174 return results; } 175 176 O data(this O)(DH5AppData[] addData...) { data(addData); return cast(O)this; } 177 O data(this O)(DH5AppData[] addData) { foreach(d; addData) this.data(d.name, d); return cast(O)this; } 178 179 O data(this O)(string[string] addData) { foreach(name, d; addData) this.data(name, d); return cast(O)this; } 180 O data(this O)(DH5AppData[string] addData) { foreach(name, d; addData) this.data(name, d); return cast(O)this; } 181 182 O data(this O)(string name, string addData) { this.data(name, DH5AppData(this, name).content(addData)); return cast(O)this; } 183 O data(this O)(string name, DH5AppData addData) { this.obj(name, addData); return cast(O)this; } 184 185 /// Remove (only) data files from objs 186 O removeData(this O)(string[] names...) { foreach(item; items) if (item.name in _objs) if (auto obj = cast(DH5AppData)_objs[item.name]) this.remove(item.name); return cast(O)this; } 187 O removeData(this O)(DH5AppData[] items...) { foreach(item; items) if (item.name in _objs) if (auto obj = cast(DH5AppData)_objs[item.name]) this.remove(item.name); return cast(O)this; } 188 189 O clearData(this O)() { foreach(name, item; _objs) if (auto obj = cast(DH5AppData)item) this.remove(name); return cast(O)this; } 190 unittest { 191 /// TODO 192 } 193 194 O styles(this O)(DH5AppStyle[] addStyles) { foreach(addStyle; addStyles) this.styles(addStyle.name, addStyle); return cast(O)this; } 195 O styles(this O)(DH5AppStyle[] addStyles...) { foreach(addStyle; addStyles) this.styles(addStyle.name, addStyle); return cast(O)this; } 196 197 O styles(this O)(DH5AppStyle[string] addStyles) { foreach(name, addStyle; addStyles) this.styles(name, addStyle); return cast(O)this; } 198 O styles(this O)(string[string] addStyles) { foreach(name, addStyle; addStyles) this.styles(name, addStyle); return cast(O)this; } 199 200 O styles(this O)(string name, string addStyle) { this.styles(name, H5AppStyle(this, name).content(addStyle)); return cast(O)this; } 201 O styles(this O)(string name, DH5AppStyle addStyle) { this.obj(name, addStyle); return cast(O)this; } 202 203 O removeStyles(this O)(string[] names...) { foreach(name; names) this.remove(name); return cast(O)this; } 204 O clearStyles(this O)() { foreach(name, item; _objs) if (auto obj = cast(DH5AppStyle)item) this.remove(name); return cast(O)this; } 205 unittest { 206 /// TODO 207 } 208 209 /// Images of an app 210 auto images() { 211 DH5AppImage[] results; 212 foreach(name, obj; _objs) if (auto result = cast(DH5AppImage)obj) results ~= result; 213 return results; } 214 auto imagesByName() { 215 DH5AppImage[string] results; 216 foreach(name, obj; _objs) if (auto result = cast(DH5AppImage)obj) results[name] = result; 217 return results; } 218 unittest { 219 assert("test" in H5App.images("test", "testContent").imagesByName()); 220 assert("xyz" !in H5App.images("test", "testContent").imagesByName()); 221 } 222 bool isImage(string name) { 223 if (name in _objs) 224 if (cast(DH5AppImage)_objs[name]) return true; 225 return false; } 226 unittest { 227 assert(H5App.images("test", "testContent").isImage("test")); 228 assert(!H5App.pages("test", "testContent").isImage("test")); 229 } 230 231 O images(this O)(string[] addImages) { foreach(addImage; addImages) this.images(addImage.name, addImages); return cast(O)this; } 232 O images(this O)(string[] addImages...) { foreach(addImage; addImages) this.images(addImage.name, addImages); return cast(O)this; } 233 234 O images(this O)(DH5AppImage[string] addImages) { foreach(name, addImage; addImages) this.images(name, addImage); return cast(O)this; } 235 O images(this O)(string[string] addImages) { foreach(name, addImage; addImages) this.images(name, addImage); return cast(O)this; } 236 237 O images(this O)(string name, string addImage) { this.images(name, H5AppImage(this, name).content(addImage)); return cast(O)this; } 238 O images(this O)(string name, DH5AppImage addImage) { this.obj(name, addImage); return cast(O)this; } 239 240 O removeImages(this O)(string[] names...) { foreach(name; names) this.remove(name); return cast(O)this; } 241 O clearImages(this O)() { foreach(name, item; _objs) if (auto obj = cast(DH5AppImage)item) this.remove(name); return cast(O)this; } 242 unittest { 243 /// TODO 244 } 245 246 /* /// Managing Scripts 247 auto getScripts() { 248 DH5AppScript[] results; 249 foreach(name, obj; _objs) if (auto result = cast(DH5AppScript)obj) results ~= result; 250 return results; } 251 auto scriptsByName() { 252 DH5AppScript[string] results; 253 foreach(name, obj; _objs) if (auto result = cast(DH5AppScript)obj) results[name] = result; 254 return results; } 255 bool isScript(string name) { 256 if (name in _objs) 257 if (cast(DH5AppScript)_objs[name]) return true; 258 return false; } 259 unittest { 260 assert(H5App.scripts("test", "testContent").isScript("test")); 261 assert(!H5App.pages("test", "testContent").isScript("test")); 262 } 263 264 O scripts(this O)(DH5AppScript[] addScripts) { foreach(addScript; addScripts) this.scripts(addScript.name, addScript); return cast(O)this; } 265 O scripts(this O)(DH5AppScript[] addScripts...) { foreach(addScript; addScripts) this.scripts(addScript.name, addScript); return cast(O)this; } 266 267 O scripts(this O)(DH5AppScript[string] addScripts) { foreach(name, addScript; addScripts) this.scripts(name, addScript); return cast(O)this; } 268 O scripts(this O)(string[string] addScripts) { foreach(name; addScripts) this.scripts(name, addScript); return cast(O)this; } 269 270 O scripts(this O)(string name, string addScript) { this.scripts(name, H5AppScript(this, name).content(addScript)); return cast(O)this; } 271 O scripts(this O)(string name, DH5AppScript addScript) { this.obj(name, addScript); return cast(O)this; } 272 273 O removeScripts(this O)(string[] names...) { foreach(name; names) this.remove(name); return cast(O)this; } 274 O clearScripts(this O)() { foreach(name, item; _objs) if (auto obj = cast(DH5AppScript)item) this.remove(name); return cast(O)this; } 275 unittest { 276 /// TODO 277 } */ 278 279 // Page handling 280 // Get all pages of an app 281 auto pages() { 282 DH5AppPage[string] results; 283 foreach(name, obj; _objs) if (auto result = cast(DH5AppPage)obj) results[name] = result; 284 return results; } 285 unittest { 286 assert(H5App.pages("test", "testcontent").pages.length == 1); 287 assert(H5App.pages("test", "testcontent").pages("test", "testcontent").pages.length == 1); 288 assert(H5App.pages("test", "testcontent").pages("test2", "testcontent").pages.length == 2); 289 } 290 291 // Get pages by names 292 DH5AppPage pageByName(string name) { 293 if (name in _objs) if (auto obj = cast(DH5AppPage)_objs[name]) return obj; 294 return null; } 295 unittest { 296 assert(H5App.pages("test", "testcontent").pageByName("test").name == "test"); 297 assert(H5App.pages("test", "testcontent").pageByName("test").name != "xyz"); 298 assert(H5App.pages("test", "testcontent").pages("test2", "testcontent").pageByName("test").name == "test"); 299 assert(H5App.pages("test", "testcontent").pages("test2", "testcontent").pageByName("test").name != "xyz"); 300 } 301 302 DH5AppPage[] pagesByName(string[] names...) { return pagesByName(names); } 303 DH5AppPage[] pagesByName(string[] names) { 304 DH5AppPage[] results; 305 foreach(name; names) if (name in _objs) if (auto obj = cast(DH5AppPage)_objs[name]) results ~= obj; 306 return results; } 307 unittest { 308 assert(H5App.pages("test", "testcontent").pagesByName("test")[0].name == "test"); 309 assert(H5App.pages("test", "testcontent").pages("test2", "testcontent").pagesByName("test", "test2")[1].name == "test2"); 310 } 311 312 // Is a (DH5)AppPage or inherit 313 bool isPage(string name) { 314 if (name in _objs) 315 if (cast(DH5AppPage)_objs[name]) return true; 316 return false; } 317 unittest { 318 assert(H5App.pages("test", "testContent").isPage("test")); 319 assert(!H5App.scripts("test", "testContent").isPage("test")); 320 } 321 322 O pages(this O)(DH5AppPage[] newPages) { foreach(page; newPages) this.pages(page); return cast(O)this; } 323 O pages(this O)(DH5AppPage[] newPages...) { foreach(page; newPages) this.pages(page); return cast(O)this; } 324 325 O pages(this O)(DH5AppPage[string] newPages) { foreach(name, page; newPages) this.pages(name, page); return cast(O)this; } 326 O pages(this O)(string[string] newPages) { foreach(name, page; newPages) this.pages(name, page); return cast(O)this; } 327 328 O pages(this O)(string name, string newPage, string[string] pageParameters = null) { this.pages(name, H5AppPage(this, name).content(newPage).parameters(pageParameters)); return cast(O)this; } 329 O pages(this O)(DH5AppPage newPage, string[string] pageParameters) { this.pages(newPage.name, newPage.app(this).parameters(pageParameters)); return cast(O)this; } 330 O pages(this O)(string name, DH5AppPage newPage, string[string] pageParameters = null) { 331 if (newPage.name.length == 0) newPage.name = name; 332 this.obj(name, newPage.app(this).parameters(pageParameters)); 333 return cast(O)this; } 334 335 O removePages(this O)(string[] names...) { foreach(name; names) if (name in _objs) if (auto obj = cast(DH5AppPage)_objs[name]) this.remove(name); return cast(O)this; } 336 O clearPages(this O)() { foreach(name, item; _objs) if (auto obj = cast(DH5AppPage)item) this.remove(name); return cast(O)this; } 337 unittest { 338 // writeln(H5App.page("test", "testcontent").pages.length); 339 // writeln(H5App.page("test", "testcontent").pages); 340 assert(H5App.pages("test", "testcontent").pages.length == 1); 341 assert(H5App.pages("test", "testcontent").removePages("test").pages.length == 0); 342 } 343 344 /// Central request handler 345 O registerApp(this O)(URLRouter router) { 346 // debug // writeln("Register app -", this.rootPath); 347 router.get(this.rootPath ~ "*", &this.request); 348 router.post(this.rootPath ~ "*", &this.request); 349 350 return cast(O)this; 351 } 352 unittest { 353 /// TODO 354 } 355 356 /// Central request handler 357 void request(HTTPServerRequest req, HTTPServerResponse res) { 358 _parameters = parameters.dup; 359 _parameters["method"] = to!string(req.method); 360 _parameters["peer"] = req.peer; 361 _parameters["host"] = req.host; 362 _parameters["path"] = req.path; 363 _parameters["rootDir"] = req.rootDir; 364 _parameters["fullURL"] = req.fullURL.toString; 365 _parameters["queryString"] = req.queryString; 366 _parameters["json"] = req.json.toString; 367 _parameters["username"] = req.username; 368 _parameters["password"] = req.password; 369 370 /// Extract appPath from URL path 371 string appPath; 372 if (indexOf(req.path, rootPath) == 0) { 373 if (req.path.length > rootPath.length) appPath = req.path[indexOf(req.path, rootPath)+rootPath.length..$]; 374 else appPath = ""; 375 376 if ((appPath.length > 1) && (appPath[$-1..$] == "/")) appPath = appPath[0..$-1]; 377 } 378 _parameters["appPath"] = appPath; 379 debug writeln(_parameters); 380 381 if (req.path == rootPath) { 382 if ("index" in _objs) { 383 _index.request(req, res, _parameters.dup); 384 return; 385 } 386 if ("error" in _objs) { 387 _error.request(req, res, _parameters.dup); 388 return; 389 } 390 } 391 392 auto pathItems = appPath.split("/"); 393 writeln("PathItems: ", pathItems); 394 395 if (appPath in _objs) { // static urls 396 writeln("Found Obj -> ", appPath); 397 398 _objs[appPath].request(req, res, _parameters.dup); 399 return; 400 } 401 402 writeln("dynamic urls"); 403 foreach (path, obj; _objs) if (path.has("*", ":", "?")) { 404 // writeln(path, " vs ", appPath); 405 string[] objPathItems = path.split("/"); 406 string[] appPathItems = appPath.split("/"); 407 // writeln("ObjPathItems: (", objPathItems.length,") ", objPathItems); 408 // writeln("AppjPathItems: (", appPathItems.length,") ", appPathItems); 409 if (objPathItems.length > appPathItems.length) continue; 410 411 bool foundPage = true; 412 foreach (index, item; objPathItems) { 413 if (!foundPage) break; 414 415 if (index >= appPathItems.length) { 416 foundPage = false; 417 break; 418 } 419 420 if (item.has("*", ":", "?")) { 421 // dynamic part 422 if ((item.indexOf(":") == 0) && (item.length > 1)) { 423 _parameters[item[1..$]] = appPathItems[index]; 424 } 425 if ((item.indexOf("?") == 0) && (item.length > 1)) { 426 _parameters[item[1..$]] = appPathItems[index]; 427 } 428 } 429 else { 430 if (item != appPathItems[index]) { 431 foundPage = false; 432 break; 433 }} 434 } 435 if (foundPage) { 436 obj.request(req, res, _parameters.dup); 437 return; 438 } 439 } 440 441 _error.request(req, res); 442 } 443 } 444 auto H5App() { return new DH5App(); } 445 auto H5App(string aName) { return new DH5App(aName); } 446 auto H5App(string aName, string aRootPath) { return new DH5App(aName, aRootPath); } 447 448 unittest { 449 /// TODO 450 }