diff options
Diffstat (limited to 'pyload/web')
-rw-r--r-- | pyload/web/api_app.py | 14 | ||||
-rw-r--r-- | pyload/web/app/index.html | 1 | ||||
-rw-r--r-- | pyload/web/app/scripts/default.js | 18 | ||||
-rw-r--r-- | pyload/web/app/scripts/helpers/ifEq.js | 14 | ||||
-rw-r--r-- | pyload/web/app/scripts/models/Setup.js | 14 | ||||
-rw-r--r-- | pyload/web/app/scripts/setup.js | 33 | ||||
-rw-r--r-- | pyload/web/app/scripts/views/setup/setupView.js | 89 | ||||
-rw-r--r-- | pyload/web/app/scripts/views/setup/systemView.js | 20 | ||||
-rw-r--r-- | pyload/web/app/scripts/views/setup/welcomeView.js | 20 | ||||
-rw-r--r-- | pyload/web/app/styles/default/style.less | 10 | ||||
-rw-r--r-- | pyload/web/app/templates/default/setup.html | 16 | ||||
-rw-r--r-- | pyload/web/app/templates/default/setup/actionbar.html | 24 | ||||
-rw-r--r-- | pyload/web/app/templates/default/setup/layout.html | 7 | ||||
-rw-r--r-- | pyload/web/app/templates/default/setup/system.html | 5 | ||||
-rw-r--r-- | pyload/web/app/templates/default/setup/welcome.html | 16 | ||||
-rw-r--r-- | pyload/web/pyload_app.py | 37 | ||||
-rw-r--r-- | pyload/web/setup_app.py | 7 | ||||
-rw-r--r-- | pyload/web/utils.py | 13 |
18 files changed, 313 insertions, 45 deletions
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): |