1 /**
2 * Copyright © DiamondMVC 2018
3 * License: MIT (https://github.com/DiamondMVC/emeralD/blob/master/LICENSE)
4 * Author: Jacob Jensen (bausshf)
5 */
6 module templates;
7 
8 import std.file : dirEntries, SpanMode, isDir, isFile, mkdirRecurse, rmdirRecurse, write, readText, read, exists, append;
9 import std.path : baseName, dirName;
10 import std.stdio : File;
11 import std.string : format, strip;
12 import std.array : split, replace;
13 import std.algorithm : map, endsWith;
14 import std.zip : ZipArchive;
15 
16 import meta : thisExeDir;
17 
18 /// Collection of templates.
19 private string[string][string] _templates;
20 
21 /// Loads the template paths.
22 void loadTemplates()
23 {
24   import std.stdio : writeln;
25   writeln(thisExeDir);
26   foreach (string root; dirEntries(thisExeDir ~ "/templates", SpanMode.shallow))
27   {
28     if (root.isDir)
29     {
30       auto rootName = baseName(root);
31 
32       foreach (string templatePath; dirEntries(root, SpanMode.shallow))
33       {
34         if (templatePath.isFile)
35         {
36           auto templateName = baseName(templatePath);
37 
38           _templates[rootName][templateName] = templatePath;
39         }
40       }
41     }
42   }
43 }
44 
45 /// Loads all remote templates.
46 void loadRemoteTemplates()
47 {
48   auto templates = File(thisExeDir ~ "/remotetemplates.emd");
49 
50   foreach (line; templates.byLine.map!(l => l.replace("\r", "")))
51   {
52     if (!line || !line.length)
53     {
54       continue;
55     }
56 
57     auto data = line.split('|');
58 
59     if (data.length != 3)
60     {
61       continue;
62     }
63 
64     immutable root = cast(immutable)data[0];
65     immutable name = cast(immutable)data[1];
66     immutable url = cast(immutable)data[2];
67 
68     if (root !in _templates || name !in _templates[root])
69     {
70       import std.net.curl : get, HTTP;
71 
72       auto templateResult = cast(string)get!HTTP(url);
73 
74       if (templateResult)
75       {
76         auto rootPath = thisExeDir ~ "/templates/" ~ root;
77 
78         if (!exists(rootPath))
79         {
80           mkdirRecurse(rootPath);
81         }
82 
83         auto path = rootPath ~ "/" ~ name;
84 
85         write(path, templateResult);
86 
87         _templates[root][name] = path;
88       }
89     }
90   }
91 }
92 
93 /**
94 * Reads a template.
95 * Params:
96 *   root = The root of the template.
97 *   name = The name of the template.
98 */
99 string readTemplate(string root, string name)
100 {
101   auto templates = _templates.get(root, null);
102 
103   if (!templates)
104   {
105     return null;
106   }
107 
108   auto path = templates.get(name, null);
109 
110   if (!path)
111   {
112     return null;
113   }
114 
115   return readText(path);
116 }
117 
118 /**
119 * Adds a remote template.
120 * Params:
121 *   root = The root of the template.
122 *   name = The name of the template.
123 *   url =  The url of the template.
124 */
125 void addRemoteTemplate(string root, string name, string url)
126 {
127   append(thisExeDir ~ "/remotetemplates.emd", "%s|%s|%s\r\n".format(root, name, url));
128 }
129 
130 /**
131 * Adds a remote scaffolding archive.
132 * Params:
133 *   name = The name of the scaffolding archive.
134 *   url =  The url of the archive. (Must be zip.)
135 */
136 void addRemoteScaffold(string name, string url)
137 {
138   import std.net.curl : download, HTTP;
139 
140   if (!url.endsWith(".zip"))
141   {
142     return;
143   }
144 
145   auto rootPath = thisExeDir ~ "/scaffold/" ~ name;
146   auto path = rootPath ~ "/__archive.zip";
147 
148   if (!exists(path))
149   {
150     rmdirRecurse(rootPath);
151     rmdirRecurse(rootPath);
152   }
153 
154   download!HTTP(url, path);
155 
156   auto zip = new ZipArchive(read(path));
157 
158 
159   foreach (name, am; zip.directory)
160   {
161     zip.expand(am);
162 
163     auto filePath = (rootPath ~ "/" ~ name).replace("\\", "/");
164     auto dir = dirName(filePath).replace("\\", "/");
165 
166     if (!exists(dir))
167     {
168       mkdirRecurse(dir);
169     }
170 
171     auto base = baseName(filePath);
172     auto data = base.split(".");
173 
174     if (data.length >= 2 && data[0] && data[0].strip().length)
175     {
176       write(filePath, am.expandedData);
177     }
178   }
179 }