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.requests = 0;
 51    file.creator = file.modifier = user;
 52    file.created = file.modified = new Date;
 53    file.update(data);
 54    site.files.add(file);
 55    return file;
 56 }
 57 
 58 /**
 59  * 
 60  */
 61 File.remove = function() {
 62    if (this.constructor === File) {
 63       this.getFile().remove();
 64       this.deleteMetadata();
 65       this.remove();
 66    }
 67    return;
 68 }
 69 
 70 /**
 71  * 
 72  * @param {String} name
 73  */
 74 File.getName = function(name) {
 75    if (name) {
 76       //return name.replace(/[^\w\d\s._-]/g, String.EMPTY);
 77       return String(name).trim().replace(/[\/\\:;?+\[\]{}|#"`<>^]/g, String.EMPTY);
 78    }
 79    return null;
 80 }
 81 
 82 /**
 83  *
 84  * @param {String } url
 85  */
 86 File.redirectOnUploadError = function(url) {
 87    if (req.data.helma_upload_error) {
 88       res.message = gettext("Sorry, the file exceeds the maximum upload limit of {0} kB.",
 89             formatNumber(app.appsProperties.uploadLimit));
 90       res.redirect(url);
 91    }
 92    return;
 93 }
 94 
 95 /**
 96  *
 97  * @param {String} url
 98  */
 99 File.redirectOnExceededQuota = function(url) {
100    if (res.handlers.site.getDiskSpace() < 0) {
101       res.message = gettext("Sorry, there is no disk space left. Please try to delete some files or images first.");
102       res.redirect(url);
103    }
104    return;
105 }
106 
107 /**
108  * @name File
109  * @constructor
110  * @property {Date} created
111  * @property {User} creator
112  * @property {Metadata} metadata
113  * @property {Date} modified
114  * @property {User} modifier
115  * @property {String} name
116  * @property {Number} parent_id
117  * @property {String} parent_type
118  * @property {String} prototype
119  * @property {Number} requests
120  * @property {Site} site
121  * @extends HopObject
122  */
123 File.prototype.constructor = function() {
124    return this;
125 }
126 
127 /**
128  * 
129  * @param {String} action
130  * @return {Boolean}
131  */
132 File.prototype.getPermission = function(action) {
133    switch (action) {
134       case ".":
135       case "main":
136       return true;
137       case "delete":
138       case "edit":
139       return this._parent.getPermission("main") &&
140             this.creator === session.user || 
141             Membership.require(Membership.MANAGER) ||
142             User.require(User.PRIVILEGED);            
143    }
144    return false;
145 }
146 
147 File.prototype.main_action = function() {
148    if (Membership.require(Membership.SUBSCRIBER) &&
149          User.require(User.REGULAR)) {
150       this.requests += 1;
151    }
152    return res.redirect(this.getUrl());
153 }
154 
155 File.prototype.edit_action = function() {
156    File.redirectOnUploadError(this.href(req.action));
157 
158    if (req.postParams.save) {
159       try {
160          File.redirectOnExceededQuota(this.href(req.action));
161          this.update(req.postParams);
162          res.message = gettext("The changes were saved successfully.");
163          res.redirect(this._parent.href());
164       } catch (ex) {
165          res.message = ex;
166          app.log(ex);
167       }
168    }
169    
170    res.data.action = this.href(req.action);
171    res.data.title = gettext("Edit File");
172    res.data.body = this.renderSkinAsString("$File#edit");
173    return this.site.renderSkin("Site#page");
174 }
175 
176 /**
177  * 
178  * @param {String} name
179  * @returns {Object}
180  */
181 File.prototype.getFormValue = function(name) {
182    var self = this;
183    
184    var getOrigin = function(str) {
185       var origin = req.postParams.file_origin || self.origin;
186       if (origin && origin.contains("://")) {
187          return origin;
188       }
189       return null;
190    }
191    
192    if (req.isPost()) {
193       if (name === "file") {
194          return getOrigin();
195       }
196       return req.postParams[name];
197    }
198    switch (name) {
199       case "file":
200       return getOrigin();
201    }
202    return this[name];
203 }
204 
205 /**
206  * 
207  * @param {Object} data
208  */
209 File.prototype.update = function(data) {
210    if (data.uploadError) {
211       app.log(data.uploadError);
212       // Looks like the file uploaded has exceeded the upload limit ...
213       throw Error(gettext("File size is exceeding the upload limit."));
214    }
215       
216    if (!data.file_origin) {
217       if (this.isTransient()) { 
218          throw Error(gettext("There was nothing to upload. Please be sure to choose a file."));
219       }
220    } else if (data.file_origin !== this.origin) {
221       var mime = data.file;
222       if (mime.contentLength < 1) {
223          mime = getURL(data.file_origin);
224          if (!mime) {
225             throw Error(gettext("Could not fetch the file from the given URL."));
226          }
227       }
228 
229       this.origin = data.file_origin;
230       var mimeName = mime.normalizeFilename(mime.name);
231       this.contentLength = mime.contentLength;
232       this.contentType = mime.contentType;
233       
234       if (!this.name) {
235           var name = File.getName(data.name) || mimeName.split(".")[0];
236           this.name = this.site.files.getAccessName(name);
237       }
238 
239       // Make the file persistent before proceeding with writing 
240       // it to disk (also see Helma bug #607)
241       this.isTransient() && this.persist();
242 
243       var extension = mimeName.substr(mimeName.lastIndexOf(".")) || String.EMPTY;
244       var fileName = this.name + extension;
245       if (fileName !== this.fileName) {
246          // Remove existing file if the file name has changed
247          this.getFile().remove();
248       }
249       this.fileName = fileName;
250       var file = this.getFile();
251       mime.writeToFile(file.getParent(), file.getName());
252    }
253       
254    // FIXME: one day? 
255    //this.setTags(data.tags || data.tag_array); 
256    this.description = data.description;
257    this.touch();
258    return;
259 }
260 
261 /**
262  * 
263  */
264 File.prototype.url_macro = function() {
265    return res.write(this.url || this.getUrl());
266 }
267 
268 /**
269  * 
270  * @param {Object} param
271  */
272 File.prototype.contentLength_macro = function(param) {
273    return res.write((this.contentLength / 1024).format("###,###") + " KB");
274 }
275 
276 /**
277  * 
278  */
279 File.prototype.getFile = function() {
280    var site = this.site || res.handlers.site;
281    return site.getStaticFile("files/" + this.fileName);
282 }
283 
284 /**
285  * 
286  */
287 File.prototype.getUrl = function() {
288    var site = this.site || res.handlers.site;
289    return site.getStaticUrl("files/" + this.fileName);
290 }
291 
292 /**
293  * @returns {String}
294  */
295 File.prototype.getConfirmText = function() {
296    return gettext("You are about to delete the file {0}.", this.name);
297 }
298