antville/code/Site/Site.js
2015-08-10 16:21:21 +02:00

1159 lines
32 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_id');
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'));
/**
* @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) {
HopObject.remove.call(this.stories);
HopObject.remove.call(this.images);
HopObject.remove.call(this.files);
HopObject.remove.call(this.polls);
HopObject.remove.call(this.entries);
HopObject.remove.call(this.members, {force: true});
Layout.remove.call(this.layout, {force: true});
this.getStaticFile().removeDirectory();
this.deleteMetadata();
this.remove();
}
return;
}
/**
*
* @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_id
* @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;
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 = new Date;
this.status = Site.BLOCKED;
this.mode = Site.DELETED;
res.message = gettext('The site {0} is queued for removal.', 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 !== Site.DELETED && data.mode === Site.DELETED) {
this.deleted = new Date;
} else if (this.job && this.mode === Site.DELETED && data.mode !== Site.DELETED) {
var job = new Admin.Job(this.job);
job.remove();
this.job = 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();
// FIXME: This prevents the UIKit fonts from loading (wrong path)
//var file = new java.io.File(root.getStaticFile('../../styles/main.min.css'));
//res.writeln(Packages.org.apache.commons.io.FileUtils.readFileToString(file, 'utf-8'));
var coreCss = this.renderSkinAsString('$Site#stylesheet');
var customCss = this.renderSkinAsString('Site#stylesheet');
lessParser.parse(coreCss, function (error, tree) {
if (error) throw error;
coreCss = tree.toCSS();
});
try {
lessParser.parse(customCss, function(error, tree) {
if (error) throw error;
customCss = tree.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.writeln(coreCss);
res.writeln(customCss);
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: encodeURI(url)
});
}).join('\n');
}
if (parts.videos) {
res.data.videos = parts.videos.map(function (url) {
return html.tagAsString('meta', {
property: 'og:video',
content: encodeURI(url)
});
}).join('\n');
}
res.data.meta = this.renderSkinAsString('$Site#meta');
this.renderSkin('Site#page');
};
/**
*
* @param {Story[]} collection
*/
Site.prototype.renderXml = function(collection) {
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 feedInfo = new rome.FeedInformationImpl();
var feedModules = new java.util.ArrayList();
feedModules.add(feedInfo);
feed.setModules(feedModules);
//feedInfo.setImage(new java.net.URL(this.getProperty('imageUrl')));
feedInfo.setSubtitle(this.tagline);
feedInfo.setSummary(this.description);
feedInfo.setAuthor(this.creator.name);
feedInfo.setOwnerName(this.creator.name);
//feedInfo.setOwnerEmailAddress(this.getProperty('email'));
*/
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);
}
entries.add(entry);
/*
entryInfo = new rome.EntryInformationImpl();
entryModules = new java.util.ArrayList();
entryModules.add(entryInfo);
entry.setModules(entryModules);
enclosure = new rome.SyndEnclosureImpl();
enclosure.setUrl(episode.getProperty('fileUrl'));
enclosure.setType(episode.getProperty('contentType'));
enclosure.setLength(episode.getProperty('filesize') || 0);
enclosures = new java.util.ArrayList();
enclosures.add(enclosure);
entry.setEnclosures(enclosures);
entryInfo.setAuthor(entry.getAuthor());
entryInfo.setBlock(false);
entryInfo.setDuration(new rome.Duration(episode.getProperty('length') || 0));
entryInfo.setExplicit(false);
entryInfo.setKeywords(episode.getProperty('keywords'));
entryInfo.setSubtitle(episode.getProperty('subtitle'));
entryInfo.setSummary(episode.getProperty('description'));
*/
}
feed.setEntries(entries);
var output = new rome.SyndFeedOutput();
res.servletResponse.setCharacterEncoding('utf-8');
output.output(feed, res.servletResponse.writer);
return;
// FIXME: Ugly hack for adding PubSubHubbub and rssCloud elements to XML
/*
var xml = output.outputString(feed);
xml = xml.replace('<rss', '<rss xmlns:atom="http://www.w3.org/2005/Atom"');
xml = xml.replace('<channel>', '<channel>\n <cloud domain="rpc.rsscloud.org" port="5337" path="/rsscloud/pleaseNotify" registerProcedure="" protocol="http-post" />');
xml = xml.replace('<channel>', '<channel>\n <atom:link rel="hub" href="' + getProperty("parss.hub") + '"/>');
return xml;
*/
}
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() {
var job = this.job && new Admin.Job(this.job);
var data = req.postParams;
if (data.submit === 'start') {
try {
if (!job) {
this.job = Admin.queue(this, 'export');
res.message = gettext('Site is scheduled for export.');
} else {
if (job.method !== 'export') {
throw Error(gettext('There is already another job queued for this site: {0}',
job.method));
}
}
} catch (ex) {
res.message = ex.toString();
app.log(res.message);
}
res.redirect(this.href(req.action));
} else if (data.submit === 'stop') {
job && job.remove();
this.job = null;
res.redirect(this.href(req.action));
}
var param = {
status: (job && job.method === 'export') ?
gettext('A Blogger export file (.xml) will be created and available for download from here within 24 hours.') :
null
}
res.handlers.file = File.getById(this.export_id) || {};
res.data.body = this.renderSkinAsString('$Site#export', param);
this.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.deleted_macro = function() {
return new Date(this.deleted.getTime() + Date.ONEDAY * Admin.SITEREMOVALGRACEPERIOD);
}
/**
*
*/
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, domain,
scheme = (req.servletRequest ? req.servletRequest.scheme : 'http') + '://';
if (domain = getProperty('domain.' + this.name)) {
parts = [scheme, domain, href];
} else if (domain = getProperty('domain.*')) {
parts = [scheme, this.name, '.', domain, href];
} else {
var mountpoint = app.appsProperties.mountpoint;
(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) {
href || (href = '');
var scheme = (req.servletRequest ? req.servletRequest.scheme : 'http') + '://';
var host = getProperty('domain.' + this.name);
host || (host = getProperty('domain.*'));
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;
}