1 /**
2 * Copyright © Yurai Web Framework 2021
3 * License: MIT (https://github.com/YuraiWeb/yurai/blob/main/LICENSE)
4 * Author: Jacob Jensen (bausshf)
5 */
6 module yurai.views.view;
7 
8 import yurai.core;
9 import yurai.views.viewresult;
10 import yurai.external.iserver;
11 
12 public abstract class View
13 {
14   private:
15   IHttpRequest _request;
16   IHttpResponse _response;
17   IServer _server;
18   string _content;
19   string[string] _placeholders;
20   string _name;
21   string _contentType;
22   string _section;
23   string[] _routes;
24 
25   protected:
26   this(IHttpRequest request, IHttpResponse response, string[] routes = null)
27   {
28     this(null, request, response, null);
29   }
30 
31   this(string name, IHttpRequest request, IHttpResponse response, string[] routes = null)
32   {
33     _name = name;
34 
35     _request = request;
36     _response = response;
37 
38     _server = _request.server;
39 
40     _routes = routes;
41 
42     _placeholders["doctype"] = "<!doctype html>";
43   }
44 
45   @property
46   {
47     IHttpRequest request() { return _request; }
48 
49     IHttpResponse response() { return _response; }
50 
51     IServer server() { return _server; }
52 
53     string name() { return _name; }
54 
55     string[] routes() { return _routes; }
56   }
57 
58   void setContentType(string contentType)
59   {
60     _contentType = contentType;
61   }
62 
63   ViewResult finalizeContent(string layout = null, bool processLayout = true)
64   {
65     if (layout && layout.length && processLayout)
66     {
67       import yurai.prebuild.viewsmap;
68 
69       auto layoutView = getView(layout, _request, _response);
70 
71       if (layoutView)
72       {
73         layoutView._name = _name;
74 
75         foreach (k,v; _placeholders)
76         {
77           layoutView.setPlaceholder(k, v);
78         }
79 
80         layoutView.setPlaceholder("view", _content);
81         return layoutView.generate(true);
82       }
83     }
84 
85     return new ViewResult(_content ? _content : "", _contentType);
86   }
87 
88   void renderPartial(string name)
89   {
90     import yurai.prebuild.viewsmap;
91 
92     auto partialView = getView(name, _request, _response);
93 
94     if (partialView)
95     {
96       foreach (k,v; _placeholders)
97       {
98         partialView.setPlaceholder(k, v);
99       }
100 
101       auto result = partialView.generateFinal(true);
102 
103       if (!result)
104       {
105         throw new Exception("Failed to render partial view.");
106       }
107 
108       append(result.content ? result.content : "");
109     }
110   }
111 
112   void renderPartialModel(string name, T)(T model)
113   {
114     mixin("import yurai.prebuild.views : view_" ~ name ~ ";");
115     import yurai.prebuild.viewsmap;
116 
117     mixin("auto partialView = cast(view_" ~ name ~ ")getView(name, _request, _response);");
118 
119     if (partialView)
120     {
121       foreach (k,v; _placeholders)
122       {
123         partialView.setPlaceholder(k, v);
124       }
125 
126       auto result = partialView.generateModel(model);
127 
128       if (!result)
129       {
130         throw new Exception("Failed to render partial view.");
131       }
132 
133       append(result.content ? result.content : "");
134     }
135   }
136 
137   void setPlaceholder(string key, string value)
138   {
139     _placeholders[key] = value;
140   }
141 
142   string getPlaceholder(string key, string defaultValue = "")
143   {
144     return _placeholders.get(key, defaultValue ? defaultValue : "");
145   }
146 
147   void appendPlaceholder(string key, string value)
148   {
149     auto content = getPlaceholder(key);
150 
151     content ~= value;
152 
153     setPlaceholder(key, content);
154   }
155 
156   void setSection(string name)
157   {
158     _section = name == "*" ? null : ("section: " ~ name);
159   }
160 
161   void escaped(T)(T data)
162   {
163     import std.conv : to;
164     import yurai.security.html;
165 
166     auto s = to!string(data);
167 
168     s = escapeHtml(s);
169 
170     append(s);
171   }
172 
173   void append(T)(T data)
174   {
175     import std.conv : to;
176 
177     import std.stdio;
178 
179     auto s = to!string(data);
180 
181     if (_section && _section != "*")
182     {
183       appendPlaceholder(_section, s);
184     }
185     else
186     {
187       _content ~= s;
188     }
189   }
190 
191   public:
192   abstract ViewResult generate(bool processLayout);
193 
194   abstract ViewResult generateFinal(bool processLayout);
195 }