463 lines
11 KiB
JavaScript
463 lines
11 KiB
JavaScript
// The Antville Project
|
||
// http://code.google.com/p/antville
|
||
//
|
||
// Copyright 2001–2014 by the Workers of Antville.
|
||
//
|
||
// Licensed under the Apache License, Version 2.0 (the ``License'');
|
||
// you may not use this file except in compliance with the License.
|
||
// You may obtain a copy of the License at
|
||
//
|
||
// http://www.apache.org/licenses/LICENSE-2.0
|
||
//
|
||
// Unless required by applicable law or agreed to in writing, software
|
||
// distributed under the License is distributed on an ``AS IS'' BASIS,
|
||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
// See the License for the specific language governing permissions and
|
||
// limitations under the License.
|
||
|
||
/**
|
||
* @fileOverview Defines the Skin prototype
|
||
*/
|
||
|
||
markgettext('Skin');
|
||
markgettext('skin');
|
||
markgettext('a skin // accusative');
|
||
|
||
/**
|
||
*
|
||
* @param {String} group
|
||
* @param {String} name
|
||
* @returns {Skin}
|
||
*/
|
||
Skin.getByName = function(group, name) {
|
||
var skinSet = (res.handlers.layout || path.layout).skins.get(group);
|
||
if (skinSet) {
|
||
var skin = skinSet.get(name);
|
||
if (skin) {
|
||
return skin;
|
||
}
|
||
}
|
||
return null;
|
||
}
|
||
|
||
/**
|
||
* @param {String} prototype
|
||
* @param {String} name
|
||
* @param {Layout} layout
|
||
*/
|
||
Skin.add = function(prototype, name, layout) {
|
||
var skin = new Skin(prototype, name);
|
||
layout.skins.add(skin);
|
||
return skin;
|
||
}
|
||
|
||
/**
|
||
*
|
||
* @param {Skin} skin
|
||
*/
|
||
Skin.remove = function() {
|
||
if (this.constructor === Skin) {
|
||
this.setSource(this.source);
|
||
this.source = null;
|
||
this.remove();
|
||
}
|
||
return;
|
||
}
|
||
|
||
/**
|
||
* @returns {String[]}
|
||
*/
|
||
Skin.getPrototypeOptions = function() {
|
||
var prototypes = [];
|
||
var content, file;
|
||
var skinFiles = app.getSkinfilesInPath(res.skinpath);
|
||
for (var name in skinFiles) {
|
||
// Include root skins only for root site
|
||
if (name === root.constructor.name && res.handlers.site !== root) {
|
||
continue;
|
||
}
|
||
if (skinFiles[name][name]) {
|
||
prototypes.push({value: name, display: name});
|
||
}
|
||
}
|
||
return prototypes.sort(new String.Sorter('display'));
|
||
}
|
||
|
||
/**
|
||
* @name Skin
|
||
* @constructor
|
||
* @param {String} prototype
|
||
* @param {String} name
|
||
* @property {Date} created
|
||
* @property {User} creator
|
||
* @property {Layout} layout
|
||
* @property {Date} modified
|
||
* @property {User} modifier
|
||
* @property {String} prototype
|
||
* @property {String} source
|
||
* @extends HopObject
|
||
*/
|
||
Skin.prototype.constructor = function(prototype, name) {
|
||
HopObject.confirmConstructor(this);
|
||
this.prototype = prototype || String.EMPTY;
|
||
this.name = name || String.EMPTY;
|
||
this.creator = this.modifier = session.user;
|
||
this.created = this.modified = new Date;
|
||
return this;
|
||
}
|
||
|
||
/**
|
||
*
|
||
* @param {String} action
|
||
* @returns {Boolean}
|
||
*/
|
||
Skin.prototype.getPermission = function(action) {
|
||
switch (action) {
|
||
case '.':
|
||
case 'main':
|
||
return true;
|
||
|
||
case 'edit':
|
||
return res.handlers.layout.getPermission('main');
|
||
}
|
||
return this.isPersistent() && res.handlers.skins.getPermission('main');
|
||
}
|
||
|
||
/**
|
||
*
|
||
* @param {String} action
|
||
* @returns {String}
|
||
*/
|
||
Skin.prototype.href = function(action) {
|
||
res.push();
|
||
res.write(res.handlers.layout.skins.href());
|
||
res.write(this.prototype);
|
||
res.write('/');
|
||
res.write(this.name);
|
||
res.write('/');
|
||
action && (res.write(action));
|
||
return res.pop();
|
||
}
|
||
|
||
Skin.prototype.main_action = function() {
|
||
if (res.handlers.site === root) {
|
||
res.contentType = 'text/plain';
|
||
res.write(this.getSource());
|
||
return;
|
||
}
|
||
res.redirect(this.href('edit'));
|
||
return;
|
||
}
|
||
|
||
Skin.prototype.edit_action = function() {
|
||
if (req.postParams.save) {
|
||
try {
|
||
var url = this.href(req.action);
|
||
this.update(req.postParams);
|
||
res.message = gettext('The changes were saved successfully.');
|
||
if (req.postParams.save == 1) {
|
||
res.redirect(url);
|
||
} else {
|
||
res.redirect(res.handlers.layout.skins.href('modified'));
|
||
}
|
||
} catch (ex) {
|
||
res.message = ex;
|
||
app.log(ex);
|
||
}
|
||
}
|
||
res.data.action = this.href(req.action);
|
||
res.data.title = gettext('Edit {0}.{1}', this.prototype, this.name);
|
||
res.data.body = this.renderSkinAsString('$Skin#edit');
|
||
res.handlers.skins.renderSkin('$Skins#page');
|
||
return;
|
||
}
|
||
|
||
/**
|
||
*
|
||
* @param {Object} data
|
||
*/
|
||
Skin.prototype.update = function(data) {
|
||
if (this.isTransient()) {
|
||
res.handlers.layout.skins.add(this);
|
||
this.source = this.getSource(); // Copies the skin file source to database
|
||
}
|
||
if (this.prototype === 'Site' && this.name === 'page') {
|
||
var macro = 'response.body';
|
||
if (!createSkin(data.source).containsMacro(macro)) {
|
||
var macro = ['<code><%', macro, '%></code>'].join(String.EMPTY);
|
||
throw Error(gettext('The {0} macro is missing. It is essential for accessing the site and must be present in this skin.', macro));
|
||
}
|
||
}
|
||
this.setSource(data.source);
|
||
this.touch();
|
||
return;
|
||
}
|
||
|
||
Skin.prototype.reset_action = function() {
|
||
if (req.postParams.proceed) {
|
||
try {
|
||
Skin.remove.call(this);
|
||
res.message = gettext('{0} was successfully reset.', gettext('Skin'));
|
||
res.redirect(res.handlers.layout.skins.href('modified'));
|
||
} catch(ex) {
|
||
res.message = ex;
|
||
app.log(ex);
|
||
}
|
||
}
|
||
|
||
res.data.action = this.href(req.action);
|
||
res.data.title = gettext('Confirm Reset');
|
||
res.data.body = this.renderSkinAsString('$HopObject#confirm', {
|
||
text: this.getConfirmText()
|
||
});
|
||
res.handlers.site.renderSkin('Site#page');
|
||
return;
|
||
}
|
||
|
||
Skin.prototype.compare_action = function() {
|
||
var originalSkin = this.source || String.EMPTY;
|
||
var diff = this.getSource().diff(originalSkin);
|
||
if (!diff) {
|
||
res.message = gettext('No differences were found.');
|
||
} else {
|
||
res.push();
|
||
var param = {}, leftLineNumber = rightLineNumber = 0;
|
||
for (let line of diff) {
|
||
if (line.deleted) {
|
||
param.right = encode(line.value);
|
||
param.leftStatus = 'added';
|
||
param.rightStatus = '';
|
||
for (let i=0; i<line.deleted.length; i++) {
|
||
leftLineNumber += 1;
|
||
param.leftLineNumber = leftLineNumber;
|
||
param.rightLineNumber = '';
|
||
param.left = encode(line.deleted[i]);
|
||
param.right = '';
|
||
this.renderSkin('$Skin#difference', param);
|
||
}
|
||
}
|
||
if (line.inserted) {
|
||
param.left = encode(line.value);
|
||
param.leftStatus = '';
|
||
param.rightStatus = 'removed';
|
||
for (let i=0; i<line.inserted.length; i++) {
|
||
rightLineNumber += 1;
|
||
param.leftLineNumber = '';
|
||
param.rightLineNumber = rightLineNumber;
|
||
param.left = '';
|
||
param.right = encode(line.inserted[i]);
|
||
this.renderSkin('$Skin#difference', param);
|
||
}
|
||
}
|
||
if (line.value !== null) {
|
||
leftLineNumber += 1;
|
||
rightLineNumber += 1;
|
||
param.leftLineNumber = leftLineNumber;
|
||
param.rightLineNumber = rightLineNumber;
|
||
param.leftStatus = param.rightStatus = '';
|
||
param.left = encode(line.value);
|
||
param.right = param.left;
|
||
this.renderSkin('$Skin#difference', param);
|
||
}
|
||
}
|
||
res.data.diff = res.pop();
|
||
}
|
||
|
||
res.data.title = gettext('Compare {0}', this.getTitle());
|
||
res.data.body = this.renderSkinAsString('$Skin#compare');
|
||
res.handlers.skins.renderSkin('$Skins#page');
|
||
return;
|
||
}
|
||
|
||
/**
|
||
*
|
||
* @return {String}
|
||
*/
|
||
Skin.prototype.getTitle = function() {
|
||
return this.prototype + '.' + this.name;
|
||
}
|
||
|
||
/**
|
||
*
|
||
* @param {String} name
|
||
* @return {Object}
|
||
*/
|
||
Skin.prototype.getFormOptions = function(name) {
|
||
switch (name) {
|
||
case 'prototype':
|
||
return Skin.getPrototypeOptions();
|
||
}
|
||
}
|
||
|
||
Skin.prototype.getFormValue = function(name) {
|
||
switch (name) {
|
||
case 'source':
|
||
return req.data.source || this.getSource();
|
||
}
|
||
return HopObject.prototype.getFormValue.apply(this, arguments);
|
||
}
|
||
|
||
/**
|
||
* @returns {String}
|
||
*/
|
||
Skin.prototype.getSource = function() {
|
||
var skin;
|
||
// FIXME: Maintain skin inheritance by checking if we target the Site skin of root
|
||
if (res.handlers.site === root && this.prototype === 'Site') {
|
||
skin = this.getSubskin('Root');
|
||
if (skin) {
|
||
return skin.getSource();
|
||
}
|
||
}
|
||
skin = this.getSubskin();
|
||
if (skin) {
|
||
return skin.getSource();
|
||
}
|
||
return null;
|
||
}
|
||
|
||
/**
|
||
*
|
||
* @param {String} source
|
||
*/
|
||
Skin.prototype.setSource = function(source) {
|
||
// FIXME: Maintain skin inheritance by checking if we target the Site skin of root
|
||
var prototype = (res.handlers.site === root && this.prototype === 'Site') ? 'Root' : this.prototype;
|
||
var skin = this.getMainSkin(prototype);
|
||
if (!skin) {
|
||
return;
|
||
}
|
||
|
||
res.push();
|
||
if (source != null) {
|
||
res.writeln('<% #' + this.name + ' %>');
|
||
res.writeln(source.trim().replace(/(<%\s*)#/g, '$1// #'));
|
||
}
|
||
var subskins = skin.getSubskinNames();
|
||
for (var i in subskins) {
|
||
if (subskins[i] !== this.name) {
|
||
res.writeln('<% #' + subskins[i] + ' %>');
|
||
source = skin.getSubskin(subskins[i]).source;
|
||
source && res.writeln(source.trim());
|
||
}
|
||
}
|
||
source = res.pop();
|
||
|
||
var file = this.getStaticFile();
|
||
if (!file.exists()) {
|
||
file.getParentFile().mkdirs();
|
||
file.createNewFile();
|
||
}
|
||
var fos = new java.io.FileOutputStream(file);
|
||
var bos = new java.io.BufferedOutputStream(fos);
|
||
var writer = new java.io.OutputStreamWriter(bos, 'UTF-8');
|
||
writer.write(source);
|
||
writer.close();
|
||
bos.close();
|
||
fos.close();
|
||
|
||
this.clearCache();
|
||
return;
|
||
}
|
||
|
||
/**
|
||
*
|
||
* @returns {java.io.File}
|
||
*/
|
||
Skin.prototype.getStaticFile = function() {
|
||
// FIXME: Maintain skin inheritance by checking if we target the Site skin of root
|
||
var prototype = (res.handlers.site === root && this.prototype === 'Site') ? 'Root' : this.prototype;
|
||
return new java.io.File(res.skinpath[0], prototype + '/' + this.prototype + '.skin');
|
||
}
|
||
|
||
/**
|
||
* @param {String} prototype
|
||
* @returns {Skin}
|
||
*/
|
||
Skin.prototype.getMainSkin = function(prototype) {
|
||
var source, skinSet = app.getSkinfilesInPath(res.skinpath)[prototype || this.prototype];
|
||
if (skinSet) {
|
||
source = skinSet[this.prototype];
|
||
if (source !== null) {
|
||
return createSkin(source);
|
||
}
|
||
}
|
||
return null;
|
||
}
|
||
|
||
/**
|
||
*
|
||
* @param prototype
|
||
* @param name
|
||
* @returns {Skin}
|
||
*/
|
||
Skin.prototype.getSubskin = function(prototype, name) {
|
||
var mainSkin = this.getMainSkin(prototype);
|
||
if (mainSkin) {
|
||
return mainSkin.getSubskin(name || this.name);
|
||
}
|
||
return null;
|
||
}
|
||
|
||
/**
|
||
*
|
||
*/
|
||
Skin.prototype.render = function() {
|
||
return renderSkin(createSkin(this.getSource()));
|
||
}
|
||
|
||
/**
|
||
*
|
||
* @param {String} source
|
||
* @returns {Boolean}
|
||
*/
|
||
Skin.prototype.equals = function(source) {
|
||
// FIXME: The removal of linebreaks is necessary but it's not very nice
|
||
var re = /\r|\n/g;
|
||
var normalize = function(str) {
|
||
return str.replace(re, String.EMPTY);
|
||
}
|
||
return normalize(source) === normalize(this.getSource());
|
||
}
|
||
|
||
/**
|
||
* @returns {String}
|
||
*/
|
||
Skin.prototype.getConfirmText = function() {
|
||
return gettext('You are about to reset the skin {0}.{1}.',
|
||
this.prototype, this.name);
|
||
}
|
||
|
||
/**
|
||
* @returns {String}
|
||
*/
|
||
Skin.prototype.toString = function() {
|
||
return 'Skin #' + this._id + ': ' + this.prototype + '.' + this.name;
|
||
}
|
||
|
||
/**
|
||
*
|
||
*/
|
||
Skin.prototype.status_macro = function() {
|
||
return this.isTransient() ? 'inherited' : 'modified';
|
||
}
|
||
|
||
/**
|
||
*
|
||
*/
|
||
Skin.prototype.content_macro = function() {
|
||
return res.write(this.getSource());
|
||
}
|
||
|
||
Skin.prototype.macro_macro = function () {
|
||
var name = this.prototype === 'Global' ? this.name : this.prototype + '.' + this.name;
|
||
res.encode('<% ');
|
||
res.write('skin ');
|
||
res.write(quote(name, '\\s'));
|
||
res.encode(' %>');
|
||
return;
|
||
};
|
||
|
||
Skin.prototype.custom_macro = function () {
|
||
return this.isPersistent() && !this.source;
|
||
};
|