1 // The Antville Project
  2 // http://code.google.com/p/antville
  3 //
  4 // Copyright 2007-2011 by Tobi Schäfer.
  5 //
  6 // Copyright 2001–2007 Robert Gaggl, Hannes Wallnöfer, Tobi Schäfer,
  7 // Matthias & Michael Platzer, Christoph Lincke.
  8 //
  9 // Licensed under the Apache License, Version 2.0 (the ``License'');
 10 // you may not use this file except in compliance with the License.
 11 // You may obtain a copy of the License at
 12 //
 13 //    http://www.apache.org/licenses/LICENSE-2.0
 14 //
 15 // Unless required by applicable law or agreed to in writing, software
 16 // distributed under the License is distributed on an ``AS IS'' BASIS,
 17 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 18 // See the License for the specific language governing permissions and
 19 // limitations under the License.
 20 //
 21 // $Revision$
 22 // $Author$
 23 // $Date$
 24 // $URL$
 25 
 26 /**
 27  * @fileOverview Defines the Comment prototype.
 28  */
 29 
 30 markgettext("File");
 31 markgettext("file");
 32 
 33 this.handleMetadata("url");
 34 this.handleMetadata("description");
 35 this.handleMetadata("contentType");
 36 this.handleMetadata("contentLength");
 37 this.handleMetadata("fileName");
 38 
 39 /**
 40  * @param {Object} data
 41  * @param {Site} site
 42  * @param {User} user
 43  * @returns {File}
 44  */
 45 File.add = function(data, site, user) {
 46    site || (site = res.handlers.site);
 47    user || (user = session.user);
 48    var file = new File;
 49    file.site = site;
 50    file.update(data);
 51    file.creator = file.modifier = user;
 52    site.files.add(file);
 53    return file;
 54 }
 55 
 56 /**
 57  * 
 58  */
 59 File.remove = function() {
 60    if (this.constructor === File) {
 61       this.getFile().remove();
 62       this.deleteMetadata();
 63       this.remove();
 64    }
 65    return;
 66 }
 67 
 68 /**
 69  * 
 70  * @param {String} name
 71  */
 72 File.getName = function(name) {
 73    if (name) {
 74       //return name.replace(/[^\w\d\s._-]/g, String.EMPTY);
 75       return String(name).trim().replace(/[\/\\:;?+\[\]{}|#"`<>^]/g, String.EMPTY);
 76    }
 77    return null;
 78 }
 79 
 80 /**
 81  *
 82  * @param {String } url
 83  */
 84 File.redirectOnUploadError = function(url) {
 85    if (req.data.helma_upload_error) {
 86       res.message = gettext("Sorry, the file exceeds the maximum upload limit of {0} kB.",
 87             formatNumber(app.appsProperties.uploadLimit));
 88       res.redirect(url);
 89    }
 90    return;
 91 }
 92 
 93 /**
 94  *
 95  * @param {String} url
 96  */
 97 File.redirectOnExceededQuota = function(url) {
 98    if (res.handlers.site.getDiskSpace() < 0) {
 99       res.message = gettext("Sorry, there is no disk space left. Please try to delete some files or images first.");
100       res.redirect(url);
101    }
102    return;
103 }
104 
105 /**
106  * @name File
107  * @constructor
108  * @property {Date} created
109  * @property {User} creator
110  * @property {Metadata} metadata
111  * @property {Date} modified
112  * @property {User} modifier
113  * @property {String} name
114  * @property {Number} parent_id
115  * @property {String} parent_type
116  * @property {String} prototype
117  * @property {Number} requests
118  * @property {Site} site
119  * @extends HopObject
120  */
121 File.prototype.constructor = function() {
122    this.creator = this.modifier = session.user;
123    this.created = this.modified = new Date;
124    this.requests = 0;
125    return this;
126 }
127 
128 /**
129  * 
130  * @param {String} action
131  * @return {Boolean}
132  */
133 File.prototype.getPermission = function(action) {
134    switch (action) {
135       case ".":
136       case "main":
137       return true;
138       case "delete":
139       case "edit":
140       return this._parent.getPermission("main") &&
141             this.creator === session.user || 
142             Membership.require(Membership.MANAGER) ||
143             User.require(User.PRIVILEGED);            
144    }
145    return false;
146 }
147 
148 File.prototype.main_action = function() {
149    if (Membership.require(Membership.SUBSCRIBER) &&
150          User.require(User.REGULAR)) {
151       this.requests += 1;
152    }
153    return res.redirect(this.getUrl());
154 }
155 
156 File.prototype.edit_action = function() {
157    File.redirectOnUploadError(this.href(req.action));
158 
159    if (req.postParams.save) {
160       try {
161          File.redirectOnExceededQuota(this.href(req.action));
162          this.update(req.postParams);
163          res.message = gettext("The changes were saved successfully.");
164          res.redirect(this._parent.href());
165       } catch (ex) {
166          res.message = ex;
167          app.log(ex);
168       }
169    }
170    
171    res.data.action = this.href(req.action);
172    res.data.title = gettext("Edit File");
173    res.data.body = this.renderSkinAsString("$File#edit");
174    return this.site.renderSkin("Site#page");
175 }
176 
177 /**
178  * 
179  * @param {String} name
180  * @returns {Object}
181  */
182 File.prototype.getFormValue = function(name) {
183    var self = this;
184    
185    var getOrigin = function(str) {
186       var origin = req.postParams.file_origin || self.origin;
187       if (origin && origin.contains("://")) {
188          return origin;
189       }
190       return null;
191    }
192    
193    if (req.isPost()) {
194       if (name === "file") {
195          return getOrigin();
196       }
197       return req.postParams[name];
198    }
199    switch (name) {
200       case "file":
201       return getOrigin();
202    }
203    return this[name];
204 }
205 
206 /**
207  * 
208  * @param {Object} data
209  */
210 File.prototype.update = function(data) {
211    if (data.uploadError) {
212       app.log(data.uploadError);
213       // Looks like the file uploaded has exceeded the upload limit ...
214       throw Error(gettext("File size is exceeding the upload limit."));
215    }
216       
217    if (!data.file_origin) {
218       if (this.isTransient()) { 
219          throw Error(gettext("There was nothing to upload. Please be sure to choose a file."));
220       }
221    } else if (data.file_origin !== this.origin) {
222       var mime = data.file;
223       if (mime.contentLength < 1) {
224          mime = getURL(data.file_origin);
225          if (!mime) {
226             throw Error(gettext("Could not fetch the file from the given URL."));
227          }
228       }
229 
230       this.origin = data.file_origin;
231       var mimeName = mime.normalizeFilename(mime.name);
232       this.contentLength = mime.contentLength;
233       this.contentType = mime.contentType;
234       
235       if (!this.name) {
236           var name = File.getName(data.name) || mimeName.split(".")[0];
237           this.name = this.site.files.getAccessName(name);
238       }
239 
240       // Make the file persistent before proceeding with writing 
241       // it to disk (also see Helma bug #607)
242       this.isTransient() && this.persist();
243 
244       var extension = mimeName.substr(mimeName.lastIndexOf(".")) || String.EMPTY;
245       var fileName = this.name + extension;
246       if (fileName !== this.fileName) {
247          // Remove existing file if the file name has changed
248          this.getFile().remove();
249       }
250       this.fileName = fileName;
251       var file = this.getFile();
252       mime.writeToFile(file.getParent(), file.getName());
253    }
254       
255    // FIXME: one day? 
256    //this.setTags(data.tags || data.tag_array); 
257    this.description = data.description;
258    this.touch();
259    return;
260 }
261 
262 /**
263  * 
264  */
265 File.prototype.url_macro = function() {
266    return res.write(this.url || this.getUrl());
267 }
268 
269 /**
270  * 
271  * @param {Object} param
272  */
273 File.prototype.contentLength_macro = function(param) {
274    return res.write((this.contentLength / 1024).format("###,###") + " KB");
275 }
276 
277 /**
278  * 
279  */
280 File.prototype.getFile = function() {
281    var site = this.site || res.handlers.site;
282    return site.getStaticFile("files/" + this.fileName);
283 }
284 
285 /**
286  * 
287  */
288 File.prototype.getUrl = function() {
289    var site = this.site || res.handlers.site;
290    return site.getStaticUrl("files/" + this.fileName);
291 }
292 
293 /**
294  * @returns {String}
295  */
296 File.prototype.getConfirmText = function() {
297    return gettext("You are about to delete the file {0}.", this.name);
298 }
299