1 /* Copyright (C) 2000-2006 Thomas Bopp, Thorsten Hampel, Ludger Merkens
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, write to the Free Software
15 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 * $Id: Document.pike,v 1.3 2010/08/18 20:32:45 astra Exp $
19 inherit "/classes/Object" : __object;
20 inherit "/base/content" : __content;
21 #include <attributes.h>
28 //! A Document is an object with content (bytes). The content is stored
29 //! in the persistency layer of steam. When content is changed the
30 //! EVENT_UPLOAD and EVENT_DOWNLOAD events are trigered.
31 class Document : public Object,content{
41 void init_document() { }
46 * Init callback function.
54 __content::init_content();
61 * Called after the document was loaded by database.
72 * Called after the document was loaded by database.
84 * Duplicate the Document and its content.
86 * @return the duplicate of this document.
88 mapping do_duplicate(void|mapping params)
90 // DocumentFactory deals with content_obj variable
91 if ( !mappingp(params) )
95 if ( params->content_id )
96 params->content_obj = 0;
98 params->mimetype = do_query_attribute(DOC_MIME_TYPE);
99 // do not copy thumbnail attribute - thumbnails are
100 // generated on the fly
101 m_delete(params, DOC_IMAGE_THUMBNAIL);
103 return ::do_duplicate( params );
107 * Destructor function of this object removes all references
108 * and deletes the content.
116 if ( mappingp(mReferences) ) {
117 foreach(indices(mReferences), object o) {
118 if ( !objectp(o) ) continue;
123 // delete all versions, try to make it as atomic as possible:
124 mapping versions = do_query_attribute(DOC_VERSIONS);
125 if ( mappingp(versions) ) {
126 // gather all versions (recursively):
127 array all_versions = ({ });
128 foreach ( values(versions), object v ) {
129 if ( !objectp(v) ) continue;
130 mapping v_versions = v->query_attribute(DOC_VERSIONS);
131 if ( !mappingp(v_versions) ) continue;
132 foreach ( values(v_versions), object v2 ) {
133 // add v's old version if it is valid and has not already been added:
134 if ( !objectp(v2) ) continue;
135 if ( search(all_versions, v2) >= 0 ) continue;
136 all_versions |= ({ v2 });
139 // check if there is any broken version reference:
140 foreach ( all_versions, object v ) {
141 if ( !objectp(v) || v->status() == PSTAT_DELETED )
142 continue; // already deleted
144 object v_versionof = v->query_attribute(OBJ_VERSIONOF);
145 if ( !objectp(v_versionof) ) {
146 steam_error( "Version mismatch: old version %O doesn't seem to be a "
147 "version of any object.", v );
150 (search( all_versions, v_versionof ) < 0) )
151 steam_error( "Version mismatch: old version %O is a version of a "
152 "different object: %O.", v, v_versionof );
154 // delete old versions, prevent recursions:
155 foreach ( all_versions, object v ) {
156 if ( !objectp(v) || v->status() == PSTAT_DELETED )
157 continue; // already deleted
159 v->set_attribute( DOC_VERSIONS, ([ ]) );
160 v->set_attribute( OBJ_VERSIONOF, 0 );
166 FATAL("Error while deleting document %O\n%O", err[0], err[1]);
167 err=catch(::delete_content());
169 FATAL("Error while deleting document content %O\n%O", err[0], err[1]);
176 * Adding data storage is redirected to objects functionality.
178 * @param function a - store function
179 * @param function b - restore function
180 * @return whether adding was successfull.
184 add_data_storage(string a,function b, function c, int|void d)
186 return __object::add_data_storage(a,b,c,d);
192 * Get the content size of this document.
194 * @return the content size in bytes.
196 int get_content_size()
198 return __content::get_content_size();
202 * Returns the id of the content inside the Database.
204 * @return the content-id inside database
206 final int get_content_id()
208 return __content::get_content_id();
212 * Callback function when a download has finished.
216 void download_finished()
218 run_event(EVENT_DOWNLOAD_FINISHED, CALLER);
224 * give status of Document similar to file->stat()
227 * @return ({ \o700, size, atime, mtime, ctime, uid, 0 })
232 int creator_id = get_creator() ? get_creator()->get_object_id() : -1;
237 do_query_attribute(OBJ_CREATION_TIME),
238 do_query_attribute(DOC_LAST_MODIFIED) ||
239 do_query_attribute(OBJ_CREATION_TIME),
240 do_query_attribute(DOC_LAST_ACCESSED),
243 query_attribute(DOC_MIME_TYPE), // aditional, should not be a prob?
248 void update_mimetype(string mime)
250 object typeModule = get_module("types");
251 if ( objectp(typeModule) ) {
252 string classname = typeModule->query_document_class_mime(mime);
253 // document classes are changed, but never to 'Document',
254 // because all derived classes only include some special functionality
255 if ( classname != get_class() && classname != "Document" ) {
256 werror("updating mimetype for %s to %s (class=%s, new_class=%s\n",
257 get_identifier(), mime, get_class(), classname);
259 object fulltext = get_module("fulltext");
260 if (objectp(fulltext))
266 mixed set_attribute(string index, mixed data)
268 mixed res = ::set_attribute(index, data);
270 object caller = CALLER;
271 if ( functionp(caller->get_object_class) &&
272 caller->get_object_class() & CLASS_FACTORY )
275 if ( index == OBJ_NAME )
277 if ( do_query_attribute(DOC_MIME_TYPE) == MIMETYPE_UNKNOWN )
279 // try to find mimetype by new name ...
280 object typeModule = get_module("types");
281 if ( objectp(typeModule) && stringp(data) && strlen(data) > 0 )
284 sscanf(data, "%*s.%s", ext);
285 string mime = typeModule->query_mime_type(ext);
286 if ( mime != MIMETYPE_UNKNOWN )
288 do_set_attribute(DOC_MIME_TYPE, mime);
289 update_mimetype(mime);
294 else if ( index == DOC_MIME_TYPE ) {
295 update_mimetype(data);
300 int get_object_class() { return ::get_object_class() | CLASS_DOCUMENT; }
301 final bool is_document() { return true; }
304 * content function used for download, this function really resides in
305 * base/content and this overridden function just runs the appropriate event
307 * @return the function for downloading (when socket has free space)
308 * @see receive_content
310 function get_content_callback(mapping vars)
314 if ( functionp(obj->get_user_object) && objectp(obj->get_user_object()) )
315 obj = obj->get_user_object();
318 try_event(EVENT_DOWNLOAD, obj);
320 do_set_attribute(DOC_LAST_ACCESSED, time());
322 run_event(EVENT_DOWNLOAD, obj);
324 return __content::get_content_callback(vars);
328 * Get the content of this document as a string.
330 * @param int|void len - optional parameter length of content to return.
331 * @return the content or the first len bytes of it.
332 * @see get_content_callback
334 string get_content(int|void len)
341 try_event(EVENT_DOWNLOAD, obj);
342 content = ::get_content(len);
344 do_set_attribute(DOC_LAST_ACCESSED, time());
346 run_event(EVENT_DOWNLOAD, obj);
350 object get_content_file(string mode, mapping vars, string|void client)
355 void check_lock(string type, void|string lock)
357 if ( type == "write" ) {
358 // if lock is available and we get lock for token everything is fine
362 if ( mappingp(locked) )
363 steam_error("Unable to write locked object, unlock first ! (#%d)",
370 * Lock the content of this object.
372 * @param object group - the locking group.
373 * @param string type - the type of the lock, "read" or "write"
374 * @return the content or the first len bytes of it.
375 * @see get_content_callback
377 mapping lock_content(object group, string type, int timeout)
380 try_event(EVENT_LOCK, CALLER, group, type, timeout);
381 if ( type == "write" ) {
382 steam_error("Cannot lock locked resources !");
383 object locks = do_query_attribute(OBJ_LOCK) || ([ ]);
384 locks[data->token] = data;
385 do_set_attribute(OBJ_LOCK, locks);
388 run_event(EVENT_LOCK, CALLER, group, type, timeout);
392 void unlock_content(void|string token)
394 try_event(EVENT_UNLOCK, CALLER);
395 mapping locks = do_query_attribute(OBJ_LOCK) || ([ ]);
397 m_delete(locks, token);
401 do_set_attribute(OBJ_LOCK, locks);
402 run_event(EVENT_UNLOCK, CALLER);
407 * Callback function called when upload is finished.
411 void content_finished()
413 __content::content_finished();
414 run_event(EVENT_UPLOAD, this_user(), get_content_size());
420 * content function used for upload, this function really resides in
421 * base/content and this overridden function just runs the appropriate event
423 * @return the function for uploading (called each time a chunk is received)
424 * @see get_content_callback
426 function receive_content(int content_size, void|string lock)
428 prepare_upload(content_size, lock);
429 return __content::receive_content(content_size);
433 void prepare_upload(int content_size, void|string lock)
437 (obj->get_object_class() & CLASS_USER) &&
438 (functionp(obj->get_user_object) ) &&
439 objectp(obj->get_user_object()) )
440 obj = obj->get_user_object();
442 check_lock("write", lock);
444 try_event(EVENT_UPLOAD, obj, content_size);
446 int version = do_query_attribute(DOC_VERSION);
450 seteuid(get_creator());
453 object oldversion = duplicate( ([ "content_id": get_content_id(),
454 oldversion->set_attribute(DOC_VERSION, version);
455 oldversion->set_attribute(DOC_LAST_MODIFIED, do_query_attribute(DOC_LAST_MODIFIED));
456 oldversion->set_attribute(DOC_USER_MODIFIED, do_query_attribute(DOC_USER_MODIFIED));
457 oldversion->set_attribute(OBJ_CREATION_TIME, do_query_attribute(OBJ_CREATION_TIME));
458 mapping versions = do_query_attribute(DOC_VERSIONS);
459 oldversion->set_attribute(DOC_VERSIONS, copy_value(versions));
460 if ( !mappingp(versions) )
462 versions[version] = oldversion;
465 do_set_attribute(DOC_VERSIONS, versions);
468 FATAL("Failed to create old version of document: %O\n%O",
473 do_set_attribute(DOC_VERSION, version);
475 set_attribute(DOC_LAST_MODIFIED, time());
476 set_attribute(DOC_USER_MODIFIED, this_user());
483 * See whether the content is locked by someone or not.
485 * @return the locking object.
491 object get_previous_version()
493 mapping versions = do_query_attribute(DOC_VERSIONS);
494 int version = do_query_attribute(DOC_VERSION);
495 if ( objectp(versions[version-1]) )
496 return versions[version-1];
497 while ( version-- > 0 )
498 if ( objectp(versions[version]) )
499 return versions[version];
505 int lm = do_query_attribute(DOC_LAST_MODIFIED);
506 string etag = sprintf("%018x",iObjectID + (lm<<64));
507 if ( sizeof(etag) > 18 ) etag = etag[(sizeof(etag)-18)..];
508 return etag[0..4]+"-"+etag[5..10]+"-"+etag[11..17];
514 return get_identifier()+"(#"+get_object_id()+","+
515 master()->describe_program(object_program(this_object()))+","+
516 get_object_class()+","+do_query_attribute(DOC_MIME_TYPE)+")";