catapela avatar catapela committed ee28bec

Initial commit of all files to the repository.

Comments (0)

Files changed (43)

+/home/catapela/Projekte/NetBeansProjects/django/distribution/django-dynamic-link/src/dynamicLink/LICENSE
+include README LICENSE
+recursive-include dynamicLink/ *
+recursive-include example/ *
+/home/catapela/Projekte/NetBeansProjects/django/distribution/django-dynamic-link/src/dynamicLink/README

dynamicLink/LICENSE

+Copyright (c) 2010, Andreas Fritz
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, are permitted 
+provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright notice, this list of
+      conditions and the following disclaimer.
+    * 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.
+ 
+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 THE COPYRIGHT HOLDER OR 
+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.

dynamicLink/README

+===========
+Description
+===========
+
+Django file streaming application to provide download links without showing the real path to the served file. The links can be set to expire by date or by clicks. It is also possible to use it for counting clicks on a download link.
+
+**License**
+
+    New BSD license
+
+**Notes**
+
+    * It's tested with Django 1.2
+    * Example project is included
+
+========
+Features
+========
+
+    * Link expires by clicks (optional)
+    * Link expires by time (optional)
+    * Is usable for counting clicks
+
+============
+Installation
+============
+
+**Dependences**
+
+    * This app
+    * Django itself, its redirects from django.contrib and its locale middleware.
+
+**Installation**
+
+    *Manual Installation*
+
+        * Download the file and unzip it.
+        * Copy the folder in your project root.
+
+    *Installation with pip*
+
+        * Type in your terminal: "pip install django-dynamic-link"
+        * With pip you can also uninstall it: "pip uninstall django-dynamic-link"
+
+    *Other possibilities*
+
+        * Open a terminal and change to the folder which contains the setup.py and then type "setup.py install"
+
+**test your installation**
+
+Go to console and type:
+
+    * :~$ python
+    * import dynamicLink
+    * dynamicLink.VERSION
+    * help(dynamicLink)
+
+**Setup**
+
+    * Add "dynamicLink" to you installed apps in the settings file.
+    * Make sure that:
+        -   'django.middleware.locale.LocaleMiddleware', is in your MIDDLEWARE_CLASSES.
+        -   'django.contrib.redirects.middleware.RedirectFallbackMiddleware', is in your MIDDLEWARE_CLASSES.
+        -   'django.contrib.redirects', is added to your INSTALLED_APPS.
+        -   Your Admin is enabled ('django.contrib.admin', is in your INSTALLED_APPS.)
+    * Add the following to your urls.py:
+        -   from dynamicLink import dl_settings
+        -   (r'^%s/' % dl_settings.DYNAMIC_LINK_URL_BASE_COMPONENT, include('dynamicLink.urls')),
+    * Change the pattern for your admin in urls.py from (r'^admin/', include(admin.site.urls)), to (r'^%sadmin/(.*)' admin.site.root),
+    * Run "python manage.py syncdb".
+    * And finally "python manage.py runserver".
+
+**Make it custom**
+
+    You have the following variables to overwrite the applications presettings.
+
+    * DYNAMIC_LINK_MEDIA
+        - Default: settings.MEDIA_ROOT
+        - A path to a directory. From this point you can walk down the subdirectories to choose your files to serve.
+    * DYNAMIC_LINK_URL_BASE_COMPONENT
+        - Default: 'serve'
+        - A string that modifies your url serve path.
+        - Example: www.example.com/DYNAMIC_LINK_URL_BASE_COMPONENT/link/3839hd8HKl3/example.zip.
+
+    Use the global settings.py in your projects root or create an extra file named dl_settings.py there (which is used then instead of settings.py. May customize and use the file from the example folder).
+
+=====
+Usage
+=====
+
+Open the admin interface and go to "Dynamiclink" section. The rest should be self-explanatory.
+
+**Hints**
+
+    * Zero value for link age means never expires.
+    * Zero value for clicks means unlimited clicks.
+    * If a link never expires you can use it for click counting.
+    * Trough the action menu you can serve a site with several links. 
+    * The filename from the created links are only for human readability. You can delete or change this filenames in any way you want.
+
+===============
+Example project
+===============
+
+djang-dynamic-links ships with an example proect.
+
+    1. First unpack the tar.gz
+    2. Than move "dynamicLink" in to the "example" folder or install it (see install section).
+    3. Run "python manage.py syncdb" and "python manage.py runserver".
+    4. Open a Browser, go to: http://127.0.0.1:8000/
+

dynamicLink/__init__.py

+import version
+import os
+
+__version__ = VERSION = version.VERSION
+__doc__ = open(os.path.join(os.path.dirname(__file__), 'README')).read()
+__docformat__ = 'reStructuredText'
Add a comment to this file

dynamicLink/__init__.pyc

Binary file added.

dynamicLink/admin.py

+#!/usr/bin/python
+# -*- coding:utf-8 -*-
+# This Python file uses the following encoding: utf-8
+
+__author__ = "Andreas Fritz - sources.e-blue.eu"
+__copyright__ = "Copyright (c) " + "28.08.2010" + " Andreas Fritz"
+__licence__ = """New BSD Licence"""
+__doc__ = """Zur Zeit noch keine Dokumentation."""
+
+from django.contrib import admin
+from models import Download
+from django.utils.translation import ugettext_lazy as _
+import api
+import presettings
+
+
+class DownLinkAdmin(admin.ModelAdmin):
+    def __call__(self, request, * args, ** kwargs):
+        self.request = request
+        return super(DownLinkAdmin, self).__call__(request, * args, ** kwargs)
+
+    list_display = ('slug', 'active', 'file', 'valid', 'clicks', 'timestamp_creation', 'link')
+    actions = ['make_link']
+    search_fields = ['slug', 'file_path', 'timestamp_creation', 'link_key']
+    list_per_page = 50
+    fieldsets = (
+                 (_(u'Link'), {
+                 'fields': ('slug', 'file_path')
+                 }),
+                 (_(u'Aditional values'), {
+                 'classes': ('collapse',),
+                 'fields': ('active', 'current_clicks', 'timeout_hours', 'max_clicks')
+                 }),
+                 )
+
+    def valid(self, obj):
+        """Shows timestamp expired or active time"""
+        diff = unicode(obj.get_timout_time()).split('.')[0]
+        if obj.timeout_time():
+            if obj.active:
+                # set active to false
+                obj.active = False
+                obj.save()
+            return '<span style="color: #FF7F00; ">%s</span>:<br/> ' \
+            % (unicode(_(u'timeout'))) + diff
+        else:
+            return diff
+    valid.allow_tags = True
+    valid.short_description = _(u'valid')
+
+    def file(self, obj):
+        """Shows truncated filename on platform indepentend length."""
+        return unicode(obj.file_path).split(presettings.DYNAMIC_LINK_MEDIA)[-1]
+    file.allow_tags = True
+    file.short_description = _(u'file')
+
+    def clicks(self, obj):
+        """Shows current and max allowed clicks in the list display"""
+        txt = '%s %s %s' % (obj.current_clicks, unicode(_(u'from')), obj.max_clicks)
+        if obj.timeout_clicks():
+            if obj.active == True:
+                # set active to false
+                obj.active = False
+                obj.save()
+            return '<span style="color: #FF7F00; ">%s</span><br/>%s' \
+            % (unicode(_('max clicks reached')), txt)
+        elif obj.max_clicks == 0:
+            return '%s %s <span style="color: #FF7F00; ">%s</span>' \
+            % (obj.current_clicks, unicode(_(u'from')), unicode(_(u'unlimited')))
+        else:
+            return txt
+    clicks.allow_tags = True
+    clicks.short_description = _(u'clicks')
+
+    def link(self, obj):
+        """Generate site and download url from link object"""
+
+        # download site with link
+        siteurl = api.DownloadSiteUrl([obj.link_key])
+        sitelink = siteurl.get_site_url(self.request)
+        sitelink = u'<span style="color: #FF7F00; ">%s:</span> \
+        <a target="new" href="%s/">%s/</a><br/>' % (unicode(_(u'Site')), sitelink, sitelink)
+
+        # direct accessable link
+        filelink = api.file_link_url(self.request, obj)
+        filelink = '<span style="color: #FF7F00; ">%s:</span> %s' % (unicode(_(u'File')), filelink)
+
+        return sitelink + filelink
+    link.allow_tags = True
+    link.short_description = _(u'link')
+
+    def make_link(modeladmin, request, queryset):
+        """Action method. Make site url from many singles objects."""
+        li = []
+        for obj in queryset:
+            li.append(obj.link_key)
+        siteurl = api.DownloadSiteUrl(li)
+        sitelink = siteurl.get_site_url(request)
+        # roesponse
+        from django.http import HttpResponse
+        return HttpResponse('<a target="new" href="%s/">%s/</a><br/>' % (sitelink, sitelink))
+    make_link.short_description = _("Make from selected a download site link")
+
+admin.site.register(Download, DownLinkAdmin)

Binary file added.

dynamicLink/api.py

+#!/usr/bin/python
+# -*- coding:utf-8 -*-
+# This Python file uses the following encoding: utf-8
+
+from models import Download
+import presettings
+
+
+def file_link_url(request, linkobject, langcode='lg'):
+    """returns the access url of the of the dynamicLink object"""
+    return '%s%s/%s/%s/link/%s/%s' % ('http://', request.META.get('HTTP_HOST'), \
+            langcode, presettings.DYNAMIC_LINK_URL_BASE_COMPONENT, linkobject.link_key, \
+            linkobject.get_filename())
+
+class DynamicLink:
+    """
+    create and access dynamic links form outsite of this app
+    """
+    def __init__(self, slug, file_path, timeout_hours=None, max_clicks=None):
+        """creates a new dynamic link"""
+        self.new_link = Download()
+        self.new_link.slug = slug
+        self.new_link.file_path = file_path
+
+        if timeout_hours:
+            self.new_link.timeout_hours = timeout_hours
+        if max_clicks:
+            self.new_link.max_clicks = max_clicks
+
+        self.new_link.save()
+
+    def get_link_key(self):
+        """get his unique key"""
+        return self.new_link.link_key
+
+    def get_link_url(self, request, langcode='lg'):
+        """access his url"""
+        return file_link_url(request, self.new_link, langcode)
+
+class DownloadSiteUrl():
+    """
+    create a download site with links from the given keys
+    """
+    def __init__(self, keylist=None):
+        """you can add a list with keys"""
+        self.keys = []
+        if keylist:
+            self.keys += keylist
+
+    def add_key(self,key):
+        """add a key to allready given keys"""
+        self.keys.append(key)
+
+    def get_site_url(self, request, langcode='lg'):
+        """returns a site urls form allready given keys"""
+        return '%s%s/%s/%s/site/%s' % ('http://', request.META.get('HTTP_HOST'), \
+        langcode, presettings.DYNAMIC_LINK_URL_BASE_COMPONENT, '-'.join(self.keys))
+
+
+
+
+
+
+
+

Binary file added.

Add a comment to this file

dynamicLink/locale/de/LC_MESSAGES/django.mo

Binary file added.

dynamicLink/locale/de/LC_MESSAGES/django.po

+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2010-10-17 22:28+0800\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1)\n"
+
+#: admin.py:26
+msgid "Link"
+msgstr "Verknüpfung"
+
+#: admin.py:29
+msgid "Aditional values"
+msgstr "Weitere Parameter"
+
+#: admin.py:44
+msgid "timeout"
+msgstr "abgelaufen"
+
+#: admin.py:48
+msgid "valid"
+msgstr "gültig"
+
+#: admin.py:54
+msgid "file"
+msgstr "Datei"
+
+#: admin.py:58 admin.py:68
+msgid "from"
+msgstr "von"
+
+#: admin.py:65
+msgid "max clicks reached"
+msgstr "Maximum erreicht"
+
+#: admin.py:68
+msgid "unlimited"
+msgstr "Unbeschränkt"
+
+#: admin.py:72
+msgid "clicks"
+msgstr "Zugriffe"
+
+#: admin.py:80
+msgid "Site"
+msgstr "Seite"
+
+#: admin.py:82
+msgid "File"
+msgstr "Datei"
+
+#: admin.py:85
+msgid "link"
+msgstr "Verknüpfung"
+
+#: admin.py:97
+msgid "Make from selected a download site link"
+msgstr "Generiere vom selektierten eine Seiten Url"
+
+#: dl_settings.py:18
+msgid "This request is faulty"
+msgstr "Diese Verknüpfung ist fehlerhaft"
+
+#: dl_settings.py:19
+msgid "Sorry, this request is already expired"
+msgstr "Der angeforderte Inhalt ist nicht mehr Verfügbar"
+
+#: models.py:31
+msgid "Platform independent file path field implementation"
+msgstr "Platform unabhängige Dateipfad-Auswahl-Feld Implementierung"
+
+#: models.py:70
+msgid "Files with whitespaces can't be used!"
+msgstr "Dateien mit Leerstellen im Dateinamen können nicht ausgewählt werden!"
+
+#: models.py:82
+msgid "slug"
+msgstr "Schlagwort"
+
+#: models.py:84
+msgid "is aktive"
+msgstr "Ist aktiv"
+
+#: models.py:89
+msgid ""
+"Select the content your like                                      to "
+"provide. File names with whitespaces won't be saved!"
+msgstr ""
+"Dateiauswahl für den zu erstellenden Link.Dateien mit Leerstellen im "
+"Dateinamen werden nicht gespeichert!"
+
+#: models.py:91
+msgid "content to serve"
+msgstr "Anzubietender Inhalt"
+
+#: models.py:103
+msgid "creation time"
+msgstr "Erstellungszeit"
+
+#: models.py:105
+msgid "timout in hours"
+msgstr "Gültigkeit in Stundenk"
+
+#: models.py:106
+msgid "Zero value means no timeout."
+msgstr "Keine Zeitüberschreitung bei Nullwert"
+
+#: models.py:107
+msgid "maximum allowed clicks"
+msgstr "Maximal zugelassene Zugriffe"
+
+#: models.py:108
+msgid "Zero value means no limitation."
+msgstr "Keine Beschränkung bei Nullwert"
+
+#: models.py:109
+msgid "current clicks"
+msgstr "Aktuelle Zugriffe"
+
+#: models.py:139
+msgid "never expires"
+msgstr "Immer gültig"
+
+#: models.py:209
+msgid "Slug"
+msgstr "Schlagwort"
+
+#: models.py:209
+msgid "Filename"
+msgstr "Dateiname"
+
+#: views.py:37
+msgid "Sorry, your request is mot available"
+msgstr "Die Anfrage ist nicht verfügbar"
+
+#: views.py:99
+msgid "File not found!"
+msgstr "Datei nicht gefunden"
+
+#: templates/dynamicLink/not_avallible.html:6
+#: templates/dynamicLink/provide.html:6
+msgid "download"
+msgstr "Download"
+
+#: templates/dynamicLink/not_avallible.html:14
+msgid "If something goes wrong please contact us with "
+msgstr "Im Falle eines Fehlers kontaktieren Sie uns bitte über "
+
+#: templates/dynamicLink/not_avallible.html:15
+#: templates/dynamicLink/provide.html:47
+msgid "our contact form"
+msgstr "unser Kontaktformular"
+
+#: templates/dynamicLink/provide.html:7
+msgid "for later use please bookmark this page"
+msgstr "Für späteren Zugriff diese Seite zu den Lesezeichen hinzugügen"
+
+#: templates/dynamicLink/provide.html:14
+msgid "Download your requested content now"
+msgstr "Jezt die angeforderten Inhalte herunterladen"
+
+#: templates/dynamicLink/provide.html:17
+msgid "Activ downloads"
+msgstr "Aktive Downloads"
+
+#: templates/dynamicLink/provide.html:19 templates/dynamicLink/provide.html:27
+msgid "Download"
+msgstr "Download"
+
+#: templates/dynamicLink/provide.html:19 templates/dynamicLink/provide.html:27
+msgid "Please click here for download your content"
+msgstr "Bitte hier klicken um das Herunterladen der Datei zu Starten"
+
+#: templates/dynamicLink/provide.html:25
+msgid "Expired downloads"
+msgstr "Nicht mehr gültige Downloads"
+
+#: templates/dynamicLink/provide.html:33
+msgid "These links are faulty"
+msgstr "Diese Verknüpfungen sind fehlerhaft"
+
+#: templates/dynamicLink/provide.html:35
+msgid "Link Error"
+msgstr "Fehlerhafte Verknüpfungen"
+
+#: templates/dynamicLink/provide.html:45
+msgid "Anything goes wrong?"
+msgstr "Probleme?"
+
+#: templates/dynamicLink/provide.html:46
+msgid "If something goes wrong with your download then contact us with"
+msgstr "Sollten Probleme auftreten kontaktieren Sie uns bitte über"
+
+#~ msgid "Select the content your like to provide."
+#~ msgstr "Auswahl der gewünschten Dateien"
Add a comment to this file

dynamicLink/locale/en/LC_MESSAGES/django.mo

Binary file added.

dynamicLink/locale/en/LC_MESSAGES/django.po

+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2010-10-17 22:28+0800\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: admin.py:26
+msgid "Link"
+msgstr ""
+
+#: admin.py:29
+msgid "Aditional values"
+msgstr ""
+
+#: admin.py:44
+msgid "timeout"
+msgstr ""
+
+#: admin.py:48
+msgid "valid"
+msgstr ""
+
+#: admin.py:54
+msgid "file"
+msgstr ""
+
+#: admin.py:58 admin.py:68
+msgid "from"
+msgstr ""
+
+#: admin.py:65
+msgid "max clicks reached"
+msgstr ""
+
+#: admin.py:68
+msgid "unlimited"
+msgstr ""
+
+#: admin.py:72
+msgid "clicks"
+msgstr ""
+
+#: admin.py:80
+msgid "Site"
+msgstr ""
+
+#: admin.py:82
+msgid "File"
+msgstr ""
+
+#: admin.py:85
+msgid "link"
+msgstr ""
+
+#: admin.py:97
+msgid "Make from selected a download site link"
+msgstr ""
+
+#: dl_settings.py:18
+msgid "This request is faulty"
+msgstr ""
+
+#: dl_settings.py:19
+msgid "Sorry, this request is already expired"
+msgstr ""
+
+#: models.py:31
+msgid "Platform independent file path field implementation"
+msgstr ""
+
+#: models.py:70
+msgid "Files with whitespaces can't be used!"
+msgstr ""
+
+#: models.py:82
+msgid "slug"
+msgstr ""
+
+#: models.py:84
+msgid "is aktive"
+msgstr ""
+
+#: models.py:89
+msgid ""
+"Select the content your like                                      to "
+"provide. File names with whitespaces won't be saved!"
+msgstr ""
+
+#: models.py:91
+msgid "content to serve"
+msgstr ""
+
+#: models.py:103
+msgid "creation time"
+msgstr ""
+
+#: models.py:105
+msgid "timout in hours"
+msgstr ""
+
+#: models.py:106
+msgid "Zero value means no timeout."
+msgstr ""
+
+#: models.py:107
+msgid "maximum allowed clicks"
+msgstr ""
+
+#: models.py:108
+msgid "Zero value means no limitation."
+msgstr ""
+
+#: models.py:109
+msgid "current clicks"
+msgstr ""
+
+#: models.py:139
+msgid "never expires"
+msgstr ""
+
+#: models.py:209
+msgid "Slug"
+msgstr ""
+
+#: models.py:209
+msgid "Filename"
+msgstr ""
+
+#: views.py:37
+msgid "Sorry, your request is mot available"
+msgstr ""
+
+#: views.py:99
+msgid "File not found!"
+msgstr ""
+
+#: templates/dynamicLink/not_avallible.html:6
+#: templates/dynamicLink/provide.html:6
+msgid "download"
+msgstr ""
+
+#: templates/dynamicLink/not_avallible.html:14
+msgid "If something goes wrong please contact us with "
+msgstr ""
+
+#: templates/dynamicLink/not_avallible.html:15
+#: templates/dynamicLink/provide.html:47
+msgid "our contact form"
+msgstr ""
+
+#: templates/dynamicLink/provide.html:7
+msgid "for later use please bookmark this page"
+msgstr ""
+
+#: templates/dynamicLink/provide.html:14
+msgid "Download your requested content now"
+msgstr ""
+
+#: templates/dynamicLink/provide.html:17
+msgid "Activ downloads"
+msgstr ""
+
+#: templates/dynamicLink/provide.html:19 templates/dynamicLink/provide.html:27
+msgid "Download"
+msgstr ""
+
+#: templates/dynamicLink/provide.html:19 templates/dynamicLink/provide.html:27
+msgid "Please click here for download your content"
+msgstr ""
+
+#: templates/dynamicLink/provide.html:25
+msgid "Expired downloads"
+msgstr ""
+
+#: templates/dynamicLink/provide.html:33
+msgid "These links are faulty"
+msgstr ""
+
+#: templates/dynamicLink/provide.html:35
+msgid "Link Error"
+msgstr ""
+
+#: templates/dynamicLink/provide.html:45
+msgid "Anything goes wrong?"
+msgstr ""
+
+#: templates/dynamicLink/provide.html:46
+msgid "If something goes wrong with your download then contact us with"
+msgstr ""

dynamicLink/models.py

+#!/usr/bin/python
+# -*- coding:utf-8 -*-
+# This Python file uses the following encoding: utf-8
+
+__author__ = "Andreas Fritz - sources.e-blue.eu"
+__copyright__ = "Copyright (c) " + "28.08.2010" + " Andreas Fritz"
+__licence__ = "New BSD Licence"
+
+
+import presettings
+from django.db import models
+import settings
+from django.utils.translation import ugettext_lazy as _
+import random
+import os
+import datetime
+
+class Download(models.Model):
+    slug = models.SlugField(verbose_name=_('slug'), blank=False, unique=True)
+    active = models.BooleanField(default=True,
+                                 verbose_name=_(u'is aktive'),
+                                 )
+    file_path = models.FilePathField(
+                                      path=presettings.DYNAMIC_LINK_MEDIA,
+                                      help_text=_(u"Select the content your like \
+                                      to provide."),
+                                      verbose_name=_(u'content to serve'),
+                                      blank=True,
+                                      recursive=True
+                                      )
+    link_key = models.CharField(max_length=50, editable=False, unique=True)
+    timestamp_creation = models.DateTimeField(
+                                              auto_now=False,
+                                              auto_now_add=True,
+                                              editable=False,
+                                              verbose_name=_(u'creation time'),
+                                              )
+    timeout_hours = models.IntegerField(verbose_name=_(u'timout in hours'), \
+                                        default=72, help_text=_('Zero value means no timeout.'))
+    max_clicks = models.IntegerField(verbose_name=_(u'maximum allowed clicks'), \
+                                     default=3, help_text=_('Zero value means no limitation.'))
+    current_clicks = models.IntegerField(default=0, verbose_name=_(u'current clicks'))
+
+    def get_filename(self):
+        return os.path.basename(self.file_path)
+
+    def timeout_clicks(self):
+        """If max clicks reached than it returns True"""
+        # dont cange the order
+        if not self.max_clicks == 0: # max clicks 0 means never expired through clicks
+            if self.current_clicks >= self.max_clicks: # if number of max clicks reached
+                return True
+
+    def timeout_time(self):
+        """If timout time is reached it returs True"""
+        if not self.timeout_hours == 0: # never timeout through time
+            if self.timestamp_creation + datetime.timedelta(hours=self.timeout_hours) < datetime.datetime.now():
+                return True
+
+    def timeout(self):
+        """
+        In case of timeout (clicks or expired time) it returns True
+        """
+        if self.timeout_time():
+            return True
+        if self.timeout_clicks():
+            return True
+
+    def get_timout_time(self):
+        """Is shown at the admit list display"""
+        if self.timeout_hours == 0:
+            return '<span style="color: #FF7F00; ">%s</span>' % unicode(_(u'never expires'))
+        return (self.timestamp_creation + datetime.timedelta(hours=self.timeout_hours)) - datetime.datetime.now()
+
+    def set_link(self, file, slug='autogenerated', timeout=None, maxclicks=None):
+        self.slug = slug
+        self.file_path = file.split(os.path.basename(settings.MEDIA_ROOT))[-1]
+        if timeout:
+            self.timeout_hours = timeout
+        if maxclicks:
+            self.max_clicks = maxclicks
+        self.save()
+
+    def __setup_instance(self):
+        """
+        Check if object instance expired and keep values of the istance actual.
+        """
+        # Keep the order of the tests
+        
+        # 1. Test of timeout
+        if self.timeout():
+            if self.active:
+                self.active = False
+                self.save()
+                raise IsExpiredError()
+
+        # 2. set max click values
+        if self.current_clicks < self.max_clicks:
+            self.current_clicks += 1
+            # check of last cklick
+            if self.current_clicks == self.max_clicks:
+                self.active = False # if it was the last allowed cklick then set to expired
+            self.save()
+        elif self.max_clicks == 0:
+            self.current_clicks += 1
+            self.save()
+
+    def get_path(self):
+        """
+        if active it returns the full path of stored file to serve.
+        if expired it returns None
+        """
+        self.__setup_instance()
+        return self.file_path
+
+    def __gen_key(self):
+        """
+        function for generating random keys
+        """
+        #key = str(time.time()).replace('.', '')
+        key = '' # for shorter keys
+        characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890'
+        key_length = 30
+        for y in range(key_length):
+            key += characters[random.randint(0, len(characters)-1)]
+        return key
+
+    def save(self, * args, ** kwargs):
+        """Perform custom methodes before saving."""
+        # If not avalible set unique link key bevor saving
+        if not self.link_key:
+            self.link_key = self.__gen_key()
+        # call the real save method
+        super(Download, self).save(*args, ** kwargs) # Call the "real" save() method
+
+    def __unicode__(self):
+        return '%s: %s, %s: %s' % (unicode(_(u'Slug')), self.slug, unicode(_(u'Filename')), self.get_filename())
+
+class IsExpiredError(Exception):
+    """Error class for expired link objects"""
+    def __init__(self, value=''):
+        self.value = presettings.TEXT_REQUEST_IS_EXPIRED + value
+    def __str__(self):
+        return repr(self.value)

Binary file added.

dynamicLink/presettings.py

+#!/usr/bin/python
+# -*- coding:utf-8 -*-
+# This Python file uses the following encoding: utf-8
+
+import settings
+from django.utils.translation import ugettext_lazy as _
+
+# A path to a directory from witch walk down so you can choose your files.
+DYNAMIC_LINK_MEDIA = settings.MEDIA_ROOT
+
+# A string that modify the serve url path:
+# /www.example.com/DYNAMIC_LINK_URL_BASE_COMPONENT/link/3839hd8HKl3/example.zip
+DYNAMIC_LINK_URL_BASE_COMPONENT = 'serve'
+
+# It's here because of not violate the DRY priciple.
+TEXT_REQUEST_DOES_NOT_EXIST = _(u'This request is faulty')
+TEXT_REQUEST_IS_EXPIRED = _(u'Sorry, this request is already expired')
+
+# Look for data that overwrite the defaults
+# - variables from dl_settigns.py overwrite thus from settings and form presettings
+# - variables from settings.py overwrite thus form presettings
+# As you see it can't be used both at once. Settings.py is only used if there
+# is no dl_settings.py
+try:
+    from dl_settings import *
+except ImportError:
+    try:
+        from settings import DYNAMIC_LINK_URL_BASE_COMPONENT
+    except ImportError:
+        pass
+    try:
+        from settings import DYNAMIC_LINK_MEDIA
+    except ImportError:
+        pass
Add a comment to this file

dynamicLink/presettings.pyc

Binary file added.

dynamicLink/templates/dynamicLink/not_avallible.html

+{% extends 'base.html' %}
+{% load i18n %}
+
+{% block title %}
+    {% trans "download" %}
+{% endblock %}
+
+{% block content %}
+
+<div class="main_container_line">
+    <h2>{{ message }}!</h2>
+    {% trans "If something goes wrong please contact us with " %}
+    <a href="/{{ request.LANGUAGE_CODE }}/feedback/contact/" name="contact if anything goes wrong">{% trans "our contact form" %}.</a>
+</div>
+    <!-- IE Clearing -->
+    <div id="ie_clearing">&nbsp;</div>
+
+{% endblock %}

dynamicLink/templates/dynamicLink/provide.html

+{% extends 'base.html' %}
+{% load i18n %}
+
+{% block title %}
+    {% trans "download" %}
+{% endblock %}
+{% block title2 %}
+    {% trans "for later use please bookmark this page" %}
+{% endblock %}
+
+{% block content %}
+
+<div class="main_container_line">
+    <h2>{% trans "Download your requested content now" %}!</h2>
+
+    {% if downloads.actives %}
+    <h3>{% trans "Activ downloads" %}:</h3>
+    {% for obj in downloads.actives %}
+    {% trans "Download" %}: <a href="/{{ request.LANGUAGE_CODE }}/{{ basepath }}/link/{{ obj.link_key }}/{{ obj.get_filename }}" name="Your Download">{{ obj.get_filename }} - {% trans "Please click here for download your content" %}.</a><br/>
+    {% endfor %}
+    <br/>
+    {% endif %}
+
+    {% if downloads.expired %}
+    <h3>{% trans "Expired downloads" %}:</h3>
+    {% for obj in downloads.expired %}
+        {% trans "Download" %}: <a href="/{{ request.LANGUAGE_CODE }}/{{ basepath }}/link/{{ obj.link_key }}/{{ obj.get_filename }}" name="Your Download">{{ obj.get_filename }} - {% trans "Please click here for download your content" %}.</a><br/>
+    {% endfor %}
+    <br/>
+    {% endif %}
+
+    {% if downloads.notexist %}
+    <h3>{% trans "These links are faulty" %}:</h3>
+    {% for key in downloads.notexist %}
+        {% trans "Link Error" %}: <a href="/{{ request.LANGUAGE_CODE }}/{{ basepath }}/link/{{ key }}/unknown" name="Does not exist">http://{{ request.META.HTTP_HOST }}/{{ request.LANGUAGE_CODE }}/{{ basepath }}/link/{{ key }}/</a><br/>
+    {% endfor %}
+    <br/>
+    {% endif %}
+
+</div>
+    <!-- IE Clearing -->
+    <div id="ie_clearing">&nbsp;</div>
+
+<div class="main_container_line">
+    <h2>{% trans "Anything goes wrong?" %}</h2>
+    {% trans "If something goes wrong with your download then contact us with" %}
+    <a href="/{{ request.LANGUAGE_CODE }}/feedback/contact/" name="contact if anything goes wrong">{% trans "our contact form" %}.</a>
+</div>
+
+{% endblock %}

dynamicLink/tests.py

+#!/usr/bin/python
+# -*- coding:utf-8 -*-
+# This Python file uses the following encoding: utf-8
+
+import unittest
+from models import Download
+from api import DynamicLink
+
+class Obj:
+    pass
+
+class DownloadTestCase(unittest.TestCase):
+    """Test of the Doanload modell"""
+    def setUp(self):
+        self.path = '/static/public/testrunner/test.jpg'
+        self.file = 'test.jpg'
+        self.downl = Download.objects.create(
+                              slug='download_model_unittest',
+                              file_path=self.path,
+                              timeout_hours = 10,
+                              max_clicks = 10,
+                              )
+
+    def test_download_methodes(self):
+        self.assertEqual(self.downl.get_filename(), 
+                        self.file,
+                        )
+        self.assertFalse(self.downl.timeout_clicks())
+        self.assertFalse(self.downl.timeout_time())
+        self.assertFalse(self.downl.timeout())
+        self.assertEqual(self.downl.get_path(), self.path)
+    
+class DynamicLinkTestCase(unittest.TestCase):
+    """Test the DynamicLink API"""
+    def setUp(self):
+        self.path = '/static/public/testrunner/test.jpg'
+        self.file = 'test.jpg'
+        self.dlink = DynamicLink(
+                      slug='api_unittest',
+                      file_path=self.path,
+                      timeout_hours = 10,
+                      max_clicks = 10,
+                      )
+
+    def test_dynamic_link_methodes(self):
+        request = Obj()
+        request.__dict__['META']={}
+        request.META['HTTP_HOST']='www.testrunner.eu'
+        link = '%s/%s/%s/%s/%s' % ('http://www.testrunner.eu',
+                                'lg',
+                                'serve/link',
+                                self.dlink.get_link_key(),
+                                self.file,
+                                )
+        self.assertEqual(link, self.dlink.get_link_url(request))
+        

dynamicLink/urls.py

+# -*- coding:utf-8 -*-
+# This Python file uses the following encoding: utf-8
+
+from django.conf.urls.defaults import patterns, url
+
+urlpatterns = patterns('', 
+                       (url(r'^site/([\w-]*)/$', 'dynamicLink.views.site')),
+                       (url(r'^link/(\w{1,100})/.*$', 'dynamicLink.views.fetch')),
+                       (url(r'^link/(\w{1,100})$', 'dynamicLink.views.fetch')), # without prefix
+                       )

Binary file added.

dynamicLink/version.py

+#!/usr/bin/python
+# -*- coding:utf-8 -*-
+# This Python file uses the following encoding: utf-8
+
+VERSION = (0,4,7,'pre2')
+APPLICATION_NAME = "Dynamic Link"
+VERSION_str = str(VERSION).strip('()').replace(',','.').replace(' ','')
+VERSION_INFO = """
+Version: %s
+Modification date: 03.08.2011
+
+Hints:
+- 0.4.7 -   - Preesettings can now be overwrite in the presettings.py in the project
+            folder, or directly in the global settings.py, or in a file
+            called dl_settings.py in the same directory as manage.py.
+            - django-dynamic-link now runs with python below version 2.6
+- 0.4.6 -   changes in setup.py to avoid to install the example project.
+- 0.4.5 -   Small adjustments at the example project.
+- 0.4.4 -   1. Because of incomplete setup.py the last distributed package
+            does't contained temples and language files.
+            2. Readme updated.
+            3. Example project added.
+- 0.4.3 -   ImportError in admin.py solved.
+- 0.4.2 -   setup.py createtd. Now listed in pypi.
+- 0.4.1 -   Remove small bug with links created with the action menu.
+
+TODO:
+    - write more unittests
+""" % (VERSION_str,)
Add a comment to this file

dynamicLink/version.pyc

Binary file added.

dynamicLink/views.py

+#!/usr/bin/python
+# -*- coding:utf-8 -*-
+# This Python file uses the following encoding: utf-8
+
+__author__ = "Andreas Fritz - sources.e-blue.eu"
+__copyright__ = "Copyright (c) " + "28.08.2010" + " Andreas Fritz"
+__licence__ = """New BSD Licence"""
+__doc__ = """Dynamic donload links with timeout and maximum rate of ckicks.
+The content will served by a stream. Path of the file on the system is covered
+through the dynamicly generated pathname in the url.py """
+
+import os
+import presettings
+from django.http import HttpResponse, HttpResponseNotFound
+from django.shortcuts import render_to_response
+from django.template import RequestContext
+import mimetypes
+from django.views.decorators.cache import cache_control
+from django.utils.translation import ugettext_lazy as _
+from models import Download, IsExpiredError
+
+
+def expired(key):
+    expired_objects = Download.objects.filter(active=False)
+    # check expired objects
+    for obj in expired_objects:
+        if key == obj.link_key:
+            return obj
+
+def active(key):
+    active_objects = Download.objects.filter(active=True)
+    # check active objects
+    for obj in active_objects:
+        if key == obj.link_key:
+            return obj
+
+def error(request, text=_(u'Sorry, your request is mot available')):
+    """returns the error page"""
+    extra_context = {'message': text}
+    template = 'dynamicLink/not_avallible.html'
+    return render_to_response(template, extra_context, context_instance=RequestContext(request))
+
+def site(request, offset):
+    """process site requests"""
+    offset = offset.split('-') # turn offset to keylist
+    obj = {'actives':[], 'expired':[], 'notexist':[]}
+    # Test if keys are valid
+    for key in offset:
+        if active(key):
+            obj['actives'].append(active(key))
+        elif expired(key):
+            obj['expired'].append(expired(key))
+        else:
+            obj['notexist'].append(key)
+    template = 'dynamicLink/provide.html'
+    extra_context = {'basepath': presettings.DYNAMIC_LINK_URL_BASE_COMPONENT, 'downloads':obj}
+    return render_to_response(template, extra_context, context_instance=RequestContext(request))
+
+def fetch(request, offset):
+    """process link requests. make desissions for every single download link"""
+    if active(offset):
+        return provide(request, offset)
+    elif expired(offset):
+        return error(request, presettings.TEXT_REQUEST_IS_EXPIRED)
+    else:
+        return error(request, presettings.TEXT_REQUEST_DOES_NOT_EXIST)
+
+@cache_control(private=True)
+def provide(request, key):
+    """
+    Return a download without the rael path to the served file. The content will
+    served by a stream.
+
+    The file will read in byte code to a socket wich will be used in the response
+    object. Headers in the response will set for the specific served content
+    referable to its type.
+    """
+    # Read database
+    # This code runs only with python >= 2.6
+#    stored_file_obj = Download.objects.get(link_key=key)
+#    try:
+#        filepath = stored_file_obj.get_path() # model method to validate and deliver path
+#    except IsExpiredError as e: # works only with python 2.6 or later (for a solution of older versions see below!)
+#        return error(request, e.value)
+
+    # alternate code that also run in older python before 2.6
+    stored_file_obj = Download.objects.get(link_key=key)
+    try:
+        filepath = stored_file_obj.get_path() # model method to validate and deliver path
+    except IsExpiredError: # works with pyhton before 2.6
+        return error(request)
+    
+    # make file path suitable for different installations
+    delimiter = presettings.DYNAMIC_LINK_MEDIA.strip('/').split('/')[-1]
+    # now we use the objects get_paht() method to be sure the object instance keep up to date.
+    file_path = os.path.normpath(presettings.DYNAMIC_LINK_MEDIA + '/' + filepath.split(delimiter)[-1])
+
+    # read file as binary
+    try:
+        fsocket = open(file_path, 'rb') # garbage collector will deal with not closed fsocket
+    except IOError:
+        stored_file_obj.active = False
+        stored_file_obj.save() # only raise the following once
+        return HttpResponseNotFound(unicode(_(u'File not found!'))) # admin will get informed by mail
+
+#    # read file as binary
+#    try:
+#        f = open(file_path, 'rb')
+#    except IOError:
+#        stored_file_obj.active = False
+#        stored_file_obj.save() # only raise the following once
+#        return HttpResponseNotFound(unicode(_(u'File not found!'))) # admin will get informed by mail
+#    fsocket = f.read()
+#    f.close()
+
+    # get file parameters
+    file_name = os.path.basename(file_path)
+    file_size = os.path.getsize(file_path)
+
+    # specify mimetype and encoding
+    auto_mimetype, auto_encoding = mimetypes.guess_type(file_path)
+    if not auto_mimetype: # for unknown types use stream
+        auto_mimetype = 'application/octet-stream'
+
+    # response object
+    response = HttpResponse(fsocket, mimetype=auto_mimetype) # object instance with mimetype and file
+    # set headers in the response object
+    # a list of headers: http://en.wikipedia.org/wiki/List_of_HTTP_header_fields
+    # encode('utf-8') assuming you're running on a server with UTF-8 as the filesystem encoding.
+    response['Content-Disposition'] = 'attachment; filename=%s' % file_name.encode('utf-8') # add correct filename
+    if auto_encoding and auto_encoding is not 'gzip':
+        # set encoding but exclude gzip from encoding headers
+        # GZip uses zlib, but on its own zlib produces content that's improperly
+        # encoded for a browser seeing 'gzip' as the content encoding.
+        response['Content-Encoding'] = auto_encoding
+    response['Content-Length'] = file_size # set response file size for the browsers progress bar
+    
+    return response

Binary file added.

example/#dl_settings.py

+#!/usr/bin/python
+# -*- coding:utf-8 -*-
+# This Python file uses the following encoding: utf-8
+
+#import os
+#import settings
+
+# CHANGE THE PRESETTINGS OF DJANGO-DYNAMIC-LINK BY EDITING THIS FILE AND
+# RENAMING IT TO dl_settings.py
+
+
+# A path to a directory from witch walk down so you can choose your files.
+# Make one active and edit
+
+#DYNAMIC_LINK_MEDIA = os.path.join(settings.MEDIA_ROOT, 'your-custom-subfolders')
+#DYNAMIC_LINK_MEDIA = '/complete/path/to/your/custom/subfolders'
+
+
+# A string that modify the serve url path:
+# /www.example.com/DYNAMIC_LINK_URL_BASE_COMPONENT/link/3839hd8HKl3/example.zip
+# Make it active and edit
+
+#DYNAMIC_LINK_URL_BASE_COMPONENT = 'your-expression'

Empty file added.

Binary file added.

example/manage.py

+#!/usr/bin/env python
+from django.core.management import execute_manager
+try:
+    import settings # Assumed to be in the same directory.
+except ImportError:
+    import sys
+    sys.stderr.write("Error: Can't find the file 'settings.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n(If the file settings.py does indeed exist, it's causing an ImportError somehow.)\n" % __file__)
+    sys.exit(1)
+
+if __name__ == "__main__":
+    execute_manager(settings)
Add a comment to this file

example/media/django-logo-negative.png.tar.gz

Binary file added.

Add a comment to this file

example/media/django-logo-negative.svg.zip

Binary file added.

Add a comment to this file

example/media/django-logo-positive.png.tar.gz

Binary file added.

Add a comment to this file

example/media/django-logo-positive.svg.zip

Binary file added.

example/settings.py

+# Django settings for example project.
+
+DEBUG = True
+TEMPLATE_DEBUG = DEBUG
+
+ADMINS = (
+    # ('Your Name', 'your_email@domain.com'),
+)
+
+MANAGERS = ADMINS
+
+import os
+dirname = os.path.dirname
+SITECODE_DIRECTORY_PATH = dirname(__file__)
+
+DATABASES = {
+    'default': {
+        'ENGINE': 'django.db.backends.sqlite3', # Add 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'.
+        'NAME': os.path.join(SITECODE_DIRECTORY_PATH, 'example.db'),  # Or path to database file if using sqlite3.
+        'USER': '',                      # Not used with sqlite3.
+        'PASSWORD': '',                  # Not used with sqlite3.
+        'HOST': '',                      # Set to empty string for localhost. Not used with sqlite3.
+        'PORT': '',                      # Set to empty string for default. Not used with sqlite3.
+    }
+}
+
+# Local time zone for this installation. Choices can be found here:
+# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
+# although not all choices may be available on all operating systems.
+# On Unix systems, a value of None will cause Django to use the same
+# timezone as the operating system.
+# If running in a Windows environment this must be set to the same as your
+# system time zone.
+TIME_ZONE = 'America/Chicago'
+
+# Avalible (natural) laguages in this project
+LANGUAGES = (
+             ('en', 'English'),
+             ('de', 'Deutsch'),
+             )
+
+# Language code for this installation. All choices can be found here:
+# http://www.i18nguy.com/unicode/language-identifiers.html
+LANGUAGE_CODE = 'en'
+
+SITE_ID = 1
+
+# If you set this to False, Django will make some optimizations so as not
+# to load the internationalization machinery.
+USE_I18N = True
+
+# If you set this to False, Django will not format dates, numbers and
+# calendars according to the current locale
+USE_L10N = True
+
+# Absolute filesystem path to the directory that will hold user-uploaded files.
+# Example: "/home/media/media.lawrence.com/"
+MEDIA_ROOT = os.path.join(SITECODE_DIRECTORY_PATH, 'media')
+
+# URL that handles the media served from MEDIA_ROOT. Make sure to use a
+# trailing slash if there is a path component (optional in other cases).
+# Examples: "http://media.lawrence.com", "http://example.com/media/"
+MEDIA_URL = 'sitemedia/'
+
+# URL prefix for admin media -- CSS, JavaScript and images. Make sure to use a
+# trailing slash.
+# Examples: "http://foo.com/media/", "/media/".
+ADMIN_MEDIA_PREFIX = '/media/'
+
+# Make this unique, and don't share it with anybody.
+SECRET_KEY = 'skd9^9qd6a#n*kkf7dv)8l6vzj=#irc64-!9fn=$_^^ty$g3vl'
+
+# List of callables that know how to import templates from various sources.
+TEMPLATE_LOADERS = (
+    'django.template.loaders.filesystem.Loader',
+    'django.template.loaders.app_directories.Loader',
+#     'django.template.loaders.eggs.Loader',
+)
+
+MIDDLEWARE_CLASSES = (
+    'django.middleware.common.CommonMiddleware',
+    'django.contrib.sessions.middleware.SessionMiddleware',
+
+    'django.middleware.locale.LocaleMiddleware', # is needet for django-dynamic-link
+    'django.contrib.redirects.middleware.RedirectFallbackMiddleware', # is needet for django-dynamic-link
+
+    'django.middleware.csrf.CsrfViewMiddleware',
+    'django.contrib.auth.middleware.AuthenticationMiddleware',
+    'django.contrib.messages.middleware.MessageMiddleware',
+)
+
+ROOT_URLCONF = 'example.urls'
+
+TEMPLATE_DIRS = (
+    # Put strings here, like "/home/html/django_templates" or "C:/www/django/templates".
+    # Always use forward slashes, even on Windows.
+    # Don't forget to use absolute paths, not relative paths.
+    os.path.join(SITECODE_DIRECTORY_PATH, 'templates'),
+)
+
+TEMPLATE_CONTEXT_PROCESSORS = (
+    'django.contrib.auth.context_processors.auth', # is needet for django-dynamic-link
+    'django.core.context_processors.request', # is needet for django-dynamic-link
+)
+
+INSTALLED_APPS = (
+    'django.contrib.auth',
+    'django.contrib.contenttypes',
+    'django.contrib.sessions',
+    'django.contrib.sites',
+    'django.contrib.messages',
+    'django.contrib.redirects', # is needet for django-dynamic-link
+
+    # Uncomment the next line to enable the admin:
+    'django.contrib.admin', # is needet for django-dynamic-link
+
+    # Uncomment the next line to enable admin documentation:
+    'django.contrib.admindocs', # is needet for django-dynamic-link
+    'dynamicLink', # is needet for django-dynamic-link
+)

Binary file added.

example/templates/base.html

+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+<title>example</title>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<meta http-equiv="content-type" content="application/xhtml+xml; charset=UTF-8">
+<meta http-equiv="content-style-type" content="text/css">
+</head>
+<body>
+
+{% load i18n %}
+
+<h1>{% block title %}{% endblock %}</h1>
+
+{% block content %}{% endblock %}
+
+</body>
+</html>
+
+

example/templates/home.html

+{% extends 'base.html' %}
+{% load i18n %}
+
+{% block title %}
+    {% trans "django-dynamic-link example site" %}
+{% endblock %}
+
+{% block content %}
+
+<b>{% trans "Navigation" %}:<br/></b>
+    
+    <a href="/admin/" name="Go to admin">{% trans "Go to admin" %}.</a>
+<br/>
+
+{% endblock %}
+from django.conf.urls.defaults import *
+from django.views.generic.simple import direct_to_template
+
+# Uncomment the next two lines to enable the admin:
+from django.contrib import admin
+admin.autodiscover()
+
+from dynamicLink import presettings
+
+urlpatterns = patterns('',
+    # Example:
+    (r'^$', direct_to_template, {'template': 'home.html'}),
+
+    # for django-dynamic-link. By default it catch url/serve/some-dynamic-link/
+    (r'^\w+/%s/' % presettings.DYNAMIC_LINK_URL_BASE_COMPONENT, include('dynamicLink.urls')),
+
+    # Uncomment the admin/doc line below to enable admin documentation:
+    (r'^admin/doc/', include('django.contrib.admindocs.urls')),
+
+    # Uncomment the next line to enable the admin:
+    (r'^admin/(.*)', admin.site.root),
+)

Binary file added.

+#!/usr/bin/python
+# -*- coding:utf-8 -*-
+# This Python file uses the following encoding: utf-8
+
+from setuptools import setup, find_packages
+import sys
+from dynamicLink import version
+import os
+
+# Read the version from a project file
+VERSION = version.VERSION_str
+
+# Get description from Readme file
+long_description = open(os.path.join(os.path.dirname(__file__), 'dynamicLink', 'README')).read()
+
+# Build a list with requirements of the app
+REQUIRES = ['setuptools']
+if sys.version_info < (2, 4):
+    REQUIRES.append('python >= 2.4')
+try:
+    import django
+    if django.VERSION < (1, 1):
+        REQUIRES.extend(['django >= 1.1', 'django < 1.3'])
+except ImportError:
+    REQUIRES.extend(['django >= 1.1', 'django < 1.3'])
+
+setup(name='django-dynamic-link',
+      version=VERSION,
+      description='A django file streaming application',
+      long_description=long_description,
+      author='Andreas Fritz',
+      author_email='djangp-dynamic-link@bitzeit.eu',
+      url='http://www.sources.e-blue.eu/de/pages/django/',
+      download_url='https://bitbucket.org/catapela/django-dynamic-link/downloads',
+      license='BSD',
+      packages=find_packages(exclude=['example',]),
+      include_package_data=True,
+      keywords="django file streaming dynamic links serve",
+      classifiers=[
+              'Development Status :: 4 - Beta',
+              'Framework :: Django',
+              'License :: OSI Approved :: BSD License',
+              'Operating System :: OS Independent',
+              'Programming Language :: Python',
+              'Environment :: Console',
+              'Natural Language :: English',
+              'Natural Language :: German',
+              'Intended Audience :: Developers',
+              'Intended Audience :: Information Technology',
+              'Topic :: Internet',
+              'Topic :: Utilities',
+              ],
+      install_requires = REQUIRES,
+      zip_safe=False,
+      )
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.