1 // 2 // The Antville Project 3 // http://code.google.com/p/antville 4 // 5 // Copyright 2001-2007 by The Antville People 6 // 7 // Licensed under the Apache License, Version 2.0 (the ``License''); 8 // you may not use this file except in compliance with the License. 9 // You may obtain a copy of the License at 10 // 11 // http://www.apache.org/licenses/LICENSE-2.0 12 // 13 // Unless required by applicable law or agreed to in writing, software 14 // distributed under the License is distributed on an ``AS IS'' BASIS, 15 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 // See the License for the specific language governing permissions and 17 // limitations under the License. 18 // 19 // $Revision$ 20 // $LastChangedBy$ 21 // $LastChangedDate$ 22 // $URL$ 23 // 24 25 /** 26 * @fileOverview Defines the Root prototype. 27 */ 28 29 /** @constant */ 30 Root.VERSION = "1.2.1"; 31 32 this.handleMetadata("creationDelay"); 33 this.handleMetadata("creationScope"); 34 this.handleMetadata("notificationScope"); 35 this.handleMetadata("phaseOutGracePeriod"); 36 this.handleMetadata("phaseOutNotificationPeriod"); 37 this.handleMetadata("phaseOutMode"); 38 this.handleMetadata("probationPeriod"); 39 this.handleMetadata("quota"); 40 this.handleMetadata("replyTo"); 41 42 /** 43 * Antville’s root object is an extent of the Site prototype. 44 * @name Root 45 * @constructor 46 * @property {Site[]} _children 47 * @property {Admin} admin 48 * @property {User[]} admins 49 * @property {Api} api 50 * @property {String} creationDelay 51 * @property {String} creationScope 52 * @property {String} notificationScope 53 * @property {String} phaseOutGracePeriod 54 * @property {String} phaseOutMode 55 * @property {String} phaseOutNotificationPeriod 56 * @property {String} probationPeriod 57 * @property {String} quote 58 * @property {String} replyTo 59 * @property {Site[]} sites 60 * @property {Site[]} updates 61 * @property {User[]} users 62 * @extends Site 63 */ 64 65 /** 66 * 67 * @param {String} action 68 * @returns {Boolean} 69 */ 70 Root.prototype.getPermission = function(action) { 71 if (action.contains("admin")) { 72 return User.require(User.PRIVILEGED); 73 } 74 switch (action) { 75 case "debug": 76 case "default.hook": 77 case "health": 78 case "mrtg": 79 case "sites": 80 case "updates.xml": 81 return true; 82 case "create": 83 return this.getCreationPermission(); 84 } 85 return Site.prototype.getPermission.apply(this, arguments); 86 } 87 88 Root.prototype.main_action = function() { 89 if (this.users.size() < 1) { 90 this.title = "Antville"; 91 this.created = new Date; 92 this.replyTo = "root@localhost"; 93 this.locale = java.util.Locale.getDefault().getLanguage(); 94 this.timeZone = java.util.TimeZone.getDefault().getID(); 95 this.layout.reset(); 96 res.redirect(this.members.href("register")); 97 } else if (session.user && this.members.owners.size() < 1) { 98 this.creator = this.modifier = this.layout.creator = 99 this.layout.modifier = session.user; 100 this.created = this.modified = this.layout.created = 101 this.layout.modified = new Date; 102 session.user.role = User.PRIVILEGED; 103 res.handlers.membership.role = Membership.OWNER; 104 } 105 return Site.prototype.main_action.apply(this); 106 } 107 108 Root.prototype.error_action = function() { 109 res.message = String.EMPTY; 110 var param = res.error ? res : session.data; 111 res.status = param.status || 500; 112 res.data.title = gettext("{0} {1} Error", root.getTitle(), param.status); 113 res.data.body = root.renderSkinAsString("$Root#error", param); 114 res.handlers.site.renderSkin("Site#page"); 115 return; 116 } 117 118 Root.prototype.notfound_action = function() { 119 res.status = 404; 120 res.data.title = gettext("{0} {1} Error", root.getTitle(), res.status); 121 res.data.body = root.renderSkinAsString("$Root#notfound", req); 122 res.handlers.site.renderSkin("Site#page"); 123 return; 124 } 125 126 Root.prototype.create_action = function() { 127 if (req.postParams.create) { 128 try { 129 var site = new Site; 130 site.update(req.postParams); 131 site.layout.reset(); 132 this.add(site); 133 site.members.add(new Membership(session.user, Membership.OWNER)); 134 root.admin.log(root, "Added site " + site.name); 135 res.message = gettext("Successfully created your site."); 136 res.redirect(site.href()); 137 } catch (ex) { 138 res.message = ex; 139 app.log(ex); 140 } 141 } 142 143 // Cannot use res.handlers.site because somehow it is always root 144 res.handlers.newSite = new Site; 145 res.handlers.example = new Site; 146 res.handlers.example.name = "foo"; 147 res.data.action = this.href(req.action); 148 res.data.title = gettext("Add Site"); 149 res.data.body = root.renderSkinAsString("$Root#create"); 150 root.renderSkin("Site#page"); 151 return; 152 } 153 154 Root.prototype.sites_action = function() { 155 var now = new Date; 156 if (!this.cache.sites || (now - this.cache.sites.modified > Date.ONEHOUR)) { 157 var sites = this.sites.list(); 158 sites.sort(new String.Sorter("title")); 159 this.cache.sites = {list: sites, modified: now}; 160 } 161 res.data.list = renderList(this.cache.sites.list, 162 "$Site#listItem", 25, req.queryParams.page); 163 res.data.pager = renderPager(this.cache.sites.list, 164 this.href(req.action), 25, req.queryParams.page); 165 res.data.title = gettext("Public Sites"); 166 res.data.body = this.renderSkinAsString("$Root#sites"); 167 root.renderSkin("Site#page"); 168 return; 169 } 170 171 Root.prototype.updates_xml_action = function() { 172 var now = new Date; 173 var feed = new rome.SyndFeedImpl(); 174 feed.setFeedType("rss_2.0"); 175 feed.setLink(root.href()); 176 feed.setTitle("Recently updated sites at " + root.title); 177 feed.setDescription(root.tagline); 178 feed.setLanguage(root.locale.replace("_", "-")); 179 feed.setPublishedDate(now); 180 var entries = new java.util.ArrayList(); 181 var entry, description; 182 var sites = root.updates.list(0, 25).sort(Number.Sorter("modified", 183 Number.Sorter.DESC)); 184 for each (var site in sites) { 185 entry = new rome.SyndEntryImpl(); 186 entry.setTitle(site.title); 187 entry.setLink(site.href()); 188 entry.setAuthor(site.creator.name); 189 entry.setPublishedDate(site.modified); 190 description = new rome.SyndContentImpl(); 191 description.setType("text/plain"); 192 description.setValue(site.tagline); 193 entry.setDescription(description); 194 entries.add(entry); 195 } 196 feed.setEntries(entries); 197 var output = new rome.SyndFeedOutput(); 198 //output.output(feed, res.servletResponse.writer); return; 199 var xml = output.outputString(feed); 200 res.contentType = "text/xml"; 201 res.write(xml); //injectXslDeclaration(xml)); 202 return; 203 } 204 205 /** 206 * Sitemap for Google Webmaster Tools 207 * (Unfortunately, utterly useless.) 208 */ 209 Root.prototype.sitemap_xml_action = function() { 210 res.contentType = "text/xml"; 211 res.writeln('<?xml version="1.0" encoding="UTF-8"?>'); 212 res.writeln('<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">'); 213 this.sites.forEach(function() { 214 res.writeln('<url>'); 215 res.writeln('<loc>' + this.href() + '</loc>'); 216 if (this.modified) { 217 res.writeln('<lastmod>' + this.modified.format("yyyy-MM-dd") + '</lastmod>'); 218 } 219 res.writeln('</url>'); 220 }); 221 res.writeln('</urlset>'); 222 return; 223 } 224 225 Root.prototype.health_action = function() { 226 var jvm = java.lang.Runtime.getRuntime(); 227 var totalMemory = jvm.totalMemory() / 1024 / 1024; 228 var freeMemory = jvm.freeMemory() / 1024 / 1024; 229 230 var param = { 231 uptime: formatNumber((new Date - app.upSince.getTime()) / 232 Date.ONEDAY, "0.##"), 233 freeMemory: formatNumber(freeMemory), 234 totalMemory: formatNumber(totalMemory), 235 usedMemory: formatNumber(totalMemory - freeMemory), 236 sessions: formatNumber(app.countSessions()), 237 cacheSize: formatNumber(getProperty("cacheSize")) 238 }; 239 240 for each (key in ["activeThreads", "freeThreads", "requestCount", 241 "errorCount", "xmlrpcCount", "cacheusage"]) { 242 param[key] = formatNumber(app[key]); 243 } 244 245 if (Admin.health) { 246 param.requestsPerUnit = formatNumber(Admin.health.requestsPerUnit); 247 param.errorsPerUnit = formatNumber(Admin.health.errorsPerUnit); 248 } 249 250 param.entries = app.data.entries.length; 251 param.mails = app.data.mails.length; 252 param.requests = 0; 253 for (var i in app.data.requests) { 254 param.requests += 1; 255 } 256 param.callbacks = app.data.callbacks.length; 257 258 res.data.title = gettext("{0} Health", root.getTitle()); 259 res.data.body = this.renderSkinAsString("$Root#health", param); 260 this.renderSkin("Site#page"); 261 } 262 263 Root.prototype.mrtg_action = function() { 264 res.contentType = "text/plain"; 265 var target = req.queryParams.target; 266 if (!target) { 267 return; 268 } 269 switch (target) { 270 case "cache": 271 res.writeln(0); 272 res.writeln(app.cacheusage * 100 / getProperty("cacheSize", 100)); 273 break; 274 case "threads": 275 res.writeln(0); 276 res.writeln(app.activeThreads * 100 / app.freeThreads); 277 break; 278 case "requests": 279 res.writeln(app.errorCount); 280 res.writeln(app.requestCount); 281 break; 282 case "users": 283 res.writeln(app.countSessions()); 284 res.writeln(root.users.size()); 285 break; 286 case "postings": 287 res.writeln(0); 288 var sql = new Sql; 289 sql.retrieve(Sql.COUNT, "content"); 290 sql.traverse(function() { 291 res.writeln(this.count); 292 }); 293 break; 294 case "uploads": 295 var sql = new Sql; 296 sql.retrieve(Sql.COUNT, "file"); 297 sql.traverse(function() { 298 res.writeln(this.count); 299 }); 300 sql.retrieve(Sql.COUNT, "image"); 301 sql.traverse(function() { 302 res.writeln(this.count); 303 }); 304 break; 305 } 306 res.writeln(app.upSince); 307 res.writeln("mrtg." + target + " of Antville version " + Root.VERSION); 308 return; 309 } 310 311 /** 312 * 313 * @param {String} name 314 * @returns {HopObject} 315 * @see Site#getMacroHandler 316 */ 317 Root.prototype.getMacroHandler = function(name) { 318 switch (name) { 319 case "admin": 320 case "api": 321 case "sites": 322 return this[name]; 323 } 324 return Site.prototype.getMacroHandler.apply(this, arguments); 325 } 326 327 /** 328 * 329 * @param {String} name 330 * @returns {Object} 331 * @see Site#getFormOptions 332 */ 333 Root.prototype.getFormOptions = function(name) { 334 switch (name) { 335 case "creationScope": 336 return Admin.getCreationScopes(); 337 case "notificationScope": 338 return Admin.getNotificationScopes(); 339 case "phaseOutMode": 340 return Admin.getPhaseOutModes(); 341 } 342 return Site.prototype.getFormOptions.apply(root, arguments); 343 } 344 345 /** 346 * @returns {Boolean} 347 */ 348 Root.prototype.getCreationPermission = function() { 349 var user; 350 if (!(user = session.user)) { 351 return false; 352 } if (User.require(User.PRIVILEGED)) { 353 return true; 354 } 355 356 switch (root.creationScope) { 357 case User.PRIVILEGED: 358 return false; 359 case User.TRUSTED: 360 return User.require(User.TRUSTED); 361 default: 362 case User.REGULAR: 363 var now = new Date, delta; 364 if (root.probationPeriod) { 365 delta = root.probationPeriod - Math.floor((now - 366 user.created) / Date.ONEDAY); 367 if (delta > 0) { 368 session.data.error = gettext("You need to wait {0} before you are allowed to create a new site.", 369 ngettext("{0} day", "{0} days", delta)); 370 return false; 371 } 372 } 373 if (root.creationDelay && user.sites.count() > 0) { 374 delta = root.creationDelay - Math.floor((now - 375 user.sites.get(0).created) / Date.ONEDAY); 376 if (delta > 0) { 377 session.data.error = gettext("You need to wait {0} before you are allowed to create a new site.", 378 ngettext("{0} day", "{0} days", delta)); 379 return false; 380 } 381 } 382 } 383 return true; 384 } 385