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