diff --git a/src/DocumentLibrary/CVS/Entries b/src/DocumentLibrary/CVS/Entries new file mode 100644 index 0000000..3c2c98e --- /dev/null +++ b/src/DocumentLibrary/CVS/Entries @@ -0,0 +1,21 @@ +/CatalogPlus.py/1.1.1.1/Fri Jan 18 04:38:36 2002// +/DLGlobals.py/1.1.1.1/Fri Jan 18 04:38:39 2002// +/Document.py/1.4/Sun Jun 23 13:08:54 2002// +/DocumentLibrary.py/1.2/Sun Jun 23 11:51:05 2002// +/DocumentStore.py/1.1.1.1/Fri Jan 18 04:38:37 2002// +/INSTALL.txt/1.1.1.1/Fri Jan 18 04:38:32 2002// +/IconImage.py/1.1.1.1/Fri Jan 18 04:38:39 2002// +/LICENSE.txt/1.1.1.1/Fri Jan 18 04:38:36 2002// +/README.txt/1.2/Sun Jun 23 12:00:33 2002// +/TODO.txt/1.1.1.1/Fri Jan 18 04:38:39 2002// +/TopicIndex.py/1.2/Sun Jun 23 13:01:12 2002// +/__init__.py/1.1.1.1/Fri Jan 18 04:38:40 2002// +/refresh.txt/1.1.1.1/Fri Jan 18 04:38:37 2002// +/text.c.patch/1.1.1.1/Fri Jan 18 04:38:33 2002// +/version.txt/1.2/Sun Jun 23 13:20:49 2002// +D/FileConverters//// +D/document_icons//// +D/dtml//// +D/instance//// +D/www//// +/HISTORY.txt/1.6/Sun Jun 23 09:41:07 2002// diff --git a/src/DocumentLibrary/CVS/Repository b/src/DocumentLibrary/CVS/Repository new file mode 100644 index 0000000..57ff92d --- /dev/null +++ b/src/DocumentLibrary/CVS/Repository @@ -0,0 +1 @@ +DocumentLibrary diff --git a/src/DocumentLibrary/CVS/Root b/src/DocumentLibrary/CVS/Root new file mode 100644 index 0000000..2084eca --- /dev/null +++ b/src/DocumentLibrary/CVS/Root @@ -0,0 +1 @@ +:pserver:anonymous@cvs.nlada-library.sourceforge.net:/cvsroot/nlada-library diff --git a/src/DocumentLibrary/CatalogPlus.py b/src/DocumentLibrary/CatalogPlus.py new file mode 100644 index 0000000..d7f142f --- /dev/null +++ b/src/DocumentLibrary/CatalogPlus.py @@ -0,0 +1,165 @@ +############################################################################## +# +# Zope Public License (ZPL) Version 1.0 +# ------------------------------------- +# +# Copyright (c) Digital Creations. All rights reserved. +# +# This license has been certified as Open Source(tm). +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# 1. Redistributions in source code must retain the above copyright +# notice, this list of conditions, and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions, and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# +# 3. Digital Creations requests that attribution be given to Zope +# in any manner possible. Zope includes a "Powered by Zope" +# button that is installed by default. While it is not a license +# violation to remove this button, it is requested that the +# attribution remain. A significant investment has been put +# into Zope, and this effort will continue if the Zope community +# continues to grow. This is one way to assure that growth. +# +# 4. All advertising materials and documentation mentioning +# features derived from or use of this software must display +# the following acknowledgement: +# +# "This product includes software developed by Digital Creations +# for use in the Z Object Publishing Environment +# (http://www.zope.org/)." +# +# In the event that the product being advertised includes an +# intact Zope distribution (with copyright and license included) +# then this clause is waived. +# +# 5. Names associated with Zope or Digital Creations must not be used to +# endorse or promote products derived from this software without +# prior written permission from Digital Creations. +# +# 6. Modified redistributions of any form whatsoever must retain +# the following acknowledgment: +# +# "This product includes software developed by Digital Creations +# for use in the Z Object Publishing Environment +# (http://www.zope.org/)." +# +# Intact (re-)distributions of any official Zope release do not +# require an external acknowledgement. +# +# 7. Modifications are encouraged but must be packaged separately as +# patches to official Zope releases. Distributions that do not +# clearly separate the patches from the original work must be clearly +# labeled as unofficial distributions. Modifications which do not +# carry the name Zope may be packaged in any form, as long as they +# conform to all of the clauses above. +# +# +# Disclaimer +# +# THIS SOFTWARE IS PROVIDED BY DIGITAL CREATIONS ``AS IS'' AND ANY +# EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL DIGITAL CREATIONS OR ITS +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# +# This software consists of contributions made by Digital Creations and +# many individuals on behalf of Digital Creations. Specific +# attributions are listed in the accompanying credits file. +# +############################################################################## + +from Products.ZCatalog.Catalog import Catalog + +try: + # Try to import PluginIndexes (Zope 2.4 or higher) + from Products.PluginIndexes.FieldIndex.FieldIndex import FieldIndex + from Products.PluginIndexes.TextIndex.TextIndex import TextIndex + from Products.PluginIndexes.KeywordIndex.KeywordIndex import KeywordIndex +except: + # Otherwise use Index names (Zope up to 2.3) + def FieldIndex(id): return 'FieldIndex' + def TextIndex(id, lexicon=None): return 'TextIndex' + def KeywordIndex(id): return 'KeywordIndex' + + +class CatalogPlus(Catalog): + """ A slightly enhanced subclass of Catalog that plays well with + CatalogAware and does subtransactions without the full weight of a ZCatalog""" + + threshold = 10000 + _v_total = 0 + _v_transaction = None + + def catalog_object(self, obj, uid=None): + """ wrapper around catalog """ + + if uid is None: + try: uid = obj.getPhysicalPath + except AttributeError: + raise CatalogError( + "A cataloged object must support the 'getPhysicalPath' " + "method if no unique id is provided when cataloging" + ) + else: uid=string.join(uid(), '/') + elif type(uid) is not type(''): + raise CatalogError('The object unique id must be a string.') + + self.catalogObject(obj, uid, None) + # None passed in to catalogObject as third argument indicates + # that we shouldn't try to commit subtransactions within any + # indexing code. We throw away the result of the call to + # catalogObject (which is a word count), because it's + # worthless to us here. + + if self.threshold is not None: + # figure out whether or not to commit a subtransaction. + t = id(get_transaction()) + if t != self._v_transaction: + self._v_total = 0 + self._v_transaction = t + self._v_total = self._v_total + 1 + # increment the _v_total counter for this thread only and get + # a reference to the current transaction. + # the _v_total counter is zeroed if we notice that we're in + # a different transaction than the last one that came by. + # self.threshold represents the number of times that + # catalog_object needs to be called in order for the catalog + # to commit a subtransaction. The semantics here mean that + # we should commit a subtransaction if our threshhold is + # exceeded within the boundaries of the current transaction. + if self._v_total > self.threshold: + get_transaction().commit(1) + self._p_jar.cacheFullSweep(3) + self._v_total = 0 + + def uncatalog_object(self, uid): + """ For CatalogAware compatibilty """ + self.uncatalogObject(uid) + + def getLexicon(self): + lexicon = self.lexicon + + if type(lexicon) == type(''): + vocabulary = getattr(self, lexicon, None) + if vocabulary: + return vocabulary.getLexicon() + else: + return None + else: + return lexicon + diff --git a/src/DocumentLibrary/DLGlobals.py b/src/DocumentLibrary/DLGlobals.py new file mode 100644 index 0000000..c535ba6 --- /dev/null +++ b/src/DocumentLibrary/DLGlobals.py @@ -0,0 +1,48 @@ +################################################################################## +# +# Kaivo Public Software License +# +# Copyright (c) 2001, Kaivo, Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# o Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# o Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# +# o Neither Kaivo nor the names of its contributors may be used to endorse +# or promote products derived from this software without specific prior +# written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL KAIVO OR ITS CONTRIBUTORS BE LIABLE FOR +# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +################################################################################## + +__doc__ = """Document Library Global Declarations""" +__version__ = '1.0b2' + +# Document Library by Casey Duncan (cduncan@kaivo.com) +# Kaivo, Inc. (http://www.kaivo.com) + +from DateTime import DateTime + +# Special flag values +document_unreviewed_date = DateTime('1970/1/1 12:00pm') +document_min_reviewed_date = DateTime('1980/1/1 12:00pm') +document_rejected_date = DateTime('1970/2/1 12:00pm') +document_no_creation_date = DateTime('1970/1/1 12:00pm') diff --git a/src/DocumentLibrary/Document.py b/src/DocumentLibrary/Document.py new file mode 100644 index 0000000..230e7cf --- /dev/null +++ b/src/DocumentLibrary/Document.py @@ -0,0 +1,604 @@ +################################################################################## +# +# Kaivo Public Software License +# +# Copyright (c) 2001, Kaivo, Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# o Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# o Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# +# o Neither Kaivo nor the names of its contributors may be used to endorse +# or promote products derived from this software without specific prior +# written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL KAIVO OR ITS CONTRIBUTORS BE LIABLE FOR +# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +################################################################################## + +__doc__ = """Document Library Document""" +__version__ = '1.0b2' + +# Document by Casey Duncan (cduncan@kaivo.com) +# Kaivo, Inc. (http://www.kaivo.com) + +from string import split, join, strip, find, rfind, replace +from time import time +from os import path, listdir +from tempfile import mktemp +import urllib +from StringIO import StringIO +from Globals import DTMLFile, package_home +from DateTime import DateTime +from OFS.Image import * +from OFS.PropertySheets import FixedSchema +from App.ImageFile import ImageFile +import DLGlobals +import Acquisition +from FileConverters import FileConverters +try: + from Products.ZCatalog.CatalogPathAwareness import CatalogAware +except ImportError: + from Products.ZCatalog.CatalogAwareness import CatalogAware + +manage_addDocumentFileForm = DTMLFile('dtml/AddDocumentForm', globals()) + +def check_content_type(context, content_type): + """Verify that the type of file is allowed""" + + if hasattr(context, 'deny_content_types') and content_type: + return not (content_type in context.deny_content_types) + + return 1 + +def manage_addDocumentFile(self,id='',file='',title='',precondition='', content_type='', + REQUEST=None): + """Creates a new Library Document File""" + + id=str(id) + title=str(title) + content_type=str(content_type) + precondition=str(precondition) + + self=self.this() + doc = Document(id, title, '', content_type, precondition, container=self) + self._setObject(doc.getId(), doc) + doc = self._getOb(doc.getId()) + # Now we "upload" the data. By doing this in two steps, we + # can use a database trick to make the upload more efficient. + doc.manage_upload(file) + + if content_type: + doc.content_type=content_type + + if not check_content_type(self, doc.content_type): + doc.manage_delObjects(doc.getId()) + raise "IllegalFileType", \ + "That file type cannot be uploaded into the library." + + if REQUEST is not None: + REQUEST['RESPONSE'].redirect(self.absolute_url()+'/manage_main') + else: + return doc + +class Document(CatalogAware, File): + """Document library document""" + + meta_type = 'Document File' + + __ac_permissions__ = File.__ac_permissions__ + ( + ('Access contents information', ('format', 'source', 'identifier', 'subject', + 'content_description', 'content_size', 'is_accepted', + 'owner_id', 'authenticated_user_is_owner')), + ('View', ('text_content', 'PrincipiaSearchSource', 'read', 'super_topics', + 'topicMap', 'all_searchable_text')), + ('Manage properties', ('acceptDocument', 'rejectDocument', 'setIcons', 'showTopics')), + ) + + manage_options = File.manage_options + ( + { 'label': 'View Plain Text', 'action': 'text_content' }, + ) + + _properties = () + + # Set default values for important properties + review_date = DLGlobals.document_unreviewed_date + + def __init__(self, id='', title='', file='', content_type='', precondition='', container=None): + if not check_content_type(self, content_type): + raise "IllegalFileType", \ + "That file type cannot be uploaded into the library." + + if not id and container: + # Create a randomized id if none was provided + container = getattr(container, 'aq_base', container) + while not id or hasattr(container, id): + id = str(time()) + + File.__init__(self, id, title, file, content_type, precondition) + + if not hasattr(self, 'icon'): # We aren't an acquisition wrapper yet + self.setIcons() + + def __getattr__(self, name): + """Allow access to index_html via the filename and + default property values""" + + if name == self.__dict__.get('filename', None): + return self.index_html + elif name[0] != '_' and name[:3] != 'aq_' and self.hasProperty(name): + # Make default property values act like attributes + for p in self._properties: + if p['id'] == name: return p['default'] + raise AttributeError, name + else: + raise AttributeError, name + + def acceptDocument(self, REQUEST=None, RESPONSE=None): + """Mark the document as accepted by setting its review_date to the current date""" + self.review_date = DateTime() + self.reindex_object() + self.showTopics() + + if REQUEST and RESPONSE and REQUEST.has_key('last_url'): + RESPONSE.redirect(REQUEST.last_url + '?' + REQUEST.get('last_query','')) + + def rejectDocument(self, REQUEST=None, RESPONSE=None): + """Mark the document as rejected by resetting its review_date""" + self.review_date = DLGlobals.document_rejected_date + self.reindex_object() + + if REQUEST and RESPONSE and REQUEST.has_key('last_url'): + RESPONSE.redirect(REQUEST.last_url + '?' + REQUEST.get('last_query','')) + + def is_accepted(self): + """Returns true if the document is indexed and has been reviewed and accepted""" + return hasattr(self.aq_base, 'topics') and self.topics \ + and self.getProperty('review_date') != DLGlobals.document_rejected_date + + # + # Content and conversion handling methods + # + + def read(self): + """Return raw data of document file + This can be memory intensive for large files. + Use the _writeToTempFile method to get the data to external programs""" + data=self.data + if type(data) is type(''): return data + + r = StringIO() + while data is not None: + r.write(data.data) + data=data.next + + return r.getvalue() + + def _writeToTempFile(self): + """Writes the contents of the document file to a temporary file and + returns the file name. Used by file converters""" + tmp_file = mktemp() + f = open(tmp_file, 'wb') + + data=self.data + if type(data) is type(''): + f.write(data) + else: + while data is not None: + f.write(data.data) + data=data.next + + f.close() + return tmp_file + + def PrincipiaSearchSource(self): + """Return the raw text representation of the document if possible""" + + # Check for a cached copy of the full text first + if hasattr(self, '_v_raw_text'): + return self._v_raw_text + + format = self.format() + + if FileConverters.has_key(format): + # use the installed converter + convert = FileConverters[format].convert + try: + # Convert the document data to text and cache the result + self._v_raw_text = convert(self) + return self._v_raw_text + except: + return '' + elif format[:5] == 'text/': + # This appears to be a raw text type, so return the data as is + return self.read() + else: + # Can't do it captain + return '' + + def text_content(self, RESPONSE=None): + """The file converted to text with proper HTTP headers set + for viewing in a browser""" + t = self.PrincipiaSearchSource() + + if RESPONSE is not None: + RESPONSE.setHeader('Last-Modified', rfc1123_date(self._p_mtime)) + RESPONSE.setHeader('Content-Type', 'text/plain') + RESPONSE.setHeader('Content-Length', len(t)) + + return t + + def content_description(self): + """Returns a friendly description of the document content""" + + format = self.format() + + if format[:5] == 'text/': + d = 'Text Document (%s)' % format[5:] + else: + sep = find(format,'/') + 1 + d = 'Document (%s)' % format[sep:] + + if FileConverters.has_key(format): + return getattr(FileConverters[format], 'content_description', d) + else: + return d + + def content_size(self): + """Returns a qualified document size in Kb or Mb""" + s = self.get_size() + + if s == 0: + return '0 bytes' + elif s <= 1024: + return '1 Kb' + elif s < 1048576: + return '%d Kb' % (s / 1024) + else: + return '%d Mb' % (s / 1048576) + + def topicMap(self): + """Returns a tuple of id,title mappings for each topic this + document belongs to""" + + r = [] + + if hasattr(self, 'topics') and hasattr(self.aq_parent, 'Index'): + indexQuery = self.aq_parent.Index.query # Acquire the index's query method + + # Iterate the topic ids assigned to this document and + # search the topic index to find the titles of the topics + for topic_id in self.topics: + topic_info = indexQuery(id=topic_id) + if topic_info: r.append({ 'id': topic_id, 'title': topic_info[0].title }) + + return tuple(r) + + def super_topics(self): + """The list of all topics this document implicitly resides in including + all parent topics up to but not including the root""" + + r = {} # Use a dict to filter out duplicates + if hasattr(self, 'topics') and hasattr(self.aq_parent, 'Index'): + indexQuery = self.aq_parent.Index.query # Acquire the index's query method + + # Iterate the topic ids assigned to this document and + # search the topic index to find the parents of these topics + for topic_id in self.topics: + for topic_info in indexQuery(id=topic_id): + topic = topic_info.getObject() + + if topic: + for parent in topic.topicParents()[1:]: + r[parent.id] = 1 + return r.keys() + + + def all_searchable_text(self): + """All searchable text data concatenated together""" + + r = [] + + # Add all string and text properties not marked hidden + for p in self.propertyMap(): + if p.get('type', '') in ['string', 'text'] and not p.get('hidden', 0): + v = self.getProperty(p['id']) + if v: r.append(v) + + # Add the titles of the topics assigned to this document + for t in self.topicMap(): + r.append(t['title']) + + # Add the full text content of the document + v = self.PrincipiaSearchSource() + if v: r.append(v) + + return join(r, '\n') + + def owner_id(self): + """The user id of the user who owns this document""" + owner = self.owner_info() + if owner and owner.has_key('id'): + return owner['id'] + else: + return None + + def authenticated_user_is_owner(self, REQUEST=None): + """Returns true if the currently authenticated user owns + the document object""" + if not REQUEST: REQUEST = self.REQUEST + return self.owner_id() == REQUEST.AUTHENTICATED_USER.getUserName() + + # + # Dublin core compliance methods + # + + creator = '' # Override Zope creator method + + def format(self): + """Return the MIME content-type of the document""" + if hasattr(self, 'aq_base'): self = self.aq_base + c_type = getattr(self, 'content_type', None) + if c_type: c_type = split(c_type, ';')[0] + return c_type + + def source(self): + """Return the URI of the source document if it was + submitted via URI""" + if hasattr(self, 'aq_base'): self = self.aq_base + return getattr(self, 'data_url', None) + + def identifier(self): + """Unambiguous document identifier""" + if not hasattr(self, 'aq_base'): return self.getId() + if hasattr(self.aq_base, 'filename'): + return self.absolute_url() + '/' + urllib.quote(self.filename) + else: + return self.absolute_url() + + def subject(self): + """Return the subject as a comma separated list of + the topic titles this document resides in""" + r = map(lambda i: i['title'], self.topicMap()) + return join(r, ', ') + + # + # Property handling methods + # + + def _getDocumentProperties(self): + """Acquire the property information from the document store""" + try: + if self._properties != self.aq_parent._documentProperties: + self._properties = self.aq_parent._documentProperties + except: + pass + + return self._properties + + def propdict(self): + dict={} + for p in self._getDocumentProperties(): + dict[p['id']]=p + return dict + + def propertyMap(self): + """Override the default property mapping for File objects. + Documents use a single property definition in the store instead""" + + # Return a tuple of mappings, giving meta-data for document properties. + r=[] + for d in self._getDocumentProperties(): + mode=d.get('mode', 'wd') + if 'd' in mode: + dd={} + dd.update(d) + d=dd + d['mode']=filter(lambda c: c != 'd', mode) + r.append(d) + + return tuple(r) + + def hasProperty(self, id): + """Return true if object has a property 'id'""" + for p in self._getDocumentProperties(): + if id==p['id']: + return 1 + return 0 + + def property_extensible_schema__(self): + """Document files have fixed property schemas if they are in + a document store which describes their properties""" + return not hasattr(self.aq_parent, '_documentProperties') + + def manage_edit(self, title, content_type, precondition='', REQUEST=None): + """Update object and reindex""" + r = File.manage_edit(self, title, content_type, precondition, + REQUEST=REQUEST) + self.setIcons() + self.reindex_object() + return r + + def _updateProperty(self, name, value): + """Update property value by name. Includes special parsing for + topic list properties""" + + if name == 'topics': + # Filter the topic code list to get the topic codes alone + topics = [] + for topic in value: + if strip(topic): topics.append(split(topic)[0]) + r = File._updateProperty(self, name, topics) + if hasattr(self.aq_base, 'review_date') and self.review_date != DLGlobals.document_unreviewed_date: + try: self.showTopics() + except: pass + else: + r = File._updateProperty(self, name, value) + + def manage_changeProperties(self, REQUEST=None, reindex=1, **kw): + """Update properties and reindex""" + r = File.manage_changeProperties(self, REQUEST, kw=kw) + if reindex: self.reindex_object() + return r + + def manage_editProperties(self, REQUEST, reindex=1): + """Edit Properties and reindex""" + r = File.manage_editProperties(self, REQUEST) + if reindex: self.reindex_object() + return r + + # + # Document data handlers + # + + def update_data(self, data, content_type=None, size=None): + """Update file data and reindex""" + r = File.update_data(self, data, content_type, size) + self.setIcons() + # Clear any cached file data + if hasattr(self, '_v_raw_text'): del self._v_raw_text + # reindex the document in the Catalog + self.reindex_object() + return r + + def manage_upload(self, file, REQUEST=None, remote=0): + """Upload new file data into the document. Extended to support + uploading data from remote servers""" + + if remote and type(file) == type(''): + url = file + + # make sure the url uses an acceptable protocol (not file or mailto) + p = find(url, ':') + if p > -1: + if url[:p] not in ('http', 'ftp', 'gopher'): + raise "URLError", \ + ('The URL specified: ' + '%s ' + 'is invalid.') % (url, url) + else: + # use HTTP if no protocol was specified + url = 'http://' + url + + self.data_url = url + # Attempt to connect and retrieve the remote document + try: + remote_file = urllib.urlopen(url) + data = remote_file.read() + if remote_file.headers.type == 'text/html': + data = unlocalizeHTML(url, data) + file = Pdata(data) # blob wrapper object from OFS.Image + file.headers = remote_file.info().dict + file.filename = 'index.html' + remote_file.close() + except IOError, error: + raise "URLError", \ + ('
An error occurred while ' + 'attempting to connect to ' + '%s
' + '
Error Message: %s
') % (url, url, error) + else: + if hasattr(self.aq_base, 'data_url'): del self.data_url + + # Set the filename + filename = getattr(file, 'filename', 'untitled') + + # Strip off the file path (if any) + for path_sep in ['\\', '/', ':']: + sep = rfind(filename, path_sep) + 1 + if sep: + filename = filename[sep:] + break + + while hasattr(self, filename): + # Make sure the filename is not the same name as existing attrs + filename = 'doc_' + filename + + self.filename = filename + + # Upload the data + return File.manage_upload(self, file, REQUEST) + + def setIcons(self): + """set the document icons based on the content-type""" + format = self.format() + sep = find(format,'/') + 1 + type = format[sep:] + sep = find(type, '-') + 1 + if sep: type = type[sep:] + large_icon_path = 'document_icons/%s_large.gif' % type + if path.exists(path.join(package_home(globals()), large_icon_path)): + self.large_icon = ImageFile(large_icon_path, globals()) + else: + self.large_icon = ImageFile('document_icons/generic_large.gif', globals()) + + small_icon_path = 'document_icons/%s_small.gif' % type + if path.exists(path.join(package_home(globals()), small_icon_path)): + self.small_icon = ImageFile(small_icon_path, globals()) + else: + self.small_icon = ImageFile('document_icons/generic_small.gif', globals()) + + self.icon = join(self.getPhysicalPath()[1:],'/') + '/small_icon' + + def showTopics(self): + """Make the document topics visible""" + for topic_id in self.topics: + topics = self.aq_parent.Index.query(id=topic_id) + for topic in topics: + topic = topic.getObject() + if topic: topic.showTopic() # Sometime getObject() returns None, gaack! + + +Globals.InitializeClass(Document) + +class DocURLopener(urllib.FancyURLopener): + """Extend the urllib FancyURLopener to raise an exception on 404 not found errors.""" + + def http_error_404(self, url, fp, errcode, errmsg, headers): + raise "NotFoundError", \ + ('
The document specified at: ' + 'http:%s ' + 'was not found. ' + 'Please double check this URL and ' + 'submit again.
') % (url, url) + +urllib._urlopener = DocURLopener() # Tell urllib to use our urlopener class + +def unlocalizeHTML(url, data): + """Add a declaration to documents that lack it to compensate for + relocating them into the library""" + + p = find(data, '') + if p > 0: + return replace(data, '', '' % base_url, 1) + elif p == -1: + p = find(data, '') + if p > 0: + return replace(data, '', '' % base_url, 1) + return data diff --git a/src/DocumentLibrary/DocumentLibrary.py b/src/DocumentLibrary/DocumentLibrary.py new file mode 100644 index 0000000..77e8414 --- /dev/null +++ b/src/DocumentLibrary/DocumentLibrary.py @@ -0,0 +1,180 @@ +################################################################################## +# +# Kaivo Public Software License +# +# Copyright (c) 2001, Kaivo, Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# o Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# o Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# +# o Neither Kaivo nor the names of its contributors may be used to endorse +# or promote products derived from this software without specific prior +# written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL KAIVO OR ITS CONTRIBUTORS BE LIABLE FOR +# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +################################################################################## + +__doc__ = """A Zope Document Library""" +__version__ = '1.0b2' + +# Document Library by Casey Duncan (cduncan@kaivo.com) +# Kaivo, Inc. (http://www.kaivo.com) + +import os +from string import strip, upper +import Globals +from Globals import DTMLFile +from OFS.Folder import Folder +from OFS.DTMLMethod import DTMLMethod +from DateTime import DateTime +from Products.ZCatalog.Vocabulary import Vocabulary +import TopicIndex +import DLGlobals +import DocumentStore + +manage_addDocumentLibraryForm = DTMLFile('dtml/AddLibraryForm', globals()) + +def manage_addDocumentLibrary(self, id, title='', vocab_id='', REQUEST=None): + """Construct a Document Library object""" + + self._setObject(id, DocumentLibrary(id, title, vocab_id, self)) + + if REQUEST is not None: + return self.manage_main(self, REQUEST, update_menu=1) + + +class DocumentLibrary(Folder): + """Document Library Zope Product""" + + meta_type = 'Document Library' + + __ac_permissions__ = Folder.__ac_permissions__ + ( + ('View', ('setFlag', 'getFlag', 'clearFlag')), + ('Add Document Libraries', ('manage_addLibraryTopicIndexRootForm', + 'manage_addLibraryTopicIndexRoot', + 'manage_addLibraryDocumentStoreForm', + 'manage_addLibraryDocumentStore')), + ) + + # Allow DocumentStores and TopicIndexRoots to be created in us + meta_types = ({'name': TopicIndex.TopicIndexRoot.meta_type, + 'action': 'manage_addLibraryTopicIndexRootForm'}, + {'name': DocumentStore.DocumentStore.meta_type, + 'action': 'manage_addLibraryDocumentStoreForm'}) + + manage_addLibraryTopicIndexRootForm = TopicIndex.manage_addLibraryTopicIndexRootForm + manage_addLibraryTopicIndexRoot = TopicIndex.manage_addLibraryTopicIndexRoot + manage_addLibraryDocumentStoreForm = DocumentStore.manage_addLibraryDocumentStoreForm + manage_addLibraryDocumentStore = DocumentStore.manage_addLibraryDocumentStore + + #manage_options = ( + # {'label': 'Contents', 'action': 'manage_main'}, + #) + + _properties = ( + { 'id': 'title', 'type': 'string', 'mode': 'w' }, + { 'id': 'hide_empty_topics', 'type': 'boolean', 'mode': 'w'}, + { 'id': 'document_types', 'type': 'lines', 'mode': 'wd' }, + { 'id': 'deny_content_types', 'type': 'lines', 'mode': 'wd' } + ) + + hide_empty_topics = 0 # for backward compatibility with existing libraries + + # Export the special values to the namespace for better portability/readability + document_unreviewed_date = DLGlobals.document_unreviewed_date + document_rejected_date = DLGlobals.document_rejected_date + document_min_reviewed_date = DLGlobals.document_min_reviewed_date + document_no_creation_date = DLGlobals.document_no_creation_date + + def __init__(self, id, title='', vocab_id='', container=None): + self.id = id + self.title = title + self.document_types = ['None Defined'] + self.hide_empty_topics = 0 + + # MIME types not allowed to be uploaded. Here we are blanket restricting all + # executable and unknown types by default. + self.deny_content_types = ['application/octet-stream'] + + if not vocab_id: + # Add a global vocabulary + vocab_id = 'Vocabulary' + ob = Vocabulary(vocab_id, '', globbing=1) + self._setObject(vocab_id, ob) + else: + # Make ourselves into an acquisition wrapper to acquire the + # Vocabulary + if container: + self = self.__of__(container) + else: + raise AttributeError, ("You cannot specify a vocab_id without " + "also specifying a container.") + # Add the document storage + ob = DocumentStore.DocumentStore('Documents', '', vocab_id, container=self) + self._setObject('Documents', ob) + + # Add the index topic root + ob = TopicIndex.TopicIndexRoot('Index', 'Top of Index', vocab_id, container=self) + self._setObject('Index', ob) + + # Add the DTML instance methods + path = os.path.join(Globals.package_home(globals()), 'instance', 'methods') + for id in os.listdir(path): + file = os.path.join(path, id) + if not os.path.isdir(file): + file = open(file) + ob = DTMLMethod(file.read(), __name__=id) + self._setObject(id, ob) + + def setFlag(self, flag_collection, flag_name, REQUEST=None): + """Sets a flag on REQUEST in the specified flag_collection, creating the + collection if it doesn't exist""" + + if REQUEST is None: + REQUEST = self.REQUEST + + flags = REQUEST.get(flag_collection, {}) + flags[flag_name] = 1 + REQUEST.set(flag_collection, flags) + + def clearFlag(self, flag_collection, flag_name, REQUEST=None): + """Clears a flag on REQUEST in the specified flag_collection, creating the + collection if it doesn't exist""" + + if REQUEST is None: + REQUEST = self.REQUEST + + flags = REQUEST.get(flag_collection, {}) + flags[flag_name] = 0 + REQUEST.set(flag_collection, flags) + + def getFlag(self, flag_collection, flag_name, REQUEST=None): + """Returns the value for a flag of the specified flag_collection. If the + flag does not exist, false is returned""" + + if REQUEST is None: + REQUEST = self.REQUEST + + flags = REQUEST.get(flag_collection, {}) + return flags.has_key(flag_name) and flags[flag_name] + +Globals.InitializeClass(DocumentLibrary) diff --git a/src/DocumentLibrary/DocumentStore.py b/src/DocumentLibrary/DocumentStore.py new file mode 100644 index 0000000..2e77f94 --- /dev/null +++ b/src/DocumentLibrary/DocumentStore.py @@ -0,0 +1,480 @@ +################################################################################## +# +# Kaivo Public Software License +# +# Copyright (c) 2001, Kaivo, Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# o Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# o Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# +# o Neither Kaivo nor the names of its contributors may be used to endorse +# or promote products derived from this software without specific prior +# written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL KAIVO OR ITS CONTRIBUTORS BE LIABLE FOR +# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +################################################################################## + +__doc__ = """Document Library Document Storage""" +__version__ = '1.0b2' + +# Document Store by Casey Duncan (cduncan@kaivo.com) +# Kaivo, Inc. (http://www.kaivo.com) + +# This initial implementation uses a BTreeFolder to store the documents +# in the ZODB. Later implementations will abstract this to allow the +# option of storing the documents in a separate file system. + +from string import strip, split, join, find +from StringIO import StringIO +import Globals +from Globals import DTMLFile +from App.ImageFile import ImageFile +from Products.BTreeFolder.BTreeFolder import BTreeFolder +from CatalogPlus import CatalogPlus, FieldIndex, TextIndex, KeywordIndex +from DateTime.DateTime import DateTime +from ZPublisher.Converters import type_converters +import DLGlobals +import Document + +manage_addLibraryDocumentStoreForm = DTMLFile('dtml/AddDocumentStoreForm', globals()) + +def manage_addLibraryDocumentStore(self, id, title='', vocab_id = None, + REQUEST=None): + """Add a document store instance to a library""" + ob = DocumentStore(id, title, vocab_id, self) + self._setObject(id, ob) + + if REQUEST is not None: + REQUEST['RESPONSE'].redirect(self.absolute_url()+'/manage_main') + + +class DocumentStore(BTreeFolder): + """Document library document storage class""" + + meta_type="Document Store" + all_meta_types=({'name': 'Document File', 'action': 'manage_addDocumentFileForm'},) + + manage_options = ( + {'label': 'Contents', 'action': 'manage_main'}, + {'label': 'Document Properties', 'action': 'manage_documentProperties'}, + {'label': 'Catalog Indexes', 'action': 'manage_catalogIndexes'}, + {'label': 'Catalog Metadata', 'action': 'manage_catalogColumns'}, + {'label': 'Catalog Maintenance', 'action': 'manage_catalogMaintenance'}, + {'label': 'Security', 'action': 'manage_access'}, + {'label': 'Undo', 'action': 'manage_UndoForm'}, + {'label': 'Owner', 'action': 'manage_owner'}, + {'label': 'Find', 'action': 'manage_findForm'}, + ) + + __ac_permissions__ = BTreeFolder.__ac_permissions__ + ( + ('Add Document Library Files', ('manage_addDocumentFileForm', 'manage_addDocumentFile', + 'addDocumentFile', 'validateDocumentProperties')), + ('Add Document Library Files Without Review', ()), + ('Manage properties', ('manage_documentProperties',)), + ('Delete objects', ('deleteDocumentFile',)), + ('Search ZCatalog', ('query', 'getpath', 'getCatalogIndexes', 'getCatalogColumns')), + ('Manage ZCatalog Entries', ('manage_reinitCatalog', 'manage_catalogColumns', + 'manage_delCatalogColumns', 'manage_addCatalogColumn', + 'manage_catalogIndexes', 'manage_delCatalogIndexes', + 'manage_addCatalogIndex','manage_catalogMaintenance')), + ) + + _documentProperties = ( + { 'id': 'title', 'type': 'string', 'required': 1, 'mode': 'w' }, + { 'id': 'content_type', 'type': 'string', 'hidden': 1, 'mode': 'w' }, + { 'id': 'filename', 'type': 'string', 'hidden': 1, 'default': '', 'mode': 'w'}, + { 'id': 'type', 'type': 'selection', 'select_variable': 'document_types', 'default':'', 'required': 1 }, + { 'id': 'description', 'type': 'text', 'default': '', 'required': 1 }, + { 'id': 'topics', 'type': 'lines', 'default': [], 'required': 1, 'mode': 'w' }, + { 'id': 'creator', 'type': 'string', 'default': '' }, + { 'id': 'date', 'type': 'date', + 'default': DLGlobals.document_no_creation_date, 'mode': 'w' }, + { 'id': 'review_date', 'type': 'date', + 'default': DLGlobals.document_unreviewed_date, 'mode': 'w' }, + ) + + _defaultCatalogColumns = ( 'id', 'title', 'identifier', 'date', 'description' ) + _defaultCatalogIndexes = ( + ('id', FieldIndex), + ('title', TextIndex), + ('type', FieldIndex), + ('description', TextIndex), + ('topics', KeywordIndex), + ('super_topics', KeywordIndex), + ('creator', TextIndex), + ('date', FieldIndex), + ('review_date', FieldIndex), + ('all_searchable_text', TextIndex), + ('owner_id', FieldIndex), + ) + + def __init__(self, id, title='', vocab_id=None, container=None): + self.id = id + self.title = title + + if vocab_id is not None and container is None: + raise AttributeError, 'You cannot specify a vocab_id without' \ + 'also specifying a container.' + if container is not None: + self=self.__of__(container) + + # Setup the document store schema and indexes + self.Catalog = CatalogPlus(vocab_id) + lexicon = self.Catalog.getLexicon() + + for name in self._defaultCatalogColumns: + self.Catalog.addColumn(name) + + for name, index_class in self._defaultCatalogIndexes: + if index_class is TextIndex: + self.Catalog.addIndex(name, index_class(name, lexicon=lexicon)) + else: + self.Catalog.addIndex(name, index_class(name)) + + try: # Be flexible with the BTreeFolder implementation + BTreeFolder.__init__(self) + except: + BTreeFolder.__init__(self, id) + + DocumentStore_icon = ImageFile('www/DocumentStore_icon.gif', globals()) + def icon(self): + """Managment icon""" + return join(self.getPhysicalPath()[1:],'/') + '/DocumentStore_icon' + + def lex(self): + """debug""" + return self.Catalog.lexicon + + # + # Document property methods + # + + def defaultPropertyValue(self, propertyId, d=None): + """Return the default value for a document property""" + for p in self._documentProperties: + if p['id'] == propertyId: + try: + return p['default'] + except KeyError: + return d + return d + + def documentPropertyMap(self): + """Return mapping of document property schema""" + return self._documentProperties + + def documentPropertyDict(self): + """Return a dictionary of property info keyed on id""" + propdict = {} + + for prop in self._documentProperties: + propdict[prop['id']] = prop + + return propdict + + def _setError(self, field, message, REQUEST, error_template): + """Set a validation error into REQUEST""" + REQUEST.set('ERROR_' + field, error_template % message) + + def validateDocumentProperties(self, REQUEST, + error_template='
%s
'): + """Validates document properties passed on the REQUEST object + raises a ValidationError and puts error messages into REQUEST if + errors are found""" + errors = 0 + + for p in self._documentProperties: + field = p['id'] + ftype = p['type'] + value = REQUEST.get(field, None) + + if p.has_key('required') and p['required'] and not value \ + or (ftype in ('lines','list','tokens') and value == ['']): + self._setError(field, 'Required Field', REQUEST, error_template) + errors = errors + 1 + elif ftype == 'date' and value: + try: + DateTime(value) + except: + self._setError(field, 'Invalid Date', REQUEST, error_template) + errors = errors + 1 + elif ftype == 'int' and value: + try: + int(value) + except: + self._setError(field, 'Invalid Integer Value', REQUEST, error_template) + errors = errors + 1 + elif ftype == 'float' and value: + try: + float(v) + except: + self._setError(field, 'Invalid Real Number', REQUEST, error_template) + errors = errors + 1 + + if REQUEST.has_key('file') \ + and not (REQUEST.file.filename or REQUEST.get('url','http://') != 'http://'): + self._setError('file', 'Specify a File or a URL to Submit', + REQUEST, error_template) + errors = errors + 1 + + if errors == 1: + message = ('1 error was found on the form. ' + 'Please correct the noted field ' + 'and resubmit the form.') + elif errors > 1: + message = ('%d errors were found on the form. ' + 'Please correct the noted fields ' + 'and resubmit the form.') % errors + + if errors: + if REQUEST.has_key('file'): + message = message + ('
Note: Due to browser limitations, ' + 'you may need to specify the file you are ' + 'submitting again.') + REQUEST.set('ERROR_FORM', error_template % message) + raise 'ValidationError', '%d Form Field Error(s) Found' % errors + + manage_documentProperties = DTMLFile('dtml/EditDocumentProperties', globals()) + + def manage_editDocumentProperties(self, REQUEST, RESPONSE=None): + """Modify the document properties' default values""" + + editedProperties = [] + + for prop in self._documentProperties: + propvalue = REQUEST.get(prop['id'], '') + proptype = prop.get('type', 'string') + + if type_converters.has_key(proptype): + propvalue = type_converters[proptype](propvalue) + + prop['default'] = propvalue + + required_flag = prop['id'] + '_required_' + + if REQUEST.has_key(required_flag) and REQUEST[required_flag]: + prop['required'] = 1 + else: + if prop.has_key('required'): del prop['required'] + + editedProperties.append(prop) + + self._documentProperties = tuple(editedProperties) + + if REQUEST and RESPONSE: + RESPONSE.redirect(REQUEST.URL1 + ('/manage_documentProperties?' + 'manage_tabs_message=Properties+Updated')) + + def manage_delDocumentProperties(self, ids=None, REQUEST=None, RESPONSE=None): + """Delete one or more document properties specified by 'ids'.""" + if not ids: + raise 'BadRequest', "No properties were specified" + + propdict=self.documentPropertyDict() + nd=self._reserved_names + for id in ids: + if not propdict.has_key(id): + raise 'BadRequest', ( + 'The property %s does not exist' % id) + if (not 'd' in propdict[id].get('mode', 'wd')) or (id in nd): + raise 'BadRequest', ( + 'The property %s cannot be deleted.' % id) + del propdict[id] + + self._documentProperties = tuple(map(lambda i: i[1], propdict.items())) + + if REQUEST and RESPONSE: + RESPONSE.redirect(REQUEST.URL1 + ('/manage_documentProperties' + '?manage_tabs_message=Properties+Deleted')) + + def manage_addDocumentProperty(self, id, default_value, type, required=0, + REQUEST=None, RESPONSE=None): + """Add a document property""" + # Raise an error if an value is wrapped. + if hasattr(default_value, 'aq_base'): + raise ValueError, 'Invalid property value: wrapped object' + + # Check that the property id is valid + docpropnames = map(lambda p: p['id'], self._documentProperties) + + if not id or id[:1]=='_' or (id[:3]=='aq_') \ + or (' ' in id) or hasattr(self.aq_base, id) or (id in docpropnames): + raise 'Bad Request', 'Invalid or duplicate property id' + + # Convert the value to the proper type + if type_converters.has_key(type): + default_value = type_converters[type](default_value) + + prop = {} + prop['id'] = id + prop['type'] = type + + if not type in ('selection', 'multiple selection'): + prop['default'] = default_value + else: + prop['select_variable'] = default_value + prop['default'] = '' + + if required: prop['required'] = 1 + self._documentProperties = self._documentProperties + (prop,) + + if REQUEST and RESPONSE: + RESPONSE.redirect('manage_documentProperties?manage_tabs_message=Property+Added') + + # + # Document management methods + # + + manage_addDocumentFileForm = Document.manage_addDocumentFileForm + manage_addDocumentFile = Document.manage_addDocumentFile + + def addDocumentFile(self, REQUEST): + """Adds a new document and sets its properties from a form based submission""" + + self=self.this() + doc = Document.Document(title=REQUEST.title, container=self) + self._setObject(doc.getId(), doc) + doc = self._getOb(doc.getId()) + + # See if the user can skip the document review + if REQUEST.AUTHENTICATED_USER.has_permission('Add Document Library Files Without Review', + self): + REQUEST.set('review_date', DateTime()) + else: + REQUEST.set('review_date', DLGlobals.document_unreviewed_date) + + # Set the properties, but don't index yet + doc.manage_changeProperties(REQUEST, reindex=0) + # See if a file or url was specified + if not REQUEST.file.filename and REQUEST.get('url', 'http://') != 'http://': + doc.manage_upload(file=REQUEST.url, remote=1) + else: + doc.manage_upload(REQUEST.file) + + if not Document.check_content_type(self, doc.content_type): + self.manage_delObjects(doc.getId()) + raise "IllegalFileType", \ + "That file type cannot be uploaded into the library." + + return doc + + def deleteDocumentFile(self, REQUEST=None, RESPONSE=None): + """Deletes a document from the document store""" + self.manage_delObjects(REQUEST.id) + + if REQUEST and RESPONSE and REQUEST.has_key('last_url'): + RESPONSE.redirect(REQUEST.last_url + '?' + REQUEST.get('last_query','')) + + # + # Catalog query support methods + # + + def query(self, REQUEST=None, **kw): + """Query the document store for matching documents""" + return apply(self.Catalog.searchResults, (REQUEST,), kw) + + def getpath(self, rid): + """ + Return the path to a cataloged object given a 'data_record_id_' + Used by the Catalog brains + """ + return self.Catalog.paths[rid] + + # + # Catalog management methods + # + + manage_catalogColumns = DTMLFile('dtml/catalogColumns', globals()) + + def getCatalogColumns(self): + """Returns the metadata columns in the catalog""" + return self.Catalog.schema.keys() + + def manage_delCatalogColumns(self, names, REQUEST=None, RESPONSE=None): + """Delete Catalog metadata column""" + if type(names) == type(''): + names = [names] + + for name in names: + self.Catalog.delColumn(name) + + if REQUEST and RESPONSE: + RESPONSE.redirect('manage_catalogColumns?manage_tabs_message=Catalog+Metadata+Deleted') + + def manage_addCatalogColumn(self, name, REQUEST=None, RESPONSE=None): + """Add Catalog metdata column""" + self.Catalog.addColumn(name) + + if REQUEST and RESPONSE: + RESPONSE.redirect('manage_catalogColumns?manage_tabs_message=Catalog+Metadata+Column+Added') + + manage_catalogIndexes = DTMLFile('dtml/catalogIndexes', globals()) + + def getCatalogIndexes(self): + """Returns the indexes in the catalog""" + return self.Catalog.indexes.values() + + def manage_delCatalogIndexes(self, names, REQUEST=None, RESPONSE=None): + """Delete Catalog index""" + if type(names) == type(''): + names = [names] + + for name in names: + self.Catalog.delIndex(name) + + if REQUEST and RESPONSE: + RESPONSE.redirect('manage_catalogIndexes?manage_tabs_message=Catalog+Index+Deleted') + + def manage_addCatalogIndex(self, name, type, REQUEST=None, RESPONSE=None): + """Add Catalog index""" + index_class={'FieldIndex': FieldIndex, + 'TextIndex': TextIndex, + 'KeywordIndex': KeywordIndex}[type] + + if index_class is TextIndex: + self.Catalog.addIndex(name, + index_class(name, lexicon=self.Catalog.getLexicon())) + else: + self.Catalog.addIndex(name, index_class(name)) + + if REQUEST and RESPONSE: + RESPONSE.redirect('manage_catalogIndexes?manage_tabs_message=Catalog+Index+Added') + + manage_catalogMaintenance = DTMLFile('dtml/catalogMaintenance', globals()) + + def manage_reinitCatalog(self, REQUEST=None, RESPONSE=None): + """Reinitialize the document store catalog""" + + self.Catalog.clear() + + for doc in self.objectValues(): + try: + doc.setIcons() + if doc.is_accepted(): doc.showTopics() + except: + pass + doc.index_object() + + if REQUEST and RESPONSE: + RESPONSE.redirect('manage_catalogMaintenance?manage_tabs_message=Catalog+Reinitialized') + +Globals.InitializeClass(DocumentStore) diff --git a/src/DocumentLibrary/FileConverters/CVS/Entries b/src/DocumentLibrary/FileConverters/CVS/Entries new file mode 100644 index 0000000..9e39766 --- /dev/null +++ b/src/DocumentLibrary/FileConverters/CVS/Entries @@ -0,0 +1,9 @@ +/__init__.py/1.1.1.1/Fri Jan 18 04:38:43 2002// +/doc.py/1.1.1.1/Fri Jan 18 04:38:43 2002// +/html.py/1.1.1.1/Fri Jan 18 04:38:43 2002// +/pdf.py/1.1.1.1/Fri Jan 18 04:38:43 2002// +/ppt.py/1.1.1.1/Fri Jan 18 04:38:43 2002// +/ps.py/1.1.1.1/Fri Jan 18 04:38:43 2002// +/wvText.xml/1.1.1.1/Fri Jan 18 04:38:43 2002// +/xls.py/1.1.1.1/Fri Jan 18 04:38:43 2002// +D diff --git a/src/DocumentLibrary/FileConverters/CVS/Repository b/src/DocumentLibrary/FileConverters/CVS/Repository new file mode 100644 index 0000000..05376e6 --- /dev/null +++ b/src/DocumentLibrary/FileConverters/CVS/Repository @@ -0,0 +1 @@ +DocumentLibrary/FileConverters diff --git a/src/DocumentLibrary/FileConverters/CVS/Root b/src/DocumentLibrary/FileConverters/CVS/Root new file mode 100644 index 0000000..2084eca --- /dev/null +++ b/src/DocumentLibrary/FileConverters/CVS/Root @@ -0,0 +1 @@ +:pserver:anonymous@cvs.nlada-library.sourceforge.net:/cvsroot/nlada-library diff --git a/src/DocumentLibrary/FileConverters/__init__.py b/src/DocumentLibrary/FileConverters/__init__.py new file mode 100644 index 0000000..6c67436 --- /dev/null +++ b/src/DocumentLibrary/FileConverters/__init__.py @@ -0,0 +1,29 @@ +"""File converters for the document library product""" + +from os import listdir + +def getFileConverters(): + """Import converter modules from this directory + and return them as a dictionary keyed by content_type. + + Each converter module should define an attribute content_type and a function + def convert(data) which should return text given data in the + format the converter understands. content_type may be a string or a + sequence of strings if several content_types are equivilant""" + + cvtr_dict = {} + + for cvtr_name in listdir(__path__[0]): + if cvtr_name[0] != '_' and cvtr_name[-3:] == '.py': + cvtr = __import__(cvtr_name[:-3], globals(), globals(), __path__) + + if hasattr(cvtr, 'content_type') and hasattr(cvtr, 'convert'): + if type(cvtr.content_type) == type(''): + cvtr_dict[cvtr.content_type] = cvtr + else: + for c_type in cvtr.content_type: + cvtr_dict[c_type] = cvtr + + return cvtr_dict + +FileConverters = getFileConverters() diff --git a/src/DocumentLibrary/FileConverters/doc.py b/src/DocumentLibrary/FileConverters/doc.py new file mode 100644 index 0000000..dfed561 --- /dev/null +++ b/src/DocumentLibrary/FileConverters/doc.py @@ -0,0 +1,26 @@ +"""MSWord doc to text file converter for Document Library""" + +from os import popen, remove, path +from Globals import package_home + +content_type = ('application/msword', 'application/ms-word', 'application/vnd.ms-word') + +content_description = """Microsoft Word [ +Download MS Word Viewer]""" + +wvConf_file = path.join(package_home(globals()), 'wvText.xml') + +# Uses the wvWare, and assumes it is in the path +# Requires write access to /tmp + +def convert(documentFile): + """Convert MSWord data to raw text""" + + tmp_name = documentFile._writeToTempFile() + text = popen('wvWare -x %s %s 2> /dev/null' % (wvConf_file, tmp_name)).read() + remove(tmp_name) + + return text + +# For increased speed, comment out line 400 of text.c in the wv source diff --git a/src/DocumentLibrary/FileConverters/html.py b/src/DocumentLibrary/FileConverters/html.py new file mode 100644 index 0000000..84305e7 --- /dev/null +++ b/src/DocumentLibrary/FileConverters/html.py @@ -0,0 +1,46 @@ +"""HTML to text file converter for Document Library""" + +from sgmllib import SGMLParser +from string import join + +content_type = ('text/html', 'text/xml', 'text/sgml') + +content_description = 'Web Page (HTML)' + +# Code taken from Dieter Maurer's CatalogSupport Module +# http://www.handshake.de/~dieter/pyprojects/zope +# Thank you! + +class _StripTagParser(SGMLParser): + '''SGML Parser removing any tags and translating HTML entities.''' + + from htmlentitydefs import entitydefs + + data= None + + def handle_data(self,data): + if self.data is None: self.data=[] + self.data.append(data) + + def __str__(self): + if self.data is None: return '' + return join(self.data,'') + + +def convert(doc): + """Convert html data to raw text""" + + p = _StripTagParser() + + try: + data = doc.data + if type(data) == type(''): + p.feed(data) + else: + while data is not None: + p.feed(data.data) + data = data.next + p.close() + return str(p) + except: + return '' diff --git a/src/DocumentLibrary/FileConverters/pdf.py b/src/DocumentLibrary/FileConverters/pdf.py new file mode 100644 index 0000000..12707e0 --- /dev/null +++ b/src/DocumentLibrary/FileConverters/pdf.py @@ -0,0 +1,19 @@ +"""PDF to text file converter for Document Library""" + +from os import popen, remove + +content_type = 'application/pdf' + +content_description = """Adobe Acrobat PDF [Download Acrobat Reader]""" + +# Uses the built-in linux pdftotext command, and assumes it is in the path +# Requires write access to /tmp + +def convert(documentFile): + """Convert pdf data to raw text""" + + tmp_name = documentFile._writeToTempFile() + text = popen('pdftotext %s -' % tmp_name).read() + remove(tmp_name) + + return text diff --git a/src/DocumentLibrary/FileConverters/ppt.py b/src/DocumentLibrary/FileConverters/ppt.py new file mode 100644 index 0000000..11f2b03 --- /dev/null +++ b/src/DocumentLibrary/FileConverters/ppt.py @@ -0,0 +1,47 @@ +"""MSPowerpoint doc to text file converter for Document Library""" + +from os import popen, remove +from sgmllib import SGMLParser +from string import join + +content_type = ('application/mspowerpoint', 'application/ms-powerpoint', + 'application/vnd.ms-powerpoint') + +content_description = """Microsoft Powerpoint [Download Powerpoint Viewer]""" + +#uses pptHTML http://www.xlhtml.org +# Requires write access to /tmp + +class _StripTagParser(SGMLParser): + '''SGML Parser removing any tags and translating HTML entities.''' + + from htmlentitydefs import entitydefs + + data= None + + def handle_data(self,data): + if self.data is None: self.data=[] + self.data.append(data) + + def __str__(self): + if self.data is None: return '' + return join(self.data,'') + + +def convert(documentFile): + """Convert MS-Powerpoint data to raw text""" + + tmp_name = documentFile._writeToTempFile() + text = popen('pptHtml %s 2> /dev/null' % (tmp_name)).read() + remove(tmp_name) + + p = _StripTagParser() + + try: + p.feed(text) + p.close() + return str(p) + except: + return '' diff --git a/src/DocumentLibrary/FileConverters/ps.py b/src/DocumentLibrary/FileConverters/ps.py new file mode 100644 index 0000000..aec0c62 --- /dev/null +++ b/src/DocumentLibrary/FileConverters/ps.py @@ -0,0 +1,22 @@ +"""Postscript to text file converter for Document Library""" + +from os import popen, remove, rename + +content_type = 'application/postscript' + +content_description = 'Adobe Postscript Document' + +# Uses the prescript program available at: +# http://www.nzdl.org/html/prescript.html +# Requires write access to /tmp + +def convert(documentFile): + """Convert postscript data to raw text""" + + tmp_name = documentFile._writeToTempFile() + rename(tmp_name, tmp_name + '.ps') + tmp_name = tmp_name + '.ps' + text = popen('prescript plain %s - 2> /dev/null' % tmp_name).read() + remove(tmp_name) + + return text diff --git a/src/DocumentLibrary/FileConverters/wvText.xml b/src/DocumentLibrary/FileConverters/wvText.xml new file mode 100644 index 0000000..e05d9fb --- /dev/null +++ b/src/DocumentLibrary/FileConverters/wvText.xml @@ -0,0 +1,355 @@ +
+ +ABW + + + + + + + + + +
+ + + + +
+ + + + +
+ + +
+ + +type="1" +type="I" +type="i" +type="A" +type="a" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +0 +0 +0 +0 +0 +0 + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/src/DocumentLibrary/FileConverters/xls.py b/src/DocumentLibrary/FileConverters/xls.py new file mode 100644 index 0000000..867d380 --- /dev/null +++ b/src/DocumentLibrary/FileConverters/xls.py @@ -0,0 +1,48 @@ +"""MSExcel doc to text file converter for Document Library + kedai@kedai.com.my""" + + +from os import popen, remove, path +from sgmllib import SGMLParser +from string import join + +content_type = ('application/msexcel', 'application/ms-excel', 'application/vnd.ms-excel') + +content_description = """Microsoft Excel [Download MS Excel Viewer]""" + +#uses xlHTML http://www.xlhtml.org +# Requires write access to /tmp + +class _StripTagParser(SGMLParser): + '''SGML Parser removing any tags and translating HTML entities.''' + + from htmlentitydefs import entitydefs + + data= None + + def handle_data(self,data): + if self.data is None: self.data=[] + self.data.append(data) + + def __str__(self): + if self.data is None: return '' + return join(self.data,'') + + +def convert(documentFile): + """Convert MS-Excel data to raw text""" + + tmp_name = documentFile._writeToTempFile() + text = popen('xlHtml -nc %s 2> /dev/null' % (tmp_name)).read() + remove(tmp_name) + + p = _StripTagParser() + + try: + p.feed(text) + p.close() + return str(p) + except: + return '' diff --git a/src/DocumentLibrary/HISTORY.txt b/src/DocumentLibrary/HISTORY.txt new file mode 100644 index 0000000..913283f --- /dev/null +++ b/src/DocumentLibrary/HISTORY.txt @@ -0,0 +1,217 @@ +DocumentLibrary Product change history + +1.0rc1 - 6/23/02 + + - Fixed file name bug when uploading documents by URL with a query string. + + - Now uses CatalogPathAwareness for virtual hosting compatibility + + - Fixed unpickleable error on file info changes in Zope 2.5+ + + - Minor doc updates. + + - Fixed instantiation bug when working with cvs checkout of DL. + +1.0b3 - 10/30/01 + + - Fixed a bug where changes to the document property "required" flag were + not being properly committed. + + - Document file icons will now be properly updated when you change the + content-type without uploading a new file. + + - All the various MIME types that are used for MS Office files are now + included. Why they need 3 different type strings for every single format + I'll never know. 8^) + + - A Postscript text converter has been added, allows Postscript documents + to be indexed. + + - The code to create the link to open the document info window has been + simplified down to one line. + + - Fixed a bug that prevented instantiating a library in Zope 2.4.2. + Thanks go to Heimo Laukkanen for reporting this. + + - Fixed a bug that prevented document_browse and index_chooser from + working in Zope 2.4.2. Thanks again to Heimo Laukkanen. + + - The document_edit form now allows you to upload a new document or URL + to replace the existing one. + + - Fixed some misspellings in the search_tips page. + +1.0b2 - 7/31/01 + + - A minor JavaScript buglet in the index_chooser has been fixed. + Thanks to Jussi Talaskivi for tracking this down. + + - A missing semi-colon in the document_batch_links has been added. + Thanks to Dave Bukove and Kedai for finding this one. + + - The filename submitted from Internet Explorer is now properly + stripped of the file path. Thanks again to Jussi Talaskivi for + pointing this out. + + - The document_edit method now properly redirects when you + are reviewing documents. Thanks again to Dave Bukove for pointing + this out. + + - An MS Powerpoint text converter has been added, rounding out + the MSOffice indexing support. Also, icons for Excel and Powerpoint + files are now included. + + - A bug that prevented permission changes on the DocumentStore has + been fixed. + + - A bug that prevented the document review link from appearing properly + has been fixed. + + - The catalog text indexes in Zope 2.4 properly acquire and use the + global (or user specified) vocabulary. + +1.0b1 - 7/16/01 + + - It was not possible to restrict access to index topics by changing + their security permissions. Doing so would make it impossible for + users to browse the library. The browsing code has now been updated + to skip unauthorized index topics. Thanks to Sedat Yilmazer for + pointing this out. + + - The document text converters have been simplified and made more + cross-platform friendly. They also consume less server memory for + large document file data. + + - There is now a delete button on the document_edit screen if you + have rights to delete the document. This is something I had added + to my own library instances, but never made it to release. Thanks to + Larry Prikockis for pointing out this omission. + + - A typo in the permissions declarations of DocumentStore has been + corrected. + + - The product now uses the PluginIndexes interface for its Catalogs + and therefore is now compatible with Zope 2.4. Yea!! + + - Form validation has been added to the document_submit and + document_edit forms. + + - Friendly error messages have been created for invalid file and + URL submissions. + + - A message now appears informing the user that a submission + succeeded. It also includes a link to the document so that the + user can edit it right after submission. For this to work, the + owner role should have the permission "Manage properties" for + the Library. + +0.5a - 6/27/01 + + - The AddDocumentFile method of the Document Store now returns the + document object that was added so that it can be further + manipulated or accessed immediately after adding it. + + - Fixed a major bug in the Document class that was causing the + text conversion to fail for large documents because their raw data + was truncated when it was passed to the converter. + + - Added a text converter for MS Excel. Thanks go out to Kendai for + contributing this code. + +0.4a - 6/19/01 + + - Added a new permission "Add Document Library Files Without Review" + which allows selected roles to do just that. + + - A bug in the document_submit and document_edit methods that prevented + the creator document field from being saved has been fixed. Thanks go + to Rogerio Atem for pointing this one out. + + - A bug where cached full-text of document files was not properly flushed + out when a new file was uploaded has been fixed. + + - The document_edit DTML form has a pragma: no-cache header set to prevent + caching of stale document data on the client-side. + + - URL to download MSWord viewer has been updated. + + - Default document property values set in the document store now act + like real attribute values of documents. + + - You can now add selection and multiple selection property types + to the document properties without blowing things up. Thanks go to + David Jacobs for finding this bug. + +0.3a - 5/25/01 + + - Fixed MIME type handling to properly deal with browsers submitting + extra data in the Content-Type header. Thanks to Dieter Stubler + for pointing this out. + + - wvWare errors (which can be numerous, if superfluous) are now + redirected to /dev/null to enhance performance and avoid filling + log files with gibberish. + + - The file name of an uploaded file or URL target is now stored in the + filename property of its document file object. This file name also + becomes an attribute of the file which allows you to use the file + name as the target of the url for a better document downloading + experience. For instance, the following document: + + http://Zope/Library/Documents/6789564.678 + + can now be accessed as: + + http://Zope/Library/Documents/6789564.678/myfile.doc + + assuming its filename is "myfile.doc". This way the client can + download the file under its original name. + + - The document identifier attribute (from Dublin Core) now returns the + full URL including the filename (as above). The default + DTML methods now use the identifier instead of absolute_url() to make + links to the documents. + + - Tightened up security a bit. + + - Added Document property management tab to document store. Yea!! + + - Added Catalog management tabs to document store. Double Yea!! + + - Made search forms repopulate explicitly from REQUEST to avoid various + namespace acquisition problems. Thanks to Joseph Schlesinger for + pointing this out. + + - You can now instantiate new Document Store and Topic Index Root + objects inside a Document Library. + + - Changed global date constants for greater Windows compatibility + (Still no Windows testing has been done by me, however) + +0.2a - 5/18/01 + + - Removed broken symlink from product directory + + - Fixed documentation typos + + - Added the ability to specify an external vocabulary when creating a + library. + + - Removed some code that was not longer used. + + - Switched pathing of wvWare config files in the MSWord file converter + to make sure the ones supplied with the product were used, rather than + relying on them being installed in their default places. + + - Various DTML instance method buglets and snafus were resolved. + + - Added a hide_empty_topics property to the library, which enables a + thinned down browser interface for use with thinly populated topic + indexes. Great when you have 100 topics and 5 documents. + + - Fixed a bug which caused cross-referenced topic indexes assigned to + documents to multiply when the document was edited. + +0.1a - 5/4/01 + + - Initial Release diff --git a/src/DocumentLibrary/INSTALL.txt b/src/DocumentLibrary/INSTALL.txt new file mode 100644 index 0000000..6dfbdc0 --- /dev/null +++ b/src/DocumentLibrary/INSTALL.txt @@ -0,0 +1,56 @@ +Kaivo DocumentLibrary Product Installation + + Compatibility + + At present the DocumentLibrary product runs on Unix + systems only. It has been tested on RedHat Linux version 7.x. + + Win32 compatibility is something that I will add (see TODO.txt) + in the future. In fact, everything except the PDF and Word + document converters should work. I have done zero testing + on this, however. + + Installation Procedure + + Extract the package into your Zope products directory. + (Zope/lib/python/Products). + + For MSWord document text indexing support, you must install + wvWare, an open source MSWord document conversion utility. + You can download wvWare from: http://www.wvWare.com/ + + Once wvWare is installed, make sure you can execute + the wvWare command as the Zope user. If you cannot, adjust + your PATH environment variable accordingly. + + The PDF converter requires the utility pdftotext which is + part of the open source Xpdf package. My RedHat + installation included this, but you can download it and + install it yourself from: http://www.foolabs.com/xpdf/ + + Like wvWare, pdftotext must be in your Zope user's path + for the converter to function. + + The Excel and Powerpoint converters requires the open source + xlHtml converter available at: http://www.xlhtml.org + + The Postscript converter requires the open source Prescript + program available at: http://www.nzdl.org/html/prescript.html + + In order for any of the above converters to work properly, the + zope process must have write access to the system temporary + directory (i.e. /tmp) + + Testing the Converters + + Once you get everything installed, you can easily test the + converters. + + In Zope, add a Document Library object. Inside it will + be a folder called Documents. In there, click on the + "Add Document File" button. Upload a document of each + type you wish to test. + + Select each document one at a time and click on the "Edit" + button. Then click on the "View Plain Text" tab. If all + goes well, the text should appear. diff --git a/src/DocumentLibrary/IconImage.py b/src/DocumentLibrary/IconImage.py new file mode 100644 index 0000000..4b2321b --- /dev/null +++ b/src/DocumentLibrary/IconImage.py @@ -0,0 +1,167 @@ +############################################################################## +# +# Zope Public License (ZPL) Version 1.0 +# ------------------------------------- +# +# Copyright (c) Digital Creations. All rights reserved. +# +# This license has been certified as Open Source(tm). +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# 1. Redistributions in source code must retain the above copyright +# notice, this list of conditions, and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions, and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# +# 3. Digital Creations requests that attribution be given to Zope +# in any manner possible. Zope includes a "Powered by Zope" +# button that is installed by default. While it is not a license +# violation to remove this button, it is requested that the +# attribution remain. A significant investment has been put +# into Zope, and this effort will continue if the Zope community +# continues to grow. This is one way to assure that growth. +# +# 4. All advertising materials and documentation mentioning +# features derived from or use of this software must display +# the following acknowledgement: +# +# "This product includes software developed by Digital Creations +# for use in the Z Object Publishing Environment +# (http://www.zope.org/)." +# +# In the event that the product being advertised includes an +# intact Zope distribution (with copyright and license included) +# then this clause is waived. +# +# 5. Names associated with Zope or Digital Creations must not be used to +# endorse or promote products derived from this software without +# prior written permission from Digital Creations. +# +# 6. Modified redistributions of any form whatsoever must retain +# the following acknowledgment: +# +# "This product includes software developed by Digital Creations +# for use in the Z Object Publishing Environment +# (http://www.zope.org/)." +# +# Intact (re-)distributions of any official Zope release do not +# require an external acknowledgement. +# +# 7. Modifications are encouraged but must be packaged separately as +# patches to official Zope releases. Distributions that do not +# clearly separate the patches from the original work must be clearly +# labeled as unofficial distributions. Modifications which do not +# carry the name Zope may be packaged in any form, as long as they +# conform to all of the clauses above. +# +# +# Disclaimer +# +# THIS SOFTWARE IS PROVIDED BY DIGITAL CREATIONS ``AS IS'' AND ANY +# EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL DIGITAL CREATIONS OR ITS +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# +# This software consists of contributions made by Digital Creations and +# many individuals on behalf of Digital Creations. Specific +# attributions are listed in the accompanying credits file. +# +############################################################################## +"""Image object uploaded via the web""" + +__version__='0.4a' + +from OFS.content_types import guess_content_type +from App.Common import rfc1123_date +from string import rfind, split +from DateTime import DateTime +from time import time +from OFS.Traversable import Traversable +from OFS.SimpleItem import Item_w__name__ +from Acquisition import Implicit +from Globals import Persistent +import string + + +class IconImage(Persistent, Implicit, Item_w__name__): + """Simple icon image objects uploaded via the web""" + + def __init__(self, id, file): + self.data=data=file.read() + + if file.headers.has_key('Content-Type'): + content_type = file.headers['Content-Type'] + else: + content_type, enc = guess_content_type(file.filename, data) + + if content_type: + self.content_type=content_type + else: + self.content_type = 'image/%s' % file.filename[rfind(file.filename,'.')+1:] + self.__name__ = id + self.lmt=time() + self.lmh=rfc1123_date(self.lmt) + + + def index_html(self, REQUEST, RESPONSE): + """Default document""" + # HTTP If-Modified-Since header handling. This is duplicated + # from OFS.Image.Image - it really should be consolidated + # somewhere... + header=REQUEST.get_header('If-Modified-Since', None) + if header is not None: + header=string.split(header, ';')[0] + # Some proxies seem to send invalid date strings for this + # header. If the date string is not valid, we ignore it + # rather than raise an error to be generally consistent + # with common servers such as Apache (which can usually + # understand the screwy date string as a lucky side effect + # of the way they parse it). + try: mod_since=long(DateTime(header).timeTime()) + except: mod_since=None + if mod_since is not None: + if getattr(self, 'lmt', None): + last_mod = long(self.lmt) + else: + last_mod = long(0) + if last_mod > 0 and last_mod <= mod_since: + RESPONSE.setStatus(304) + return '' + + RESPONSE.setHeader('Content-Type', self.content_type) + RESPONSE.setHeader('Last-Modified', self.lmh) + return self.data + + def __str__(self): + """Returns the source path for the image""" + return string.join(self.getPhysicalPath()[1:], '/') + + __repr__ = __str__ + + HEAD__roles__=None + def HEAD(self, REQUEST, RESPONSE): + """ """ + RESPONSE.setHeader('Content-Type', self.content_type) + RESPONSE.setHeader('Last-Modified', self.lmh) + return '' + + def __len__(self): + # This is bogus and needed because of the way Python tests truth. + return 1 + + diff --git a/src/DocumentLibrary/LICENSE.txt b/src/DocumentLibrary/LICENSE.txt new file mode 100644 index 0000000..42887ce --- /dev/null +++ b/src/DocumentLibrary/LICENSE.txt @@ -0,0 +1,37 @@ +################################################################################## +# +# Kaivo Public Software License +# +# Copyright (c) 2001, Kaivo, Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# o Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# o Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# +# o Neither Kaivo nor the names of its contributors may be used to endorse +# or promote products derived from this software without specific prior +# written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL KAIVO OR ITS CONTRIBUTORS BE LIABLE FOR +# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +################################################################################## + +This product includes software developed by Digital Creations +for use in the Z Object Publishing Environment (http://www.zope.org/). diff --git a/src/DocumentLibrary/README.txt b/src/DocumentLibrary/README.txt new file mode 100644 index 0000000..dc50377 --- /dev/null +++ b/src/DocumentLibrary/README.txt @@ -0,0 +1,130 @@ +DocumentLibrary Product + + The DocumentLibrary is designed to be a drop-in Zope product + that allows you to create full-text searchable and browsable + document libraries. It requires Zope 2.3.2 or higher. + It also requires the BTreeFolder product version 0.2, which + is included in the package. + + Document Storage + + This product comes with a special document file class that + allows you to store and index several file formats + in the library. Since they are Zope objects, you can + also associate arbitrary metadata with your documents. + The default installation includes a subset of the + Dublin Core document metadata standard: + + - identifier:* Absolute URL of the document + + - title: User specified title + + - creator: Original author + + - description: Document abstract + + - date: Creation or revision date + + - type: General category + + - format:* MIME format of the document data + + - source:* URI of original document (if any) + + - subject:* Topic index titles assigned to document. + + *Indicates attributes that are derived programmatically. + + In addition, the following properties are available: + + - review_date: Date the document was reviewed. + + - topics: Python list of topic index ids assigned. + + - filename: The original file name of the file submitted. + + Documents stored in the library have fixed property sheets + that are managed centrally. This allows you to modify the + property metadata schema for all documents at once. + + The documents are indexed when they are submitted to the + library. Currently full-text indexing is supported for + the following file formats: + + - Plain Text + + - HTML/XML + + - PDF + + - Postscript + + - Microsoft Word (6.0/95, 97 & 2000 formats) + + - Microsoft Excel + + - Microsoft Powerpoint + + A plug-in architecture for full-text converters has been + implemented to make adding support for new file formats + as simple as possible. + + Document file objects can also be used independently of the + library if you want to use their text indexing facilities + separately. + + Topic Index + + The library supports a hierarchical indexing system to + categorize documents. A single document can be assigned to + as many different topic indexes as desired. + + To enhance the look and feel of your library interface, + Topic Indexes can be assigned custom icons from within the + Zope management interface. + + The default user interface is a hierarchical drill-down + through the index. It is essentially a simplified tree + interface. This can be completely customized for your needs + by modifying the DTML methods in the library. + + An entire topic index hierarchy can be imported at once + from a text file containing a tabbed outline. A sample + legal services topic index is available for download. + + Searching + + The library includes two interfaces for users to use for + queries: a simple search that searches all document + meta-data and text simultaneously and an advanced search + that allows more refined searches. + + The search machinery uses Zope Catalogs, and so has simple + boolean search support ("and", "or", "and not" and near "...") + for textual content and meta-data. + + Localized searching is also supported to allow users to + perform searches on only those documents under a specific + topic index. + + Document Submission and Review + + Documents can be submitted to the library by any users you + choose to grant this permissions to, including anonymous users. + Documents are submitted by uploading a file from the user's + computer or by specifying a URL where the document resides. + If a URL is specified, the file data is retrieved and stored + in the document file at upload time. + + To guard against inappropriate submissions, a review feature + is implemented to allow one or more reviewers to approve or + reject submissions. To allow the delegation of this review + process to several people, each reviewer can be assigned a + separate topic index that review documents for. + + A further safeguard is a list of file types that are not + allowed in the library. This can be modified to restrict + certain unsafe file types. By default, all unknown binary + files (such as executables) cannot be uploaded into the + library. This can be modified by going to the Properties tab + of a document library object. diff --git a/src/DocumentLibrary/TODO.txt b/src/DocumentLibrary/TODO.txt new file mode 100644 index 0000000..d7f5e07 --- /dev/null +++ b/src/DocumentLibrary/TODO.txt @@ -0,0 +1,42 @@ +Things To Do for the DocumentLibrary Product + +_Please note_: that offers of money or free food and beverages +may be effective in changing this prioritization. + + Near Term + + - Create interface files for the help system, add help system + docs, and clean up to better conform to guidelines. + + - Write API docs. + + - Implement support for indexing additional file formats such + as WordPerfect, Excel, etc. + + - Support cross-format conversions such as Word to PDF, PDF + to HTML, etc. + + - Swill malted/caffeinated beverages. <==repeat as needed + + In a While + + - Support multiple file formats per document. + + - Support Win32. This is mostly a function of getting cross- + platform file converters and taking time to test. + + Long Term + + - Support storage of document data in file system. + + - Support file histories for revision management. + + - Multiple language support. + + - Implement some reporting functionality. + + - Implement XML/RDF publication of document info. + + Once I Retire + + - Implement clever WebDAV support for intranet libraries. diff --git a/src/DocumentLibrary/TopicIndex.py b/src/DocumentLibrary/TopicIndex.py new file mode 100644 index 0000000..3771842 --- /dev/null +++ b/src/DocumentLibrary/TopicIndex.py @@ -0,0 +1,401 @@ +################################################################################## +# +# Kaivo Public Software License +# +# Copyright (c) 2001, Kaivo, Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# o Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# o Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# +# o Neither Kaivo nor the names of its contributors may be used to endorse +# or promote products derived from this software without specific prior +# written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL KAIVO OR ITS CONTRIBUTORS BE LIABLE FOR +# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +################################################################################## + +__doc__ = """Document Library Topic Index""" +__version__ = '1.0b2' + +from os import path +from string import join, find, strip +import Globals +from Globals import DTMLFile +from App.ImageFile import ImageFile +from OFS.Folder import Folder +from Acquisition import aq_base +from CatalogPlus import CatalogPlus, FieldIndex, TextIndex, KeywordIndex +from IconImage import IconImage +try: + from Products.ZCatalog.CatalogPathAwareness import CatalogAware +except ImportError: + from Products.ZCatalog.CatalogAwareness import CatalogAware + +class TopicIndexBase(Folder): + """Document Library Index Topic Base Class""" + + all_meta_types = ({'name': 'Topic Index', 'action': 'manage_addTopicIndexForm'},) + + __ac_permissions__ = Folder.__ac_permissions__ + ( + ('Add Library Topic Indexes', ('manage_addTopicIndexForm', 'manage_addTopicIndex')), + ('View', ('topicParents', 'topicPath', 'getTopicId')), + ) + + isLibraryTopicIndex = 1 + + def __init__(self, id, title=''): + self.id = id + self.title = title + self.documents = [] + + manage_addTopicIndexForm = DTMLFile('dtml/AddIndexForm', globals()) + + def manage_addTopicIndex(self, id, title='', REQUEST=None): + """Construct a Topic Index object""" + + self._setObject(id, TopicIndex(id, title)) + + if REQUEST is not None: + return self.manage_main(self, REQUEST, update_menu=1) + + def topicParents(self): + """Return a list of topic indexes from the topic index root down to this topic""" + + r = [self] + parent = self.aq_parent + + while hasattr(parent.aq_explicit, 'isLibraryTopicIndex'): + r.append(parent) + parent = parent.aq_parent + + r.reverse() + return r + + def topicPath(self): + """Returns the path from the index root to this topic index""" + parent_ids = map(lambda ob: ob.getId(), self.topicParents()) + return join(parent_ids[1:],'/') + + def getTopicId(self): + """Returns the topic id of the current index topic. + Used to pass the id without namespace collisions""" + if self.meta_type != TopicIndexRoot.meta_type: + return self.id + else: + return None + + TopicIndexBase_icon = ImageFile('www/DefaultTopicIndex_icon.gif', globals()) + def icon(self): + root = self.topicParents()[0] + return join(root.getPhysicalPath()[1:],'/') + '/TopicIndexBase_icon' + + +class TopicIndex(CatalogAware, TopicIndexBase): + """Document Library Index Topic""" + + meta_type = 'Topic Index' + + _properties = TopicIndexBase._properties + ( + { 'id': 'visible', 'type': 'boolean', 'mode': 'w' }, + ) + + manage_options = TopicIndexBase.manage_options + ( + { 'label': 'Icon', 'action': 'manage_changeIconForm' }, + ) + + visible = 0 + + # Icon management stuff + + manage_changeIconForm = DTMLFile('dtml/IndexIconForm', globals()) + + def manage_changeIcon(self, file, REQUEST=None, RESPONSE=None): + """Change topic index icon""" + self.topic_icon = IconImage('topic_icon', file) + + if REQUEST is not None and RESPONSE is not None: + return self.manage_changeIconForm(REQUEST, RESPONSE) + + def icon(self): + if hasattr(self.aq_base, 'topic_icon'): + return self.topic_icon + elif hasattr(self, 'default_icon'): + return self.default_icon + else: + return TopicIndexBase.icon(self) + + def manage_changeProperties(self, REQUEST=None, **kw): + """Update properties and reindex""" + r = TopicIndexBase.manage_changeProperties(self, REQUEST, kw=kw) + + # Check for afterEditTopicIndex hook method and call it + if hasattr(self, 'afterEditTopicIndex'): + self.afterEditTopicIndex() + + self.reindex_object() + return r + + def manage_editProperties(self, REQUEST): + """Edit Properties and reindex""" + r = TopicIndexBase.manage_editProperties(self, REQUEST) + + # Check for afterEditTopicIndex hook method and call it + if hasattr(self, 'afterEditTopicIndex'): + self.afterEditTopicIndex() + + self.reindex_object() + return r + + def _setPropValue(self, id, value): + """Override this private method from PropertyManager to make sure + setting the visible property propagates up though the parents""" + + if id == 'visible' and value: + # Make sure the visible value is set to 1 as a value for indexing purposes + TopicIndexBase._setPropValue(self, 'visible', 1) + self.showTopic() + else: + TopicIndexBase._setPropValue(self, id, value) + + def manage_renameObject(self, id, new_id, REQUEST=None): + """Rename object and reindex""" + # Check for beforeDeleteTopicIndex hook method and call it + if hasattr(self, 'beforeDeleteTopicIndex'): + self.beforeDeleteTopicIndex() + + r = TopicIndexBase.manage_renameObject(self, id, new_id, REQUEST) + + self.reindex_object() + return r + + def manage_afterAdd(self, item, container): + """Call afterAddTopicIndex hook method if any""" + if hasattr(self, 'afterAddTopicIndex'): + self.afterAddTopicIndex() + + self.index_object() + + def manage_beforeDelete(self, item, container): + """Call beforeDeleteTopicIndex hook method if any""" + if hasattr(self, 'beforeDeleteTopicIndex'): + self.beforeDeleteTopicIndex() + + self.unindex_object() + + def manage_afterClone(self, item): + """Call afterAddTopicIndex hook method if any""" + if hasattr(self, 'afterAddTopicIndex'): + self.afterAddTopicIndex() + + self.index_object() + + def showTopic(self): + """Make this topic and parent topics visible""" + ob = self + + while ob.meta_type == self.meta_type: + ob.visible = 1 + ob.reindex_object() + ob = ob.aq_parent + + def hideTopic(self): + """Make this topic invisible""" + self.visible = 0 + self.reindex_object() + + +Globals.InitializeClass(TopicIndex) + +manage_addLibraryTopicIndexRootForm = DTMLFile('dtml/AddTopicIndexRootForm', globals()) + +def manage_addLibraryTopicIndexRoot(self, id, title='', vocab_id = None, + REQUEST=None): + """Add a document store instance to a library""" + ob = TopicIndexRoot(id, title, vocab_id, self) + self._setObject(id, ob) + + if REQUEST is not None: + REQUEST['RESPONSE'].redirect(self.absolute_url()+'/manage_main') + + +class TopicIndexRoot(TopicIndexBase): + """Document Library Topic Index Root""" + + meta_type = 'Topic Index Root' + + manage_options = TopicIndexBase.manage_options + ( + { 'label': 'Import Topic Index', 'action': 'manage_importIndexForm' }, + { 'label': 'Default Topic Icon', 'action': 'manage_changeIconForm' } + ) + + __ac_permissions__ = TopicIndexBase.__ac_permissions__ + ( + ('Manage properties', ('manage_changeIconForm', 'manage_changeIcon')), + ('Add Document Library Topics', ('manage_importIndexForm', 'manage_importIndex')), + ('Delete objects', ('manage_importIndexForm', 'manage_importIndex')), + ('Search ZCatalog', ('query', 'getpath')), + ) + + manage_changeIconForm = DTMLFile('dtml/IndexIconForm', globals()) + + def manage_changeIcon(self, file, REQUEST=None, RESPONSE=None): + """Change topic index icon""" + self.default_icon = IconImage('default_icon', file) + + if REQUEST is not None and RESPONSE is not None: + return self.manage_changeIconForm(REQUEST, RESPONSE) + + TopicIndexRoot_icon = ImageFile('www/TopicIndexRoot_icon.gif', globals()) + def icon(self): + """Management icon""" + return join(self.getPhysicalPath()[1:],'/') + '/TopicIndexRoot_icon' + + def _initCatalog(self, vocab_id): + """Setup the index root catalog""" + self.Catalog = CatalogPlus(vocab_id) + lexicon = self.Catalog.getLexicon() + + self.Catalog.addColumn('id') + self.Catalog.addIndex('id', FieldIndex('id')) + self.Catalog.addColumn('title') + self.Catalog.addIndex('title', TextIndex('title', lexicon=lexicon)) + self.Catalog.addIndex('visible', FieldIndex('visible')) + + def __init__(self, id, title='', vocab_id=None, container=None): + if vocab_id is not None and container is None: + raise AttributeError, ("You cannot specify a vocab_id without " + "also specifying a container.") + if container is not None: + self=self.__of__(container) + + self._initCatalog(vocab_id) + TopicIndexBase.__init__(aq_base(self), id, title) + + manage_importIndexForm = DTMLFile('dtml/ImportIndexForm', globals()) + + def manage_importIndex(self, file, clear=1, delimiter=';', RESPONSE=None): + """Create a topics from a text outline file""" + + if delimiter == '\\t': delimiter = '\t' + + if clear: + # Clear existing topics + self.manage_delObjects(self.objectIds(TopicIndex.meta_type)) + # We shouldn't need to reinit the catalog, but... + self._initCatalog(self.Catalog.lexicon) + + # Execute clearIndexTopics hook method if any + if hasattr(self, 'clearIndexTopics'): + self.clearIndexTopics() + + topic_id_seq=0 + indent=0 + new_index=None + line_num = 0 + drill = [self] + + for line in file.readlines(): + line_num = line_num + 1 + + # figure out the indent of this line + last_indent = indent + indent = 0 + while line[indent] == '\t' and indent < len(line): + indent = indent + 1 + + if indent - last_indent > 1: + raise 'IndentError', 'Indentation error at line %d of index file' % line_num + + if indent > last_indent: + if new_index is not None: + drill.append(new_index) + else: + raise 'IndentError', 'Indentation error at line %d of index file' % line_num + elif indent < last_indent: + drill = drill[:indent + 1] + + id, title = getTopicInfo(line[indent:], delimiter) + + if title: + index = drill[-1] + if not id: + id = nextTopicId(index, topic_id_seq) + topic_id_seq = int(id) + 1 + new_index = TopicIndex(id, title) + index._setObject(id, new_index) + new_index = index._getOb(id) + new_index.index_object() + + if RESPONSE is not None: + RESPONSE.redirect('manage_main') + + # + # Catalog query support methods + # + + def query(self, REQUEST=None, **kw): + """Query the Index for matching topics""" + return apply(self.Catalog.searchResults, (REQUEST,), kw) + + def getpath(self, rid): + """ + Return the path to a cataloged object given a 'data_record_id_' + Used by the Catalog brains + """ + return self.Catalog.paths[rid] + + def getTopicIds(self, title): + """Return a list of topic ids that match the title query string""" + topics = self.query(title=title) + if topics: + return map(lambda t: t.id, topics) + else: + return [] + +Globals.InitializeClass(TopicIndexRoot) + +def getTopicInfo(line, delimiter): + """Return the title and id from the topic import file line""" + + d = find(line, delimiter) + l = len(delimiter) + + if d > 0: + title = strip(line[:d]) + id = strip(line[d + l:]) + else: + id = '' + title = strip(line) + + return (id, title) + +def nextTopicId(index, seq, pad='000000'): + """Returns the next topic id string and increments the sequence for import""" + + i=index.aq_base + id ='' + + while not id or hasattr(i, id): + id = str(seq) + if len(id) < len(pad): id = pad[len(id):] + id + seq = seq + 1 + + return id diff --git a/src/DocumentLibrary/__init__.py b/src/DocumentLibrary/__init__.py new file mode 100644 index 0000000..0e6f23f --- /dev/null +++ b/src/DocumentLibrary/__init__.py @@ -0,0 +1,26 @@ +import DocumentLibrary +import Document +import DocumentStore +import TopicIndex + +def initialize(context): + """Initialize Document Library Product""" + + context.registerClass( + DocumentLibrary.DocumentLibrary, + constructors = ( + DocumentLibrary.manage_addDocumentLibraryForm, + DocumentLibrary.manage_addDocumentLibrary + ), + permission = 'Add Document Libraries', + icon = 'www/DocumentLibrary_icon.gif' + ) + + context.registerClass( + Document.Document, + constructors = ( + Document.manage_addDocumentFileForm, + Document.manage_addDocumentFile + ), + permission = 'Add Document Library Files' + ) diff --git a/src/DocumentLibrary/document_icons/CVS/Entries b/src/DocumentLibrary/document_icons/CVS/Entries new file mode 100644 index 0000000..3de68ee --- /dev/null +++ b/src/DocumentLibrary/document_icons/CVS/Entries @@ -0,0 +1,13 @@ +/excel_large.gif/1.1.1.1/Fri Jan 18 04:38:40 2002// +/excel_small.gif/1.1.1.1/Fri Jan 18 04:38:40 2002// +/generic_large.gif/1.1.1.1/Fri Jan 18 04:38:40 2002// +/generic_small.gif/1.1.1.1/Fri Jan 18 04:38:40 2002// +/html_large.gif/1.1.1.1/Fri Jan 18 04:38:40 2002// +/html_small.gif/1.1.1.1/Fri Jan 18 04:38:40 2002// +/msword_large.gif/1.1.1.1/Fri Jan 18 04:38:40 2002// +/msword_small.gif/1.1.1.1/Fri Jan 18 04:38:40 2002// +/pdf_large.gif/1.1.1.1/Fri Jan 18 04:38:40 2002// +/pdf_small.gif/1.1.1.1/Fri Jan 18 04:38:40 2002// +/powerpoint_large.gif/1.1.1.1/Fri Jan 18 04:38:40 2002// +/powerpoint_small.gif/1.1.1.1/Fri Jan 18 04:38:40 2002// +D diff --git a/src/DocumentLibrary/document_icons/CVS/Repository b/src/DocumentLibrary/document_icons/CVS/Repository new file mode 100644 index 0000000..fdea3fb --- /dev/null +++ b/src/DocumentLibrary/document_icons/CVS/Repository @@ -0,0 +1 @@ +DocumentLibrary/document_icons diff --git a/src/DocumentLibrary/document_icons/CVS/Root b/src/DocumentLibrary/document_icons/CVS/Root new file mode 100644 index 0000000..2084eca --- /dev/null +++ b/src/DocumentLibrary/document_icons/CVS/Root @@ -0,0 +1 @@ +:pserver:anonymous@cvs.nlada-library.sourceforge.net:/cvsroot/nlada-library diff --git a/src/DocumentLibrary/document_icons/excel_large.gif b/src/DocumentLibrary/document_icons/excel_large.gif new file mode 100644 index 0000000..403e175 --- /dev/null +++ b/src/DocumentLibrary/document_icons/excel_large.gif Binary files differ diff --git a/src/DocumentLibrary/document_icons/excel_small.gif b/src/DocumentLibrary/document_icons/excel_small.gif new file mode 100644 index 0000000..99e4022 --- /dev/null +++ b/src/DocumentLibrary/document_icons/excel_small.gif Binary files differ diff --git a/src/DocumentLibrary/document_icons/generic_large.gif b/src/DocumentLibrary/document_icons/generic_large.gif new file mode 100644 index 0000000..4b2b6fb --- /dev/null +++ b/src/DocumentLibrary/document_icons/generic_large.gif Binary files differ diff --git a/src/DocumentLibrary/document_icons/generic_small.gif b/src/DocumentLibrary/document_icons/generic_small.gif new file mode 100644 index 0000000..831dea0 --- /dev/null +++ b/src/DocumentLibrary/document_icons/generic_small.gif Binary files differ diff --git a/src/DocumentLibrary/document_icons/html_large.gif b/src/DocumentLibrary/document_icons/html_large.gif new file mode 100644 index 0000000..6386df8 --- /dev/null +++ b/src/DocumentLibrary/document_icons/html_large.gif Binary files differ diff --git a/src/DocumentLibrary/document_icons/html_small.gif b/src/DocumentLibrary/document_icons/html_small.gif new file mode 100644 index 0000000..2f87585 --- /dev/null +++ b/src/DocumentLibrary/document_icons/html_small.gif Binary files differ diff --git a/src/DocumentLibrary/document_icons/msword_large.gif b/src/DocumentLibrary/document_icons/msword_large.gif new file mode 100644 index 0000000..03ef2dc --- /dev/null +++ b/src/DocumentLibrary/document_icons/msword_large.gif Binary files differ diff --git a/src/DocumentLibrary/document_icons/msword_small.gif b/src/DocumentLibrary/document_icons/msword_small.gif new file mode 100644 index 0000000..b04162f --- /dev/null +++ b/src/DocumentLibrary/document_icons/msword_small.gif Binary files differ diff --git a/src/DocumentLibrary/document_icons/pdf_large.gif b/src/DocumentLibrary/document_icons/pdf_large.gif new file mode 100644 index 0000000..ff58b01 --- /dev/null +++ b/src/DocumentLibrary/document_icons/pdf_large.gif Binary files differ diff --git a/src/DocumentLibrary/document_icons/pdf_small.gif b/src/DocumentLibrary/document_icons/pdf_small.gif new file mode 100644 index 0000000..20bcf99 --- /dev/null +++ b/src/DocumentLibrary/document_icons/pdf_small.gif Binary files differ diff --git a/src/DocumentLibrary/document_icons/powerpoint_large.gif b/src/DocumentLibrary/document_icons/powerpoint_large.gif new file mode 100644 index 0000000..1921256 --- /dev/null +++ b/src/DocumentLibrary/document_icons/powerpoint_large.gif Binary files differ diff --git a/src/DocumentLibrary/document_icons/powerpoint_small.gif b/src/DocumentLibrary/document_icons/powerpoint_small.gif new file mode 100644 index 0000000..c19f363 --- /dev/null +++ b/src/DocumentLibrary/document_icons/powerpoint_small.gif Binary files differ diff --git a/src/DocumentLibrary/dtml/AddDocumentForm.dtml b/src/DocumentLibrary/dtml/AddDocumentForm.dtml new file mode 100755 index 0000000..0ef1012 --- /dev/null +++ b/src/DocumentLibrary/dtml/AddDocumentForm.dtml @@ -0,0 +1,66 @@ + + + + +

+A Document File is an enhanced file object that allows full text +searchability of file formats such as MSWord, PDF and HTML. It +also contains enhanced support for the +Dublin Core Metadata Initiative. +

+

+Select a file to upload from your local computer by clicking the +Browse button. +

+ +
+ + + + + + + + + + + + + + + + + +
+
+ Id +
+
+ +
+
+ Title +
+
+ +
+
+ File +
+
+ +
+ +
+ +
+
+
+ + + diff --git a/src/DocumentLibrary/dtml/AddDocumentStoreForm.dtml b/src/DocumentLibrary/dtml/AddDocumentStoreForm.dtml new file mode 100755 index 0000000..133a10e --- /dev/null +++ b/src/DocumentLibrary/dtml/AddDocumentStoreForm.dtml @@ -0,0 +1,64 @@ + + + +

+A document store is used by a document library to store and +catalog document files. +

+ +
+ + + + + + + + + + + + + + + + + +
+
+ Id +
+
+ +
+
+ Title +
+
+ +
+
+ Vocabulary +
+
+
+ +
+
+ +
+ +
+
+
+ + diff --git a/src/DocumentLibrary/dtml/AddIndexForm.dtml b/src/DocumentLibrary/dtml/AddIndexForm.dtml new file mode 100755 index 0000000..1143503 --- /dev/null +++ b/src/DocumentLibrary/dtml/AddIndexForm.dtml @@ -0,0 +1,46 @@ + + + +

+Add a document library index topic. +

+ +
+ + + + + + + + + + + + + +
+
+ Id +
+
+ +
+
+ Title +
+
+ +
+ +
+ +
+
+
+ + diff --git a/src/DocumentLibrary/dtml/AddLibraryForm.dtml b/src/DocumentLibrary/dtml/AddLibraryForm.dtml new file mode 100755 index 0000000..1d39193 --- /dev/null +++ b/src/DocumentLibrary/dtml/AddLibraryForm.dtml @@ -0,0 +1,65 @@ + + + +

+A document library stores document files in a topic hierarchy. +It provides functionality for users to browse, search and add documents +through a web interface. +

+ +
+ + + + + + + + + + + + + + + + + +
+
+ Id +
+
+ +
+
+ Title +
+
+ +
+
+ Vocabulary +
+
+
+ +
+
+ +
+ +
+
+
+ + diff --git a/src/DocumentLibrary/dtml/AddTopicIndexRootForm.dtml b/src/DocumentLibrary/dtml/AddTopicIndexRootForm.dtml new file mode 100755 index 0000000..b4d6f76 --- /dev/null +++ b/src/DocumentLibrary/dtml/AddTopicIndexRootForm.dtml @@ -0,0 +1,64 @@ + + + +

+A library topic index root is the container of a library's +topic index hierarchy. In it you can create library topic indexes. +It also provides support for topic index searching.

+ +
+ + + + + + + + + + + + + + + + + +
+
+ Id +
+
+ +
+
+ Title +
+
+ +
+
+ Vocabulary +
+
+
+ +
+
+ +
+ +
+
+
+ + diff --git a/src/DocumentLibrary/dtml/CVS/Entries b/src/DocumentLibrary/dtml/CVS/Entries new file mode 100644 index 0000000..98e2664 --- /dev/null +++ b/src/DocumentLibrary/dtml/CVS/Entries @@ -0,0 +1,13 @@ +/AddDocumentForm.dtml/1.1.1.1/Fri Jan 18 04:38:42 2002// +/AddDocumentStoreForm.dtml/1.1.1.1/Fri Jan 18 04:38:41 2002// +/AddIndexForm.dtml/1.1.1.1/Fri Jan 18 04:38:41 2002// +/AddLibraryForm.dtml/1.1.1.1/Fri Jan 18 04:38:41 2002// +/AddTopicIndexRootForm.dtml/1.1.1.1/Fri Jan 18 04:38:42 2002// +/EditDocumentProperties.dtml/1.1.1.1/Fri Jan 18 04:38:42 2002// +/ImportIndexForm.dtml/1.1.1.1/Fri Jan 18 04:38:41 2002// +/IndexIconForm.dtml/1.1.1.1/Fri Jan 18 04:38:42 2002// +/ManagerMain.dtml/1.1.1.1/Fri Jan 18 04:38:42 2002// +/catalogColumns.dtml/1.1.1.1/Fri Jan 18 04:38:42 2002// +/catalogIndexes.dtml/1.1.1.1/Fri Jan 18 04:38:42 2002// +/catalogMaintenance.dtml/1.1.1.1/Fri Jan 18 04:38:43 2002// +D diff --git a/src/DocumentLibrary/dtml/CVS/Repository b/src/DocumentLibrary/dtml/CVS/Repository new file mode 100644 index 0000000..6b5599e --- /dev/null +++ b/src/DocumentLibrary/dtml/CVS/Repository @@ -0,0 +1 @@ +DocumentLibrary/dtml diff --git a/src/DocumentLibrary/dtml/CVS/Root b/src/DocumentLibrary/dtml/CVS/Root new file mode 100644 index 0000000..2084eca --- /dev/null +++ b/src/DocumentLibrary/dtml/CVS/Root @@ -0,0 +1 @@ +:pserver:anonymous@cvs.nlada-library.sourceforge.net:/cvsroot/nlada-library diff --git a/src/DocumentLibrary/dtml/EditDocumentProperties.dtml b/src/DocumentLibrary/dtml/EditDocumentProperties.dtml new file mode 100755 index 0000000..e7f41be --- /dev/null +++ b/src/DocumentLibrary/dtml/EditDocumentProperties.dtml @@ -0,0 +1,252 @@ + + + + +

+You are currently working in version +

+
+ +
+ +

+Modify the property schema and default property values for the +documents stored here. Properties added or removed here affect +all documents contained in this document store. +

+ + + + + + + + + + + + + + + + + + + + + + + +
+   + +
+ Name +
+
+
+ Default Value +
+
+
+ Type +
+
+
+ Required Field? +
+
+ + + + + +
+ +
+
+ + + + "> + + "> + + "> + + "> + + CHECKED> + + "> + + + + + + + + +
+ +
+ +
+ +
+ +
+ No value for . +
+
+ + + + +
+ +
+ +
+ +
+ +
+ No value for . +
+
+ + Unknown property type +
+ + + +
+ +
+
+ &dtml-type; +
+
+ checked> +
  +
+ + + +
+
+ + +

+Properties allow you to assign simple values to Zope objects. There are +currently no properties defined for this item. To add a property, enter a name, type +and value and click the "Add" button. +

+ +
+
+ +
+ +

+To add a new property for all documents, enter a name, type and value for the new +property and click the "Add" button. +

+ + + + + + + + + + + + + +
+
+ Name +
+
+ + + Type + +
+ +
+
+
+ Default Value +
+
+ + +
+ + +     + +
+
+
+ + + + diff --git a/src/DocumentLibrary/dtml/ImportIndexForm.dtml b/src/DocumentLibrary/dtml/ImportIndexForm.dtml new file mode 100755 index 0000000..01c4e10 --- /dev/null +++ b/src/DocumentLibrary/dtml/ImportIndexForm.dtml @@ -0,0 +1,64 @@ + + + + + +

+Creates a topic index from a text file outline. The tabbed indentation +of each line determines the level of each index topic created. The title and +id +of each topic can be specified by delimiting the two on a given line: title;id. +The default delimiter is a semicolon (;). If no delimiter is used, the +text of the line becomes the topic's title and the id is assigned in sequence. +

+

+WARNING: +The existing index topics will be cleared! +

+

+Select the index file to upload from your local computer by clicking the +Browse button. +

+ +
+ + + + + + + + + + + + + + +
+
+ Tabbed Index Text File +
+
+ +
+
+ id/title delimiter +
+
+ + (\t = tab) +
+ +
+ +
+
+
+ + + diff --git a/src/DocumentLibrary/dtml/IndexIconForm.dtml b/src/DocumentLibrary/dtml/IndexIconForm.dtml new file mode 100755 index 0000000..4d2a25d --- /dev/null +++ b/src/DocumentLibrary/dtml/IndexIconForm.dtml @@ -0,0 +1,49 @@ + + + + + +

+Select the icon file to upload from your local computer by clicking the +Browse button. +

+ +
+ + + + + + + + + + + + + + + +
+
+ Icon File +
+
+ +
+ +
+ +
+
+ + icon +
+
+ + + diff --git a/src/DocumentLibrary/dtml/ManagerMain.dtml b/src/DocumentLibrary/dtml/ManagerMain.dtml new file mode 100755 index 0000000..eeca31e --- /dev/null +++ b/src/DocumentLibrary/dtml/ManagerMain.dtml @@ -0,0 +1,259 @@ + + + + + + + + + +
+ + + + + + +
  +
+
+ 1"> + + + + + + + + +
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + &dtml-meta_type; + +   + + +
+ + &dtml-sequence-key; (&dtml-title;) + + + + This item has been modified in this version + + This item has been modified in another version + (&dtml-locked_in_version;) + + +
+
+
+ + + + + 1 Kb + 1048576"> + Mb + + Kb + + + +   + + +   + +
+
+
+ +
+
+ + + + + + +
+
+ + + + + + + + + + + + + + + + +
+
+ + + + + + +
+
+There are currently no items in &dtml-title_or_id; +

+
+ + +
+ + +
+
+
+ + + +
+
+
+ + + + + + + + + + + + + + + + + + + diff --git a/src/DocumentLibrary/dtml/catalogColumns.dtml b/src/DocumentLibrary/dtml/catalogColumns.dtml new file mode 100755 index 0000000..02bcc42 --- /dev/null +++ b/src/DocumentLibrary/dtml/catalogColumns.dtml @@ -0,0 +1,62 @@ + + + +

+This list defines what document file meta-data is available to the document +list interface. +

+
+ + + + + + + + + + + + +
+ + +
+ +
+
+ +
+ +
+
+
+
+ +
+ + + + + + + + + +
+
+ Add Metadata +
+
+ +
+ +
+ +
+
+ +
+ + diff --git a/src/DocumentLibrary/dtml/catalogIndexes.dtml b/src/DocumentLibrary/dtml/catalogIndexes.dtml new file mode 100755 index 0000000..a73f231 --- /dev/null +++ b/src/DocumentLibrary/dtml/catalogIndexes.dtml @@ -0,0 +1,115 @@ + + + +

+This list defines the search indexes available for documents stored +here.

+ +

+Text Indexes break text up into individual words, and +are often referred to as full-text indexes. Text indexes +sort results by score meaning they return hits in order +from the most relevant to the lest relevant. +

+ +

+Field Indexes treat the value of an objects attributes +atomically. +

+ +

+Keyword Indexes index a sequence of objects that act as +'keywords' for an object. A Keyword Index will return any objects +that have one or more keywords specified in a search query. +

+ +
+ + + + + + + + + + + + + + + + + + + + + + +
  +
Index Name
+
Index Type
+ + +
+ &dtml-id; +
+
+
&dtml-meta_type;
+ +
+ +
+
+ + +
+
+ + + + + + + + + + + + + +
+
+ Add Index +
+
+ +
+
+ Of Type +
+
+
+ +
+
+ +
+ +
+
+
+ + + + + + + + diff --git a/src/DocumentLibrary/dtml/catalogMaintenance.dtml b/src/DocumentLibrary/dtml/catalogMaintenance.dtml new file mode 100755 index 0000000..249eac3 --- /dev/null +++ b/src/DocumentLibrary/dtml/catalogMaintenance.dtml @@ -0,0 +1,117 @@ + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ Document Catalog Maintenance +
+
+

Reinitializing the document catalog will + completely clear the catalog, rebuild the indexes and metadata + elements and reindex all of the documents contained here. This + can take a very long time to complete if you have many documents + in your library. +

+
+
+ +
+
+ +
+
+ Subtransactions +
+
+ +

Subtransactions allow Zope to commit small + parts of a transaction over a period of time instead of all at + once. For ZCatalog, this means using subtransactions can + signficantly reduce the memory requirements needed to index huge + amounts of text all at once. Currently, subtransactions are only + applied to text indexes.

+ +

If enabled, subtransactions will reduce the memory + requirements of ZCatalog, but at the expense of speed. + If you choose to enable subtransactions, you can adjust how often + ZCatalog commits a subtransactions by adjusting the + threshold below.

+ +

If you are using ZCatalog and ZSQL Methods + in the same transaction, you must disable + subtransactions, they are not compatible with ZSQL Methods.

+
+
+

Subtransactions are + + Enabled + + Disabled +

+
+
+
+ + + + + +
+
+
+

The Subtransaction threshold is the number of + objects cataloged + in the context of a single transaction that the catalog + will index before it commits a subtransaction. If this number + is low, the Catalog will take longer to index but consume less + memory. If this number is higher, the Catalog will index + quickly but consume much more memory.

+
+
+
+ + +
+
+ +
+ + + + + + diff --git a/src/DocumentLibrary/instance/CVS/Entries b/src/DocumentLibrary/instance/CVS/Entries new file mode 100644 index 0000000..9c832b9 --- /dev/null +++ b/src/DocumentLibrary/instance/CVS/Entries @@ -0,0 +1 @@ +D/methods//// diff --git a/src/DocumentLibrary/instance/CVS/Repository b/src/DocumentLibrary/instance/CVS/Repository new file mode 100644 index 0000000..97e539f --- /dev/null +++ b/src/DocumentLibrary/instance/CVS/Repository @@ -0,0 +1 @@ +DocumentLibrary/instance diff --git a/src/DocumentLibrary/instance/CVS/Root b/src/DocumentLibrary/instance/CVS/Root new file mode 100644 index 0000000..2084eca --- /dev/null +++ b/src/DocumentLibrary/instance/CVS/Root @@ -0,0 +1 @@ +:pserver:anonymous@cvs.nlada-library.sourceforge.net:/cvsroot/nlada-library diff --git a/src/DocumentLibrary/instance/methods/--README-- b/src/DocumentLibrary/instance/methods/--README-- new file mode 100755 index 0000000..20fc88d --- /dev/null +++ b/src/DocumentLibrary/instance/methods/--README-- @@ -0,0 +1,147 @@ +Document Library Product Setup: + +If you are reading this, you are well on your way to getting +your document library online. There are still a few things +left to do before you start using it: + +Style Sheet: + + A sample style sheet is included with this product. It illustrates + the different CSS classes used throughout and gives them some + basic presentation. To use the included style sheet, include the + following code in the element of your standard_html_header: + + + + If you use the ZStyleSheet product, simply save the style sheet + to a file and import it in. + +Topic Index: + + In the library there is an Index object which holds the topic index + for your library. Inside you can create a hierarchy of topics with as + many levels as desired. This can be done using the Zope managment + interface or by importing a tab-indented outline from a text file. + The format is as follows: + + - The number of leading tabs on each line denotes the topic index level. + a line with no tabs is a top level topic index. + + - Text following the leading tabs (if any) specifies the title of the + topic index. + + - Optionally you may specify an id for each topic index by adding a + delimiter character of your choosing followed by the id string. ids + for topics residing in the same parent topic must be unique. However, + you can cross-reference topic indices under different parents by + giving them the same id string. + + - If an id is not specified for a topic index, an arbitrary one is + assigned to it as part of a unique sequence. + + Below is an example of a topic index file, which uses a semicolon (;) + to delimit the title and id. You can using any character as a delimiter + excluding newline. + + Bread;100 + White;100.1 + Butter Top;100.1.1 + French;100.1.2 + Italian;100.1.3 + Wheat;100.2 + Rye;100.3 + Marble;100.3.1 + Caraway;100.3.2 + Pumpernickel;100.4 + Cold Cuts;101 + Beef;101.1 + Roast Beef;101.1.1 + Corned Beef;101.1.2 + Pastrami;101.1.3 + Ham;101.2 + Baked Ham;101.2.1 + Honey Ham;101.2.2 + Turkey;101.3 + Pastrami;101.1.3 + Condiments;102 + Mayo;102.1 + Mustard;102.2 + Yellow;102.2.1 + Dijon;102.2.2 + Brown;102.2.3 + Ketchup;102.3 + + You get the idea. In the above example, Pastrami is cross-referenced + under Beef and Turkey. A document placed under one is automatically + placed under the other. They are actually the same topic as far as + the library is concerned. + + To import an index from a file, click on the Index object and use + the "Import Topic Index" tab. + + A complete legal services index is available for download as a + functional example. + +Library Properties: + + The library object's property sheet contains three properties that + you should modify to suit your needs: + + Document Types + + A more general categorization of documents is made using the + type property. You can define the set of types for the documents + in your library by adding them to the document_types lines + propery of the library object. The items added here will be + presented as options to the user when using the advanced search + or submitting a document. + + Forbidden Content Types + + You can forbid certain MIME file types from being submitted + by modifying the deny_content_types property of the library. + By default, application/octet-stream is forbidden so that + executables and generic binary files cannot be submitted. + + Hide Empty Topic Indexes + + If you have an expansive index and few documents, it can be + beneficial to hide empty topics when browsing the library. To + enable this feature, set the library property hide_empty_topics + to true. By default, all topics are hidden. When a document + is approved on the review screen, any topics assigned to that + document and their parents become visible. Topic indexes are + only hidden when browsing, when assigning topics to documents + using the topic chooser, all topics are shown. + +Permissions: + + Below are the permissions needed for users to be able to perform + specific tasks in the library: + + Task Object Permissions + --------------------------------------------------------------------- + Browse & Search Document library Access Content Information + View + Query Vocabulary + Search ZCatalog + + Submit Documents Documents Folder Add Document Library Files + + Review Submitted Documents Folder Manage Properties + Documents + + Manage Topic Indices Index Add Library Topic Indexes + Manage Properites + Delete Objects + +For additional information on this product, read the text files in the +DocumentLibrary directory in your Zope Products directory. + +I hope you find the document library product useful. If you have +questions, comments or want to report a bug, email me at: +cduncan@kaivo.com + +-------------------------------------------------------------------------- + +Copyright (c) 2001 Kaivo, Inc. http://www.kaivo.com diff --git a/src/DocumentLibrary/instance/methods/CVS/Entries b/src/DocumentLibrary/instance/methods/CVS/Entries new file mode 100644 index 0000000..5f75027 --- /dev/null +++ b/src/DocumentLibrary/instance/methods/CVS/Entries @@ -0,0 +1,22 @@ +/--README--/1.1.1.1/Fri Jan 18 04:38:46 2002// +/advanced_search_form/1.2/Sun Jun 23 12:09:34 2002// +/document_batch_links/1.2/Sun Jun 23 12:09:34 2002// +/document_browse/1.2/Sun Jun 23 12:09:34 2002// +/document_edit/1.2/Sun Jun 23 12:09:34 2002// +/document_info/1.2/Sun Jun 23 12:09:34 2002// +/document_list/1.2/Sun Jun 23 12:09:34 2002// +/document_list_item/1.2/Sun Jun 23 12:09:34 2002// +/document_review/1.2/Sun Jun 23 12:09:34 2002// +/document_search/1.2/Sun Jun 23 12:09:34 2002// +/document_submit/1.2/Sun Jun 23 12:09:34 2002// +/document_submit_file_error/1.2/Sun Jun 23 12:09:34 2002// +/document_submit_message/1.2/Sun Jun 23 12:09:34 2002// +/document_submit_url_error/1.2/Sun Jun 23 12:09:34 2002// +/index_chooser/1.2/Sun Jun 23 12:09:34 2002// +/index_html/1.2/Sun Jun 23 12:09:34 2002// +/library_style_sheet/1.2/Sun Jun 23 12:09:34 2002// +/nav_links/1.2/Sun Jun 23 12:09:34 2002// +/search_tips/1.2/Sun Jun 23 12:09:34 2002// +/show_hide_topics/1.2/Sun Jun 23 12:09:34 2002// +/simple_search_form/1.2/Sun Jun 23 12:09:34 2002// +D diff --git a/src/DocumentLibrary/instance/methods/CVS/Repository b/src/DocumentLibrary/instance/methods/CVS/Repository new file mode 100644 index 0000000..f0cbe91 --- /dev/null +++ b/src/DocumentLibrary/instance/methods/CVS/Repository @@ -0,0 +1 @@ +DocumentLibrary/instance/methods diff --git a/src/DocumentLibrary/instance/methods/CVS/Root b/src/DocumentLibrary/instance/methods/CVS/Root new file mode 100644 index 0000000..2084eca --- /dev/null +++ b/src/DocumentLibrary/instance/methods/CVS/Root @@ -0,0 +1 @@ +:pserver:anonymous@cvs.nlada-library.sourceforge.net:/cvsroot/nlada-library diff --git a/src/DocumentLibrary/instance/methods/advanced_search_form b/src/DocumentLibrary/instance/methods/advanced_search_form new file mode 100755 index 0000000..caa3bf1 --- /dev/null +++ b/src/DocumentLibrary/instance/methods/advanced_search_form @@ -0,0 +1,97 @@ + + +advanced_search_form method + + User interface for advanced library queries + + Author: Casey Duncan (casey_duncan@yahoo.com) + + Dependencies: + document_search method + nav_links method + + Arguments: + None + + CSS classes used: + document-library + help-text + + + + + + +
+

Advanced Document Search

+

+ Enter search criteria in as many document data fields as desired. Entering more + criteria will result in a more refined search. + Visit the search tips page for help creating your + search criteria. + + For easier searching, use the simple search. + +

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Title" />
Type + +
Subject Area" />
Creator" />
DatedFrom: + " /> +  To: + " />
  + +
+ +
+
 
 
+
+ +
+ diff --git a/src/DocumentLibrary/instance/methods/document_batch_links b/src/DocumentLibrary/instance/methods/document_batch_links new file mode 100755 index 0000000..9fcde26 --- /dev/null +++ b/src/DocumentLibrary/instance/methods/document_batch_links @@ -0,0 +1,32 @@ + + +document_batch_links method + + Displays links to the next and previous batches in the + document list. + + Author: Casey Duncan (casey_duncan@yahoo.com) + + Arguments: + start Batch start + documents Document list sequence + + CSS classes used: + batch-links + + + + diff --git a/src/DocumentLibrary/instance/methods/document_browse b/src/DocumentLibrary/instance/methods/document_browse new file mode 100755 index 0000000..077ebf4 --- /dev/null +++ b/src/DocumentLibrary/instance/methods/document_browse @@ -0,0 +1,85 @@ + + +document_browse method + + Displays a hierarchy of linked topic indexes allowing the user to navigate around the + index and find documents. + + Author: Casey Duncan (casey_duncan@yahoo.com) + + Dependencies: + document_list method + + CSS classes used: + title Topic index title + + + + + + If we are executed outside of the context of the Index, then + call ourself again inside the context of the Index. + + + + + + + + + + + + Create indented backlinks to higher level topics + + +
    +
  • + + + + + + + + + + + List any subtopics in this topic + + + +
      + + + +
    • + + +
    • +
      + + +
    +
    + + +
    + + + List any documents in this topic + + + + + + Close backlink lists + + +
  • +
+
+ +
+
diff --git a/src/DocumentLibrary/instance/methods/document_edit b/src/DocumentLibrary/instance/methods/document_edit new file mode 100755 index 0000000..00a42f7 --- /dev/null +++ b/src/DocumentLibrary/instance/methods/document_edit @@ -0,0 +1,177 @@ + + +document_edit method + + Form for users to edit document properties. Requires + "Manage properties" rights on the Documents folder. + + Author: Casey Duncan (casey_duncan@yahoo.com) + + Arguments: + last_url (optional) URL to return to + last_query (optional) query string from last_url + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+

+ + Edit Document File +

+ +(* Denotes required fields) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Title * + &dtml.missing.-ERROR_title;">
Type * + &dtml.missing.-ERROR_type; + +
Description * + &dtml.missing.-ERROR_description; + +
Subject Topic Areas * + &dtml.missing.-ERROR_topics; + Enter or select document topic codes. To specify multiple topics, + put each code on its own line or use the + topic chooser + to find the topic codes for your document.
+ + + + + +
Creator + &dtml.missing.-ERROR_creator;">
Creation Date + &dtml.missing.-ERROR_date;">
Document + Enter a new URL or specify a new file below. +
URL
File + + +
Current File Name: &dtml-filename;
+
+
  + + + + + +      + + +      + + +
+ +
+ + diff --git a/src/DocumentLibrary/instance/methods/document_info b/src/DocumentLibrary/instance/methods/document_info new file mode 100755 index 0000000..cdfe9c0 --- /dev/null +++ b/src/DocumentLibrary/instance/methods/document_info @@ -0,0 +1,102 @@ + + +document_info method + + Displays document properties to the user. + + Author: Casey Duncan (casey_duncan@yahoo.com) + + Dependencies: + document_list method + + CSS classes used: + document-library Entire document topic hierarchy + title Topic index title + format + size + type + date + author + subject + description + + + + + + &dtml-title; + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + &dtml-title; +
&dtml-description;
+
+ Format + + +
+ Size + + +
+ Type + + +
+ Date + + +
+ Creator + + +
+ Subject Topic Area(s) + + +
+
+
+ +
+
+ + diff --git a/src/DocumentLibrary/instance/methods/document_list b/src/DocumentLibrary/instance/methods/document_list new file mode 100755 index 0000000..54a45b2 --- /dev/null +++ b/src/DocumentLibrary/instance/methods/document_list @@ -0,0 +1,67 @@ + + +document_list method + + Queries the document store and displays a batched list of documents + search arguments are passed on REQUEST + + Author: Casey Duncan (casey_duncan@yahoo.com) + + Arguments: + batch_size List batch size (optional) + empty_list_msg Message for empty result set (optional) + + Dependencies: + document_list_item + document_batch_links + + CSS classes used: + document-library + empty Empty document list message text + + + +Set the review date for only reviewed documents + + + +
+ + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + +
+
diff --git a/src/DocumentLibrary/instance/methods/document_list_item b/src/DocumentLibrary/instance/methods/document_list_item new file mode 100755 index 0000000..861f512 --- /dev/null +++ b/src/DocumentLibrary/instance/methods/document_list_item @@ -0,0 +1,45 @@ + + +document_list_item method + + Display template for listing documents in the + document_list method. + + Author: Casey Duncan (casey_duncan@yahoo.com) + + CSS Classes Used: + title Document title + description Document description + actions Action links + admin Administrative action links + + + + + + Document + + + + + &dtml-description; + +
+ [ + + +  |  + + + + Edit +  |  + + more info ] +
+ + diff --git a/src/DocumentLibrary/instance/methods/document_review b/src/DocumentLibrary/instance/methods/document_review new file mode 100755 index 0000000..7281682 --- /dev/null +++ b/src/DocumentLibrary/instance/methods/document_review @@ -0,0 +1,151 @@ + + +document_review method + + Queries the document store and displays a batched list of documents + with a document review interface + search arguments are passed on REQUEST + + Author: Casey Duncan (casey_duncan@yahoo.com) + + Arguments: + empty_list_msg: Message for empty result set (optional) + + Dependencies: + document_batch_links + + CSS classes used: + document-library + doc-list Document list items + row-heading Table row heading + row-shade Shaded table row + row-normal Normal table row + review-button Document review action button + empty Empty document list message text + + + + + + Set the review date for rejected documents + + + Set the review date for unreviewed documents + + + + + + + +

Rejected Documents

+ +

Documents Awaiting Your Review

+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 Submit DateSizeCreatorCreation DateSubject AreasActions
  + + + + Unknown + + + + + + + + + +   + + + +
+ + + + + + + + + + +
+ Document + + + +
+ No documents were found. +
+ +
+
+ +
+ diff --git a/src/DocumentLibrary/instance/methods/document_search b/src/DocumentLibrary/instance/methods/document_search new file mode 100755 index 0000000..5f40b7b --- /dev/null +++ b/src/DocumentLibrary/instance/methods/document_search @@ -0,0 +1,55 @@ + + +document_search method + + Create search queries from form input and display results to + the user + + Author: Casey Duncan (casey_duncan@yahoo.com) + + Arguments: + All document indexes + dated_from, dated_to + + Dependencies: + document_list + + + +Don't allow empty simple searches + + + + + + + + + + +Setup date ranges if specified + + + + + + + + + + + + + +Setup topic index title search + + + + +

Library Search Results

+ + + +
+ + diff --git a/src/DocumentLibrary/instance/methods/document_submit b/src/DocumentLibrary/instance/methods/document_submit new file mode 100755 index 0000000..390cb83 --- /dev/null +++ b/src/DocumentLibrary/instance/methods/document_submit @@ -0,0 +1,154 @@ + + +document_submit method + + User form to add documents to the library. + + Author: Casey Duncan (casey_duncan@yahoo.com) + + CSS classes used: + document-library Entire document topic hierarchy + required Required field marker + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + To submit a document to the library, fill out this form. + + (* Denotes required fields) + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Title * + &dtml.missing.-ERROR_title;">
Type * + &dtml.missing.-ERROR_type; + +
Description * + &dtml.missing.-ERROR_description; + +
Subject Topic Areas * + &dtml.missing.-ERROR_topics; + Enter or select document topic codes. To specify multiple topics, + put each code on its own line or use the + topic chooser + to find the topic codes for your document.
+ + + > + +
Creator + &dtml.missing.-ERROR_creator;">
Creation Date + &dtml.missing.-ERROR_date;">
Document * + &dtml.missing.-ERROR_file; + If the document is a web page, enter the URL below. + If you want to upload a file from your computer, + click on the Browse... button to select the file. +
URL">
File
  + +      + +
+
+
+ + diff --git a/src/DocumentLibrary/instance/methods/document_submit_file_error b/src/DocumentLibrary/instance/methods/document_submit_file_error new file mode 100644 index 0000000..be4006a --- /dev/null +++ b/src/DocumentLibrary/instance/methods/document_submit_file_error @@ -0,0 +1,35 @@ + + +document_submit_file_error method + + Error message and help for invalid file submissions. + + Author: Casey Duncan (casey_duncan@yahoo.com) + + + +

Document Submission Error

+

+Sorry, the file you specified could not be submitted to the library. +It either could not be opened or it is not a valid file format. To correct +this problem try one of the following: +

+
    +
  • Make sure the file is not open in another program on your computer. + If it is, close the file and try again.
  • +
  • Make sure the file is not an executable program file, these + file types (commonly with the extension ".exe" or ".com") + cannot be submitted to the library.
  • +
  • Make sure the file name has the proper file extension in its file + name, for instance: ".doc" for MS Word documents, ".pdf" for + PDF documents, etc. Note: the extension may not be visible + when you browse for the file. If the file appears with the proper + icon in the browse window, then the extension is correct.
  • +
  • Make sure that you have the proper rights to open the file on + your computer. Try opening it on your computer using the appropriate + program. If you cannot access the file, see your system + administrator for help.
  • +
+

+To return to the form to resubmit your document, click on the +Back button on your browser's toolbar.

diff --git a/src/DocumentLibrary/instance/methods/document_submit_message b/src/DocumentLibrary/instance/methods/document_submit_message new file mode 100644 index 0000000..27c908f --- /dev/null +++ b/src/DocumentLibrary/instance/methods/document_submit_message @@ -0,0 +1,26 @@ + + +document_submit_message method + + Message for users submitting new documents + + Author: Casey Duncan (casey_duncan@yahoo.com) + + + + +

Your document + was submitted sucessfully. + + Your document will be reviewed and will become available in the library + when it is approved. + + + This is commented out until I can track down a somewhat silly + security issue. Uncomment it to see what I mean... + + Edit the document data. + + +

+
diff --git a/src/DocumentLibrary/instance/methods/document_submit_url_error b/src/DocumentLibrary/instance/methods/document_submit_url_error new file mode 100644 index 0000000..ebd73e2 --- /dev/null +++ b/src/DocumentLibrary/instance/methods/document_submit_url_error @@ -0,0 +1,24 @@ + + +document_submit_url_error method + + Error message and help for invalid URL submissions. + + Author: Casey Duncan (casey_duncan@yahoo.com) + + + +

Document Submission Error

+

The URL you specified for the document could not be submitted because: +

+

To correct this problem try the following:

+
    +
  • Confirm that you can access the URL as entered using your web browser. + Click on the URL link above to test it in your browser
  • +
  • Make sure you URL uses the http, ftp or gopher protocols. This is denoted + at the front of the URL (ie http://). Other protocols (such as file: or mailto:) + are not allowed for submitting documents. If the protocol is omitted, + http is assumed.
  • +
+

To return to the form to resubmit your document, click on the +Back button on your browser's toolbar.

diff --git a/src/DocumentLibrary/instance/methods/index_chooser b/src/DocumentLibrary/instance/methods/index_chooser new file mode 100755 index 0000000..3c7a068 --- /dev/null +++ b/src/DocumentLibrary/instance/methods/index_chooser @@ -0,0 +1,193 @@ + + +index_chooser method + + Popup index chooser that allows user to select on (or more) topics. + Uses JavaScript to populate a topic list on the parent form. + + Author: Casey Duncan (casey_duncan@yahoo.com) + + Arguments: + topic_form Name of parent form on parent window + topic_field Name of field on parent form (optional, defaults to "topics") + single Flag to specify whether on a single topic can be selected (option, defaults to false) + + CSS classes used: + advocate-library Entire document topic hierarchy + title Topic index title + code Topic index code + + + + + + If we are executed outside of the context of the Index, then + call ourself again inside the context of the Index. + + + + + + + + <dtml-var name="document_title"> + + + + +
+
+ + + + + + + + + +

Browse the topic index below. The topic code numbers used for indexing +are to the right of the topic names. To search for a topic by name, enter a +word or phrase in the search box below.

+ +

+ + " /> + +

+ + + + + + + + + + + + + +
+ + + + + &dtml-title_or_id; + + + > + + + +  () +   + +
+ +

More topics were found then are displayed here. Try using more specific words to refine your search

+
+ +

No topics were found.

+
+

+ Browse Topic Index +

+ + + + + + + Create indented backlinks to higher level topics + + +
    +
  • + + + + + + + + + + + +  () +   + + + + + List any subtopics in this topic + + + +
      + + +
    • + + +  () +   +
    • + + +
    +
    +
    + + + Close backlink lists + + +
  • +
+
+ +
+
+ + +

Selected index:
+ + +

Currently selected index topics:
+ + +


+ +

+ +
+ + +
diff --git a/src/DocumentLibrary/instance/methods/index_html b/src/DocumentLibrary/instance/methods/index_html new file mode 100755 index 0000000..b552a31 --- /dev/null +++ b/src/DocumentLibrary/instance/methods/index_html @@ -0,0 +1,55 @@ + + +index_html method + + Document library home page. Displays the simple search form, + topic browser and navigation links. + + Author: Casey Duncan (casey_duncan@yahoo.com) + + Dependencies: + simple_search_form method + document_browse method + nav_links method + + CSS classes used: + document-library Entire document topic hierarchy + intro Introduction text + + + + +
+ + Display a intro blurb at the top only when the user first enters the library + + + + + + + +
+ + +

+ Welcome to the document library. You can use this page to + browse and + search + the documents here. + + If you have a document that you would like to add to the library, you can + submit it here. + +

+
+
+
+ + +
+ +
+ +
+ diff --git a/src/DocumentLibrary/instance/methods/library_style_sheet b/src/DocumentLibrary/instance/methods/library_style_sheet new file mode 100755 index 0000000..23cfd69 --- /dev/null +++ b/src/DocumentLibrary/instance/methods/library_style_sheet @@ -0,0 +1,87 @@ + + +library_style_sheet method + + Style sheet for use with the document library. + Insert inline or link to it in standard_html_header + + Author: Casey Duncan (casey_duncan@yahoo.com) + + + + + diff --git a/src/DocumentLibrary/instance/methods/nav_links b/src/DocumentLibrary/instance/methods/nav_links new file mode 100755 index 0000000..39e5d28 --- /dev/null +++ b/src/DocumentLibrary/instance/methods/nav_links @@ -0,0 +1,68 @@ + + +nav_links method + + Presents appropriate links to the user dynamically. + + Author: Casey Duncan (casey_duncan@yahoo.com) + + CSS classes used: + nav-links Navigation Links + + + + diff --git a/src/DocumentLibrary/instance/methods/search_tips b/src/DocumentLibrary/instance/methods/search_tips new file mode 100755 index 0000000..92229ac --- /dev/null +++ b/src/DocumentLibrary/instance/methods/search_tips @@ -0,0 +1,85 @@ + + +search_tips method + + Search help information (static). + + Author: Casey Duncan (casey_duncan@yahoo.com) + + CSS classes used: + document-library Entire document topic hierarchy + intro Introduction text + search-tips + tip-subject + tip-definition + search-word + search-example + + + + +
+

Document Library Search Tips

+

+ When searching for documents, there are several techniques you + can use to improve the accuracy of your search results. These terms allow you + to formulate partial word, boolean and phrase matching search queries. Below are examples of + the query language that is used to perform searches in the document + library. You can also combine the techniques below in a single search to + further refine your query. +

+ +
+
Simple word searches
+
If you enter multiple words for your search query the + results will be + documents containing one or more of the words in your query. Queries are + not case sensitive, so they will match the word(s) only based on spelling. + Documents matching more of the words will appear first in the search results. + Because a document only need to match one of the words you specify, + you may find that your search results contain irrelevant documents. +
Example: housing landlord rent
+ This search query would find all documents containing one or more of the + words specified. +
+
Refining your results to match multiple words
+
Using the and search + term between the + words you are searching for will limit the search results only to documents + containing all of the specified words. +
Example: housing and landlord and rent
+ This search query would find only documents containing all of the + words specified. +
+
Explicitly omitting results
+
You might also want to omit documents containing certain words. + Using the and not search term between the + words you are searching for will omit any documents containing the word + you specify. +
Example: housing and landlord and not discrimination
+ This search query would find documents containing the first two words + and omit any documents also containing the last word. +
+
Searching for phrases
+
If the word combination you are searching for always occurs + in a particular phrase, + you will get better search results using a phrase matching query. To match a particular + phrase, place it in double quotation marks. In order for a document to match, the + words in the phrase must occur in the document in the same order as they appear in the query. +
Example: "fair housing"
+ This search query would find only documents containing the phrase specified. +
+
Matching partial words
+
You can match multiple word forms simultaneously using + partial word matching. If, for + instance you want to guarantee that all documents containing any form of a word + (plural, singular, past tense, etc.) are found, you can use the asteriks (*) and + question mark (?) wildcard characters in your search queries. The question + mark matches any single character and the asterik matches zero or more characters. +
Example: discriminat*
+ This query would find documents containing the words "discriminate", "discriminated", + "discrimination", etc. +
+
+
+ diff --git a/src/DocumentLibrary/instance/methods/show_hide_topics b/src/DocumentLibrary/instance/methods/show_hide_topics new file mode 100755 index 0000000..6b72f70 --- /dev/null +++ b/src/DocumentLibrary/instance/methods/show_hide_topics @@ -0,0 +1,19 @@ + +show_hide_topics method + + Toggles a cookie flag to indicate whether empty topics are shown. + To enable this feature, set the hide_empty_topics property of the + document library object to true. + + Author: Casey Duncan (casey_duncan@yahoo.com) + + + + + + + + + + diff --git a/src/DocumentLibrary/instance/methods/simple_search_form b/src/DocumentLibrary/instance/methods/simple_search_form new file mode 100755 index 0000000..af75e34 --- /dev/null +++ b/src/DocumentLibrary/instance/methods/simple_search_form @@ -0,0 +1,36 @@ + + +simple_search_form method + + HTML interface to search all textual indexes simultaneously + + Author: Casey Duncan (casey_duncan@yahoo.com) + + CSS classes used: + simple-search Simple search form class + search-note Scope note for search form + + + + + +
+ +
diff --git a/src/DocumentLibrary/refresh.txt b/src/DocumentLibrary/refresh.txt new file mode 100644 index 0000000..6e7380b --- /dev/null +++ b/src/DocumentLibrary/refresh.txt @@ -0,0 +1 @@ +Refresh enabled diff --git a/src/DocumentLibrary/text.c.patch b/src/DocumentLibrary/text.c.patch new file mode 100644 index 0000000..dd7d7a5 --- /dev/null +++ b/src/DocumentLibrary/text.c.patch @@ -0,0 +1,19 @@ +*** wv/text.c Sun Jan 21 16:54:18 2001 +--- text.c Thu Mar 15 15:23:03 2001 +*************** +*** 397,403 **** + + if(wv_iconv(iconv_handle, &ibuf, &ibuflen, &obuf, &obuflen) == (size_t)-1) + { +! wvError(("iconv failed errno: %d, to:%s from:%s\n",errno, t_code, f_code)); + /* I'm torn here - do i just announce the failure, continue, or copy over to the other buffer? */ + + /* errno is usually 84 (illegal byte sequence) +--- 397,403 ---- + + if(wv_iconv(iconv_handle, &ibuf, &ibuflen, &obuf, &obuflen) == (size_t)-1) + { +! /*wvError(("iconv failed errno: %d, to:%s from:%s\n",errno, t_code, f_code));*/ + /* I'm torn here - do i just announce the failure, continue, or copy over to the other buffer? */ + + /* errno is usually 84 (illegal byte sequence) diff --git a/src/DocumentLibrary/version.txt b/src/DocumentLibrary/version.txt new file mode 100644 index 0000000..0f82de4 --- /dev/null +++ b/src/DocumentLibrary/version.txt @@ -0,0 +1 @@ +1.0rc1 diff --git a/src/DocumentLibrary/www/CVS/Entries b/src/DocumentLibrary/www/CVS/Entries new file mode 100644 index 0000000..da35356 --- /dev/null +++ b/src/DocumentLibrary/www/CVS/Entries @@ -0,0 +1,7 @@ +/DefaultTopicIndex_icon.gif/1.1.1.1/Fri Jan 18 04:38:41 2002// +/DocumentLibrary_icon.gif/1.1.1.1/Fri Jan 18 04:38:40 2002// +/DocumentStore_icon.gif/1.1.1.1/Fri Jan 18 04:38:41 2002// +/TopicIndexRoot_icon.gif/1.1.1.1/Fri Jan 18 04:38:41 2002// +/Topic_icon.gif/1.1.1.1/Fri Jan 18 04:38:40 2002// +/folder_icon_lg.gif/1.1.1.1/Fri Jan 18 04:38:40 2002// +D diff --git a/src/DocumentLibrary/www/CVS/Repository b/src/DocumentLibrary/www/CVS/Repository new file mode 100644 index 0000000..6840bd2 --- /dev/null +++ b/src/DocumentLibrary/www/CVS/Repository @@ -0,0 +1 @@ +DocumentLibrary/www diff --git a/src/DocumentLibrary/www/CVS/Root b/src/DocumentLibrary/www/CVS/Root new file mode 100644 index 0000000..2084eca --- /dev/null +++ b/src/DocumentLibrary/www/CVS/Root @@ -0,0 +1 @@ +:pserver:anonymous@cvs.nlada-library.sourceforge.net:/cvsroot/nlada-library diff --git a/src/DocumentLibrary/www/DefaultTopicIndex_icon.gif b/src/DocumentLibrary/www/DefaultTopicIndex_icon.gif new file mode 100755 index 0000000..1a4a118 --- /dev/null +++ b/src/DocumentLibrary/www/DefaultTopicIndex_icon.gif Binary files differ diff --git a/src/DocumentLibrary/www/DocumentLibrary_icon.gif b/src/DocumentLibrary/www/DocumentLibrary_icon.gif new file mode 100755 index 0000000..7c15fbb --- /dev/null +++ b/src/DocumentLibrary/www/DocumentLibrary_icon.gif Binary files differ diff --git a/src/DocumentLibrary/www/DocumentStore_icon.gif b/src/DocumentLibrary/www/DocumentStore_icon.gif new file mode 100755 index 0000000..71bedb1 --- /dev/null +++ b/src/DocumentLibrary/www/DocumentStore_icon.gif Binary files differ diff --git a/src/DocumentLibrary/www/TopicIndexRoot_icon.gif b/src/DocumentLibrary/www/TopicIndexRoot_icon.gif new file mode 100755 index 0000000..8d31c10 --- /dev/null +++ b/src/DocumentLibrary/www/TopicIndexRoot_icon.gif Binary files differ diff --git a/src/DocumentLibrary/www/Topic_icon.gif b/src/DocumentLibrary/www/Topic_icon.gif new file mode 100755 index 0000000..415c01a --- /dev/null +++ b/src/DocumentLibrary/www/Topic_icon.gif Binary files differ diff --git a/src/DocumentLibrary/www/folder_icon_lg.gif b/src/DocumentLibrary/www/folder_icon_lg.gif new file mode 100755 index 0000000..eee205a --- /dev/null +++ b/src/DocumentLibrary/www/folder_icon_lg.gif Binary files differ