├── .editorconfig ├── .gitignore ├── LICENSE.md ├── README.md ├── __init__.py ├── db └── .gitignore ├── fileshack ├── __init__.py ├── admin.py ├── apps.py ├── compat │ ├── __init__.py │ └── timezone.py ├── models.py ├── static │ └── fileshack │ │ ├── css │ │ ├── accesscode.css │ │ ├── base.css │ │ ├── fileshack.css │ │ ├── plain.css │ │ ├── progress.css │ │ └── unsubscribe.css │ │ ├── img │ │ ├── box-selected.png │ │ ├── box-selected.svg │ │ ├── box.png │ │ ├── box.svg │ │ ├── button-blue-tall.png │ │ ├── button-blue.png │ │ ├── button-green.png │ │ ├── button-lightblue.png │ │ ├── button-lightgreen.png │ │ ├── button-lightred.png │ │ ├── button-lightsilver-tall.png │ │ ├── button-lightsilver.png │ │ ├── button-red.png │ │ ├── button-watch-submit-active.png │ │ ├── button-watch-submit.png │ │ ├── button-watcher-delete-active.png │ │ ├── button-watcher-delete.png │ │ ├── doc.png │ │ ├── error-close.png │ │ ├── error-close.svg │ │ ├── error-wide.png │ │ ├── error-wide.svg │ │ ├── error.png │ │ ├── error.svg │ │ ├── info-close.png │ │ ├── info-close.svg │ │ ├── info-wide.png │ │ ├── info-wide.svg │ │ ├── info.png │ │ ├── info.svg │ │ ├── input-blue-tall.png │ │ ├── progress-indeterminate.png │ │ ├── progress-indeterminate.svg │ │ ├── progress.png │ │ ├── progress.svg │ │ └── watch-dialog-top.png │ │ └── js │ │ ├── accesscode.js │ │ ├── collections.js │ │ ├── fileshack.js │ │ ├── misc.js │ │ ├── models.js │ │ ├── mootools-more.js │ │ ├── mootools.js │ │ ├── progress.js │ │ └── views.js ├── templates │ └── fileshack │ │ ├── 404.html │ │ ├── 500.html │ │ ├── accesscode.html │ │ ├── base.html │ │ ├── iframe.html │ │ ├── index.html │ │ └── unsubscribe.html ├── templatetags │ ├── __init__.py │ └── staticfiles.py ├── urls.py └── views.py ├── fileshackproject ├── __init__.py ├── settings.py ├── settings_local-example.py ├── urls.py ├── wsgi.py └── wsgi_virtualenv.py ├── manage.py ├── media └── .gitignore └── static └── .gitignore /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | insert_final_newline = true 6 | 7 | [*.{py,html,css,js}] 8 | charset = utf-8 9 | indent_style = space 10 | indent_size = 4 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.komodoproject 3 | .komodotools 4 | *~ 5 | /fileshackproject/settings_local.py 6 | /django 7 | /db/fileshack.sqlite 8 | 9 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012-2022 Peter Kuma 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Please visit [fileshack.peterkuma.net](https://fileshack.peterkuma.net) for more information. 2 | -------------------------------------------------------------------------------- /__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterkuma/fileshackproject/3676f17f48a219aa31ca564ab8747093586fb8a1/__init__.py -------------------------------------------------------------------------------- /db/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | 4 | -------------------------------------------------------------------------------- /fileshack/__init__.py: -------------------------------------------------------------------------------- 1 | default_app_config = 'fileshack.apps.Fileshack' 2 | -------------------------------------------------------------------------------- /fileshack/admin.py: -------------------------------------------------------------------------------- 1 | from .models import * 2 | from django.contrib import admin 3 | 4 | class StoreAdmin(admin.ModelAdmin): 5 | list_display = ("__str__",) 6 | 7 | class ItemAdmin(admin.ModelAdmin): 8 | pass 9 | 10 | admin.site.register(Store, StoreAdmin) 11 | admin.site.register(Item, ItemAdmin) 12 | 13 | -------------------------------------------------------------------------------- /fileshack/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | from django.db.models import signals 3 | 4 | def create_default_store(sender, app_config, verbosity, **kwargs): 5 | # Only create the default sites in databases where Django created the table. 6 | if verbosity >= 2: 7 | print("Creating default Store object") 8 | from .models import Store 9 | Store().save() 10 | 11 | class Fileshack(AppConfig): 12 | name = 'fileshack' 13 | verbose_name = 'Fileshack' 14 | 15 | def ready(self): 16 | signals.post_migrate.connect(create_default_store, sender=self) 17 | -------------------------------------------------------------------------------- /fileshack/compat/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterkuma/fileshackproject/3676f17f48a219aa31ca564ab8747093586fb8a1/fileshack/compat/__init__.py -------------------------------------------------------------------------------- /fileshack/compat/timezone.py: -------------------------------------------------------------------------------- 1 | # Compatibility with Django 1.3. 2 | 3 | from datetime import datetime 4 | 5 | def now(): 6 | return datetime.now() 7 | -------------------------------------------------------------------------------- /fileshack/models.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2012-2020 Peter Kuma 2 | # 3 | # Permission is hereby granted, free of charge, to any person obtaining a copy 4 | # of this software and associated documentation files (the "Software"), to deal 5 | # in the Software without restriction, including without limitation the rights 6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | # copies of the Software, and to permit persons to whom the Software is 8 | # furnished to do so, subject to the following conditions: 9 | # 10 | # The above copyright notice and this permission notice shall be included in 11 | # all copies or substantial portions of the Software. 12 | # 13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | # THE SOFTWARE. 20 | 21 | from django.db.models import * 22 | from django.utils.translation import gettext_lazy as _ 23 | from django.core.files.storage import default_storage 24 | from django.conf import settings 25 | from django.urls import reverse 26 | 27 | import os 28 | from random import choice 29 | from datetime import datetime as dt 30 | from datetime import timedelta 31 | import base64 32 | import hashlib, hmac 33 | import urllib 34 | 35 | class Store(Model): 36 | path = CharField(_("path"), help_text=_("Path relative to the base URL under which the store is accessible. Leave empty if not sure."), unique=True, blank=True, max_length=200) 37 | media = CharField(_("media"), help_text=_("Directiory under MEDIA_PATH into which files will be uploaded. Leave empty if not sure."), blank=True, max_length=200) 38 | accesscode = CharField(_("access code"), help_text=_("Protect access to the store by an access code."), max_length=200, blank=True) 39 | item_limit = IntegerField(_("item size limit"), help_text=_("Limit the size of a single file (MB)."), default=0) 40 | store_limit = IntegerField(_("store size limit"), help_text=_("Limit the size of the entire store (MB)."), default=0) 41 | protect_files = BooleanField(_("protect files"), help_text=_("Protect files by a random string, so that they cannot be downloaded by guessing their name."), default=True) 42 | allow_watch = BooleanField(_("allow watch"), help_text=_('Allow users to subscribe to receive e-mail updates. Requires cron (see documentation).'), default=False) 43 | watch_delay = PositiveIntegerField(_("watch delay"), help_text=_("Minimum delay between two notifications in minutes. Only applies when Allow watch is enabled."), default=360) 44 | 45 | def __str__(self): 46 | url = self.get_absolute_url() 47 | if url.startswith("/"): 48 | url = url[1:] 49 | return "default" if url == "" else url 50 | 51 | def get_absolute_url(self): 52 | url = reverse("fileshack:index", kwargs=dict(store_path=self.path)) 53 | url = urllib.parse.unquote(url) 54 | if url.endswith("//"): url = url[:-1] # Ugly hack. 55 | return url 56 | 57 | def total(self): 58 | if self.items.count() == 0: return 0 59 | return self.items.all().aggregate(Sum("size"))["size__sum"] 60 | 61 | def item_upload_to(instance, filename): 62 | key = "" 63 | if(instance.store.protect_files): 64 | chars = "abcdefghijklmnopqrstuvwxyz0123456789" 65 | key = "".join([choice(chars) for i in range(10)]) 66 | return os.path.join(instance.store.media, key, filename) 67 | 68 | class Item(Model): 69 | store = ForeignKey(Store, 70 | verbose_name=_("store"), 71 | related_name="items", 72 | on_delete=CASCADE 73 | ) 74 | fileobject = FileField(_("file"), upload_to=item_upload_to) 75 | created = DateTimeField(_("created"), auto_now_add=True) 76 | uploaded = DateTimeField(_("uploaded"), auto_now_add=True) 77 | modified = DateTimeField(_("modified"), auto_now=True) 78 | size_total = IntegerField(_("size total"), default=0) 79 | size = IntegerField(_("size"), default=0) 80 | 81 | def delete(self): 82 | dir = os.path.dirname(os.path.join(settings.MEDIA_ROOT, self.fileobject.name)) 83 | try: self.fileobject.delete() 84 | except OSError: pass 85 | try: os.rmdir(dir) 86 | except OSError: pass 87 | return Model.delete(self) 88 | 89 | def status(self): 90 | if self.size < self.size_total and dt.now(self.modified.tzinfo) - self.modified > timedelta(seconds=10): 91 | return 'STALE' 92 | elif self.size < self.size_total: 93 | return 'UPLOADING' 94 | else: 95 | return 'READY' 96 | 97 | def exists(self): 98 | return self.fileobject and default_storage.exists(self.fileobject.path) 99 | 100 | def get_absolute_url(self): 101 | if self.status() == "READY": 102 | return self.fileobject.url 103 | elif self.status() == "UPLOADING": 104 | return reverse("fileshack:download", kwargs={ 105 | "store_path": "" if self.store.path == "" else self.store.path+"/", 106 | "item_id": self.id, 107 | }) 108 | else: 109 | return "" 110 | 111 | def name(self): 112 | try: 113 | return os.path.basename(self.fileobject.name) 114 | except (OSError,ValueError): 115 | return None 116 | 117 | def size_human(self): 118 | size = self.size() 119 | for u in ("B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"): 120 | unit = u 121 | if size < 1024: 122 | break 123 | size = size/1024.0 124 | if unit == "B" or unit == "KB": 125 | return u"%.0f %s" % (size, unit) 126 | else: 127 | return u"%.1f %s" % (size, unit) 128 | 129 | def simple(self): 130 | return { 131 | "id": self.id, 132 | "status": self.status(), 133 | "name": self.name(), 134 | "url": self.get_absolute_url(), 135 | "size": self.size, 136 | "size_total": self.size_total, 137 | "modified": self.modified, 138 | "created": self.created, 139 | "uploaded": self.uploaded, 140 | } 141 | 142 | def __str__(self): 143 | return self.name() 144 | 145 | class Meta: 146 | ordering = [('created')] 147 | 148 | class User(Model): 149 | email = EmailField(_("e-mail"), max_length=254, unique=True) 150 | created = DateTimeField(_("created"), auto_now_add=True) 151 | modified = DateTimeField(_("modified"), auto_now=True) 152 | last_notification = DateTimeField(_("last notification"), null=True, blank=True) 153 | 154 | def unsubscribe_hmac(self): 155 | h = hmac.HMAC(settings.SECRET_KEY.encode('utf-8')) 156 | h.update(b"unsubscribe:"+self.email.encode('utf-8')) 157 | return base64.urlsafe_b64encode(h.digest()) 158 | 159 | def unsubscribe_url(self): 160 | return reverse("fileshack:unsubscribe") + "?" + urllib.parse.urlencode( 161 | {"u": self.email, "hmac": self.unsubscribe_hmac()} 162 | ) 163 | 164 | class Meta: 165 | verbose_name = _("user") 166 | verbose_name_plural = ("users") 167 | ordering = [("created")] 168 | 169 | 170 | class Watcher(Model): 171 | user = ForeignKey(User, 172 | verbose_name=_("user"), 173 | related_name="watchers", 174 | on_delete=CASCADE 175 | ) 176 | store = ForeignKey(Store, 177 | verbose_name=_("store"), 178 | related_name="watchers", 179 | on_delete=CASCADE 180 | ) 181 | created = DateTimeField(_("created"), auto_now_add=True) 182 | modified = DateTimeField(_("modified"), auto_now=True) 183 | 184 | def simple(self): 185 | return { 186 | "id": self.id, 187 | "email": self.user.email, 188 | "last_notification": self.user.last_notification, 189 | "created": self.created, 190 | } 191 | 192 | class Meta: 193 | verbose_name = _("watcher") 194 | verbose_name_plural = _("watchers") 195 | ordering = [("created")] 196 | unique_together = ("user", "store") 197 | -------------------------------------------------------------------------------- /fileshack/static/fileshack/css/accesscode.css: -------------------------------------------------------------------------------- 1 | #preload-1 { width: 0; height: 0; background: url(../img/input-blue-tall.png); } 2 | #preload-2 { width: 0; height: 0; background: url(../img/button-blue-tall.png); } 3 | 4 | h1 { 5 | margin-top: 150px; 6 | } 7 | 8 | #container { 9 | text-align: center; 10 | } 11 | 12 | #accesscode-form { 13 | margin: 0 auto; 14 | width: 525px; 15 | } 16 | 17 | #accesscode-form input[name="accesscode"] { 18 | width: 400px; 19 | height: 44px; 20 | border-radius: 5px; 21 | -moz-border-radius: 5px; 22 | -webkit-border-radius: 5px; 23 | -o-border-radius: 5px; 24 | padding: 6px; 25 | border: 3px solid #dddddd; 26 | font-size: 24pt; 27 | } 28 | 29 | #accesscode-form input[name="accesscode"]:hover, 30 | #accesscode-form input[name="accesscode"]:focus, 31 | #accesscode-form input[name="accesscode"].active { 32 | border: 3px solid #2dacf8; 33 | background: url(../img/input-blue-tall.png); 34 | outline: none; 35 | } 36 | 37 | #accesscode-form button[type="submit"] { 38 | display: block; 39 | float: right; 40 | width: 80px; 41 | height: 62px; 42 | font-size: 20pt; 43 | color: #bbb; 44 | border: 3px solid #dddddd; 45 | border-radius: 5px; 46 | -moz-border-radius: 5px; 47 | -webkit-border-radius: 5px; 48 | -o-border-radius: 5px; 49 | background: url("../img/button-lightsilver-tall.png") repeat-x; 50 | } 51 | 52 | #accesscode-form button[type="submit"].active { 53 | color: black; 54 | border: 3px solid #0088cb; 55 | background: url(../img/button-blue-tall.png); 56 | outline: none; 57 | } 58 | 59 | #error { 60 | width: 600px; 61 | height: 162px; 62 | margin: -8px auto 0 auto; 63 | text-align: left; 64 | background: url(../img/error.png) no-repeat; 65 | position: relative; 66 | } 67 | 68 | #error .label { 69 | font-size: 23pt; 70 | padding: 52px 0 2px 30px; 71 | } 72 | 73 | #error .message { 74 | font-size: 19pt; 75 | color: #fdeeee; 76 | padding: 0 0 15px 30px; 77 | } 78 | 79 | @media (max-width: 820px) { 80 | h1 { 81 | margin-top: 50px; 82 | } 83 | 84 | #accesscode-form { 85 | width: 285px; 86 | } 87 | 88 | #accesscode-form input[name="accesscode"] { 89 | width: 185px; 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /fileshack/static/fileshack/css/base.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: 'Istok Web', helvetica, 'Nimbus Sans', Arial, sans-serif; 3 | font-size: 12pt; 4 | text-align: center; 5 | } 6 | 7 | #container { 8 | margin: 0 auto; 9 | text-align: left; 10 | width: 820px; 11 | max-width: 100%; 12 | min-width: 360px; 13 | } 14 | 15 | h1 { 16 | font-size: 28pt; 17 | margin-top: 40px; 18 | margin-bottom: 5%; 19 | } 20 | 21 | h1 span { 22 | font-size: 22pt; 23 | font-family: helvetica, 'Nimbus Sans', Arial, sans-serif; 24 | font-weight: normal; 25 | color: #888a85; 26 | } 27 | 28 | input { 29 | font-size: inherit; 30 | } 31 | 32 | button { 33 | border-radius: 5px; 34 | -moz-border-radius: 5px; 35 | -webkit-border-radius: 5px; 36 | -o-border-radius: 5px; 37 | font-size: inherit; 38 | font-weight: bold; 39 | padding: 5px 10px; 40 | cursor: pointer; 41 | font-family: helvetica, "Nimbus Sans", Arial, sans-serif; 42 | border: 1px solid rgba(0,0,0,0.2); 43 | text-shadow: 1px 1px 0 rgba(255, 255, 255, 0.4); 44 | color: silver; 45 | background-repeat: repeat-x; 46 | background-image: url("../img/button-lightsilver.png"); 47 | } 48 | 49 | button:hover,button:focus { 50 | box-shadow: 0 0 1px 1px rgba(0, 0, 0, 0.1); 51 | -moz-box-shadow: 0 0 1px 1px rgba(0, 0, 0, 0.1); 52 | -webkit-box-shadow: 0 0 1px 1px rgba(0, 0, 0, 0.1); 53 | -o-box-shadow: 0 0 1px 1px rgba(0, 0, 0, 0.1); 54 | } 55 | 56 | button::-moz-focus-inner { 57 | border: none; 58 | } 59 | 60 | :invalid { 61 | box-shadow: none; 62 | } 63 | 64 | :-moz-submit-invalid { 65 | box-shadow: none; 66 | } 67 | 68 | :-moz-ui-invalid { 69 | box-shadow:none; 70 | } 71 | 72 | @media (max-width: 820px) { 73 | h1 { 74 | margin-top: 20px; 75 | line-height: 120%; 76 | } 77 | 78 | h1 span { 79 | display: block; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /fileshack/static/fileshack/css/fileshack.css: -------------------------------------------------------------------------------- 1 | /*@import url(http://fonts.googleapis.com/css?family=Istok+Web:400,700,400italic,700italic);*/ 2 | 3 | #preload-1 { width: 0; height: 0; background: url(../img/button-lightblue.png); } 4 | #preload-2 { width: 0; height: 0; background: url(../img/button-lightred.png); } 5 | #preload-3 { width: 0; height: 0; background: url(../img/box-selected.png); } 6 | #preload-4 { width: 0; height: 0; background: url(../img/button-green.png); } 7 | #preload-5 { width: 0; height: 0; background: url(../img/button-watch-submit-active.png); } 8 | #preload-6 { width: 0; height: 0; background: url(../img/button-watcher-delete-active.png); } 9 | 10 | body { 11 | text-align: center; 12 | margin: 5% 0; 13 | } 14 | 15 | #container { 16 | margin: 0 auto; 17 | text-align: left; 18 | width: 820px; 19 | max-width: 100%; 20 | min-width: 360px; 21 | box-sizing: border-box; 22 | padding: 0 20px; 23 | } 24 | 25 | #controls { 26 | float: right; 27 | margin: 12px 0; 28 | } 29 | 30 | form[action="logout/"] { 31 | float: right; 32 | margin: 0 0 0 10px; 33 | } 34 | 35 | form[action="logout/"] button:hover, 36 | form[action="logout/"] button:focus { 37 | background-image: url("../img/button-lightblue.png"); 38 | color: #003366; 39 | } 40 | 41 | #watchbtn { 42 | float: right; 43 | } 44 | 45 | #watchbtn:hover,#watchbtn:focus,#watchbtn.active { 46 | color: #015a01; 47 | background-image: url("../img/button-green.png"); 48 | box-shadow: 0 0 1px 1px rgba(0, 0, 0, 0.1); 49 | -moz-box-shadow: 0 0 1px 1px rgba(0, 0, 0, 0.1); 50 | -webkit-box-shadow: 0 0 1px 1px rgba(0, 0, 0, 0.1); 51 | -o-box-shadow: 0 0 1px 1px rgba(0, 0, 0, 0.1); 52 | } 53 | 54 | #watch-dialog { 55 | position: absolute; 56 | right: 0; 57 | top: 34px; 58 | z-index: 100; 59 | width: 255px; 60 | font-size: 12pt; 61 | } 62 | 63 | #watch-dialog .top { 64 | background: url("../img/watch-dialog-top.png") no-repeat center top; 65 | height: 16px; 66 | width: 100%; 67 | z-index: 40; 68 | position: relative; 69 | top: 1px; 70 | } 71 | 72 | #watch-dialog .dialog { 73 | width: 100%; 74 | background-color: white; 75 | border: 1px solid rgba(0,0,0,0.2); 76 | position: absolute; 77 | font-size: 10pt; 78 | box-shadow: 1px 1px 0 black; 79 | -moz-box-shadow: 1px 1px 0 black; 80 | -webkit-box-shadow: 1px 1px 0 black; 81 | -o-box-shadow: 1px 1px 0 black; 82 | border-radius: 1px; 83 | z-index: 20; 84 | } 85 | 86 | #watch-dialog .new { 87 | padding: 10px 8px; 88 | background: #f8f8f8; 89 | } 90 | 91 | #watch-dialog .new button[type="submit"] { 92 | display: block; 93 | width: 25px; 94 | height: 23px; 95 | position: relative; 96 | float: right; 97 | border-radius: 3px; 98 | background: url("../img/button-watch-submit.png") no-repeat center center; 99 | } 100 | 101 | #watch-dialog .new button[type="submit"].active { 102 | background: url("../img/button-watch-submit-active.png") no-repeat center center; 103 | border: 1px solid #0088cb; 104 | } 105 | 106 | #watch-dialog .new input[name="email"] { 107 | display: block; 108 | border: 1px solid rgba(0,0,0,0.2); 109 | border-radius: 3px; 110 | width: 198px; 111 | height: 20px; 112 | padding: 2px 5px 0px 5px; 113 | background-repeat: repeat-x; 114 | font-family: inherit; 115 | font-size: inherit; 116 | box-shadow: none; 117 | } 118 | 119 | #watch-dialog .new input[name="email"]:focus { 120 | outline: none; 121 | } 122 | 123 | #watch-dialog .new input[name="email"]:disabled { 124 | background: #eee; 125 | } 126 | 127 | #watch-error { 128 | background: #f85050; 129 | height: 20px; 130 | padding: 10px 15px; 131 | } 132 | 133 | #watch-error a { 134 | 135 | } 136 | 137 | #watch-error .details { 138 | float: right; 139 | } 140 | 141 | #watch-dialog .watcher { 142 | padding: 10px 10px 10px 12px; 143 | border-top: 1px solid rgba(0, 0, 0, 0.2); 144 | } 145 | 146 | #watch-dialog .watcher:hover .deletebtn { 147 | background: url("../img/button-watcher-delete-active.png") no-repeat center center; 148 | } 149 | 150 | #watch-dialog .watcher .email { 151 | width: 200px; 152 | text-overflow: ellipsis; 153 | overflow: hidden; 154 | line-height: 20px; 155 | } 156 | 157 | #watch-dialog .deletebtn { 158 | float: right; 159 | display: block; 160 | border: none; 161 | background: url("../img/button-watcher-delete.png") no-repeat center center; 162 | width: 12px; 163 | height: 20px; 164 | } 165 | 166 | #watch-dialog .deletebtn:hover, 167 | #watch-dialog .deletebtn:focus 168 | { 169 | box-shadow: none; 170 | -moz-box-shadow: none; 171 | -webkit-box-shadow: none; 172 | -o-box-shadow: none; 173 | } 174 | 175 | #dropbox { 176 | position: relative; 177 | background: #dcf1fe; 178 | width: 100%; 179 | height: 100px; 180 | border-radius: 10px; 181 | -moz-border-radius: 10px; 182 | -webkit-border-radius: 10px; 183 | -o-border-radius: 10px; 184 | color: #7da0a1; 185 | text-align: center; 186 | font-size: 18pt; 187 | font-family: helvetica, 'Nimbus Sans', Arial, sans-serif; 188 | line-height: 100px; 189 | -moz-transition: background 0.2s; 190 | -o-transition: background 0.2s; 191 | -webkit-transition: background 0.2s; 192 | -ms-transition: background 0.2s; 193 | transition: background 0.2s; 194 | margin-bottom: 25px; 195 | } 196 | 197 | #dropbox:hover { 198 | background: #c0e6fd; 199 | -moz-transition: background 0.2s; 200 | -o-transition: background 0.2s; 201 | -webkit-transition: background 0.2s; 202 | -ms-transition: background 0.2s; 203 | transition: background 0.2s; 204 | } 205 | 206 | #dropbox-file { 207 | visibility: hidden; 208 | position: absolute; 209 | top: 0px; 210 | left: 0px; 211 | text-align: center; 212 | width: 100%; 213 | font-size: 12pt; 214 | } 215 | 216 | .box { 217 | display: block; 218 | text-decoration: none; 219 | color: inherit; 220 | height: 80px; 221 | padding: 18px 20px; 222 | margin: 10px auto; 223 | border: 0.5px solid #888a85; 224 | border-radius: 8px; 225 | box-shadow: 1px 1px #d3d7cf; 226 | position: relative; 227 | background: url("../img/doc.png") 20px center/67px no-repeat; 228 | } 229 | 230 | .item.stale .box { 231 | cursor: default; 232 | } 233 | 234 | .item .desc { display: none; } 235 | .item .size { display: none; } 236 | .item .deletebtn { display: none; } 237 | .item .buttons { display: none; } 238 | .item .progress_container { display: none; } 239 | .item .progressbar { display: none; } 240 | .item .progressbar_alt { display: none; } 241 | 242 | .item.complete .desc { display: block; } 243 | .item.complete .size { display: block; } 244 | .item.complete .deletebtn { display: block; } 245 | 246 | .item.pending .buttons { display: block; } 247 | .item.pending .progress_container { display: block; } 248 | .item.pending .progressbar { display: block; } 249 | 250 | .item.unfinished .progress_container { display: block; } 251 | .item.unfinished .progressbar { display: block; } 252 | 253 | .item.stale .deletebtn { display: block; } 254 | .item.stale .progress_container { display: block; } 255 | .item.stale .progressbar { display: block; } 256 | 257 | .box:hover,.box:focus,.item.selected .box,.unfinished-item.selected .box,.foreign-item.selected .box { 258 | background: url("../img/doc.png") 20px center/67px no-repeat, linear-gradient(#ffffff, #fafafa); 259 | } 260 | 261 | .item .name { 262 | font-size: 18pt; 263 | margin-top: 5px; 264 | margin-left: 85px; 265 | margin-bottom: 10px; 266 | white-space: nowrap; 267 | overflow: hidden; 268 | text-overflow: ellipsis; 269 | line-height: 150%; 270 | position: absolute; 271 | width: calc(100% - 260px); 272 | } 273 | 274 | .item.pending .name,.item.stale .name,.item.unfinished .name { 275 | width: calc(100% - 350px); 276 | } 277 | 278 | .item .progress_container { 279 | float: right; 280 | width: 200px; 281 | height: 100px; 282 | text-align: right; 283 | } 284 | 285 | .item .progress { 286 | color: #555753; 287 | font-size: 26pt; 288 | line-height: 150%; 289 | } 290 | 291 | .item.pending .progressbar,.item.stale .progressbar { 292 | width: calc(100% - 350px); 293 | height: 25px; 294 | margin-left: 85px; 295 | margin-top: 50px; 296 | position: absolute; 297 | } 298 | 299 | .item.unfinished .progressbar { 300 | width: calc(100% - 350px); 301 | height: 25px; 302 | margin-left: 85px; 303 | margin-top: 50px; 304 | position: absolute; 305 | } 306 | 307 | .item .progress_size,.item .progress_size { 308 | font-size: 14pt; 309 | color: #555753; 310 | line-height: 120%; 311 | } 312 | 313 | .item.pending .progress_size,.item.stale .progress_size { 314 | margin-right: -90px; 315 | } 316 | 317 | .item .buttons { 318 | width: 100px; 319 | height: 100px; 320 | float: right; 321 | text-align: right; 322 | } 323 | 324 | .item .pause { 325 | width: 90px; 326 | margin: 4px 0; 327 | } 328 | 329 | .item .pause:hover,.item .pause:focus { 330 | border: 1px solid #0088cb; 331 | background: url(../img/button-blue.png); 332 | } 333 | 334 | .item .cancelbtn { 335 | margin: 4px 0; 336 | } 337 | 338 | .item { 339 | display: block; 340 | color: inherit; 341 | text-decoration: inherit; 342 | } 343 | 344 | .item .desc { 345 | position: absolute; 346 | font-size: 12pt; 347 | margin-top: 55px; 348 | margin-left: 85px; 349 | color: #555753; 350 | line-height: 120%; 351 | } 352 | 353 | .item .size { 354 | font-size: 20pt; 355 | color: #555753; 356 | float: right; 357 | line-height: 150%; 358 | } 359 | 360 | .item .deletebtn { 361 | float: right; 362 | clear: right; 363 | margin-top: 6px; 364 | } 365 | 366 | .item.stale .deletebtn { 367 | margin-left: 20px; 368 | } 369 | 370 | .box:hover .deletebtn,.box:focus .deletebtn, 371 | .box:hover .cancelbtn,.box:focus .cancelbtn { 372 | background-image: url("../img/button-lightred.png"); 373 | color: #980101; 374 | } 375 | 376 | .error,.info { 377 | width: 770px; 378 | height: 62px; 379 | margin-left: -10px; 380 | background-repeat: no-repeat; 381 | background-position: 4px center; 382 | position: relative; 383 | top: -20px; 384 | padding: 44px 35px 44px 35px; 385 | } 386 | 387 | .error { color: black; background-image: url(../img/error-wide.png); } 388 | .info { color: #002b3d; background-image: url(../img/info-wide.png); } 389 | 390 | .error_label,.info_label { 391 | font-size: 22pt; 392 | margin-bottom: 4px; 393 | line-height: 150%; 394 | } 395 | 396 | .error_message,.info_message { 397 | font-size: 18pt; 398 | line-height: 140%; 399 | } 400 | 401 | .error_message { color: #fdeeee; } 402 | .info_message { color: #339900; } 403 | 404 | .error_message a,.info_message a { 405 | color: inherit; 406 | } 407 | 408 | .error_close,.error_close:hover,.error_close:focus, 409 | .info_close,.info_close:hover,.info_close:focus { 410 | float: right; 411 | background-color: transparent; 412 | background-repeat: no-repeat; 413 | background-position: center center; 414 | width: 35px; 415 | height: 35px; 416 | padding: 0; 417 | margin-right: -14px; 418 | border: none; 419 | box-shadow: none; 420 | -moz-box-shadow: none; 421 | -webkit-box-shadow: none; 422 | -o-box-shadow: none; 423 | } 424 | 425 | .error_close,.error_close:hover,.error_close:focus { 426 | background-image: url(../img/error-close.png); 427 | } 428 | 429 | .info_close,.info_close:hover,.info_close:focus { 430 | background-image: url(../img/info-close.png); 431 | } 432 | 433 | .error_details { 434 | position: absolute; 435 | bottom: 26px; 436 | right: 35px; 437 | color: white; 438 | font-size: 14pt; 439 | } 440 | 441 | .info_yes { 442 | float: right; 443 | } 444 | 445 | .info_no { 446 | float: right; 447 | margin-right: 8px; 448 | } 449 | 450 | .info_yes,.info_no,.info_yes:hover,.info_no:hover { 451 | background-image: url("../img/button-lightgreen.png"); 452 | color: rgba(0, 0, 0, 0.8); 453 | } 454 | 455 | .info_yes:hover,.info_no:hover { 456 | box-shadow: 0 0 2px 1px rgba(0, 0, 0, 0.3); 457 | -moz-box-shadow: 0 0 2px 1px rgba(0, 0, 0, 0.3); 458 | -webkit-box-shadow: 0 0 2px 1px rgba(0, 0, 0, 0.3); 459 | -o-box-shadow: 0 0 2px 1px rgba(0, 0, 0, 0.3); 460 | } 461 | 462 | @media (max-width: 820px) { 463 | #container { 464 | padding: 0 5px; 465 | } 466 | 467 | .box { 468 | padding: 9px 10px; 469 | background: none; 470 | } 471 | 472 | .box:hover,.box:focus,.item.selected .box,.unfinished-item.selected .box,.foreign-item.selected .box { 473 | background: linear-gradient(#ffffff, #fafafa); 474 | } 475 | 476 | .item .name { 477 | width: calc(100% - 150px); 478 | } 479 | 480 | .item.pending .name,.item.stale .name,.item.unfinished .name { 481 | width: calc(100% - 240px); 482 | } 483 | 484 | .item .name,.item .desc { 485 | margin-left: 0; 486 | } 487 | 488 | .item.pending .progressbar,.item.stale .progressbar,.item.unfinished .progressbar { 489 | margin-left: 0; 490 | width: calc(100% - 240px); 491 | } 492 | } 493 | -------------------------------------------------------------------------------- /fileshack/static/fileshack/css/plain.css: -------------------------------------------------------------------------------- 1 | h1 { 2 | margin-top: 150px; 3 | } 4 | 5 | #container { 6 | text-align: center; 7 | } 8 | 9 | #error { 10 | margin: -40px auto 0 auto; 11 | text-align: left; 12 | width: 600px; 13 | height: 162px; 14 | background: url(../img/error.png) no-repeat; 15 | } 16 | 17 | #error .label { 18 | font-size: 23pt; 19 | padding: 52px 0 2px 30px; 20 | } 21 | 22 | #error .message { 23 | font-size: 19pt; 24 | color: #fdeeee; 25 | padding: 0 0 15px 30px; 26 | } 27 | 28 | #error a { 29 | color: inherit; 30 | } 31 | 32 | #info { 33 | margin: -40px auto 0 auto; 34 | text-align: left; 35 | width: 600px; 36 | height: 164px; 37 | background: url(../img/info.png) no-repeat; 38 | } 39 | 40 | #info .label { 41 | font-size: 23pt; 42 | padding: 52px 0 2px 30px; 43 | } 44 | 45 | #info .message { 46 | font-size: 19pt; 47 | color: #339900; 48 | padding: 0 0 15px 30px; 49 | } 50 | 51 | #info a { 52 | color: inherit; 53 | } 54 | -------------------------------------------------------------------------------- /fileshack/static/fileshack/css/progress.css: -------------------------------------------------------------------------------- 1 | .progress-emulation { 2 | background-image: url("../img/progress.png"); 3 | background-repeat: no-repeat; 4 | background-position: -1000px center; 5 | border: 1px solid #888; 6 | border-radius: 3px; 7 | height: 25px; 8 | } 9 | 10 | .progress-emulation-indeterminate { 11 | background-image: url("../img/progress-indeterminate.png"); 12 | } 13 | -------------------------------------------------------------------------------- /fileshack/static/fileshack/css/unsubscribe.css: -------------------------------------------------------------------------------- 1 | h1 { 2 | margin-top: 150px; 3 | } 4 | 5 | #container { 6 | text-align: center; 7 | } 8 | -------------------------------------------------------------------------------- /fileshack/static/fileshack/img/box-selected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterkuma/fileshackproject/3676f17f48a219aa31ca564ab8747093586fb8a1/fileshack/static/fileshack/img/box-selected.png -------------------------------------------------------------------------------- /fileshack/static/fileshack/img/box-selected.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 28 | 35 | 45 | 47 | 51 | 55 | 56 | 67 | 69 | 73 | 77 | 78 | 89 | 91 | 95 | 99 | 100 | 110 | 112 | 116 | 120 | 121 | 122 | 140 | 142 | 143 | 145 | image/svg+xml 146 | 148 | 149 | 150 | 151 | 152 | 157 | 168 | 179 | 187 | 195 | 203 | 211 | 212 | 213 | -------------------------------------------------------------------------------- /fileshack/static/fileshack/img/box.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterkuma/fileshackproject/3676f17f48a219aa31ca564ab8747093586fb8a1/fileshack/static/fileshack/img/box.png -------------------------------------------------------------------------------- /fileshack/static/fileshack/img/box.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 28 | 35 | 45 | 47 | 51 | 55 | 56 | 67 | 69 | 73 | 77 | 78 | 89 | 91 | 95 | 99 | 100 | 101 | 119 | 121 | 122 | 124 | image/svg+xml 125 | 127 | 128 | 129 | 130 | 131 | 136 | 147 | 158 | 166 | 174 | 182 | 190 | 191 | 192 | -------------------------------------------------------------------------------- /fileshack/static/fileshack/img/button-blue-tall.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterkuma/fileshackproject/3676f17f48a219aa31ca564ab8747093586fb8a1/fileshack/static/fileshack/img/button-blue-tall.png -------------------------------------------------------------------------------- /fileshack/static/fileshack/img/button-blue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterkuma/fileshackproject/3676f17f48a219aa31ca564ab8747093586fb8a1/fileshack/static/fileshack/img/button-blue.png -------------------------------------------------------------------------------- /fileshack/static/fileshack/img/button-green.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterkuma/fileshackproject/3676f17f48a219aa31ca564ab8747093586fb8a1/fileshack/static/fileshack/img/button-green.png -------------------------------------------------------------------------------- /fileshack/static/fileshack/img/button-lightblue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterkuma/fileshackproject/3676f17f48a219aa31ca564ab8747093586fb8a1/fileshack/static/fileshack/img/button-lightblue.png -------------------------------------------------------------------------------- /fileshack/static/fileshack/img/button-lightgreen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterkuma/fileshackproject/3676f17f48a219aa31ca564ab8747093586fb8a1/fileshack/static/fileshack/img/button-lightgreen.png -------------------------------------------------------------------------------- /fileshack/static/fileshack/img/button-lightred.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterkuma/fileshackproject/3676f17f48a219aa31ca564ab8747093586fb8a1/fileshack/static/fileshack/img/button-lightred.png -------------------------------------------------------------------------------- /fileshack/static/fileshack/img/button-lightsilver-tall.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterkuma/fileshackproject/3676f17f48a219aa31ca564ab8747093586fb8a1/fileshack/static/fileshack/img/button-lightsilver-tall.png -------------------------------------------------------------------------------- /fileshack/static/fileshack/img/button-lightsilver.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterkuma/fileshackproject/3676f17f48a219aa31ca564ab8747093586fb8a1/fileshack/static/fileshack/img/button-lightsilver.png -------------------------------------------------------------------------------- /fileshack/static/fileshack/img/button-red.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterkuma/fileshackproject/3676f17f48a219aa31ca564ab8747093586fb8a1/fileshack/static/fileshack/img/button-red.png -------------------------------------------------------------------------------- /fileshack/static/fileshack/img/button-watch-submit-active.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterkuma/fileshackproject/3676f17f48a219aa31ca564ab8747093586fb8a1/fileshack/static/fileshack/img/button-watch-submit-active.png -------------------------------------------------------------------------------- /fileshack/static/fileshack/img/button-watch-submit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterkuma/fileshackproject/3676f17f48a219aa31ca564ab8747093586fb8a1/fileshack/static/fileshack/img/button-watch-submit.png -------------------------------------------------------------------------------- /fileshack/static/fileshack/img/button-watcher-delete-active.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterkuma/fileshackproject/3676f17f48a219aa31ca564ab8747093586fb8a1/fileshack/static/fileshack/img/button-watcher-delete-active.png -------------------------------------------------------------------------------- /fileshack/static/fileshack/img/button-watcher-delete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterkuma/fileshackproject/3676f17f48a219aa31ca564ab8747093586fb8a1/fileshack/static/fileshack/img/button-watcher-delete.png -------------------------------------------------------------------------------- /fileshack/static/fileshack/img/doc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterkuma/fileshackproject/3676f17f48a219aa31ca564ab8747093586fb8a1/fileshack/static/fileshack/img/doc.png -------------------------------------------------------------------------------- /fileshack/static/fileshack/img/error-close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterkuma/fileshackproject/3676f17f48a219aa31ca564ab8747093586fb8a1/fileshack/static/fileshack/img/error-close.png -------------------------------------------------------------------------------- /fileshack/static/fileshack/img/error-close.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 20 | 27 | 34 | 41 | 49 | 53 | 54 | 58 | 62 | 63 | 70 | 71 | 89 | 91 | 92 | 94 | image/svg+xml 95 | 97 | 98 | 99 | 100 | 101 | 106 | 113 | 120 | 127 | 134 | 141 | 148 | 149 | 150 | -------------------------------------------------------------------------------- /fileshack/static/fileshack/img/error-wide.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterkuma/fileshackproject/3676f17f48a219aa31ca564ab8747093586fb8a1/fileshack/static/fileshack/img/error-wide.png -------------------------------------------------------------------------------- /fileshack/static/fileshack/img/error-wide.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 21 | 23 | 30 | 37 | 44 | 51 | 58 | 66 | 70 | 71 | 75 | 79 | 80 | 81 | 99 | 101 | 102 | 104 | image/svg+xml 105 | 107 | 108 | 109 | 110 | 111 | 116 | 125 | 133 | 141 | 150 | 151 | 152 | -------------------------------------------------------------------------------- /fileshack/static/fileshack/img/error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterkuma/fileshackproject/3676f17f48a219aa31ca564ab8747093586fb8a1/fileshack/static/fileshack/img/error.png -------------------------------------------------------------------------------- /fileshack/static/fileshack/img/error.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 20 | 27 | 34 | 42 | 46 | 47 | 51 | 55 | 56 | 57 | 75 | 77 | 78 | 80 | image/svg+xml 81 | 83 | 84 | 85 | 86 | 87 | 92 | 101 | 109 | 117 | 126 | 127 | 128 | -------------------------------------------------------------------------------- /fileshack/static/fileshack/img/info-close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterkuma/fileshackproject/3676f17f48a219aa31ca564ab8747093586fb8a1/fileshack/static/fileshack/img/info-close.png -------------------------------------------------------------------------------- /fileshack/static/fileshack/img/info-close.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 28 | 35 | 45 | 48 | 52 | 56 | 57 | 67 | 70 | 74 | 78 | 79 | 83 | 87 | 88 | 89 | 107 | 109 | 110 | 112 | image/svg+xml 113 | 115 | 116 | 117 | 118 | 119 | 124 | 128 | 132 | 137 | 138 | 139 | -------------------------------------------------------------------------------- /fileshack/static/fileshack/img/info-wide.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterkuma/fileshackproject/3676f17f48a219aa31ca564ab8747093586fb8a1/fileshack/static/fileshack/img/info-wide.png -------------------------------------------------------------------------------- /fileshack/static/fileshack/img/info-wide.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 20 | 27 | 34 | 41 | 48 | 55 | 62 | 70 | 74 | 75 | 82 | 89 | 96 | 100 | 104 | 105 | 112 | 119 | 120 | 138 | 140 | 141 | 143 | image/svg+xml 144 | 146 | 147 | 148 | 149 | 150 | 155 | 164 | 172 | 180 | 189 | 190 | 191 | -------------------------------------------------------------------------------- /fileshack/static/fileshack/img/info.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterkuma/fileshackproject/3676f17f48a219aa31ca564ab8747093586fb8a1/fileshack/static/fileshack/img/info.png -------------------------------------------------------------------------------- /fileshack/static/fileshack/img/info.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 20 | 27 | 34 | 38 | 42 | 43 | 50 | 58 | 62 | 63 | 70 | 77 | 78 | 96 | 98 | 99 | 101 | image/svg+xml 102 | 104 | 105 | 106 | 107 | 108 | 113 | 122 | 130 | 138 | 147 | 148 | 149 | -------------------------------------------------------------------------------- /fileshack/static/fileshack/img/input-blue-tall.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterkuma/fileshackproject/3676f17f48a219aa31ca564ab8747093586fb8a1/fileshack/static/fileshack/img/input-blue-tall.png -------------------------------------------------------------------------------- /fileshack/static/fileshack/img/progress-indeterminate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterkuma/fileshackproject/3676f17f48a219aa31ca564ab8747093586fb8a1/fileshack/static/fileshack/img/progress-indeterminate.png -------------------------------------------------------------------------------- /fileshack/static/fileshack/img/progress-indeterminate.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 28 | 35 | 45 | 47 | 51 | 55 | 56 | 66 | 69 | 73 | 77 | 78 | 88 | 90 | 94 | 98 | 99 | 109 | 110 | 128 | 130 | 131 | 133 | image/svg+xml 134 | 136 | 137 | 138 | 139 | 140 | 145 | 155 | 165 | 175 | 176 | 177 | -------------------------------------------------------------------------------- /fileshack/static/fileshack/img/progress.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterkuma/fileshackproject/3676f17f48a219aa31ca564ab8747093586fb8a1/fileshack/static/fileshack/img/progress.png -------------------------------------------------------------------------------- /fileshack/static/fileshack/img/progress.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 28 | 35 | 45 | 47 | 51 | 55 | 56 | 66 | 69 | 73 | 77 | 78 | 88 | 90 | 94 | 98 | 99 | 109 | 116 | 126 | 128 | 132 | 136 | 137 | 146 | 149 | 153 | 157 | 158 | 168 | 169 | 187 | 189 | 190 | 192 | image/svg+xml 193 | 195 | 196 | 197 | 198 | 199 | 204 | 214 | 224 | 231 | 238 | 239 | 240 | -------------------------------------------------------------------------------- /fileshack/static/fileshack/img/watch-dialog-top.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterkuma/fileshackproject/3676f17f48a219aa31ca564ab8747093586fb8a1/fileshack/static/fileshack/img/watch-dialog-top.png -------------------------------------------------------------------------------- /fileshack/static/fileshack/js/accesscode.js: -------------------------------------------------------------------------------- 1 | document.addEvent('domready', function() { 2 | input = $('accesscode'); 3 | button = $('ok'); 4 | 5 | function update() { 6 | if (input.value != '') { 7 | input.addClass('active'); 8 | button.addClass('active'); 9 | } else { 10 | input.removeClass('active'); 11 | button.removeClass('active'); 12 | } 13 | } 14 | 15 | input.addEvent('keyup', update); 16 | input.addEvent('change', update); 17 | update() 18 | 19 | if ($('error')) { 20 | $('error').style.opacity = 0; 21 | var fx = new Fx.Morph('error', { duration: 500 }); 22 | fx.start({'opacity': 1}); 23 | } 24 | }); 25 | -------------------------------------------------------------------------------- /fileshack/static/fileshack/js/collections.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 Peter Kuma 3 | 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | 23 | var Collection = new Class({ 24 | Implements: Events, 25 | 26 | initialize: function() { 27 | this.views = {}; 28 | }, 29 | 30 | get: function(id) { 31 | return this.views[id]; 32 | }, 33 | 34 | all: function() { 35 | return this.views; 36 | }, 37 | 38 | find: function(func) { 39 | var view = null; 40 | Object.each(this.views, function(v) { 41 | if (view) return; // Already found. 42 | if (func(v)) view = v; 43 | }); 44 | return view; 45 | }, 46 | 47 | add: function(view) { 48 | var this_ = this; 49 | var id = view.model[view.model.pk]; 50 | if (this.views[id]) return; 51 | this.views[id] = view; 52 | view.model.addEvent('change', function() { 53 | if (id != view.model[view.model.pk]) { 54 | this_.views[view.model[view.model.pk]] = view; 55 | delete this_.views[id]; 56 | id = view.model.id; 57 | } 58 | this_.fireEvent('change', view); 59 | }); 60 | view.model.addEvent('remove', function() { 61 | this_.remove(view); 62 | }); 63 | this.fireEvent('add', view); 64 | }, 65 | 66 | remove: function(view) { 67 | delete this.views[view.model[view.model.pk]]; 68 | }, 69 | 70 | contains: function(id) { 71 | return (id in this.views); 72 | } 73 | }); 74 | -------------------------------------------------------------------------------- /fileshack/static/fileshack/js/fileshack.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 Peter Kuma 3 | 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | 23 | var FileShack = new Class({ 24 | Implements: Options, 25 | 26 | options: { 27 | updateInterval: 2000 28 | }, 29 | 30 | initialize: function(options) { 31 | var this_ = this; 32 | 33 | this.setOptions(options); 34 | this.lastUpdate = undefined; 35 | 36 | this.items = new Collection(); 37 | this.items.addEvent('add', function(view) { 38 | $('list').insertBefore(view.el, $('list').firstChild); 39 | }); 40 | 41 | var bootstrap = JSON.decode($('bootstrap').get('text')); 42 | this.bootstrap(bootstrap.items); 43 | 44 | var dropbox = $('dropbox'); 45 | var dropboxInput = $('dropbox-input'); 46 | 47 | // Drag & Drop. 48 | if (typeof dropbox.addEventListener != 'undefined' && 49 | typeof dropbox.ondrop != 'undefined' && 50 | typeof File != 'undefined') 51 | { 52 | // Register Drag & Drop events. 53 | dropbox.addEventListener("dragenter", function(e) { e.preventDefault(); }, false); 54 | dropbox.addEventListener("dragover", function(e) { e.preventDefault(); }, false); 55 | dropbox.addEventListener("drop", function(e) { 56 | e.preventDefault(); 57 | if (e.dataTransfer.files) 58 | Array.each(e.dataTransfer.files, function(f) { this_.upload(f); }); 59 | }, false); 60 | } else { 61 | // Switch dropbox text, drag & drop is not supported. 62 | $('dropbox-text').setStyle('display', 'none'); 63 | $('dropbox-text-nodragndrop').setStyle('display', 'block'); 64 | } 65 | 66 | var clickDelegated = false; 67 | dropboxInput.onclick = function(e) { 68 | clickDelegated = true; 69 | if (e && e.stopPropagation) e.stopPropagation(); 70 | }; 71 | 72 | if (Browser.ie && Browser.version <= 7) { 73 | this.fallback(); 74 | } else { 75 | // Delegate click on dropbox to the hidden input file element. 76 | dropbox.addEvent('click', function() { 77 | if (typeof File != 'undefined') { 78 | dropboxInput.click(); 79 | window.setTimeout(function() { if (!clickDelegated) this_.fallback(); }, 100); 80 | } else { // Fallback to upload by the iframe hack (IE). 81 | this_.uploadIFrame(); 82 | } 83 | }); 84 | } 85 | 86 | dropboxInput.addEvent('change', function() { 87 | this_.upload(dropbox); 88 | dropbox.reset(); 89 | }); 90 | 91 | //window.setInterval(function() { this_.update() }, this.options.updateInterval); 92 | //this.update(); 93 | 94 | if ($('watchbtn')) { 95 | // Watch dialog. 96 | watch = new Watch(); 97 | watch.bootstrap(bootstrap.watchers); 98 | } 99 | }, 100 | 101 | bootstrap: function(items) { 102 | var this_ = this; 103 | Array.each(items, function(item) { 104 | var view = new ItemView(new Item()); 105 | view.model.update(item); 106 | this_.items.add(view); 107 | }); 108 | }, 109 | 110 | fallback: function() { 111 | var this_ = this; 112 | // Show the file upload input form. 113 | $('dropbox-text').setStyle('display', 'none'); 114 | $('dropbox-text-nodragndrop').setStyle('display', 'none'); 115 | $('dropbox-file').setStyle('visibility', 'visible'); 116 | }, 117 | 118 | update: function() { 119 | var this_ = this; 120 | var xhr = new XMLHttpRequest(); 121 | 122 | if (this.lastUpdate) xhr.open('GET', 'update/' + this.lastUpdate + '/'); 123 | else xhr.open('GET', 'update/'); 124 | 125 | var this_ = this; 126 | xhr.onreadystatechange = function(e) { 127 | if (this.readyState == 4) { 128 | json = JSON.decode(this.responseText); 129 | this_.removeStaleItems(json.item_ids); 130 | Array.each(json.items, function(item) { 131 | if (this_.items.get(item.id)) { // Existing item. 132 | // Do not update pending items. 133 | if (this_.items.get(item.id).model.type != 'pending') 134 | this_.items.get(item.id).model.update(item); 135 | } else { // New item. 136 | var view = new ItemView(new Item()); 137 | view.model.update(item); 138 | this_.items.add(view); 139 | } 140 | }); 141 | this_.lastUpdate = json.time; 142 | } 143 | }; 144 | xhr.send(); 145 | }, 146 | 147 | upload: function(data) { 148 | var this_ = this; 149 | // If input[type="file"].files is supported, upload per parts. 150 | if (typeof HTMLFormElement != 'undefined' && data instanceof HTMLFormElement && 151 | data.file && data.file.files) 152 | { 153 | Array.each(data.file.files, function(file) { this_.upload(file); }); 154 | return null; 155 | } 156 | 157 | // Determine name and size of the file. 158 | if (typeof File != 'undefined' && data instanceof File) { 159 | // Older browsers. 160 | if (data.fileName) var name = data.fileName; 161 | if (data.fileSize) var size = data.fileSize; 162 | // Newer browsers. 163 | if (data.name) var name = data.name; 164 | if (data.size) var size = data.size; 165 | } else if (data.file) { 166 | var name = basename(data.file.value); 167 | } else { 168 | var name = ''; 169 | var size = 0; 170 | } 171 | 172 | // Is there a stale item with the same size and name? 173 | var i = this.items.find(function(i) { 174 | if (!(i.model.type == 'stale' || i.model.type == 'pending' && i.isError())) 175 | return false; 176 | if (i.model.size_total != size) return false; 177 | if (i.model.name == name) return true; 178 | var n = i.model.name; 179 | // Django appends _#no to duplicate file names, account for that. 180 | if (n.contains('_')) { 181 | var index = n.lastIndexOf('_'); 182 | var first = n.substring(0, index); 183 | var second = n.substring(index); 184 | n = first + second.substring(second.indexOf('.')); 185 | } 186 | if (n == name) return true; 187 | return false; 188 | }); 189 | 190 | // If this is a File, ask the user about resume. 191 | if (i && typeof File != 'undefined' && data instanceof File) { 192 | var c = confirm('A stale file with the same name and size has been found.\nDo you want to resume uploading?'); 193 | if (c) { 194 | item = i; 195 | item.clearError(); 196 | } 197 | } 198 | 199 | if (!item) { 200 | var item = new ItemView(new Item({ 201 | type: 'pending', 202 | 'name': name, 203 | size: 0, 204 | size_total: size, 205 | status: 'UPLOADING' 206 | })); 207 | this.items.add(item); 208 | } 209 | 210 | if (ITEM_SIZE_LIMIT > 0 && size > ITEM_SIZE_LIMIT) { 211 | item.onError({ 212 | label: ITEM_SIZE_LIMIT_ERROR_LABEL, 213 | message: ITEM_SIZE_LIMIT_ERROR_MESSAGE 214 | }); 215 | return null; 216 | } 217 | 218 | if (typeof File != 'undefined' && data instanceof File && 219 | typeof FormData != 'undefined' && 220 | (data.slice || data.mozSlice || data.webkitSlice)) 221 | { 222 | item.model.upload(data); 223 | } else if (typeof File != 'undefined' && data instanceof File && typeof FileReader != 'undefined') { 224 | var reader = new FileReader() 225 | reader.onload = function(e) { item.model.upload(e.target.result); }; 226 | reader.readAsBinaryString(data); 227 | } else if (typeof HTMLFormElement != 'undefined' && data instanceof HTMLFormElement && typeof FormData != 'undefined') { 228 | item.model.upload(new FormData(data)); 229 | } else if (typeof File != 'undefined' && data instanceof File && typeof FormData != 'undefined') { 230 | var formData = new FormData(); 231 | formData.append('file', data); 232 | item.model.upload(formData); 233 | } else if (typeof File != 'undefined' && data instanceof File) { 234 | item.model.upload(data.getAsBinary()); 235 | } else { 236 | data.submit(); 237 | } 238 | 239 | return item; 240 | }, 241 | 242 | uploadIFrame: function() { 243 | var this_ = this; 244 | var iframe = $('iframe'); 245 | var form = iframe.contentDocument.forms[0]; 246 | 247 | form.file.onchange = function() { 248 | var item = this_.upload(form); 249 | iframe.onload = function() { 250 | var responseText = iframe.contentDocument.body.innerHTML; 251 | iframe.onload = null; 252 | iframe.src = iframe.src; 253 | try { 254 | var response = JSON.decode(responseText); 255 | } catch(e) { 256 | item.onError({ 257 | label: 'Upload failed', 258 | message: 'The server responded with an invalid message', 259 | details: responseText 260 | }); 261 | return; 262 | } 263 | item.model.update(response.item); 264 | if (response.status != 'success') { 265 | item.onError({ 266 | label: response.error_label, 267 | message: response.error_message, 268 | details: response.details 269 | }); 270 | return; 271 | } 272 | item.model.set('type', 'complete'); 273 | item.model.update(response.item); 274 | }; 275 | }; 276 | form.file.click(); 277 | }, 278 | 279 | removeStaleItems: function(validIds) { 280 | Object.each(this.items.all(), function(item) { 281 | if (item.model.type != 'pending' && !validIds.contains(item.model.id)) 282 | item.model.remove(); 283 | }); 284 | } 285 | }); 286 | 287 | var Watch = new Class({ 288 | initialize: function() { 289 | var this_ = this; 290 | 291 | this.watchers = new Collection(); 292 | this.watchers.addEvent('add', function(view) { 293 | $('watch-list').appendChild(view.el); 294 | }); 295 | 296 | this.watchbtn = $('watchbtn'); 297 | this.dialog = $('watch-dialog'); 298 | this.form = $$('#watch-dialog .new')[0]; 299 | this.email = $$('#watch-dialog .new input[name="email"]')[0]; 300 | this.submit = $$('#watch-dialog .new button[type="submit"]')[0]; 301 | this.error = $('watch-error'); 302 | 303 | if (!this.form.checkValidity) { 304 | this.form.checkValidity = function() { 305 | var pattern = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/; 306 | return this.email.value.match(pattern); 307 | } 308 | } 309 | 310 | var fx = new Fx.Reveal(this.dialog, { 311 | transition: Fx.Transitions.Sine.easeOut, 312 | duration: 100 313 | }); 314 | 315 | window.addEvent('resize', function() { this_.positionDialog(); }); 316 | this.positionDialog(); 317 | 318 | this.email.addEvent('change', function() { this_.render(); }); 319 | this.email.addEvent('keyup', function() { this_.render(); }); 320 | this.watchbtn.addEvent('click', function() { 321 | if (this_.watchbtn.hasClass('active')) { 322 | this_.watchbtn.removeClass('active'); 323 | } else { 324 | this_.watchbtn.addClass('active'); 325 | } 326 | fx.toggle(); 327 | }); 328 | this.submit.addEvent('click', function(e) { 329 | if (e.preventDefault) e.preventDefault(); 330 | if (!this_.form.checkValidity()) return; 331 | var watcher = new WatcherView(new Watcher({ 332 | email: this_.email.value, 333 | })); 334 | watcher.model.addEvent('save', function() { 335 | this_.form.reset(); 336 | this_.submit.removeClass('active'); 337 | this_.error.hide(); 338 | this_.watchers.add(watcher); 339 | this_.email.disabled = false; 340 | this_.submit.disabled = false; 341 | }); 342 | watcher.model.addEvent('error', function() { 343 | this_.email.disabled = false; 344 | this_.submit.disabled = false; 345 | }); 346 | this_.email.disabled = true; 347 | this_.submit.disabled = true; 348 | watcher.model.save(); 349 | }); 350 | }, 351 | 352 | positionDialog: function() { 353 | var display = this.dialog.getStyle('display'); 354 | var visibility = this.dialog.getStyle('visibility'); 355 | this.dialog.setStyle('visibility', 'hidden'); 356 | this.dialog.setStyle('display', 'block'); 357 | this.dialog.setStyle('left', this.watchbtn.getPosition().x + 358 | this.watchbtn.getSize().x/2 - 359 | this.dialog.getSize().x/2); 360 | this.dialog.setStyle('top', this.watchbtn.getPosition().y + 361 | this.watchbtn.getSize().y); 362 | this.dialog.setStyle('display', display); 363 | this.dialog.setStyle('visibility', visibility); 364 | }, 365 | 366 | render: function() { 367 | if (this.form.checkValidity()) this.submit.addClass('active'); 368 | else this.submit.removeClass('active'); 369 | }, 370 | 371 | bootstrap: function(watchers) { 372 | var this_ = this; 373 | Array.each(watchers, function(watcher) { 374 | var view = new WatcherView(new Watcher()); 375 | view.model.update(watcher); 376 | this_.watchers.add(view); 377 | }); 378 | } 379 | }); 380 | 381 | document.addEvent('domready', function() { 382 | var fs = new FileShack(); 383 | }); 384 | 385 | 386 | 387 | 388 | 389 | 390 | -------------------------------------------------------------------------------- /fileshack/static/fileshack/js/misc.js: -------------------------------------------------------------------------------- 1 | function uuid() { 2 | return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { 3 | var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8); 4 | return v.toString(16); 5 | }); 6 | } 7 | 8 | function bytesToHuman(nBytes) { 9 | if (isNaN(nBytes)) return '0 B'; 10 | var aMultiples = ['KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']; 11 | var nMultiple = 0; 12 | var out = nBytes + ' B'; 13 | for (nApprox = nBytes / 1024; nApprox > 1; nApprox /= 1024, nMultiple++) { 14 | if (nMultiple > 0) out = nApprox.toFixed(1) + ' ' + aMultiples[nMultiple]; 15 | else out = nApprox.toFixed(0) + ' ' + aMultiples[nMultiple]; 16 | } 17 | return out; 18 | } 19 | 20 | function basename(path) { 21 | path = path.substring(path.lastIndexOf('/') + 1); 22 | path = path.substring(path.lastIndexOf('\\') + 1); 23 | return path; 24 | } 25 | -------------------------------------------------------------------------------- /fileshack/static/fileshack/js/models.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 Peter Kuma 3 | 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | 23 | var Model = new Class({ 24 | Implements: Events, 25 | 26 | defaults: { 27 | pk: 'id' 28 | }, 29 | 30 | initialize: function(attributes) { 31 | for (var attr in this.defaults) { 32 | this[attr] = this.defaults[attr]; 33 | } 34 | for (var attr in attributes) { 35 | this[attr] = attributes[attr]; 36 | } 37 | }, 38 | 39 | set: function(attr, value) { 40 | if (this[attr] != value) { 41 | this[attr] = value; 42 | this.fireEvent('change'); 43 | } 44 | }, 45 | 46 | get: function(attr) { 47 | return this[attr]; 48 | }, 49 | 50 | update: function(json) { 51 | for (var attr in json) { 52 | this[attr] = json[attr]; 53 | } 54 | this.fireEvent('change'); 55 | }, 56 | 57 | remove: function() { 58 | this.fireEvent('remove'); 59 | } 60 | }); 61 | 62 | var Item = new Class({ 63 | Extends: Model, 64 | 65 | defaults: { 66 | id: -1, 67 | type: 'complete', // 'pending', 'unfinished', 'stale', 'complete'. 68 | name: '', 69 | url: '', 70 | size: 0, 71 | size_total: 0, 72 | status: 'READY', // 'READY', 'UPLOADING', 'STALE'. 73 | created: new Date(), 74 | uploaded: new Date(), 75 | modified: new Date() 76 | }, 77 | 78 | initialize: function(attributes) { 79 | this.parent(attributes); 80 | this.id = uuid(); 81 | }, 82 | 83 | update: function(json) { 84 | if (this.type == 'pending') 85 | ; // Do nothing. 86 | else if (json.status == 'READY') 87 | this.type = 'complete'; 88 | else if (json.status == 'UPLOADING') 89 | this.type = 'unfinished'; 90 | else if (json.status == 'STALE') 91 | this.type = 'stale'; 92 | 93 | for (var attr in json) { 94 | if (this.type == 'pending' && (attr == 'status' || attr == 'size')) 95 | continue; 96 | this[attr] = json[attr]; 97 | } 98 | this.modified = new Date().parse(json.modified); 99 | this.uploaded = new Date().parse(json.uploaded); 100 | this.created = new Date().parse(json.created); 101 | 102 | this.fireEvent('change'); 103 | }, 104 | 105 | remove: function() { 106 | if (this.xhr) this.xhr.abort(); 107 | //this.parent(); 108 | this.fireEvent('remove'); 109 | }, 110 | 111 | del: function() { 112 | if (this.xhr) this.xhr.abort(); 113 | 114 | var xhr = new XMLHttpRequest(); 115 | xhr.open('POST', 'delete/' + this.id + '/'); 116 | xhr.setRequestHeader('X-CSRFToken', CSRF_TOKEN); 117 | 118 | var this_ = this; 119 | xhr.onreadystatechange = function(e) { 120 | if (this.readyState == this.DONE) { 121 | this_.remove(); 122 | } 123 | }; 124 | xhr.send('csrfmiddlewaretoken='+CSRF_TOKEN); 125 | 126 | this.remove(); 127 | }, 128 | 129 | upload: function(data, offset, len) { 130 | if (typeof offset == 'undefined') offset = this.size; 131 | if (typeof data == 'string') this.set('size_total', data.length); 132 | if (data.size) this.set('size_total', data.size); 133 | if (typeof len == 'undefined') len = CHUNK_SIZE; 134 | if (this.size + len > this.size_total) 135 | len = this.size_total - this.size; 136 | 137 | this.set('type', 'pending'); 138 | 139 | this.xhr = new XMLHttpRequest(); 140 | this.xhr.open('POST', 'upload/' + this.id + '/'); 141 | 142 | var this_ = this; 143 | var startTime = new Date().getTime(); 144 | 145 | if (this.xhr.upload) { 146 | this.xhr.upload.addEventListener('progress', function(e) { 147 | if (!this_.size_total) this_.set('size_total', e.total); 148 | if (!len) len = this_.size_total; 149 | this_.set('size', offset + e.loaded/e.total*len); 150 | }, false); 151 | 152 | this.xhr.upload.addEventListener('load', function(e) { 153 | if (typeof data == 'string') this_.set('size', offset + len); 154 | }, false); 155 | } 156 | 157 | this.xhr.onreadystatechange = function(e) { 158 | if (this.readyState == 4) { 159 | try { 160 | var response = JSON.decode(this.responseText); 161 | } catch(e) { 162 | this_.fireEvent('error', { 163 | label: 'Upload failed', 164 | message: 'The server responded with an invalid message', 165 | details: this.responseText 166 | }); 167 | } 168 | 169 | if (this.status == 200 && response.status == 'success') { 170 | this_.size = offset + len; 171 | this_.update(response.item); 172 | 173 | if (this_.size < this_.size_total) { 174 | elapsedTime = new Date().getTime() - startTime; 175 | if (elapsedTime < CHUNK_UPLOAD_LOW*1000) { 176 | len *= 2; 177 | //if (len > 64*1024*1024) len = 64*1024*1024; 178 | //console.log('Doubling chunk size'); 179 | } 180 | if (elapsedTime > CHUNK_UPLOAD_HIGH*1000) { 181 | len /= 2; 182 | if (len < 32*1024) len = 32*1024; 183 | //console.log('Halving chunk size'); 184 | } 185 | 186 | // Upload the next chunk. 187 | this_.upload(data, this_.size, len); 188 | } else { 189 | // We are done. 190 | this_.set('type', 'complete'); 191 | } 192 | } else if (this.status == 403) { 193 | this_.fireEvent('error', { 194 | label: 'Your session expired', 195 | message_html: 'Please log in again' 196 | }); 197 | } else if (this.status != 0) { 198 | // Revert to the original size as the chunk upload failed. 199 | this_.set('size', offset); 200 | if (response) { 201 | this_.fireEvent('error', { 202 | label: response.error_label, 203 | message: response.error_message 204 | }); 205 | } else { 206 | this_.fireEvent('error', { 207 | label: 'Application Error', 208 | message: this.status+' '+this.statusText, 209 | details: this.responseText 210 | }); 211 | } 212 | } else { 213 | // Revert to the original size as the chunk upload failed. 214 | this_.set('size', offset); 215 | this_.fireEvent('error', { 216 | label: 'Upload failed', 217 | message: 'The upload was terminated before completion' 218 | }); 219 | } 220 | } 221 | }; 222 | 223 | if (typeof File != 'undefined' && data instanceof File && typeof FormData != 'undefined') { 224 | if (data.mozSlice) var chunk = data.mozSlice(offset, offset + len); 225 | else if (data.webkitSlice) var chunk = data.webkitSlice(offset, offset + len); 226 | else var chunk = data.slice(offset, offset + len); 227 | var body = new FormData(); 228 | body.append('file', chunk); 229 | } else if(typeof data == 'string') { 230 | var boundary = 'xxxxxxxxxxxxxxxxxxxx'; 231 | var body = '--' + boundary + '\r\n'; 232 | body += 'Content-Disposition: form-data; name="file"; filename="' + encodeURIComponent(this.name) + '"\r\n'; 233 | body += 'Content-Type: application/octet-stream\r\n'; 234 | body += '\r\n'; 235 | body += window.btoa(data.substring(offset, offset + len)) + '\r\n'; 236 | body += '--' + boundary + '--\r\n'; 237 | this.xhr.setRequestHeader('Content-Type', 'multipart/form-data; boundary=' + boundary); 238 | this.xhr.setRequestHeader('X-File-Encoding', 'base64'); 239 | } else { 240 | var body = data; 241 | len = this.size_total - offset; 242 | } 243 | 244 | if (this.size_total) this.xhr.setRequestHeader('X-File-Size', this.size_total); 245 | this.xhr.setRequestHeader('X-File-Offset', offset); 246 | this.xhr.setRequestHeader('X-File-Name', encodeURIComponent(this.name)); 247 | this.xhr.setRequestHeader('X-CSRFToken', CSRF_TOKEN); 248 | this.xhr.send(body); 249 | body = null; 250 | } 251 | }); 252 | 253 | var Watcher = new Class({ 254 | Extends: Model, 255 | 256 | defaults: { 257 | pk: 'email', 258 | email: '', 259 | last_notification: new Date(), 260 | created: new Date() 261 | }, 262 | 263 | save: function() { 264 | var this_ = this; 265 | 266 | var req = new Request.JSON({ 267 | url: 'watch/', 268 | headers: { 'X-CSRFToken': CSRF_TOKEN }, 269 | data: { 270 | email: this.email, 271 | }, 272 | onSuccess: function(response) { 273 | this_.update(response.watcher); 274 | this_.fireEvent('save'); 275 | }, 276 | onFailure: function(xhr) { 277 | if (xhr.status == 403) { 278 | this_.fireEvent('error', { 279 | label: 'Session expired', 280 | message_html: 'Please log in again' 281 | }); 282 | } else if (xhr.status != 0) { 283 | try { 284 | var response = JSON.decode(xhr.responseText); 285 | this_.fireEvent('error', response); 286 | } catch(e) { 287 | this_.fireEvent('error', { 288 | message: xhr.statusText, 289 | details: xhr.responseText 290 | }); 291 | } 292 | } else { 293 | this_.fireEvent('error', { 294 | message: 'Request failed' 295 | }); 296 | } 297 | } 298 | }); 299 | req.send(); 300 | }, 301 | 302 | del: function() { 303 | var this_ = this; 304 | var req = new Request.JSON({ 305 | url: 'unwatch/', 306 | headers: { 'X-CSRFToken': CSRF_TOKEN }, 307 | data: { email: this.email }, 308 | onFailure: function(xhr) { 309 | if (xhr.status == 403) { 310 | this_.fireEvent('error', { 311 | label: 'Session expired', 312 | message_html: 'Please log in again' 313 | }); 314 | } else if (xhr.status != 0) { 315 | this_.fireEvent('error', { 316 | message: xhr.statusText, 317 | details: xhr.responseText 318 | }); 319 | } else { 320 | this_.fireEvent('error', { 321 | message: 'Request failed' 322 | }); 323 | } 324 | } 325 | }).send(); 326 | 327 | this_.remove(); 328 | } 329 | }); 330 | -------------------------------------------------------------------------------- /fileshack/static/fileshack/js/mootools-more.js: -------------------------------------------------------------------------------- 1 | // MooTools: the javascript framework. 2 | // Load this file's selection again by visiting: http://mootools.net/more/d27f0021e6d51da6cf8b5983ba128ca2 3 | // Or build this file again with packager using: packager build More/More More/Date More/Element.Shortcuts More/Fx.Reveal 4 | /* 5 | --- 6 | copyrights: 7 | - [MooTools](http://mootools.net) 8 | 9 | licenses: 10 | - [MIT License](http://mootools.net/license.txt) 11 | ... 12 | */ 13 | MooTools.More={version:"1.4.0.1",build:"a4244edf2aa97ac8a196fc96082dd35af1abab87"};(function(){var b=function(c){return c!=null;};var a=Object.prototype.hasOwnProperty; 14 | Object.extend({getFromPath:function(e,f){if(typeof f=="string"){f=f.split(".");}for(var d=0,c=f.length;d3&&a<21)?"th":["th","st","nd","rd","th"][Math.min(a%10,4)]; 26 | },lessThanMinuteAgo:"less than a minute ago",minuteAgo:"about a minute ago",minutesAgo:"{delta} minutes ago",hourAgo:"about an hour ago",hoursAgo:"about {delta} hours ago",dayAgo:"1 day ago",daysAgo:"{delta} days ago",weekAgo:"1 week ago",weeksAgo:"{delta} weeks ago",monthAgo:"1 month ago",monthsAgo:"{delta} months ago",yearAgo:"1 year ago",yearsAgo:"{delta} years ago",lessThanMinuteUntil:"less than a minute from now",minuteUntil:"about a minute from now",minutesUntil:"{delta} minutes from now",hourUntil:"about an hour from now",hoursUntil:"about {delta} hours from now",dayUntil:"1 day from now",daysUntil:"{delta} days from now",weekUntil:"1 week from now",weeksUntil:"{delta} weeks from now",monthUntil:"1 month from now",monthsUntil:"{delta} months from now",yearUntil:"1 year from now",yearsUntil:"{delta} years from now"}); 27 | (function(){var a=this.Date;var f=a.Methods={ms:"Milliseconds",year:"FullYear",min:"Minutes",mo:"Month",sec:"Seconds",hr:"Hours"};["Date","Day","FullYear","Hours","Milliseconds","Minutes","Month","Seconds","Time","TimezoneOffset","Week","Timezone","GMTOffset","DayOfYear","LastMonth","LastDayOfMonth","UTCDate","UTCDay","UTCFullYear","AMPM","Ordinal","UTCHours","UTCMilliseconds","UTCMinutes","UTCMonth","UTCSeconds","UTCMilliseconds"].each(function(s){a.Methods[s.toLowerCase()]=s; 28 | });var p=function(u,t,s){if(t==1){return u;}return u28){return 1;}if(y==0&&s<-2){x=new a(x).decrement("day",u); 38 | u=0;}w=new a(x.get("year"),0,1).get("day")||7;if(w>4){t=-7;}}else{w=new a(x.get("year"),0,1).get("day");}t+=x.get("dayofyear");t+=6-u;t+=(7+w-v)%7;return(t/7); 39 | },getOrdinal:function(s){return a.getMsg("ordinal",s||this.get("date"));},getTimezone:function(){return this.toString().replace(/^.*? ([A-Z]{3}).[0-9]{4}.*$/,"$1").replace(/^.*?\(([A-Z])[a-z]+ ([A-Z])[a-z]+ ([A-Z])[a-z]+\)$/,"$1$2$3"); 40 | },getGMTOffset:function(){var s=this.get("timezoneOffset");return((s>0)?"-":"+")+p((s.abs()/60).floor(),2)+p(s%60,2);},setAMPM:function(s){s=s.toUpperCase(); 41 | var t=this.get("hr");if(t>11&&s=="AM"){return this.decrement("hour",12);}else{if(t<12&&s=="PM"){return this.increment("hour",12);}}return this;},getAMPM:function(){return(this.get("hr")<12)?"AM":"PM"; 42 | },parse:function(s){this.set("time",a.parse(s));return this;},isValid:function(s){if(!s){s=this;}return typeOf(s)=="date"&&!isNaN(s.valueOf());},format:function(s){if(!this.isValid()){return"invalid date"; 43 | }if(!s){s="%x %X";}if(typeof s=="string"){s=g[s.toLowerCase()]||s;}if(typeof s=="function"){return s(this);}var t=this;return s.replace(/%([a-z%])/gi,function(v,u){switch(u){case"a":return a.getMsg("days_abbr")[t.get("day")]; 44 | case"A":return a.getMsg("days")[t.get("day")];case"b":return a.getMsg("months_abbr")[t.get("month")];case"B":return a.getMsg("months")[t.get("month")]; 45 | case"c":return t.format("%a %b %d %H:%M:%S %Y");case"d":return p(t.get("date"),2);case"e":return p(t.get("date"),2," ");case"H":return p(t.get("hr"),2); 46 | case"I":return p((t.get("hr")%12)||12,2);case"j":return p(t.get("dayofyear"),3);case"k":return p(t.get("hr"),2," ");case"l":return p((t.get("hr")%12)||12,2," "); 47 | case"L":return p(t.get("ms"),3);case"m":return p((t.get("mo")+1),2);case"M":return p(t.get("min"),2);case"o":return t.get("ordinal");case"p":return a.getMsg(t.get("ampm")); 48 | case"s":return Math.round(t/1000);case"S":return p(t.get("seconds"),2);case"T":return t.format("%H:%M:%S");case"U":return p(t.get("week"),2);case"w":return t.get("day"); 49 | case"x":return t.format(a.getMsg("shortDate"));case"X":return t.format(a.getMsg("shortTime"));case"y":return t.get("year").toString().substr(2);case"Y":return t.get("year"); 50 | case"z":return t.get("GMTOffset");case"Z":return t.get("Timezone");}return u;});},toISOString:function(){return this.format("iso8601");}}).alias({toJSON:"toISOString",compare:"diff",strftime:"format"}); 51 | var k=["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],h=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];var g={db:"%Y-%m-%d %H:%M:%S",compact:"%Y%m%dT%H%M%S","short":"%d %b %H:%M","long":"%B %d, %Y %H:%M",rfc822:function(s){return k[s.get("day")]+s.format(", %d ")+h[s.get("month")]+s.format(" %Y %H:%M:%S %Z"); 52 | },rfc2822:function(s){return k[s.get("day")]+s.format(", %d ")+h[s.get("month")]+s.format(" %Y %H:%M:%S %z");},iso8601:function(s){return(s.getUTCFullYear()+"-"+p(s.getUTCMonth()+1,2)+"-"+p(s.getUTCDate(),2)+"T"+p(s.getUTCHours(),2)+":"+p(s.getUTCMinutes(),2)+":"+p(s.getUTCSeconds(),2)+"."+p(s.getUTCMilliseconds(),3)+"Z"); 53 | }};var c=[],n=a.parse;var r=function(v,x,u){var t=-1,w=a.getMsg(v+"s");switch(typeOf(x)){case"object":t=w[x.get(v)];break;case"number":t=w[x];if(!t){throw new Error("Invalid "+v+" index: "+x); 54 | }break;case"string":var s=w.filter(function(y){return this.test(y);},new RegExp("^"+x,"i"));if(!s.length){throw new Error("Invalid "+v+" string");}if(s.length>1){throw new Error("Ambiguous "+v); 55 | }t=s[0];}return(u)?w.indexOf(t):t;};var i=1900,o=70;a.extend({getMsg:function(t,s){return Locale.get("Date."+t,s);},units:{ms:Function.from(1),second:Function.from(1000),minute:Function.from(60000),hour:Function.from(3600000),day:Function.from(86400000),week:Function.from(608400000),month:function(t,s){var u=new a; 56 | return a.daysInMonth(t!=null?t:u.get("mo"),s!=null?s:u.get("year"))*86400000;},year:function(s){s=s||new a().get("year");return a.isLeapYear(s)?31622400000:31536000000; 57 | }},daysInMonth:function(t,s){return[31,a.isLeapYear(s)?29:28,31,30,31,30,31,31,30,31,30,31][t];},isLeapYear:function(s){return((s%4===0)&&(s%100!==0))||(s%400===0); 58 | },parse:function(v){var u=typeOf(v);if(u=="number"){return new a(v);}if(u!="string"){return v;}v=v.clean();if(!v.length){return null;}var s;c.some(function(w){var t=w.re.exec(v); 59 | return(t)?(s=w.handler(t)):false;});if(!(s&&s.isValid())){s=new a(n(v));if(!(s&&s.isValid())){s=new a(v.toInt());}}return s;},parseDay:function(s,t){return r("day",s,t); 60 | },parseMonth:function(t,s){return r("month",t,s);},parseUTC:function(t){var s=new a(t);var u=a.UTC(s.get("year"),s.get("mo"),s.get("date"),s.get("hr"),s.get("min"),s.get("sec"),s.get("ms")); 61 | return new a(u);},orderIndex:function(s){return a.getMsg("dateOrder").indexOf(s)+1;},defineFormat:function(s,t){g[s]=t;return this;},defineParser:function(s){c.push((s.re&&s.handler)?s:l(s)); 62 | return this;},defineParsers:function(){Array.flatten(arguments).each(a.defineParser);return this;},define2DigitYearStart:function(s){o=s%100;i=s-o;return this; 63 | }}).extend({defineFormats:a.defineFormat.overloadSetter()});var d=function(s){return new RegExp("(?:"+a.getMsg(s).map(function(t){return t.substr(0,3); 64 | }).join("|")+")[a-z]*");};var m=function(s){switch(s){case"T":return"%H:%M:%S";case"x":return((a.orderIndex("month")==1)?"%m[-./]%d":"%d[-./]%m")+"([-./]%y)?"; 65 | case"X":return"%H([.:]%M)?([.:]%S([.:]%s)?)? ?%p? ?%z?";}return null;};var j={d:/[0-2]?[0-9]|3[01]/,H:/[01]?[0-9]|2[0-3]/,I:/0?[1-9]|1[0-2]/,M:/[0-5]?\d/,s:/\d+/,o:/[a-z]*/,p:/[ap]\.?m\.?/,y:/\d{2}|\d{4}/,Y:/\d{4}/,z:/Z|[+-]\d{2}(?::?\d{2})?/}; 66 | j.m=j.I;j.S=j.M;var e;var b=function(s){e=s;j.a=j.A=d("days");j.b=j.B=d("months");c.each(function(u,t){if(u.format){c[t]=l(u.format);}});};var l=function(u){if(!e){return{format:u}; 67 | }var s=[];var t=(u.source||u).replace(/%([a-z])/gi,function(w,v){return m(v)||w;}).replace(/\((?!\?)/g,"(?:").replace(/ (?!\?|\*)/g,",? ").replace(/%([a-z%])/gi,function(w,v){var x=j[v]; 68 | if(!x){return v;}s.push(v);return"("+x.source+")";}).replace(/\[a-z\]/gi,"[a-z\\u00c0-\\uffff;&]");return{format:u,re:new RegExp("^"+t+"$","i"),handler:function(y){y=y.slice(1).associate(s); 69 | var v=new a().clearTime(),x=y.y||y.Y;if(x!=null){q.call(v,"y",x);}if("d" in y){q.call(v,"d",1);}if("m" in y||y.b||y.B){q.call(v,"m",1);}for(var w in y){q.call(v,w,y[w]); 70 | }return v;}};};var q=function(s,t){if(!t){return this;}switch(s){case"a":case"A":return this.set("day",a.parseDay(t,true));case"b":case"B":return this.set("mo",a.parseMonth(t,true)); 71 | case"d":return this.set("date",t);case"H":case"I":return this.set("hr",t);case"m":return this.set("mo",t-1);case"M":return this.set("min",t);case"p":return this.set("ampm",t.replace(/\./g,"")); 72 | case"S":return this.set("sec",t);case"s":return this.set("ms",("0."+t)*1000);case"w":return this.set("day",t);case"Y":return this.set("year",t);case"y":t=+t; 73 | if(t<100){t+=i+(t0&&b>0)?true:this.style.display!="none";},toggle:function(){return this[this.isDisplayed()?"hide":"show"](); 77 | },hide:function(){var b;try{b=this.getStyle("display");}catch(a){}if(b=="none"){return this;}return this.store("element:_originalDisplay",b||"").setStyle("display","none"); 78 | },show:function(a){if(!a&&this.isDisplayed()){return this;}a=a||this.retrieve("element:_originalDisplay")||"block";return this.setStyle("display",(a=="none")?"block":a); 79 | },swapClass:function(a,b){return this.removeClass(a).addClass(b);}});Document.implement({clearSelection:function(){if(window.getSelection){var a=window.getSelection(); 80 | if(a&&a.removeAllRanges){a.removeAllRanges();}}else{if(document.selection&&document.selection.empty){try{document.selection.empty();}catch(b){}}}}});(function(){var b=function(e,d){var f=[]; 81 | Object.each(d,function(g){Object.each(g,function(h){e.each(function(i){f.push(i+"-"+h+(i=="border"?"-width":""));});});});return f;};var c=function(f,e){var d=0; 82 | Object.each(e,function(h,g){if(g.test(f)){d=d+h.toInt();}});return d;};var a=function(d){return !!(!d||d.offsetHeight||d.offsetWidth);};Element.implement({measure:function(h){if(a(this)){return h.call(this); 83 | }var g=this.getParent(),e=[];while(!a(g)&&g!=document.body){e.push(g.expose());g=g.getParent();}var f=this.expose(),d=h.call(this);f();e.each(function(i){i(); 84 | });return d;},expose:function(){if(this.getStyle("display")!="none"){return function(){};}var d=this.style.cssText;this.setStyles({display:"block",position:"absolute",visibility:"hidden"}); 85 | return function(){this.style.cssText=d;}.bind(this);},getDimensions:function(d){d=Object.merge({computeSize:false},d);var i={x:0,y:0};var h=function(j,e){return(e.computeSize)?j.getComputedSize(e):j.getSize(); 86 | };var f=this.getParent("body");if(f&&this.getStyle("display")=="none"){i=this.measure(function(){return h(this,d);});}else{if(f){try{i=h(this,d);}catch(g){}}}return Object.append(i,(i.x||i.x===0)?{width:i.x,height:i.y}:{x:i.width,y:i.height}); 87 | },getComputedSize:function(d){d=Object.merge({styles:["padding","border"],planes:{height:["top","bottom"],width:["left","right"]},mode:"both"},d);var g={},e={width:0,height:0},f; 88 | if(d.mode=="vertical"){delete e.width;delete d.planes.width;}else{if(d.mode=="horizontal"){delete e.height;delete d.planes.height;}}b(d.styles,d.planes).each(function(h){g[h]=this.getStyle(h).toInt(); 89 | },this);Object.each(d.planes,function(i,h){var k=h.capitalize(),j=this.getStyle(h);if(j=="auto"&&!f){f=this.getDimensions();}j=g[h]=(j=="auto")?f[h]:j.toInt(); 90 | e["total"+k]=j;i.each(function(m){var l=c(m,g);e["computed"+m.capitalize()]=l;e["total"+k]+=l;});},this);return Object.append(e,g);}});})();(function(){var a=function(d){var b=d.options.hideInputs; 91 | if(window.OverText){var c=[null];OverText.each(function(e){c.include("."+e.options.labelClass);});if(c){b+=c.join(", ");}}return(b)?d.element.getElements(b):null; 92 | };Fx.Reveal=new Class({Extends:Fx.Morph,options:{link:"cancel",styles:["padding","border","margin"],transitionOpacity:!Browser.ie6,mode:"vertical",display:function(){return this.element.get("tag")!="tr"?"block":"table-row"; 93 | },opacity:1,hideInputs:Browser.ie?"select, input, textarea, object, embed":null},dissolve:function(){if(!this.hiding&&!this.showing){if(this.element.getStyle("display")!="none"){this.hiding=true; 94 | this.showing=false;this.hidden=true;this.cssText=this.element.style.cssText;var d=this.element.getComputedSize({styles:this.options.styles,mode:this.options.mode}); 95 | if(this.options.transitionOpacity){d.opacity=this.options.opacity;}var c={};Object.each(d,function(f,e){c[e]=[f,0];});this.element.setStyles({display:Function.from(this.options.display).call(this),overflow:"hidden"}); 96 | var b=a(this);if(b){b.setStyle("visibility","hidden");}this.$chain.unshift(function(){if(this.hidden){this.hiding=false;this.element.style.cssText=this.cssText; 97 | this.element.setStyle("display","none");if(b){b.setStyle("visibility","visible");}}this.fireEvent("hide",this.element);this.callChain();}.bind(this));this.start(c); 98 | }else{this.callChain.delay(10,this);this.fireEvent("complete",this.element);this.fireEvent("hide",this.element);}}else{if(this.options.link=="chain"){this.chain(this.dissolve.bind(this)); 99 | }else{if(this.options.link=="cancel"&&!this.hiding){this.cancel();this.dissolve();}}}return this;},reveal:function(){if(!this.showing&&!this.hiding){if(this.element.getStyle("display")=="none"){this.hiding=false; 100 | this.showing=true;this.hidden=false;this.cssText=this.element.style.cssText;var d;this.element.measure(function(){d=this.element.getComputedSize({styles:this.options.styles,mode:this.options.mode}); 101 | }.bind(this));if(this.options.heightOverride!=null){d.height=this.options.heightOverride.toInt();}if(this.options.widthOverride!=null){d.width=this.options.widthOverride.toInt(); 102 | }if(this.options.transitionOpacity){this.element.setStyle("opacity",0);d.opacity=this.options.opacity;}var c={height:0,display:Function.from(this.options.display).call(this)}; 103 | Object.each(d,function(f,e){c[e]=0;});c.overflow="hidden";this.element.setStyles(c);var b=a(this);if(b){b.setStyle("visibility","hidden");}this.$chain.unshift(function(){this.element.style.cssText=this.cssText; 104 | this.element.setStyle("display",Function.from(this.options.display).call(this));if(!this.hidden){this.showing=false;}if(b){b.setStyle("visibility","visible"); 105 | }this.callChain();this.fireEvent("show",this.element);}.bind(this));this.start(d);}else{this.callChain();this.fireEvent("complete",this.element);this.fireEvent("show",this.element); 106 | }}else{if(this.options.link=="chain"){this.chain(this.reveal.bind(this));}else{if(this.options.link=="cancel"&&!this.showing){this.cancel();this.reveal(); 107 | }}}return this;},toggle:function(){if(this.element.getStyle("display")=="none"){this.reveal();}else{this.dissolve();}return this;},cancel:function(){this.parent.apply(this,arguments); 108 | if(this.cssText!=null){this.element.style.cssText=this.cssText;}this.hiding=false;this.showing=false;return this;}});Element.Properties.reveal={set:function(b){this.get("reveal").cancel().setOptions(b); 109 | return this;},get:function(){var b=this.retrieve("reveal");if(!b){b=new Fx.Reveal(this);this.store("reveal",b);}return b;}};Element.Properties.dissolve=Element.Properties.reveal; 110 | Element.implement({reveal:function(b){this.get("reveal").setOptions(b).reveal();return this;},dissolve:function(b){this.get("reveal").setOptions(b).dissolve(); 111 | return this;},nix:function(b){var c=Array.link(arguments,{destroy:Type.isBoolean,options:Type.isObject});this.get("reveal").setOptions(b).dissolve().chain(function(){this[c.destroy?"destroy":"dispose"](); 112 | }.bind(this));return this;},wink:function(){var c=Array.link(arguments,{duration:Type.isNumber,options:Type.isObject});var b=this.get("reveal").setOptions(c.options); 113 | b.reveal().chain(function(){(function(){b.dissolve();}).delay(c.duration||2000);});}});})(); -------------------------------------------------------------------------------- /fileshack/static/fileshack/js/progress.js: -------------------------------------------------------------------------------- 1 | document.addEvent('domready', function() { 2 | var pbars = $$('progress'); 3 | pbars.each(emulateProgress); 4 | }); 5 | 6 | function emulateProgress(pb) { 7 | if (typeof HTMLProgressElement != 'undefined') return; 8 | pb.addClass('progress-emulation'); 9 | updateProgress(pb); 10 | } 11 | 12 | function updateProgress(pb) { 13 | if (typeof HTMLProgressElement != 'undefined') return; 14 | // Sadly, this does not work in IE: 15 | // var w = pb.getSize().x; 16 | var w = 400; // Ugly hack. 17 | if (pb.value) { 18 | pb.removeClass('progress-emulation-indeterminate'); 19 | var fx = pb.retrieve('fx'); 20 | if (fx) { 21 | fx.cancel(); 22 | pb.store('fx', null); 23 | } 24 | pb.setStyle('background-position', (pb.value/100*w-1000) + 'px center'); 25 | } else { 26 | pb.addClass('progress-emulation-indeterminate'); 27 | var fx = pb.retrieve('fx'); 28 | if (!fx) { 29 | fx = new Fx.Tween(pb, { 30 | duration: 'long', 31 | transition: 'linear', 32 | property: 'background-position', 33 | link: 'chain' 34 | }); 35 | pb.store('fx', fx); 36 | var atStart = true; 37 | fx.addEvent('complete', function() { 38 | if (atStart) fx.start(-1000+w-100); 39 | else fx.start(-1000); 40 | atStart = !atStart; 41 | }); 42 | fx.start(-1000, -1000+w-100); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /fileshack/static/fileshack/js/views.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 Peter Kuma 3 | 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | 23 | var View = new Class({ 24 | attributes: [], 25 | 26 | initialize: function(model) { 27 | this.model = model; 28 | this.el = $(this.template).clone(true, false); 29 | 30 | var this_ = this; 31 | Array.each(this.attributes, function(attr) { 32 | this_[attr] = this_.el.getElements('.'+attr)[0]; 33 | }); 34 | model.addEvent('change', function() { this_.render(); }); 35 | model.addEvent('remove', function() { 36 | if(this_.el.parentNode) { 37 | this_.el.parentNode.removeChild(this_.el); 38 | } 39 | }); 40 | this.render(); 41 | }, 42 | 43 | render: function() { 44 | return this; 45 | } 46 | }); 47 | 48 | var ItemView = new Class({ 49 | Extends: View, 50 | 51 | template: 'item-template', 52 | attributes: ['box', 'name', 'size', 'progress_container', 'progress', 53 | 'progressbar', 'progressbar_alt', 'progress_size', 54 | 'uploaded', 'desc', 'deletebtn', 'cancelbtn', 'buttons', 55 | 'error', 'error_label', 'error_message', 'error_details', 'error_close'], 56 | 57 | initialize: function(model) { 58 | this.parent(model); 59 | var this_ = this; 60 | model.addEvent('error', function(e) { this_.onError(e); }); 61 | this.deletebtn.addEvent('click', function(e) { 62 | e.preventDefault(); 63 | this_.model.del(); 64 | return false; 65 | }); 66 | this.cancelbtn.addEvent('click', function(e) { 67 | e.preventDefault(); 68 | this_.model.del(); 69 | return false; 70 | }); 71 | this.error_close.addEvent('click', function(e) { 72 | this_.clearError(); 73 | if (this_.model.size > 0) 74 | this_.model.set('type', 'stale'); 75 | else 76 | this_.model.del(); 77 | }); 78 | if (!this.progressbar) { 79 | this.progressbar = this.progressbar_alt; 80 | this.progressbar.removeClass('progressbar_alt'); 81 | this.progressbar.addClass('progressbar'); 82 | } 83 | emulateProgress(this.progressbar); 84 | }, 85 | 86 | show: function(what) { 87 | var this_ = this; 88 | Array.each(what, function(w) { this_[w].setStyle('display', 'block'); }); 89 | }, 90 | 91 | hide: function(what) { 92 | var this_ = this; 93 | Array.each(what, function(w) { this_[w].setStyle('display', 'none'); }); 94 | }, 95 | 96 | render: function() { 97 | this.name.set('text', this.model.name); 98 | this.size.set('text', bytesToHuman(this.model.size_total)); 99 | this.uploaded.set('text', this.model.uploaded.format('%e %B %Y')); 100 | this.progress_size.set('text', bytesToHuman(this.model.size) + ' / ' + bytesToHuman(this.model.size_total)); 101 | if (this.progressbar) { 102 | if (this.model.size_total != 0) 103 | this.progressbar.value = (this.model.size * 100)/this.model.size_total; 104 | else 105 | this.progressbar.value = 1; 106 | updateProgress(this.progressbar); 107 | } 108 | var percentage = 0; 109 | if (this.model.size > 0 && this.model.size_total > 0) 110 | percentage = Math.round((this.model.size * 100)/this.model.size_total); 111 | this.progress.set('text', percentage + ' %'); 112 | if (this.model.type == 'stale') this.progress.set('text', 'stale'); 113 | if (this.model.type == 'pending' && !this.model.size_total) { 114 | this.progress.set('text', ''); 115 | this.progress_size.set('text', ''); 116 | } 117 | 118 | if (this.model.url) this.box.href = this.model.url; 119 | else this.box.href = 'javascript: return false'; 120 | 121 | this.el.removeClass('complete'); 122 | this.el.removeClass('pending'); 123 | this.el.removeClass('unfinished'); 124 | this.el.removeClass('stale'); 125 | this.el.addClass(this.model.type); 126 | }, 127 | 128 | onError: function(e) { 129 | this.error.setStyle('display', 'block'); 130 | this.error_label.set('text', e.label); 131 | if (e.message_html) this.error_message.set('html', e.message_html); 132 | else this.error_message.set('text', e.message) 133 | if (e.details && window.btoa) { 134 | this.error_details.setStyle('display', 'block'); 135 | this.error_details.href = 'data:text/html;base64,' + window.btoa(e.details); 136 | } 137 | this.el.addClass('selected'); 138 | }, 139 | 140 | clearError: function() { 141 | this.error.setStyle('display', 'none'); 142 | }, 143 | 144 | isError: function() { 145 | return this.error.getStyle('display') == 'block'; 146 | } 147 | }); 148 | 149 | var WatcherView = new Class({ 150 | Extends: View, 151 | 152 | template: 'watcher-template', 153 | attributes: ['email', 'deletebtn'], 154 | 155 | initialize: function(model) { 156 | this.parent(model); 157 | var this_ = this; 158 | this.deletebtn.addEvent('click', function() { 159 | this_.model.del(); 160 | }); 161 | this.model.addEvent('error', function(e) { this_.onError(e); }); 162 | }, 163 | 164 | render: function() { 165 | this.email.set('text', this.model.email); 166 | }, 167 | 168 | onError: function(e) { 169 | var label = $$('#watch-error .label')[0]; 170 | var message = $$('#watch-error .message')[0]; 171 | var details = $$('#watch-error .details')[0]; 172 | 173 | if (e.label) label.set('text', e.label+'.'); 174 | if (e.message_html) 175 | message.set('html', e.message_html); 176 | else 177 | message.set('text', e.message); 178 | if (e.details) { 179 | details.href = 'data:text/html;base64,' + window.btoa(e.details); 180 | details.show(); 181 | } 182 | $('watch-error').show(); 183 | } 184 | }); 185 | -------------------------------------------------------------------------------- /fileshack/templates/fileshack/404.html: -------------------------------------------------------------------------------- 1 | {% extends "fileshack/base.html" %} 2 | {% load staticfiles %} 3 | {% load i18n %} 4 | {% block extrahead %} 5 | 6 | 7 | {% endblock %} 8 | {% block content %} 9 |
10 |
11 | {% if stores_number > 0 %} 12 | {% trans "There is no such page" %} 13 | {% else %} 14 | {% trans "There is nothing at all!" %} 15 | {% endif %} 16 |
17 |
18 | {% if store %} 19 | {% blocktrans with url=store.get_absolute_url %} 20 | Are you looking for this store? 21 | {% endblocktrans %} 22 | {% else %} 23 | {% if stores_number > 0 %} 24 | {% trans "Have you typed the correct URL?" %} 25 | {% else %} 26 | {% blocktrans with url=admin_url %} 27 | Use the admin interface to add stores 28 | {% endblocktrans %} 29 | {% endif %} 30 | {% endif %} 31 |
32 |
33 | {% endblock %} 34 | -------------------------------------------------------------------------------- /fileshack/templates/fileshack/500.html: -------------------------------------------------------------------------------- 1 | {% extends "fileshack/base.html" %} 2 | {% load staticfiles %} 3 | {% load i18n %} 4 | {% block extrahead %} 5 | 6 | 7 | {% endblock %} 8 | {% block content %} 9 |
10 |
{% trans "Request could not be served" %}
11 |
12 | {% trans "An error occurred in the application" %} 13 |
14 |
15 | {% endblock %} 16 | -------------------------------------------------------------------------------- /fileshack/templates/fileshack/accesscode.html: -------------------------------------------------------------------------------- 1 | {% extends "fileshack/base.html" %} 2 | {% load staticfiles %} 3 | {% load i18n %} 4 | {% block extrahead %} 5 | 6 | 7 | 8 | 9 | {% endblock %} 10 | {% block header %} 11 |

{% trans "Welcome to fileshack," %} {% trans "enter the access code" %}

12 | {% endblock %} 13 | {% block content %} 14 |
15 | {% csrf_token %} 16 | 17 | 18 |
19 | {% if error_label %} 20 |
{{ error_label }}
{{ error_message }}
21 | {% endif %} 22 | 23 |
24 |
25 | {% endblock %} 26 | -------------------------------------------------------------------------------- /fileshack/templates/fileshack/base.html: -------------------------------------------------------------------------------- 1 | {% load staticfiles %} 2 | {% load i18n %} 3 | 4 | 5 | 6 | {% block title %}Fileshack{% endblock %} 7 | 8 | 9 | 10 | {% block extrahead %}{% endblock %} 11 | 12 | 13 |
14 | {% block controls %}{% endblock %} 15 | {% block header %} 16 |

{% trans "Welcome to fileshack" %}, {% trans "drop your items" %}

17 | {% endblock %} 18 | {% block content %}{% endblock %} 19 |
20 | 21 | 22 | -------------------------------------------------------------------------------- /fileshack/templates/fileshack/iframe.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Fileshack 5 | 6 | 7 |
8 | 9 | 10 |
11 | 12 | 13 | -------------------------------------------------------------------------------- /fileshack/templates/fileshack/index.html: -------------------------------------------------------------------------------- 1 | {% extends "fileshack/base.html" %} 2 | {% load staticfiles %} 3 | {% load i18n %} 4 | {% block extrahead %} 5 | 6 | 7 | 8 | 9 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | {% endblock %} 25 | {% block controls %} 26 |
27 | {% if store.accesscode %} 28 |
29 | 30 | 31 |
32 | {% endif %} 33 | {% if store.allow_watch %} 34 | 35 | 56 | {% endif %} 57 |
58 | {% endblock %} 59 | {% block content %} 60 |
61 | 62 |
63 | {% trans "Drop your item here or click" %} 64 | 65 |
66 | {% trans "Upload" %} 67 | 68 |
69 | 70 |
71 |
72 | 75 | 108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 | {% endblock %} 117 | -------------------------------------------------------------------------------- /fileshack/templates/fileshack/unsubscribe.html: -------------------------------------------------------------------------------- 1 | {% load staticfiles %} 2 | {% load i18n %} 3 | 4 | 5 | 6 | Fileshack 7 | 8 | 9 | 10 | 11 | 12 |
13 |

Welcome to fileshack, drop your items

14 | {% if result == "success" %} 15 |
16 |
{% trans "Subscription removed" %}
17 |
18 | {% trans "You will not receive any more messages" %} 19 |
20 |
21 | {% else %} 22 |
23 |
24 | {% if result == "doesnotexist" %} 25 | {% trans "Actually," %} 26 | {% else %} 27 | {% trans "Request rejected" %} 28 | {% endif %} 29 |
30 |
31 | {% if result == "doesnotexist" %} 32 | {% trans "The e-mail address is not in our database" %} 33 | {% else %} 34 | {% trans "The verification code in the URL is invalid" %} 35 | {% endif %} 36 |
37 |
38 | {% endif %} 39 |
40 | 41 | 42 | -------------------------------------------------------------------------------- /fileshack/templatetags/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterkuma/fileshackproject/3676f17f48a219aa31ca564ab8747093586fb8a1/fileshack/templatetags/__init__.py -------------------------------------------------------------------------------- /fileshack/templatetags/staticfiles.py: -------------------------------------------------------------------------------- 1 | from urllib.parse import urljoin 2 | from django import template 3 | from django.conf import settings 4 | 5 | register = template.Library() 6 | 7 | @register.simple_tag 8 | def static(path): 9 | return urljoin(settings.STATIC_URL, path) 10 | -------------------------------------------------------------------------------- /fileshack/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import include 2 | from django.urls import re_path 3 | from fileshack.views import * 4 | 5 | app_name = 'fileshack' 6 | 7 | urlpatterns = [ 8 | re_path(r'^cron/$', cron, name='cron'), 9 | re_path(r'^unsubscribe/$', unsubscribe, name='unsubscribe'), 10 | re_path(r'^(?P.*)logout/$', logout), 11 | re_path(r'^(?P.*)iframe/$', iframe), 12 | re_path(r'^(?P.*)upload/$', simple_upload), 13 | re_path(r'^(?P.*)upload/(?P[^/]+)/$', upload), 14 | re_path(r'^(?P.*)delete/(?P[0-9]+)/$', delete), 15 | re_path(r'^(?P.*)download/(?P[0-9]+)/$', download, name='download'), 16 | re_path(r'^(?P.*)update/$', update), 17 | re_path(r'^(?P.*)update/(?P[0-9]{4}-[0-9]{2}-[0-9]{2}_[0-9]{2}:[0-9]{2}:[0-9]{2})/$', update), 18 | re_path(r'^(?P.*)unwatch/$', unwatch), 19 | re_path(r'^(?P.*)watch/$', watch), 20 | re_path(r'^(?P.*)/$', index, name='index'), 21 | re_path(r'^(?P)$', index), 22 | ] 23 | -------------------------------------------------------------------------------- /fileshackproject/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterkuma/fileshackproject/3676f17f48a219aa31ca564ab8747093586fb8a1/fileshackproject/__init__.py -------------------------------------------------------------------------------- /fileshackproject/settings.py: -------------------------------------------------------------------------------- 1 | # Django settings for fileshackproject. 2 | import os 3 | 4 | BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 5 | 6 | DEBUG = True 7 | TEMPLATE_DEBUG = DEBUG 8 | 9 | ADMINS = ( 10 | # ('Your Name', 'your_email@example.com'), 11 | ) 12 | 13 | SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https') 14 | 15 | MANAGERS = ADMINS 16 | 17 | DATABASES = { 18 | 'default': { 19 | 'ENGINE': 'django.db.backends.sqlite3', # Add 'postgresql_psycopg2', 'mysql', 'sqlite3' or 'oracle'. 20 | 'NAME': os.path.join(BASE_DIR, 'db/fileshack.sqlite'), # Or path to database file if using sqlite3. 21 | 'USER': '', # Not used with sqlite3. 22 | 'PASSWORD': '', # Not used with sqlite3. 23 | 'HOST': '', # Set to empty string for localhost. Not used with sqlite3. 24 | 'PORT': '', # Set to empty string for default. Not used with sqlite3. 25 | } 26 | } 27 | 28 | # Local time zone for this installation. Choices can be found here: 29 | # http://en.wikipedia.org/wiki/List_of_tz_zones_by_name 30 | # although not all choices may be available on all operating systems. 31 | # On Unix systems, a value of None will cause Django to use the same 32 | # timezone as the operating system. 33 | # If running in a Windows environment this must be set to the same as your 34 | # system time zone. 35 | TIME_ZONE = 'UTC' 36 | 37 | # Language code for this installation. All choices can be found here: 38 | # http://www.i18nguy.com/unicode/language-identifiers.html 39 | LANGUAGE_CODE = 'en-us' 40 | 41 | SITE_ID = 1 42 | 43 | # If you set this to False, Django will make some optimizations so as not 44 | # to load the internationalization machinery. 45 | USE_I18N = True 46 | 47 | # If you set this to False, Django will not format dates, numbers and 48 | # calendars according to the current locale. 49 | USE_L10N = True 50 | 51 | # If you set this to False, Django will not use timezone-aware datetimes. 52 | USE_TZ = True 53 | 54 | # Absolute filesystem path to the directory that will hold user-uploaded files. 55 | # Example: "/home/media/media.lawrence.com/media/" 56 | MEDIA_ROOT = os.path.join(BASE_DIR, 'media/') 57 | 58 | # URL that handles the media served from MEDIA_ROOT. Make sure to use a 59 | # trailing slash. 60 | # Examples: "http://media.lawrence.com/media/", "http://example.com/media/" 61 | MEDIA_URL = '/media/' 62 | 63 | # Absolute path to the directory static files should be collected to. 64 | # Don't put anything in this directory yourself; store your static files 65 | # in apps' "static/" subdirectories and in STATICFILES_DIRS. 66 | # Example: "/home/media/media.lawrence.com/static/" 67 | STATIC_ROOT = os.path.join(BASE_DIR, 'static/') 68 | 69 | # URL prefix for static files. 70 | # Example: "http://media.lawrence.com/static/" 71 | STATIC_URL = '/static/' 72 | 73 | # Additional locations of static files 74 | STATICFILES_DIRS = ( 75 | # Put strings here, like "/home/html/static" or "C:/www/django/static". 76 | # Always use forward slashes, even on Windows. 77 | # Don't forget to use absolute paths, not relative paths. 78 | ) 79 | 80 | # List of finder classes that know how to find static files in 81 | # various locations. 82 | STATICFILES_FINDERS = ( 83 | 'django.contrib.staticfiles.finders.FileSystemFinder', 84 | 'django.contrib.staticfiles.finders.AppDirectoriesFinder', 85 | # 'django.contrib.staticfiles.finders.DefaultStorageFinder', 86 | ) 87 | 88 | # Make this unique, and don't share it with anybody. 89 | SECRET_KEY = '-nn1sd&@mo4q4oztc*$c*d!l11)&%q@dxa^(zbtte6m1q#4_p_' 90 | 91 | TEMPLATES = [ 92 | { 93 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 94 | 'DIRS': [], 95 | 'APP_DIRS': True, 96 | 'OPTIONS': { 97 | 'context_processors': [ 98 | 'django.contrib.auth.context_processors.auth', 99 | 'django.contrib.messages.context_processors.messages', 100 | 'django.template.context_processors.request' 101 | ] 102 | } 103 | }, 104 | ] 105 | 106 | # List of callables that know how to import templates from various sources. 107 | TEMPLATE_LOADERS = ( 108 | 'django.template.loaders.filesystem.Loader', 109 | 'django.template.loaders.app_directories.Loader', 110 | # 'django.template.loaders.eggs.Loader', 111 | ) 112 | 113 | MIDDLEWARE = ( 114 | 'django.middleware.common.CommonMiddleware', 115 | 'django.contrib.sessions.middleware.SessionMiddleware', 116 | 'django.middleware.csrf.CsrfViewMiddleware', 117 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 118 | 'django.contrib.messages.middleware.MessageMiddleware', 119 | # Uncomment the next line for simple clickjacking protection: 120 | # 'django.middleware.clickjacking.XFrameOptionsMiddleware', 121 | ) 122 | 123 | ROOT_URLCONF = 'fileshackproject.urls' 124 | 125 | # Python dotted path to the WSGI application used by Django's runserver. 126 | WSGI_APPLICATION = 'fileshackproject.wsgi.application' 127 | 128 | TEMPLATE_DIRS = ( 129 | # Put strings here, like "/home/html/django_templates" or "C:/www/django/templates". 130 | # Always use forward slashes, even on Windows. 131 | # Don't forget to use absolute paths, not relative paths. 132 | ) 133 | 134 | INSTALLED_APPS = ( 135 | 'django.contrib.auth', 136 | 'django.contrib.contenttypes', 137 | 'django.contrib.sessions', 138 | 'django.contrib.sites', 139 | 'django.contrib.messages', 140 | 'django.contrib.staticfiles', 141 | 'django.contrib.admin', 142 | 'django.contrib.admindocs', 143 | 'fileshack', 144 | ) 145 | 146 | SESSION_EXPIRE_AT_BROWSER_CLOSE = True 147 | 148 | # Serve static and media files even when DEBUG == False. 149 | SERVE_STATIC = False 150 | 151 | DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' 152 | 153 | # List of hosts which are allowed to run scheduled tasks. 154 | FILESHACK_CRON_HOSTS = ('localhost',) 155 | 156 | # Shared secret for running scheduled tasks from hosts not listed 157 | # in FILESHACK_CRON_HOSTS. 158 | FILESHACK_CRON_SECRET = '' 159 | 160 | FILESHACK_EMAIL_FROM = 'no-reply@example.org' 161 | 162 | # Override the settings with local modifications. 163 | from .settings_local import * 164 | -------------------------------------------------------------------------------- /fileshackproject/settings_local-example.py: -------------------------------------------------------------------------------- 1 | #DEBUG = False 2 | 3 | #ALLOWED_HOSTS = [] 4 | 5 | #ADMINS = ( 6 | # # ('Your Name', 'your_email@example.com'), 7 | #) 8 | 9 | #DATABASES = { 10 | # 'default': { 11 | # 'ENGINE': 'django.db.backends.sqlite3', # Add 'postgresql_psycopg2', 'mysql', 'sqlite3' or 'oracle'. 12 | # 'NAME': '/var/www/fileshackproject/fileshack.sqlite', # Or path to database file if using sqlite3. 13 | # 'USER': '', # Not used with sqlite3. 14 | # 'PASSWORD': '', # Not used with sqlite3. 15 | # 'HOST': '', # Set to empty string for localhost. Not used with sqlite3. 16 | # 'PORT': '', # Set to empty string for default. Not used with sqlite3. 17 | # } 18 | #} 19 | 20 | #TIME_ZONE = 'UTC' 21 | 22 | #MEDIA_ROOT = '/var/www/fileshackproject/media/' 23 | #MEDIA_URL = '/media/' 24 | #STATIC_ROOT = '/var/www/fileshackproject/static/' 25 | #STATIC_URL = '/static/' 26 | 27 | # Serve static and media files even when DEBUG == False. 28 | #SERVE_STATIC = True 29 | 30 | # List of hosts which are allowed to run scheduled tasks. 31 | #FILESHACK_CRON_HOSTS = ('localhost',) 32 | 33 | # Shared secret for running scheduled tasks from hosts not listed 34 | # in FILESHACK_CRON_HOSTS. 35 | #FILESHACK_CRON_SECRET = '' 36 | 37 | #EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend' 38 | #EMAIL_HOST = 'localhost' 39 | #EMAIL_HOST_PASSWORD = '' 40 | #EMAIL_HOST_USER = '' 41 | #EMAIL_PORT = 25 42 | #EMAIL_USE_TLS = True 43 | 44 | # E-mail address from which notifications are sent. 45 | #FILESHACK_EMAIL_FROM = 'no-reply@example.org' 46 | 47 | # Fileshack: Be sure to fill in a unique SECRET_KEY. Otherwise recipients 48 | # of e-mail updates will not be able to unsubscribe by following a link. 49 | # 50 | # Use python3 -c 'from random import choice; print("".join([choice("abcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*(-_=+)") for i in range(50)]))' 51 | # to generate a unique SECRET_KEY. 52 | #SECRET_KEY = '' 53 | -------------------------------------------------------------------------------- /fileshackproject/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | from django.conf.urls import include 3 | from django.urls import re_path 4 | from django.contrib import admin 5 | from django.conf import settings 6 | import django.views.static 7 | import fileshack.urls 8 | 9 | admin.autodiscover() 10 | 11 | handler404 = 'fileshack.views.page_not_found' 12 | handler500 = 'fileshack.views.server_error' 13 | 14 | urlpatterns = [] 15 | 16 | if settings.DEBUG or settings.SERVE_STATIC: 17 | urlpatterns += [ 18 | re_path(r'^static/(?P.*)$', django.views.static.serve, {'document_root': settings.STATIC_ROOT}), 19 | re_path(r'^media/(?P.*)$', django.views.static.serve, {'document_root': settings.MEDIA_ROOT}), 20 | ] 21 | 22 | urlpatterns += [ 23 | # Be sure to comment out the following line in a production environment! 24 | #url(r'^static/(?P.*)$', 'django.views.static.serve', {'document_root': settings.STATIC_ROOT}), 25 | #url(r'^media/(?P.*)$', 'django.views.static.serve', {'document_root': settings.MEDIA_ROOT}), 26 | path('admin/doc/', include('django.contrib.admindocs.urls')), 27 | path('admin/', admin.site.urls), 28 | re_path(r'^', include(fileshack.urls)), 29 | ] 30 | -------------------------------------------------------------------------------- /fileshackproject/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for fileshackproject project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/3.2/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.wsgi import get_wsgi_application 13 | 14 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'fileshackproject.settings') 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /fileshackproject/wsgi_virtualenv.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for fileshackproject. 3 | 4 | This module contains the WSGI application used by Django's development server 5 | and any production WSGI deployments. It should expose a module-level variable 6 | named ``application``. Django's ``runserver`` and ``runfcgi`` commands discover 7 | this application via the ``WSGI_APPLICATION`` setting. 8 | 9 | Usually you will have the standard Django WSGI application here, but it also 10 | might make sense to replace the whole Django WSGI application with a custom one 11 | that later delegates to the Django one. For example, you could introduce WSGI 12 | middleware here, or combine a Django application with an application of another 13 | framework. 14 | 15 | """ 16 | import os 17 | import sys 18 | 19 | from os.path import dirname 20 | activate_this = os.path.join(dirname(dirname(dirname(__file__))), 'bin/activate_this.py') 21 | execfile(activate_this, dict(__file__=activate_this)) 22 | 23 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "fileshackproject.settings") 24 | sys.path.append(os.path.dirname(os.path.dirname(__file__))) 25 | 26 | # This application object is used by any WSGI server configured to use this 27 | # file. This includes Django's development server, if the WSGI_APPLICATION 28 | # setting points here. 29 | #from django.core.wsgi import get_wsgi_application 30 | #application = get_wsgi_application() 31 | 32 | import django.core.handlers.wsgi 33 | application = django.core.handlers.wsgi.WSGIHandler() 34 | 35 | # Apply WSGI middleware here. 36 | # from helloworld.wsgi import HelloWorldApplication 37 | # application = HelloWorldApplication(application) 38 | -------------------------------------------------------------------------------- /manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import os, sys 3 | 4 | if __name__ == "__main__": 5 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "fileshackproject.settings") 6 | 7 | from django.core.management import execute_from_command_line 8 | 9 | execute_from_command_line(sys.argv) 10 | -------------------------------------------------------------------------------- /media/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | 4 | -------------------------------------------------------------------------------- /static/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | 4 | --------------------------------------------------------------------------------