1 // The Antville Project
  2 // http://code.google.com/p/antville
  3 //
  4 // Copyright 2001-2007 by The Antville People
  5 //
  6 // Licensed under the Apache License, Version 2.0 (the ``License'');
  7 // you may not use this file except in compliance with the License.
  8 // You may obtain a copy of the License at
  9 //
 10 //    http://www.apache.org/licenses/LICENSE-2.0
 11 //
 12 // Unless required by applicable law or agreed to in writing, software
 13 // distributed under the License is distributed on an ``AS IS'' BASIS,
 14 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 15 // See the License for the specific language governing permissions and
 16 // limitations under the License.
 17 //
 18 // $Revision$
 19 // $LastChangedBy$
 20 // $LastChangedDate$
 21 // $URL$
 22 //
 23 
 24 /**
 25  * @fileOverview Defines the Skin prototype
 26  */
 27 
 28 
 29 /**
 30  * 
 31  * @param {String} group
 32  * @param {String} name
 33  * @returns {Skin}
 34  */Skin.getByName = function(group, name) {
 35    var skinSet = (res.handlers.layout || path.layout).skins.get(group);
 36    if (skinSet) {
 37       var skin = skinSet.get(name);
 38       if (skin) {
 39          return skin;
 40       } 
 41    }
 42    return null;
 43 }
 44 
 45 /**
 46  * 
 47  * @param {Skin} skin
 48  */
 49 Skin.remove = function(skin) {
 50    skin || (skin = this);
 51    if (skin.constructor === Skin) {
 52       if (skin.source) {
 53          skin.setSource(skin.source);
 54       } 
 55       skin.source = null;
 56       skin.remove();
 57    }
 58    return;
 59 }
 60 
 61 /**
 62  * @returns  {String[]}
 63  */
 64 Skin.getPrototypeOptions = function() {
 65    var prototypes = [];
 66    var content, file;
 67    var skinFiles = app.getSkinfilesInPath(res.skinpath);
 68    for (var name in skinFiles) {
 69       // Include root skins only for root site
 70       if (name === root.constructor.name && res.handlers.site !== root) {
 71          continue;
 72       }
 73       if (skinFiles[name][name]) {
 74          prototypes.push({value: name, display: name});
 75       }
 76    }
 77    return prototypes.sort(new String.Sorter("display"));
 78 }
 79 
 80 /**
 81  * @name Skin
 82  * @constructor
 83  * @param {String} prototype
 84  * @param {String} name
 85  * @property {Date} created
 86  * @property {User} creator
 87  * @property {Layout} layout
 88  * @property {Date} modified
 89  * @property {User} modifier
 90  * @property {String} prototype
 91  * @property {String} source
 92  * @extends HopObject
 93  */
 94 Skin.prototype.constructor = function(prototype, name) {
 95    this.prototype = prototype || String.EMPTY;
 96    this.name = name || String.EMPTY;
 97    this.creator = this.modifier = session.user;
 98    this.created = this.modified = new Date;
 99    return this;
100 }
101 
102 /**
103  * 
104  * @param {String} action
105  * @returns {Boolean}
106  */
107 Skin.prototype.getPermission = function(action) {
108    switch (action) {
109       case ".":
110       case "main":
111       return true;
112    }
113    return res.handlers.skins.getPermission("main");
114 }
115 
116 /**
117  * 
118  * @param {String} action
119  * @returns {String}
120  */
121 Skin.prototype.href = function(action) {
122    res.push();
123    res.write(res.handlers.layout.skins.href());
124    res.write(this.prototype);
125    res.write("/");
126    res.write(this.name);
127    res.write("/");
128    action && (res.write(action));
129    return res.pop();
130 }
131 
132 Skin.prototype.main_action = function() {
133    if (res.handlers.site === root) {
134       res.contentType = "text/plain";
135       res.write(this.getSource());
136       return;
137    }
138    res.redirect(this.href("edit"));
139    return;
140 }
141 
142 Skin.prototype.edit_action = function() {
143    if (req.postParams.save) {
144       try {
145          var url = this.href(req.action);
146          this.update(req.postParams);
147          res.message = gettext("The changes were saved successfully.");
148          if (req.postParams.save == 1) {
149             res.redirect(url);
150          } else {
151             res.redirect(res.handlers.layout.skins.href("modified"));
152          }
153       } catch (ex) {
154          res.message = ex;
155          app.log(ex);
156       }
157    }
158    res.data.action = this.href(req.action);
159    res.data.title = gettext('Edit skin {0}.{1} of {2}', this.prototype, 
160          this.name, res.handlers.site.title);
161    res.data.body = this.renderSkinAsString("$Skin#edit");
162    res.handlers.skins.renderSkin("$Skins#page");
163    return;
164 }
165 
166 /**
167  * 
168  * @param {Object} data
169  */
170 Skin.prototype.update = function(data) {
171    if (this.isTransient()) {
172       res.handlers.layout.skins.add(this);
173       this.source = this.getSource();
174    }
175    this.setSource(data.source);   
176    this.touch();
177    return;
178 }
179 
180 Skin.prototype.reset_action = function() {
181    if (req.postParams.proceed) {
182       try {
183          var str = this.toString();
184          res.debug(this.source);
185          Skin.remove(this);
186          res.message = gettext("{0} was successfully reset.", str);
187          res.redirect(res.handlers.layout.skins.href("modified"));
188       } catch(ex) {
189          res.message = ex;
190          app.log(ex);
191       }
192    }
193 
194    res.data.action = this.href(req.action);
195    res.data.title = gettext("Confirm reset of {0}", this);
196    res.data.body = this.renderSkinAsString("$HopObject#confirm", {
197       text: gettext('You are about to reset {0}.', this)
198    });
199    res.handlers.skins.renderSkin("$Skins#page");
200    return;
201 }
202 
203 Skin.prototype.compare_action = function() {
204    var originalSkin = this.source || "";
205    var diff = originalSkin.diff(this.getSource());
206    if (!diff) {
207       res.data.status = gettext("No differences were found");
208    } else {
209       res.push();
210       var sp = new Object();
211       for (var i in diff) {
212          var line = diff[i];
213          sp.num = line.num;
214          if (line.deleted) {
215             sp.status = "DEL";
216             sp["class"] = "removed";
217             for (var j=0;j<line.deleted.length;j++) {
218                sp.num = line.num + j;
219                sp.line = encode(line.deleted[j]);
220                this.renderSkin("$Skin#difference", sp);
221             }
222          }
223          if (line.inserted) {
224             sp.status = "ADD";
225             sp["class"] = "added";
226             for (var j=0;j<line.inserted.length;j++) {
227                sp.num = line.num + j;
228                sp.line = encode(line.inserted[j]);
229                this.renderSkin("$Skin#difference", sp);
230             }
231          }
232          if (line.value != null) {
233             sp.status = " ";
234             sp["class"] = "line";
235             sp.line = encode(line.value);
236             this.renderSkin("$Skin#difference", sp);
237          }
238       }
239       res.data.diff = res.pop();
240    }
241    res.data.title = gettext("Compare versions of skin {0}.{1}", 
242          this.prototype, this.name);
243    res.data.body = this.renderSkinAsString("$Skin#compare");
244    res.handlers.skins.renderSkin("$Skins#page");
245    return;
246 }
247 
248 /**
249  * 
250  * @param {String} name
251  * @return {Object}
252  */
253 Skin.prototype.getFormOptions = function(name) {
254    switch (name) {
255       case "prototype":
256       return Skin.getPrototypeOptions();
257    }
258 }
259 
260 /**
261  * @returns {String}
262  */
263 Skin.prototype.getSource = function() {
264    var skinSet = app.getSkinfilesInPath(res.skinpath)[this.prototype];
265    if (skinSet) {
266       var mainSkin = skinSet[this.prototype];
267       if (mainSkin) {
268          var skin = createSkin(mainSkin).getSubskin(this.name);
269          if (skin) {
270             return skin.getSource();
271          }
272       }
273    }
274    return null; //String.EMPTY;
275 }
276 
277 /**
278  * 
279  * @param {String} source
280  */
281 Skin.prototype.setSource = function(source) {
282    var skin = this.getMainSkin();
283    if (!skin) {
284       return;
285    }
286 
287    res.push();
288    if (source != null) {
289       res.writeln("<% #" + this.name + " %>");
290       res.writeln(source.trim().replace(/(<%\s*)#/g, "$1// #"));
291    }
292    var subskins = skin.getSubskinNames();
293    for (var i in subskins) {
294       if (subskins[i] !== this.name) {
295          res.writeln("<% #" + subskins[i] + " %>");
296          source = skin.getSubskin(subskins[i]).source;
297          source && res.writeln(source.trim());
298       }
299    }
300    source = res.pop();
301 
302    var file = this.getStaticFile(res.skinpath[0], skin);   
303    if (!file.exists()) {
304       file.getParentFile().mkdirs();
305       file.createNewFile();
306    }
307    var fos = new java.io.FileOutputStream(file);
308    var bos = new java.io.BufferedOutputStream(fos);
309    var writer = new java.io.OutputStreamWriter(bos, "UTF-8");
310    writer.write(source);
311    writer.close();
312    bos.close();
313    fos.close();
314 
315    this.clearCache();
316    return;
317 }
318 
319 /**
320  * 
321  * @param {String} fpath
322  * @returns {java.io.File}
323  */
324 Skin.prototype.getStaticFile = function(fpath) {
325    return new java.io.File(fpath, this.prototype + "/" + 
326          this.prototype + ".skin");
327 }
328 
329 /**
330  * @returns {Skin}
331  */
332 Skin.prototype.getMainSkin = function() {
333    var skinSet = app.getSkinfilesInPath(res.skinpath)[this.prototype];
334    if (skinSet && skinSet[this.prototype]) {
335       return createSkin(skinSet[this.prototype]);
336    }
337    return null;
338 }
339 
340 /**
341  * 
342  */
343 Skin.prototype.render = function() {
344    return renderSkin(createSkin(this.getSource()));
345 }
346 
347 /**
348  * 
349  * @param {String} source
350  * @returns {Boolean}
351  */
352 Skin.prototype.equals = function(source) {
353    // FIXME: The removal of linebreaks is necessary but it's not very nice
354    var re = /\r|\n/g;
355    var normalize = function(str) {
356       return str.replace(re, String.EMPTY);
357    }
358    return normalize(source) === normalize(this.getSource());
359 }
360 
361 /**
362  * @returns {String}
363  */
364 Skin.prototype.toString = function() {
365    return "Skin #" + this._id + ": " + this.prototype + "." + this.name;
366 }
367 
368 /**
369  * 
370  */
371 Skin.prototype.status_macro = function() {
372    return this.isTransient() ? "inherited" : "modified"; 
373 }
374 
375 /**
376  * 
377  */
378 Skin.prototype.content_macro = function() {
379    return res.write(this.getSource());
380 }
381