1936 lines
		
	
	
	
		
			59 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			1936 lines
		
	
	
	
		
			59 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| //
 | |
| // The Antville Project
 | |
| // http://code.google.com/p/antville
 | |
| //
 | |
| // Copyright 2001-2007 by The Antville People
 | |
| //
 | |
| // 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.
 | |
| //
 | |
| // $Revision$
 | |
| // $LastChangedBy$
 | |
| // $LastChangedDate$
 | |
| // $URL$
 | |
| //
 | |
| 
 | |
| /**
 | |
|  * constructor function for site objects
 | |
|  * @param String Title
 | |
|  * @param String Alias
 | |
|  * @param Object Creator
 | |
|  */
 | |
| Site.prototype.constructor = function(title, alias, creator) {
 | |
|    this.title = title;
 | |
|    this.alias = alias;
 | |
|    this.creator = creator;
 | |
|    this.createtime = this.lastoffline = new Date();
 | |
|    this.email = creator.email;
 | |
|    this.online = 0;
 | |
|    this.blocked = 0;
 | |
|    this.trusted = creator.trusted;
 | |
|    this.enableping = 0;
 | |
| 
 | |
|    // create initial preferences
 | |
|    var prefs = new HopObject();
 | |
|    prefs.tagline = null;
 | |
|    prefs.discussions = 1;
 | |
|    prefs.usercontrib = 0;
 | |
|    prefs.archive = 1;
 | |
|    prefs.days = 3;
 | |
|    // retrieve locale-object from root
 | |
|    var loc = root.getLocale();
 | |
|    prefs.language = loc.getLanguage();
 | |
|    prefs.country = loc.getCountry();
 | |
|    prefs.timezone = root.getTimeZone().getID();
 | |
|    prefs.longdateformat = "EEEE, dd. MMMM yyyy, h:mm a";
 | |
|    prefs.shortdateformat = "yyyy.MM.dd, HH:mm";
 | |
|    this.preferences_xml = Xml.writeToString(prefs);
 | |
|    return this;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * main action
 | |
|  */
 | |
| Site.prototype.main_action = function() {
 | |
|    if (this.allstories.size() == 0) {
 | |
|       res.data.body = this.renderSkinAsString("welcome");
 | |
|       if (session.user) {
 | |
|          if (session.user == this.creator)
 | |
|             res.data.body += this.renderSkinAsString("welcomeowner");
 | |
|          if (session.user.sysadmin)
 | |
|             res.data.body += this.renderSkinAsString("welcomesysadmin");
 | |
|       }
 | |
|    } else {
 | |
|       this.renderStorylist(req.data.day);
 | |
|       res.data.body = this.renderSkinAsString("main");
 | |
|    }
 | |
|    res.data.title = this.title;
 | |
|    this.renderSkin("page");
 | |
|    logAccess();
 | |
|    return;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * edit action
 | |
|  */
 | |
| Site.prototype.edit_action = function() {
 | |
|    if (req.data.cancel) {
 | |
|       res.redirect(this.href());
 | |
|    } else if (req.data.rebuildIndex) {
 | |
|       app.data.indexManager.queueForRebuilding(this.alias);
 | |
|       res.message = new Message("rebuildIndex");
 | |
|       res.redirect(this.href(req.action));
 | |
|    } else if (req.data.save) {
 | |
|       try {
 | |
|          res.message = this.evalPreferences(req.data, session.user);
 | |
|          res.redirect(this.href(req.action));
 | |
|       } catch (err) {
 | |
|          res.message = err.toString();
 | |
|       }
 | |
|    }
 | |
| 
 | |
|    res.data.action = this.href(req.action);
 | |
|    res.data.title = getMessage("Site.preferencesTitle", {siteTitle: this.title});
 | |
|    res.data.body = this.renderSkinAsString("edit");
 | |
|    this.renderSkin("page");
 | |
|    return;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * delete action
 | |
|  */
 | |
| Site.prototype.delete_action = function() {
 | |
|    if (req.data.cancel)
 | |
|       res.redirect(this.href());
 | |
|    else if (req.data.remove) {
 | |
|       try {
 | |
|          res.message = root.deleteSite(this);
 | |
|          res.redirect(root.href());
 | |
|       } catch (err) {
 | |
|          res.message = err.toString();
 | |
|       }
 | |
|    }
 | |
| 
 | |
|    res.data.action = this.href(req.action);
 | |
|    res.data.title = getMessage("Site.deleteTitle");
 | |
|    var skinParam = {
 | |
|       description: getMessage("Site.deleteDescription"),
 | |
|       detail: this.title
 | |
|    };
 | |
|    res.data.body = this.renderSkinAsString("delete", skinParam);
 | |
|    this.renderSkin("page");
 | |
|    return;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * wrapper to access colorpicker also from site
 | |
|  */
 | |
| Site.prototype.colorpicker_action = function() {
 | |
|    res.handlers.site = this;
 | |
|    root.colorpicker_action();
 | |
|    return;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * wrapper to make style.skin public
 | |
|  */
 | |
| Site.prototype.main_css_action = function() {
 | |
|    res.dependsOn(this.modifytime);
 | |
|    res.dependsOn(res.handlers.layout.modifytime);
 | |
|    res.dependsOn(res.handlers.layout.skins.getSkinSource("Site", "style"));
 | |
|    res.digest();
 | |
|    res.contentType = "text/css";
 | |
|    this.renderSkin("style");
 | |
|    return;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * wrapper to make javascript.skin public
 | |
|  */
 | |
| Site.prototype.main_js_action = function() {
 | |
|    res.dependsOn(this.modifytime);
 | |
|    res.dependsOn(res.handlers.layout.modifytime);
 | |
|    res.dependsOn(res.handlers.layout.skins.getSkinSource("Site", "javascript"));
 | |
|    res.digest();
 | |
|    res.contentType = "text/javascript";
 | |
|    this.renderSkin("javascript");
 | |
|    root.renderSkin("systemscripts");
 | |
|    return;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * wrapper for rss feed
 | |
|  */
 | |
| Site.prototype.rss_xml_action = function() {
 | |
|    res.redirect(root.href("rss"));
 | |
|    return;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * rss feed
 | |
|  */
 | |
| Site.prototype.rss_action = function() {
 | |
|    res.contentType = "text/xml";
 | |
|    res.dependsOn(this.lastupdate);
 | |
|    res.digest();
 | |
| 
 | |
|    var now = new Date();
 | |
|    var systitle = root.getTitle();
 | |
|    var sdf = new java.text.SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
 | |
|    sdf.setTimeZone(new java.util.SimpleTimeZone(0, "UTC"));
 | |
| 
 | |
|    var collection, subtitle;
 | |
|    switch (true) {
 | |
|       case (req.data.show == "all") :
 | |
|          collection = this.allcontent;
 | |
|          subtitle = "with comments";
 | |
|          break;
 | |
|       // FIXME: i don't think a day makes much sense as rss output [tobi]
 | |
|       case (req.data.day != null) :
 | |
|          collection = this.get(req.data.day);
 | |
|          subtitle = req.data.day;
 | |
|          break;
 | |
|       case (req.data.topic != null) :
 | |
|          collection = this.topics.get(req.data.topic);
 | |
|          subtitle = req.data.topic;
 | |
|          break;
 | |
|       default :
 | |
|          collection = this.allstories;
 | |
|    }
 | |
|    var size = (collection != null) ? collection.size() : 0;
 | |
| 
 | |
|    var max = req.data.max ? parseInt(req.data.max) : 7;
 | |
|    max = Math.min(max, size);
 | |
|    max = Math.min(max, 10);
 | |
| 
 | |
|    var item = {};
 | |
|    if (max > 0 && this.online) {
 | |
|       var items = new java.lang.StringBuffer();
 | |
|       var resources = new java.lang.StringBuffer();
 | |
|       collection.prefetchChildren(0, max);
 | |
|       for (var i=0; i<max; i++) {
 | |
|          var story = collection.get(i);
 | |
|          var item = {
 | |
|             url: story.href(),
 | |
|             title: story.getRenderedContentPart("title").stripTags(),
 | |
|             text: story.getRenderedContentPart("text").stripTags(),
 | |
|             publisher: systitle,
 | |
|             creator: story.creator.name,
 | |
|             date: sdf.format(story.createtime),
 | |
|             subject: story.topic ? story.topic : "",
 | |
|             year: story.createtime.getFullYear()
 | |
|          };
 | |
|          if (story.creator.publishemail)
 | |
|             item.email = story.creator.email.entitize();
 | |
|          if (!item.title) {
 | |
|             // shit happens: if a content part contains a markup
 | |
|             // element only, String.clip() will return nothing...
 | |
|             if (!item.text )
 | |
|                item.title = "...";
 | |
|             else {
 | |
|                var embody = item.text.embody(10, "...", "\\s");
 | |
|                item.title = embody.head;
 | |
|                item.text = embody.tail;
 | |
|             }
 | |
|          }
 | |
|          items.append(story.renderSkinAsString("rssItem", item));
 | |
|          resources.append(story.renderSkinAsString("rssResource", item));
 | |
|       }
 | |
| 
 | |
|       var site = {
 | |
|          subtitle: subtitle,
 | |
|          url: this.href(),
 | |
|          title: systitle,
 | |
|          creator: this.creator.name,
 | |
|          year: now.getFullYear(),
 | |
|          lastupdate: max > 0 ? sdf.format(this.lastUpdate): sdf.format(this.createtime),
 | |
|          items: items.toString(),
 | |
|          resources: resources.toString()
 | |
|       };
 | |
|       if (this.email)
 | |
|          site.email = this.email.entitize();
 | |
|       else if (this.creator.publishemail)
 | |
|          site.email = this.creator.email.entitize();
 | |
|       this.renderSkin("rss", site);
 | |
|    }
 | |
|    return;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * this action tries to get a file with the given name
 | |
|  * if it finds it, it increases the request-counter of this file
 | |
|  * sets the appropriate mimetype and redirects the browser to the file
 | |
|  */
 | |
| Site.prototype.getfile_action = function() {
 | |
|    var f = this.files.get(req.data.name);
 | |
|    if (f) {
 | |
|       f.requestcnt++;
 | |
|       res.contentType = f.mimetype;
 | |
|       res.redirect(f.getUrl());
 | |
|    } else {
 | |
|       res.message = getMessage("error.fileNotFound", req.data.name);
 | |
|       res.redirect(this.href());
 | |
|    }
 | |
|    return;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * most read stories of a site
 | |
|  */
 | |
| Site.prototype.mostread_action = function() {
 | |
|    res.data.title = getMessage("Site.mostReadTitle", {siteTitle: this.title});
 | |
|    res.data.body = this.renderSkinAsString("mostread");
 | |
|    this.renderSkin("page");
 | |
|    return;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * referrers of a site
 | |
|  */
 | |
| Site.prototype.referrers_action = function() {
 | |
|    if (req.data.permanent && session.user) {
 | |
|       try {
 | |
|          this.checkEdit(session.user, req.data.memberlevel);
 | |
|       } catch (err) {
 | |
|          res.message = err.toString();
 | |
|          res.redirect(this.href());
 | |
|          return;
 | |
|       }
 | |
|       var urls = req.data.permanent_array ?
 | |
|                  req.data.permanent_array : [req.data.permanent];
 | |
|       res.push();
 | |
|       res.write(this.preferences.getProperty("spamfilter"));
 | |
|       for (var i in urls) {
 | |
|          res.write("\n");
 | |
|          res.write(urls[i].replace(/\?/g, "\\\\?"));
 | |
|       }
 | |
|       this.preferences.setProperty("spamfilter", res.pop());
 | |
|       res.redirect(this.href(req.action));
 | |
|       return;
 | |
|    }
 | |
|    res.data.action = this.href("referrers");
 | |
|    res.data.title = getMessage("Site.referrersReadTitle", {siteTitle: this.title});
 | |
|    res.data.body = this.renderSkinAsString("referrers");
 | |
|    this.renderSkin("page");
 | |
|    return;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * search action
 | |
|  */
 | |
| Site.prototype.search_action = function() {
 | |
|    var self = this;
 | |
| 
 | |
|    /**
 | |
|     * private method for rendering the results
 | |
|     */
 | |
|    var renderResult = function(hits, itemsPerPage, pageIdx) {
 | |
|       var currIdx = 0;
 | |
|       var size = hits.length();
 | |
|       var validCnt = 0;
 | |
|    
 | |
|       var totalPages = Math.ceil(size/itemsPerPage);
 | |
|       if (isNaN(pageIdx) || pageIdx > totalPages || pageIdx < 0)
 | |
|          pageIdx = 0;
 | |
|       var start = (pageIdx * itemsPerPage);
 | |
|       stop = Math.min(start + itemsPerPage, size);
 | |
|       res.push();
 | |
|       while (currIdx < size && validCnt < stop) {
 | |
|          var item = Story.getById(hits.doc(currIdx).get("id"));
 | |
|          if (item) {
 | |
|             var status = (item instanceof Comment) ? item.story.online : item.online;
 | |
|             if (status > 0) {
 | |
|                if (validCnt >= start) {
 | |
|                   item.renderSkin("searchview", {score: Math.round(hits.score(currIdx) * 100)});
 | |
|                }
 | |
|                validCnt++;
 | |
|             } else {
 | |
|                // "correct" the number of hits since
 | |
|                // the story/comment is offline
 | |
|                total--;
 | |
|             }
 | |
|          }
 | |
|          currIdx++;
 | |
|       }
 | |
|       return res.pop();
 | |
|    }
 | |
| 
 | |
|    /**
 | |
|     * main action body
 | |
|     */
 | |
|    if (req.isGet() && req.data.q != null) {
 | |
|       var query = stripTags(req.data.q);
 | |
|       var queryArr = ["q=" + query];
 | |
| 
 | |
|       // construct the filter query
 | |
|       var fq = new Search.BooleanQuery();
 | |
|       fq.addQuery(new Search.RangeQuery("online", 1, 2));
 | |
|       // filter by creator
 | |
|       if (req.data.c) {
 | |
|          fq.addTerm("creator", req.data.c);
 | |
|          queryArr.push("c=" + req.data.c);
 | |
|       }
 | |
|       // filter by createtime
 | |
|       if (req.data.ct) {
 | |
|          var then = new Date();
 | |
|          var min = new Date(then.setMonth(then.getMonth() - parseInt(req.data.ct, 10)));
 | |
|          min = min.format("yyyyMMdd", this.getLocale(), this.getTimeZone());
 | |
|          fq.addQuery(new Search.RangeQuery("createtime", min));
 | |
|          queryArr.push("ct=" + req.data.ct);
 | |
|       }
 | |
|       var filter = new Search.QueryFilter(fq);
 | |
| 
 | |
|       // construct the query itself
 | |
|       var q = new Search.BooleanQuery();
 | |
|       q.addTerm("site", this._id);
 | |
|       // occurrence
 | |
|       queryArr.push("o=" + req.data.o);
 | |
|       switch (req.data.o) {
 | |
|          case "topic":
 | |
|             q.addTerm("topic", query);
 | |
|             break;
 | |
|          case "title":
 | |
|             q.addTerm("title", query);
 | |
|             break;
 | |
|          case "text":
 | |
|             q.addTerm("text", query);
 | |
|             break;
 | |
|          default:
 | |
|             q.addTerm(["title", "text", "topic"], query);
 | |
|             break;
 | |
|       }
 | |
| 
 | |
|       var index = this.getIndex();
 | |
|       if (!index) {
 | |
|          res.message = getMessage("error.searchNothingFound", encodeForm(query));
 | |
|       } else {
 | |
|          var now = new Date();
 | |
|          var searcher = new index.Searcher();
 | |
|          var total = searcher.search(q, filter);
 | |
|          res.data.resultlist = renderResult(searcher.hits, 10, req.data.page);
 | |
|          switch(total) {
 | |
|             case 0:
 | |
|                res.message = getMessage("error.searchNothingFound", encodeForm(query));
 | |
|                break;
 | |
|             case 1:
 | |
|                res.message = getMessage("confirm.resultOne", encodeForm(query));
 | |
|                break;
 | |
|             default:
 | |
|                res.message = getMessage("confirm.resultMany", [encodeForm(query), total]);
 | |
|                break;
 | |
|          }
 | |
|          res.data.pagenavigation = renderPageNavigation(total, this.href(req.action) + "?" + queryArr.join("&"), 10, req.data.page);
 | |
|          searcher.close();
 | |
|       }
 | |
|       app.debug("[" + this.alias + "] search for " + q.toString() +
 | |
|                 ", filter: " + filter.toString() +
 | |
|                 " (" + (new Date()).diff(now) + "ms)");
 | |
|    }
 | |
| 
 | |
|    res.data.action = this.href(req.action);
 | |
|    res.data.title = this.title;
 | |
|    res.data.body = this.renderSkinAsString("searchresult");
 | |
|    this.renderSkin("page");
 | |
|    return;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * subscribe action
 | |
|  */
 | |
| Site.prototype.subscribe_action = function() {
 | |
|    // create a new member-object and add it to membership-mountpoint
 | |
|    this.members.add(new Membership(session.user));
 | |
|    res.message = new Message("subscriptionCreate", this.title);
 | |
|    res.redirect(this.href());
 | |
|    return;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * unsubscribe action
 | |
|  */
 | |
| Site.prototype.unsubscribe_action = function() {
 | |
|    if (req.data.cancel)
 | |
|       res.redirect(this.members.href("subscriptions"));
 | |
|    else if (req.data.remove) {
 | |
|       try {
 | |
|          res.message = this.members.deleteMembership(this.members.get(session.user.name), session.user);
 | |
|          res.redirect(this.members.href("subscriptions"));
 | |
|       } catch (err) {
 | |
|          res.message = err.toString();
 | |
|       }
 | |
|    }
 | |
| 
 | |
|    res.data.title = getMessage("Site.subscription.deleteTitle", {siteTitle: this.title});
 | |
|    var skinParam = {
 | |
|       description: getMessage("Site.subscription.deleteDescription"),
 | |
|       details: this.title
 | |
|    };
 | |
|    res.data.body = this.renderSkinAsString("delete", skinParam);
 | |
|    this.renderSkin("page");
 | |
|    return;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * context menu extension
 | |
|  */
 | |
| Site.prototype.menuext_action = function() {
 | |
|    this.renderSkin("menuext");
 | |
|    return;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * context menu extension (accessible as /menuext.reg)
 | |
|  */
 | |
| Site.prototype.menuext_reg_action = function() {
 | |
|    res.contentType = "text/plain";
 | |
|    this.renderSkin("menuext.reg");
 | |
|    return;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * robots.txt action
 | |
|  */
 | |
| Site.prototype.robots_txt_action = function() {
 | |
|    res.contentType = "text/plain";
 | |
|    this.renderSkin("robots");
 | |
|    return;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * macro rendering title
 | |
|  */
 | |
| Site.prototype.title_macro = function(param) {
 | |
|    if (param.as == "editor")
 | |
|       Html.input(this.createInputParam("title", param));
 | |
|    else {
 | |
|       if (param && param.linkto) {
 | |
|          if (param.linkto == "main")
 | |
|             param.linkto = "";
 | |
|          Html.openLink({href: this.href(param.linkto)});
 | |
|          if (this.title && this.title.trim())
 | |
|             res.write(stripTags(this.title));
 | |
|          else
 | |
|             res.write("<em>[untitled]</em>");
 | |
|          Html.closeLink();
 | |
|       } else
 | |
|          res.write(this.title);
 | |
|    }
 | |
|    return;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * macro rendering alias
 | |
|  */
 | |
| Site.prototype.alias_macro = function(param) {
 | |
|    if (param.as == "editor")
 | |
|       Html.input(this.createInputParam("alias", param));
 | |
|    else
 | |
|       res.write(this.alias);
 | |
|    return;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * macro rendering tagline
 | |
|  */
 | |
| Site.prototype.tagline_macro = function(param) {
 | |
|    if (param.as == "editor")
 | |
|       Html.input(this.preferences.createInputParam("tagline", param));
 | |
|    else if (this.preferences.getProperty("tagline"))
 | |
|       res.write(stripTags(this.preferences.getProperty("tagline")));
 | |
|    return;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * macro rendering email
 | |
|  */
 | |
| Site.prototype.email_macro = function(param) {
 | |
|    if (param.as == "editor")
 | |
|       Html.input(this.createInputParam("email", param));
 | |
|    else
 | |
|       res.write(this.email);
 | |
|    return;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * macro rendering lastupdate
 | |
|  */
 | |
| Site.prototype.lastupdate_macro = function(param) {
 | |
|    if (this.lastupdate)
 | |
|       res.write(formatTimestamp(this.lastupdate, param.format));
 | |
|    return;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * macro rendering online-status
 | |
|  */
 | |
| Site.prototype.online_macro = function(param) {
 | |
|    if (param.as == "editor") {
 | |
|       var inputParam = this.createCheckBoxParam("online", param);
 | |
|       if (req.data.save && !req.data.online)
 | |
|          delete inputParam.checked;
 | |
|       Html.checkBox(inputParam);
 | |
|    } else if (this.online)
 | |
|       res.write(param.yes ? param.yes : getMessage("generic.yes"));
 | |
|    else
 | |
|       res.write(param.no ? param.no : getMessage("generic.no"));
 | |
|    return;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * macro rendering discussion-flag
 | |
|  */
 | |
| Site.prototype.hasdiscussions_macro = function(param) {
 | |
|    if (param.as == "editor") {
 | |
|       var inputParam = this.preferences.createCheckBoxParam("discussions", param);
 | |
|       if (req.data.save && !req.data.preferences_discussions)
 | |
|          delete inputParam.checked;
 | |
|       Html.checkBox(inputParam);
 | |
|    } else
 | |
|       res.write(this.preferences.getProperty("discussions") ? 
 | |
|                 getMessage("generic.yes") : getMessage("generic.no"));
 | |
|    return;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * macro rendering usercontrib-flag
 | |
|  */
 | |
| Site.prototype.usermaycontrib_macro = function(param) {
 | |
|    if (param.as == "editor") {
 | |
|       var inputParam = this.preferences.createCheckBoxParam("usercontrib", param);
 | |
|       if (req.data.save && !req.data.preferences_usercontrib)
 | |
|          delete inputParam.checked;
 | |
|       Html.checkBox(inputParam);
 | |
|    } else
 | |
|       res.write(this.preferences.getProperty("usercontrib") ? 
 | |
|                 getMessage("generic.yes") : getMessage("generic.no"));
 | |
|    return;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * macro rendering nr. of days to show on site-fontpage
 | |
|  */
 | |
| Site.prototype.showdays_macro = function(param) {
 | |
|    if (param.as == "editor")
 | |
|       Html.input(this.preferences.createInputParam("days", param));
 | |
|    else
 | |
|       res.write(this.preferences.getProperty("days"));
 | |
|    return;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * macro rendering archive-flag
 | |
|  */
 | |
| Site.prototype.showarchive_macro = function(param) {
 | |
|    if (param.as == "editor") {
 | |
|       var inputParam = this.preferences.createCheckBoxParam("archive", param);
 | |
|       if (req.data.save && !req.data.preferences_archive)
 | |
|          delete inputParam.checked;
 | |
|       Html.checkBox(inputParam);
 | |
|    } else
 | |
|       res.write(this.preferences.getProperty("archive") ? 
 | |
|                 getMessage("generic.yes") : getMessage("generic.no"));
 | |
|    return;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * macro rendering enableping-flag
 | |
|  */
 | |
| Site.prototype.enableping_macro = function(param) {
 | |
|    if (param.as == "editor") {
 | |
|       var inputParam = this.createCheckBoxParam("enableping", param);
 | |
|       if (req.data.save && !req.data.enableping)
 | |
|          delete inputParam.checked;
 | |
|       Html.checkBox(inputParam);
 | |
|    } else
 | |
|       res.write(this.enableping ? getMessage("generic.yes") : getMessage("generic.no"));
 | |
|    return;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * macro rendering default longdateformat
 | |
|  */
 | |
| Site.prototype.longdateformat_macro = function(param) {
 | |
|    if (param.as == "chooser")
 | |
|       renderDateformatChooser("longdateformat", this.getLocale(),
 | |
|                               this.preferences.getProperty("longdateformat"));
 | |
|    else if (param.as == "editor")
 | |
|       Html.input(this.preferences.createInputParam("longdateformat", param));
 | |
|    else
 | |
|       res.write(this.preferences.getProperty("longdateformat"));
 | |
|    return;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * macro rendering default shortdateformat
 | |
|  */
 | |
| Site.prototype.shortdateformat_macro = function(param) {
 | |
|    if (param.as == "chooser")
 | |
|       renderDateformatChooser("shortdateformat", this.getLocale(),
 | |
|                               this.preferences.getProperty("shortdateformat"));
 | |
|    else if (param.as == "editor")
 | |
|       Html.input(this.preferences.createInputParam("shortdateformat", param));
 | |
|    else
 | |
|       res.write(this.preferences.getProperty("shortdateformat"));
 | |
|    return;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * macro rendering loginStatus of user
 | |
|  * valid params:  -  loginSkin
 | |
|  *                -  logoutSkin
 | |
|  */
 | |
| Site.prototype.loginstatus_macro = function(param) {
 | |
|    if (session.user)
 | |
|       this.members.renderSkin("statusloggedin");
 | |
|    else if (req.action != "login")
 | |
|       this.members.renderSkin("statusloggedout");
 | |
|    return;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * macro rendering two different navigation-skins
 | |
|  * depending on user-status & rights
 | |
|  */
 | |
| Site.prototype.navigation_macro = function(param) {
 | |
|    if (param["for"] == "users" && !param.modules) {
 | |
|       // FIXME: this is left for backwards-compatibility
 | |
|       // sometime in the future we'll get rid of the usernavigation.skin
 | |
|       res.write("... ");
 | |
|       Html.link({href: "http://project.antville.org/project/stories/146"}, "<strong>README</strong>");
 | |
|       Html.tag("br");
 | |
|       Html.tag("br");
 | |
|       this.renderSkin("usernavigation");
 | |
|    }
 | |
|    if (!session.user)
 | |
|       return;
 | |
|    switch (param["for"]) {
 | |
|       case "contributors" :
 | |
|          if (session.user.sysadmin ||
 | |
|              this.preferences.getProperty("usercontrib") ||
 | |
|              req.data.memberlevel >= CONTRIBUTOR)
 | |
|             this.renderSkin("contribnavigation");
 | |
|          break;
 | |
|       case "admins" :
 | |
|          if (session.user.sysadmin || req.data.memberlevel >= ADMIN)
 | |
|             this.renderSkin("adminnavigation");
 | |
|          break;
 | |
|    }
 | |
|    if (param.modules != null) {
 | |
|       var mods = param.modules.split(",");
 | |
|       if (mods.length == 1 && mods[0] == "all") {
 | |
|          for (var i in app.modules)
 | |
|             this.applyModuleMethod(app.modules[i], "renderSiteNavigation", param);
 | |
|       } else {
 | |
|          for (var i in mods)
 | |
|             this.applyModuleMethod(app.modules[mods[i]], "renderSiteNavigation", param);
 | |
|       }
 | |
|    }
 | |
|    return;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * call the site navigation render method
 | |
|  * of a module
 | |
|  */
 | |
| Site.prototype.moduleNavigation_macro = function(param) {
 | |
|    if (!param.module)
 | |
|       return;
 | |
|    this.applyModuleMethod(app.modules[param.module],
 | |
|                           "renderSiteNavigation", param);
 | |
|    return;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * macro renders a calendar
 | |
|  * version 2
 | |
|  */
 | |
| Site.prototype.calendar_macro = function(param) {
 | |
|    // do nothing if there is not a single story :-))
 | |
|    // or if archive of this site is disabled
 | |
|    if (!this.allstories.size() || !this.preferences.getProperty("archive"))
 | |
|       return;
 | |
|    // define variables needed in this function
 | |
|    var calParam = new Object();
 | |
|    var dayParam = new Object();
 | |
|    var weekParam = new Object();
 | |
|    res.push();
 | |
| 
 | |
|    // create new calendar-object
 | |
|    var cal = java.util.Calendar.getInstance(this.getTimeZone(), this.getLocale());
 | |
|    var symbols = this.getDateSymbols();
 | |
| 
 | |
|    // render header-row of calendar
 | |
|    var firstDayOfWeek = cal.getFirstDayOfWeek();
 | |
|    var weekdays = symbols.getShortWeekdays();
 | |
|    res.push();
 | |
|    for (var i=0;i<7;i++) {
 | |
|       dayParam.day = weekdays[(i+firstDayOfWeek-1)%7+1];
 | |
|       this.renderSkin("calendardayheader", dayParam);
 | |
|    }
 | |
|    weekParam.week = res.pop();
 | |
|    this.renderSkin("calendarweek", weekParam);
 | |
| 
 | |
|    cal.set(java.util.Calendar.DATE, 1);
 | |
|    // check whether there's a day or a story in path
 | |
|    // if so, use it to determine the month to render
 | |
|    if (path.Story)
 | |
|       var today = path.Story.day.toString();
 | |
|    else if (path.Day && path.Day._prototype == "Day")
 | |
|       var today = path.Day.groupname.toString();
 | |
|    if (today) {
 | |
|       // instead of using String.toDate
 | |
|       // we do it manually here to avoid that a day like 20021001
 | |
|       // would be changed to 20020930 in some cases
 | |
|       cal.set(java.util.Calendar.YEAR, parseInt(today.substring(0, 4), 10));
 | |
|       cal.set(java.util.Calendar.MONTH, parseInt(today.substring(4, 6), 10)-1);
 | |
|    }
 | |
|    // nr. of empty days in rendered calendar before the first day of month appears
 | |
|    var pre = (7-firstDayOfWeek+cal.get(java.util.Calendar.DAY_OF_WEEK)) % 7;
 | |
|    var days = cal.getActualMaximum(java.util.Calendar.DATE);
 | |
|    var weeks = Math.ceil((pre + days) / 7);
 | |
|    var daycnt = 1;
 | |
| 
 | |
|    var monthNames = symbols.getMonths();
 | |
|    calParam.month = monthNames[cal.get(java.util.Calendar.MONTH)];
 | |
|    calParam.year =  cal.get(java.util.Calendar.YEAR);
 | |
| 
 | |
|    // pre-render the year and month so we only have to append the days as we loop
 | |
|    var currMonth = formatTimestamp(new Date(cal.getTime().getTime()), "yyyyMM");
 | |
|    // remember the index of the first and last days within this month.
 | |
|    // this is needed to optimize previous and next month links.
 | |
|    var lastDayIndex = 9999999;
 | |
|    var firstDayIndex = -1;
 | |
| 
 | |
|    for (var i=0;i<weeks;i++) {
 | |
|       res.push();
 | |
|       for (var j=0;j<7;j++) {
 | |
|          dayParam.skin = "calendarday";
 | |
|          if ((i == 0 && j < pre) || daycnt > days)
 | |
|             dayParam.day = " ";
 | |
|          else {
 | |
|             var currGroupname = currMonth+daycnt.format("00");
 | |
|             var linkText = daycnt < 10 ? " " + daycnt + " " : daycnt.toString();
 | |
|             var currGroup = this.get(currGroupname);
 | |
|             if (currGroup) {
 | |
|                var idx = this.contains(currGroup);
 | |
|                if (idx > -1) {
 | |
|                   if  (idx > firstDayIndex)
 | |
|                      firstDayIndex = idx;
 | |
|                   if (idx < lastDayIndex)
 | |
|                      lastDayIndex = idx;
 | |
|                }
 | |
|                dayParam.day = Html.linkAsString({href: currGroup.href()}, linkText);
 | |
|             } else {
 | |
|                dayParam.day = linkText;
 | |
|             }
 | |
|             if (currGroupname == today)
 | |
|                dayParam.skin = "calendarselday";
 | |
|             daycnt++;
 | |
|          }
 | |
|          this.renderSkin(dayParam.skin, dayParam);
 | |
|       }
 | |
|       weekParam.week = res.pop();
 | |
|       this.renderSkin("calendarweek", weekParam);
 | |
|    }
 | |
|    // set day to last day of month and try to render next month
 | |
|    // check what the last day of the month is
 | |
|    calParam.back = this.renderLinkToPrevMonth(firstDayIndex, currMonth + "01", monthNames);
 | |
|    calParam.forward = this.renderLinkToNextMonth(lastDayIndex, currMonth + "31", monthNames);
 | |
|    calParam.calendar = res.pop();
 | |
|    this.renderSkin("calendar", calParam);
 | |
|    return;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * macro renders age
 | |
|  */
 | |
| Site.prototype.age_macro = function(param) {
 | |
|    res.write(Math.floor((new Date() - this.createtime) / ONEDAY));
 | |
|    return;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * macro renders a list of recently added/updated stories/comments
 | |
|  * of this site
 | |
|  */
 | |
| Site.prototype.history_macro = function(param) {
 | |
|    try {
 | |
|       this.checkView(session.user, req.data.memberlevel);
 | |
|    } catch (deny) {
 | |
|       return;
 | |
|    }
 | |
|    var limit = param.limit ? parseInt(param.limit, 10) : 5;
 | |
|    var cnt = i = 0;
 | |
|    var size = this.lastmod.size();
 | |
|    var discussions = this.preferences.getProperty("discussions");
 | |
|    while (cnt < limit && i < size) {
 | |
|       if (i % limit == 0)
 | |
|          this.lastmod.prefetchChildren(i, limit);
 | |
|       var item = this.lastmod.get(i++);
 | |
|       switch (item._prototype) {
 | |
|          case "Story":
 | |
|             if (param.show == "comments")
 | |
|                continue;
 | |
|             break;
 | |
|          case "Comment":
 | |
|             if (param.show == "stories" || !item.story.online ||
 | |
|                   !item.story.discussions || !discussions)
 | |
|                continue;
 | |
|             break;
 | |
|       }
 | |
|       item.renderSkin("historyview");
 | |
|       cnt++;
 | |
|    }
 | |
|    return;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * macro renders a list of available locales as dropdown
 | |
|  */
 | |
| Site.prototype.localechooser_macro = function(param) {
 | |
|    renderLocaleChooser(this.getLocale());
 | |
|    return;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * macro renders a list of available time zones as dropdown
 | |
|  */
 | |
| Site.prototype.timezonechooser_macro = function(param) {
 | |
|    renderTimeZoneChooser(this.getTimeZone());
 | |
|    return;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * renders a list of most read pages, ie. a link
 | |
|  * to a story together with the read counter et al.
 | |
|  */
 | |
| Site.prototype.listMostRead_macro = function() {
 | |
|    var param = new Object();
 | |
|    var size = this.mostread.size();
 | |
|    for (var i=0; i<size; i++) {
 | |
|       var s = this.mostread.get(i);
 | |
|       param.reads = s.reads;
 | |
|       param.rank = i+1;
 | |
|       s.renderSkin("mostread", param);
 | |
|    }
 | |
|    return;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * renders a list of referrers, ie. a link
 | |
|  * to a url together with the read counter et al.
 | |
|  */
 | |
| Site.prototype.listReferrers_macro = function() {
 | |
|    var c = getDBConnection("antville");
 | |
|    var dbError = c.getLastError();
 | |
|    if (dbError)
 | |
|       return getMessage("error.database", dbError);
 | |
|    // we're doing this with direct db access here
 | |
|    // (there's no need to do it with prototypes):
 | |
|    var d = new Date();
 | |
|    d.setDate(d.getDate()-1); // 24 hours ago
 | |
|    var query = "select ACCESSLOG_REFERRER, count(*) as \"COUNT\" from AV_ACCESSLOG " +
 | |
|       "where ACCESSLOG_F_SITE = " + this._id + " and ACCESSLOG_DATE > {ts '" +
 | |
|       d.format("yyyy-MM-dd HH:mm:ss") + "'} group by ACCESSLOG_REFERRER "+
 | |
|       "order by \"COUNT\" desc, ACCESSLOG_REFERRER asc;";
 | |
|    var rows = c.executeRetrieval(query);
 | |
|    var dbError = c.getLastError();
 | |
|    if (dbError)
 | |
|       return getMessage("error.database", dbError);
 | |
|    var skinParam = new Object();
 | |
|    var referrer;
 | |
|    while (rows.next()) {
 | |
|       skinParam.count = rows.getColumnItem("COUNT");
 | |
|       referrer = rows.getColumnItem("ACCESSLOG_REFERRER");
 | |
|       skinParam.referrer = encode(referrer);
 | |
|       skinParam.text = encode(referrer.length > 50 ? referrer.substring(0, 50) + "..." : referrer);
 | |
|       this.renderSkin("referrerItem", skinParam);
 | |
|    }
 | |
|    rows.release();
 | |
|    return;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * renders the xml button for use
 | |
|  * when referring to an rss feed
 | |
|  */
 | |
| Site.prototype.xmlbutton_macro = function(param) {
 | |
|    param.linkto = this.href("rss");   
 | |
|    DefaultImages.render("xmlbutton", param);
 | |
|    return;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * renders the searchbox
 | |
|  */
 | |
| Site.prototype.searchbox_macro = function(param) {
 | |
|    this.renderSkin("searchbox");
 | |
|    return;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * function renders the months of the archive
 | |
|  */
 | |
| Site.prototype.monthlist_macro = function(param) {
 | |
|    if (!this.stories.size() || !this.preferences.getProperty("archive"))
 | |
|       return;
 | |
|    var size = param.limit ? Math.min(this.size(), param.limit) : this.size();
 | |
|    for (var i=0;i<size;i++) {
 | |
|       var curr = this.get(i);
 | |
|       var next = this.get(i+1);
 | |
|       if (!next || next.groupname.substring(0, 6) < curr.groupname.substring(0, 6)) {
 | |
|          res.write(param.itemprefix);
 | |
|          Html.openLink({href: curr.href()});
 | |
|          var ts = curr.groupname.substring(0, 6).toDate("yyyyMM", this.getTimeZone());
 | |
|          res.write(formatTimestamp(ts, param.format ? param.format : "MMMM yyyy"));
 | |
|          Html.closeLink();
 | |
|          res.write(param.itemsuffix);
 | |
|       }
 | |
|    }
 | |
|    return;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * proxy-macro for layout chooser
 | |
|  */
 | |
| Site.prototype.layoutchooser_macro = function(param) {
 | |
|    if (this.layout)
 | |
|       param.selected = this.layout.alias;
 | |
|    this.layouts.layoutchooser_macro(param);
 | |
|    return;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * macro rendering recipients for email notification
 | |
|  * param.event: storycreate/commentcreate/textupdate/upload
 | |
|  * please add some error message for undefined param.event
 | |
|  */
 | |
| Site.prototype.notify_macro = function(param) {
 | |
|    var notifyContributors = param.notifyContributors ? 
 | |
|       param.notifyContributors : getMessage("Site.notifyContributors");
 | |
|    var notifyAdmins = param.notifyAdmins ? 
 | |
|       param.notifyAdmins : getMessage("Site.notifyAdmins");
 | |
|    var notifyNobody = param.notifyNobody ? 
 | |
|       param.notifyNobody : getMessage("Site.notifyNobody");
 | |
| 
 | |
|    var pref = this.preferences.getProperty("notify_" + param.event);
 | |
|    if (param.as == "editor") {
 | |
|       var options = new Array(notifyNobody, notifyAdmins, notifyContributors);
 | |
|       Html.dropDown({name: "notify_" + param.event}, options, pref);
 | |
|    } else {
 | |
|       switch (pref) {
 | |
|          case 2:
 | |
|             return notifyContributors;
 | |
|          case 1:
 | |
|             return notifyAdmins;
 | |
|          default:
 | |
|             return notifyNobody;
 | |
|       }
 | |
|    }
 | |
|    return;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * macro rendering notification settings if enabled
 | |
|  */
 | |
| Site.prototype.notification_macro = function(param) {
 | |
|    if (this.isNotificationEnabled())
 | |
|       this.renderSkin("notification");
 | |
|    return;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * render generic preference editor or value
 | |
|  */
 | |
| Site.prototype.preferences_macro = function(param) {
 | |
|    if (param.as == "editor") {
 | |
|       var inputParam = this.preferences.createInputParam(param.name, param);
 | |
|       delete inputParam.part;
 | |
|       if (param.cols || param.rows)
 | |
|          Html.textArea(inputParam);
 | |
|       else
 | |
|          Html.input(inputParam);
 | |
|    } else
 | |
|       res.write(this.preferences.getProperty(param.name));
 | |
|    return;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * output spamfilter data appropriate
 | |
|  * for client-side javascript code
 | |
|  */
 | |
| Site.prototype.spamfilter_macro = function(param) {
 | |
|    var str = this.preferences.getProperty("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;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * macro returns the used disk space for this site
 | |
|  */
 | |
| Site.prototype.diskusage_macro = function(param) {
 | |
|    res.write(this.getDiskUsage().format("###,###") + " KB");
 | |
|    return;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * macro checks if there are any modules present
 | |
|  * and if they need to be included in the system setup page
 | |
|  */
 | |
| Site.prototype.modulePreferences_macro = function(param) {
 | |
|    for (var i in app.modules)
 | |
|       this.applyModuleMethod(app.modules[i], "renderPreferences", param);
 | |
|    return;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * catch some special needs before passing the 
 | |
|  * macro call up to the HopObject prototype
 | |
|  * FIXME: this is probably to hackish...
 | |
|  */
 | |
| Site.prototype.switch_macro = function(param) {
 | |
|    if (param.name == "userMayEdit") {
 | |
|       try {
 | |
|          // FIXME: unfortunately, the check* methods are
 | |
|          // not very handy, anymore... (need try/catch block)
 | |
|          this.checkEdit(session.user, req.data.memberlevel);
 | |
|          res.write(param.on);
 | |
|       } catch (err) {
 | |
|          res.write(param.off);
 | |
|          return;
 | |
|       }
 | |
|    } else
 | |
|       HopObject.switch_macro.apply(this, [param]);
 | |
|    return;
 | |
| };
 | |
| 
 | |
| /**   
 | |
|  * returns the number of members of this site
 | |
|  */   
 | |
| Site.prototype.membercounter_macro = function(param) {
 | |
|    return this.members.size();
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * renders a dropdown containing the possible occurrences
 | |
|  * of search terms in a content object
 | |
|  */
 | |
| Site.prototype.searchOccurrence_macro = function() {
 | |
|    var options = [["", "anywhere"],
 | |
|                   ["title", "in the title"],
 | |
|                   ["text", "in the text"],
 | |
|                   ["topic", "in the topic name"]];
 | |
|    Html.dropDown({name: "o"}, options, req.data.o);
 | |
|    return;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * renders a dropdown containing some reasonable
 | |
|  * timespans for searching
 | |
|  */
 | |
| Site.prototype.searchCreatetime_macro = function() {
 | |
|    var options = [["", "anytime"],
 | |
|                   ["1", "the past month"],
 | |
|                   ["2", "the past 2 months"],
 | |
|                   ["4", "the past 4 months"],
 | |
|                   ["6", "the past half year"],
 | |
|                   ["12", "the past year"]];
 | |
|    Html.dropDown({name: "ct"}, options, req.data.ct);
 | |
|    return;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * macro counts
 | |
|  */
 | |
| Site.prototype.sysmgr_count_macro = function(param) {
 | |
|    if (!param || !param.what)
 | |
|       return;
 | |
|    // this macro is allowed just for sysadmins
 | |
|    if (!session.user.sysadmin)
 | |
|       return;
 | |
|    switch (param.what) {
 | |
|       case "stories" :
 | |
|          return this.allstories.size();
 | |
|       case "comments" :
 | |
|          return this.allcontent.size() - this.allstories.size();
 | |
|       case "images" :
 | |
|          return this.images.size();
 | |
|       case "files" :
 | |
|          return this.files.size();
 | |
|    }
 | |
|    return;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * function renders the statusflags for this site
 | |
|  */
 | |
| Site.prototype.sysmgr_statusflags_macro = function(param) {
 | |
|    // this macro is allowed just for sysadmins
 | |
|    if (!session.user.sysadmin)
 | |
|       return;
 | |
|    if (this.trusted)
 | |
|       res.write("<span class=\"flagDark\" style=\"background-color:#009900;\">TRUSTED</span>");
 | |
|    if (!this.online)
 | |
|       res.write("<span class=\"flagDark\" style=\"background-color:#CC0000;\">PRIVATE</span>");
 | |
|    else
 | |
|       res.write("<span class=\"flagDark\" style=\"background-color:#006600;\">PUBLIC</span>");      
 | |
|    if (this.blocked)
 | |
|       res.write("<span class=\"flagDark\" style=\"background-color:#000000;\">BLOCKED</span>");
 | |
|    return;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * function renders an edit-link
 | |
|  */
 | |
| Site.prototype.sysmgr_editlink_macro = function(param) {
 | |
|    // this macro is allowed just for sysadmins
 | |
|    if (!session.user.sysadmin || req.data.item == this.alias)
 | |
|       return;
 | |
|    param.linkto = "sites";
 | |
|    param.urlparam = "item=" + this.alias + "&action=edit";
 | |
|    if (req.data.page)
 | |
|       param.urlparam += "&page=" + req.data.page;
 | |
|    param.anchor = this.alias;
 | |
|    Html.openTag("a", root.manage.createLinkParam(param));
 | |
|    res.write(param.text ? param.text : "edit");
 | |
|    Html.closeTag("a");
 | |
|    return;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * function renders a delete-link
 | |
|  */
 | |
| Site.prototype.sysmgr_deletelink_macro = function(param) {
 | |
|    // this macro is allowed just for sysadmins
 | |
|    if (!session.user.sysadmin || req.data.item == this.alias)
 | |
|       return;
 | |
|    param.linkto = "sites";
 | |
|    param.urlparam = "item=" + this.alias + "&action=remove";
 | |
|    if (req.data.page)
 | |
|       param.urlparam += "&page=" + req.data.page;
 | |
|    param.anchor = this.alias;
 | |
|    Html.openTag("a", root.manage.createLinkParam(param));
 | |
|    res.write(param.text ? param.text : "delete");
 | |
|    Html.closeTag("a");
 | |
|    return;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * macro renders the trust-state of this site
 | |
|  */
 | |
| Site.prototype.sysmgr_trusted_macro = function(param) {
 | |
|    // this macro is allowed just for sysadmins
 | |
|    if (!session.user.sysadmin)
 | |
|       return;
 | |
|    if (param.as == "editor") {
 | |
|       var options = [getMessage("generic.no"), getMessage("generic.yes")];
 | |
|       Html.dropDown({name: "trusted"}, options, this.trusted);
 | |
|    } else
 | |
|       res.write(this.trusted ? getMessage("generic.yes") : getMessage("generic.no"));
 | |
|    return;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * macro renders the block-state of this site
 | |
|  */
 | |
| Site.prototype.sysmgr_blocked_macro = function(param) {
 | |
|    // this macro is allowed just for sysadmins
 | |
|    if (!session.user.sysadmin)
 | |
|       return;
 | |
|    if (param.as == "editor") {
 | |
|       var options = [getMessage("generic.no"), getMessage("generic.yes")];
 | |
|       Html.dropDown({name: "blocked"}, options, this.blocked);
 | |
|    } else
 | |
|       res.write(this.blocked ? getMessage("generic.yes") : getMessage("generic.no"));
 | |
|    return;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * function saves new properties of site
 | |
|  * @param Obj Object containing the form values
 | |
|  * @param Obj User-Object modifying this site
 | |
|  * @throws Exception
 | |
|  */
 | |
| Site.prototype.evalPreferences = function(param, modifier) {
 | |
|    if (!evalEmail(param.email))
 | |
|       throw new Exception("emailInvalid");
 | |
|    this.title = stripTags(param.title);
 | |
|    this.email = param.email;
 | |
|    if (this.online && !param.online)
 | |
|       this.lastoffline = new Date();
 | |
|    this.online = param.online ? 1 : 0;
 | |
|    this.enableping = param.enableping ? 1 : 0;
 | |
| 
 | |
|    // store new preferences
 | |
|    var prefs = new HopObject();
 | |
|    for (var i in param) {
 | |
|       if (i.startsWith("preferences_"))
 | |
|          prefs[i.substring(12)] = param[i];
 | |
|    }
 | |
|    prefs.days = !isNaN(parseInt(param.preferences_days, 10)) ? parseInt(param.preferences_days, 10) : 3;
 | |
|    prefs.discussions = param.preferences_discussions ? 1 : 0;
 | |
|    prefs.usercontrib = param.preferences_usercontrib ? 1 : 0;
 | |
|    prefs.archive = param.preferences_archive ? 1 : 0;
 | |
|    // store selected locale
 | |
|    if (param.locale) {
 | |
|       var loc = param.locale.split("_");
 | |
|       prefs.language = loc[0];
 | |
|       prefs.country = loc.length == 2 ? loc[1] : null;
 | |
|    }
 | |
|    prefs.timezone = param.timezone;
 | |
|    prefs.longdateformat = param.longdateformat;
 | |
|    prefs.shortdateformat = param.shortdateformat;
 | |
| 
 | |
|    // layout
 | |
|    this.layout = param.layout ? this.layouts.get(param.layout) : null;
 | |
| 
 | |
|    // e-mail notification
 | |
|    prefs.notify_create = parseInt(param.notify_create, 10) || null;
 | |
|    prefs.notify_update = parseInt(param.notify_update, 10) || null;
 | |
|    prefs.notify_upload = parseInt(param.notify_upload, 10) || null;
 | |
| 
 | |
|    // store preferences
 | |
|    this.preferences.setAll(prefs);
 | |
|    // call the evalPreferences method of every module
 | |
|    for (var i in app.modules)
 | |
|       this.applyModuleMethod(app.modules[i], "evalPreferences", param);
 | |
| 
 | |
| 
 | |
|    // reset cached locale, timezone and dateSymbols
 | |
|    this.cache.locale = null;
 | |
|    this.cache.timezone = null;
 | |
|    this.cache.dateSymbols = null;
 | |
| 
 | |
|    this.modifytime = new Date();
 | |
|    this.modifier = modifier;
 | |
|    return new Message("update");
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * function checks if language and country were specified
 | |
|  * for this site. if so, it returns the specified Locale-object
 | |
|  * otherwise it calls getLocale() for root
 | |
|  */
 | |
| Site.prototype.getLocale = function() {
 | |
|    var locale = this.cache.locale;
 | |
|    if (locale)
 | |
|        return locale;
 | |
|    if (this.preferences.getProperty("language")) {
 | |
|       if (this.preferences.getProperty("country"))
 | |
|          locale = new java.util.Locale(this.preferences.getProperty("language"),
 | |
|                                        this.preferences.getProperty("country"));
 | |
|       else
 | |
|          locale = new java.util.Locale(this.preferences.getProperty("language"));
 | |
|    } else
 | |
|       locale = root.getLocale();
 | |
|    this.cache.locale =locale;
 | |
|    return locale;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * function returns the (already cached) DateFormatSymbols according
 | |
|  * to the locale defined for a site
 | |
|  */
 | |
| Site.prototype.getDateSymbols = function() {
 | |
|    var symbols = this.cache.dateSymbols;
 | |
|    if (symbols)
 | |
|       return symbols;
 | |
|    this.cache.dateSymbols = new java.text.DateFormatSymbols(this.getLocale());
 | |
|    return this.cache.dateSymbols;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * function returns the (already cached) TimeZone-Object
 | |
|  * according to site-preferences
 | |
|  */
 | |
| Site.prototype.getTimeZone = function() {
 | |
|    var tz = this.cache.timezone;
 | |
|    if (tz)
 | |
|        return tz;
 | |
|    if (this.preferences.getProperty("timezone"))
 | |
|        tz = java.util.TimeZone.getTimeZone(this.preferences.getProperty("timezone"));
 | |
|    else
 | |
|        tz = root.getTimeZone();
 | |
|    this.cache.timezone = tz;
 | |
|    return tz;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * function deletes all assets of a site (recursive!)
 | |
|  */
 | |
| Site.prototype.deleteAll = function() {
 | |
|    this.images.deleteAll();
 | |
|    this.files.deleteAll();
 | |
|    // FIXME: add deleting of all layouts!
 | |
|    // this.layouts.deleteAll();
 | |
|    this.stories.deleteAll();
 | |
|    this.members.deleteAll();
 | |
|    return true;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * send notification to weblogs.com
 | |
|  * that this site was updated
 | |
|  * @return Object with properties error and message
 | |
|  */
 | |
| Site.prototype.ping = function() {
 | |
|    var title = this.title ? this.title : this.alias;
 | |
| 
 | |
|    // we're doing it the xml-rpc way
 | |
|    // (specs at http://newhome.weblogs.com/directory/11)
 | |
|    var xr = new Remote("http://rpc.weblogs.com/RPC2");
 | |
|    var ping = xr.weblogUpdates.ping(title, this.href());
 | |
|    if (!ping.result)
 | |
|       return;
 | |
|    var result = new Object();
 | |
|    result.error = ping.result.flerror;
 | |
|    result.message = ping.result.message;
 | |
| 
 | |
|    if (result.error)
 | |
|       app.log("Error when notifying weblogs.com for updated site \"" + this.alias + "\": " + result.message);
 | |
| 
 | |
|    // lastping is always set to now to prevent blogs
 | |
|    // hanging in the scheduler if a fatal error occurs
 | |
|    this.lastping = new Date();
 | |
|    return result;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  *  href URL postprocessor. If a virtual host mapping is defined
 | |
|  *  for this site's alias, use it. Otherwise, use normal site URL.
 | |
|  */
 | |
| Site.prototype.processHref = function(href) {
 | |
|    var vhost = app.properties["vhost." + this.alias];
 | |
|    if (vhost)
 | |
|       return vhost + href;
 | |
|    else
 | |
|       return app.properties.defaulthost + "/" + this.alias + href;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * basic check if email notification is enabled for a site
 | |
|  * @param Obj site object
 | |
|  * @return Boolean true if notification is enabled, false otherwise
 | |
|  */
 | |
| Site.prototype.isNotificationEnabled = function() {
 | |
|    if (root.sys_allowEmails == 1 || root.sys_allowEmails == 2 && this.trusted)
 | |
|       return true;
 | |
|    return false;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * send e-mail notification if necessary
 | |
|  * @param String type of changes (e.g. createStory)
 | |
|  * @param HopObject the HopObject the changes were applied to
 | |
|  */
 | |
| Site.prototype.sendNotification = function(type, obj) {
 | |
|    var notify = this.preferences.getProperty("notify_" + type);
 | |
|    if (obj.online === 0 || !notify || notify == 0)
 | |
|       return;
 | |
|    var recipients = new Array();
 | |
|    for (var i=0; i<this.members.size(); i++) {
 | |
|       var m = this.members.get(i);
 | |
|       if ((type != "update" && m.user == obj.creator) || (type == "update" && m.user == obj.modifier))
 | |
|          continue;
 | |
|       if (notify == 1 && m.level >= CONTENTMANAGER)
 | |
|          recipients.push(m.user.email);
 | |
|       else if (notify == 2 && m.level >= CONTRIBUTOR)
 | |
|          recipients.push(m.user.email);
 | |
|    }
 | |
|    if (recipients.length > 0) {
 | |
|       var param = {
 | |
|          user: obj.modifier ? obj.modifier.name :
 | |
|             (obj.creator ? obj.creator.name : null),
 | |
|          url: obj.href()
 | |
|       };
 | |
|       var sender = root.sys_title + "<" + root.sys_email + ">";
 | |
|       var subject = "[" + root.sys_title + "] " + getMessage("mail.notification");
 | |
|       var body = this.renderSkinAsString("notificationMail", param);
 | |
|       sendMail(sender, recipients, subject, body);
 | |
|    }
 | |
|    return;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * return the currently enabled layout object
 | |
|  */
 | |
| Site.prototype.getLayout = function() {
 | |
|    if (this.layout)
 | |
|       return this.layout;
 | |
|    return root.getLayout();
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * render the path to the static directory
 | |
|  * of this site
 | |
|  * @param String name of subdirectory (optional)
 | |
|  */
 | |
| Site.prototype.staticPath = function(subdir) {
 | |
|    res.write(app.properties.staticPath);
 | |
|    res.write(this.alias);
 | |
|    res.write("/");
 | |
|    if (subdir)
 | |
|       res.write(subdir);
 | |
|    return;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * return the path to the static directory
 | |
|  * of this site
 | |
|  * @param String name of subdirectory (optional)
 | |
|  * @return String path to the static directory
 | |
|  */
 | |
| Site.prototype.getStaticPath = function(subdir) {
 | |
|    res.push();
 | |
|    this.staticPath(subdir);
 | |
|    return res.pop();
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * render the url of the static directory
 | |
|  * of this site
 | |
|  * @param String optional subdirectory
 | |
|  */
 | |
| Site.prototype.staticUrl = function(subdir) {
 | |
|    res.write(app.properties.staticUrl);
 | |
|    res.write(this.alias);
 | |
|    res.write("/");
 | |
|    if (subdir)
 | |
|       res.write(subdir);
 | |
|    return;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * return the url of the static directory
 | |
|  * of this site
 | |
|  * @param String optional subdirectory
 | |
|  * @return String static url
 | |
|  */
 | |
| Site.prototype.getStaticUrl = function(subdir) {
 | |
|    res.push();
 | |
|    this.staticUrl(subdir);
 | |
|    return res.pop();
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * return the directory containing static contents
 | |
|  * @param String subdirectory (optional)
 | |
|  * @return Object File object
 | |
|  */
 | |
| Site.prototype.getStaticDir = function(subdir) {
 | |
|    var f = new Helma.File(this.getStaticPath(subdir));
 | |
|    f.mkdir();
 | |
|    return f;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * function returns the title of a site
 | |
|  */
 | |
| Site.prototype.getTitle = function() {
 | |
|    if (this.title && this.title.trim())
 | |
|      return stripTags(this.title);
 | |
|    else
 | |
|      return "[" + getMessage("generic.untitled") + "]";
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * function returns the used disk space for this site in Kilobyte
 | |
|  */
 | |
| Site.prototype.getDiskUsage = function() {
 | |
|    if (this.diskusage == null) {
 | |
|       this.diskusage = 0;
 | |
|       for (var i=0; i<this.files.count(); i++)
 | |
|          this.diskusage += this.files.get(i).filesize;
 | |
|       for (var i=0; i<this.images.count(); i++) {
 | |
|          if (this.images.get(i).filesize == null)
 | |
|             this.images.get(i).filesize = this.images.get(i).getFile().getLength();
 | |
|          this.diskusage += this.images.get(i).filesize;
 | |
|       }
 | |
|    }
 | |
|    return Math.round(this.diskusage / 1024);
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * function returns the disk quota in Kilobyte for this site
 | |
|  */
 | |
| Site.prototype.getDiskQuota = function() {
 | |
|    if (this.trusted || !root.sys_diskQuota) 
 | |
|       return Infinity;
 | |
|    else 
 | |
|       return root.sys_diskQuota;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * returns the corresponding Index object for a site
 | |
|  * for performance reasons the Index object is cached
 | |
|  */
 | |
| Site.prototype.getIndex = function(force) {
 | |
|    if (!this.cache.index || force) {
 | |
|       var baseDir = new Helma.File(app.properties.indexPath);
 | |
|       var index, analyzer;
 | |
|       if (this.getLocale().getLanguage() == java.util.Locale.GERMAN)
 | |
|          analyzer = Search.getAnalyzer(java.util.Locale.GERMAN);
 | |
|       else
 | |
|          analyzer = Search.getAnalyzer();
 | |
|       // try to mount an existing index, if this fails create a new one
 | |
|       try {
 | |
|          index = Search.mountIndex(this.alias, baseDir, analyzer);
 | |
|          app.log("[" + this.alias + "] mounted index " + index);
 | |
|       } catch (e) {
 | |
|          try {
 | |
|             index = Search.createIndex(this.alias, baseDir, analyzer);
 | |
|             app.log("[" + this.alias + "] created index " + index);
 | |
|          } catch (e) {
 | |
|             throw ("[" + this.alias + "] Error: unable to mount or create index");
 | |
|             return;
 | |
|          }
 | |
|       }
 | |
|       this.cache.index = index;
 | |
|    }
 | |
|    return this.cache.index;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * re-indexes all stories and comments of a site
 | |
|  * @param Object instance of Search.Index
 | |
|  * @return void
 | |
|  */
 | |
| Site.prototype.rebuildIndex = function() {
 | |
|    /**
 | |
|     * private method for constructing an index document
 | |
|     * based on the data retrieved via direct db
 | |
|     */
 | |
|    function getIndexDocument() {
 | |
|       var doc = new Search.Document();
 | |
|       doc.addField("prototype", rows.getColumnItem("TEXT_PROTOTYPE"));
 | |
|       switch (rows.getColumnItem("TEXT_PROTOTYPE")) {
 | |
|          case "Comment":
 | |
|             doc.addField("story", rows.getColumnItem("TEXT_F_TEXT_STORY"), {store: true, index: true, tokenize: false});
 | |
|             if (parent = rows.getColumnItem("TEXT_F_TEXT_PARENT"))
 | |
|                doc.addField("parent", parent, {store: true, index: true, tokenize: false});
 | |
|             break;
 | |
|          default:
 | |
|             doc.addField("day", rows.getColumnItem("TEXT_DAY"), {store: true, index: true, tokenize: false});
 | |
|             if (topic = rows.getColumnItem("TEXT_TOPIC"))
 | |
|                doc.addField("topic", topic, {store: true, index: true, tokenize: true});
 | |
|             break;
 | |
|       }
 | |
|    
 | |
|       doc.addField("online", rows.getColumnItem("TEXT_ISONLINE"), {store: true, index: true, tokenize: false});
 | |
|       doc.addField("site", self._id, {store: true, index: true, tokenize: false});
 | |
|       doc.addField("id", rows.getColumnItem("TEXT_ID"), {store: true, index: true, tokenize: false});
 | |
|       var content = Xml.readFromString(rows.getColumnItem("TEXT_CONTENT"));
 | |
|       for (var propName in content) {
 | |
|          doc.addField((propName == "title") ? "title" : "text",
 | |
|                       stripTags(content[propName]),
 | |
|                       {store: false, index: true, tokenize: true});
 | |
|       }
 | |
|       if (creator = rows.getColumnItem("USER_NAME")) {
 | |
|          doc.addField("creator", creator, {store: false, index: true, tokenize: false});
 | |
|          doc.addField("createtime", (new Date(rows.getColumnItem("TEXT_CREATETIME").getTime())).format("yyyyMMdd", locale, timeZone),
 | |
|                       {store: false, index: true, tokenize: false});
 | |
|       }
 | |
|       return doc;
 | |
|    }
 | |
| 
 | |
|    var parent, topic, title, text, creator;
 | |
|    // lock the index queue to prevent it
 | |
|    // from being flushed by the IndexManager
 | |
|    var queue = app.data.indexManager.getQueue(this);
 | |
|    queue.lock();
 | |
|    var index = this.getIndex();
 | |
|    index.clear();
 | |
| 
 | |
|    var buf = new java.util.Vector(500, 500);
 | |
|    var cnt = 0;
 | |
|    var now = new Date();
 | |
|    var start = new Date();
 | |
|    var locale = this.getLocale();
 | |
|    var timeZone = this.getTimeZone();
 | |
|    var dbCon = getDBConnection("antville");
 | |
|    var rows = dbCon.executeRetrieval("select TEXT_ID, TEXT_PROTOTYPE, TEXT_DAY, TEXT_TOPIC, TEXT_ALIAS, TEXT_F_TEXT_STORY, TEXT_F_TEXT_PARENT, TEXT_PROTOTYPE, TEXT_ISONLINE, TEXT_CONTENT, USER_NAME, TEXT_CREATETIME from AV_TEXT, AV_USER where TEXT_F_SITE = " + this._id + " and TEXT_F_USER_CREATOR = USER_ID");
 | |
|    app.log("[" + this.alias + "] retrieved indexable contents in " + ((new Date()).diff(now)) + " ms");
 | |
|    if (dbCon.lastError != null) {
 | |
|       app.log("[" + this.alias + "] unable to retrieve indexable contents, reason: " + dbCon.lastError);
 | |
|    } else {
 | |
|       var self = this;
 | |
|       while (rows.next()) {
 | |
|          try {
 | |
|             buf.add(getIndexDocument());
 | |
|             if (cnt > 0 && cnt % 2000 == 0) {
 | |
|                index.addDocument(buf);
 | |
|                buf.clear();
 | |
|                app.log("[" + this.alias + "] added " + cnt + " documents to index (last 1000 in " + ((new Date()).diff(now)) + " ms)");
 | |
|                now = new Date();
 | |
|             }
 | |
|          } catch (e) {
 | |
|             app.log("[" + this.alias + "] Error: unable to add document " + cnt + " to index. Reason: " + e.toString());
 | |
|          }
 | |
|          cnt++;
 | |
|       }
 | |
|       rows.release();
 | |
|       index.addDocument(buf);
 | |
|       app.log("[" + this.alias + "] finished adding " + cnt + " documents to index in " + ((new Date()).diff(start)) + " ms");
 | |
|       index.optimize();
 | |
|    }
 | |
| 
 | |
|    // unlock the queue again
 | |
|    queue.unlock();
 | |
|    return cnt;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * check if there are any stories in the previous month
 | |
|  */
 | |
| Site.prototype.renderLinkToPrevMonth = function(firstDayIndex, currentMonth, monthNames) {
 | |
|    var l = this.size();
 | |
|    if (l == 0 || l <= firstDayIndex)
 | |
|       return " ";
 | |
| 
 | |
|    var prevDay = this.get(firstDayIndex + 1);
 | |
|    if (prevDay && prevDay.groupname < currentMonth) {
 | |
|       var month = prevDay.groupName.toString().substring(4, 6) - 1;
 | |
|       return Html.linkAsString({href: prevDay.href()}, monthNames[month]);
 | |
|    } else {
 | |
|       return " ";
 | |
|    }
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * check if there are any stories in the previous month
 | |
|  */
 | |
| Site.prototype.renderLinkToNextMonth = function(lastDayIndex, currentMonth, monthNames) {
 | |
|    var l = this.size();
 | |
|    if (l == 0 || lastDayIndex == 0)
 | |
|       return " ";
 | |
| 
 | |
|    var nextDay = this.get(lastDayIndex - 1);
 | |
|    if (nextDay && nextDay.groupname > currentMonth) {
 | |
|       var month = nextDay.groupName.toString().substring(4, 6) - 1;
 | |
|       return Html.linkAsString({href: nextDay.href()}, monthNames[month]);
 | |
|    } else {
 | |
|       return " ";
 | |
|    }
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * function renders the list of stories for site-(front-)pages
 | |
|  * and assigns the rendered list to res.data.storylist
 | |
|  * scrollnavigation-links to previous and next page(s) are also
 | |
|  * assigned to res.data (res.data.prevpage, res.data.nextpage)
 | |
|  * using this separate renderFunction instead of doing the stuff
 | |
|  * in storylist_macro() was necessary for completely independent
 | |
|  * placement of the prevpage- and nextpage-links
 | |
|  * @param Int Index-position to start with
 | |
|  */
 | |
| Site.prototype.renderStorylist = function(day) {
 | |
|    var size = this.size();
 | |
|    var idx = 0;
 | |
| 
 | |
|    // if no day is specified, start with today. we may need 
 | |
|    // to search for today's entries (or the latest entry 
 | |
|    // before today) because there may be stories posted for 
 | |
|    // future days. (HW)
 | |
|    var startdayString = day;
 | |
|    if (!startdayString)
 | |
|       startdayString = formatTimestamp(new Date(), "yyyyMMdd");
 | |
| 
 | |
|    var startday = this.get(startdayString);
 | |
|    if (startday && startday.size()>0) {
 | |
|       idx = this.contains(startday);
 | |
|    } else {
 | |
|       // loop through days until we find a day less or equal than 
 | |
|       // the one we're looking for.
 | |
|       for (var i=0; i<size; i++) {
 | |
|          if (startdayString >= this.get(i).groupname) {
 | |
|             this.get(i).prefetchChildren();
 | |
|             idx = i;
 | |
|             break;
 | |
|          }
 | |
|       }
 | |
|    }
 | |
|    var days = this.preferences.getProperty("days") ? this.preferences.getProperty("days") : 2;
 | |
|    days = Math.min (days, 14);  // render 14 days max
 | |
|    this.prefetchChildren(idx, days);
 | |
| 
 | |
|    // only display "newer stories" if we are actually browsing the archive, 
 | |
|    // and the day parameter has been explicitly specified, 
 | |
|    // i.e. suppress the link if we are on the home page and there are 
 | |
|    // stories on future days. (HW)
 | |
|    if (idx > 0 && day) {
 | |
|       var sp = new Object();
 | |
|       var prev = this.get (Math.max(0, idx-days));
 | |
|       sp.url = this.href() + "?day=" + prev.groupname;
 | |
|       sp.text = getMessage("Story.newerStories");
 | |
|       res.data.prevpage = renderSkinAsString("prevpagelink", sp);
 | |
|    }
 | |
|    days = Math.min(idx + days++, this.size());
 | |
|    res.push();
 | |
|    while (idx < days) {
 | |
|       var day = this.get(idx++);
 | |
|       day.get(0).renderSkin("dayheader");
 | |
|       for (var i=0;i<day.size();i++)
 | |
|          day.get(i).renderSkin("preview");
 | |
|    }
 | |
|    res.data.storylist = res.pop();
 | |
|    if (idx < size) {
 | |
|       var sp = new Object();
 | |
|       var next = this.get (idx);
 | |
|       sp.url = this.href() + "?day=" + next.groupname;
 | |
|       sp.text = getMessage("Story.olderStories");
 | |
|       res.data.nextpage = renderSkinAsString("nextpagelink", sp);
 | |
|    }
 | |
|    return;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * permission check (called by hopobject.onRequest())
 | |
|  * @param String name of action
 | |
|  * @param Obj User object
 | |
|  * @param Int Membership level
 | |
|  * @return Obj Exception object or null
 | |
|  */
 | |
| Site.prototype.checkAccess = function(action, usr, level) {
 | |
|    var url = this.members.href("login");
 | |
|    try {
 | |
|       switch (action) {
 | |
|          case "main" :
 | |
|             if (!this.online)
 | |
|                checkIfLoggedIn();
 | |
|             url = root.href();
 | |
|             this.checkView(usr, level);
 | |
|             break;
 | |
|          case "edit" :
 | |
|             checkIfLoggedIn();
 | |
|             url = this.href();
 | |
|             this.checkEdit(usr, level);
 | |
|             break;
 | |
|          case "delete" :
 | |
|             checkIfLoggedIn();
 | |
|             url = this.href();
 | |
|             this.checkDelete(usr, level);
 | |
|             break;
 | |
|          case "getfile" :
 | |
|             if (!this.online)
 | |
|                checkIfLoggedIn();
 | |
|             url = root.href();
 | |
|             this.checkView(usr, level);
 | |
|             break;
 | |
|          case "mostread" :
 | |
|             if (!this.online)
 | |
|                checkIfLoggedIn();
 | |
|             url = root.href();
 | |
|             this.checkView(usr, level);
 | |
|             break;
 | |
|          case "referrers" :
 | |
|             if (!this.online)
 | |
|                checkIfLoggedIn();
 | |
|             url = root.href();
 | |
|             this.checkView(usr, level);
 | |
|             break;
 | |
|          case "search" :
 | |
|             if (!this.online)
 | |
|                checkIfLoggedIn();
 | |
|             url = root.href();
 | |
|             this.checkView(usr, level);
 | |
|             break;
 | |
|          case "subscribe" :
 | |
|             checkIfLoggedIn();
 | |
|             this.checkSubscribe(usr, level);
 | |
|             break;
 | |
|          case "unsubscribe" :
 | |
|             checkIfLoggedIn();
 | |
|             this.checkUnsubscribe(usr, level);
 | |
|             break;
 | |
|       }
 | |
|    } catch (deny) {
 | |
|       res.message = deny.toString();
 | |
|       res.redirect(url);
 | |
|    }
 | |
|    return;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * check if a user is allowed to view a site
 | |
|  * @param Obj Userobject
 | |
|  * @param Int Permission-Level
 | |
|  */
 | |
| Site.prototype.checkView = function(usr, level) {
 | |
|    if (!this.online) {
 | |
|       // if not logged in or not logged in with sufficient permissions
 | |
|       if (!usr || (level < CONTRIBUTOR && !usr.sysadmin))
 | |
|          throw new DenyException("siteView");
 | |
|    }
 | |
|    return;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * check if user is allowed to edit the preferences of this site
 | |
|  * @param Obj Userobject
 | |
|  * @param Int Permission-Level
 | |
|  * @return String Reason for denial (or null if allowed)
 | |
|  */
 | |
| Site.prototype.checkEdit = function(usr, level) {
 | |
|    if (!usr.sysadmin && (level & MAY_EDIT_PREFS) == 0)
 | |
|       throw new DenyException("siteEdit");
 | |
|    return null;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * check if user is allowed to delete the site
 | |
|  * (only SysAdmins or the creator of a site are allowed to delete it!)
 | |
|  * @param Obj Userobject
 | |
|  * @return String Reason for denial (or null if allowed)
 | |
|  */
 | |
| Site.prototype.checkDelete = function(usr) {
 | |
|    if (!usr.sysadmin && usr != this.creator)
 | |
|       throw new DenyException("siteDelete");
 | |
|    return;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * function checks if user is allowed to sign up
 | |
|  * @param Obj Userobject
 | |
|  * @param Int Permission-Level
 | |
|  * @return String Reason for denial (or null if allowed)
 | |
|  */
 | |
| Site.prototype.checkSubscribe = function(usr, level) {
 | |
|    if (level != null)
 | |
|       throw new Exception("subscriptionExist");
 | |
|    else if (!this.online)
 | |
|       throw new DenyException("siteView");
 | |
|    return;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * check if user is allowed to unsubscribe
 | |
|  * @param Obj Userobject
 | |
|  * @return String Reason for denial (or null if allowed)
 | |
|  */
 | |
| Site.prototype.checkUnsubscribe = function(usr, level) {
 | |
|    if (level == null)
 | |
|       throw new Exception("subscriptionNoExist");
 | |
|    else if (level == ADMIN) {
 | |
|       // Admins are not allowed to remove a subscription
 | |
|       throw new DenyException("unsubscribe");
 | |
|    }
 | |
|    return;
 | |
| };
 |