antville/code/Site/Site.js

1146 lines
31 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// The Antville Project
// http://code.google.com/p/antville
//
// Copyright 20012014 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 Site prototype.
*/
markgettext('Site');
markgettext('site');
markgettext('a site // accusative');
this.handleMetadata('archiveMode');
this.handleMetadata('callbackMode');
this.handleMetadata('callbackUrl');
this.handleMetadata('closed');
this.handleMetadata('commentMode');
this.handleMetadata('configured');
this.handleMetadata('deleted');
this.handleMetadata('export');
this.handleMetadata('imageDimensionLimits');
this.handleMetadata('import_id');
this.handleMetadata('job');
this.handleMetadata('locale');
this.handleMetadata('notes');
this.handleMetadata('notificationMode');
this.handleMetadata('notified');
this.handleMetadata('pageSize');
this.handleMetadata('pageMode');
this.handleMetadata('spamfilter');
this.handleMetadata('tagline');
this.handleMetadata('timeZone');
this.handleMetadata('title');
this.handleMetadata('trollFilter');
/**
* Ffunction
* @returns {String[]}
* @see defineConstants
*/
Site.getStatus = defineConstants(Site, markgettext('Blocked'),
markgettext('Regular'), markgettext('Trusted'));
/**
* @function
* @returns {String[]}
* @see defineConstants
*/
Site.getModes = defineConstants(Site, markgettext('Deleted'),
markgettext('Closed'), markgettext('Restricted'),
markgettext('Public'), markgettext('Open'));
/**
* @function
* @returns {String[]}
* @see defineConstants
*/
Site.getPageModes = defineConstants(Site, markgettext('days') /* ,
markgettext('stories') */ );
/**
* @function
* @returns {String[]}
* @see defineConstants
*/
Site.getCommentModes = defineConstants(Site, markgettext('disabled'),
markgettext('enabled'));
/**
* @function
* @returns {String[]}
* @see defineConstants
*/
Site.getArchiveModes = defineConstants(Site, markgettext('closed'),
markgettext('public'));
/**
* @function
* @returns {String[]}
* @see defineConstants
*/
Site.getNotificationModes = defineConstants(Site, markgettext('Nobody'),
markgettext('Owner'), markgettext('Manager'), markgettext('Contributor'),
markgettext('Subscriber'));
/**
* @function
* @returns {String[]}
* @see defineConstants
*/
Site.getCallbackModes = defineConstants(Site, markgettext('disabled'),
markgettext('enabled'));
/**
*
*/
Site.getDeletionDate = function() {
return new Date(Date.now() + Date.ONEDAY * 0);
};
/**
* @param {String} name A unique identifier also used in the URL of a site
* @param {String} title An arbitrary string branding a site
* @param {User} user
*/
Site.add = function(data, user) {
HopObject.confirmConstructor(Site);
var now = new Date;
var user = user || session.user || {};
var site = new Site;
if (!data.name) {
throw Error(gettext('Please enter a name for your new site.'));
}
if (data.name.length > 30) {
throw Error(gettext('The chosen name is too long. Please enter a shorter one.'));
}
var name = stripTags(decodeURIComponent(data.name));
if (name !== data.name || /[^\u00a0-\uffff\w\-]/.test(data.name)) {
// We check if name can be used in vhost environment by allowing all Unicode characters
// but only ASCII letters A—z, digits 0—9, the underscore “_” and the hyphen “-”.
throw Error(gettext('Please avoid special characters or HTML code in the name field.'));
} else if (name !== root.getAccessName(name)) {
throw Error(gettext('There already is a site with this name.'));
}
site.name = java.net.IDN.toASCII.constructor === Function ?
java.net.IDN.toASCII(name, java.net.IDN.ALLOW_UNASSIGNED) : name;
site.title = data.title || name;
site.map({
configured: now,
created: now,
creator: user,
modified: now,
modifier: user,
status: user.status === User.PRIVILEGED ? Site.TRUSTED : user.status,
mode: Site.CLOSED,
commentMode: Site.ENABLED,
archiveMode: Site.PUBLIC,
spamFilter: String.EMPTY,
trollFilter: String.EMPTY
});
site.update(site);
Layout.add(site);
root.add(site);
return site;
}
/**
*
* @param {Site} site
*/
Site.remove = function() {
if (this.constructor !== Site || this === root) return;
const sql = new Sql();
const id = this._id;
const dir = this.getStaticFile();
app.log('Removing site #' + id);
this.remove();
root.cache.sites = null;
sql.execute('delete from site where id = $0', id);
sql.execute('delete from content where site_id = $0', id);
sql.execute('delete from file where site_id = $0', id);
sql.execute('delete from membership where site_id = $0', id);
sql.execute("delete from image where parent_type = 'Site' and parent_id = $0", id);
sql.execute("delete from metadata where parent_type = 'Site' and parent_id = $0", id);
sql.execute('delete from skin where layout_id in (select id from layout where site_id = $0)', id);
sql.execute('delete from layout where site_id = $0', id);
sql.execute('delete from tag_hub where tag_id in (select id from tag where site_id = $0)', id);
sql.execute('delete from tag where site_id = $0', id);
let subQuery = 'select id from poll where site_id';
sql.execute('delete from vote where choice_id in (select id from choice where poll_id in ($0 = $1))', subQuery, id);
sql.execute('delete from choice where poll_id in ($0 = $1)', subQuery, id);
sql.execute('delete from poll where site_id = $0', id);
dir.removeDirectory();
};
/**
*
* @param {String} name
* @returns {Site}
*/
Site.getByName = function(name) {
return root.get(name);
}
/**
*
* @param {String} mode
* @returns {Boolean}
*/
Site.require = function(mode) {
var modes = [Site.CLOSED, Site.RESTRICTED, Site.PUBLIC, Site.OPEN];
return modes.indexOf(res.handlers.site.mode) >= modes.indexOf(mode);
}
/**
* A Site object is the basic container of Antville.
* @name Site
* @constructor
* @property {Tag[]} $tags
* @property {Archive} archive
* @property {String} archiveMode The way the archive of a site is displayed
* @property {String} commentMode The way comments of a site are displayed
* @property {Date} created The date and time of site creation
* @property {User} creator A reference to a user who created a site
* @property {Date} deleted
* @property {String} export
* @property {Files} files
* @property {Tags} galleries
* @property {Images} images
* @property {String} import_id
* @property {String} job
* @property {Layout} layout
* @property {String} locale The place and language settings of a site
* @property {Members} members
* @property {Metadata} metadata
* @property {String} mode The access level of a site
* @property {Date} modified The date and time when a site was last modified
* @property {User} modifier A reference to a user who modified a site
* @property {String} notificationMode The way notifications are sent from a site
* @property {Date} notified The date and time of the last notification sent to
* the owners of a site
* @property {String} pageMode The way stories of a site are displayed
* @property {Number} pageSize The amount of stories to be displayed simultaneously
* @property {Polls} polls
* @property {String} status The trust level of a site
* @property {Stories} stories
* @property {String} tagline An arbitrary text describing a site
* @property {Tags} tags
* @property {String} timeZone The time and date settings of a site
* @extends HopObject
*/
Site.prototype.constructor = function() {
HopObject.confirmConstructor(this);
return this;
}
/**
*
* @param {String} action
* @returns {Boolean}
*/
Site.prototype.getPermission = function(action) {
switch (action) {
case 'backup.js':
case 'contact':
case 'main.js':
case 'main.css':
case 'error':
case 'notfound':
case 'robots.txt':
case 'search.xml':
case 'user.js':
return true;
}
switch (action) {
case '.':
case 'main':
case 'comments.xml':
case 'rss.xml':
case 'rss.xsl':
case 'search':
case 'stories.xml':
return Site.require(Site.PUBLIC) ||
(Site.require(Site.RESTRICTED) &&
Membership.require(Membership.CONTRIBUTOR)) ||
((Site.require(Site.DELETED) || Site.require(Site.CLOSED)) &&
Membership.require(Membership.OWNER)) ||
User.require(User.PRIVILEGED);
case 'delete':
return this !== root && this.getPermission('edit');
case 'edit':
case 'export':
case 'referrers':
return Membership.require(Membership.OWNER) ||
User.require(User.PRIVILEGED);
case 'subscribe':
return Site.require(Site.PUBLIC) &&
User.require(User.REGULAR) &&
!Membership.require(Membership.SUBSCRIBER);
case 'unsubscribe':
var membership = Membership.getByName(session.user.name, this);
return membership ? membership.getPermission('delete') : false;
case 'import':
case '$Site#admin':
return User.require(User.PRIVILEGED);
}
}
Site.prototype.main_action = function() {
this.renderPage({
type: 'website',
schema: 'http://schema.org/WebSite',
title: this.getTitle(),
description: this.tagline || String.EMPTY,
body: this.renderSkinAsString(this.mode === Site.DELETED ? '$Site#deleted' : 'Site#main'),
images: [(this.layout.images.get('favicon') || Images.Default['favicon.png']).getUrl()],
links: this.renderSkinAsString('$Site#links')
});
this.log();
return;
};
Site.prototype.edit_action = function() {
if (req.postParams.save) {
try {
this.update(req.postParams);
res.message = gettext('The changes were saved successfully.');
res.redirect(this.href(req.action));
} catch (ex) {
res.message = ex;
app.log(ex);
}
}
res.data.action = this.href(req.action);
res.data.title = gettext('Settings');
res.data.body = this.renderSkinAsString('$Site#edit');
this.renderSkin('Site#page');
return;
}
Site.prototype.delete_action = function () {
if (req.postParams.proceed) {
if (this.stories.size() + this.images.size() + this.files.size() + this.polls.size() < 1) {
// If a site contains no content, delete it immediately
HopObject.prototype.delete_action.call(this);
} else {
// Otherwise, queue for deletion
this.deleted = Site.getDeletionDate();
this.mode = Site.DELETED;
res.message = gettext('The site {0} is queued for deletion.', this.name);
}
this.log(root, 'Deleted site ' + this.name);
res.redirect(this.href());
} else {
HopObject.prototype.delete_action.call(this);
}
}
/**
*
* @param {String} name
* @returns {Object}
*/
Site.prototype.getFormOptions = function(name) {
switch (name) {
case 'archiveMode':
return Site.getArchiveModes();
case 'commentMode':
return Site.getCommentModes();
case 'locale':
return getLocales(this.getLocale());
case 'layout':
return this.getLayouts();
case 'mode':
return Site.getModes();
case 'notificationMode':
return Site.getNotificationModes();
case 'pageMode':
return Site.getPageModes();
case 'status':
return Site.getStatus();
case 'timeZone':
return getTimeZones(this.getLocale());
case 'callbackMode':
return Site.getCallbackModes();
default:
return HopObject.prototype.getFormOptions.apply(this, arguments);
}
};
Site.prototype.getFormValue = function (name) {
switch (name) {
case 'maxImageWidth':
return this.imageDimensionLimits[0];
case 'maxImageHeight':
return this.imageDimensionLimits[1];
case 'trollFilter':
var trolls = this.getMetadata('trollFilter');
return trolls ? trolls.join('\n') : String.EMPTY;
}
return HopObject.prototype.getFormValue.apply(this, arguments);
};
/**
* @returns {String}
*/
Site.prototype.getConfirmText = function() {
return gettext('You are about to delete the site {0}.',
this.getTitle());
};
Site.prototype.getConfirmExtra = function () {
return this.renderSkinAsString('$Site#delete');
};
/**
*
* @param {Object} data
*/
Site.prototype.update = function(data) {
if (this.mode !== data.mode) {
this.deleted = data.mode === Site.DELETED ? new Date() : null;
}
data.maxImageWidth = Math.abs(data.maxImageWidth) || Infinity;
data.maxImageHeight = Math.abs(data.maxImageHeight) || Infinity;
this.map({
archiveMode: data.archiveMode || Site.CLOSED,
callbackMode: data.callbackMode || Site.DISABLED,
callbackUrl: data.callbackUrl || this.callbackUrl || String.EMPTY,
imageDimensionLimits: [data.maxImageWidth, data.maxImageHeight],
commentMode: data.commentMode || Site.DISABLED,
locale: data.locale || root.getLocale().toString(),
mode: data.mode || Site.CLOSED,
notificationMode: data.notificationMode || Site.NOBODY,
pageMode: data.pageMode || Site.DAYS,
pageSize: parseInt(data.pageSize, 10) || 3,
spamfilter: data.spamfilter || String.EMPTY,
tagline: data.tagline || String.EMPTY,
title: stripTags(data.title) || this.name,
timeZone: data.timeZone || root.getTimeZone().getID(),
trollFilter: data.trollFilter ? data.trollFilter.split(/\r\n|\r|\n/).filter(function (item) {
return item.length > 0;
}) : []
});
if (User.require(User.PRIVILEGED)) {
this.status = data.status;
this.notes = data.notes;
}
this.configured = new Date;
this.modifier = session.user;
this.clearCache();
return;
}
Site.prototype.main_css_action = function() {
res.contentType = 'text/css';
res.dependsOn(String(Root.VERSION));
res.dependsOn(this.layout.modified);
HopObject.confirmConstructor(Skin);
res.dependsOn((new Skin('Site', 'stylesheet')).getStaticFile().lastModified());
res.digest();
res.push();
this.renderSkin('$Site#stylesheet');
this.renderSkin('Site#stylesheet');
var css = res.pop();
try {
lessParser.parse(css, function(error, less) {
if (error) throw error;
res.write(less.toCSS());
});
} catch (ex) {
var message = [ex.type, 'error in line', ex.line, 'column', ex.column, 'of', ex.filename + ':', ex.message/*, '/', ex.extract[1]*/].join(String.SPACE);
res.writeln('/**');
res.writeln('LESS parser got exception when rendering custom CSS.');
res.writeln(message)
res.writeln('**/');
console.error(message);
res.write(css);
}
return;
}
Site.prototype.main_js_action = function() {
res.contentType = 'text/javascript';
res.dependsOn(String(Root.VERSION));
HopObject.confirmConstructor(Skin);
res.dependsOn((new Skin('Site', 'javascript')).getStaticFile().lastModified());
res.digest();
var file = new java.io.File(root.getStaticFile('../../scripts/main.min.js'));
res.writeln(Packages.org.apache.commons.io.FileUtils.readFileToString(file, 'utf-8'));
this.renderSkin('$Site#javascript');
Claustra.invoke(req.path);
return;
}
// FIXME: compatibility
Site.prototype.user_js_action = function() {
res.contentType = 'text/javascript';
HopObject.confirmConstructor(Skin);
res.dependsOn((new Skin('Site', 'javascript')).getStaticFile().lastModified());
res.digest();
this.renderSkin('Site#javascript');
return;
}
Site.prototype.backup_js_action = function() {
if (req.isPost()) {
var data = session.data.backup = {};
for (var key in req.postParams) {
data[key] = req.postParams[key];
}
} else {
res.contentType = 'text/javascript';
res.write(JSON.stringify(session.data.backup || {}));
session.data.backup = null;
}
return;
}
Site.prototype.rss_xml_action = function() {
res.contentType = 'application/rss+xml';
res.dependsOn(this.modified);
res.digest();
this.renderXml(this.stories.union);
return;
}
Site.prototype.stories_xml_action = function() {
res.contentType = 'application/rss+xml';
res.dependsOn(this.modified);
res.digest();
this.renderXml(this.stories.recent);
return;
}
Site.prototype.comments_xml_action = function() {
res.contentType = 'application/rss+xml';
res.dependsOn(this.modified);
res.digest();
this.renderXml(this.stories.comments);
return;
}
Site.prototype.search_xml_action = function() {
res.contentType = 'application/opensearchdescription+xml';
this.renderSkin('$Site#opensearchdescription');
return;
}
Site.prototype.renderPage = function (parts) {
for (var key in parts) {
res.data[key] = parts[key];
}
if (parts.images) {
res.data.images = parts.images.map(function (url) {
return html.tagAsString('meta', {
property: 'og:image',
name: 'twitter:image',
itemprop: 'image',
content: url
});
}).join('\n');
}
if (parts.videos) {
res.data.videos = parts.videos.map(function (url) {
return html.tagAsString('meta', {
property: 'og:video',
content: url
});
}).join('\n');
}
res.data.meta = this.renderSkinAsString('$Site#meta');
this.renderSkin('Site#page');
};
/**
*
* @param {Story[]} collection
*/
Site.prototype.renderXml = function(collection) {
if (!collection) collection = this.stories.recent;
var now = new Date;
var feed = new rome.SyndFeedImpl();
feed.setFeedType('rss_2.0');
feed.setLink(this.href());
feed.setTitle(this.title);
feed.setDescription(this.tagline || String.EMPTY);
feed.setLanguage(this.locale.replace('_', '-'));
feed.setPublishedDate(now);
var entry, entryInfo, entryModules;
var enclosure, enclosures, keywords;
var entries = new java.util.ArrayList();
var description;
var list = collection.constructor === Array ? collection : collection.list(0, 25);
for each (var item in list) {
entry = new rome.SyndEntryImpl();
entry.setTitle(item.title || formatDate(item.created, 'date'));
entry.setLink(item.href());
entry.setAuthor(item.creator.name);
entry.setPublishedDate(item.created);
if (item.text) {
description = new rome.SyndContentImpl();
description.setType('text/html');
// FIXME: Work-around for org.jdom.IllegalDataException caused by some ASCII control characters
description.setValue(item.format_filter(item.text).replace(/[\x00-\x1f^\x0a^\x0d]/g, String.EMPTY));
entry.setDescription(description);
if (item.constructor === Story) {
var content = new rome.SyndContentImpl();
content.setType('text/html');
content.setValue(item.renderSkinAsString('$Story#instant', {
text: stripTags(description.getValue())
}));
var contents = new java.util.ArrayList();
contents.add(content);
entry.setContents(contents);
}
}
entries.add(entry);
}
feed.setEntries(entries);
var output = new rome.SyndFeedOutput();
res.servletResponse.setCharacterEncoding('utf-8');
output.output(feed, res.servletResponse.writer);
return;
}
Site.prototype.rss_xsl_action = function() {
res.charset = 'UTF-8';
res.contentType = 'text/xml';
renderSkin('Global#xslStylesheet');
return;
}
Site.prototype.referrers_action = function() {
if (req.data.permanent && this.getPermission('edit')) {
var urls = req.data.permanent_array;
res.push();
res.write(this.getMetadata('spamfilter'));
for (var i in urls) {
res.write('\n');
res.write(urls[i].replace(/\?/g, '\\\\?'));
}
this.setMetadata('spamfilter', res.pop());
res.redirect(req.data.http_referer);
return;
}
res.push();
var self = this;
var sql = new Sql;
sql.retrieve(Sql.REFERRERS, 'Site', this._id);
sql.traverse(function() {
if (this.requests && this.referrer) {
this.text = encode(this.referrer.head(50));
this.referrer = encode(this.referrer);
self.renderSkin('$Site#referrer', this);
}
});
res.data.action = this.href(req.action);
res.data.list = res.pop();
res.data.title = gettext('Referrers');
res.data.body = this.renderSkinAsString('$Site#referrers');
this.renderSkin('Site#page');
return;
}
Site.prototype.search_action = function () {
if (req.data.q) {
var limit = 50;
var search = this.search(Story, req.data.q, limit);
var commentSearch = this.search(Comment, req.data.q, limit);
if (search.result.length < 1) {
res.message = search.message;
} else if (search.exceeded && commentSearch.exceeded) {
res.message = gettext('Found more than {0} results. Please try a more specific query.', 2 * limit);
}
search.result = search.result.concat(commentSearch.result);
search.result.sort(new Number.Sorter('created', Number.Sorter.DESC));
res.push();
search.result.forEach(function (story) {
story.renderSkin('$Story#search');
});
res.data.result = res.pop();
res.data.count = search.result.length;
req.data.q = search.term;
}
res.data.title = gettext('Search');
res.data.body = this.renderSkinAsString('$Site#search');
this.renderSkin('Site#page');
return;
};
Site.prototype.subscribe_action = function() {
try {
Membership.add(session.user, Membership.SUBSCRIBER, this);
res.message = gettext('Successfully subscribed to site {0}.', this.title);
res.redirect(this.members.href('subscriptions'));
} catch (ex) {
app.log(ex);
res.message = ex.toString();
}
res.redirect(this.href());
return;
}
Site.prototype.unsubscribe_action = function() {
if (req.postParams.proceed) {
try {
Membership.remove.call(Membership.getByName(session.user.name));
res.message = gettext('Successfully unsubscribed from site {0}.', this.title);
res.redirect(this.href());
} catch (ex) {
app.log(ex)
res.message = ex.toString();
}
}
User.setLocation();
res.data.title = gettext('Confirm Unsubscribe');
res.data.body = this.renderSkinAsString('$HopObject#confirm', {
text: gettext('You are about to unsubscribe from the site {0}.', this.title)
});
this.renderSkin('Site#page');
return;
}
Site.prototype.export_action = function() {
const data = req.postParams;
const param = {};
const href = this.href(req.action);
let job = new Admin.Job(this.job || {});
if (data.submit === 'export') {
try {
if (job.method && job.method !== 'export') {
throw Error(gettext('There is already another job queued for this site: {0}', job.method));
}
this.job = Admin.queue(this, 'export');
res.message = gettext('The site is queued for export.');
} catch (ex) {
res.message = ex.toString();
app.log(res.message);
}
res.redirect(href);
} else if (data.submit === 'cancel') {
this.job = job.remove();
res.redirect(href);
}
if (job.method === 'export') {
param.status = gettext('The site data will be available for download from here, soon.');
}
res.data.title = 'Export Site ' + this.name;
res.handlers.file = File.getById(this.export) || {};
res.data.body = this.renderSkinAsString('$Site#export', param);
res.handlers.site.renderSkin('Site#page');
return;
}
Site.prototype.import_action = function() {
var job = this.job && new Admin.Job(this.job);
var file = this.import_id && File.getById(this.import_id);
var data = req.postParams;
if (data.submit === 'start') {
try {
if (job) {
if (job.method === 'import') {
job.remove();
this.job = null;
} else if (job.method) {
throw Error(gettext('There is already another job queued for this site: {0}',
job.method));
}
}
file && File.remove.call(file);
data.file_origin = data.file.name;
file = new File;
file.site = this;
file.update(data);
this.files.add(file);
file.creator = session.user;
this.job = Admin.queue(this, 'import');
this.import_id = file._id;
res.message = gettext('Site is scheduled for import.');
res.redirect(this.href(req.action));
} catch (ex) {
res.message = ex.toString();
app.log(res.message);
}
} else if (data.submit === 'stop') {
file && File.remove.call(file);
job && job.remove();
this.job = null;
this.import_id = null;
res.redirect(this.href(req.action));
}
res.handlers.file = File.getById(this.import_id) || {};
res.data.body = this.renderSkinAsString('$Site#import');
this.renderSkin('Site#page');
return;
}
Site.prototype.robots_txt_action = function() {
res.contentType = 'text/plain';
this.renderSkin('Site#robots');
return;
};
Site.prototype.contact_action = function () {
var username = req.data.name;
var membership;
if (username) membership = Membership.getByName(username);
if (!membership) membership = this.members.owners.get(0);
try {
if (!membership) throw Error(gettext('Something went wrong.'));
res.redirect(membership.href('contact'));
} catch (ex) {
res.message = ex.toString();
res.redirect(req.data.http_referer);
}
};
/**
*
* @param {String} name
* @returns {HopObject}
*/
Site.prototype.getMacroHandler = function(name) {
switch (name) {
case 'archive':
case 'comments':
case 'files':
case 'galleries':
case 'images':
case 'layout':
case 'members':
case 'polls':
case 'stories':
case 'tags':
return this[name];
default:
return null;
}
}
/**
*
*/
Site.prototype.stories_macro = function() {
if (this.stories.featured.size() < 1) {
this.renderSkin('Site#welcome');
if (session.user) {
if (session.user === this.creator) {
session.user.renderSkin('$User#welcome');
}
if (this === root && User.require(User.PRIVILEGED)) {
this.admin.renderSkin('$Admin#welcome');
}
}
} else {
this.archive.renderSkin('Archive#main');
}
return;
}
/**
*
* @param {Object} param
*/
Site.prototype.calendar_macro = function(param) {
if (this.archiveMode !== Site.PUBLIC) {
return;
}
var calendar = new jala.Date.Calendar(this.archive);
//calendar.setAccessNameFormat('yyyy/MM/dd');
calendar.setHrefFormat('/yyyy/MM/dd/');
calendar.setLocale(this.getLocale());
calendar.setTimeZone(this.getTimeZone());
calendar.render(this.archive.getDate());
return;
}
/**
*
* @param {Object} param
*/
Site.prototype.age_macro = function(param) {
res.write(Math.floor((new Date() - this.created) / Date.ONEDAY));
return;
}
/**
*
* @param {Object} param
* @param {String} name
*/
Site.prototype.static_macro = function(param, name, mode) {
return this.getStaticUrl(name);
}
/**
*
*/
Site.prototype.spamfilter_macro = function() {
var str = this.getMetadata('spamfilter');
if (!str) {
return;
}
var items = str.replace(/\r/g, '').split('\n');
for (var i in items) {
res.write('"');
res.write(items[i]);
res.write('"');
if (i < items.length-1) {
res.write(',');
}
}
return;
};
/**
*
*/
Site.prototype.diskspace_macro = function() {
var quota = this.getQuota();
var usage = this.getDiskSpace(quota);
res.write(usage > 0 ? formatNumber(usage, '#,###.#') : 0);
res.write(' MB ' + (quota ? gettext('free') : gettext('used')));
return;
}
Site.prototype.search = function (type, term, limit) {
var search = {
message: '',
result: [],
exceeded: false
};
var query = Sql[type.name.toUpperCase() + '_SEARCH'];
if (!query) {
return search;
}
term = String(term || String.EMPTY).trim();
// FIXME: Is this still necessary?
// Remove single and double ticks (aka false quotes)
//term = term.replace(/(?:\x22|\x27)/g, String.EMPTY);
if (term === '') {
search.message = gettext('Please enter a query in the search form.');
} else if (term) {
var counter = 0;
var sql = new Sql({quote: false});
sql.retrieve(query, this._id, term, limit + 1);
sql.traverse(function () {
if (counter < limit) {
search.result.push(Story.getById(this.id));
}
counter += 1;
});
if (counter > limit) {
search.exceeded = true;
search.message = gettext('Found more than {0} results. Please try a more specific query.', limit);
}
}
search.term = term;
return search;
};
/**
* @returns {java.util.Locale}
*/
Site.prototype.getLocale = function() {
var locale = this.cache.locale;
if (locale) return locale;
if (this.locale) {
locale = java.util.Locale.forLanguageTag(this.locale);
} else {
locale = java.util.Locale.getDefault();
}
return this.cache.locale = locale;
};
/**
* @returns {java.util.TimeZone}
*/
Site.prototype.getTimeZone = function() {
var timeZone;
if (timeZone = this.cache.timeZone) {
return timeZone;
}
if (this.timeZone) {
timeZone = java.util.TimeZone.getTimeZone(this.timeZone);
} else {
timeZone = java.util.TimeZone.getDefault();
}
this.cache.timezone = timeZone;
return timeZone;
}
/**
* @returns {Number}
*/
Site.prototype.getQuota = function() {
return this.status !== Site.TRUSTED ? root.quota : null;
}
/**
* @param {Number} quota
* @returns {float}
*/
Site.prototype.getDiskSpace = function(quota) {
quota || (quota = this.getQuota());
var dir = new java.io.File(this.getStaticFile());
var size = Packages.org.apache.commons.io.FileUtils.sizeOfDirectory(dir);
var factor = 1024 * 1024; // MB
return (quota ? quota * factor - size : size) / factor;
}
/**
*
* @param {String} href
*/
Site.prototype.processHref = function(href) {
var parts;
var scheme = getHrefScheme();
var domain = getProperty('domain.' + this.name);
if (domain) {
parts = [scheme, domain, href];
} else {
domain = getProperty('domain.*');
if (domain) {
parts = [scheme, this.name, '.', domain, href];
} else {
var mountpoint = app.appsProperties.mountpoint;
if (mountpoint === '/') mountpoint = ''; // Prevents double slashes
parts = [scheme, req.data.http_host, mountpoint, href];
}
}
return parts.join('');
}
/**
*
* @param {String} type
* @param {String} group
* @returns {Tag[]}
*/
Site.prototype.getTags = function(type, group) {
var handler;
type = type.toLowerCase();
switch (type) {
case 'story':
case 'tags':
handler = this.stories;
type = 'tags';
break;
case 'image':
case 'galleries':
handler = this.images;
type = 'galleries';
break;
}
switch (group) {
case Tags.ALL:
return handler[type];
case Tags.OTHER:
case Tags.ALPHABETICAL:
return handler[group + type.titleize()];
default:
return handler['alphabetical' + type.titleize()].get(group);
}
return null;
}
/**
*
* @param {String} tail
* @returns {helma.File}
*/
Site.prototype.getStaticFile = function(tail) {
var fpath = 'sites/' + this.name;
tail && (fpath += '/' + tail);
return new helma.File(app.appsProperties['static'], fpath);
}
/**
*
* @param {String} tail
* @returns {String}
*/
Site.prototype.getStaticUrl = function(href) {
if (!href) href = '';
var scheme = getHrefScheme();
var host = getProperty('domain.' + this.name);
if (!host) host = getProperty('domain.*');
if (!host) host = req.data.http_host;
return [scheme, host, app.appsProperties.staticMountpoint, '/sites/', this.name, '/', href].join('');
}
/**
*
* @param {Object} ref
*/
Site.prototype.callback = function(ref) {
if (this.callbackMode === Site.ENABLED && this.callbackUrl) {
app.data.callbacks.push({
site: this._id,
handler: ref.constructor,
id: ref._id
});
}
return;
}