diff options
Diffstat (limited to 'pyload')
20 files changed, 413 insertions, 105 deletions
diff --git a/pyload/Core.py b/pyload/Core.py index 5e083a14e..4f20ae33e 100644 --- a/pyload/Core.py +++ b/pyload/Core.py @@ -277,10 +277,8 @@ class Core(object):          self.version = CURRENT_VERSION -        # TODO: Re-enable when its working again -        # TODO: Don't forget it -        if False and not exists("pyload.conf") and not tests: -            from Setup import Setup +        if not exists("pyload.conf") and not tests: +            from setup.Setup import Setup              print "This is your first start, running configuration assistant now."              self.config = ConfigParser() diff --git a/pyload/setup/Setup.py b/pyload/setup/Setup.py index 78afb7fcc..cea960885 100644 --- a/pyload/setup/Setup.py +++ b/pyload/setup/Setup.py @@ -30,17 +30,48 @@ from pyload.utils.fs import abspath, dirname, exists, join, makedirs  from pyload.utils import get_console_encoding  from pyload.web.ServerThread import WebServer +from system import get_system_info +from dependencies import deps  class Setup():      """      pyLoads initial setup configuration assistant      """ +    @staticmethod +    def check_system(): +        return get_system_info() + + +    @staticmethod +    def check_deps(): +        result = { +            "core": [], +            "opt": [] +        } + +        for d in deps: +            avail, v = d.check() +            check = { +                "name": d.name, +                "avail": avail, +                "v": v +            } +            if d.optional: +                result["opt"].append(check) +            else: +                result["core"].append(check) + +        return result + +      def __init__(self, path, config):          self.path = path          self.config = config          self.stdin_encoding = get_console_encoding(sys.stdin.encoding)          self.lang = None +        self.db = None +          # We will create a timestamp so that the setup will be completed in a specific interval          self.timestamp = time() @@ -72,9 +103,13 @@ class Setup():          cli = self.ask("Use commandline for configuration instead?", self.no, bool=True)          if cli: -            self.start_cli() -        else: -            raw_input() +            print "Not implemented yet!" +            print "Use web configuration or config files" + +        raw_input() + +        return True +      def start_cli(self): @@ -93,34 +128,8 @@ class Setup():          print _("When you are ready for system check, hit enter.")          raw_input() -        #self.get_page_next() - - -        if len(avail) < 5: -            print _("Features missing: ") -            print - -            if not self.check_module("Crypto"): -                print _("no py-crypto available") -                print _("You need this if you want to decrypt container files.") -                print "" - -            if not ssl: -                print _("no SSL available") -                print _("This is needed if you want to establish a secure connection to core or webinterface.") -                print _("If you only want to access locally to pyLoad ssl is not useful.") -                print "" - -            if not captcha: -                print _("no Captcha Recognition available") -                print _("Only needed for some hosters and as freeuser.") -                print "" -            if not js: -                print _("no JavaScript engine found") -                print _("You will need this for some Click'N'Load links. Install Spidermonkey, ossp-js, pyv8 or rhino") - -            print _("You can abort the setup now and fix some dependencies if you want.") +        # TODO: new system check + deps          con = self.ask(_("Continue with setup?"), self.yes, bool=True) @@ -151,12 +160,11 @@ class Setup():              if ssl:                  self.conf_ssl() +        print "" +        print _("Do you want to configure webinterface?") +        web = self.ask(_("Configure webinterface?"), self.yes, bool=True)          if web: -            print "" -            print _("Do you want to configure webinterface?") -            web = self.ask(_("Configure webinterface?"), self.yes, bool=True) -            if web: -                self.conf_web() +            self.conf_web()          print ""          print _("Setup finished successfully.") @@ -182,18 +190,11 @@ class Setup():          db.shutdown()          print "" -        print _("External clients (GUI, CLI or other) need remote access to work over the network.") -        print _("However, if you only want to use the webinterface you may disable it to save ram.") -        self.config["remote"]["activated"] = self.ask(_("Enable remote access"), self.yes, bool=True) - -        print ""          langs = self.config.getMetaData("general", "language")          self.config["general"]["language"] = self.ask(_("Language"), "en", langs.type.split(";"))          self.config["general"]["download_folder"] = self.ask(_("Download folder"), "Downloads")          self.config["download"]["max_downloads"] = self.ask(_("Max parallel downloads"), "3") -        #print _("You should disable checksum proofing, if you have low hardware requirements.") -        #self.config["general"]["checksum"] = self.ask(_("Proof checksum?"), "y", bool=True)          reconnect = self.ask(_("Use Reconnect?"), self.no, bool=True)          self.config["reconnect"]["activated"] = reconnect @@ -247,12 +248,8 @@ class Setup():              languages=[self.config["general"]["language"], "en"], fallback=True)          translation.install(True) -        from pyload.database import DatabaseBackend - -        db = DatabaseBackend(None) -        db.setup() +        self.openDB() -        noaction = True          try:              while True:                  print _("Select action") @@ -267,14 +264,12 @@ class Setup():                      print ""                      username = self.ask(_("Username"), "User")                      password = self.ask("", "", password=True) -                    db.addUser(username, password) -                    noaction = False +                    self.db.addUser(username, password)                  elif action == "2":                      print ""                      print _("Users")                      print "-----" -                    users = db.getAllUserData() -                    noaction = False +                    users = self.db.getAllUserData()                      for user in users.itervalues():                          print user.name                      print "-----" @@ -283,14 +278,31 @@ class Setup():                      print ""                      username = self.ask(_("Username"), "")                      if username: -                        db.removeUserByName(username) -                        noaction = False +                        self.db.removeUserByName(username)                  elif action == "4": -                    db.syncSave() +                    self.db.syncSave()                      break          finally: -            if not noaction: -                db.shutdown() +            self.closeDB() + +    def addUser(self, username, password): +        self.openDB() +        try: +            self.db.addUser(username, password) +        finally: +            self.closeDB() + +    def openDB(self): +        from pyload.database import DatabaseBackend + +        if self.db is None: +            self.db = DatabaseBackend(None) +            self.db.setup() + +    def closeDB(self): +        if self.db is not None: +            self.db.syncSave() +            self.db.shutdown()      def conf_path(self, trans=False):          if trans: diff --git a/pyload/setup/dependencies.py b/pyload/setup/dependencies.py index 53457de93..f7a0e4ae7 100644 --- a/pyload/setup/dependencies.py +++ b/pyload/setup/dependencies.py @@ -1,5 +1,7 @@  # -*- coding: utf-8 -*- +import inspect +  # Provide gettext marker  _ = lambda x: x @@ -50,7 +52,7 @@ class Python(Dependency):      def getVersion(self):          import sys -        ".".join(str(v) for v in sys.version_info[:3]) +        return ".".join(str(v) for v in sys.version_info[:3])  class JSON(Dependency): @@ -58,8 +60,7 @@ class JSON(Dependency):      optional = False      def isStatisfied(self): -        # TODO -        return True +        return find_module("json") or find_module("simplejson")  class PyCurl(Dependency): @@ -67,8 +68,7 @@ class PyCurl(Dependency):      optional = False      def isStatisfied(self): -        # TODO -        return True +        return find_module("pycurl")  class Sqlite(Dependency): @@ -76,9 +76,8 @@ class Sqlite(Dependency):      optional = False      def isStatisfied(self): -        # TODO -        return True +        return find_module("sqlite3") or find_module("pysqlite2")  # 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 +deps = [Python, Sqlite, PyCurl, JSON]
\ No newline at end of file diff --git a/pyload/setup/system.py b/pyload/setup/system.py index 6e7039331..dab6d1d17 100644 --- a/pyload/setup/system.py +++ b/pyload/setup/system.py @@ -3,6 +3,8 @@  import sys  import os +from new_collections import OrderedDict +  # gettext decorator, translated only when needed  _ = lambda x: x @@ -17,12 +19,12 @@ def get_system_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() -        } +        info = OrderedDict([ +            (_("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/web/app/scripts/models/Setup.js b/pyload/web/app/scripts/models/Setup.js index 82a2978db..424edf452 100644 --- a/pyload/web/app/scripts/models/Setup.js +++ b/pyload/web/app/scripts/models/Setup.js @@ -4,10 +4,30 @@ define(['jquery', 'backbone', 'underscore', 'app', 'utils/apitypes'],          return Backbone.Model.extend({ +            url: App.apiUrl('setup'),              defaults: {                  lang: 'en', +                system: null, +                deps: null,                  user: null,                  password: null +            }, + +            fetch: function(options) { +                options || (options = {}); +                options.url = App.apiUrl('setup'); +                return Backbone.Model.prototype.fetch.call(this, options); +            }, + +            // will get a 409 on success +            submit: function(options) { +                options || (options = {}); +                options.url = App.apiUrl('setup_done'); +                options.data = { +                    user: this.get('user'), +                    password: this.get('password') +                }; +                return Backbone.Model.prototype.fetch.call(this, options);              }          }); diff --git a/pyload/web/app/scripts/views/setup/finishedView.js b/pyload/web/app/scripts/views/setup/finishedView.js new file mode 100644 index 000000000..9f0f8db19 --- /dev/null +++ b/pyload/web/app/scripts/views/setup/finishedView.js @@ -0,0 +1,25 @@ +define(['jquery', 'backbone', 'underscore', 'app', 'hbs!tpl/setup/finished'], +    function($, Backbone, _, App, template) { +        'use strict'; + +        return Backbone.Marionette.ItemView.extend({ + +            name: 'Finished', +            template: template, + +            events: { +                'click .btn-blue': 'confirm' +            }, + +            ui: { +            }, + +            onRender: function() { +            }, + +            confirm: function() { +                this.model.submit(); +            } + +        }); +    });
\ No newline at end of file diff --git a/pyload/web/app/scripts/views/setup/setupView.js b/pyload/web/app/scripts/views/setup/setupView.js index 7636a0bc2..8ab6fba51 100644 --- a/pyload/web/app/scripts/views/setup/setupView.js +++ b/pyload/web/app/scripts/views/setup/setupView.js @@ -1,6 +1,6 @@ -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) { +define(['jquery', 'backbone', 'underscore', 'app', 'models/Setup', 'hbs!tpl/setup/layout', 'hbs!tpl/setup/actionbar', 'hbs!tpl/setup/error', +    './welcomeView', './systemView', './userView', './finishedView'], +    function($, Backbone, _, App, Setup, template, templateBar, templateError, welcomeView, systemView, userView, finishedView) {          'use strict';          return Backbone.Marionette.ItemView.extend({ @@ -15,11 +15,14 @@ define(['jquery', 'backbone', 'underscore', 'app', 'models/Setup', 'hbs!tpl/setu              pages: [                  welcomeView, -                systemView +                systemView, +                userView, +                finishedView              ],              page: 0,              view: null, +            error: null,              initialize: function() {                  var self = this; @@ -52,37 +55,66 @@ define(['jquery', 'backbone', 'underscore', 'app', 'models/Setup', 'hbs!tpl/setu                  });                  this.listenTo(this.model, 'page:next', function() { -                    self.openPage(self.page++); +                    self.openPage(self.page + 1);                  });                  this.listenTo(this.model, 'page:prev', function() { -                    self.openPage(self.page--); +                    self.openPage(self.page - 1);                  }); + +                this.listenTo(this.model, 'error', this.onError); +                this.model.fetch();              },              openPage: function(page) {                  console.log('Change page', page);                  // check if number is reasonable -                if (!_.isNumber(page) || !_.isFinite(page)) +                if (!_.isNumber(page) || !_.isFinite(page) || page < 0 || page >= this.pages.length)                      return;                  if (page === this.page)                      return; +                // Render error directly +                if (this.error) { +                    this.onRender(); +                    return; +                } +                  this.page = page; -                this.onRender(); + +                var self = this; +                this.ui.page.fadeOut({complete: function() { +                    self.onRender(); +                }});                  this.model.trigger('page:changed', page);              }, +            onError: function(model, xhr) { +                console.log('Setup error', xhr); +                this.error = xhr; +                this.onRender(); +            }, +              onRender: function() { +                  // close old opened view                  if (this.view)                      this.view.close(); -                // TODO: animation +                // Render error if occurred +                if (this.error) { +                    this.ui.page.html(templateError(this.error)); +                    return; +                } +                  this.view = new this.pages[this.page]({model: this.model});                  this.ui.page.empty(); -                this.ui.page.append(this.view.render().$el); + +                var el = this.view.render().el; +                this.ui.page.append(el); + +                this.ui.page.fadeIn();              }          }); diff --git a/pyload/web/app/scripts/views/setup/systemView.js b/pyload/web/app/scripts/views/setup/systemView.js index 11e50213d..b4c0f7e12 100644 --- a/pyload/web/app/scripts/views/setup/systemView.js +++ b/pyload/web/app/scripts/views/setup/systemView.js @@ -8,12 +8,17 @@ define(['jquery', 'backbone', 'underscore', 'app', 'hbs!tpl/setup/system'],              template: template,              events: { +                'click .btn-blue': 'nextPage'              },              ui: {              },              onRender: function() { +            }, + +            nextPage: function() { +                this.model.trigger('page:next');              }          }); diff --git a/pyload/web/app/scripts/views/setup/userView.js b/pyload/web/app/scripts/views/setup/userView.js new file mode 100644 index 000000000..95eaa0dc2 --- /dev/null +++ b/pyload/web/app/scripts/views/setup/userView.js @@ -0,0 +1,39 @@ +define(['jquery', 'backbone', 'underscore', 'app', 'hbs!tpl/setup/user'], +    function($, Backbone, _, App, template) { +        'use strict'; + +        return Backbone.Marionette.ItemView.extend({ + +            name: 'User', +            template: template, + +            events: { +                'click .btn-blue': 'submit' +            }, + +            ui: { +                username: '#username', +                password: '#password', +                password2: '#password2' +            }, + +            onRender: function() { +            }, + +            submit: function() { +                var pw = this.ui.password.val(); +                var pw2 = this.ui.password2.val(); + +                // TODO more checks and error messages +                if (pw !== pw2) { +                    return; +                } + +                this.model.set('user', this.ui.username.val()); +                this.model.set('password', pw); + +                this.model.trigger('page:next'); +            } + +        }); +    });
\ 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 index 4affc9075..a964e0d42 100644 --- a/pyload/web/app/scripts/views/setup/welcomeView.js +++ b/pyload/web/app/scripts/views/setup/welcomeView.js @@ -8,12 +8,17 @@ define(['jquery', 'backbone', 'underscore', 'app', 'hbs!tpl/setup/welcome'],              template: template,              events: { +                'click .btn-blue': 'nextPage'              },              ui: {              },              onRender: function() { +            }, + +            nextPage: function() { +                this.model.trigger('page:next');              }          }); diff --git a/pyload/web/app/styles/default/main.less b/pyload/web/app/styles/default/main.less index 6bf21e80b..c978a3b90 100644 --- a/pyload/web/app/styles/default/main.less +++ b/pyload/web/app/styles/default/main.less @@ -13,6 +13,7 @@  @import "settings";  @import "accounts";  @import "admin"; +@import "setup";  @ResourcePath: "../..";  @DefaultFont: 'Abel', sans-serif; diff --git a/pyload/web/app/styles/default/setup.less b/pyload/web/app/styles/default/setup.less new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/pyload/web/app/styles/default/setup.less diff --git a/pyload/web/app/templates/default/setup/error.html b/pyload/web/app/templates/default/setup/error.html new file mode 100644 index 000000000..37ce51283 --- /dev/null +++ b/pyload/web/app/templates/default/setup/error.html @@ -0,0 +1,14 @@ +{{#ifEq status 410}} +    <h2 class="text-warning">{{ _ "Setup timed out" }}</h2> +    <p>{{ _ "Setup was closed due to inactivity. Please restart it to continue configuration." }}</p> +{{else}} +{{#ifEq status 409}} +    <h2 class="text-success">{{ _ "Setup finished" }}</h2> +    <p>{{ _ "Setup was successful. You can restart pyLoad now." }}</p> +{{else}} +    <h2 class="text-error"> +        {{ _ "Setup failed" }} +    </h2> +    <p>{{ _ "Try to restart it or open a bug report." }}</p> +{{/ifEq}} +{{/ifEq}}
\ No newline at end of file diff --git a/pyload/web/app/templates/default/setup/finished.html b/pyload/web/app/templates/default/setup/finished.html new file mode 100644 index 000000000..abe93b352 --- /dev/null +++ b/pyload/web/app/templates/default/setup/finished.html @@ -0,0 +1,23 @@ +{{#if user}} + +<h2> +    {{ _ "Nearly Done" }} +</h2> + +<p> +    {{ _ "Please check your settings." }} +</p> + +<p> +    <strong>Username:</strong> {{user}} +</p> + +<button class="btn btn-large btn-blue"> +    {{ _ "Confirm" }} +</button> + +{{else}} + +<h2>{{ _ "Pleae add a user first." }}</h2> + +{{/if}}
\ 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 index 7b75e53b1..2e986173a 100644 --- a/pyload/web/app/templates/default/setup/layout.html +++ b/pyload/web/app/templates/default/setup/layout.html @@ -3,5 +3,8 @@          {{ _ "Setup" }}      </h1>  </div> -<div class="span8 setup-page"> +<div class="span8"> +    <div class="hero-unit setup-page"> + +    </div>  </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 index 84a217b19..0c5023669 100644 --- a/pyload/web/app/templates/default/setup/system.html +++ b/pyload/web/app/templates/default/setup/system.html @@ -1,5 +1,56 @@ -<h1>{{ _ "System" }} </h1> +<h3>{{ _ "System" }} </h3> -<h2>{{_ "Dependencies" }}</h2> +<dl class="dl-horizontal"> +    {{#each system}} +        <dt>{{ @key }}</dt> +        <dd>{{ this }}</dd> +    {{/each}} +</dl> -<h2>{{ _ "Optional" }}</h2>
\ No newline at end of file +<h3>{{_ "Dependencies" }}</h3> +<dl class="dl-horizontal"> +    {{#each deps.core}} +    <dt>{{ name }}</dt> +    <dd> +        {{#if avail}} +            <span class="text-success"> +                <i class="icon-ok"></i> +                {{#if v}} +                 ({{v}}) +                {{/if}} +            </span> +        {{else}} +            <span class="text-error"> +                <i class="icon-remove"></i> +            </span> +        {{/if}} +    </dd> +    {{/each}} +</dl> + + +<h4>{{ _ "Optional" }}</h4> +<dl class="dl-horizontal"> +    {{#each deps.opt}} +    <dt>{{ name }}</dt> +    <dd> +        {{#if avail}} +            <span class="text-success"> +                {{ _ "available" }} +                {{#if v}} +                 ({{v}}) +                {{/if}} +            </span> +        {{else}} +            <span class="text-error"> +                {{ _ "not available" }} +            </span> +        {{/if}} +    </dd> +    {{/each}} +</dl> + + +<button class="btn btn-blue"> +    {{ _ "Next" }} +</button>
\ No newline at end of file diff --git a/pyload/web/app/templates/default/setup/user.html b/pyload/web/app/templates/default/setup/user.html new file mode 100644 index 000000000..fe3f2de71 --- /dev/null +++ b/pyload/web/app/templates/default/setup/user.html @@ -0,0 +1,34 @@ +<form class="form-horizontal"> +    <div class="control-group"> +        <label class="control-label"> +            Username +        </label> + +        <div class="controls"> +            <input type="text" id="username" placeholder="User"> +        </div> +    </div> +    <div class="control-group"> +        <label class="control-label"> +            Password +        </label> + +        <div class="controls"> +            <input type="password" id="password"> +        </div> +    </div> +    <div class="control-group"> +        <label class="control-label"> +            Password (again) +        </label> + +        <div class="controls"> +            <input type="password" id="password2"> +        </div> +    </div> +    <div class="control-group"> +        <div class="controls"> +                <a class="btn btn-blue">Submit</a> +        </div> +    </div> +</form>
\ 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 index f5c5af4d7..5a4f74d9f 100644 --- a/pyload/web/app/templates/default/setup/welcome.html +++ b/pyload/web/app/templates/default/setup/welcome.html @@ -1,16 +1,14 @@ -<div class="hero-unit"> -    <h1>{{ _ "Welcome!" }}</h1> +<h1>{{ _ "Welcome!" }}</h1> -    <p>{{ _ "pyLoad is running and ready for configuration." }}</p> +<p>{{ _ "pyLoad is running and ready for configuration." }}</p> -    <p> -        {{ _ "Select your language:" }} -        <select> -            <option>en</option> -        </select> -    </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 +<button class="btn btn-large btn-blue"> +    {{ _ "Start configuration" }} +</button>
\ No newline at end of file diff --git a/pyload/web/pyload_app.py b/pyload/web/pyload_app.py index 1ec7cf4c9..1c89e2ada 100644 --- a/pyload/web/pyload_app.py +++ b/pyload/web/pyload_app.py @@ -70,7 +70,11 @@ def index():      # 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 +    web = None +    if PYLOAD: +        web = PYLOAD.getConfigValue('webinterface', 'port') +    elif SETUP: +        web = SETUP.config['webinterface']['port']      # Render variables into the html page      if resp.status_code == 200: diff --git a/pyload/web/setup_app.py b/pyload/web/setup_app.py index 5163f9cc6..680c5ae89 100644 --- a/pyload/web/setup_app.py +++ b/pyload/web/setup_app.py @@ -3,26 +3,69 @@  from time import time +from pyload.utils import json_dumps +  from bottle import route, request, response, HTTPError, redirect  from webinterface import PROJECT_DIR, SETUP  from utils import add_json_header +# returns http error +def error(code, msg): +    return HTTPError(code, json_dumps(msg), **dict(response.headers)) + +  def setup_required(func):      def _view(*args, **kwargs): +        global timestamp +          # setup needs to be running          if SETUP is None: -            redirect("/nopermission") +            return error(404, "Not Found") + +        # setup finished +        if timestamp == 0: +            return error(409, "Done") + +        # setup timed out due to inactivity +        if timestamp + TIMEOUT * 60 < time(): +            return error(410, "Timeout") + +        timestamp = time()          return func(*args, **kwargs) +      return _view  # setup will close after inactivity  TIMEOUT = 15  timestamp = time() +  @route("/setup")  @setup_required  def setup(): -    pass # TODO +    add_json_header(response) + +    return json_dumps({ +        "system": SETUP.check_system(), +        "deps": SETUP.check_deps() +    }) + + +@route("/setup_done") +@setup_required +def setup_done(): +    global timestamp +    add_json_header(response) + +    SETUP.addUser( +        request.params['user'], +        request.params['password'] +    ) + +    # mark setup as finished +    timestamp = 0 + +    return error(409, "Done")  | 
