ExtFile/0040755000570700000240000000000007347362650011733 5ustar greheineionaExtFile/ExtImage.py0100644000570700000240000004446607347252362014021 0ustar greheineiona"""ExtImage product module.""" ############################################################################### # # Copyright (c) 2001 Gregor Heine . All rights reserved. # ExtFile Home: http://www.zope.org/Members/MacGregor/ExtFile/index_html # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # 1. Redistributions of 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. The name of the author may not be used to endorse or promote products # derived from this software without specific prior written permission # # Disclaimer # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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. # # In accordance with the license provided for by the software upon # which some of the source code has been derived or used, the following # acknowledgement is hereby provided : # # "This product includes software developed by Digital Creations # for use in the Z Object Publishing Environment # (http://www.zope.org/)." # ############################################################################### __doc__ = """ExtImage product module. The ExtImage-Product works like the Zope Image-product, but stores the uploaded image externally in a repository-direcory. It creates a preview of the image (requires PIL). $Id: ExtImage.py,v 1.30.2.1 2025/09/10 23:56:16 gregor Exp $""" __version__='$Release: 1.1.3 $'[10:-2] import Globals from __main__ import * import Zope from Products.ExtFile.ExtFile import * from Globals import HTMLFile, MessageDialog from DateTime import DateTime from types import IntType, StringType import urllib, os, string from os.path import join, isfile try: from cStringIO import StringIO except: from StringIO import StringIO NO_PREVIEW = 0 GENERATE = 1 UPLOAD_NORESIZE = 2 UPLOAD_RESIZE = 3 manage_addExtImageForm = HTMLFile('extImageAdd', globals()) def manage_addExtImage(self, id='', title='', descr='', file='', preview='', content_type='', create_prev=0, maxx='', maxy='', ratio=0, permission_check=0, REQUEST=None): """ Add a ExtImage to a folder """ # print string.split(self._d.absolute_url(),'/')[3:] if not id and hasattr(file,'filename'): # generate id from filename and make sure, it has no 'bad' chars id = file.filename title = title or id id = id[max(string.rfind(id,'/'), string.rfind(id,'\\'), string.rfind(id,':') )+1:] id = string.translate(id, TRANSMAP) self = self.this() tempExtImage = ExtImage(id, title, descr, permission_check) self._setObject(id, tempExtImage) self._getOb(id).manage_file_upload(file, content_type, 0, create_prev, maxx, maxy, ratio) if create_prev==UPLOAD_NORESIZE or create_prev==UPLOAD_RESIZE: self._getOb(id).manage_file_upload(preview, content_type, 1, create_prev, maxx, maxy, ratio) if REQUEST is not None: return MessageDialog(title = 'Created', message = 'The ExtImage %s was successfully created!' % id, action = './manage_main',) class ExtImage(ExtFile): # what permissions make sense for us? __ac_permissions__=( ('View management screens', ('manage_tabs', 'manage_main', 'manage_uploadForm')), ('Change permissions', ('manage_access',)), ('Change ExtFile/ExtImage', ('manage_editExtFile', 'manage_del_prev', 'manage_create_prev', 'manage_file_upload', 'manage_http_upload')), ('FTP access', ('manage_FTPstat', 'manage_FTPget', 'manage_FTPlist')), ('Download ExtFile/ExtImage',()), ('View', ('index_html', 'icon_html', 'icon_gif', 'preview_html', 'preview', 'link', 'tag', 'is_broken', 'is_webviewable')), ) # what do people think they're adding? meta_type = 'ExtImage' # default,min,max-sizes for the preview image _image_size={'default':256,'min':10,'max':999} ################################ # Init method # ################################ def __init__(self, id, title='', descr='', permission_check=0): """initialize a new instance of ExtImage""" ExtImage.inheritedAttribute("__init__")(self, id, title, descr, permission_check) # determine the int-values of the size of preview-image self.prev_filename = [] self.prev_content_type = '' self.has_preview = 0 ################################ # Public methods # ################################ def __str__(self): return self.tag() def tag(self, preview=0, icon=0, height=None, width=None, alt=None, scale=0, xscale=0, yscale=0, border='0', REQUEST=None, **args): """ Generate an HTML IMG tag for this image, with customization. Arguments to self.tag() can be any valid attributes of an IMG tag. 'src' will always be an absolute pathname, to prevent redundant downloading of images. Defaults are applied intelligently for 'height', 'width', and 'alt'. If specified, the 'scale', 'xscale', and 'yscale' keyword arguments will be used to automatically adjust the output height and width values of the image tag. Adopted and adapted from OFS/image.py """ if REQUEST is None and hasattr(self,'REQUEST'): REQUEST = self.REQUEST if not self._access_permitted(REQUEST): preview = 1 if preview or not self.is_webviewable(): url = '%s?preview=1' % self.absolute_url() img_width, img_height = self._getImageSize(self.prev_filename) elif icon: url = '%s?icon=1' % self.absolute_url() img_width, img_height = (32, 32) else: url = self.absolute_url() img_width, img_height = self._getImageSize(self.filename) height = height or img_height width = width or img_width # Auto-scaling support xdelta = xscale or scale ydelta = yscale or scale if xdelta and width != None: width = str(int(width) * xdelta) if ydelta and height != None: height = str(int(height) * ydelta) if alt is None: alt = self.title_or_id() strg = '%sself._image_size['max']: maxx = self._image_size['max'] if maxyself._image_size['max']: maxy = self._image_size['max'] return maxx, maxy def _undo (self): """ restore filename after undo or copy-paste """ if self.has_preview: fn = self._get_filename(self.prev_filename) if not isfile(fn) and isfile(fn+'.undo'): os.rename(fn+'.undo', fn) # rename preview return ExtImage.inheritedAttribute("_undo")(self) def _get_ufn(self, filename=None): """ If no unique filename has been generated, generate one otherwise, return the existing one. """ if filename is not None: return ExtImage.inheritedAttribute("_get_ufn") (self, filename) new_fn = '' if self.filename: test_fn = self._get_filename(self.filename) + '.preview' if not isfile(test_fn) and not isfile(test_fn+'.undo'): new_fn = self.filename[:] if not new_fn: new_fn = ExtImage.inheritedAttribute("_get_ufn") (self, self.prev_filename) if new_fn != self.prev_filename: new_fn[-1] = new_fn[-1] + '.preview' return new_fn ################################ # Special management methods # ################################ def manage_afterClone(self, item): """ When a copy of the object is created (zope copy-paste-operation), this function is called by CopySupport.py. A copy of the external file is created and self.filename is changed. """ try: self.absolute_url(1) # this raises an exception, if no context except: pass else: new_fn = self._get_new_ufn() new_prev_fn = new_fn[:] new_prev_fn[-1] = new_prev_fn[-1] + '.preview' # rename preview-file if self.has_preview and self.filename!=self.prev_filename: old_prev_fn = self._get_filename(self.prev_filename) if isfile(old_prev_fn): self._copy(old_prev_fn, self._get_filename(new_prev_fn)) self.prev_filename = new_prev_fn else: self.prev_filename = [] self.has_preview = 0 elif self.has_preview: old_fn = self._get_filename(self.filename) if isfile(old_fn) or isfile(old_fn+'.undo'): self.prev_filename = new_fn else: self.prev_filename = [] self.has_preview = 0 else: self.prev_filename = [] return ExtImage.inheritedAttribute("manage_afterClone") \ (self, item, new_fn) return ExtImage.inheritedAttribute("manage_afterClone") \ (self, item) def manage_afterAdd(self, item, container): """ When a copy of the object is created (zope copy-paste-operation), this function is called by CopySupport.py. A copy of the external file is created and self.filename is changed. """ return ExtImage.inheritedAttribute("manage_afterAdd") \ (self, item, container) def manage_beforeDelete(self, item, container): """ This method is called, when the object is deleted. To support undo-functionality and because this happens too, when the object is moved (cut-paste) or renamed, the external file is not deleted. It are just renamed to filename.undo and remains in the repository, until it is deleted manually. """ if self.has_preview and self.filename!=self.prev_filename: prev_fn = self._get_filename(self.prev_filename) try: # if UNDO_POLICY==NO_BACKUPS: os.remove(prev_fn) # else: os.rename(prev_fn,prev_fn+'.undo') os.rename(prev_fn,prev_fn+'.undo') except OSError: pass return ExtImage.inheritedAttribute("manage_beforeDelete") \ (self, item, container) Globals.default__class_init__(ExtImage) ExtFile/extImageEdit.dtml0100644000570700000240000000631207347251324015160 0ustar greheineiona=0"> Edit ExtImage <dtml-var title_or_id>

Edit ExtImage

Filename: (Download/View)
File Size: ( Bytes)  
Content Type:
Image Pixel: x  
Icon: (View)
Preview Size: ( Bytes) (View)  
Preview Pixel: x x   Keep aspect ratio
   
 
Title:
Description:
 
 
 
ExtFile/LICENSE.txt0100644000570700000240000000344107316205262013544 0ustar greheineionaCopyright (c) 2001 Gregor Heine . All rights reserved. ExtFile Home: http://www.zope.org/Members/MacGregor/ExtFile/index_html Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of 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. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission Disclaimer THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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. In accordance with the license provided for by the software upon which some of the source code has been derived or used, the following acknowledgement is hereby provided : "This product includes software developed by Digital Creations for use in the Z Object Publishing Environment (http://www.zope.org/)." ExtFile/README.txt0100644000570700000240000003103007316205230013405 0ustar greheineionaExtFile/ExtImage Product, Version 1.1.x Copyright (c) 2001 Gregor Heine (mac.gregor@gmx.de). All rights reserved. ExtFile Home: http://www.zope.org/Members/MacGregor/ExtFile/index_html ============================= Contents ============================= 1. Product Description 2. Requirements 3. Installation Instructions 4. Usage 4.1. Creation of an ExtFile/ExtImage object 4.2. Public methods ('View' permission) 4.3. Management methods 4.4. The 'Download ExtFile/ExtImage' permission 5. Additional settings 5.1. Directory structure 5.2. Filename generation 5.3. Undo handling 6. Method appendix 6.1. ExtFile/ExtImage public methods 6.2. Additional public methods for ExtImage 7. Supported image formats ============================= 1. Product Description ============================= The ExtFile Product stores large files in an external file-repository and is able to display icons for different MIME-Types. The ExtImage Product additionally creates preview-thumbnails from various images and displays them. ExtFile and ExtImage basically work like the Zope File and Image products. The difference is, that the File/Image Products stores the (binary) file inside the ZODB, wheras ExtFile/ExtImage stores it externally in a repository directory (default: /var/reposit). Only meta data (like title and description) are stored in the Database. This prevents the Database swelling up quickly, when many large files are uploaded and thus increasing database performance. And what about LocalFS? Doesn't it do the same thing? The difference between ExtFile/ExtImage and LocalFS is that you create one LocalFS object to access many files, whereas you create one ExtFile object per file. LocalFS works like a window through which you get access to files outside the Database. ExtFile/ExtImage on the other hand has a one-to-one relation between files and objects - you create one ExtFile/ExtImage object for each file. ExtFile/ExtImage has full Zope Cut/Copy/Paste and Undo Support. ============================= 2. Requirements ============================= Any Zope 2.x will do. (Tested with 2.1.6, 2.2.2 and 2.3.0) The ExtImage Product requires PIL (Python Imaging Library, http://www.pythonware.com/products/pil/index.htm) to create preview-images. ============================= 3. Installation Instructions ============================= Unzip the tarball in your Products directory and restart zope. If you like to configure the handling and structuring of the external files in the repository see 5. Additional settings ============================= 4. Usage ============================= 4.1. Creation of an ExtFile/ExtImage object Create an instance of ExtFile or ExtImage by adding an ExtFile-item (resp. ExtImage) in the Zope management screen. Choose a file (required) and enter an id, a title and a description (optional). If no id is entered, the filename is used as id. For ExtImage you can choose, if you do not want any preview, if ExtImage shold generate a preview image from the given file or if you want to specify a second file, which is used as the preview image. In that case you can either use this image as it is or you can let ExtImage resize it to a different size. If you want the preview image to be generated, you must enter its (x,y)size and choose, if you want to keep the aspect ratio of the image for the preview-image or if you want it scaled to the exact size entered. To set an extra permission check for the download of the file, enable "Use 'Download ExtFile/ExtImage' permission" (see 4.4.) 4.2. Public methods ('View' permission) The raw file is called through the 'index_html' method, the icon by the 'icon_gif' method and the preview image by the 'preview' method (only available in ExtImage). To view the meta data of a file, call the 'view' method. For a complete list of all public methods and attributes, see 4.6. and 4.7 4.3. Management methods To be able to fully access and use the management methods the permissions 'View management screens', 'Change ExtFile/ExtImage' and 'FTP access' must be enabled. In the upload-tab of the management screen you can upload a new version of the file (or the preview in ExtImages) via local upload or http upload. 4.4. The 'Download ExtFile/ExtImage' permission Usually, you want everybody with the 'View' permission to be able to download the file (or view the image) stored in an ExtFile/ExtImage object. In some cases (e.g. commercial websites) the administrator would want to let people with 'View' permission only to see the preview image, or the meta-information of the file, or an icon for it and allow only users with a special permission to view/download the file itself. In these cases, enable the "Use 'Download ExtFile/ExtImage' permission" while creating a new ExtFile/ExtImage object and set that permission for the role, you want to give access to the file. You enable/disable this permission check later by setting 'use_download_permission_check' in the properties tab to either 1 (enabled) or 0 (disbaled). ============================= 5. Additional settings ============================= 5.1. Directory structure At the moment there are three ways to structure the files in the repository. You can switch between those modes by setting the constant 'REPOSITORY' (in line 69 of ExtFile.py) to the respective value. - FLAT (default): The 'classic mode'. All files are stored in one single directory which is by default var/reposit/ - SYNC_ZODB: Starting from the base repository dir (default is var/reposit) the file is stored in a directory that corresponds to the url of its object. E.g. if you add an ExtFile object in the folder spamFolder/eggsFolder, the file goes to var/reposit/spamFolder/eggsFolder. Please note, that this does not mean, that the directory structure of the repository is kept in sync with the ZODB afterwards. Renaming a parent folder or moving the ExtFile object to a different folder has no effect on the files in the repository. They stay where they are, troughout the lifetime of the object. - SLICED: The first two characters of the filename (resp. the id) determine in which sub-directory (starting from the base repository directory) the file is stored. E.g. the file spam.txt is stored in var/reposit/s/p/, eggs.txt is stored as var/reposit/e/g/. This gives you up to approx. 70 subdirecories in the base repository, each containing up to approx. 70 subdirecories in wich the files are stored. 5.2. Filename generation You can customize the filename of the files in the repository. A unique filename is generated, each time an ExtFile/ExtImage object is added. The format of this filename is specified by a format string stored in the constant FILE_FORMAT in line 73 of ExtFile.py. It may contain the following elements: %u - The name of the user, that adds the object. %p - The relative-url-path of the object, separated by underscores. E.g. if you add an ExtFile object in the folder spamFolder/eggsFolder, %p is expanded to 'spamFolder_eggsFolder'. %n - The name of the added file (or the id of the object), without the extension. E.g. if the id is spam.txt, %n is expanded to 'spam'. %e - The extension of the added file including the dot, e.g. '.txt'. %c - A counter starting at 0 %t - The current date and time as a 10 digit number (mmddHHMMSS) The format string may contain any of the first four elements and must contain one of the two last elements (counter or time). The last two elements are used to generate a unique filename, simply by increasing the value as long as a file with that name already exists. Examples: %n%c%e -> spam.txt, eggs1.txt, multiple.dots23.txt, ... (default) %t%n%e -> 0101000001spam.txt, 1231235959eggs.txt, ... (classic) %n(%u)%c%e -> spam(gregor).txt, eggs(a_user)100.txt, ... %p_%n%c%e -> folder1_folder2_spam.txt, folderA_folderB_eggs1.txt, ... 5.3. Undo handling To be able to support Zope's undo functionality, the external file is not deleted, when the object is deleted in the Zope management interface, but it is renamed to 'filename.undo'. You can set a 'level of undo' which determines in wich cases the external file is overwritten/deleted or renamed to '.undo'. The constant UNDO_POLICY in line 75 of ExtFile.py can be set to one of the following values: - NO_BACKUPS: ExtFile/ExtImage never makes '.undo' files. If you delete an object, the file in the repository is deleted aswell, if you upload a new version of the file, the old version is overwritten. In this case an undo of a deleted object causes a broken ExtFile object because the external file doen't exist any more. (This option has been removed, because it produced broken objects after a cut/paste or rename operation.) - BACKUP_ON_DELETE (default): When an object is deleted, the external file is renamed to '.undo'. This guarantees, that objects always have a valid reference to a file in the repository even if they are deleted this transaction is undo'ed afterwards. But if you update the file with a new version, the old version is overwritten and even if the update operation is undo'ed, the object still references the new version of the file. - ALWAYS_BACKUP: On every transaction that alters the external file, a backup of the original file is left in the repository. So if you upload a new version and then undo this transaction the object references the old version of the file again. On the other hand, this can lead to unreferenced non-undo files in the repository. You should occasionally (e.g. when packing the ZODB) delete the '.undo' files in the repository directory (default: var/reposit). Thou it is possible to upload a new version of the file, this is only recommended, when you want to update the file with a new version. Don't use the upload function to replace the file with a completely different one, because the id of the objects remains the same. ============================= 6. Method appendix ============================= 6.1. ExtFile/ExtImage public methods - index_html(): Returns the (binary) file - icon_gif(), index_html(icon=1): Returns the icon for the file's MIME-Type - link(text=self.title_or_id, **args): Return a HTML link tag to the file: text, **args are all other parameters for the tag. - icon_html(): HTML '' tag wrapper around icon_gif with a link to index_html: - is_broken(): Returns true if external file is 'broken', otherwise 1 (this can happen, when a file in the repository direcory was accidentially deleted or a database undo was performed, after the .undo file was deleted) - getContentType: Returns the MIME-Type of the file. - rawsize(), get_size(), getSize(): returns the size of the file in bytes. - size(): returns a 'stringified' filesize (e.g. '1.23 MB') 6.2. Additional public methods for ExtImage - preview(), index_html(preview=1): Returns the binary preview image. - tag(preview=0, height=None, width=None, alt=None, scale=0, xscale=0, yscale=0, border="0", **args): Generate an HTML IMG tag for this image, with customization. Arguments to tag() can be any valid attributes of an IMG tag.'src' is always the absolute pathname of the object. 'preview' is either 0 for the image or 1 for the preview. Defaults are applied intelligently for 'height', 'width', and 'alt'. If specified, the 'scale', 'xscale', and 'yscale' keyword arguments will be used to automatically adjust the output height and width values of the image tag. **args are all other parameters for the tag. - preview_html(): Same as tag(preview=1) - width(), height(), prev_width(), prev_height(): The dimensions (in pixel) of the image or the preview. - prev_rawsize(), prev_size(): Same methods for the preview as for the image. - is_webviewable(): Returns 1 if the image is viewable in a webbrowser (gif, jpeg or png), otherwise 0 ============================= 7. Supported image formats ============================= The ExtImage product can create a preview from the image you upload. To be able to do this, it requires Python Imaging Library (PIL) installed and working. Depending on your PIL configuration most common image file formats are supported. In short: every image file format that can be read by your PIL installation can be used by ExtImage to create preview images. These are (to name the most common): BMP, GIF, JPEG, TIFF, PNG, PCX, PNG, PSD The preview image is always created in the JPEG format. ExtFile/version.txt0100644000570700000240000000002607347251346014153 0ustar greheineionaExtFile/ExtImage-1-1-3ExtFile/CHANGES.txt0100644000570700000240000001332107347362650013541 0ustar greheineiona================================ ExtFile/ExtImage Product Changes ================================ Version 1.1.3 (Stable) - Date: 2025/09/11 - Made ExtFile/ExtImage Zope 2.4.x compliant (note that it still does NOT work with Zope 2.4.0 and 2.4.1beta because of a bug in CopySupport.py) - Fixed bug in preview_html that caused a false redirect (thanks Geir Hellum) Version 1.1.2 (Stable) - Date: 2025/06/25 - Fixed bug in extImageEdit.dtml that caused a Zope Error when editing ExtFile. - Adjusted manage_add methods to match behavior of Zope File/Image objects - __str__ re-added, was removed by accident - Fixed bug that caused a wrong file name in the repository when the id didn't contain a dot. - Some minor bug fixes and internal changes Version 1.1.1 (Stable) - Date: 2025/06/13 Fixed a bug in _access_permitted. This caused erratic diplays of the icon instead of the file/image when index_html was called. Thanks to Tim again. Version 1.1.0 (Stable) - Date: 2025/06/05 Whooah, there it finally is. So, what has changed since the beta releases: - Directory structure of the repository can be customized - Filename in the repository can be customized (see 5.1 and 5.2 in README.txt) - Problem, causing unreferenced files in the repository during a unfinished copy/paste operation fixed. - 'NO_BACKUPS' undo policy removed, because it didn't support cut/paste. - View method was removed (didn't make much sense anyway) - The filename of the repository-file is stored in a list - Due to that, the new UpgradePatch must be applied to all earlier versions. - Stupid 'default_catalog' removed, thanks to Tim McLaughlin - Some smaller bugfixes, corrections, ... not worth mentioning - Finally: general code revision (so, don't try to diff for the changes...) A great _thanks_ goes out to Sean Treadway, for many many good ideas and inspirations! ***Important Note: To upgrade ExtFile/ExtImage objects from any version older than 1.1.0 (including the 1.1.0 beta releases!) to 1.1.0 you must apply the latest version of the UpgradePatch-to-1.1. Version 1.1.0.beta3 (Development) - Date: 2025/02/26 Version 1.1.0.beta2 (Development) - Date: 2025/02/21 Version 1.1.0.beta1 (Development) - Date: 2025/02/20 Many, many internal changes and some new features which will (hopefully) make ExtFile/ExtImage even more reliable. - You can set a 'level of undo' which determines in wich cases the external file is overwritten/deleted or renamed to '.undo'. (see README.txt) - 'id collision bug' fixed, which caused unreferenced files in the repository, when an id of a new object already existed (thanks to Fred Yanowski). Instead of in the __init__ method, the file data is now passed to the object after it has been successfully added, via manage_file_upload. - 'wrong file size bug' fixed, which caused a wrong file size and resolution of the image and preview, after a new file was uploaded followed by an undo of this transaction. The filesize and resolution attributes have been replaced by corresponding methods which determine the respective value directtly from the file(e.g. width(), height(), prev_size(), ...) - Objects can be assigned an alternative id (instead of the filename) by the optional 'id' field in the manage_add method of ExtFile and ExtImage. - Uploaded preview images can be resized during upload (like the generated previews). - Images can be dynamically resized (thanks to Arno Gross): the index_html method now has two additional parameters ('width' and 'height') which cause the image to be resized on the fly. - Documentation updated. Version 1.0.3 (Stable) - Date: 2025/02/26 Catch-up release, which inculdes some new features from Release 1.1.0 (dynamic resizing of images) and which fixes some bugs that were discovered during the development of Release 1.1.0 This is most probably the last 1.0.x release. Version 1.0.2 (Stable) - Date: 2025/02/07 Preview images can optionally be uploaded instead of generated. 'Upload' tab: Allow upload of preview images 'Meta Data' tab removed (now in 'Edit' tab) Preview images in the repository now have the extension '.preview' (was '.jpg') Zope 2.3. management screen L&F compliance HTTP upload bug fixed Some other minor bugfixes Version 1.0.1 (Stable) - Date: 2025/01/11 Fixed file permissions problem in .tgz file. All files now have -rw-r--r--, all directories drwxr-xr-x. Changed false name of www/extfile.gif Version 1.0.0 (Stable) - Date: 2025/12/18 - License changed from GPL to ZPL - FTP Support (Upload/Download) - You can now disable preview creation when adding an ExtImage. - Preview image can now be deleted and (re-)created. - Added optional permission 'Download ExtFile/ExtImage' to restrict access to the image/file. - Added public methods in ExtFile: link, is_broken, __str__, get_size, getContentType - Added public methods in ExtImage: tag, is_webviewable, manage_del_prev, manage_create_prev - Many internal rearrangements :-) Version 0.9.2 (Development) - Date: 2025/09/06 Some minor (potential) bugs in manage_afterClone and manage_add methods fixed. Changed the way unique filenames are generated. Added _min_size and _max_size class variables to ExtImage to limit the minimal and maximal preview size. Modified the manage_http_upload method to determine the size of the new file. Version 0.9.1 (Development) - Date: 2025/08/24 Important security fix in the index_html method, which prevents that any file in the local filesystem (readeable for the UID of the zope process) could be read through the web. Access permissions in __ac_permissions__ fixed. Added If-modified-since header handling in index_html. Version 0.9 (Development) - Date: 2025/08/10 The first public release of the product. Thou it is in use in my company for quite a while, I still regard it as in development. ExtFile/ExtFile.py0100644000570700000240000005465507347252372013660 0ustar greheineiona""" ExtFile product module """ ############################################################################### # # Copyright (c) 2001 Gregor Heine . All rights reserved. # ExtFile Home: http://www.zope.org/Members/MacGregor/ExtFile/index_html # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # 1. Redistributions of 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. The name of the author may not be used to endorse or promote products # derived from this software without specific prior written permission # # Disclaimer # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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. # # In accordance with the license provided for by the software upon # which some of the source code has been derived or used, the following # acknowledgement is hereby provided : # # "This product includes software developed by Digital Creations # for use in the Z Object Publishing Environment # (http://www.zope.org/)." # ############################################################################### __doc__ = """ ExtFile product module. The ExtFile-Product works like the Zope File-product, but stores the uploaded file externally in a repository-direcory. $Id: ExtFile.py,v 1.37.2.1 2025/09/10 23:56:16 gregor Exp $ """ __version__='$Release: 1.1.3 $'[10:-2] import Globals from __main__ import * import Zope from Products.ZCatalog.CatalogAwareness import CatalogAware from OFS.SimpleItem import SimpleItem from OFS.PropertyManager import PropertyManager from Globals import HTMLFile, MessageDialog from OFS.content_types import guess_content_type from webdav.common import rfc1123_date from DateTime import DateTime import urllib, os, types, string from os.path import join, isfile try: from cStringIO import StringIO except: from StringIO import StringIO FLAT = 0 SYNC_ZODB = 1 SLICED = 2 REPOSITORY = FLAT # format for the files in the repository: # %u=user, %p=path, %n=file name, %e=file extension, %c=counter, %t=time FILE_FORMAT = "%n%c%e" BACKUP_ON_DELETE = 0 ALWAYS_BACKUP = 1 UNDO_POLICY = BACKUP_ON_DELETE bad_chars = ' ,;()[]{}ݟ' good_chars = '_________AAAAAAaaaaaaCcEEEEEeeeeeIIIIiiiiNnOOOOOOooooooSssUUUUuuuuYYyyZz' TRANSMAP = string.maketrans(bad_chars, good_chars) manage_addExtFileForm = HTMLFile('extFileAdd', globals()) def manage_addExtFile(self, id='', title='', descr='', file='', content_type='', permission_check=0, REQUEST=None): """ Add a ExtFile to a folder. """ # print self._d.getPhysicalPath() if not id and hasattr(file,'filename'): # generate id from filename and make sure, it has no 'bad' chars id = file.filename title = title or id id = id[max(string.rfind(id,'/'), string.rfind(id,'\\'), string.rfind(id,':') )+1:] id = string.translate(id, TRANSMAP) self = self.this() tempExtFile = ExtFile(id, title, descr, permission_check) self._setObject(id, tempExtFile) self._getOb(id).manage_file_upload(file, content_type) if REQUEST is not None: return MessageDialog(title = 'Created', message = 'The ExtFile %s was successfully created!' % id, action = './manage_main',) class ExtFile(CatalogAware, SimpleItem, PropertyManager): # what properties have we? _properties=( {'id':'title', 'type':'string'}, {'id':'descr', 'type':'string'}, {'id':'content_type', 'type':'string'}, {'id':'use_download_permission_check', 'type':'int'}, ) # what management options are there? manage_options = ( {'label':'Edit', 'action': 'manage_main' }, {'label':'View/Download', 'action': '' }, {'label':'Upload', 'action': 'manage_uploadForm' }, {'label':'Properties', 'action': 'manage_propertiesForm' }, {'label':'Security', 'action': 'manage_access' }, ) # what permissions make sense for us? __ac_permissions__=( ('View management screens', ('manage_tabs', 'manage_main', 'manage_uploadForm')), ('Change permissions', ('manage_access',)), ('Change ExtFile/ExtImage', ('manage_editExtFile', 'manage_file_upload', 'manage_http_upload', 'PUT')), ('FTP access', ('manage_FTPstat', 'manage_FTPget', 'manage_FTPlist')), ('Download ExtFile/ExtImage',()), ('View', ('index_html', 'icon_html', 'icon_gif', 'link', 'is_broken', 'get_size', 'getContentType', '__str__' )), ) # what do people think they're adding? meta_type = 'ExtFile' # location of the file-repository _repository = ['var','reposit'] use_download_permission_check = 0 # MIME-Type Dictionary. To add a MIME-Type, add a file in the directory # icons/_category_/_subcategory-icon-file_ # example: Icon tifficon.gif for the MIME-Type image/tiff goes to # icons/image/tifficon.gif and the dictionary must be updated like this: # 'image':{'tiff':'tifficon.gif','default':'default.gif'}, ... _types={'image': {'default':'default.gif'}, 'text': {'html':'html.gif', 'xml':'xml.gif', 'default':'default.gif', 'python':'py.gif'}, 'application': {'pdf':'pdf.gif', 'zip':'zip.gif', 'tar':'zip.gif', 'msword':'doc.gif', 'excel':'xls.gif', 'powerpoint':'ppt.gif', 'default':'default.gif'}, 'video': {'default':'default.gif'}, 'audio': {'default':'default.gif'}, 'default':'default.gif' } ################################ # Init method # ################################ def __init__(self, id, title='', descr='', permission_check=0): """ initialize a new instance of ExtFile """ self.id = id self.title = title self.descr = descr self.use_download_permission_check = permission_check self.__version__ = __version__ self.filename = [] self.content_type = '' ################################ # Public methods # ################################ def __str__(self): return self.index_html() def __len__(self): return 1 def index_html (self, icon=0, preview=0, width=None, height=None, REQUEST=None): """ return the file with it's corresponding MIME-type """ # HTTP If-Modified-Since header handling. (Copied from OFS/Image.py) if REQUEST is None and hasattr(self,'REQUEST'): REQUEST = self.REQUEST if REQUEST is not None: header = REQUEST.get_header('If-Modified-Since', None) if header is not None: header = string.split(header, ';')[0] try: mod_since = long(DateTime(header).timeTime()) except: mod_since = None if mod_since is not None: if self._p_mtime: last_mod = long(self._p_mtime) else: last_mod = long(0) if last_mod > 0 and last_mod < mod_since: RESPONSE.setStatus(304) return '' if hasattr(self,'has_preview') and self.has_preview: has_preview = 1 else: has_preview = 0 if not self._access_permitted(REQUEST): preview = 1 if (preview and not has_preview): icon = 1 if icon: filename = join(SOFTWARE_HOME, 'Products', 'ExtFile', self.getIconPath()) content_type = 'image/gif' elif preview: filename = self._get_filename(self.prev_filename) content_type = self.prev_content_type else: filename = self._get_filename(self.filename) content_type = self.content_type cant_read_exc = "Can't read: " if not isfile(filename): self._undo() if isfile(filename): size = os.stat(filename)[6] # file size else: filename = join(SOFTWARE_HOME, 'Products', 'ExtFile', 'icons', 'broken.gif') try: size = os.stat(filename)[6] except: raise cant_read_exc, ("%s (%s)" %(self.id, filename)) content_type = 'image/gif' icon = 1 data = StringIO() if icon==0 and width is not None and height is not None: try: from PIL import Image im = Image.open(filename) if im.mode!='RGB' and im.mode!='CMYK': im = im.convert("RGB") im.draft(None,(int(width),int(height))) im.load() im = im.resize((int(width),int(height)), Image.BICUBIC) im.save(data, 'JPEG') except: self._copy(filename, data) else: data.seek(0,2) size = data.tell() content_type = 'image/jpeg' else: self._copy(filename, data) if REQUEST is not None: last_mod = rfc1123_date(self._p_mtime) REQUEST.RESPONSE.setHeader('Last-Modified', last_mod) REQUEST.RESPONSE.setHeader('Content-Type', content_type) REQUEST.RESPONSE.setHeader('Content-Length', size) return data.getvalue() def view_image_or_file(self): """ The default view of the contents of the File or Image. """ raise 'Redirect', self.absolute_url() def link(self, text='', **args): """ return a HTML link tag to the file """ if text=='': text = self.title_or_id() strg = '=0: file = self._types[cat][item] break return join('icons',cat,file) else: return join('icons',self._types['default']) ################################ # Protected management methods # ################################ # Management Interface manage_main = HTMLFile('extFileEdit', globals()) def manage_editExtFile(self, title='', descr='', REQUEST=None): """ Manage the edited values """ if self.title!=title: self.title = title if self.descr!=descr: self.descr = descr # update ZCatalog self.reindex_object() if REQUEST is not None: return MessageDialog( title = 'Edited', message = "The properties of %s have been changed!" % self.id, action = './manage_main', ) # File upload Interface manage_uploadForm = HTMLFile('extFileUpload', globals()) def manage_file_upload(self, file='', content_type='', REQUEST=None): """ Upload file from local directory """ new_fn = self._get_ufn(self.filename) self._copy(file, self._get_filename(new_fn)) self.content_type = self._get_content_type(file, file.read(100), self.id, content_type or self.content_type) self.filename = new_fn if REQUEST is not None: return MessageDialog(title = 'Uploaded', message = "The file was uploaded successfully!", action = './manage_main',) def manage_http_upload(self, url, REQUEST=None): """ Upload file from http-server """ url = urllib.quote(url,'/:') new_fn = self._get_ufn(self.filename) cant_read_exc = "Can't open: " try: fp_in = urllib.urlopen(url) except: raise cant_read_exc, url self._copy(fp_in, self._get_filename(new_fn)) try: instream = open(self._get_filename(new_fn), 'rb') self.content_type = self._get_content_type(instream, instream.read(100), self.id, self.content_type) instream.close() except: if UNDO_POLICY==ALWAYS_BACKUP: os.remove(self._get_filename(new_fn)) else: self.filename = new_fn if REQUEST is not None: return MessageDialog(title = 'Uploaded', message = "The file was uploaded successfully!", action = './manage_main',) manage_FTPget = index_html def PUT(self, REQUEST, RESPONSE): """ Handle HTTP PUT requests """ self.dav__init(REQUEST, RESPONSE) content_type = REQUEST.get_header('content-type', None) instream = REQUEST['BODYFILE'] new_fn = self._get_ufn(self.filename) self._copy(instream, self._get_filename(new_fn)) try: self.content_type = self._get_content_type(instream, instream.read(100), self.id, content_type or self.content_type) except: if UNDO_POLICY==ALWAYS_BACKUP: os.remove(self._get_filename(new_fn)) else: self.filename = new_fn RESPONSE.setStatus(204) return RESPONSE ################################ # Private methods # ################################ def _access_permitted(self, REQUEST): """ check if the user is allowed to download the file """ if hasattr(self,'use_download_permission_check') and \ self.use_download_permission_check and \ (REQUEST is None or not REQUEST.has_key('AUTHENTICATED_USER') or not REQUEST['AUTHENTICATED_USER'].has_permission( 'Download ExtFile/ExtImage', self) ): return 0 else: return 1 def _get_content_type(self, file, body, id, content_type=None): """ determine the mime-type """ headers = getattr(file, 'headers', None) if headers and headers.has_key('content-type'): content_type = headers['content-type'] else: if type(body) is not type(''): body = body.data content_type, enc = guess_content_type(getattr(file,'filename',id), body, content_type) return content_type def _getMIMECatAndSub(self, mime_string): """ Split MIME String into Category and Subcategory """ cat = mime_string[:string.find(mime_string, '/')] # MIME-category sub = mime_string[string.find(mime_string, '/')+1:] # sub-category return cat, sub def _copy(self, infile, outfile): """ read binary data from infile and write it to outfile infile and outfile my be strings, in which case a file with that name is opened, or filehandles, in which case they are accessed directly. """ if type(infile) is types.StringType: try: instream = open(infile, 'rb') except IOError: self._undo() try: instream = open(infile, 'rb') except IOError: raise IOError, ("%s (%s)" %(self.id, infile)) close_in = 1 else: instream = infile close_in = 0 if type(outfile) is types.StringType: try: outstream = open(outfile, 'wb') except IOError: raise IOError, ("%s (%s)" %(self.id, outfile)) close_out = 1 else: outstream = outfile close_out = 0 try: blocksize = 2<<16 block = instream.read(blocksize) outstream.write(block) while len(block)==blocksize: block = instream.read(blocksize) outstream.write(block) except IOError: raise IOError, ("%s (%s)" %(self.id, filename)) try: instream.seek(0) except: pass if close_in: instream.close() if close_out: outstream.close() def _bytetostring (self, value): """ Convert an int-value (file-size in bytes) to an String with the file-size in Byte, KB or MB """ bytes = float(value) if bytes>=1000: bytes = bytes/1024 if bytes>=1000: bytes = bytes/1024 typ = ' MB' else: typ = ' KB' else: typ = ' Bytes' strg = '%4.2f'%bytes strg = strg[:4] if strg[3]=='.': strg = strg[:3] strg = strg+typ return strg def _undo (self): """ restore filename after undo or copy-paste """ fn = self._get_filename(self.filename) if not isfile(fn) and isfile(fn+'.undo'): os.rename(fn+'.undo', fn) def _get_filename(self, filename=''): """ Generate the full filename, incuding directories from self._repository and self.filename """ path = INSTANCE_HOME for item in self._repository: path = join(path,item) if type(filename)==types.ListType: for item in filename: path = join(path,item) elif filename!='': path = join(path,filename) return path def _get_ufn(self, filename): """ If no unique filename has been generated, generate one otherwise, return the existing one. """ if UNDO_POLICY==ALWAYS_BACKUP or filename==[]: new_fn = self._get_new_ufn() else: new_fn = filename[:] if filename: old_fn = self._get_filename(filename) if UNDO_POLICY==ALWAYS_BACKUP: try: os.rename(old_fn, old_fn+'.undo') except: pass else: try: os.rename(old_fn+'.undo', old_fn) except: pass return new_fn def _get_new_ufn(self): """ Create a new unique filename """ rel_url_list = string.split(self.absolute_url(1), '/')[:-1] rel_url_list = filter(None, rel_url_list) pos = string.rfind(self.id, '.') if (pos+1): id_name = self.id[:pos] id_ext = self.id[pos:] else: id_name = self.id id_ext = '' # generate directory structure dirs = [] if REPOSITORY==SYNC_ZODB: dirs = rel_url_list elif REPOSITORY==SLICED: slice_depth = 2 # modify here, if you want a different slice depth slice_width = 1 # modify here, if you want a different slice width temp = id_name for i in range(slice_depth): if len(temp)=0: fileformat = string.replace(fileformat, "%t", "%c") counter = int(DateTime().strftime('%m%d%H%M%S')) else: counter = 0 invalid_format_exc = "Invalid file format: " if string.find(fileformat, "%c")==-1: raise invalid_format_exc, FILE_FORMAT # user (%u) if string.find(fileformat, "%u")>=0: if (hasattr(self, "REQUEST") and self.REQUEST.has_key('AUTHENTICATED_USER')): user = self.REQUEST['AUTHENTICATED_USER'].name fileformat = string.replace(fileformat, "%u", user) else: fileformat = string.replace(fileformat, "%u", "") # path (%p) if string.find(fileformat, "%p")>=0: temp = string.joinfields (rel_url_list, "_") fileformat = string.replace(fileformat, "%p", temp) # file and extension (%n and %e) if string.find(fileformat,"%n")>=0 or string.find(fileformat,"%e")>=0: fileformat = string.replace(fileformat, "%n", id_name) fileformat = string.replace(fileformat, "%e", id_ext) # make the directories path = self._get_filename(dirs) if not os.path.isdir(path): mkdir_exc = "Can't create directory: " try: os.makedirs(path) except: raise mkdir_exc, path # search for unique filename if counter: fn = join(path, string.replace(fileformat, "%c", `counter`)) else: fn = join(path, string.replace(fileformat, "%c", '')) while (isfile(fn) or isfile(fn+'.preview') or isfile(fn+'.undo') or isfile(fn+'.preview.undo')): counter = counter+1 fn = join(path, string.replace(fileformat, "%c", `counter`)) if counter: fileformat = string.replace(fileformat, "%c", `counter`) else: fileformat = string.replace(fileformat, "%c", '') dirs.append(fileformat) return dirs ################################ # Special management methods # ################################ def manage_afterClone(self, item, new_fn=None): """ When a copy of the object is created (zope copy-paste-operation), this function is called by CopySupport.py. A copy of the external file is created and self.filename is changed. """ try: self.absolute_url(1) # this raises an exception, if no context except: self._v_has_been_cloned=1 else: old_fn = self._get_filename(self.filename) new_fn = new_fn or self._get_new_ufn() if isfile(old_fn): self._copy(old_fn, self._get_filename(new_fn)) self.filename = new_fn return ExtFile.inheritedAttribute ("manage_afterClone") (self, item) def manage_afterAdd(self, item, container): """ This method is called, whenever _setObject in ObjectManager gets called. This is the case after a normal add and if the object is a result of cut-paste- or rename-operation. In the first case, the external files doesn't exist yet, otherwise it was renamed to .undo by manage_beforeDelete before and must be restored by _undo(). """ self._undo() if hasattr(self, "_v_has_been_cloned"): delattr(self, "_v_has_been_cloned") self.manage_afterClone(item) return ExtFile.inheritedAttribute ("manage_afterAdd") \ (self, item, container) def manage_beforeDelete(self, item, container): """ This method is called, when the object is deleted. To support undo-functionality and because this happens too, when the object is moved (cut-paste) or renamed, the external file is not deleted. It are just renamed to filename.undo and remains in the repository, until it is deleted manually. """ fn = self._get_filename(self.filename) try: os.rename(fn, fn+'.undo') except OSError: pass return ExtFile.inheritedAttribute ("manage_beforeDelete") \ (self, item, container) Globals.default__class_init__(ExtFile) ExtFile/__init__.py0100644000570700000240000000266407347251334014045 0ustar greheineiona__doc__ = """ExtFile initialization module. """ __version__ = '1.1.x' # Step #1: import your Python class so we can refer to it later import ExtFile import ExtImage # Step #2: define the initialize() function. def initialize(context): """Initialize the ExtFile product.""" try: """Try to register the product.""" context.registerClass( ExtFile.ExtFile, # Which is the addable bit? constructors = ( # The first of these is called ExtFile.manage_addExtFileForm, # when someone adds the product; ExtFile.manage_addExtFile), # the second is named here so we # can give people permission to call it. icon = 'www/extFile.gif' # This icon was provided by the ) # Zope 1 product-in-Python demo. context.registerClass( ExtImage.ExtImage, # Which is the addable bit? constructors = ( # The first of these is called ExtImage.manage_addExtImageForm, # when someone adds the product; ExtImage.manage_addExtImage), # the second is named here so we # can give people permission to call it. icon = 'www/extImage.gif' # This icon was provided by the ) # Zope 1 product-in-Python demo. except: """If you can't register the product, tell someone.""" import sys, traceback, string type, val, tb = sys.exc_info() sys.stderr.write(string.join(traceback.format_exception(type, val, tb), '')) del type, val, tb ExtFile/extFileAdd.dtml0100644000570700000240000000344007316205274014616 0ustar greheineiona=0"> Add ExtFile

Add ExtFile

Id:  (optional)
Title:
Description:
File:
 
Permission: Use 'Download ExtFile/ExtImage' permission
 
 
ExtFile/extFileEdit.dtml0100644000570700000240000000357407316205300015011 0ustar greheineiona=0"> Edit ExtFile <dtml-var title_or_id>

Edit ExtFile

Filename: (Download)
File Size: ( Bytes)  
Content Type:
Icon: (View)
Title:
Description:
   
 
ExtFile/extFileUpload.dtml0100644000570700000240000000256307316205376015362 0ustar greheineiona=0"> Upload ExtFile
 
Local upload:
File:
 
 
HTTP upload:
URL:
 
ExtFile/extImageAdd.dtml0100644000570700000240000001140607316205212014752 0ustar greheineiona=0"> Add ExtImage

Add ExtImage

Id:  (optional)
Title:
Description:
Image File:
 
Preview: No preview
  Generate from image file specified above
  Use file below and don't resize:
  Use file below and resize:
 
Size:   x       keep aspect ratio
 
Permission: Use 'Download ExtFile/ExtImage' permission
 
 
ExtFile/extImageUpload.dtml0100644000570700000240000000312507316205236015513 0ustar greheineiona=0"> Upload ExtImage
Upload affects: File/Image Preview
 
Local upload:
File:
 
 
HTTP upload:
url:
 
ExtFile/www/0040755000570700000240000000000007347252212012547 5ustar greheineionaExtFile/www/extFile.gif0100644000570700000240000000025207244537070014636 0ustar greheineionaGIF89aPPP!,@V(zܧP1cl @ 6E$(e|(B+fH01"X֣X XlitXN++,-bH;@lƞnWaN_e$;ExtFile/www/extImage.gif0100644000570700000240000000035707244537070015007 0ustar greheineionaGIF89a-/_`$ia#nRAl5NWm,b6+ng43ooPPP!,@l#rdɉiZ"Yd@Fp(-k"N#T7iP\NH" $gZ-O,˥{,Q7|%?t bc0 .$!;ExtFile/icons/0040755000570700000240000000000007347252212013036 5ustar greheineionaExtFile/icons/broken.gif0100644000570700000240000000050307244537070015004 0ustar greheineionaGIF89a 333!, @Pȉv-AiWVpx`8Vq<%!%&"͎* Uj(#X jeOzV0nji^Dc.CE'e^eTZrfbc?@\w7dm'cp2z(< l)+Kj(uMr),k&_w26]עJzQbXZYfhW|7btNA tD;ExtFile/icons/default.gif0100644000570700000240000000040607300572152015143 0ustar greheineionaGIF89a Ƅ!, @ZO)˸8}3aFb(rLsV!kp04])/f+0*j,X*v1"\ƘfPR-*Q]l`G!}V\@T>uaes;|+bi4*7q\i%ob\gr(MpR4-'.WDQ69߅tcS ;ExtFile/icons/types.html0100644000570700000240000000256607306745040015077 0ustar greheineionaMIME-Type Icons

Icons for different MIME-Types

(Point your mouse on the icon and see the alt-text)

Broken:
Default:
Application:
Audio:
Image:
Text:
Video:
ExtFile/icons/video/0040755000570700000240000000000007347252212014144 5ustar greheineionaExtFile/icons/video/default.gif0100644000570700000240000000264207244537070016264 0ustar greheineionaGIF89a yz|}fbc_Y[JGHZWXQLNZUW\WY ?=>:89icgoln@?FF00aVV''0⾾ ?<.E+-z(OOKYKS^S/0/898EFE@A@bcb`a` !$+28@hp}}mᄄu卍񺺳SSQ\\Z??>PPOZZYKIEA?=>=܍! " 'CJ}Y9Ayz~zRǵ! Ӆ #m*d\QcGz8JԨ 7obyիG/){x b 4R Ց2Z"NjonG7BF-M V̖3tᖱ:<3_0, q#s ꔥN2I=tLh'a% (P76fҬH5$ 6p5HezThfh6U5Vq(QAUbVP6:Dh"rG BLn9C:@,N0lT@5q10UX , aP`6Sb@C&뭑WQ@?@%j A 8C7ިptb'a9`Ï>pS 10 6\{J,DɍN9t N($2CKU^3;촓N#4!LTH2̈S9c1πsN8,̿ ٫dCs!3tE,;ExtFile/icons/text/0040755000570700000240000000000007347252212014022 5ustar greheineionaExtFile/icons/text/default.gif0100644000570700000240000000033107244537070016133 0ustar greheineionaGIF89a !, @(:O)8}udieQ"nؾ L>@Sň # %(\`Xp)SeznN1Y%7'IdyfVz)Eq`3$p{ߨ>W-1S 6@N[lsC m-8U]O!Y@EpOV~]EQ/([D|8i]oǿ̏ 'TNf,jjiw Wl)CI{tv)fu#(D5V6V~.?;9KGgDJY@@BA@:CECB E DKJOE"S&[&X-WF5DAFBAEEB I G 8LVP&^&\&[&^&K4bDCD(aGI"Go ;efg?r"OW]Ѡ+-n]ffԟΪ#+* 'owtxtJRIMx#BM/j{`cPXIN[έtuftDkp)0\v~ї133.\\Z*1E0/r0.ni *rn$bрx  94RwVl6rPԷmwT}.g5aU*ǘltUeJ>:7)Wq,l-xM3W(Q1lQ0g)W`Lxj7,)8 eRLEHX51$-DCC!, @ G*\(Lt%  `K-W?=‚jxHU t*2ܢL' :;%L^ecFU9 q_`Êϝ='QCf +8zU&`2`=mM:=#ŰiXM'_)tk8_>>:^Yױ$֟$G)Bȑ#H|I҄0L0KŝD!%LƵ4x4T0 7pX- < $b 4*|RL4(,tI#gL1 t3  J Q=@j0@44G bL9∃H|p#bPD)TV))ehUؖsDX?%e%J~Є\ԹE^PFLؠ^,Š3uP o8"r!fp!D(; 003 Ut.83$2\ϓ]C` 18 ?.`.# 9X'Ds *1Pȹ0@.l0XRJ0pxҰϹaqC t/l@:ll(<sPjF C9t#,3(@!mabP,X,dRJ8p3$(# %CdȰ %S! RO T^Wy$<p~)%;ExtFile/icons/text/py.gif0100644000570700000240000000246307244537070015147 0ustar greheineionaGIF89a PSòmcḛϦbaj%%00w)~~১GGH\uiv{r"s !x%#t'CGrt7`8>+i i } m]=l7! "#B2mh)+-*uKgD37=96?69h%)z+XMJO<>1cZX4][AL4]\'9M7]kYisu~qmxXi~XfNY}rUUV(XY8}}zzppoollkkhhggXXVVSSRQJJFFEE==::88BBYY^^ WW HH CC&%??bb$]]#((KK!++44QQ*bb3jj8FF'HH)^^;NN4OO8SS>``Jnn]ccTeeXxCC@~}{yvwtqmb`y|uytvripiLG~~qIG4wvm]P}hJ<qjX Ƅ!, @G*\П2t)+U j"ze̹L:l!6(Q > 9掅i2FyR__*Q ASQ+DS ޳g]֐SNS/ ?Їӿ TU5+lX2N< n$+ ǏC #*m2aasĤ QTWׇCTn0dɓ0\fox }ͤ){1SD0*)sI*  C/DI 6@S%$#(V^qR0> (D (ړe뮇;ExtFile/icons/text/xml.gif0100644000570700000240000000062707244537070015317 0ustar greheineionaGIF89a 1c1c11c1111cƄ!, @ dihl$=O< E狀J1(W5\tJJNŽyq, : "hVc}~^xZc$[\P>acK oI@bfh/2  3rtv%[;7VTZP%;ȍ˿^9ˏ*P01,-3E}#[gj5mE Iu7"*70 @c |{o%}q'?؊TOT:Cf c"=K 0cʜIJf8E;ExtFile/icons/image/0040755000570700000240000000000007347252212014120 5ustar greheineionaExtFile/icons/image/default.gif0100644000570700000240000000062407244537070016236 0ustar greheineionaGIF89a Ƶsss!!!!, @ 8gZy MrlI 1b#P ȌPVx!i0V»ʀ[b0чgFa}O Y(P@!S&S)e*gyk/mF2Chtz @ Ds:|noiIf+_pM ϟ{4J]%% $SV ]'TKO)^ q0D$!?PCePx%DA؋ch Ĥ,_jQd/27$&& 2g]Dυ-CyePe&Aի";ExtFile/icons/audio/0040755000570700000240000000000007347252212014137 5ustar greheineionaExtFile/icons/audio/default.gif0100644000570700000240000000035107244537070016252 0ustar greheineionaGIF89a Ƅ!, @AtXB HbpS1 C$H#(tYXOrt[[2\F@ VVt$g>}cm[b1`0M:T]B%@?){?.R-1l˕ ;ExtFile/icons/application/0040755000570700000240000000000007347252212015341 5ustar greheineionaExtFile/icons/application/default.gif0100644000570700000240000000040607244537070017455 0ustar greheineionaGIF89a Ƅ!, @ZO)˸8}3aFb(rLsV!kp04])/f+0*j,X*v1"\ƘfPR-*Q]l`G!}V\@T>uaes;|+bi4*7q\i%ob\gr(MpR4-'.WDQ69߅tcS ;ExtFile/icons/application/doc.gif0100644000570700000240000000040407244537070016574 0ustar greheineionaGIF89a Ƅ!, @)tJTEHVhVCk, ;9hJ!] EjmH$dquݢW#a!$ Z2h? ]~|</[VN0:N_W @>?ev>mCg?$m)u&Aw_n#q%y&xlλQC4OЃρڑ ;ExtFile/icons/application/pdf.gif0100644000570700000240000000051407244537070016602 0ustar greheineionaGIF89a 1ƵsssRRR!, @ 8gZ !(z 2lI +lFRC BD*5=R 5P/ ݮrݬG#nX-aܲN'dڣTpps2hx[=mnZ7A5Yb,+C_:+?An6a6VXx'TvwzP|!R* Xy}T3JW-iשՌ[2^[5ܒ3l7f GAX$b  (L59EBE;ExtFile/icons/application/ppt.gif0100644000570700000240000000036507244537070016640 0ustar greheineionaGIF89a Ƅ!, @̐Ik5%iddibYUd4uu1A` rWd"]0왻I(d1M8  AbUSw\2ش=l4V M 1ig[ ]TgOecd:6 K4&q*vÒ79ͨˑX̕SWv> c_ ;ExtFile/icons/application/zip.gif0100644000570700000240000000262307244537070016636 0ustar greheineionaGIF89a Sg*Tl=[zFgNlDiDhLl,YspFdsށvi5gu8gu Sd4ivtBJL^rWg VfVd+Lt|Lrzf Q^gu\-X`7dlGyX}Zw|mFqwGntRw}Otyo$go4ls9lry8]a`OT9SY%dj4nt;eiEswFrv>Abf)`c@nqaDikc9dfHghSuvg|}L1cdtϪMKvZ4iٻ7οZ*L^57qkѨܯ b!@Ѐs*| ⪜9{}KvRNN^"qrL@! ՟b=31hf[;酉5|Wm*޿|ۄ9rSOOmJ?ժo!)^hR+Q"v@G [`}$ 6g(W'i&VtabfZe) @0Jفc @P5X5d580M:q 3E)⥗ 75C