diff options
| author | 2013-09-29 13:03:17 +0200 | |
|---|---|---|
| committer | 2013-09-29 13:03:17 +0200 | |
| commit | 6a997661dc5c259f844531382a90a4ca120f1233 (patch) | |
| tree | 085a76d4bac208963649a62f9393e0c0b049e869 | |
| parent | rewritten decrypter and info fetching thread (diff) | |
| download | pyload-6a997661dc5c259f844531382a90a4ca120f1233.tar.xz | |
basics for web setup
29 files changed, 458 insertions, 218 deletions
| diff --git a/pyload/Core.py b/pyload/Core.py index f97cfcf3b..5e083a14e 100644 --- a/pyload/Core.py +++ b/pyload/Core.py @@ -120,21 +120,21 @@ class Core(object):                      elif option in ("-d", "--debug"):                          self.doDebug = True                      elif option in ("-u", "--user"): -                        from Setup import Setup +                        from setup.Setup import Setup                          self.config = ConfigParser()                          s = Setup(pypath, self.config)                          s.set_user()                          exit()                      elif option in ("-s", "--setup"): -                        from Setup import Setup +                        from setup.Setup import Setup                          self.config = ConfigParser()                          s = Setup(pypath, self.config)                          s.start()                          exit()                      elif option == "--changedir": -                        from Setup import Setup +                        from setup.Setup import Setup                          self.config = ConfigParser()                          s = Setup(pypath, self.config) diff --git a/pyload/datatypes/OnlineCheck.py b/pyload/datatypes/OnlineCheck.py index 2797828bf..b0b19cf76 100644 --- a/pyload/datatypes/OnlineCheck.py +++ b/pyload/datatypes/OnlineCheck.py @@ -5,6 +5,7 @@ from time import time  from pyload.Api import OnlineCheck as OC +  class OnlineCheck:      """  Helper class that holds result of an initiated online check """ @@ -16,6 +17,10 @@ class OnlineCheck:          self.timestamp = time() +    def isStale(self, timeout=5): +        """ checks if the data was updated or accessed recently """ +        return self.timestamp + timeout * 60 < time() +      def update(self, result):          self.timestamp = time()          self.result.update(result) diff --git a/pyload/Setup.py b/pyload/setup/Setup.py index d2ec3731f..78afb7fcc 100644 --- a/pyload/Setup.py +++ b/pyload/setup/Setup.py @@ -2,7 +2,7 @@  # -*- coding: utf-8 -*-  ############################################################################### -#   Copyright(c) 2008-2012 pyLoad Team +#   Copyright(c) 2008-2013 pyLoad Team  #   http://www.pyload.org  #  #   This file is part of pyLoad. @@ -16,7 +16,6 @@  #   @author: RaNaN  ############################################################################### -import pyload.utils.pylgettext as gettext  import os  import sys  import socket @@ -26,6 +25,7 @@ from getpass import getpass  from time import time  from sys import exit +from pyload.utils import pylgettext as gettext  from pyload.utils.fs import abspath, dirname, exists, join, makedirs  from pyload.utils import get_console_encoding  from pyload.web.ServerThread import WebServer @@ -48,40 +48,37 @@ class Setup():          self.yes = "yes"          self.no = "no" -      def start(self): +        import __builtin__ +        # set the gettext translation +        __builtin__._ = lambda x: x +          web = WebServer(pysetup=self)          web.start()          error = web.check_error() + +        # TODO: start cli in this case          if error: #todo errno 44 port already in use              print error          url = "http://%s:%d/" % (socket.gethostbyname(socket.gethostname()), web.port) -        print "Setup is started" +        print "Setup is running at %s" % url          opened = webbrowser.open_new_tab(url)          if not opened: -            print "Please point your browser to %s" % url - - -        self.ask_lang() +            print "Please point your browser to the url above." -        print "" -        print _("Would you like to configure pyLoad via Webinterface?") -        print _("You need a Browser and a connection to this PC for it.") -        print _("Url would be: http://hostname:8000/") -        viaweb = self.ask(_("Start initial webinterface for configuration?"), self.yes, bool=True) -        if viaweb: -            self.start_web() -        else: +        cli = self.ask("Use commandline for configuration instead?", self.no, bool=True) +        if cli:              self.start_cli() - - +        else: +            raw_input()      def start_cli(self): +        self.ask_lang()          print _("Welcome to the pyLoad Configuration Assistent.")          print _("It will check your system and make a basic setup in order to run pyLoad.") @@ -168,23 +165,6 @@ class Setup():          return True -    def start_web(self): -        print "" -        print _("Webinterface running for setup.") -        # TODO start browser? -        try: -            from pyload.web import ServerThread -            ServerThread.setup = self -            from pyload.web import webinterface -            webinterface.run_simple() -            self.web = True -            return True -        except Exception, e: -            print "Webinterface failed with this error: ", e -            print "Falling back to commandline setup." -            self.start_cli() - -      def conf_basic(self):          print ""          print _("## Basic Setup ##") diff --git a/pyload/setup/System_Checks.py b/pyload/setup/System_Checks.py deleted file mode 100644 index cef46956b..000000000 --- a/pyload/setup/System_Checks.py +++ /dev/null @@ -1,126 +0,0 @@ -#!/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 getpass import getpass -import module.common.pylgettext as gettext -import os -from os import makedirs -from os.path import abspath, dirname, exists, join -from subprocess import PIPE, call -import sys -from sys import exit -from module.utils import get_console_encoding - -class System_Checks(): -    def __init__(self): -        self.result = "" - -    def print_str(self, text, translate = True): -        if translate: -            self.result += _(text) + "\n" -        else: -            self.result += text + "\n" - -    def print_dep(self, name, value): -        """Print Status of dependency""" -        if value: -            self.print_str(name + ": OK", False) -        else: -            self.print_str(name + ": missing", False) - -    def check_basic(self): -        self.result = "" #clear result -        python = False -        if sys.version_info[:2] > (2, 7): -            self.print_str("Your python version is to new, Please use Python 2.6/2.7") -        elif sys.version_info[:2] < (2, 5): -            self.print_str("Your python version is to old, Please use at least Python 2.5") -        else: -            self.print_str("Python Version: OK") -            python = True - -        curl = self.check_module("pycurl") -        self.print_dep("pycurl", curl) - -        sqlite = self.check_module("sqlite3") -        self.print_dep("sqlite3", sqlite) - -        beaker = self.check_module("beaker") -        self.print_dep("beaker", beaker) - -        jinja = True -        try: -            import jinja2 -            v = jinja2.__version__ -            if v and "unknown" not in v: -                if not v.startswith("2.5") and not v.startswith("2.6"): -                    self.print_str("Your installed jinja2 version %s seems too old.") % jinja2.__version__ -                    self.print_str("You can safely continue but if the webinterface is not working,") -                    self.print_str("please upgrade or deinstall it, pyLoad includes a sufficient jinja2 library.") -                    jinja = False -        except: -            pass -        self.print_dep("jinja2", jinja) -         -        return self.result, (python and curl and sqlite and (beaker or jinja)) - -    def check_ssl(self): -        self.result = "" #clear result -        ssl = self.check_module("OpenSSL") -        self.print_dep("py-OpenSSL", ssl) -        return self.result, ssl - -    def check_crypto(self): -        self.result = "" #clear result -        crypto = self.check_module("Crypto") -        self.print_dep("pycrypto", crypto) -        return self.result, crypto - -    def check_captcha(self): -        self.result = "" #clear result -        pil = self.check_module("Image") -        self.print_dep("py-imaging", pil) -        if os.name == "nt": -            tesser = self.check_prog([join(pypath, "tesseract", "tesseract.exe"), "-v"]) -        else: -            tesser = self.check_prog(["tesseract", "-v"]) -        self.print_dep("tesseract", tesser) -        return self.result, pil and tesser - -    def check_js(self): -        self.result = "" #clear result -        from module.common import JsEngine -        js = True if JsEngine.ENGINE else False -        self.print_dep(_("JS engine"), js) -        return self.result, pil and tesser - -    def check_module(self, module): -        try: -            __import__(module) -            return True -        except: -            return False - -    def check_prog(self, command): -        pipe = PIPE -        try: -            call(command, stdout=pipe, stderr=pipe) -            return True -        except: -            return False -         diff --git a/pyload/setup/__init__.py b/pyload/setup/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/pyload/setup/__init__.py diff --git a/pyload/setup/dependencies.py b/pyload/setup/dependencies.py new file mode 100644 index 000000000..53457de93 --- /dev/null +++ b/pyload/setup/dependencies.py @@ -0,0 +1,84 @@ +# -*- coding: utf-8 -*- + +# Provide gettext marker +_ = lambda x: x + + +def find_module(name): +    from imp import find_module + +    try: +        f, pathname, desc = find_module(name) +        if f is not None: +            f.close() +        return True +    except: +        return False + + +class Dependency(object): +    name = None +    optional = True +    desc = None + +    @classmethod +    def check(cls): +        """  Returns (availability, version) as tuple """ +        inst = cls() +        avail = inst.isStatisfied() +        v = None +        if avail: +            v = inst.getVersion() + +        return avail, v + +    def isStatisfied(self): +        raise NotImplementedError + +    def getVersion(self): +        return None + + +class Python(Dependency): +    name = "Python" +    optional = False + +    def isStatisfied(self): +        # obviously +        return True + +    def getVersion(self): +        import sys + +        ".".join(str(v) for v in sys.version_info[:3]) + + +class JSON(Dependency): +    name = "json" +    optional = False + +    def isStatisfied(self): +        # TODO +        return True + + +class PyCurl(Dependency): +    name = "pycurl" +    optional = False + +    def isStatisfied(self): +        # TODO +        return True + + +class Sqlite(Dependency): +    name = "sqlite" +    optional = False + +    def isStatisfied(self): +        # TODO +        return True + +# TODO: ssl, crypto, image, tesseract, js + +deps = [x for x in locals().itervalues() if issubclass(x, Dependency) and x is not Dependency]
\ No newline at end of file diff --git a/pyload/setup/system.py b/pyload/setup/system.py new file mode 100644 index 000000000..6e7039331 --- /dev/null +++ b/pyload/setup/system.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- + +import sys +import os + +# gettext decorator, translated only when needed +_ = lambda x: x + +# platform usually don't change at runtime +info = None + + +def get_system_info(): +    """ Returns system information as dict """ +    global info + +    if info is None: +        import platform + +        info = { +            _("Platform"): platform.platform(), +            _("Version"): sys.version, +            _("Path"): os.path.abspath(""), +            _("Encoding"): sys.getdefaultencoding(), +            _("FS-Encoding"): sys.getfilesystemencoding() +        } + +    return info
\ No newline at end of file diff --git a/pyload/threads/BaseThread.py b/pyload/threads/BaseThread.py index b7912e924..3655480dd 100644 --- a/pyload/threads/BaseThread.py +++ b/pyload/threads/BaseThread.py @@ -10,6 +10,7 @@ from traceback import format_exc  from pyload.utils import primary_uid  from pyload.utils.fs import listdir, join, save_join, stat, exists +from pyload.setup.system import get_system_info  class BaseThread(Thread): @@ -137,9 +138,7 @@ class BaseThread(Thread):      def getSystemDump(self):          dump = "SYSTEM:\n\n" -        dump += """Platform: %s -Version: %s -Encoding: %s -FS-Encoding: %s -        """ % (sys.platform, sys.version, sys.getdefaultencoding(), sys.getfilesystemencoding()) +        for k,v in get_system_info().iteritems(): +            dump += "%s: %s\n" % (k, v) +          return dump diff --git a/pyload/threads/ThreadManager.py b/pyload/threads/ThreadManager.py index ff8bfe8d7..07b0cd6e9 100644 --- a/pyload/threads/ThreadManager.py +++ b/pyload/threads/ThreadManager.py @@ -167,11 +167,14 @@ class ThreadManager:              self.assignJob()              #it may be failed non critical so we try it again -        if (self.infoCache or self.infoResults) and self.timestamp < time(): +        if self.infoCache and self.timestamp < time():              self.infoCache.clear() -            self.infoResults.clear()              self.log.debug("Cleared Result cache") +        for rid in self.infoResults.keys(): +            if self.infoResults[rid].isStale(): +                del self.infoResults[rid] +      def tryReconnect(self):          """checks if reconnect needed""" diff --git a/pyload/utils/JsEngine.py b/pyload/utils/JsEngine.py index ef7494d16..3318ffb2a 100644 --- a/pyload/utils/JsEngine.py +++ b/pyload/utils/JsEngine.py @@ -48,7 +48,7 @@ if not ENGINE:  if not ENGINE or DEBUG:      try: -        find_module("PyV8") +        import PyV8          ENGINE = "pyv8"          PYV8 = True      except: diff --git a/pyload/utils/fs.py b/pyload/utils/fs.py index 92cc605e7..52bf0bd51 100644 --- a/pyload/utils/fs.py +++ b/pyload/utils/fs.py @@ -44,7 +44,6 @@ def exists(path):  def makedirs(path, mode=0755):      return os.makedirs(fs_encode(path), mode) -# fs_decode?  def listdir(path):      return [fs_decode(x) for x in os.listdir(fs_encode(path))] diff --git a/pyload/web/api_app.py b/pyload/web/api_app.py index d0a41b4d0..9370e671f 100644 --- a/pyload/web/api_app.py +++ b/pyload/web/api_app.py @@ -6,7 +6,7 @@ from traceback import format_exc, print_exc  from bottle import route, request, response, HTTPError, parse_auth -from utils import set_session, get_user_api +from utils import set_session, get_user_api, add_json_header  from webinterface import PYLOAD, session  from pyload.Api import ExceptionObject @@ -14,12 +14,6 @@ from pyload.remote.json_converter import loads, dumps, BaseEncoder  from pyload.utils import remove_chars -def add_header(r): -    r.headers.replace("Content-type", "application/json") -    r.headers.append("Cache-Control", "no-cache, must-revalidate") -    r.headers.append("Access-Control-Allow-Origin", request.get_header('Origin', '*')) -    r.headers.append("Access-Control-Allow-Credentials", "true") -  # returns http error  def error(code, msg):      return HTTPError(code, dumps(msg), **dict(response.headers)) @@ -29,7 +23,7 @@ def error(code, msg):  @route("/api/<func><args:re:[^#?]*>")  @route("/api/<func><args:re:[^#?]*>", method="POST")  def call_api(func, args=""): -    add_header(response) +    add_json_header(response)      s = request.environ.get('beaker.session')      # Accepts standard http auth @@ -96,7 +90,7 @@ def call_api(func, args=""):  @route("/api/login")  @route("/api/login", method="POST")  def login(): -    add_header(response) +    add_json_header(response)      username = request.params.get("username")      password = request.params.get("password") @@ -128,7 +122,7 @@ def login():  @route("/api/logout")  @route("/api/logout", method="POST")  def logout(): -    add_header(response) +    add_json_header(response)      s = request.environ.get('beaker.session')      s.delete() diff --git a/pyload/web/app/index.html b/pyload/web/app/index.html index 4a4195b13..bf75d40ed 100644 --- a/pyload/web/app/index.html +++ b/pyload/web/app/index.html @@ -41,6 +41,7 @@          // TODO          window.pathPrefix = '/';          window.wsAddress = configValue('{{ws}}', 'ws://%s:7227'); +        window.setup = configValue('{{setup}}', 'false');          require(['config'], function(Config) {              require(['default'], function(App) { diff --git a/pyload/web/app/scripts/default.js b/pyload/web/app/scripts/default.js index 428ec2b28..91b46715e 100644 --- a/pyload/web/app/scripts/default.js +++ b/pyload/web/app/scripts/default.js @@ -1,5 +1,5 @@ -define('default', ['backbone', 'jquery', 'app', 'router', 'models/UserSession'], -    function(Backbone, $, App, Router, UserSession) { +define('default', ['require', 'backbone', 'jquery', 'app', 'router', 'models/UserSession'], +    function(require, Backbone, $, App, Router, UserSession) {          'use strict';          // Global ajax options @@ -21,9 +21,17 @@ define('default', ['backbone', 'jquery', 'app', 'router', 'models/UserSession'],          };          $(function() { -            App.user = new UserSession(); -            App.router = new Router(); -            App.start(); +            // load setup async +            if (window.setup === 'true') { +                require(['setup'], function(SetupRouter) { +                    App.router = new SetupRouter(); +                    App.start(); +                }); +            } else { +                App.user = new UserSession(); +                App.router = new Router(); +                App.start(); +            }          });          return App; diff --git a/pyload/web/app/scripts/helpers/ifEq.js b/pyload/web/app/scripts/helpers/ifEq.js new file mode 100644 index 000000000..1c8a71b61 --- /dev/null +++ b/pyload/web/app/scripts/helpers/ifEq.js @@ -0,0 +1,14 @@ +define('helpers/ifEq', ['underscore', 'handlebars'], +    function(_, Handlebars) { +        /*jshint validthis:true */ +        'use strict'; +        function ifEq(v1, v2, options) { +            if (v1 === v2) { +                return options.fn(this); +            } +            return options.inverse(this); +        } + +        Handlebars.registerHelper('ifEq', ifEq); +        return ifEq; +    }); diff --git a/pyload/web/app/scripts/models/Setup.js b/pyload/web/app/scripts/models/Setup.js new file mode 100644 index 000000000..82a2978db --- /dev/null +++ b/pyload/web/app/scripts/models/Setup.js @@ -0,0 +1,14 @@ +define(['jquery', 'backbone', 'underscore', 'app', 'utils/apitypes'], +    function($, Backbone, _, App, Api) { +        'use strict'; + +        return Backbone.Model.extend({ + +            defaults: { +                lang: 'en', +                user: null, +                password: null +            } + +        }); +    });
\ No newline at end of file diff --git a/pyload/web/app/scripts/setup.js b/pyload/web/app/scripts/setup.js new file mode 100644 index 000000000..94d370078 --- /dev/null +++ b/pyload/web/app/scripts/setup.js @@ -0,0 +1,33 @@ +/** + * Router and controller used in setup mode + */ +define([ +    // Libraries +    'backbone', +    'marionette', +    'app', + +    // Views +    'views/setup/setupView' +], +    function(Backbone, Marionette, App, SetupView) { +        'use strict'; + +        return Backbone.Marionette.AppRouter.extend({ + +            appRoutes: { +                '': 'setup' +            }, + +            controller: { + +                setup: function() { + +                    var view = new SetupView(); +                    App.actionbar.show(new view.actionbar()); +                    App.content.show(view); +                } + +            } +        }); +    }); diff --git a/pyload/web/app/scripts/views/setup/setupView.js b/pyload/web/app/scripts/views/setup/setupView.js new file mode 100644 index 000000000..7636a0bc2 --- /dev/null +++ b/pyload/web/app/scripts/views/setup/setupView.js @@ -0,0 +1,89 @@ +define(['jquery', 'backbone', 'underscore', 'app', 'models/Setup', 'hbs!tpl/setup/layout', 'hbs!tpl/setup/actionbar', +    './welcomeView', './systemView'], +    function($, Backbone, _, App, Setup, template, templateBar, welcomeView, systemView) { +        'use strict'; + +        return Backbone.Marionette.ItemView.extend({ +            template: template, + +            events: { +            }, + +            ui: { +                page: '.setup-page' +            }, + +            pages: [ +                welcomeView, +                systemView +            ], + +            page: 0, +            view: null, + +            initialize: function() { +                var self = this; +                this.model = new Setup(); + +                this.actionbar = Backbone.Marionette.ItemView.extend({ +                    template: templateBar, +                    view: this, +                    events: { +                        'click .select-page': 'selectPage' +                    }, + +                    initialize: function() { +                        this.listenTo(self.model, 'page:changed', this.render); +                    }, + +                    serializeData: function() { +                        return { +                            page: this.view.page, +                            max: this.view.pages.length - 1, +                            pages: _.map(this.view.pages, function(p) { +                                return p.prototype.name; +                            }) +                        }; +                    }, + +                    selectPage: function(e) { +                        this.view.openPage(parseInt($(e.target).data('page'), 10)); +                    } + +                }); +                this.listenTo(this.model, 'page:next', function() { +                    self.openPage(self.page++); +                }); +                this.listenTo(this.model, 'page:prev', function() { +                    self.openPage(self.page--); +                }); +            }, + +            openPage: function(page) { +                console.log('Change page', page); +                // check if number is reasonable +                if (!_.isNumber(page) || !_.isFinite(page)) +                    return; + +                if (page === this.page) +                    return; + +                this.page = page; +                this.onRender(); + +                this.model.trigger('page:changed', page); +            }, + +            onRender: function() { +                // close old opened view +                if (this.view) +                    this.view.close(); + +                // TODO: animation +                this.view = new this.pages[this.page]({model: this.model}); +                this.ui.page.empty(); +                this.ui.page.append(this.view.render().$el); +            } + +        }); +    });
\ No newline at end of file diff --git a/pyload/web/app/scripts/views/setup/systemView.js b/pyload/web/app/scripts/views/setup/systemView.js new file mode 100644 index 000000000..11e50213d --- /dev/null +++ b/pyload/web/app/scripts/views/setup/systemView.js @@ -0,0 +1,20 @@ +define(['jquery', 'backbone', 'underscore', 'app', 'hbs!tpl/setup/system'], +    function($, Backbone, _, App, template) { +        'use strict'; + +        return Backbone.Marionette.ItemView.extend({ + +            name: 'System', +            template: template, + +            events: { +            }, + +            ui: { +            }, + +            onRender: function() { +            } + +        }); +    });
\ No newline at end of file diff --git a/pyload/web/app/scripts/views/setup/welcomeView.js b/pyload/web/app/scripts/views/setup/welcomeView.js new file mode 100644 index 000000000..4affc9075 --- /dev/null +++ b/pyload/web/app/scripts/views/setup/welcomeView.js @@ -0,0 +1,20 @@ +define(['jquery', 'backbone', 'underscore', 'app', 'hbs!tpl/setup/welcome'], +    function($, Backbone, _, App, template) { +        'use strict'; + +        return Backbone.Marionette.ItemView.extend({ + +            name: 'Language', +            template: template, + +            events: { +            }, + +            ui: { +            }, + +            onRender: function() { +            } + +        }); +    });
\ No newline at end of file diff --git a/pyload/web/app/styles/default/style.less b/pyload/web/app/styles/default/style.less index b24e5ff21..da0e68991 100644 --- a/pyload/web/app/styles/default/style.less +++ b/pyload/web/app/styles/default/style.less @@ -231,15 +231,13 @@ header { //  background-color: @greyDark;  }  .actionbar { +  padding-top: 2px;    padding-bottom: 3px; -  margin-bottom: 0; +  margin-bottom: 5px;    border-bottom: 1px dashed @grey;    height: @actionbar-height; -  padding-top: 2px; -  margin-bottom: 5px; -    & > li > a, & > li > button {      margin-top: 4px;    } @@ -259,6 +257,10 @@ header { //  background-color: @greyDark;      margin-bottom: 0;    } +  select { +    margin: 2px 0 0; +  } +    input, button {      padding-top: 2px;      padding-bottom: 2px; diff --git a/pyload/web/app/templates/default/setup.html b/pyload/web/app/templates/default/setup.html deleted file mode 100644 index e5c9f4b8c..000000000 --- a/pyload/web/app/templates/default/setup.html +++ /dev/null @@ -1,16 +0,0 @@ -{% extends 'default/base.html' %} -{% block title %} -    {{_("Setup")}} - {{ super()}} -{% endblock %} - -{% block content %} -    <div class="hero-unit"> -        <h1>You did it!</h1> -        <p>pyLoad is running and ready for configuration.</p> -        <p> -            <a class="btn btn-primary btn-large"> -                Go on -            </a> -        </p> -    </div> -{% endblock %}
\ No newline at end of file diff --git a/pyload/web/app/templates/default/setup/actionbar.html b/pyload/web/app/templates/default/setup/actionbar.html new file mode 100644 index 000000000..b109025b7 --- /dev/null +++ b/pyload/web/app/templates/default/setup/actionbar.html @@ -0,0 +1,24 @@ +<ul class="actionbar nav span8 offset3"> +    <li class="pull-left"> +        <ul class="breadcrumb"> +            {{#each pages}} +            <li> +                <a href="#" class="{{#ifEq ../page @index}}active {{/ifEq}}select-page" +                   data-page="{{@index}}">{{this}}</a> +                {{#ifEq ../max @index}} + +                {{else}} +                    <span class="divider"> +                        <i class="icon-long-arrow-right"></i> +                    </span> +                {{/ifEq}} +            </li> +            {{/each}} +        </ul> +    </li> +    <li class="pull-right"> +        <select> +            <option>en</option> +        </select> +    </li> +</ul>
\ No newline at end of file diff --git a/pyload/web/app/templates/default/setup/layout.html b/pyload/web/app/templates/default/setup/layout.html new file mode 100644 index 000000000..7b75e53b1 --- /dev/null +++ b/pyload/web/app/templates/default/setup/layout.html @@ -0,0 +1,7 @@ +<div class="span3"> +    <h1 class="vertical-header"> +        {{ _ "Setup" }} +    </h1> +</div> +<div class="span8 setup-page"> +</div>
\ No newline at end of file diff --git a/pyload/web/app/templates/default/setup/system.html b/pyload/web/app/templates/default/setup/system.html new file mode 100644 index 000000000..84a217b19 --- /dev/null +++ b/pyload/web/app/templates/default/setup/system.html @@ -0,0 +1,5 @@ +<h1>{{ _ "System" }} </h1> + +<h2>{{_ "Dependencies" }}</h2> + +<h2>{{ _ "Optional" }}</h2>
\ No newline at end of file diff --git a/pyload/web/app/templates/default/setup/welcome.html b/pyload/web/app/templates/default/setup/welcome.html new file mode 100644 index 000000000..f5c5af4d7 --- /dev/null +++ b/pyload/web/app/templates/default/setup/welcome.html @@ -0,0 +1,16 @@ +<div class="hero-unit"> +    <h1>{{ _ "Welcome!" }}</h1> + +    <p>{{ _ "pyLoad is running and ready for configuration." }}</p> + +    <p> +        {{ _ "Select your language:" }} +        <select> +            <option>en</option> +        </select> +    </p> + +    <button class="btn btn-large btn-blue"> +       {{ _ "Start configuration" }} +    </button> +</div>
\ No newline at end of file diff --git a/pyload/web/pyload_app.py b/pyload/web/pyload_app.py index b182816c2..1ec7cf4c9 100644 --- a/pyload/web/pyload_app.py +++ b/pyload/web/pyload_app.py @@ -23,7 +23,9 @@ from bottle import route, static_file, response, request, redirect, template  from webinterface import PYLOAD, PROJECT_DIR, SETUP, APP_PATH, UNAVAILALBE -from utils import login_required +from utils import login_required, add_json_header, select_language + +from pyload.utils import json_dumps  APP_ROOT = join(PROJECT_DIR, APP_PATH) @@ -45,27 +47,42 @@ def download(fid, api):      return static_file(name, path, download=True) +@route("/i18n") +@route("/i18n/:lang") +def i18n(lang=None): +    add_json_header(response) + +    if lang is None: +        pass +        # TODO use lang from PYLOAD.config or setup +    else: +        # TODO auto choose language +        lang = select_language(["en"]) + +    return json_dumps({}) +  @route('/')  def index():      if UNAVAILALBE: -        return server_static("unavailable.html") - -    if SETUP: -        # TODO show different page -        pass +        return serve_static("unavailable.html") -    resp = server_static('index.html') +    resp = serve_static('index.html') +    # set variable depending on setup mode +    setup = 'false' if SETUP is None else 'true' +    ws = PYLOAD.getWSAddress() if PYLOAD else False +    web = PYLOAD.getConfigValue('webinterface', 'port') if PYLOAD else False      # Render variables into the html page      if resp.status_code == 200:          content = resp.body.read() -        resp.body = template(content, ws=PYLOAD.getWSAddress(), web=PYLOAD.getConfigValue('webinterface', 'port')) +        resp.body = template(content, ws=ws, web=web, setup=setup) +        resp.content_length = len(resp.body)      return resp  # Very last route that is registered, could match all uris  @route('/<path:path>') -def server_static(path): +def serve_static(path):      response.headers['Expires'] = time.strftime("%a, %d %b %Y %H:%M:%S GMT",                                                  time.gmtime(time.time() + 60 * 60 * 24 * 7))      response.headers['Cache-control'] = "public" @@ -76,7 +93,7 @@ def server_static(path):      # gzipped and clients accepts it      # TODO: index.html is not gzipped, because of template processing -    if GZIPPED[path] and "gzip" in  request.get_header("Accept-Encoding", "") and path != "index.html": +    if GZIPPED[path] and "gzip" in request.get_header("Accept-Encoding", "") and path != "index.html":          response.headers['Vary'] = 'Accept-Encoding'          response.headers['Content-Encoding'] = 'gzip'          path += ".gz" diff --git a/pyload/web/setup_app.py b/pyload/web/setup_app.py index cd44ad08e..5163f9cc6 100644 --- a/pyload/web/setup_app.py +++ b/pyload/web/setup_app.py @@ -1,10 +1,14 @@  #!/usr/bin/env python  # -*- coding: utf-8 -*- +from time import time +  from bottle import route, request, response, HTTPError, redirect  from webinterface import PROJECT_DIR, SETUP +from utils import add_json_header +  def setup_required(func):      def _view(*args, **kwargs):          # setup needs to be running @@ -14,6 +18,9 @@ def setup_required(func):          return func(*args, **kwargs)      return _view +# setup will close after inactivity +TIMEOUT = 15 +timestamp = time()  @route("/setup")  @setup_required diff --git a/pyload/web/utils.py b/pyload/web/utils.py index dae987f84..e94089185 100644 --- a/pyload/web/utils.py +++ b/pyload/web/utils.py @@ -6,6 +6,11 @@ from bottle import request, HTTPError, redirect  from webinterface import PYLOAD, SETUP +def add_json_header(r): +    r.headers.replace("Content-type", "application/json") +    r.headers.append("Cache-Control", "no-cache, must-revalidate") +    r.headers.append("Access-Control-Allow-Origin", request.get_header('Origin', '*')) +    r.headers.append("Access-Control-Allow-Credentials", "true")  def set_session(request, user):      s = request.environ.get('beaker.session') @@ -53,6 +58,14 @@ def is_mobile():          return True      return False +def select_language(langs): + +    accept = request.headers.get('Accept-Language', '') +    # TODO + +    return langs[0] + +  def login_required(perm=None):      def _dec(func): | 
