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: DocumentFactory.pike,v 1.2 2009/08/07 15:22:36 nicke Exp $
19 inherit "/factories/ObjectFactory";
21 #include <attributes.h>
27 class DocumentFactory : public ObjectFactory{
35 * Initialization callback for the factory.
43 init_class_attribute(DOC_LAST_MODIFIED, CMD_TYPE_TIME,
45 0, EVENT_ATTRIBUTES_CHANGE, 0,
46 CONTROL_ATTR_SERVER, 0);
48 init_class_attribute(DOC_USER_MODIFIED, CMD_TYPE_OBJECT,
49 "last modified by user",
50 0, EVENT_ATTRIBUTES_CHANGE, 0,
51 CONTROL_ATTR_SERVER, 0);
53 init_class_attribute(DOC_LAST_ACCESSED, CMD_TYPE_TIME,
55 0, EVENT_ATTRIBUTES_CHANGE, 0,
56 CONTROL_ATTR_SERVER, 0);
58 init_class_attribute(DOC_MIME_TYPE, CMD_TYPE_STRING,
59 "for example text/html",
60 0, EVENT_ATTRIBUTES_CHANGE, 0,
61 CONTROL_ATTR_SERVER, "");
63 init_class_attribute(DOC_VERSIONS, CMD_TYPE_MAPPING,
64 "versioning of a document",
65 0, EVENT_ATTRIBUTES_CHANGE, 0,
66 CONTROL_ATTR_SERVER, ([ ]));
68 init_class_attribute(DOC_TYPE, CMD_TYPE_STRING,
69 "the document type/extension",
70 0, EVENT_ATTRIBUTES_CHANGE, 0,
71 CONTROL_ATTR_SERVER,"");
73 init_class_attribute(DOC_TIMES_READ, CMD_TYPE_INT, "how often read",
74 0, EVENT_ATTRIBUTES_CHANGE, 0,
75 CONTROL_ATTR_SERVER, 0);
77 init_class_attribute(DOC_AUTHORS, CMD_TYPE_ARRAY, "Names of Authors",
78 0, EVENT_ATTRIBUTES_CHANGE, 0,
79 CONTROL_ATTR_USER, ({ }) );
83 init_class_attribute(DOC_BIBTEX, CMD_TYPE_STRING,
84 "Bibtex entry of a publication",
85 0, EVENT_ATTRIBUTES_CHANGE, 0,
86 CONTROL_ATTR_USER, "");
91 * Create a new instance of a document. The vars mapping should contain
92 * the following entries:
93 * url - the filename or
95 * mimetype - the mime type (optional)
96 * externURL - content should be downloaded from the given URL.
98 * @param mapping vars - some variables for the document creation.
99 * @return the newly created document.
101 object execute(mapping vars)
103 string ext, doc_class, fname, mimetype;
108 string url = vars["url"];
109 if ( !stringp(url) ) {
115 fname = basename(url);
116 folder = dirname(url);
117 if ( strlen(folder) > 0 ) {
118 cont = get_module("filepath:tree")->path_to_object(folder, true);
119 if ( !objectp(cont) )
120 steam_error("The Container " + folder + " was not found!");
121 if ( !(cont->get_object_class() & CLASS_CONTAINER) )
122 steam_error("The destination path is not a container !");
127 try_event(EVENT_EXECUTE, CALLER, url);
129 if (!mappingp(vars->attributes))
130 vars->attributes = ([ ]);
132 if ( stringp(vars["mimetype"]) )
133 mimetype = vars["mimetype"];
134 else if ( stringp(vars["attributes"][DOC_MIME_TYPE]) )
135 mimetype = vars["attributes"][DOC_MIME_TYPE];
137 if ( !stringp(mimetype) ||
138 mimetype == "auto-detect" ||
139 search(mimetype, "/") == -1 )
141 tokens = fname / ".";
142 if ( sizeof(tokens) >= 2 ) {
143 ext = tokens[-1]; // last token ?
144 ext = lower_case(ext);
149 mimetype = _TYPES->query_mime_type(ext);
150 doc_class = _TYPES->query_document_class(ext);
154 doc_class = _TYPES->query_document_class_mime(mimetype);
156 vars->attributes[DOC_MIME_TYPE] = mimetype;
158 if ( vars->transient ) {
159 if ( mappingp(vars->attributes) )
160 vars->attributes[OBJ_TEMP] = 1;
162 vars->attributes = ([ OBJ_TEMP : 1 ]);
165 SECURITY_LOG("creating " + doc_class);
166 if ( objectp(vars["move"]) ) {
167 obj = object_create(fname, doc_class, vars["move"],
169 vars["attributesAcquired"],
170 vars["attributesLocked"],
172 vars["sanctionMeta"]);
174 else if ( objectp(cont) )
176 // Object is created somewhere
177 SECURITY_LOG("Creating new object in "+ folder);
178 obj = object_create(fname, doc_class, cont,
180 vars["attributesAcquired"],
181 vars["attributesLocked"],
183 vars["sanctionMeta"]);
186 SECURITY_LOG("Creating new object in void");
188 fname, doc_class, 0, vars["attributes"],
189 vars["attributesAcquired"], vars["attributesLocked"],
191 vars["sanctionMeta"]);
194 function obj_set_attribute = obj->get_function("do_set_attribute");
196 if ( objectp(obj) ) {
197 obj_set_attribute(DOC_TYPE, ext);
198 obj_set_attribute(DOC_MIME_TYPE, mimetype);
200 if ( objectp(vars["acquire"]) )
201 obj->set_acquire(vars["acquire"]);
203 if ( objectp(vars["content_obj"]) ) {
204 string content = vars["content_obj"]->get_content();
205 if ( stringp(content) )
206 obj->set_content(content);
208 if ( vars->content_id ) {
209 object caller = CALLER;
210 if ( !_SECURITY->valid_object(caller) )
211 steam_error("Calling object tries to change content id - not authorized!");
212 if ( caller->get_content_id() != vars->content_id )
213 steam_error("Only versioning is able to reuse content ids (caller: %O)!", caller);
214 obj->set_content_id(vars->content_id);
218 if ( stringp(vars["externURL"]) ) {
222 if ( sscanf(vars->externURL, "%s?%s", uri, r_vars) == 2 ) {
224 array index = r_vars / "&";
225 if ( arrayp(index) ) {
226 foreach(index, string zuweisung) {
227 sscanf(zuweisung, "%s=%s", k, v);
232 sscanf(r_vars, "%s=%s", k, v);
236 thread_create(download_url, obj, uri, va);
239 run_event(EVENT_EXECUTE, CALLER, url);
245 * Download content from an extern URL.
247 * @param object doc - the document
248 * @param string url - the URL to download from.
251 void download_url(object doc, string url, void|mapping vars)
253 MESSAGE("Downloading url: %s, %O", url, vars);
254 array res = Protocols.HTTP.get_url_nice(url, vars);
256 doc->set_content(res[1]);
257 doc->set_attribute(DOC_MIME_TYPE, res[0]);
260 FATAL("An error occured while trying to download %s", url);
265 int change_document_class(object doc)
267 // change class depending on mime type
268 if ( !_SECURITY->access_write(0, doc, CALLER) )
271 string mime = doc->query_attribute(DOC_MIME_TYPE);
272 object typesModule = get_module("types");
273 if ( !objectp(typesModule) )
274 steam_error("Unable to find required types module !");
275 string classname = typesModule->query_document_class_mime(mime);
276 if ( classname != doc->get_class() ) {
277 if ( _Persistence->change_object_class(doc, classname) ) {
278 MESSAGE("DocumentFactory: Changing class from " +
279 doc->get_class() + " to " + classname);
280 call(doc->drop, 0.0);
283 FATAL("DocumentFactory: Failed to change class from "+
284 doc->get_class() + " to " + classname);
289 string get_identifier() { return "Document.factory"; }
290 string get_class_name() { return "Document"; }
291 int get_class_id() { return CLASS_DOCUMENT; }
295 object doc = execute( ([ "name" : "test it.jpg", ]) );
296 Test.test( "image mimetype in .jpg document",
297 ( search(doc->query_attribute(DOC_MIME_TYPE), "image/") == 0 ) );
300 doc = execute ( ([ "name": "test.html", "mimetype":"text/html", ]) );
301 Test.test( "text/html mimetype produces DocHTML class",
302 (doc->get_object_class() & CLASS_DOCHTML) );
303 Test.test( "got same mimetype as defined",
304 ( doc->query_attribute(DOC_MIME_TYPE) == "text/html" ) );
305 doc->set_content("test");
308 doc->get_content() == "test");
310 object dup = doc->duplicate();
311 Test.test("Duplicated Object",
312 dup->get_object_class()==doc->get_object_class());
313 Test.test("Duplicated Name",
314 dup->get_identifier() == doc->get_identifier());
315 Test.test("Duplicated Content",
316 dup->get_content() == doc->get_content());
321 doc = execute ( ([ "name": "test.test.html", ]) );
322 Test.test( ".html ending produces DocHTML",
323 (doc->get_object_class() & CLASS_DOCHTML) );
324 Test.test( "got same mimetype as expected",
325 ( doc->query_attribute(DOC_MIME_TYPE) == "text/html" ) );
327 // try to switch document type
328 doc->set_attribute(DOC_MIME_TYPE, "text/xsl");
329 // document classes are changed, but never to 'Document',
330 // because all derived classes only include some special functionality
331 string mclass = get_module("types")->query_document_class_mime("text/xsl");
333 Test.test( "text/xsl produces DocXSL",
334 ( mclass == "DocXSL" ) );
336 Test.test( "URL with folder submitted throws",
337 catch( execute( ([ "url": "time/money" ]) ) ) );
339 object test1 = execute( ([ "name" : "test", ]) );
340 Test.test( "name test produces unknown mimetype",
341 ( test1->query_attribute(DOC_MIME_TYPE) == MIMETYPE_UNKNOWN ) );
343 test1->set_attribute(OBJ_NAME, "test.xsl");
345 object script = execute( ([ "name": "test.pike", ]) );
346 Test.test( ".pike provides DocLpc instance",
347 ( script->get_class() == "DocLpc" ) );
348 Test.start_test(script);
350 object xsl = execute( ([ "name":"test.xsl" ]) );
351 Test.start_test(xsl);
354 cont = get_module("filepath:url")->path_to_object("/__test__");
358 cont = get_factory(CLASS_CONTAINER)->execute((["name":"test_container"]));
359 cont->set_acquire(0);
360 cont->set_attribute(OBJ_URL, "/__test__");
361 cont->move(OBJ("/"));
363 Test.test("container path",
364 _FILEPATH->object_to_filename(cont) == "/test_container",
365 "Wrong path for Container: "+_FILEPATH->object_to_filename(cont));
367 object file = execute( (["name":"test.txt"]) );
368 file->set_content("Hello World!");
371 file->query_attribute(OBJ_PATH)=="/test_container/test.txt",
372 "False path for test.txt in test_container: "+
373 file->query_attribute(OBJ_PATH));
374 doc->set_attribute(OBJ_NAME, "doc.xsl");
376 cont->set_attribute(OBJ_NAME, "testcontainer");
377 Test.test("renamed path",
378 file->query_attribute(OBJ_PATH)=="/testcontainer/test.txt",
379 "False path for test.txt in testcontainer: " +
380 file->query_attribute(OBJ_PATH));
382 if ( objectp(doc) && objectp(test1) ) {
383 // needs thread/call_out because Protocols.HTTP would block
384 Test.add_test_function( test_more, 5, doc, test1, mclass, cont, 1 );
387 Test.skipped( "additional tests", "test objects were not created" );
393 void test_threaded(object cont, object doc, object test1)
395 object user = this_user() || USER("root");
397 Standards.URI url = Standards.URI("https://"+_Server->query_config("web_server") + "/create.xml");
399 if ( objectp(OBJ("/documents/create.xml")) ) {
401 url->password = user->get_ticket(time()+600);
402 Protocols.HTTP.put_url(url, CONTENTOF("/documents/create.xml"));
403 Test.test( "uploading /create.xml to "+ url->get_path_query(),
404 objectp(doc = _FILEPATH->path_to_object("/create.xml")) );
405 Test.test( "correct creator of uploaded /create.xml",
406 (objectp(doc) && doc->get_creator() == user ) );
407 Test.test( "correct mimetype of uploaded /create.xml",
408 (objectp(doc) && doc->query_attribute(DOC_MIME_TYPE) == "text/xml" ) );
411 Test.skipped( "upload", "no create.xml installed" );
414 if ( stringp(_Server->query_config("web_server")) )
415 query = Protocols.HTTP.get_url("http://"+_Server->query_config("web_server") + "/__test__/");
417 query = (mapping)query;
418 Test.test( "fetching restricted container gives 401",
419 ( query->status == 401 ), "fetch gave %O", query->status );
420 query = (mapping)Protocols.HTTP.get_url("http://"+_Server->query_config("web_server") + "/__test__/test.txt");
421 Test.test( "fetching restricted file gives 401",
422 ( query->status == 401 ), "fetch gave %O\n data: %O",
423 query->status, query->data );
424 query = (mapping)Protocols.HTTP.get_url("http://"+_Server->query_config("web_server") + "/__test__/doc.xsl");
425 Test.test( "fetching restricted empty file gives 401",
426 ( query->status == 401 ), "fetch gave %O", query->status );
429 url = Standards.URI("http://"+_Server->query_config("web_server") + "/__test__/test.txt");
431 url->password = user->get_ticket(time()+60);
432 object test = cont->get_object_byname("test.txt");
434 Test.test("looking for test.txt", objectp(test));
436 test->sanction_object(GROUP("steam"), SANCTION_READ);
438 query = (mapping)Protocols.HTTP.get_url(url);
439 Test.test( "fetching published file gives 200",
440 ( query->status == 200 ), "fetch gave %O", query->status );
443 Test.skipped( "fetching restricted objects",
444 "no web_server defined" );
446 if ( objectp(doc) ) doc->delete();
447 if ( objectp(test1) ) test1->delete();
448 if ( objectp(cont) ) cont->delete();
454 void test_more(object doc, object test1, string mclass, object cont, int nr_try )
456 werror("DocumentFactory->test_more(%O,%O,%O,%O,%O)\n",
457 doc, test1, mclass,cont,nr_try);
458 if ( doc->status() != PSTAT_DISK || test1->status() != PSTAT_DISK ) {
460 MESSAGE("DocumentFactory: test_more() failed to drop documents !");
461 Test.skipped( "additional tests", "failed to drop documents, tried %d "
465 MESSAGE("DocumentFactory: test_more() waiting for drop of documents !");
468 Test.add_test_function( test_more, 5, doc, test1, mclass, cont, nr_try+1 );
472 Test.test( "changing document class", ( doc->get_class() == mclass ),
473 "class of %O is %O, should be DocXSL",
474 doc, doc->get_class() );
476 Test.add_test_function_thread( test_threaded, 0, cont, doc, test1 );
479 array get_all_objects()
481 array result = ({ });
482 array classes = ({ "Document", "DocXSL", "DocLpc", "DocGraphics", "DocXML",
484 foreach(classes, string clname) {
485 result += _Database->get_objects_by_class("/classes/"+clname);