sync webui plugin with rev48

This commit is contained in:
Marcos Pinto 2007-09-29 06:39:06 +00:00
parent 9a050065a9
commit 53e65e9554
19 changed files with 238 additions and 100 deletions

View File

@ -33,18 +33,24 @@
plugin_name = "Web User interface"
plugin_author = "Martijn Voncken"
plugin_version = "rev."
plugin_description = """A Web based User Interface (and dbus-ipc)
beta test version, disclaimer, etc..
"""
plugin_description = "A Web based User Interface\n"
import deluge.common, deluge.pref
import deluge.common
import deluge.pref
from deluge.dialogs import show_popup_warning
from dbus_interface import DbusManager
import gtk
import os
from subprocess import Popen
from md5 import md5
import random
random.seed()
plugin_version += open(os.path.join(os.path.dirname(__file__),'revno')).read()
plugin_description += (
open(os.path.join(os.path.dirname(__file__),'version')).read())
def deluge_init(deluge_path):
global path
@ -72,19 +78,24 @@ class plugin_WebUi:
if not self.config.get('port'): #ugly way to detect new config file.
#set default values:
self.config.set("port", 8112)
self.config.set("user", "deluge")
self.config.set("pwd", "deluge")
#future->use deluge-core setting for download_dir (if it is set)
self.config.set("download_dir", os.path.expanduser("~/"))
self.config.set("torrent_dir", os.path.expanduser("~/"))
self.config.set("button_style", 2)
self.config.set("auto_refresh", False)
self.config.set("auto_refresh_secs", 4)
self.config.set("template", "deluge")
self.config.save(self.config_file)
if not self.config.get("pwd_salt"):
self.config.set("pwd_salt", "invalid")
self.config.set("pwd_md5", "invalid")
self.dbusManager = DbusManager(deluge_core, deluge_interface
, self.config, self.config_file)
print dir(self.dbusManager)
self.start_server()
def unload(self):
@ -135,9 +146,11 @@ class ConfigDialog(gtk.Dialog):
if os.path.isdir(os.path.join(template_path, dirname))]
self.port = self.add_widget(_('Port Number'), gtk.SpinButton())
self.user = self.add_widget(_('User'), gtk.Entry())
self.pwd = self.add_widget(_('Password'), gtk.Entry())
self.pwd1 = self.add_widget(_('New Password'), gtk.Entry())
self.pwd2 = self.add_widget(_('New Password(confirm)'), gtk.Entry())
self.template = self.add_widget(_('Template'), gtk.combo_box_new_text())
self.button_style = self.add_widget(_('Button Style'),
gtk.combo_box_new_text())
self.download_dir = self.add_widget(_('Download Directory'),
gtk.FileChooserButton(_('Download Directory')))
self.torrent_dir = self.add_widget(_('Torrent Directory'),
@ -147,16 +160,24 @@ class ConfigDialog(gtk.Dialog):
self.torrent_dir.set_action(gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER)
self.port.set_range(80, 65536)
self.port.set_increments(1, 10)
self.pwd1.set_visibility(False)
self.pwd2.set_visibility(False)
for item in self.templates:
self.template.append_text(item)
self.button_style
if not self.config.get("template") in self.templates:
self.config.set("template","deluge")
self.user.set_text(self.config.get("user"))
self.pwd.set_text(self.config.get("pwd"))
for item in [_('Text and image'), _('Image Only'), _('Text Only')]:
self.button_style.append_text(item)
if not self.config.get("button_style"):
self.config.set("button_style", 2)
self.port.set_value(int(self.config.get("port")))
self.template.set_active(
self.templates.index(self.config.get("template")))
self.button_style.set_active(self.config.get("button_style"))
self.torrent_dir.set_filename(self.config.get("torrent_dir"))
self.download_dir.set_filename(self.config.get("download_dir"))
self.vbox.pack_start(self.vb, True, True, 0)
@ -177,11 +198,21 @@ class ConfigDialog(gtk.Dialog):
self.add_buttons(dgtk.STOCK_CLOSE, dgtk.RESPONSE_CLOSE)
def save_config(self):
print 'save config'
self.config.set("user", self.user.get_text())
self.config.set("pwd", self.pwd.get_text())
if self.pwd1.get_text() > '':
if self.pwd1.get_text() <> self.pwd2.get_text():
show_popup_warning(self,_("Confirmed Password <> New Password\n"
+ "Password was not changed"))
else:
salt = str(random.getrandbits(500))
m = md5()
m.update(salt)
m.update(unicode(self.pwd1.get_text()))
self.config.set("pwd_salt", salt)
self.config.set("pwd_md5", m.digest())
self.config.set("port", int(self.port.get_value()))
self.config.set("template", self.template.get_active_text())
self.config.set("button_style", self.button_style.get_active())
self.config.set("torrent_dir", self.torrent_dir.get_filename())
self.config.set("download_dir",self.download_dir.get_filename())
self.config.save(self.plugin.config_file)

View File

@ -0,0 +1 @@
curl -F torrent=@./test1.torrent -F pwd=deluge http://localhost:8112/remote/torrent/add

View File

@ -39,6 +39,7 @@ import dbus
import deluge.common as common
from dbus_pythonize import pythonize
import base64
from md5 import md5
import random
random.seed()
@ -182,7 +183,7 @@ class DbusManager(dbus.service.Object):
not in 0.6
"""
retval = self.config.get(str(key))
print 'get webui config:', str(key), retval
#print 'get webui config:', str(key), retval
if retval == None:
retval = False #dbus does not accept None :(
@ -195,10 +196,18 @@ class DbusManager(dbus.service.Object):
return data from wevbui config.
not in 0.6
"""
print 'set webui config:', str(key), pythonize(value)
#print 'set webui config:', str(key), pythonize(value)
self.config.set(str(key), pythonize(value))
self.config.save(self.config_file)
@dbus.service.method(dbus_interface=dbus_interface,
in_signature="s",out_signature="b")
def check_pwd(self, pwd):
m = md5()
m.update(self.config.get('pwd_salt'))
m.update(pwd)
return (m.digest() == self.config.get('pwd_md5'))
#internal
def _add_torrent(self, filename):
#dbus types break pickle, again.....

View File

@ -50,7 +50,7 @@ from webpy022 import template
import dbus
import gettext, os, platform, locale
import gettext, os, platform, locale, traceback
import random
import base64
from operator import attrgetter
@ -105,7 +105,7 @@ def deluge_page_noauth(func):
web.header("Content-Type", "text/html; charset=utf-8")
web.header("Cache-Control", "no-cache, must-revalidate")
res = func(self, name)
print res
print unicode(res)
return deco
def check_session(func):
@ -139,6 +139,17 @@ def error_page(error):
web.header("Cache-Control", "no-cache, must-revalidate")
print render.error(error)
def remote(func):
"decorator for remote api's"
def deco(self, name):
try:
print func(self, name)
except Exception, e:
print 'error:' + e.message
print '-'*20
print traceback.format_exc()
return deco
#/framework
#utils:
@ -191,7 +202,7 @@ def template_crop(text, end):
return text[0:end - 3] + '...'
return text
def sort_head(id,name):
def template_sort_head(id,name):
#got tired of doing these complex things inside templetor..
vars = web.input(sort=None, order=None)
active_up = False
@ -210,18 +221,23 @@ def sort_head(id,name):
render = template.render('templates/%s/' % proxy.get_webui_config('template'))
template.Template.globals['crop'] = template_crop
template.Template.globals['fspeed'] = common.fspeed
template.Template.globals['fsize'] = common.fsize
template.Template.globals['sorted'] = sorted
template.Template.globals['_'] = _ #gettext/translations
template.Template.globals['deluge_web_version'] = ('rev.'
+ open(os.path.join(os.path.dirname(__file__),'revno')).read())
template.Template.globals['render'] = render #for easy resuse of templates
template.Template.globals['sort_head'] = sort_head
template.Template.globals['get_config'] = proxy.get_webui_config
template.Template.globals['self_url'] = web.changequery
template.Template.globals.update({
'sort_head': template_sort_head,
'crop': template_crop,
'_': _ , #gettext/translations
'str': str, #because % in templetor is broken.
'sorted': sorted,
'get_config': proxy.get_webui_config,
'self_url': web.changequery,
'fspeed': common.fspeed,
'fsize': common.fsize,
'render': render, #for easy resuse of templates
'button_style': (proxy.get_webui_config('button_style')),
'rev': ('rev.' +
open(os.path.join(os.path.dirname(__file__),'revno')).read()),
'version': (
open(os.path.join(os.path.dirname(__file__),'version')).read())
})
#/template-defs
#routing:
@ -237,9 +253,12 @@ urls = (
"/refresh/set(.*)", "refresh_set",
"/refresh/(.*)", "refresh",
"/home(.*)", "home",
"/about(.*)", "about",
#default-pages
"/", "login",
"", "login"
"", "login",
#remote-api:
"/remote/torrent/add(.*)", "remote_torrent_add"
)
#/routing
@ -248,20 +267,20 @@ urls = (
class login:
@deluge_page_noauth
def GET(self, name):
return render.login()
vars = web.input(error = None)
return render.login(vars.error)
def POST(self, name):
vars = web.input(var = None, pwd = None)
vars = web.input(pwd = None)
if (vars.user == proxy.get_webui_config('user')
and vars.pwd == proxy.get_webui_config('pwd')):
if proxy.check_pwd(vars.pwd):
#start new session
session_id = str(random.random())
SESSIONS[session_id] = {"user":vars.user}
SESSIONS[session_id] = {"not":"used"}
setcookie("session_id", session_id)
do_redirect()
else:
error_page(_("Username or Password is invalid."))
seeother('/login?error=1')
class home:
@check_session
@ -332,6 +351,23 @@ class torrent_add:
else:
error_page(_("no data."))
class remote_torrent_add:
"""
For use in remote scripts etc.
POST user and file
Example : curl -F torrent=@./test1.torrent -F pwd=deluge http://localhost:8112/remote/torrent/add"
"""
@remote
def POST(self, name):
vars = web.input(pwd = None, torrent = {})
if not proxy.check_pwd(vars.pwd):
return 'error:wrong password'
data_b64 = base64.b64encode(vars.torrent.file.read())
proxy.add_torrent_filecontent(vars.torrent.filename,data_b64)
return 'ok'
class torrent_delete:
@deluge_page
def GET(self, torrent_id):
@ -383,6 +419,12 @@ class refresh_set:
else:
error_page(_('refresh must be > 0'))
class about:
@deluge_page_noauth
def GET(self, name):
return render.about()
#/pages
if __name__ == "__main__":

View File

@ -1 +1 @@
40
48

View File

@ -31,6 +31,15 @@ button.deluge_button {
color: #000;
}
div.error {
background-color:#FFFFFF;
color:#AA0000;
font-weight:bold;
-moz-border-radius:10px;
width:200px;
margin-bottom:20px;
padding:10px;
}
/* Hides from IE-mac \*/ * html .clearfix {height: 1%;} .clearfix {display: block;} /* End hide from IE-mac */

View File

@ -0,0 +1,39 @@
$:render.header(_('About'))
<div class="panel" style="text-align:left">
<h2>Version</h2>
<pre>$version </pre>
<h2>Links</h2>
<ul>
<li><a href="http://deluge-torrent.org">Deluge</a></li>
<li><a href="http://forum.deluge-torrent.org/viewtopic.php?f=9&t=425">
WebUi forum Thread</a>
</li>
<li><a href="http://www.gnu.org/licenses/old-licenses/gpl-2.0.html">GPL v2
</a></li>
</ul>
<h2>Authors</h2>
<ul>
<h3>WebUi</h3>
<ul>
<li>Martijn Voncken</li>
</ul>
<h3>Template</h3>
<ul>
<li>Martijn Voncken</li>
<li>somedude</li>
</ul>
<h3>Deluge</h3>
<ul>
<li>Zach Tibbitts</li>
<li>Alon Zakai</li>
<li>Alon Zakai</li>
<li>Marcos Pinto</li>
<li>Andrew Resch</li>
<li>Alex Dedul</li>
</ul>
</ul>
*and all other authors/helpers/contributors I forgot to mention.
</div>
$:render.footer()

View File

@ -1,6 +1,6 @@
$def with (error_msg)
$:header(_('Error'))
$:render.header(_('Error'))
<pre class="error">
$error_msg
</pre>
$:footer()
$:render.footer()

View File

@ -1,15 +1,13 @@
$def with (title)
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Deluge:$title</title>
<link rel="icon" href="/static/images/deluge_icon.gif" type="image/gif" />
<link rel="shortcut icon" href="/static/images/deluge_icon.gif" type="image/gif" />
<link rel="stylesheet" type="text/css" href="/static/simple_site_style.css" />
</head>
<body>
<div id="page">

View File

@ -54,5 +54,4 @@ $:render.part_button('POST', '/resume_all', _('Resume all'), 'tango/media-playba
$:render.part_refresh()
$:render.footer()

View File

@ -1,13 +1,13 @@
$def with (error)
$:render.header(_('Login'))
<div class="panel">
$if error > 0:
<div class="error">$_("Password is invalid,try again")</div>
<form method="POST" id="loginform" action='/login'>
<div id="loginpanel">
<div class="form_row">
<span class="form_label">$_('User')</span>
<input type="text" name="user" id="user" class="form_input">
</div>
<div class="form_row">
<span class="form_label">$_('Pass')</span>
<span class="form_label">$_('Password')</span>
<input type="password" name="pwd" id="pwd" class="form_input">
</div>
<div class="form_row">
@ -15,6 +15,9 @@ $:render.header(_('Login'))
<input type="submit" name="submit"
id="submit" value="Submit" class="form_input">
</div>
<br />
<a href="/about">$_('About')</a>
</div>
</form>
</div>

View File

@ -3,11 +3,20 @@ $def with (method, url, title, image='')
<form method="$method" action="$url" class="deluge_button">
<button type="submit" class="deluge_button">
<input type="hidden" name="redir" value="$self_url()">
$if image:
<image src="/static/images/$image" class="button"/>
$else:
<!--no image-->
$title
$if (button_style == 0):
$title
$if image:
<image src="/static/images/$image" class="button" alt="$title"/>
$if (button_style == 1):
$if image:
<image src="/static/images/$image" class="button" alt="$title"/>
$else:
$title
$if (button_style == 2):
$title
</button>
</form>
</div>

View File

@ -1,5 +1,5 @@
<div class="panel">
<div id='refresh'>
<div id='refresh_panel'>
<div class="panel" >
$_('Auto refresh:')
$if get_config('auto_refresh'):
($(get_config('refresh')) $_('seconds')) &nbsp;

View File

@ -3,7 +3,7 @@ $:render.header(_('Set Timeout'))
<form action="/refresh/set" method="POST">
$_('Refresh page every:')
<input type="text" name="refresh" value="$get_config('refresh')" size="3">
$_('Seconds')
$_('seconds')
<input type="submit" value="$_('Set')">
</form>
</div>

View File

@ -80,17 +80,7 @@ class="deluge_button">
name="$torrent.action" value="$torrent.id">
</form>
<div class="deluge_button">
<form method="GET" action="/torrent/delete/$torrent.id" class="deluge_button">
<button type="submit" class="deluge_button">
<image src="/static/images/tango/user-trash.png" /> $_('Delete')
</button>
</form>
</div>
</form>
$:render.part_button('GET', '/torrent/delete/' + str(torrent.id), _('Remove'), 'tango/user-trash.png')
<br>
[<a onclick="javascript:toggle_dump()">$_('Debug:Data Dump')</a>]

View File

@ -10,16 +10,21 @@ Or use scite and my config: http://mvoncken.sohosted.com/deluge/SciTEUser.proper
template language: http://webpy.org/templetor
Exposed methods and variables (c&p from deluge_webserver):
template.Template.globals['crop'] = template_crop
template.Template.globals['fspeed'] = common.fspeed
template.Template.globals['fsize'] = common.fsize
template.Template.globals['sorted'] = sorted
template.Template.globals['_'] = _ #gettext/translations
template.Template.globals['deluge_web_version'] = '0.3alfa'
template.Template.globals['render'] = render #for easy resuse of templates
template.Template.globals['sort_head'] = sort_head
template.Template.globals['get_config'] = proxy.get_webui_config
'sort_head': template_sort_head,
'crop': template_crop,
'_': _ , #gettext/translations
'str': str, #because % in templetor is broken.
'sorted': sorted,
'get_config': proxy.get_webui_config,
'self_url': web.changequery,
'fspeed': common.fspeed,
'fsize': common.fsize,
'render': render, #for easy resuse of templates
'button_style': (proxy.get_webui_config('button_style')),
'rev': ('rev.' +
open(os.path.join(os.path.dirname(__file__),'revno')).read()),
'version': (
open(os.path.join(os.path.dirname(__file__),'version')).read())
I will update this file if there is interest in making templates.

View File

@ -1,5 +1 @@
revision-id: mvoncken@gmail.com-20070923193628-omqar5yaxatdmfu0
date: 2007-09-23 21:36:28 +0200
build-date: 2007-09-23 21:54:09 +0200
revno: 30
branch-nick: WebUi
48

View File

@ -1,6 +1,10 @@
Commented out some code to enable a relative redirect.
1:Commented out some code to enable a relative redirect.
This is not according to HTTP/1.1 Spec
But many deluge users will want to route the webui through firewalls/routers or use apache redirects.
2:Disabled logging in the builtin http-server.

View File

@ -6,11 +6,11 @@ import net
def runbasic(func, server_address=("0.0.0.0", 8080)):
"""
Runs a simple HTTP server hosting WSGI app `func`. The directory `static/`
Runs a simple HTTP server hosting WSGI app `func`. The directory `static/`
is hosted statically.
Based on [WsgiServer][ws] from [Colin Stewart][cs].
[ws]: http://www.owlfish.com/software/wsgiutils/documentation/wsgi-server-api.html
[cs]: http://www.owlfish.com/
"""
@ -62,17 +62,17 @@ def runbasic(func, server_address=("0.0.0.0", 8080)):
try:
try:
for data in result:
if data:
if data:
self.wsgi_write_data(data)
finally:
if hasattr(result, 'close'):
if hasattr(result, 'close'):
result.close()
except socket.error, socket_err:
# Catch common network errors and suppress them
if (socket_err.args[0] in \
(errno.ECONNABORTED, errno.EPIPE)):
(errno.ECONNABORTED, errno.EPIPE)):
return
except socket.timeout, socket_timeout:
except socket.timeout, socket_timeout:
return
except:
print >> web.debug, traceback.format_exc(),
@ -92,7 +92,7 @@ def runbasic(func, server_address=("0.0.0.0", 8080)):
else:
self.run_wsgi_app()
def wsgi_start_response(self, response_status, response_headers,
def wsgi_start_response(self, response_status, response_headers,
exc_info=None):
if (self.wsgi_sent_headers):
raise Exception \
@ -117,8 +117,8 @@ def runbasic(func, server_address=("0.0.0.0", 8080)):
class WSGIServer(SocketServer.ThreadingMixIn, BaseHTTPServer.HTTPServer):
def __init__(self, func, server_address):
BaseHTTPServer.HTTPServer.__init__(self,
server_address,
BaseHTTPServer.HTTPServer.__init__(self,
server_address,
WSGIHandler)
self.app = func
self.serverShuttingDown = 0
@ -128,7 +128,7 @@ def runbasic(func, server_address=("0.0.0.0", 8080)):
def runsimple(func, server_address=("0.0.0.0", 8080)):
"""
Runs [CherryPy][cp] WSGI server hosting WSGI app `func`.
Runs [CherryPy][cp] WSGI server hosting WSGI app `func`.
The directory `static/` is hosted statically.
[cp]: http://www.cherrypy.org
@ -180,7 +180,7 @@ def runsimple(func, server_address=("0.0.0.0", 8080)):
else:
value = self.wfile.getvalue()
yield value
class WSGIWrapper(BaseHTTPRequestHandler):
"""WSGI wrapper for logging the status and serving static files."""
def __init__(self, app):
@ -200,20 +200,23 @@ def runsimple(func, server_address=("0.0.0.0", 8080)):
return self.app(environ, xstart_response)
def log(self, status, environ):
#mvoncken,no logging..
return
outfile = environ.get('wsgi.errors', web.debug)
req = environ.get('PATH_INFO', '_')
protocol = environ.get('ACTUAL_SERVER_PROTOCOL', '-')
method = environ.get('REQUEST_METHOD', '-')
host = "%s:%s" % (environ.get('REMOTE_ADDR','-'),
host = "%s:%s" % (environ.get('REMOTE_ADDR','-'),
environ.get('REMOTE_PORT','-'))
#@@ It is really bad to extend from
#@@ It is really bad to extend from
#@@ BaseHTTPRequestHandler just for this method
time = self.log_date_time_string()
print >> outfile, self.format % (host, time, protocol,
print >> outfile, self.format % (host, time, protocol,
method, req, status)
func = WSGIWrapper(func)
server = CherryPyWSGIServer(server_address, func, server_name="localhost")