diff options
author | RaNaN <Mast3rRaNaN@hotmail.de> | 2011-02-02 21:46:15 +0100 |
---|---|---|
committer | RaNaN <Mast3rRaNaN@hotmail.de> | 2011-02-02 21:46:15 +0100 |
commit | 7a503302fbe6fcc23af94de2fe313298c3a6d95c (patch) | |
tree | 724a8581008d3d78fed0be965161e330eb4b1064 /module/web | |
parent | closes #226, #227 (diff) | |
download | pyload-7a503302fbe6fcc23af94de2fe313298c3a6d95c.tar.xz |
removed django => now using bottle, new builtin threaded ssl server
Dont forget to install jinja and beaker !
Diffstat (limited to 'module/web')
32 files changed, 3208 insertions, 997 deletions
diff --git a/module/web/ServerThread.py b/module/web/ServerThread.py index db5e3be05..7f47f80c6 100644 --- a/module/web/ServerThread.py +++ b/module/web/ServerThread.py @@ -1,17 +1,11 @@ #!/usr/bin/env python from __future__ import with_statement from os.path import exists -from os.path import join -from os.path import abspath -from os import makedirs -from subprocess import PIPE -from subprocess import Popen -from subprocess import call -from sys import version_info -from cStringIO import StringIO import threading -import sys import logging +import sqlite3 + +import webinterface core = None log = logging.getLogger("log") @@ -25,188 +19,108 @@ class WebServer(threading.Thread): self.running = True self.server = pycore.config['webinterface']['server'] self.https = pycore.config['webinterface']['https'] + self.cert = pycore.config["ssl"]["cert"] + self.key = pycore.config["ssl"]["key"] + self.host = pycore.config['webinterface']['host'] + self.port = pycore.config['webinterface']['port'] + self.setDaemon(True) - + def run(self): - sys.path.append(join(pypath, "module", "web")) - avail = ["builtin"] - host = self.core.config['webinterface']['host'] - port = self.core.config['webinterface']['port'] - serverpath = join(pypath, "module", "web") - path = join(abspath(""), "servers") - out = StringIO() - - if not exists("pyload.db"): - #print "########## IMPORTANT ###########" - #print "### Database for Webinterface does not exitst, it will not be available." - #print "### Please run: python %s syncdb" % join(self.pycore.path, "module", "web", "manage.py") - #print "### You have to add at least one User, to gain access to webinterface: python %s createsuperuser" % join(self.pycore.path, "module", "web", "manage.py") - #print "### Dont forget to restart pyLoad if you are done." + self.checkDB() + + if self.https: + if not exists(self.cert) or not exists(self.key): + log.warning(_("SSL certificates not found.")) + self.https = False + + if self.server in ("lighttpd", "nginx"): + log.warning(_("Sorry, we dropped support for starting %s directly within pyLoad") % self.server) + log.warning(_("You can use the threaded server which offers good performance and ssl,")) + log.warning(_("of course you can still use your existing %s with pyLoads fastcgi server") % self.server) + log.warning(_("sample configs are located in the module/web/servers directory")) + self.server = "builtin" + + if self.server == "fastcgi": + try: + import flup + except: + log.warning(_("Can't use %(server)s, python-flup is not installed!") % { + "server": self.server}) + self.server = "builtin" + + if self.server == "fastcgi": + self.start_fcgi() + elif self.server == "threaded": + self.start_threaded() + else: + self.start_builtin() + + + def checkDB(self): + conn = sqlite3.connect('web.db') + c = conn.cursor() + c.execute("SELECT * from users LIMIT 1") + empty = True + if c.fetchone(): + empty = False + + c.close() + conn.close() + + if not empty: + return True + + if exists("pyload.db"): + log.info(_("Converting old database to new web.db")) + conn = sqlite3.connect('pyload.db') + c = conn.cursor() + c.execute("SELECT username, password, email from auth_user WHERE is_superuser") + users = [] + for r in c: + pw = r[1].split("$") + users.append((r[0], pw[1] + pw[2], r[2])) + + c.close() + conn.close() + + conn = sqlite3.connect('web.db') + c = conn.cursor() + c.executemany("INSERT INTO users(name, password, email) VALUES (?,?,?)", users) + conn.commit() + c.close() + conn.close() + return True + + else: log.warning(_("Database for Webinterface does not exitst, it will not be available.")) log.warning(_("Please run: python pyLoadCore.py -s")) log.warning(_("Go through the setup and create a database and add an user to gain access.")) - return None - - try: - import flup - avail.append("fastcgi") - except: - pass - - try: - call(["lighttpd", "-v"], stdout=PIPE, stderr=PIPE) - import flup - avail.append("lighttpd") - - except: - pass - - try: - call(["nginx", "-v"], stdout=PIPE, stderr=PIPE) - import flup - avail.append("nginx") - except: - pass - - - try: - if self.https: - if exists(self.core.config["ssl"]["cert"]) and exists(self.core.config["ssl"]["key"]): - if not exists("ssl.pem"): - key = file(self.core.config["ssl"]["key"], "rb") - cert = file(self.core.config["ssl"]["cert"], "rb") - - pem = file("ssl.pem", "wb") - pem.writelines(key.readlines()) - pem.writelines(cert.readlines()) - - key.close() - cert.close() - pem.close() - - else: - log.warning(_("SSL certificates not found.")) - self.https = False - else: - pass - except: - self.https = False - - - if not self.server in avail: - log.warning(_("Can't use %(server)s, either python-flup or %(server)s is not installed!") % {"server": self.server}) - self.server = "builtin" + return False + + + def start_builtin(self): + + if self.https: + log.warning(_("The simple builtin server offers no SSL, please consider using threaded instead")) + self.core.log.info(_("Starting builtin webserver: %(host)s:%(port)d") % {"host": self.host, "port": self.port}) + webinterface.run_simple(host=self.host, port=self.port) - if self.server == "nginx": - - if not exists(join(path, "nginx")): - makedirs(join(path, "nginx")) - - config = file(join(serverpath, "servers", "nginx_default.conf"), "rb") - content = config.read() - config.close() - - content = content.replace("%(path)", join(path, "nginx")) - content = content.replace("%(host)", host) - content = content.replace("%(port)", str(port)) - content = content.replace("%(media)", join(serverpath, "media")) - content = content.replace("%(version)", ".".join(map(str, version_info[0:2]))) - - if self.https: - content = content.replace("%(ssl)", """ - ssl on; - ssl_certificate %s; - ssl_certificate_key %s; - """ % (abspath(self.core.config["ssl"]["cert"]), abspath(self.core.config["ssl"]["key"]) )) - else: - content = content.replace("%(ssl)", "") - - new_config = file(join(path, "nginx.conf"), "wb") - new_config.write(content) - new_config.close() - - command = ['nginx', '-c', join(path, "nginx.conf")] - self.p = Popen(command, stderr=PIPE, stdin=PIPE, stdout=Output(out)) - - log.info(_("Starting nginx Webserver: %(host)s:%(port)d") % {"host": host, "port": port}) - import run_fcgi - run_fcgi.handle("daemonize=false", "method=threaded", "host=127.0.0.1", "port=9295") - - - elif self.server == "lighttpd": - - if not exists(join(path, "lighttpd")): - makedirs(join(path, "lighttpd")) - - - config = file(join(serverpath, "servers", "lighttpd_default.conf"), "rb") - content = config.readlines() - config.close() - content = "".join(content) - - content = content.replace("%(path)", join("servers", "lighttpd")) - content = content.replace("%(host)", host) - content = content.replace("%(port)", str(port)) - content = content.replace("%(media)", join(serverpath, "media")) - content = content.replace("%(version)", ".".join(map(str, version_info[0:2]))) - - if self.https: - content = content.replace("%(ssl)", """ - ssl.engine = "enable" - ssl.pemfile = "%s" - ssl.ca-file = "%s" - """ % (abspath("ssl.pem") , abspath(self.core.config["ssl"]["cert"])) ) - else: - content = content.replace("%(ssl)", "") - new_config = file(join("servers", "lighttpd.conf"), "wb") - new_config.write(content) - new_config.close() - - command = ['lighttpd', '-D', '-f', join(path, "lighttpd.conf")] - self.p = Popen(command, stderr=PIPE, stdin=PIPE, stdout=Output(out)) - - log.info(_("Starting lighttpd Webserver: %(host)s:%(port)d") % {"host": host, "port": port}) - import run_fcgi - run_fcgi.handle("daemonize=false", "method=threaded", "host=127.0.0.1", "port=9295") - - - elif self.server == "fastcgi": - #run fastcgi on port - import run_fcgi - run_fcgi.handle("daemonize=false", "method=threaded", "host=127.0.0.1", "port=%s" % str(port)) + def start_threaded(self): + if self.https: + self.core.log.info(_("Starting threaded SSL webserver: %(host)s:%(port)d") % {"host": self.host, "port": self.port}) else: - self.core.log.info(_("Starting django builtin Webserver: %(host)s:%(port)d") % {"host": host, "port": port}) - import run_server - run_server.handle(host, port) + self.cert = "" + self.key = "" + self.core.log.info(_("Starting threaded webserver: %(host)s:%(port)d") % {"host": self.host, "port": self.port}) - def quit(self): + webinterface.run_threaded(host=self.host, port=self.port, cert=self.cert, key=self.key) + + def start_fcgi(self): - try: - if self.server == "lighttpd" or self.server == "nginx": - self.p.kill() - #self.p2.kill() - return True - - else: - #self.p.kill() - return True - except: - pass - - - self.running = False - -class Output: - def __init__(self, stream): - self.stream = stream - - def fileno(self): - return 1 - - def write(self, data): # Do nothing - return None - #self.stream.write(data) - #self.stream.flush() - def __getattr__(self, attr): - return getattr(self.stream, attr) + self.core.log.info(_("Starting fastcgi server: %(host)s:%(port)d") % {"host": self.host, "port": self.port}) + webinterface.run_fcgi(host=self.host, port=self.port) + + def quit(self): + self.running = False
\ No newline at end of file diff --git a/module/web/cnl_app.py b/module/web/cnl_app.py new file mode 100644 index 000000000..058a298d3 --- /dev/null +++ b/module/web/cnl_app.py @@ -0,0 +1,151 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +from os.path import join +import re +from urllib import unquote +from base64 import standard_b64decode +from binascii import unhexlify + +from bottle import route, request, HTTPError +from webinterface import PYLOAD, DL_ROOT, JS + +try: + from Crypto.Cipher import AES +except: + pass + + +def local_check(function): + def _view(*args, **kwargs): + if request.environ.get('REMOTE_ADDR', "0") in ('127.0.0.1', 'localhost') \ + or request.environ.get('HTTP_HOST','0') == '127.0.0.1:9666': + return function(*args, **kwargs) + else: + return HTTPError(403, "Forbidden") + + return _view + + +@route("/flash") +@route("/flash", method="POST") +@local_check +def flash(): + return "JDownloader" + +@route("/flash/add", method="POST") +@local_check +def add(request): + package = request.POST.get('referer', 'ClickAndLoad Package') + urls = filter(lambda x: x != "", request.POST['urls'].split("\n")) + + PYLOAD.add_package(package, urls, False) + + return "" + +@route("/flash/addcrypted", method="POST") +@local_check +def addcrypted(): + + package = request.forms.get('referer', 'ClickAndLoad Package') + dlc = request.forms['crypted'].replace(" ", "+") + + dlc_path = join(DL_ROOT, package.replace("/", "").replace("\\", "").replace(":", "") + ".dlc") + dlc_file = file(dlc_path, "wb") + dlc_file.write(dlc) + dlc_file.close() + + try: + PYLOAD.add_package(package, [dlc_path], False) + except: + return HTTPError() + else: + return "success" + +@route("/flash/addcrypted2", method="POST") +@local_check +def addcrypted2(): + + package = request.forms.get("source", "ClickAndLoad Package") + crypted = request.forms["crypted"] + jk = request.forms["jk"] + + crypted = standard_b64decode(unquote(crypted.replace(" ", "+"))) + if JS: + jk = "%s f()" % jk + jk = JS.eval(jk) + + else: + try: + jk = re.findall(r"return ('|\")(.+)('|\")", jk)[0][1] + except: + ## Test for some known js functions to decode + if jk.find("dec") > -1 and jk.find("org") > -1: + org = re.findall(r"var org = ('|\")([^\"']+)", jk)[0][1] + jk = list(org) + jk.reverse() + jk = "".join(jk) + else: + print "Could not decrypt key, please install py-spidermonkey or ossp-js" + + try: + Key = unhexlify(jk) + except: + print "Could not decrypt key, please install py-spidermonkey or ossp-js" + return "failed" + + IV = Key + + obj = AES.new(Key, AES.MODE_CBC, IV) + result = obj.decrypt(crypted).replace("\x00", "").replace("\r","").split("\n") + + result = filter(lambda x: x != "", result) + + try: + PYLOAD.add_package(package, result, False) + except: + return "failed can't add" + else: + return "success" + +@route("/flashgot", method="POST") +@local_check +def flashgot(request): + if request.environ['HTTP_REFERER'] != "http://localhost:9666/flashgot" and request.environ['HTTP_REFERER'] != "http://127.0.0.1:9666/flashgot": + return HTTPError() + + autostart = int(request.forms.get('autostart', 0)) + package = request.forms.get('package', "FlashGot") + urls = filter(lambda x: x != "", request.forms['urls'].split("\n")) + folder = request.forms.get('dir', None) + + PYLOAD.add_package(package, urls, autostart) + + return "" + +@route("/crossdomain.xml") +@local_check +def crossdomain(): + rep = "<?xml version=\"1.0\"?>\n" + rep += "<!DOCTYPE cross-domain-policy SYSTEM \"http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd\">\n" + rep += "<cross-domain-policy>\n" + rep += "<allow-access-from domain=\"*\" />\n" + rep += "</cross-domain-policy>" + return rep + + +@route("/flash/checkSupportForUrl") +@local_check +def checksupport(): + + url = request.GET.get("url") + res = PYLOAD.checkURLs([url]) + supported = (not res[0][1] is None) + + return str(supported).lower() + +@route("/jdcheck.js") +@local_check +def jdcheck(): + rep = "jdownloader=true;\n" + rep += "var version='9.581;'" + return rep diff --git a/module/web/createsuperuser.py b/module/web/createsuperuser.py deleted file mode 100644 index 0ff1d15b8..000000000 --- a/module/web/createsuperuser.py +++ /dev/null @@ -1,43 +0,0 @@ -""" -Management utility to create superusers. -""" - -import os -import sys - -os.environ["DJANGO_SETTINGS_MODULE"] = 'settings' -sys.path.append(os.path.join(pypath, "module", "web")) - -import getpass -import re -from optparse import make_option -from django.contrib.auth.models import User -from django.core import exceptions -from django.core.management.base import BaseCommand, CommandError -from django.utils.translation import ugettext as _ - -RE_VALID_USERNAME = re.compile('[\w.@+-]+$') - - -def handle(username, email): - #username = options.get('username', None) - #email = options.get('email', None) - interactive = False - - # Do quick and dirty validation if --noinput - if not interactive: - if not username or not email: - raise CommandError("You must use --username and --email with --noinput.") - if not RE_VALID_USERNAME.match(username): - raise CommandError("Invalid username. Use only letters, digits, and underscores") - - password = '' - default_username = '' - - User.objects.create_superuser(username, email, password) - print "Superuser created successfully." - -if __name__ == "__main__": - username = sys.argv[1] - email = sys.argv[2] - handle(username, email)
\ No newline at end of file diff --git a/module/web/filters.py b/module/web/filters.py new file mode 100644 index 000000000..1b10f7cb4 --- /dev/null +++ b/module/web/filters.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +import os +from os.path import abspath, commonprefix, join +from time import strftime, mktime, gmtime + +quotechar = "::/" + +try: + from os.path import relpath +except: + from posixpath import curdir, sep, pardir + def relpath(path, start=curdir): + """Return a relative version of a path""" + if not path: + raise ValueError("no path specified") + start_list = abspath(start).split(sep) + path_list = abspath(path).split(sep) + # Work out how much of the filepath is shared by start and path. + i = len(commonprefix([start_list, path_list])) + rel_list = [pardir] * (len(start_list)-i) + path_list[i:] + if not rel_list: + return curdir + return join(*rel_list) + + +def quotepath(path): + try: + return path.replace("../", quotechar) + except AttributeError: + return path + except: + return "" + +def unquotepath(path): + try: + return path.replace(quotechar, "../") + except AttributeError: + return path + except: + return "" + +def path_make_absolute(path): + p = os.path.abspath(path) + if p[-1] == os.path.sep: + return p + else: + return p + os.path.sep + +def path_make_relative(path): + p = relpath(path) + if p[-1] == os.path.sep: + return p + else: + return p + os.path.sep + +def truncate(value, n): + if (n - len(value)) < 3: + return value[:n]+"..." + return value + +def date(date, format): + return date
\ No newline at end of file diff --git a/module/web/json_app.py b/module/web/json_app.py new file mode 100644 index 000000000..2c95eaf5b --- /dev/null +++ b/module/web/json_app.py @@ -0,0 +1,322 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import base64 +from os.path import join +from traceback import print_exc + +from bottle import route, request, HTTPError, validate + +from webinterface import PYLOAD + +from utils import login_required + + +def format_time(seconds): + seconds = int(seconds) + + hours, seconds = divmod(seconds, 3600) + minutes, seconds = divmod(seconds, 60) + return "%.2i:%.2i:%.2i" % (hours, minutes, seconds) + +def get_sort_key(item): + return item["order"] + + +@route("/json/status") +@route("/json/status", method="POST") +@login_required('can_see_dl') +def status(): + try: + status = PYLOAD.status_server() + status['captcha'] = PYLOAD.is_captcha_waiting() + return status + except: + return HTTPError() + + +@route("/json/links") +@route("/json/links", method="POST") +@login_required('can_see_dl') +def links(): + try: + links = PYLOAD.status_downloads() + ids = [] + for link in links: + ids.append(link['id']) + + if link['status'] == 12: + link['info'] = "%s @ %s kb/s" % (link['format_eta'], round(link['speed'], 2)) + elif link['status'] == 5: + link['percent'] = 0 + link['size'] = 0 + link['kbleft'] = 0 + link['info'] = _("waiting %s") % link['format_wait'] + else: + link['info'] = "" + + data = {'links': links, 'ids': ids} + return data + except Exception, e: + return HTTPError() + +@route("/json/queue") +@login_required('can_see_dl') +def queue(): + try: + return PYLOAD.get_queue() + + except: + return HTTPError() + + +@route("/json/pause") +@login_required('can_change_satus') +def pause(): + try: + return PYLOAD.pause_server() + + except: + return HTTPError() + + +@route("/json/unpause") +@login_required('can_change_status') +def unpause(): + try: + return PYLOAD.unpause_server() + + except: + return HTTPError() + + +@route("/json/cancel") +@login_required('can_change_status') +def cancel(): + try: + return PYLOAD.stop_downloads() + except: + return HTTPError() + +@route("/json/packages") +@login_required('can_see_dl') +def packages(): + try: + data = PYLOAD.get_queue() + + for package in data: + package['links'] = [] + for file in PYLOAD.get_package_files(package['id']): + package['links'].append(PYLOAD.get_file_info(file)) + + return data + + except: + return HTTPError() + +@route("/json/package/:id") +@validate(id=int) +@login_required('pyload.can_see_dl') +def package(id): + try: + data = PYLOAD.get_package_data(id) + + for pyfile in data["links"].itervalues(): + if pyfile["status"] == 0: + pyfile["icon"] = "status_finished.png" + elif pyfile["status"] in (2, 3): + pyfile["icon"] = "status_queue.png" + elif pyfile["status"] in (9, 1): + pyfile["icon"] = "status_offline.png" + elif pyfile["status"] == 5: + pyfile["icon"] = "status_waiting.png" + elif pyfile["status"] == 8: + pyfile["icon"] = "status_failed.png" + elif pyfile["status"] in (11, 13): + pyfile["icon"] = "status_proc.png" + else: + pyfile["icon"] = "status_downloading.png" + + tmp = data["links"].values() + tmp.sort(key=get_sort_key) + data["links"] = tmp + return data + + except: + return HTTPError() + +@route("/json/package_order/:ids") +@login_required('can_add') +def package_order(ids): + try: + pid, pos = ids.split("|") + PYLOAD.order_package(int(pid), int(pos)) + return "success" + except: + return HTTPError() + +@route("/json/link/:id") +@validate(id=int) +@login_required('can_see_dl') +def link(id): + try: + data = PYLOAD.get_file_info(id) + return data + except: + return HTTPError() + +@route("/json/remove_link/:id") +@validate(id=int) +@login_required('can_delete') +def remove_link(id): + try: + PYLOAD.del_links([id]) + return "success" + except Exception, e: + return HTTPError() + +@route("/json/restart_link/:id") +@validate(id=int) +@login_required('can_add') +def restart_link(id): + try: + PYLOAD.restart_file(id) + return "success" + except Exception: + return HTTPError() + +@route("/json/abort_link/:id") +@validate(id=int) +@login_required('can_delete') +def abort_link(id): + try: + PYLOAD.stop_download("link", id) + return "success" + except: + return HTTPError() + +@route("/json/link_order/:ids") +@login_required('can_add') +def link_order(ids): + try: + pid, pos = ids.split("|") + PYLOAD.order_file(int(pid), int(pos)) + return "success" + except: + return HTTPError() + +@route("/json/add_package") +@route("/json/add_package", method="POST") +@login_required('can_add') +def add_package(): + name = request.forms['add_name'] + queue = int(request.forms['add_dest']) + links = request.forms['add_links'].split("\n") + pw = request.forms.get("add_password", "").strip("\n\r") + + try: + f = request.files['add_file'] + + if name is None or name == "": + name = f.name + + fpath = join(PYLOAD.get_conf_val("general", "download_folder"), "tmp_" + f.name) + destination = open(fpath, 'wb') + for chunk in f.chunks(): + destination.write(chunk) + destination.close() + links.insert(0, fpath) + except: + pass + + if name is None or name == "": + return HTTPError() + + links = map(lambda x: x.strip(), links) + links = filter(lambda x: x != "", links) + + pack = PYLOAD.add_package(name, links, queue) + if pw: + data = {"password": pw} + PYLOAD.set_package_data(pack, data) + + return "success" + + +@route("/json/remove_package/:id") +@validate(id=int) +@login_required('can_delete') +def remove_package(id): + try: + PYLOAD.del_packages([id]) + return "success" + except Exception, e: + return HTTPError() + +@route("/json/restart_package/:id") +@validate(id=int) +@login_required('can_add') +def restart_package(id): + try: + PYLOAD.restart_package(id) + return "success" + except Exception: + print_exc() + return HTTPError() + +@route("/json/move_package/:dest/:id") +@validate(dest=int, id=int) +@login_required('can_add') +def move_package(dest, id): + try: + PYLOAD.move_package(dest, id) + return "success" + except: + return HTTPError() + +@route("/json/edit_package", method="POST") +@login_required('can_add') +def edit_package(): + try: + id = int(request.forms.get("pack_id")) + data = {"name": request.forms.get("pack_name"), + "folder": request.forms.get("pack_folder"), + "priority": request.forms.get("pack_prio"), + "password": request.forms.get("pack_pws")} + + PYLOAD.set_package_data(id, data) + return "success" + + except: + return HTTPError() + +@route("/json/set_captcha") +@route("/json/set_captcha", method="POST") +@login_required('can_add') +def set_captcha(): + if request.environ.get('REQUEST_METHOD', "GET") == "POST": + try: + PYLOAD.set_captcha_result(request.forms["cap_id"], request.forms["cap_text"]) + except: + pass + + id, binary, typ = PYLOAD.get_captcha_task() + + if id: + binary = base64.standard_b64encode(str(binary)) + src = "data:image/%s;base64,%s" % (typ, binary) + + return {'captcha': True, 'src': src, 'id': id} + else: + return {'captcha': False} + + +@route("/json/delete_finished") +@login_required('pyload.can_delete') +def delete_finished(): + return {"del": PYLOAD.delete_finished()} + +@route("/json/restart_failed") +@login_required('pyload.can_delete') +def restart_failed(): + return PYLOAD.restart_failed()
\ No newline at end of file diff --git a/module/web/locale/cs/LC_MESSAGES/django.mo b/module/web/locale/cs/LC_MESSAGES/django.mo Binary files differnew file mode 100644 index 000000000..1eebf6ce1 --- /dev/null +++ b/module/web/locale/cs/LC_MESSAGES/django.mo diff --git a/module/web/manage.py b/module/web/manage.py deleted file mode 100755 index 34b964ffc..000000000 --- a/module/web/manage.py +++ /dev/null @@ -1,13 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -from django.core.management import execute_manager - -try: - import settings # Assumed to be in the same directory. -except ImportError: - import sys - sys.stderr.write("Error: Can't find the file 'settings.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n(If the file settings.py does indeed exist, it's causing an ImportError somehow.)\n" % __file__) - sys.exit(1) - -if __name__ == "__main__": - execute_manager(settings)
\ No newline at end of file diff --git a/module/web/middlewares.py b/module/web/middlewares.py new file mode 100644 index 000000000..745d7e6b5 --- /dev/null +++ b/module/web/middlewares.py @@ -0,0 +1,124 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import gzip + +try: + from cStringIO import StringIO +except ImportError: + from StringIO import StringIO + +class StripPathMiddleware(object): + def __init__(self, app): + self.app = app + + def __call__(self, e, h): + e['PATH_INFO'] = e['PATH_INFO'].rstrip('/') + return self.app(e, h) + + +class PrefixMiddleware(object): + def __init__(self, app, prefix="/pyload"): + self.app = app + self.prefix = prefix + + def __call__(self, e, h): + path = e["PATH_INFO"] + if path.startswith(self.prefix): + e['PATH_INFO'] = path.relace(self.prefix, "", 1) + return self.app(e, h) + +# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org) +# Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php + +# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org) +# Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php + +# WSGI middleware +# Gzip-encodes the response. + +class GZipMiddleWare(object): + + def __init__(self, application, compress_level=6): + self.application = application + self.compress_level = int(compress_level) + + def __call__(self, environ, start_response): + if 'gzip' not in environ.get('HTTP_ACCEPT_ENCODING', ''): + # nothing for us to do, so this middleware will + # be a no-op: + return self.application(environ, start_response) + response = GzipResponse(start_response, self.compress_level) + app_iter = self.application(environ, + response.gzip_start_response) + if app_iter is not None: + response.finish_response(app_iter) + + return response.write() + +def header_value(headers, key): + for header, value in headers: + if key.lower() == header.lower(): + return value + +def update_header(headers, key, value): + remove_header(headers, key) + headers.append((key, value)) + +def remove_header(headers, key): + for header, value in headers: + if key.lower() == header.lower(): + headers.remove((header, value)) + break + +class GzipResponse(object): + + def __init__(self, start_response, compress_level): + self.start_response = start_response + self.compress_level = compress_level + self.buffer = StringIO() + self.compressible = False + self.content_length = None + self.headers = () + + def gzip_start_response(self, status, headers, exc_info=None): + self.headers = headers + ct = header_value(headers,'content-type') + ce = header_value(headers,'content-encoding') + self.compressible = False + if ct and (ct.startswith('text/') or ct.startswith('application/')) \ + and 'zip' not in ct: + self.compressible = True + if ce: + self.compressible = False + if self.compressible: + headers.append(('content-encoding', 'gzip')) + remove_header(headers, 'content-length') + self.headers = headers + self.status = status + return self.buffer.write + + def write(self): + out = self.buffer + out.seek(0) + s = out.getvalue() + out.close() + return [s] + + def finish_response(self, app_iter): + if self.compressible: + output = gzip.GzipFile(mode='wb', compresslevel=self.compress_level, + fileobj=self.buffer) + else: + output = self.buffer + try: + for s in app_iter: + output.write(s) + if self.compressible: + output.close() + finally: + if hasattr(app_iter, 'close'): + app_iter.close() + content_length = self.buffer.tell() + update_header(self.headers, "Content-Length" , str(content_length)) + self.start_response(self.status, self.headers)
\ No newline at end of file diff --git a/module/web/pyload_app.py b/module/web/pyload_app.py new file mode 100644 index 000000000..ab0cbfb00 --- /dev/null +++ b/module/web/pyload_app.py @@ -0,0 +1,483 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, + or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see <http://www.gnu.org/licenses/>. + + @author: RaNaN +""" +from copy import deepcopy +import datetime +from datetime import datetime +from itertools import chain +from operator import itemgetter +import os + +import sqlite3 +import time +from os import listdir +from os import stat +from os.path import isdir +from os.path import isfile +from os.path import join +from sys import getfilesystemencoding +from hashlib import sha1 +from urllib import unquote + +from bottle import route, static_file, request, response, redirect, HTTPError + +from webinterface import PYLOAD, PROJECT_DIR + +from utils import render_to_response, parse_permissions, parse_userdata, formatSize, login_required +from filters import relpath, quotepath, unquotepath + +# Helper + +def pre_processor(): + s = request.environ.get('beaker.session') + user = parse_userdata(s) + perms = parse_permissions(s) + return {"user": user, + 'status': PYLOAD.status_server(), + 'captcha': PYLOAD.is_captcha_waiting(), + 'perms': perms} + + +def get_sort_key(item): + return item[1]["order"] + + +def base(messages): + return render_to_response('base.html', {'messages': messages}, [pre_processor]) + + +## Views + +@route('/media/:path#.+#') +def server_static(path): + response.header['Expires'] = time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(time.time() + 60 * 60 * 24 * 7)) + response.header['Cache-control'] = "public" + return static_file(path, root=join(PROJECT_DIR, "media")) + +@route('/favicon.ico') +def favicon(): + return static_file("favicon.ico", root=join(PROJECT_DIR, "media", "img")) + +@route('/login', method="GET") +def login(): + return render_to_response("login.html", proc=[pre_processor]) + +@route("/login", method="POST") +def login_post(): + user = request.forms.get("username") + password = request.forms.get("password") + + conn = sqlite3.connect('web.db') + c = conn.cursor() + c.execute('SELECT name, password, role, permission,template FROM "users" WHERE name=?', (user,)) + r = c.fetchone() + c.close() + conn.commit() + conn.close() + + if not r: + return render_to_response("login.html", {"errors": True}, [pre_processor]) + + salt = r[1][:5] + pw = r[1][5:] + + hash = sha1(salt + password) + if hash.hexdigest() == pw: + s = request.environ.get('beaker.session') + s["authenticated"] = True + s["name"] = r[0] + s["role"] = r[2] + s["perms"] = r[3] + s["template"] = r[4] + s.save() + + return redirect("/") + + + else: + return render_to_response("login.html", {"errors": True}, [pre_processor]) + +@route("/logout") +def logout(): + s = request.environ.get('beaker.session') + s.delete() + return render_to_response("logout.html", proc=[pre_processor]) + + +@route("/") +@route("/home") +@login_required("can_see_dl") +def home(): + res = PYLOAD.status_downloads() + + for link in res: + if link["status"] == 12: + link["information"] = "%s kB @ %s kB/s" % (link["size"] - link["kbleft"], link["speed"]) + + return render_to_response("home.html", {"res": res}, [pre_processor]) + + +@route("/queue") +@login_required("can_see_dl") +def queue(): + queue = PYLOAD.get_queue_info() + + data = zip(queue.keys(), queue.values()) + data.sort(key=get_sort_key) + + return render_to_response('queue.html', {'content': data}, [pre_processor]) + +@route("/collector") +@login_required('can_see_dl') +def collector(): + queue = PYLOAD.get_collector_info() + + data = zip(queue.keys(), queue.values()) + data.sort(key=get_sort_key) + + return render_to_response('collector.html', {'content': data}, [pre_processor]) + +@route("/downloads") +@login_required('can_download') +def downloads(): + root = PYLOAD.get_conf_val("general", "download_folder") + + if not isdir(root): + return base([_('Download directory not found.')]) + data = { + 'folder': [], + 'files': [] + } + + for item in sorted(listdir(root)): + if isdir(join(root, item)): + folder = { + 'name': item, + 'path': item, + 'files': [] + } + for file in sorted(listdir(join(root, item))): + try: + if isfile(join(root, item, file)): + folder['files'].append(file) + except: + pass + + data['folder'].append(folder) + elif isfile(join(root, item)): + data['files'].append(item) + + return render_to_response('downloads.html', {'files': data}, [pre_processor]) + +@route("/downloads/get/:path#.+#") +@login_required("can_download") +def get_download(path): + path = unquote(path) + #@TODO some files can not be downloaded + + root = PYLOAD.get_conf_val("general", "download_folder") + + path = path.replace("..", "") + try: + return static_file(path, root) + + except Exception, e: + print e + return HTTPError(404, "File not Found.") + +@route("/settings") +@route("/settings", method="POST") +@login_required('can_change_status') +def config(): + conf = PYLOAD.get_config() + plugin = PYLOAD.get_plugin_config() + accs = PYLOAD.get_accounts() + messages = [] + + for section in chain(conf.itervalues(), plugin.itervalues()): + for key, option in section.iteritems(): + if key == "desc": continue + + if ";" in option["type"]: + option["list"] = option["type"].split(";") + + if request.environ.get('REQUEST_METHOD', "GET") == "POST": + errors = [] + + for key, value in request.POST.iteritems(): + if not "|" in key: continue + sec, skey, okey = key.split("|")[:] + + if sec == "General": + if conf.has_key(skey): + if conf[skey].has_key(okey): + try: + if str(conf[skey][okey]['value']) != value: + PYLOAD.set_conf_val(skey, okey, value) + except Exception, e: + errors.append("%s | %s : %s" % (skey, okey, e)) + else: + continue + else: + continue + + elif sec == "Plugin": + if plugin.has_key(skey): + if plugin[skey].has_key(okey): + try: + if str(plugin[skey][okey]['value']) != value: + PYLOAD.set_conf_val(skey, okey, value, "plugin") + except Exception, e: + errors.append("%s | %s : %s" % (skey, okey, e)) + else: + continue + else: + continue + elif sec == "Accounts": + if ";" in okey: + action, name = okey.split(";") + if action == "delete": + PYLOAD.remove_account(skey, name) + + if okey == "newacc" and value: + # add account + + pw = request.POST.get("Accounts|%s|newpw" % skey) + PYLOAD.update_account(skey, value, pw) + + for pluginname, accdata in accs.iteritems(): + for data in accdata: + newpw = request.POST.get("Accounts|%s|password;%s" % (pluginname, data["login"]), "").strip() + time = request.POST.get("Accounts|%s|time;%s" % (pluginname, data["login"]), "").strip() + + if newpw or (time and (not data["options"].has_key("time") or [time] != data["options"]["time"])): + PYLOAD.update_account(pluginname, data["login"], newpw, {"time": [time]}) + + if errors: + messages.append(_("Error occured when setting the following options:")) + messages.append("") + messages += errors + else: + messages.append(_("All options were set correctly.")) + + accs = deepcopy(PYLOAD.get_accounts(False, False)) + for accounts in accs.itervalues(): + for data in accounts: + if data["trafficleft"] == -1: + data["trafficleft"] = _("unlimited") + elif not data["trafficleft"]: + data["trafficleft"] = _("not available") + else: + data["trafficleft"] = formatSize(data["trafficleft"]) + + if data["validuntil"] == -1: + data["validuntil"] = _("unlimited") + elif not data["validuntil"]: + data["validuntil"] = _("not available") + else: + t = time.localtime(data["validuntil"]) + data["validuntil"] = time.strftime("%d.%m.%Y", t) + + if data["options"].has_key("time"): + try: + data["time"] = data["options"]["time"][0] + except: + data["time"] = "invalid" + + + return render_to_response('settings.html', + {'conf': {'Plugin': plugin, 'General': conf, 'Accounts': accs}, 'errors': messages}, + [pre_processor]) + +@route("/package_ui.js") +@login_required('can_see_dl') +def package_ui(): + response.header['Expires'] = time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(time.time() + 60 * 60 * 24 * 7)) + response.header['Cache-control'] = "public" + return render_to_response('package_ui.js') + + +@route("/filechooser") +@route("/pathchooser") +@route("/filechooser/:file#.+#") +@route("/pathchooser/:path#.+#") +@login_required('can_change_status') +def path(file="", path=""): + if file: + type = "file" + else: + type = "folder" + + path = os.path.normpath(unquotepath(path)) + + if os.path.isfile(path): + oldfile = path + path = os.path.dirname(path) + else: + oldfile = '' + + abs = False + + if os.path.isdir(path): + if os.path.isabs(path): + cwd = os.path.abspath(path) + abs = True + else: + cwd = relpath(path) + else: + cwd = os.getcwd() + + try: + cwd = cwd.encode("utf8") + except: + pass + + cwd = os.path.normpath(os.path.abspath(cwd)) + parentdir = os.path.dirname(cwd) + if not abs: + if os.path.abspath(cwd) == "/": + cwd = relpath(cwd) + else: + cwd = relpath(cwd) + os.path.sep + parentdir = relpath(parentdir) + os.path.sep + + if os.path.abspath(cwd) == "/": + parentdir = "" + + try: + folders = os.listdir(cwd) + except: + folders = [] + + files = [] + + for f in folders: + try: + f = f.decode(getfilesystemencoding()) + data = {} + data['name'] = f + data['fullpath'] = join(cwd, f) + data['sort'] = data['fullpath'].lower() + data['modified'] = datetime.fromtimestamp(int(os.path.getmtime(join(cwd, f)))) + data['ext'] = os.path.splitext(f)[1] + except: + continue + + if os.path.isdir(join(cwd, f)): + data['type'] = 'dir' + else: + data['type'] = 'file' + + if os.path.isfile(join(cwd, f)): + data['size'] = os.path.getsize(join(cwd, f)) + + power = 0 + while (data['size']/1024) > 0.3: + power += 1 + data['size'] /= 1024. + units = ('', 'K','M','G','T') + data['unit'] = units[power]+'Byte' + else: + data['size'] = '' + + files.append(data) + + files = sorted(files, key=itemgetter('type', 'sort')) + + return render_to_response('pathchooser.html', {'cwd': cwd, 'files': files, 'parentdir': parentdir, 'type': type, 'oldfile': oldfile, 'absolute': abs}, []) + +@route("/logs") +@route("/logs/:item") +@route("/logs/:item", method="POST") +@login_required('can_see_logs') +def logs(item=-1): + s = request.environ.get('beaker.session') + + perpage = s.get('perpage', 34) + reversed = s.get('reversed', False) + + warning = "" + conf = PYLOAD.get_config() + if not conf['log']['file_log']['value']: + warning = "Warning: File log is disabled, see settings page." + + perpage_p = ((20,20), (34, 34), (40, 40), (100, 100), (0,'all')) + fro = None + + if request.environ.get('REQUEST_METHOD', "GET") == "POST": + try: + fro = datetime.strptime(request.forms['from'], '%d.%m.%Y %H:%M:%S') + except: + pass + try: + perpage = int(request.forms['perpage']) + s['perpage'] = perpage + + reversed = bool(request.forms.get('reversed', False)) + s['reversed'] = reversed + except: + pass + + s.save() + + try: + item = int(item) + except: + pass + + log = PYLOAD.get_log() + if not perpage: + item = 0 + + if item < 1 or type(item) is not int: + item = 1 if len(log) - perpage + 1 < 1 else len(log) - perpage + 1 + + if type(fro) is datetime: # we will search for datetime + item = -1 + + data = [] + counter = 0 + perpagecheck = 0 + for l in log: + counter += 1 + + if counter >= item: + try: + date,time,level,message = l.split(" ", 3) + dtime = datetime.strptime(date+' '+time, '%d.%m.%Y %H:%M:%S') + except: + dtime = None + date = '?' + time = ' ' + level = '?' + message = l + if item == -1 and dtime is not None and fro <= dtime: + item = counter #found our datetime + if item >= 0: + data.append({'line': counter, 'date': date+" "+time, 'level':level, 'message': message}) + perpagecheck += 1 + if fro is None and dtime is not None: #if fro not set set it to first showed line + fro = dtime + if perpagecheck >= perpage > 0: + break + + if fro is None: #still not set, empty log? + fro = datetime.now() + if reversed: + data.reverse() + return render_to_response('logs.html', {'warning': warning, 'log': data, 'from': fro.strftime('%d.%m.%Y %H:%M:%S'), 'reversed': reversed, 'perpage':perpage, 'perpage_p':sorted(perpage_p), 'iprev': 1 if item - perpage < 1 else item - perpage, 'inext': (item + perpage) if item+perpage < len(log) else item}, [pre_processor])
\ No newline at end of file diff --git a/module/web/run_fcgi.py b/module/web/run_fcgi.py deleted file mode 100644 index 8091de5ea..000000000 --- a/module/web/run_fcgi.py +++ /dev/null @@ -1,170 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -import os -import sys - -from flup.server.fcgi_base import BaseFCGIServer -from flup.server.fcgi_base import FCGI_RESPONDER -from flup.server.threadedserver import ThreadedServer - - -os.environ["DJANGO_SETTINGS_MODULE"] = 'settings' - -def handle(*args, **options): - from django.conf import settings - from django.utils import translation - # Activate the current language, because it won't get activated later. - try: - translation.activate(settings.LANGUAGE_CODE) - except AttributeError: - pass - #from django.core.servers.fastcgi import runfastcgi - runfastcgi(args) - - -FASTCGI_OPTIONS = { - 'protocol': 'fcgi', - 'host': None, - 'port': None, - 'socket': None, - 'method': 'fork', - 'daemonize': None, - 'workdir': '/', - 'pidfile': None, - 'maxspare': 5, - 'minspare': 2, - 'maxchildren': 50, - 'maxrequests': 0, - 'debug': None, - 'outlog': None, - 'errlog': None, - 'umask': None, -} - - -def runfastcgi(argset=[], **kwargs): - options = FASTCGI_OPTIONS.copy() - options.update(kwargs) - for x in argset: - if "=" in x: - k, v = x.split('=', 1) - else: - k, v = x, True - options[k.lower()] = v - - try: - import flup - except ImportError, e: - print >> sys.stderr, "ERROR: %s" % e - print >> sys.stderr, " Unable to load the flup package. In order to run django" - print >> sys.stderr, " as a FastCGI application, you will need to get flup from" - print >> sys.stderr, " http://www.saddi.com/software/flup/ If you've already" - print >> sys.stderr, " installed flup, then make sure you have it in your PYTHONPATH." - return False - - flup_module = 'server.' + options['protocol'] - - if options['method'] in ('prefork', 'fork'): - wsgi_opts = { - 'maxSpare': int(options["maxspare"]), - 'minSpare': int(options["minspare"]), - 'maxChildren': int(options["maxchildren"]), - 'maxRequests': int(options["maxrequests"]), - } - flup_module += '_fork' - elif options['method'] in ('thread', 'threaded'): - wsgi_opts = { - 'maxSpare': int(options["maxspare"]), - 'minSpare': int(options["minspare"]), - 'maxThreads': int(options["maxchildren"]), - } - else: - print "ERROR: Implementation must be one of prefork or thread." - - wsgi_opts['debug'] = options['debug'] is not None - - #try: - # module = importlib.import_module('.%s' % flup_module, 'flup') - # WSGIServer = module.WSGIServer - #except: - # print "Can't import flup." + flup_module - # return False - - # Prep up and go - from django.core.handlers.wsgi import WSGIHandler - - if options["host"] and options["port"] and not options["socket"]: - wsgi_opts['bindAddress'] = (options["host"], int(options["port"])) - elif options["socket"] and not options["host"] and not options["port"]: - wsgi_opts['bindAddress'] = options["socket"] - elif not options["socket"] and not options["host"] and not options["port"]: - wsgi_opts['bindAddress'] = None - else: - return fastcgi_help("Invalid combination of host, port, socket.") - - daemon_kwargs = {} - if options['outlog']: - daemon_kwargs['out_log'] = options['outlog'] - if options['errlog']: - daemon_kwargs['err_log'] = options['errlog'] - if options['umask']: - daemon_kwargs['umask'] = int(options['umask']) - - ownWSGIServer(WSGIHandler(), **wsgi_opts).run() - -class ownThreadedServer(ThreadedServer): - def _installSignalHandlers(self): - return - - def _restoreSignalHandlers(self): - return - - -class ownWSGIServer(BaseFCGIServer, ownThreadedServer): - - def __init__(self, application, environ=None, - multithreaded=True, multiprocess=False, - bindAddress=None, umask=None, multiplexed=False, - debug=True, roles=(FCGI_RESPONDER,), forceCGI=False, **kw): - BaseFCGIServer.__init__(self, application, - environ=environ, - multithreaded=multithreaded, - multiprocess=multiprocess, - bindAddress=bindAddress, - umask=umask, - multiplexed=multiplexed, - debug=debug, - roles=roles, - forceCGI=forceCGI) - for key in ('jobClass', 'jobArgs'): - if kw.has_key(key): - del kw[key] - ownThreadedServer.__init__(self, jobClass=self._connectionClass, - jobArgs=(self,), **kw) - - def _isClientAllowed(self, addr): - return self._web_server_addrs is None or \ - (len(addr) == 2 and addr[0] in self._web_server_addrs) - - def run(self): - """ - The main loop. Exits on SIGHUP, SIGINT, SIGTERM. Returns True if - SIGHUP was received, False otherwise. - """ - self._web_server_addrs = os.environ.get('FCGI_WEB_SERVER_ADDRS') - if self._web_server_addrs is not None: - self._web_server_addrs = map(lambda x: x.strip(), - self._web_server_addrs.split(',')) - - sock = self._setupSocket() - - ret = ownThreadedServer.run(self, sock) - - self._cleanupSocket(sock) - - return ret - -if __name__ == "__main__": - handle(*sys.argv[1:]) - diff --git a/module/web/run_server.py b/module/web/run_server.py deleted file mode 100755 index 2dc97353a..000000000 --- a/module/web/run_server.py +++ /dev/null @@ -1,90 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -import os -import sys -import django -from django.core.servers.basehttp import AdminMediaHandler, WSGIServerException, WSGIServer, WSGIRequestHandler -from django.core.handlers.wsgi import WSGIHandler - -os.environ["DJANGO_SETTINGS_MODULE"] = 'settings' - -class Output: - def __init__(self, stream): - self.stream = stream - def write(self, data): # Do nothing - return None - #self.stream.write(data) - #self.stream.flush() - def __getattr__(self, attr): - return getattr(self.stream, attr) - -#sys.stderr = Output(sys.stderr) -#sys.stdout = Output(sys.stdout) - -def handle(* args): - try: - if len(args) == 1: - try: - addr, port = args[0].split(":") - except: - addr = "127.0.0.1" - port = args[0] - else: - addr = args[0] - port = args[1] - except: - addr = '127.0.0.1' - port = '8000' - - #print addr, port - - admin_media_path = '' - shutdown_message = '' - quit_command = (sys.platform == 'win32') and 'CTRL-BREAK' or 'CONTROL-C' - - from django.conf import settings - from django.utils import translation - - #print "Django version %s, using settings %r" % (django.get_version(), settings.SETTINGS_MODULE) - #print "Development server is running at http://%s:%s/" % (addr, port) - #print "Quit the server with %s." % quit_command - - translation.activate(settings.LANGUAGE_CODE) - - try: - handler = AdminMediaHandler(WSGIHandler(), admin_media_path) - run(addr, int(port), handler) - #@TODO catch unimportant Broken Pipe Errors - - except WSGIServerException, e: - # Use helpful error messages instead of ugly tracebacks. - ERRORS = { - 13: "You don't have permission to access that port.", - 98: "That port is already in use.", - 99: "That IP address can't be assigned-to.", - } - try: - error_text = ERRORS[e.args[0].args[0]] - except (AttributeError, KeyError): - error_text = str(e) - sys.stderr.write(("Error: %s" % error_text) + '\n') - # Need to use an OS exit because sys.exit doesn't work in a thread - #os._exit(1) - except KeyboardInterrupt: - if shutdown_message: - print shutdown_message - sys.exit(0) - -class ownRequestHandler(WSGIRequestHandler): - def log_message(self, format, *args): - return - - -def run(addr, port, wsgi_handler): - server_address = (addr, port) - httpd = WSGIServer(server_address, ownRequestHandler) - httpd.set_app(wsgi_handler) - httpd.serve_forever() - -if __name__ == "__main__": - handle(*sys.argv[1:]) diff --git a/module/web/settings.py b/module/web/settings.py deleted file mode 100644 index 5a836e11c..000000000 --- a/module/web/settings.py +++ /dev/null @@ -1,165 +0,0 @@ -# -*- coding: utf-8 -*-
-# Django settings for pyload project.
-
-DEBUG = True
-TEMPLATE_DEBUG = DEBUG
-
-import os
-import sys
-import django
-
-SERVER_VERSION = "0.4.4"
-
-PROJECT_DIR = os.path.dirname(__file__)
-
-#chdir(dirname(abspath(__file__)) + sep)
-
-PYLOAD_DIR = os.path.join(PROJECT_DIR,"..","..")
-
-sys.path.append(PYLOAD_DIR)
-
-
-sys.path.append(os.path.join(PYLOAD_DIR, "module"))
-
-import InitHomeDir
-sys.path.append(pypath)
-
-config = None
-#os.chdir(PROJECT_DIR) # UNCOMMENT FOR LOCALE GENERATION
-
-
-try:
- import module.web.ServerThread
- if not module.web.ServerThread.core:
- raise Exception
- PYLOAD = module.web.ServerThread.core.server_methods
- config = module.web.ServerThread.core.config
-except:
- import xmlrpclib
- ssl = ""
-
- from module.ConfigParser import ConfigParser
- config = ConfigParser()
-
- if config.get("ssl", "activated"):
- ssl = "s"
-
- server_url = "http%s://%s:%s@%s:%s/" % (
- ssl,
- config.username,
- config.password,
- config.get("remote", "listenaddr"),
- config.get("remote", "port")
- )
-
- PYLOAD = xmlrpclib.ServerProxy(server_url, allow_none=True)
-
-DEBUG = TEMPLATE_DEBUG = config.get("general","debug_mode")
-
-from module.JsEngine import JsEngine
-JS = JsEngine()
-
-TEMPLATE = config.get('webinterface','template')
-DL_ROOT = os.path.join(PYLOAD_DIR, config.get('general','download_folder'))
-LOG_ROOT = os.path.join(PYLOAD_DIR, config.get('log','log_folder'))
-
-ADMINS = (
- # ('Your Name', 'your_email@domain.com'),
- )
-
-MANAGERS = ADMINS
-
-DATABASE_ENGINE = 'sqlite3' # 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'.
-#DATABASE_NAME = os.path.join(PROJECT_DIR, 'pyload.db') # Or path to database file if using sqlite3.
-DATABASE_NAME = 'pyload.db' # Or path to database file if using sqlite3.
-DATABASE_USER = '' # Not used with sqlite3.
-DATABASE_PASSWORD = '' # Not used with sqlite3.
-DATABASE_HOST = '' # Set to empty string for localhost. Not used with sqlite3.
-DATABASE_PORT = '' # Set to empty string for default. Not used with sqlite3.
-
-# Local time zone for this installation. Choices can be found here:
-# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
-# although not all choices may be available on all operating systems.
-# If running in a Windows environment this must be set to the same as your
-# system time zone.
-if (django.VERSION[0] > 1 or django.VERSION[1] > 1) and os.name != "nt":
- zone = None
-else:
- zone = 'Europe'
-TIME_ZONE = zone
-
-# Language code for this installation. All choices can be found here:
-# http://www.i18nguy.com/unicode/language-identifiers.html
-LANGUAGE_CODE = config.get("general","language")
-
-SITE_ID = 1
-
-# If you set this to False, Django will make some optimizations so as not
-# to load the internationalization machinery.
-USE_I18N = True
-
-# Absolute path to the directory that holds media.
-# Example: "/home/media/media.lawrence.com/"
-MEDIA_ROOT = os.path.join(PROJECT_DIR, "media/")
-
-
-# URL that handles the media served from MEDIA_ROOT. Make sure to use a
-# trailing slash if there is a path component (optional in other cases).
-# Examples: "http://media.lawrence.com", "http://example.com/media/"
-
-#MEDIA_URL = 'http://localhost:8000/media'
-MEDIA_URL = '/media/' + config.get('webinterface','template') + '/'
-#MEDIA_URL = os.path.join(PROJECT_DIR, "media/")
-
-LOGIN_REDIRECT_URL = "/"
-
-# URL prefix for admin media -- CSS, JavaScript and images. Make sure to use a
-# trailing slash.
-# Examples: "http://foo.com/media/", "/media/".
-ADMIN_MEDIA_PREFIX = '/admin/media/'
-
-# Make this unique, and don't share it with anybody.
-SECRET_KEY = '+u%%1t&c7!e$0$*gu%w2$@to)h0!&x-r*9e+-=wa4*zxat%x^t'
-
-# List of callables that know how to import templates from various sources.
-TEMPLATE_LOADERS = (
- 'django.template.loaders.filesystem.load_template_source',
- 'django.template.loaders.app_directories.load_template_source',
- # 'django.template.loaders.eggs.load_template_source',
- )
-
-
-MIDDLEWARE_CLASSES = (
- 'django.middleware.gzip.GZipMiddleware',
- 'django.middleware.http.ConditionalGetMiddleware',
- 'django.contrib.sessions.middleware.SessionMiddleware',
- 'django.middleware.locale.LocaleMiddleware',
- 'django.middleware.common.CommonMiddleware',
- 'django.contrib.auth.middleware.AuthenticationMiddleware',
- #'django.contrib.csrf.middleware.CsrfViewMiddleware',
- 'django.contrib.csrf.middleware.CsrfResponseMiddleware'
- )
-
-ROOT_URLCONF = 'urls'
-
-TEMPLATE_DIRS = (
- # Put strings here, like "/home/html/django_templates" or "C:/www/django/templates".
- # Always use forward slashes, even on Windows.
- # Don't forget to use absolute paths, not relative paths.
- os.path.join(PROJECT_DIR, "templates"),
- )
-
-INSTALLED_APPS = (
- 'django.contrib.auth',
- 'django.contrib.contenttypes',
- 'django.contrib.sessions',
- #'django.contrib.sites',
- 'django.contrib.admin',
- 'pyload',
- 'ajax',
- 'cnl',
- )
-
-
-AUTH_PROFILE_MODULE = 'pyload.UserProfile'
-LOGIN_URL = '/login/'
diff --git a/module/web/syncdb.py b/module/web/syncdb.py deleted file mode 100644 index 669f22681..000000000 --- a/module/web/syncdb.py +++ /dev/null @@ -1,152 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -import os -import sys - -os.environ["DJANGO_SETTINGS_MODULE"] = 'settings' -sys.path.append(os.path.join(pypath, "module", "web")) - -from django.conf import settings -from django.core.management.base import NoArgsCommand -from django.core.management.color import no_style -from django.core.management.sql import custom_sql_for_model, emit_post_sync_signal -from django.db import connections, router, transaction, models, DEFAULT_DB_ALIAS -from django.utils.datastructures import SortedDict -from django.utils.importlib import import_module - - - -def handle_noargs(**options): - - verbosity = int(options.get('verbosity', 1)) - interactive = False - show_traceback = options.get('traceback', False) - - style = no_style() - - # Import the 'management' module within each installed app, to register - # dispatcher events. - for app_name in settings.INSTALLED_APPS: - try: - import_module('.management', app_name) - except ImportError, exc: - # This is slightly hackish. We want to ignore ImportErrors - # if the "management" module itself is missing -- but we don't - # want to ignore the exception if the management module exists - # but raises an ImportError for some reason. The only way we - # can do this is to check the text of the exception. Note that - # we're a bit broad in how we check the text, because different - # Python implementations may not use the same text. - # CPython uses the text "No module named management" - # PyPy uses "No module named myproject.myapp.management" - msg = exc.args[0] - if not msg.startswith('No module named') or 'management' not in msg: - raise - - db = options.get('database', DEFAULT_DB_ALIAS) - connection = connections[db] - cursor = connection.cursor() - - # Get a list of already installed *models* so that references work right. - tables = connection.introspection.table_names() - seen_models = connection.introspection.installed_models(tables) - created_models = set() - pending_references = {} - - # Build the manifest of apps and models that are to be synchronized - all_models = [ - (app.__name__.split('.')[-2], - [m for m in models.get_models(app, include_auto_created=True) - if router.allow_syncdb(db, m)]) - for app in models.get_apps() - ] - def model_installed(model): - opts = model._meta - converter = connection.introspection.table_name_converter - return not ((converter(opts.db_table) in tables) or - (opts.auto_created and converter(opts.auto_created._meta.db_table) in tables)) - - manifest = SortedDict( - (app_name, filter(model_installed, model_list)) - for app_name, model_list in all_models - ) - - # Create the tables for each model - for app_name, model_list in manifest.items(): - for model in model_list: - # Create the model's database table, if it doesn't already exist. - if verbosity >= 2: - print "Processing %s.%s model" % (app_name, model._meta.object_name) - sql, references = connection.creation.sql_create_model(model, style, seen_models) - seen_models.add(model) - created_models.add(model) - for refto, refs in references.items(): - pending_references.setdefault(refto, []).extend(refs) - if refto in seen_models: - sql.extend(connection.creation.sql_for_pending_references(refto, style, pending_references)) - sql.extend(connection.creation.sql_for_pending_references(model, style, pending_references)) - if verbosity >= 1 and sql: - print "Creating table %s" % model._meta.db_table - for statement in sql: - cursor.execute(statement) - tables.append(connection.introspection.table_name_converter(model._meta.db_table)) - - - transaction.commit_unless_managed(using=db) - - # Send the post_syncdb signal, so individual apps can do whatever they need - # to do at this point. - emit_post_sync_signal(created_models, verbosity, interactive, db) - - # The connection may have been closed by a syncdb handler. - cursor = connection.cursor() - - # Install custom SQL for the app (but only if this - # is a model we've just created) - for app_name, model_list in manifest.items(): - for model in model_list: - if model in created_models: - custom_sql = custom_sql_for_model(model, style, connection) - if custom_sql: - if verbosity >= 1: - print "Installing custom SQL for %s.%s model" % (app_name, model._meta.object_name) - try: - for sql in custom_sql: - cursor.execute(sql) - except Exception, e: - sys.stderr.write("Failed to install custom SQL for %s.%s model: %s\n" % \ - (app_name, model._meta.object_name, e)) - if show_traceback: - import traceback - traceback.print_exc() - transaction.rollback_unless_managed(using=db) - else: - transaction.commit_unless_managed(using=db) - else: - if verbosity >= 2: - print "No custom SQL for %s.%s model" % (app_name, model._meta.object_name) - - # Install SQL indicies for all newly created models - for app_name, model_list in manifest.items(): - for model in model_list: - if model in created_models: - index_sql = connection.creation.sql_indexes_for_model(model, style) - if index_sql: - if verbosity >= 1: - print "Installing index for %s.%s model" % (app_name, model._meta.object_name) - try: - for sql in index_sql: - cursor.execute(sql) - except Exception, e: - sys.stderr.write("Failed to install index for %s.%s model: %s\n" % \ - (app_name, model._meta.object_name, e)) - transaction.rollback_unless_managed(using=db) - else: - transaction.commit_unless_managed(using=db) - - #from django.core.management import call_command - #call_command('loaddata', 'initial_data', verbosity=verbosity, database=db) - -if __name__ == "__main__": - handle_noargs()
\ No newline at end of file diff --git a/module/web/syncdb_django11.py b/module/web/syncdb_django11.py deleted file mode 100644 index c579718e0..000000000 --- a/module/web/syncdb_django11.py +++ /dev/null @@ -1,154 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -import os -import sys - -os.environ["DJANGO_SETTINGS_MODULE"] = 'settings' -sys.path.append(os.path.join(pypath, "module", "web")) - -from django.core.management.base import NoArgsCommand -from django.core.management.color import no_style -from django.utils.importlib import import_module -from optparse import make_option - -try: - set -except NameError: - from sets import Set as set # Python 2.3 fallback - -def handle_noargs(**options): - from django.db import connection, transaction, models - from django.conf import settings - from django.core.management.sql import custom_sql_for_model, emit_post_sync_signal - - verbosity = int(options.get('verbosity', 1)) - interactive = False - show_traceback = options.get('traceback', False) - - style = no_style() - - # Import the 'management' module within each installed app, to register - # dispatcher events. - for app_name in settings.INSTALLED_APPS: - try: - import_module('.management', app_name) - except ImportError, exc: - # This is slightly hackish. We want to ignore ImportErrors - # if the "management" module itself is missing -- but we don't - # want to ignore the exception if the management module exists - # but raises an ImportError for some reason. The only way we - # can do this is to check the text of the exception. Note that - # we're a bit broad in how we check the text, because different - # Python implementations may not use the same text. - # CPython uses the text "No module named management" - # PyPy uses "No module named myproject.myapp.management" - msg = exc.args[0] - if not msg.startswith('No module named') or 'management' not in msg: - raise - - cursor = connection.cursor() - - # Get a list of already installed *models* so that references work right. - tables = connection.introspection.table_names() - seen_models = connection.introspection.installed_models(tables) - created_models = set() - pending_references = {} - - # Create the tables for each model - for app in models.get_apps(): - app_name = app.__name__.split('.')[-2] - model_list = models.get_models(app) - for model in model_list: - # Create the model's database table, if it doesn't already exist. - if verbosity >= 2: - print "Processing %s.%s model" % (app_name, model._meta.object_name) - if connection.introspection.table_name_converter(model._meta.db_table) in tables: - continue - sql, references = connection.creation.sql_create_model(model, style, seen_models) - seen_models.add(model) - created_models.add(model) - for refto, refs in references.items(): - pending_references.setdefault(refto, []).extend(refs) - if refto in seen_models: - sql.extend(connection.creation.sql_for_pending_references(refto, style, pending_references)) - sql.extend(connection.creation.sql_for_pending_references(model, style, pending_references)) - if verbosity >= 1 and sql: - print "Creating table %s" % model._meta.db_table - for statement in sql: - cursor.execute(statement) - tables.append(connection.introspection.table_name_converter(model._meta.db_table)) - - # Create the m2m tables. This must be done after all tables have been created - # to ensure that all referred tables will exist. - for app in models.get_apps(): - app_name = app.__name__.split('.')[-2] - model_list = models.get_models(app) - for model in model_list: - if model in created_models: - sql = connection.creation.sql_for_many_to_many(model, style) - if sql: - if verbosity >= 2: - print "Creating many-to-many tables for %s.%s model" % (app_name, model._meta.object_name) - for statement in sql: - cursor.execute(statement) - - transaction.commit_unless_managed() - - # Send the post_syncdb signal, so individual apps can do whatever they need - # to do at this point. - emit_post_sync_signal(created_models, verbosity, interactive) - - # The connection may have been closed by a syncdb handler. - cursor = connection.cursor() - - # Install custom SQL for the app (but only if this - # is a model we've just created) - for app in models.get_apps(): - app_name = app.__name__.split('.')[-2] - for model in models.get_models(app): - if model in created_models: - custom_sql = custom_sql_for_model(model, style) - if custom_sql: - if verbosity >= 1: - print "Installing custom SQL for %s.%s model" % (app_name, model._meta.object_name) - try: - for sql in custom_sql: - cursor.execute(sql) - except Exception, e: - sys.stderr.write("Failed to install custom SQL for %s.%s model: %s\n" % \ - (app_name, model._meta.object_name, e)) - if show_traceback: - import traceback - traceback.print_exc() - transaction.rollback_unless_managed() - else: - transaction.commit_unless_managed() - else: - if verbosity >= 2: - print "No custom SQL for %s.%s model" % (app_name, model._meta.object_name) - # Install SQL indicies for all newly created models - for app in models.get_apps(): - app_name = app.__name__.split('.')[-2] - for model in models.get_models(app): - if model in created_models: - index_sql = connection.creation.sql_indexes_for_model(model, style) - if index_sql: - if verbosity >= 1: - print "Installing index for %s.%s model" % (app_name, model._meta.object_name) - try: - for sql in index_sql: - cursor.execute(sql) - except Exception, e: - sys.stderr.write("Failed to install index for %s.%s model: %s\n" % \ - (app_name, model._meta.object_name, e)) - transaction.rollback_unless_managed() - else: - transaction.commit_unless_managed() - - # Install the 'initial_data' fixture, using format discovery - #from django.core.management import call_command - #call_command('loaddata', 'initial_data', verbosity=verbosity) - -if __name__ == "__main__": - handle_noargs()
\ No newline at end of file diff --git a/module/web/templates/jinja/default/base.html b/module/web/templates/jinja/default/base.html new file mode 100644 index 000000000..04c6dfbad --- /dev/null +++ b/module/web/templates/jinja/default/base.html @@ -0,0 +1,317 @@ +<?xml version="1.0" ?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
+<link rel="stylesheet" type="text/css" href="/media/default/css/default.css"/>
+
+<link rel="stylesheet" type="text/css" href="/media/default/css/window.css"/>
+
+<script type="text/javascript" src="/media/default/js/funktions.js"></script>
+<script type="text/javascript" src="/media/default/js/mootools-1.2.5-core.js"></script>
+<script type="text/javascript" src="/media/default/js/mootools-1.2.4.4-more.js"></script>
+
+<title>{% block title %}pyLoad {{_("Webinterface")}}{% endblock %}</title>
+
+<script type="text/javascript">
+var add_bg, add_box, cap_box, cap_info;
+document.addEvent("domready", function(){
+
+ add_bg = new Fx.Tween($('add_bg'));
+ add_box = new Fx.Tween($('add_box'));
+ cap_box = new Fx.Tween($('cap_box'));
+
+ add_bg.set("opacity", 0);
+ add_box.set("opacity", 0);
+ cap_box.set("opacity", 0);
+
+
+ $('add_form').onsubmit=function() {
+ $('add_form').target = 'upload_target';
+ if ($('add_name').value == "" && $('add_file').value != " "){
+ alert("{{_("Please Enter a packagename.")}}");
+ return false
+ }else{
+ out();
+ }
+ };
+
+ $('add_reset').addEvent('click', function(){
+ out();
+ });
+
+ var jsonStatus = new Request.JSON({
+ url: "/json/status",
+ onSuccess: LoadJsonToContent,
+ secure: false,
+ async: true,
+ initialDelay: 0,
+ delay: 4000,
+ limit: 30000
+ });
+
+ $('action_play').addEvent('click', function(){
+ new Request({method: 'get', url: '/json/unpause'}).send();
+ });
+
+
+ $('action_cancel').addEvent('click', function(){
+ new Request({method: 'get', url: '/json/cancel'}).send();
+ });
+
+
+ $('action_stop').addEvent('click', function(){
+ new Request({method: 'get', url: '/json/pause'}).send();
+ });
+
+ $('cap_info').addEvent('click', function(){
+ load_cap("get", "");
+ show_cap();
+ });
+
+ $('cap_reset').addEvent('click', function(){
+ hide_cap()
+ });
+
+ $('cap_form').addEvent('submit', function(e){
+ submit_cap();
+ e.stop()
+ });
+
+ jsonStatus.startTimer();
+
+});
+
+function LoadJsonToContent(data)
+{
+ $("speed").set('text', Math.round(data.speed*100)/100);
+ $("aktiv").set('text', data.activ);
+ $("aktiv_from").set('text', data.queue);
+
+ if (data.captcha){
+ $("cap_info").setStyle('display', 'inline');
+ }else{
+ $("cap_info").setStyle('display', 'none');
+ }
+
+ if (data.download) {
+ $("time").set('text', " {{_("on")}}");
+ $("time").setStyle('background-color', "#8ffc25");
+
+ }else{
+ $("time").set('text', " {{_("off")}}");
+ $("time").setStyle('background-color', "#fc6e26");
+ }
+
+ if (data.reconnect){
+ $("reconnect").set('text', " {{_("on")}}");
+ $("reconnect").setStyle('background-color', "#8ffc25");
+ }
+ else{
+ $("reconnect").set('text', " {{_("off")}}");
+ $("reconnect").setStyle('background-color', "#fc6e26");
+ }
+}
+function bg_show(){
+ $("add_bg").setStyle('display', 'block');
+ add_bg.start('opacity',0.8);
+}
+
+function bg_hide(){
+ add_bg.start('opacity',0).chain(function(){
+ $('add_bg').setStyle('display', 'none');
+ });
+}
+
+function show(){
+ bg_show();
+ $("add_form").reset();
+ $("add_box").setStyle('display', 'block');
+ add_box.start('opacity',1)
+}
+
+function out(){
+ bg_hide();
+ add_box.start('opacity',0).chain(function(){
+ $('add_box').setStyle('display', 'none');
+ });
+}
+function show_cap(){
+ bg_show();
+ $("cap_box").setStyle('display', 'block');
+ cap_box.start('opacity',1)
+}
+
+function hide_cap(){
+ bg_hide();
+ cap_box.start('opacity',0).chain(function(){
+ $('cap_box').setStyle('display', 'none');
+ });
+}
+
+function load_cap(method, post){
+ new Request.JSON({
+ url: "/json/set_captcha",
+ onSuccess: function(data){
+ if (data.captcha){
+ $('cap_img').set('src', data.src);
+ $('cap_span').setStyle('display', 'block');
+ $$('#cap_form p')[0].set('text', '{{_("Please read the text on the captcha.")}}');
+ $('cap_id').set('value', data.id);
+ } else{
+ $('cap_img').set('src', '');
+ $('cap_span').setStyle('display', 'none');
+ $$('#cap_form p')[0].set('text', '{{_("No Captchas to read.")}}');
+ }
+ },
+ secure: false,
+ async: true,
+ method: method
+ }).send(post);
+}
+
+function submit_cap(){
+ load_cap("post", "cap_id="+ $('cap_id').get('value') +"&cap_text=" + $('cap_text').get('value') );
+ $('cap_text').set('value', '');
+ return false;
+}
+
+
+function AddBox()
+{
+ if ($("add_box").getStyle("display") == "hidden" || $("add_box").getStyle("display") == "none" || $("add_box").getStyle("opacity" == 0))
+ {
+ show();
+ }
+ else
+ {
+ out();
+ }
+}
+
+</script>
+
+{% block head %}
+{% endblock %}
+</head>
+<body>
+<a class="anchor" name="top" id="top"></a>
+
+<div id="head-panel">
+
+ <div id="head-search-and-login">
+
+ {% if user.is_authenticated %}
+
+<span id="cap_info" style="display: {% if captcha %}inline{%else%}none{% endif %}">
+<img src="/media/default/img/images.png" alt="Captcha:" style="vertical-align:middle; margin:2px" />
+<span style="font-weight: bold; cursor: pointer; margin-right: 2px;">{{_("Captcha waiting")}}</span>
+</span>
+
+<img src="/media/default/img/head-login.png" alt="User:" style="vertical-align:middle; margin:2px" /><span style="padding-right: 2px;">{{user.name}}</span>
+ <ul id="user-actions">
+ <li><a href="/logout" class="action logout" rel="nofollow">{{_("Logout")}}</a></li>
+ {% if user.is_staff %}
+ <li><a href="/admin" class="action profile" rel="nofollow">{{_("Administrate")}}</a></li>
+ {% endif %}
+
+ </ul>
+{% else %}
+ <span style="padding-right: 2px;">{{_("Please Login!")}}</span>
+{% endif %}
+
+ </div>
+
+ <a href="/"><img id="head-logo" src="/media/default/img/pyload-logo-edited3.5-new-font-small.png" alt="pyLoad" /></a>
+
+ <div id="head-menu">
+ <ul>
+
+ {% block menu %}
+ <li class="selected">
+ <a href="/" title=""><img src="/media/default/img/head-menu-home.png" alt="" /> {{_("Home")}}</a>
+ </li>
+ <li>
+ <a href="/queue/" title=""><img src="/media/default/img/head-menu-queue.png" alt="" /> {{_("Queue")}}</a>
+ </li>
+ <li>
+ <a href="/collector/" title=""><img src="/media/default/img/head-menu-collector.png" alt="" /> {{_("Collector")}}</a>
+ </li>
+ <li>
+ <a href="/downloads/" title=""><img src="/media/default/img/head-menu-development.png" alt="" /> {{_("Downloads")}}</a>
+ </li>
+ <li class="right">
+ <a href="/logs/" class="action index" accesskey="x" rel="nofollow"><img src="/media/default/img/head-menu-index.png" alt="" />{{_("Logs")}}</a>
+ </li>
+ <li class="right">
+ <a href="/settings/" class="action index" accesskey="x" rel="nofollow"><img src="/media/default/img/head-menu-config.png" alt="" />{{_("Config")}}</a>
+ </li>
+ {% endblock %}
+
+ </ul>
+ </div>
+
+ <div style="clear:both;"></div>
+</div>
+
+{% if perms.can_change_status %}
+<ul id="page-actions2">
+ <li id="action_play"><a href="#" class="action play" accesskey="o" rel="nofollow">{{_("Start")}}</a></li>
+ <li id="action_stop"><a href="#" class="action stop" accesskey="o" rel="nofollow">{{_("Stop")}}</a></li>
+ <li id="action_cancel"><a href="#" class="action cancel" accesskey="o" rel="nofollow">{{_("Cancel")}}</a></li>
+ <li id="action_add"><a href="javascript:AddBox();" class="action add" accesskey="o" rel="nofollow" >{{_("Add")}}</a></li>
+</ul>
+{% endif %}
+
+{% if perms.can_see_dl %}
+<ul id="page-actions">
+ <li><span class="time">{{_("Download:")}}</span><a id="time" style=" background-color: {% if status.download %}#8ffc25{% else %} #fc6e26{% endif %}; padding-left: 0cm; padding-right: 0.1cm; "> {% if status.download %}{{_("on")}}{% else %}{{_("off")}}{% endif %}</a></li>
+ <li><span class="reconnect">{{_("Reconnect:")}}</span><a id="reconnect" style=" background-color: {% if status.reconnect %}#8ffc25{% else %} #fc6e26{% endif %}; padding-left: 0cm; padding-right: 0.1cm; "> {% if status.reconnect %}{{_("on")}}{% else %}{{_("off")}}{% endif %}</a></li>
+ <li><a class="action backlink">{{_("Speed:")}} <b id="speed">{{ status.speed }}</b> kb/s</a></li>
+ <li><a class="action cog">{{_("Active:")}} <b id="aktiv">{{ status.activ }}</b> / <b id="aktiv_from">{{ status.queue }}</b></a></li>
+ <li><a href="" class="action revisions" accesskey="o" rel="nofollow">{{_("Reload page")}}</a></li>
+</ul>
+{% endif %}
+
+{% block pageactions %}
+{% endblock %}
+<br/>
+
+<div id="body-wrapper" class="dokuwiki">
+
+<div id="content" lang="en" dir="ltr">
+
+<h1>{% block subtitle %}pyLoad - {{_("Webinterface")}}{% endblock %}</h1>
+
+{% block statusbar %}
+{% endblock %}
+
+
+<br/>
+
+<div class="level1" style="clear:both">
+</div>
+
+{% for message in messages %}
+ <b><p>{{message}}</p></b>
+{% endfor %}
+
+{% block content %}
+{% endblock content %}
+
+ <hr style="clear: both;" />
+
+<div id="foot">© 2008-2011 pyLoad Team
+<a href="#top" class="action top" accesskey="x"><span>{{_("Back to top")}}</span></a><br />
+<!--<div class="breadcrumbs"></div>-->
+
+</div>
+</div>
+</div>
+
+{% include "default/window.html" %}
+{% include "default/captcha.html" %}
+</body>
+</html>
diff --git a/module/web/templates/jinja/default/captcha.html b/module/web/templates/jinja/default/captcha.html new file mode 100644 index 000000000..b3be3deca --- /dev/null +++ b/module/web/templates/jinja/default/captcha.html @@ -0,0 +1,35 @@ +<iframe id="upload_target" name="upload_target" src="" style="display: none; width:0;height:0"></iframe>
+<!--<div id="add_box" style="left:50%; top:200px; margin-left: -450px; width: 900px; position: absolute; background: #FFF; padding: 10px 10px 10px 10px; display:none;">-->
+
+ <!--<div style="width: 900px; text-align: right;"><b onclick="AddBox();">[Close]</b></div>-->
+<div id="cap_box" class="myform">
+ <form id="cap_form" action="/json/set_captcha" method="POST" enctype="multipart/form-data" onsubmit="return false;">
+<h1>{{_("Captcha reading")}}</h1>
+<p>{{_("Please read the text on the captcha.")}}</p>
+
+<span id="cap_span">
+
+<label>{{_("Captcha")}}
+<span class="small">{{_("The captcha.")}}</span>
+</label>
+<span class="cont">
+ <img id="cap_img" style="padding: 2px;" src="">
+</span>
+
+<label>{{_("Text")}}
+<span class="small">{{_("Input the text on the captcha.")}}</span>
+</label>
+<input id="cap_text" name="cap_text" type="text" size="20" />
+<input type="hidden" value="" name="cap_id" id="cap_id"/>
+
+</span>
+
+<button id="cap_submit" type="submit">{{_("Submit")}}</button>
+<button id="cap_reset" style="margin-left:0px;" type="reset">{{_("Close")}}</button>
+
+<div class="spacer"></div>
+
+
+</form>
+
+</div>
\ No newline at end of file diff --git a/module/web/templates/jinja/default/collector.html b/module/web/templates/jinja/default/collector.html new file mode 100644 index 000000000..3e6b47234 --- /dev/null +++ b/module/web/templates/jinja/default/collector.html @@ -0,0 +1,84 @@ +{% extends 'default/base.html' %}
+{% block head %}
+
+<script type="text/javascript" src="/package_ui.js"></script>
+
+<script type="text/javascript">
+
+document.addEvent("domready", function(){
+ var pUI = new PackageUI("url", 0);
+});
+</script>
+{% endblock %}
+
+{% block title %}{{_("Collector")}} - {{super()}} {% endblock %}
+{% block subtitle %}{{_("Collector")}}{% endblock %}
+
+{% block menu %}
+<li>
+ <a href="/" title=""><img src="/media/default/img/head-menu-home.png" alt="" /> {{_("Home")}}</a>
+</li>
+<li>
+ <a href="/queue/" title=""><img src="/media/default/img/head-menu-queue.png" alt="" /> {{_("Queue")}}</a>
+</li>
+<li class="selected">
+ <a href="/collector/" title=""><img src="/media/default/img/head-menu-collector.png" alt="" /> {{_("Collector")}}</a>
+</li>
+<li>
+ <a href="/downloads/" title=""><img src="/media/default/img/head-menu-development.png" alt="" /> {{_("Downloads")}}</a>
+</li>
+<li class="right">
+ <a href="/logs/" class="action index" accesskey="x" rel="nofollow"><img src="/media/default/img/head-menu-index.png" alt="" />{{_("Logs")}}</a>
+</li>
+<li class="right">
+ <a href="/settings/" class="action index" accesskey="x" rel="nofollow"><img src="/media/default/img/head-menu-config.png" alt="" />{{_("Config")}}</a>
+</li>{% endblock %}
+
+{% block pageactions %}
+<ul id="page-actions-more">
+ <li id="del_finished"><a style="padding: 0; font-weight: bold;" href="#">{{_("Delete Finished")}}</a></li>
+ <li id="restart_failed"><a style="padding: 0; font-weight: bold;" href="#">{{_("Restart Failed")}}</a></li>
+</ul>
+{% endblock %}
+
+{% block content %}
+<div id="load-success" style="opacity: 0; float: right; color: white; background-color: #90ee90; padding: 4px; -moz-border-radius: 5px; border-radius: 5px; font-weight: bold; margin-left: -100%; margin-top: -10px;">{{_("success")}}</div>
+<div id="load-failure" style="opacity: 0; float: right; color: white; background-color: #f08080; padding: 4px; -moz-border-radius: 5px; border-radius: 5px; font-weight: bold; margin-left: -100%; margin-top: -10px;">{{_("failure")}}</div>
+<div id="load-indicator" style="opacity: 0; float: right; margin-top: -10px;">
+ <img src="/media/default/img/ajax-loader.gif" alt="" style="padding-right: 5px"/>
+ {{_("loading")}}
+</div>
+
+<ul id="package-list" style="list-style: none; padding-left: 0; margin-top: -10px;">
+{% for id, package in content %}
+ <li>
+<div id="package_{{id}}" class="package">
+ <div class="order" style="display: none;">{{ package.order }}</div>
+
+ <div class="packagename" style="cursor: pointer;">
+ <img class="package_drag" src="/media/default/img/folder.png" style="cursor: move; margin-bottom: -2px">
+ <span class="name">{{package.name}}</span>
+
+ <span class="buttons" style="opacity:0">
+ <img title="{{_("Delete Package")}}" style="cursor: pointer" width="12px" height="12px" src="/media/default/img/delete.png" />
+
+ <img title="{{_("Restart Package")}}" style="margin-left: -10px; cursor: pointer" height="12px" src="/media/default/img/arrow_refresh.png" />
+
+ <img title="{{_("Edit Package")}}" style="margin-left: -10px; cursor: pointer" height="12px" src="/media/default/img/pencil.png" />
+
+ <img title="{{_("Move Package to Queue")}}" style="margin-left: -10px; cursor: pointer" height="12px" src="/media/default/img/package_go.png" />
+ </span>
+ </div>
+ <div id="children_{{id}}" style="display: none;" class="children">
+ <span class="child_secrow">{{_("Folder:")}} <span class="folder">{{package.folder}}</span> | {{_("Password:")}} <span class="password">{{package.password}}</span> | {{_("Priority:")}} <span class="prio">{{package.priority}}</span></span>
+ <ul id="sort_children_{{id}}" style="list-style: none; padding-left: 0">
+ </ul>
+ </div>
+</div>
+ </li>
+{% endfor %}
+</ul>
+
+{% include "default/edit_package.html" %}
+
+{% endblock %}
\ No newline at end of file diff --git a/module/web/templates/jinja/default/downloads.html b/module/web/templates/jinja/default/downloads.html new file mode 100644 index 000000000..813dc8d06 --- /dev/null +++ b/module/web/templates/jinja/default/downloads.html @@ -0,0 +1,50 @@ +{% extends 'default/base.html' %} + +{% block title %}Downloads - {{super()}} {% endblock %} + +{% block menu %} +<li> + <a href="/" title=""><img src="/media/default/img/head-menu-home.png" alt="" /> {{_("Home")}}</a> +</li> +<li> + <a href="/queue/" title=""><img src="/media/default/img/head-menu-queue.png" alt="" /> {{_("Queue")}}</a> +</li> +<li> + <a href="/collector/" title=""><img src="/media/default/img/head-menu-collector.png" alt="" /> {{_("Collector")}}</a> +</li> +<li class="selected"> + <a href="/downloads/" title=""><img src="/media/default/img/head-menu-development.png" alt="" /> {{_("Downloads")}}</a> +</li> +<li class="right"> + <a href="/logs/" class="action index" accesskey="x" rel="nofollow"><img src="/media/default/img/head-menu-index.png" alt="" />{{_("Logs")}}</a> +</li> +<li class="right"> + <a href="/settings/" class="action index" accesskey="x" rel="nofollow"><img src="/media/default/img/head-menu-config.png" alt="" />{{_("Config")}}</a> +</li> +{% endblock %} + +{% block subtitle %} +{{_("Downloads")}} +{% endblock %} + +{% block content %} + +<ul> + {% for folder in files.folder %} + <li> + {{ folder.name }} + <ul> + {% for file in folder.files %} + <li><a href='get/{{ folder.path|escape }}/{{ file|escape }}'>{{file}}</a></li> + {% endfor %} + </ul> + </li> + {% endfor %} + + {% for file in files.files %} + <li> <a href='get/{{ file|escape }}'>{{ file }}</a></li> + {% endfor %} + +</ul> + +{% endblock %}
\ No newline at end of file diff --git a/module/web/templates/jinja/default/edit_package.html b/module/web/templates/jinja/default/edit_package.html new file mode 100644 index 000000000..0c9dcff42 --- /dev/null +++ b/module/web/templates/jinja/default/edit_package.html @@ -0,0 +1,40 @@ +<div id="pack_box" class="myform" style="z-index: 2">
+<form id="pack_form" action="/json/edit_package" method="POST" enctype="multipart/form-data">
+<h1>{{_("Edit Package")}}</h1>
+<p>{{_("Edit the package detais below.")}}</p>
+<input name="pack_id" id="pack_id" type="hidden" value=""/>
+<label for="pack_name">{{_("Name")}}
+<span class="small">{{_("The name of the package.")}}</span>
+</label>
+<input id="pack_name" name="pack_name" type="text" size="20" />
+
+<label for="pack_folder">{{_("Folder")}}
+<span class="small">{{_("Name of subfolder for these downloads.")}}</span>
+</label>
+<input id="pack_folder" name="pack_folder" type="text" size="20" />
+
+<label for="pack_prio">{{_("Priority")}}
+<span class="small">{{_("Priority of the package.")}}</span>
+</label>
+ <select name="pack_prio" id="pack_prio">
+ <option value="3">{{_("highest")}}</option>
+ <option value="2">{{_("higher")}}</option>
+ <option value="1">{{_("high")}}</option>
+ <option value="0" selected="selected">{{_("normal")}}</option>
+ <option value="-1">{{_("low")}}</option>
+ <option value="-2">{{_("lower")}}</option>
+ <option value="-3">{{_("lowest")}}</option>
+ </select>
+
+<label for="pack_pws">{{_("Password")}}
+<span class="small">{{_("List of passwords used for unrar.")}}</span>
+</label>
+<textarea rows="3" name="pack_pws" id="pack_pws"></textarea>
+
+<button type="submit">{{_("Submit")}}</button>
+<button id="pack_reset" style="margin-left: 0" type="reset">{{_("Reset")}}</button>
+<div class="spacer"></div>
+
+</form>
+
+</div>
\ No newline at end of file diff --git a/module/web/templates/jinja/default/home.html b/module/web/templates/jinja/default/home.html new file mode 100644 index 000000000..b2cef2cb7 --- /dev/null +++ b/module/web/templates/jinja/default/home.html @@ -0,0 +1,241 @@ +{% extends 'default/base.html' %}
+{% block head %}
+
+<script type="text/javascript">
+
+var em;
+var operafix = (navigator.userAgent.toLowerCase().search("opera") >= 0);
+
+document.addEvent("domready", function(){
+ em = new EntryManager();
+});
+
+var EntryManager = new Class({
+ initialize: function(){
+ this.json = new Request.JSON({
+ url: "json/links",
+ secure: false,
+ async: true,
+ onSuccess: this.update.bind(this),
+ initialDelay: 0,
+ delay: 2500,
+ limit: 30000
+ });
+
+ this.ids = [{% for link in content %}
+ {% if forloop.last %}
+ {{ link.id }}
+ {% else %}
+ {{ link.id }},
+ {% endif %}
+ {% endfor %}];
+
+ this.entries = [];
+ this.container = $('LinksAktiv');
+
+ this.parseFromContent();
+
+ this.json.startTimer();
+ },
+ parseFromContent: function(){
+ this.ids.each(function(id,index){
+ var entry = new LinkEntry(id);
+ entry.parse();
+ this.entries.push(entry)
+ }, this);
+ },
+ update: function(data){
+
+ try{
+ this.ids = this.entries.map(function(item){
+ return item.id
+ });
+
+ this.ids.filter(function(id){
+ return !this.ids.contains(id)
+ },data).each(function(id){
+ var index = this.ids.indexOf(id);
+ this.entries[index].remove();
+ this.entries = this.entries.filter(function(item){return item.id != this},id);
+ this.ids = this.ids.erase(id)
+ }, this);
+
+ data.links.each(function(link, i){
+ if (this.ids.contains(link.id)){
+
+ var index = this.ids.indexOf(link.id);
+ this.entries[index].update(link)
+
+ }else{
+ var entry = new LinkEntry(link.id);
+ entry.insert(link);
+ this.entries.push(entry);
+ this.ids.push(link.id);
+ this.container.adopt(entry.elements.tr,entry.elements.pgbTr);
+ entry.fade.start('opacity', 1);
+ entry.fadeBar.start('opacity', 1);
+
+ }
+ }, this)
+ }catch(e){
+ //alert(e)
+ }
+ }
+});
+
+
+var LinkEntry = new Class({
+ initialize: function(id){
+ this.id = id
+ },
+ parse: function(){
+ this.elements = {
+ tr: $("link_{id}".substitute({id: this.id})),
+ name: $("link_{id}_name".substitute({id: this.id})),
+ status: $("link_{id}_status".substitute({id: this.id})),
+ info: $("link_{id}_info".substitute({id: this.id})),
+ bleft: $("link_{id}_kbleft".substitute({id: this.id})),
+ percent: $("link_{id}_percent".substitute({id: this.id})),
+ remove: $("link_{id}_remove".substitute({id: this.id})),
+ pgbTr: $("link_{id}_pgb_tr".substitute({id: this.id})),
+ pgb: $("link_{id}_pgb".substitute({id: this.id}))
+ };
+ this.initEffects();
+ },
+ insert: function(item){
+ try{
+
+ this.elements = {
+ tr: new Element('tr', {
+ 'html': '',
+ 'styles':{
+ 'opacity': 0
+ }
+ }),
+ name: new Element('td', {
+ 'html': item.name
+ }),
+ status: new Element('td', {
+ 'html': item.statusmsg
+ }),
+ info: new Element('td', {
+ 'html': item.info
+ }),
+ bleft: new Element('td', {
+ 'html': HumanFileSize(item.size)
+ }),
+ percent: new Element('span', {
+ 'html': item.percent+ '% / '+ HumanFileSize(item.size-item.bleft)
+ }),
+ remove: new Element('img',{
+ 'src': 'media/default/img/control_cancel.png',
+ 'styles':{
+ 'vertical-align': 'middle',
+ 'margin-right': '-20px',
+ 'margin-left': '5px',
+ 'margin-top': '-2px',
+ 'cursor': 'pointer'
+ }
+ }),
+ pgbTr: new Element('tr', {
+ 'html':''
+ }),
+ pgb: new Element('div', {
+ 'html': ' ',
+ 'styles':{
+ 'height': '4px',
+ 'width': item.percent+'%',
+ 'background-color': '#ddd'
+ }
+ })
+ };
+
+ this.elements.tr.adopt(this.elements.name,this.elements.status,this.elements.info,this.elements.bleft,new Element('td').adopt(this.elements.percent,this.elements.remove));
+ this.elements.pgbTr.adopt(new Element('td',{'colspan':5}).adopt(this.elements.pgb));
+ this.initEffects();
+ }catch(e){
+ alert(e)
+ }
+ },
+ initEffects: function(){
+ if(!operafix)
+ this.bar = new Fx.Morph(this.elements.pgb, {unit: '%', duration: 5000, link: 'link', fps:30});
+ this.fade = new Fx.Tween(this.elements.tr);
+ this.fadeBar = new Fx.Tween(this.elements.pgbTr);
+
+ this.elements.remove.addEvent('click', function(){
+ new Request({method: 'get', url: '/json/abort_link/'+this.id}).send();
+ }.bind(this));
+
+ },
+ update: function(item){
+ this.elements.name.set('text', item.name);
+ this.elements.status.set('text', item.statusmsg);
+ this.elements.info.set('text', item.info);
+ this.elements.bleft.set('text', item.format_size);
+ this.elements.percent.set('text', item.percent+ '% / '+ HumanFileSize(item.size-item.bleft));
+ if(!operafix)
+ {
+ this.bar.start({
+ 'width': item.percent,
+ 'background-color': [Math.round(120/100*item.percent),100,100].hsbToRgb().rgbToHex()
+ });
+ }
+ else
+ {
+ this.elements.pgb.set(
+ 'styles', {
+ 'height': '4px',
+ 'width': item.percent+'%',
+ 'background-color': [Math.round(120/100*item.percent),100,100].hsbToRgb().rgbToHex(),
+ });
+ }
+ },
+ remove: function(){
+ this.fade.start('opacity',0).chain(function(){this.elements.tr.dispose();}.bind(this));
+ this.fadeBar.start('opacity',0).chain(function(){this.elements.pgbTr.dispose();}.bind(this));
+
+ }
+ });
+</script>
+
+{% endblock %}
+
+{% block subtitle %}
+{{_("Active Downloads")}}
+{% endblock %}
+
+{% block content %}
+<table width="100%" class="queue">
+ <thead>
+ <tr class="header">
+ <th>{{_("Name")}}</th>
+ <th>{{_("Status")}}</th>
+ <th>{{_("Information")}}</th>
+ <th>{{_("Size")}}</th>
+ <th>{{_("Progress")}}</th>
+ </tr>
+ </thead>
+ <tbody id="LinksAktiv">
+
+ {% for link in content %}
+ <tr id="link_{{ link.id }}">
+ <td id="link_{{ link.id }}_name">{{ link.name }}</td>
+ <td id="link_{{ link.id }}_status">{{ link.status }}</td>
+ <td id="link_{{ link.id }}_info">{{ link.info }}</td>
+ <td id="link_{{ link.id }}_kbleft">{{ link.format_size }}</td>
+ <td>
+ <span id="link_{{ link.id }}_percent">{{ link.percent }}% /{{ link.kbleft }}</span>
+ <img id="link_{{ link.id }}_remove" style="vertical-align: middle; margin-right: -20px; margin-left: 5px; margin-top: -2px; cursor:pointer;" src="media/default/img/control_cancel.png"/>
+ </td>
+ </tr>
+ <tr id="link_{{ link.id }}_pgb_tr">
+ <td colspan="5">
+ <div id="link_{{ link.id }}_pgb" class="progressBar" style="background-color: green; height:4px; width: {{ link.percent }}%;"> </div>
+ </td>
+ </tr>
+ {% endfor %}
+
+ </tbody>
+</table>
+{% endblock %}
\ No newline at end of file diff --git a/module/web/templates/jinja/default/login.html b/module/web/templates/jinja/default/login.html new file mode 100644 index 000000000..0e9e4d568 --- /dev/null +++ b/module/web/templates/jinja/default/login.html @@ -0,0 +1,35 @@ +{% extends 'default/base.html' %} + +{% block title %}{{_("Login")}} - {{super()}} {% endblock %} + +{% block content %} + +<div class="centeralign"> +<form action="" method="post" accept-charset="utf-8" id="login"> + <div class="no"> + <input type="hidden" name="do" value="login" /> + <fieldset> + <legend>Login</legend> + <label> + <span>{{_("Username")}}</span> + <input type="text" size="20" name="username"/> + </label> + <br /> + <label> + <span>{{_("Password")}}</span> + <input type="password" size="20" name="password"> + </label> + <br /> + <input type="submit" value="Login" class="button" /> + </fieldset> + </div> +</form> + +{% if errors %} +<p>{{_("Your username and password didn't match. Please try again.")}}</p> +{% endif %} + +</div> +<br> + +{% endblock %} diff --git a/module/web/templates/jinja/default/logout.html b/module/web/templates/jinja/default/logout.html new file mode 100644 index 000000000..d3f07472b --- /dev/null +++ b/module/web/templates/jinja/default/logout.html @@ -0,0 +1,9 @@ +{% extends 'default/base.html' %} + +{% block head %} +<meta http-equiv="refresh" content="3; url=/"> +{% endblock %} + +{% block content %} +<p><b>{{_("You were successfully logged out.")}}</b></p> +{% endblock %}
\ No newline at end of file diff --git a/module/web/templates/jinja/default/logs.html b/module/web/templates/jinja/default/logs.html new file mode 100644 index 000000000..7a95b4364 --- /dev/null +++ b/module/web/templates/jinja/default/logs.html @@ -0,0 +1,61 @@ +{% extends 'default/base.html' %} + +{% block title %}{{_("Logs")}} - {{super()}} {% endblock %} +{% block subtitle %}{{_("Logs")}}{% endblock %} +{% block head %} +<link rel="stylesheet" type="text/css" href="/media/default/css/log.css"/> +{% endblock %} +{% block menu %} +<li> + <a href="/" title=""><img src="/media/default/img/head-menu-home.png" alt="" /> {{_("Home")}}</a> +</li> +<li> + <a href="/queue/" title=""><img src="/media/default/img/head-menu-queue.png" alt="" /> {{_("Queue")}}</a> +</li> +<li> + <a href="/collector/" title=""><img src="/media/default/img/head-menu-collector.png" alt="" /> {{_("Collector")}}</a> +</li> +<li> + <a href="/downloads/" title=""><img src="/media/default/img/head-menu-development.png" alt="" /> {{_("Downloads")}}</a> +</li> +<li class="right selected"> + <a href="/logs/" class="action index" accesskey="x" rel="nofollow"><img src="/media/default/img/head-menu-index.png" alt="" />{{_("Logs")}}</a> +</li> +<li class="right"> + <a href="/settings/" class="action index" accesskey="x" rel="nofollow"><img src="/media/default/img/head-menu-config.png" alt="" />{{_("Config")}}</a> +</li> +{% endblock %} + +{% block content %} +<div style="clear: both;"></div> + +<div class="logpaginator"><a href="{{ "/logs/1" }}"><< {{_("Start")}}</a> <a href="{{ "/logs/" + iprev|string }}">< {{_("prev")}}</a> <a href="{{ "/logs/" + inext|string }}">{{_("next")}} ></a> <a href="/logs/">{{_("End")}} >></a></div> +<div class="logperpage"> + <form id="logform1" action="" method="POST"> + <label for="reversed">Reversed:</label> + <input type="checkbox" name="reversed" onchange="this.form.submit();" {% if reversed %} checked="checked" {% endif %} /> + <label for="perpage">Lines per page:</label> + <select name="perpage" onchange="this.form.submit();"> + {% for value in perpage_p %} + <option value="{{value.0}}"{% if value.0 == perpage %} selected="selected" {% endif %}>{{value.1}}</option> + {% endfor %} + </select> + </form> +</div> +<div class="logwarn">{{warning}}</div> +<div style="clear: both;"></div> +<div class="logdiv"> + <table class="logtable" cellpadding="0" cellspacing="0"> + {% for line in log %} + <tr><td class="logline">{{line.line}}</td><td>{{line.date}}</td><td class="loglevel">{{line.level}}</td><td>{{line.message}}</td></tr> + {% endfor %} + </table> +</div> +<div class="logform"> +<form id="logform2" action="" method="POST"> + <label for="from">Jump to time:</label><input type="text" name="from" size="15" value="{{from}}"/> + <input type="submit" value="ok" /> +</form> +</div> +<div style="clear: both; height: 10px;"> </div> +{% endblock %}
\ No newline at end of file diff --git a/module/web/templates/jinja/default/package_ui.js b/module/web/templates/jinja/default/package_ui.js new file mode 100644 index 000000000..45e284903 --- /dev/null +++ b/module/web/templates/jinja/default/package_ui.js @@ -0,0 +1,408 @@ +var load, success, fail, pack_box; + +document.addEvent("domready", function() { + load = new Fx.Tween($("load-indicator"), {link: "cancel"}); + success = new Fx.Tween($("load-success"), {link: "chain"}); + fail = new Fx.Tween($("load-failure"), {link: "chain"}); + + [load,success,fail].each(function(fx) { + fx.set("opacity", 0) + }); + + pack_box = new Fx.Tween($('pack_box')); + $('pack_reset').addEvent('click', function() { + hide_pack() + }); +}); + +function indicateLoad() { + //$("load-indicator").reveal(); + load.start("opacity", 1) +} + +function indicateFinish() { + load.start("opacity", 0) +} + +function indicateSuccess() { + indicateFinish(); + success.start("opacity", 1).chain(function() { + (function() { + success.start("opacity", 0); + }).delay(250); + }); + +} + +function indicateFail() { + indicateFinish(); + fail.start("opacity", 1).chain(function() { + (function() { + fail.start("opacity", 0); + }).delay(250); + }); +} + +function show_pack() { + bg_show(); + $("pack_box").setStyle('display', 'block'); + pack_box.start('opacity', 1) +} + +function hide_pack() { + bg_hide(); + pack_box.start('opacity', 0).chain(function() { + $('pack_box').setStyle('display', 'none'); + }); +} + +var PackageUI = new Class({ + initialize: function(url, type) { + this.url = url; + this.type = type; + this.packages = []; + this.parsePackages(); + + this.sorts = new Sortables($("package-list"), { + constrain: false, + clone: true, + revert: true, + opacity: 0.4, + handle: ".package_drag", + //onStart: this.startSort, + onComplete: this.saveSort.bind(this) + }); + + $("del_finished").addEvent("click", this.deleteFinished.bind(this)); + $("restart_failed").addEvent("click", this.restartFailed.bind(this)); + + }, + + parsePackages: function() { + $("package-list").getChildren("li").each(function(ele) { + var id = ele.getFirst().get("id").match(/[0-9]+/); + this.packages.push(new Package(this, id, ele)) + }.bind(this)) + }, + + loadPackages: function() { + }, + + deleteFinished: function() { + indicateLoad(); + new Request.JSON({ + method: 'get', + url: '/json/delete_finished', + onSuccess: function(data) { + if (data.del.length > 0) { + window.location.reload() + } else { + this.packages.each(function(pack) { + pack.close(); + }); + indicateSuccess(); + } + }.bind(this), + onFailure: indicateFail + }).send(); + }, + + restartFailed: function() { + indicateLoad(); + new Request.JSON({ + method: 'get', + url: '/json/restart_failed', + onSuccess: function(data) { + this.packages.each(function(pack) { + pack.close(); + }); + indicateSuccess(); + }.bind(this), + onFailure: indicateFail + }).send(); + }, + + startSort: function(ele, copy) { + }, + + saveSort: function(ele, copy) { + var order = []; + this.sorts.serialize(function(li, pos) { + if (li == ele && ele.retrieve("order") != pos) { + order.push(ele.retrieve("pid") + "|" + pos) + } + li.store("order", pos) + }); + if (order.length > 0) { + indicateLoad(); + new Request.JSON({ + method: 'get', + url: '/json/package_order/' + order[0], + onSuccess: indicateFinish, + onFailure: indicateFail + }).send(); + } + } + +}); + +var Package = new Class({ + initialize: function(ui, id, ele, data) { + this.ui = ui; + this.id = id; + this.linksLoaded = false; + + if (!ele) { + this.createElement(data); + } else { + this.ele = ele; + this.order = ele.getElements("div.order")[0].get("html"); + this.ele.store("order", this.order); + this.ele.store("pid", this.id); + this.parseElement(); + } + + var pname = this.ele.getElements(".packagename")[0]; + this.buttons = new Fx.Tween(this.ele.getElements(".buttons")[0], {link: "cancel"}); + this.buttons.set("opacity", 0); + + pname.addEvent("mouseenter", function(e) { + this.buttons.start("opacity", 1) + }.bind(this)); + + pname.addEvent("mouseleave", function(e) { + this.buttons.start("opacity", 0) + }.bind(this)); + + + }, + + createElement: function() { + alert("create") + }, + + parseElement: function() { + var imgs = this.ele.getElements('img'); + + this.name = this.ele.getElements('.name')[0]; + this.folder = this.ele.getElements('.folder')[0]; + this.password = this.ele.getElements('.password')[0]; + this.prio = this.ele.getElements('.prio')[0]; + + imgs[1].addEvent('click', this.deletePackage.bind(this)); + + imgs[2].addEvent('click', this.restartPackage.bind(this)); + + imgs[3].addEvent('click', this.editPackage.bind(this)); + + imgs[4].addEvent('click', this.movePackage.bind(this)); + + this.ele.getElement('.packagename').addEvent('click', this.toggle.bind(this)); + + }, + + loadLinks: function() { + indicateLoad(); + new Request.JSON({ + method: 'get', + url: '/json/package/' + this.id, + onSuccess: this.createLinks.bind(this), + onFailure: indicateFail + }).send(); + }, + + createLinks: function(data) { + var ul = $("sort_children_{id}".substitute({"id": this.id})); + ul.erase("html"); + data.links.each(function(link) { + var li = new Element("li", { + "style": { + "margin-left": 0 + } + }); + + var html = "<span style='cursor: move' class='child_status sorthandle'><img src='/media/default/img/{icon}' style='width: 12px; height:12px;'/></span>\n".substitute({"icon": link.icon}); + html += "<span style='font-size: 15px'>{name}</span><br /><div class='child_secrow'>".substitute({"name": link.name}); + html += "<span class='child_status'>{statusmsg}</span>{error} ".substitute({"statusmsg": link.statusmsg, "error":link.error}); + html += "<span class='child_status'>{format_size}</span>".substitute({"format_size": link.format_size}); + html += "<span class='child_status'>{plugin}</span> ".substitute({"plugin": link.plugin}); + html += "<img title='{{_("Delete Link")}}' style='cursor: pointer;' width='10px' height='10px' src='/media/default/img/delete.png' /> "; + html += "<img title='{{_("Restart Link")}}' style='cursor: pointer;margin-left: -4px' width='10px' height='10px' src='/media/default/img/arrow_refresh.png' /></div>"; + + var div = new Element("div", { + "id": "file_" + link.id, + "class": "child", + "html": html + }); + + li.store("order", link.order); + li.store("lid", link.id); + + li.adopt(div); + ul.adopt(li); + }); + this.sorts = new Sortables(ul, { + constrain: false, + clone: true, + revert: true, + opacity: 0.4, + handle: ".sorthandle", + onComplete: this.saveSort.bind(this) + }); + this.registerLinkEvents(); + this.linksLoaded = true; + indicateFinish(); + this.toggle(); + }, + + registerLinkEvents: function() { + this.ele.getElements('.child').each(function(child) { + var lid = child.get('id').match(/[0-9]+/); + var imgs = child.getElements('.child_secrow img'); + imgs[0].addEvent('click', function(e) { + new Request({ + method: 'get', + url: '/json/remove_link/' + this, + onSuccess: function() { + $('file_' + this).nix() + }.bind(this), + onFailure: indicateFail + }).send(); + }.bind(lid)); + + imgs[1].addEvent('click', function(e) { + new Request({ + method: 'get', + url: '/json/restart_link/' + this, + onSuccess: function() { + var ele = $('file_' + this); + var imgs = ele.getElements("img"); + imgs[0].set("src", "/media/default/img/status_queue.png"); + var spans = ele.getElements(".child_status"); + spans[1].set("html", "queued"); + indicateSuccess(); + }.bind(this), + onFailure: indicateFail + }).send(); + }.bind(lid)); + }); + }, + + toggle: function() { + var child = this.ele.getElement('.children'); + if (child.getStyle('display') == "block") { + child.dissolve(); + } else { + if (!this.linksLoaded) { + this.loadLinks(); + } else { + child.reveal(); + } + } + }, + + deletePackage: function(event) { + indicateLoad(); + new Request({ + method: 'get', + url: '/json/remove_package/' + this.id, + onSuccess: function() { + this.ele.nix(); + indicateFinish(); + }.bind(this), + onFailure: indicateFail + }).send(); + event.stop(); + }, + + restartPackage: function(event) { + indicateLoad(); + new Request({ + method: 'get', + url: '/json/restart_package/' + this.id, + onSuccess: function() { + this.close(); + indicateSuccess(); + }.bind(this), + onFailure: indicateFail + }).send(); + event.stop(); + }, + + close: function() { + var child = this.ele.getElement('.children'); + if (child.getStyle('display') == "block") { + child.dissolve(); + } + var ul = $("sort_children_{id}".substitute({"id": this.id})); + ul.erase("html"); + this.linksLoaded = false; + }, + + movePackage: function(event) { + indicateLoad(); + new Request({ + method: 'get', + url: '/json/move_package/' + ((this.ui.type + 1) % 2) + "/" + this.id, + onSuccess: function() { + this.ele.nix(); + indicateFinish(); + }.bind(this), + onFailure: indicateFail + }).send(); + event.stop(); + }, + + editPackage: function(event) { + $("pack_form").removeEvents("submit"); + $("pack_form").addEvent("submit", this.savePackage.bind(this)); + + $("pack_id").set("value", this.id); + $("pack_name").set("value", this.name.get("text")); + $("pack_folder").set("value", this.folder.get("text")); + $("pack_pws").set("value", this.password.get("text")); + + var prio = 3; + $("pack_prio").getChildren("option").each(function(item, index) { + item.erase("selected"); + if (prio.toString() == this.prio.get("text")) { + item.set("selected", "selected"); + } + prio--; + }.bind(this)); + + + show_pack(); + event.stop(); + }, + + savePackage: function(event) { + $("pack_form").send(); + this.name.set("text", $("pack_name").get("value")); + this.folder.set("text", $("pack_folder").get("value")); + this.password.set("text", $("pack_pws").get("value")); + this.prio.set("text", $("pack_prio").get("value")); + hide_pack(); + event.stop(); + }, + + saveSort: function(ele, copy) { + var order = []; + this.sorts.serialize(function(li, pos) { + if (li == ele && ele.retrieve("order") != pos) { + order.push(ele.retrieve("lid") + "|" + pos) + } + li.store("order", pos) + }); + if (order.length > 0) { + indicateLoad(); + new Request.JSON({ + method: 'get', + url: '/json/link_order/' + order[0], + onSuccess: indicateFinish, + onFailure: indicateFail + }).send(); + } + } + +});
\ No newline at end of file diff --git a/module/web/templates/jinja/default/pathchooser.html b/module/web/templates/jinja/default/pathchooser.html new file mode 100644 index 000000000..d00637055 --- /dev/null +++ b/module/web/templates/jinja/default/pathchooser.html @@ -0,0 +1,76 @@ +<html> +<head> + <script class="javascript"> + function chosen() + { + opener.ifield.value = document.forms[0].p.value; + close(); + } + function exit() + { + close(); + } + function setInvalid() { + document.forms[0].send.disabled = 'disabled'; + document.forms[0].p.style.color = '#FF0000'; + } + function setValid() { + document.forms[0].send.disabled = ''; + document.forms[0].p.style.color = '#000000'; + } + function setFile(file) + { + document.forms[0].p.value = file; + setValid(); + + } + </script> + <link rel="stylesheet" type="text/css" href="/media/default/css/pathchooser.css"/> +</head> +<body{% if type == 'file' %}{% if not oldfile %} onload="setInvalid();"{% endif %}{% endif %}> +<center> + <div id="paths"> + <form method="get" action="?" onSubmit="chosen();" onReset="exit();"> + <input type="text" name="p" value="{{ oldfile|default(cwd) }}" size="60" onfocus="setValid();"> + <input type="submit" value="Ok" name="send"> + </form> + + {% if type == 'folder' %} + <span class="path_abs_rel">{{_("Path")}}: <a href="{{ "/pathchooser" + cwd|path_make_absolute|quotepath }}"{% if absolute %} style="text-decoration: underline;"{% endif %}>{{_("absolute")}}</a> | <a href="{{ "/pathchooser/" + cwd|path_make_relative|quotepath }}"{% if not absolute %} style="text-decoration: underline;"{% endif %}>{{_("relative")}}</a></span> + {% else %} + <span class="path_abs_rel">{{_("Path")}}: <a href="{{ "/filechooser/" + cwd|path_make_absolute|quotepath }}"{% if absolute %} style="text-decoration: underline;"{% endif %}>{{_("absolute")}}</a> | <a href="{{ "/filechooser/" + cwd|path_make_relative|quotepath }}"{% if not absolute %} style="text-decoration: underline;"{% endif %}>{{_("relative")}}</a></span> + {% endif %} + </div> + <table border="0" cellspacing="0" cellpadding="3"> + <tr> + <th>{{_("name")}}</th> + <th>{{_("size")}}</th> + <th>{{_("type")}}</th> + <th>{{_("last modified")}}</th> + </tr> + {% if parentdir %} + <tr> + <td colspan="4"> + <a href="{% if type == 'folder' %}{{ "/pathchooser/" + parentdir|quotepath }}{% else %}{{ "/filechooser/" + parentdir|quotepath }}{% endif %}"><span class="parentdir">{{_("parent directory")}}</span></a> + </td> + </tr> + {% endif %} +{% for file in files %} + <tr> + {% if type == 'folder' %} + <td class="name">{% if file.type == 'dir' %}<a href="{{ "/pathchooser/" + file.fullpath|quotepath }}" title="{{ file.fullpath }}"><span class="path_directory">{{ file.name|truncate(25) }}</span></a>{% else %}<span class="path_file" title="{{ file.fullpath }}">{{ file.name|truncate(25) }}{% endif %}</span></td> + {% else %} + <td class="name">{% if file.type == 'dir' %}<a href="{{ "/filechooser/" + file.fullpath|quotepath }}" title="{{ file.fullpath }}"><span class="file_directory">{{ file.name|truncate(25) }}</span></a>{% else %}<a href="#" onclick="setFile('{{ file.fullpath }}');" title="{{ file.fullpath }}"><span class="file_file">{{ file.name|truncate(25) }}{% endif %}</span></a></td> + {% endif %} + <td class="size">{{ file.size|float|filesizeformat }}</td> + <td class="type">{% if file.type == 'dir' %}directory{% else %}{{ file.ext|default("file") }}{% endif %}</td> + <td class="mtime">{{ file.modified|date("d.m.Y - H:i:s") }}</td> + <tr> +<!-- <tr> + <td colspan="4">{{_("no content")}}</td> + </tr> --> +{% endfor %} + </table> + </center> +</body> +</html>
\ No newline at end of file diff --git a/module/web/templates/jinja/default/queue.html b/module/web/templates/jinja/default/queue.html new file mode 100644 index 000000000..e72871873 --- /dev/null +++ b/module/web/templates/jinja/default/queue.html @@ -0,0 +1,85 @@ +{% extends 'default/base.html' %}
+{% block head %}
+
+<script type="text/javascript" src="/package_ui.js"></script>
+
+<script type="text/javascript">
+
+document.addEvent("domready", function(){
+ var pUI = new PackageUI("url",1);
+});
+</script>
+{% endblock %}
+
+{% block title %}{{_("Queue")}} - {{super()}} {% endblock %}
+{% block subtitle %}{{_("Queue")}}{% endblock %}
+
+{% block menu %}
+<li>
+ <a href="/" title=""><img src="/media/default/img/head-menu-home.png" alt="" /> {{_("Home")}}</a>
+</li>
+<li class="selected">
+ <a href="/queue/" title=""><img src="/media/default/img/head-menu-queue.png" alt="" /> {{_("Queue")}}</a>
+</li>
+<li>
+ <a href="/collector/" title=""><img src="/media/default/img/head-menu-collector.png" alt="" /> {{_("Collector")}}</a>
+</li>
+<li>
+ <a href="/downloads/" title=""><img src="/media/default/img/head-menu-development.png" alt="" /> {{_("Downloads")}}</a>
+</li>
+<li class="right">
+ <a href="/logs/" class="action index" accesskey="x" rel="nofollow"><img src="/media/default/img/head-menu-index.png" alt="" />{{_("Logs")}}</a>
+</li>
+<li class="right">
+ <a href="/settings/" class="action index" accesskey="x" rel="nofollow"><img src="/media/default/img/head-menu-config.png" alt="" />{{_("Config")}}</a>
+</li>
+{% endblock %}
+
+{% block pageactions %}
+<ul id="page-actions-more">
+ <li id="del_finished"><a style="padding: 0; font-weight: bold;" href="#">{{_("Delete Finished")}}</a></li>
+ <li id="restart_failed"><a style="padding: 0; font-weight: bold;" href="#">{{_("Restart Failed")}}</a></li>
+</ul>
+{% endblock %}
+
+{% block content %}
+<div id="load-success" style="opacity: 0; float: right; color: white; background-color: #90ee90; padding: 4px; -moz-border-radius: 5px; border-radius: 5px; font-weight: bold; margin-left: -100%; margin-top: -10px;">{{_("success")}}</div>
+<div id="load-failure" style="opacity: 0; float: right; color: white; background-color: #f08080; padding: 4px; -moz-border-radius: 5px; border-radius: 5px; font-weight: bold; margin-left: -100%; margin-top: -10px;">{{_("failure")}}</div>
+<div id="load-indicator" style="opacity: 0; float: right; margin-top: -10px;">
+ <img src="/media/default/img/ajax-loader.gif" alt="" style="padding-right: 5px"/>
+ {{_("loading")}}
+</div>
+
+<ul id="package-list" style="list-style: none; padding-left: 0; margin-top: -10px;">
+{% for id, package in content %}
+ <li>
+<div id="package_{{id}}" class="package">
+ <div class="order" style="display: none;">{{ package.order }}</div>
+
+ <div class="packagename" style="cursor: pointer;">
+ <img class="package_drag" src="/media/default/img/folder.png" style="cursor: move; margin-bottom: -2px">
+ <span class="name">{{package.name}}</span>
+
+ <span class="buttons" style="opacity:0">
+ <img title="{{_("Delete Package")}}" style="cursor: pointer" width="12px" height="12px" src="/media/default/img/delete.png" />
+
+ <img title="{{_("Restart Package")}}" style="margin-left: -10px; cursor: pointer" height="12px" src="/media/default/img/arrow_refresh.png" />
+
+ <img title="{{_("Edit Package")}}" style="margin-left: -10px; cursor: pointer" height="12px" src="/media/default/img/pencil.png" />
+
+ <img title="{{_("Move Package to Collector")}}" style="margin-left: -10px; cursor: pointer" height="12px" src="/media/default/img/package_go.png" />
+ </span>
+ </div>
+ <div id="children_{{id}}" style="display: none;" class="children">
+ <span class="child_secrow">{{_("Folder:")}} <span class="folder">{{package.folder}}</span> | {{_("Password:")}} <span class="password">{{package.password}}</span> | {{_("Priority:")}} <span class="prio">{{package.priority}}</span></span>
+ <ul id="sort_children_{{id}}" style="list-style: none; padding-left: 0">
+ </ul>
+ </div>
+</div>
+ </li>
+{% endfor %}
+</ul>
+
+{% include "default/edit_package.html" %}
+
+{% endblock %}
\ No newline at end of file diff --git a/module/web/templates/jinja/default/settings.html b/module/web/templates/jinja/default/settings.html new file mode 100644 index 000000000..18bc78e30 --- /dev/null +++ b/module/web/templates/jinja/default/settings.html @@ -0,0 +1,232 @@ +{% extends 'default/base.html' %} + +{% block title %}{{ _("Config") }} - {{ super() }} {% endblock %} +{% block subtitle %}{{ _("Config") }}{% endblock %} + +{% block head %} + <script type="text/javascript"> + window.addEvent('domready', function() { + $$('#toptabs a').addEvent('click', function(e) { + $$('#toptabs a').removeProperty('class'); + e.target.set('class', 'selected'); + + $$('#tabs span').removeProperty('class'); + $('g_' + e.target.get('href').substring(1)).set('class', 'selected'); + + var firstsel = $$('#tabs span.selected a')[0]; + firstsel.fireEvent('click', {target: firstsel}); + return false; + }); + + $$('#tabs a').addEvent('click', function(e) { + $$('#tabs a').removeProperty('class'); + e.target.set('class', 'selected'); + + $$('div.tabContent').set('class', 'tabContent hide'); + $(e.target.get('href').substring(1)).set('class', 'tabContent'); + return false; + }); + + $$('#toptabs a')[0].set('class', 'selected'); + $$('#tabs span')[0].set('class', 'selected'); + + var firstsel = $$('#tabs span.selected a')[0]; + firstsel.fireEvent('click', {target: firstsel}); + }); + + + </script> + +{% endblock %} + +{% block menu %} + <li> + <a href="/" title=""><img src="/media/default/img/head-menu-home.png" alt=""/> {{ _("Home") }}</a> + </li> + <li> + <a href="/queue/" title=""><img src="/media/default/img/head-menu-queue.png" alt=""/> {{ _("Queue") }}</a> + </li> + <li> + <a href="/collector/" title=""><img src="/media/default/img/head-menu-collector.png" + alt=""/> {{ _("Collector") }}</a> + </li> + <li> + <a href="/downloads/" title=""><img src="/media/default/img/head-menu-development.png" + alt=""/> {{ _("Downloads") }}</a> + </li> + <li class="right"> + <a href="/logs/" class="action index" accesskey="x" rel="nofollow"><img + src="/media/default/img/head-menu-index.png" alt=""/>{{ _("Logs") }}</a> + </li> + <li class="right selected"> + <a href="/settings/" class="action index" accesskey="x" rel="nofollow"><img + src="/media/default/img/head-menu-config.png" alt=""/>{{ _("Config") }}</a> + </li> +{% endblock %} + +{% block content %} + + <ul id="toptabs" class="tabs"> + {% for configname, config in conf.iteritems() %} + <li><a href="#{{configname}}">{{ configname }}</a></li> + {% endfor %} + </ul> + + <div id="tabsback"> + <ul id="tabs" class="tabs"> + {% for configname, config in conf.iteritems() %} + <span id="g_{{configname}}"> + {% if configname != "Accounts" %} + {% for skey, section in config.iteritems() %} + <li><a href="#{{configname}}{{skey}}">{{ section.desc }}</a></li> + {% endfor %} + {% else %} + {% for skey, section in config.iteritems() %} + <li><a href="#{{configname}}{{skey}}">{{ skey }}</a></li> + {% endfor %} + {% endif %} + </span> + {% endfor %} + </ul> + </div> + <form id="horizontalForm" action="" method="POST" autocomplete="off"> + {% for configname, config in conf.iteritems() %} + {% if configname != "Accounts" %} + {% for skey, section in config.iteritems() %} + <div class="tabContent" id="{{configname}}{{skey}}"> + <table class="settable"> + {% for okey, option in section.iteritems() %} + {% if okey != "desc" %} + <tr> + <td><label for="{{configname}}|{{skey}}|{{okey}}" + style="color:#424242;">{{ option.desc }}:</label></td> + <td> + {% if option.type == "bool" %} + <select id="{{skey}}|{{okey}}" name="{{configname}}|{{skey}}|{{okey}}"> + <option {% if option.value %} selected="selected" + {% endif %}value="True">{{ _("on") }}</option> + <option {% if not option.value %} selected="selected" + {% endif %}value="False">{{ _("off") }}</option> + </select> + {% elif ";" in option.type %} + <select id="{{skey}}|{{okey}}" name="{{configname}}|{{skey}}|{{okey}}"> + {% for entry in option.list %} + <option {% if option.value == entry %} + selected="selected" {% endif %}>{{ entry }}</option> + {% endfor %} + </select> + {% elif option.type == "folder" %} + <input name="{{configname}}|{{skey}}|{{okey}}" type="text" + id="{{skey}}|{{okey}}" value="{{option.value}}"/> + <input name="browsebutton" type="button" + onclick="ifield = document.getElementById('{{skey}}|{{okey}}'); pathchooser = window.open('{% if option.value %}{{ "/pathchooser/" + option.value|quotepath }}{% else %}{{ pathroot }}{% endif %}', 'pathchooser', 'scrollbars=yes,toolbar=no,menubar=no,statusbar=no,width=650,height=300'); pathchooser.ifield = ifield; window.ifield = ifield;" + value="{{_("Browse")}}"/> + {% elif option.type == "file" %} + <input name="{{configname}}|{{skey}}|{{okey}}" type="text" + id="{{skey}}|{{okey}}" value="{{option.value}}"/> + <input name="browsebutton" type="button" + onclick="ifield = document.getElementById('{{skey}}|{{okey}}'); filechooser = window.open('{% if option.value %}{{ "/filechooser/" + option.value|quotepath }}{% else %}{{ fileroot }}{% endif %}', 'filechooser', 'scrollbars=yes,toolbar=no,menubar=no,statusbar=no,width=650,height=300'); filechooser.ifield = ifield; window.ifield = ifield;" + value="{{_("Browse")}}"/> + {% else %} + <input id="{{skey}}|{{okey}}" name="{{configname}}|{{skey}}|{{okey}}" + type="text" value="{{option.value}}"/> + {% endif %} + </td> + </tr> + {% endif %} + {% endfor %} + </table> + </div> + {% endfor %} + {% else %} + <!-- Accounts --> + {% for plugin, accounts in config.iteritems() %} + <div class="tabContent" id="{{configname}}{{plugin}}"> + <table class="settable"> + {% for account in accounts %} + <tr> + <td><label for="{{configname}}|{{plugin}}|password;{{account.login}}" + style="color:#424242;">{{ account.login }}:</label></td> + <td> + <input id="{{plugin}}|password;{{account.login}}" + name="{{configname}}|{{plugin}}|password;{{account.login}}" + type="password" value="{{account.password}}" size="14"/> + </td> + <td> + {{ _("Status:") }} + {% if account.valid %} + <span style="font-weight: bold; color: #006400;"> + {{ _("valid") }} + {% else %} + <span style="font-weight: bold; color: #8b0000;"> + {{ _("not valid") }} + {% endif %} + </span> + </td> + <td> + {{ _("Valid until:") }} + <span style="font-weight: bold;"> + {{ account.validuntil }} + </span> + </td> + <td> + {{ _("Traffic left:") }} + <span style="font-weight: bold;"> + {{ account.trafficleft }} + </span> + </td> + <td> + {{ _("Time:") }} + <input id="{{configname}}|{{plugin}}|time;{{account.login}}" + name="{{configname}}|{{plugin}}|time;{{account.login}}" type="text" + size="7" value="{{account.time}}"/> + </td> + <td> + {{ _("Delete? ") }} + <input id="{{configname}}|{{plugin}}|delete;{{account.login}}" + name="{{configname}}|{{plugin}}|delete;{{account.login}}" type="checkbox" + value="True"/> + </td> + </tr> + + {% endfor %} + <tr> + <td> </td> + </tr> + + <tr> + <td><label for="{{configname}}|{{plugin}}" + style="color:#424242;">{{ _("New account:") }}</label></td> + + <td> + <input id="{{plugin}}|newacc" name="{{configname}}|{{plugin}}|newacc" type="text" + size="14"/> + </td> + </tr> + <tr> + <td><label for="{{configname}}|{{plugin}}" + style="color:#424242;">{{ _("New password:") }}</label></td> + + <td> + <input id="{{configname}}|{{plugin}}" name="{{configname}}|{{plugin}}|newpw" + type="password" size="14"/> + </td> + </tr> + + </table> + </div> + {% endfor %} + {% endif %} + {% endfor %} + {% if conf %} + <input class="submit" type="submit" value="{{_("Submit")}}"/> + </form> + + <br> + {% for message in errors %} + <b>{{ message }}</b><br> + {% endfor %} + + {% endif %} + +{% endblock %} diff --git a/module/web/templates/jinja/default/test.html b/module/web/templates/jinja/default/test.html new file mode 100644 index 000000000..b4f17f134 --- /dev/null +++ b/module/web/templates/jinja/default/test.html @@ -0,0 +1,12 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" + "http://www.w3.org/TR/html4/loose.dtd"> +<html> +<head> + <title>Test</title> +</head> +<body> +<h1>Template Test</h1> +{{ user }} +{{ status }} +</body> +</html>
\ No newline at end of file diff --git a/module/web/templates/jinja/default/window.html b/module/web/templates/jinja/default/window.html new file mode 100644 index 000000000..734745887 --- /dev/null +++ b/module/web/templates/jinja/default/window.html @@ -0,0 +1,45 @@ +<iframe id="upload_target" name="upload_target" src="" style="display: none; width:0;height:0"></iframe>
+<div id="add_bg" style="filter:alpha(opacity:80);KHTMLOpacity:0.80;MozOpacity:0.80;opacity:0.80; background:#000; width:100%; height: 100%; position:fixed; top:0; left:0; display:none;"> </div>
+<!--<div id="add_box" style="left:50%; top:200px; margin-left: -450px; width: 900px; position: absolute; background: #FFF; padding: 10px 10px 10px 10px; display:none;">-->
+
+ <!--<div style="width: 900px; text-align: right;"><b onclick="AddBox();">[Close]</b></div>-->
+<div id="add_box" class="myform">
+<form id="add_form" action="/json/add_package" method="POST" enctype="multipart/form-data">
+<h1>{{_("Add Package")}}</h1>
+<p>{{_("Paste your links or upload a container.")}}</p>
+<label for="add_name">{{_("Name")}}
+<span class="small">{{_("The name of the new package.")}}</span>
+</label>
+<input id="add_name" name="add_name" type="text" size="20" />
+
+<label for="add_links">{{_("Links")}}
+<span class="small">{{_("Paste your links here")}}</span>
+</label>
+<textarea rows="5" name="add_links" id="add_links"></textarea>
+
+<label for="add_password">{{_("Password")}}
+ <span class="small">{{_("Password for RAR-Archive")}}</span>
+</label>
+<input id="add_password" name="add_password" type="text" size="20">
+
+<label>{{_("File")}}
+<span class="small">{{_("Upload a container.")}}</span>
+</label>
+<input type="file" name="add_file" id="add_file"/>
+
+<label for="add_dest">{{_("Destination")}}
+</label>
+<span class="cont">
+ {{_("Queue")}}
+ <input type="radio" name="add_dest" id="add_dest" value="1" checked="checked"/>
+ {{_("Collector")}}
+ <input type="radio" name="add_dest" id="add_dest2" value="0"/>
+</span>
+
+<button type="submit">{{_("Add Package")}}</button>
+<button id="add_reset" style="margin-left:0;" type="reset">{{_("Reset")}}</button>
+<div class="spacer"></div>
+
+</form>
+
+</div>
\ No newline at end of file diff --git a/module/web/urls.py b/module/web/urls.py deleted file mode 100644 index 9fe11f925..000000000 --- a/module/web/urls.py +++ /dev/null @@ -1,26 +0,0 @@ -# -*- coding: utf-8 -*- -from django.conf.urls.defaults import * -from django.contrib import admin -from django.conf import settings - - -admin.autodiscover() - -urlpatterns = patterns('', - # Example: - - # Uncomment the admin/doc line below and add 'django.contrib.admindocs' - # to INSTALLED_APPS to enable admin documentation: - # (r'^admin/doc/', include('django.contrib.admindocs.urls')), - - (r'^admin/', include(admin.site.urls)), # django 1.0 not working - (r'^json/', include('ajax.urls')), - (r'^flashgot$', 'cnl.views.flashgot'), - (r'^flash(got)?/?', include('cnl.urls')), - (r'^crossdomain.xml$', 'cnl.views.crossdomain'), - (r'^jdcheck.js', 'cnl.views.jdcheck'), - (r'^favicon\.ico$', 'django.views.generic.simple.redirect_to', {'url': '/media/img/favicon.ico'}), - (r'^media/(?P<path>.*)$', 'django.views.static.serve', - {'document_root': settings.MEDIA_ROOT}), - (r'^', include('pyload.urls')), - ) diff --git a/module/web/utils.py b/module/web/utils.py new file mode 100644 index 000000000..cf3f2d5f3 --- /dev/null +++ b/module/web/utils.py @@ -0,0 +1,86 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, + or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this plrogram; if not, see <http://www.gnu.org/licenses/>. + + @author: RaNaN +""" +from os.path import join, abspath, commonprefix + +from bottle import request, HTTPError, redirect, ServerAdapter + +from webinterface import env, TEMPLATE + +def render_to_response(name, args={}, proc=[]): + for p in proc: + args.update(p()) + + t = env.get_template(join(TEMPLATE, name)) + return t.render(**args) + +def parse_permissions(session): + perms = {"can_change_status": False, + "can_see_dl": False} + + if not session.get("authenticated", False): + return perms + + perms["can_change_status"] = True + perms["can_see_dl"] = True + + return perms + +def parse_userdata(session): + return {"name": session.get("name", "Anonymous"), + "is_staff": True, + "is_authenticated": session.get("authenticated", False)} + +def formatSize(size): + """formats size of bytes""" + size = int(size) + steps = 0 + sizes = ["KB", "MB", "GB", "TB"] + + while size > 1000: + size /= 1024.0 + steps += 1 + + return "%.2f %s" % (size, sizes[steps]) + +def login_required(perm=None): + def _dec(func): + def _view(*args, **kwargs): + s = request.environ.get('beaker.session') + if s.get("name", None) and s.get("authenticated", False): + if perm: + pass + #print perm + return func(*args, **kwargs) + else: + if request.header.get('X-Requested-With') == 'XMLHttpRequest': + return HTTPError(403, "Forbidden") + else: + return redirect("/login") + + return _view + + return _dec + +class CherryPyWSGI(ServerAdapter): + + def run(self, handler): + from wsgiserver import CherryPyWSGIServer + + server = CherryPyWSGIServer((self.host, self.port), handler) + server.start() diff --git a/module/web/webinterface.py b/module/web/webinterface.py new file mode 100644 index 000000000..fe59c57b1 --- /dev/null +++ b/module/web/webinterface.py @@ -0,0 +1,151 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, + or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see <http://www.gnu.org/licenses/>. + + @author: RaNaN +""" + +import sys +import gettext +import sqlite3 + +from os.path import join, abspath,dirname, exists +from os import makedirs + +PROJECT_DIR = abspath(dirname(__file__)) +PYLOAD_DIR = abspath(join(PROJECT_DIR, "..", "..")) + +sys.path.append(PYLOAD_DIR) +sys.path.append(join(PYLOAD_DIR, "module", "lib")) + +from module import InitHomeDir + +import bottle +from bottle import run, app + +from jinja2 import Environment, FileSystemLoader, FileSystemBytecodeCache +from middlewares import StripPathMiddleware, GZipMiddleWare, PrefixMiddleware + +try: + import module.web.ServerThread + + if not module.web.ServerThread.core: + raise Exception + PYLOAD = module.web.ServerThread.core.server_methods + config = module.web.ServerThread.core.config +except: + import xmlrpclib + + ssl = "" + + from module.ConfigParser import ConfigParser + + config = ConfigParser() + + if config.get("ssl", "activated"): + ssl = "s" + + server_url = "http%s://%s:%s@%s:%s/" % ( + ssl, + config.username, + config.password, + config.get("remote", "listenaddr"), + config.get("remote", "port") + ) + + PYLOAD = xmlrpclib.ServerProxy(server_url, allow_none=True) + +from module.JsEngine import JsEngine + +JS = JsEngine() + +TEMPLATE = config.get('webinterface', 'template') +DL_ROOT = join(PYLOAD_DIR, config.get('general', 'download_folder')) +LOG_ROOT = join(PYLOAD_DIR, config.get('log', 'log_folder')) +DEBUG = config.get("general","debug_mode") +bottle.debug(DEBUG) + +def setup_database(): + conn = sqlite3.connect('web.db') + c = conn.cursor() + c.execute( + 'CREATE TABLE IF NOT EXISTS "users" ("id" INTEGER PRIMARY KEY AUTOINCREMENT, "name" TEXT NOT NULL, "email" TEXT DEFAULT "" NOT NULL, "password" TEXT NOT NULL, "role" INTEGER DEFAULT 0 NOT NULL, "permission" INTEGER DEFAULT 0 NOT NULL, "template" TEXT DEFAULT "default" NOT NULL)') + c.close() + conn.commit() + conn.close() + +setup_database() + + +if not exists(join("tmp", "jinja_cache")): + makedirs(join("tmp", "jinja_cache")) + +bcc = FileSystemBytecodeCache(join("tmp","jinja_cache")) +env = Environment(loader=FileSystemLoader(join(PROJECT_DIR, "templates", "jinja")), extensions=['jinja2.ext.i18n'], trim_blocks=True, auto_reload=False, bytecode_cache=bcc) + +from filters import quotepath, path_make_relative, path_make_absolute, truncate,date + +env.filters["quotepath"] = quotepath +env.filters["truncate"] = truncate +env.filters["date"] = date +env.filters["path_make_relative"] = path_make_relative +env.filters["path_make_absolute"] = path_make_absolute + + +translation = gettext.translation("django", join(PROJECT_DIR, "locale"), + languages=["en", config.get("general","language")]) +translation.install(True) +env.install_gettext_translations(translation) + +from beaker.middleware import SessionMiddleware + +session_opts = { + 'session.type': 'file', + 'session.cookie_expires': -1, + 'session.data_dir': './tmp', + 'session.auto': False +} + +web = StripPathMiddleware(SessionMiddleware(app(), session_opts)) +web = PrefixMiddleware(web) +web = GZipMiddleWare(web) + +import pyload_app +import json_app +import cnl_app + + +def run_simple(host="0.0.0.0", port="8000"): + run(app=web, host=host, port=port, quiet=True) + +def run_threaded(host="0.0.0.0", port="8000", theads=3, cert="", key=""): + from wsgiserver import CherryPyWSGIServer + if cert and key: + CherryPyWSGIServer.ssl_certificate = cert + CherryPyWSGIServer.ssl_private_key = key + + CherryPyWSGIServer.numthreads = theads + + from utils import CherryPyWSGI + run(app=web, host=host, port=port, server=CherryPyWSGI, quiet=True) + +def run_fcgi(host="0.0.0.0", port="8000"): + from bottle import FlupFCGIServer + run(app=web, host=host, port=port, server=FlupFCGIServer, quiet=True) + + +if __name__ == "__main__": + + run(app=web, port=8001)
\ No newline at end of file |