Fixed bug #790382 by using the Mako template engine to pass Python variables into the template and tweaking a few things in the JS.

bzr-revno: 1599
This commit is contained in:
Raoul Snyman 2011-05-31 20:57:59 +02:00
commit d4a75148b8
6 changed files with 117 additions and 59 deletions

View File

@ -27,91 +27,109 @@
--> -->
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<title>OpenLP 2.0 Remote</title> <title>${app_title}</title>
<link rel="stylesheet" href="/files/jquery.mobile.css" /> <link rel="stylesheet" href="/files/jquery.mobile.css" />
<link rel="stylesheet" href="/files/openlp.css" /> <link rel="stylesheet" href="/files/openlp.css" />
<script type="text/javascript" src="/files/jquery.js"></script> <script type="text/javascript" src="/files/jquery.js"></script>
<script type="text/javascript" src="/files/openlp.js"></script> <script type="text/javascript" src="/files/openlp.js"></script>
<script type="text/javascript" src="/files/jquery.mobile.js"></script> <script type="text/javascript" src="/files/jquery.mobile.js"></script>
<script type="text/javascript">
translationStrings = {
"go_live": "${go_live}",
"add_to_service": "${add_to_service}",
"no_results": "${no_results}",
"back": "${back}"
}
</script>
</head> </head>
<body> <body>
<div data-role="page" id="home"> <div data-role="page" id="home">
<div data-role="header"> <div data-role="header">
<h1>OpenLP 2.0 Remote</h1> <h1>${app_title}</h1>
</div> </div>
<div data-role="content"> <div data-role="content">
<div data-role="controlgroup"> <div data-role="controlgroup">
<a href="#service-manager" data-role="button" data-icon="arrow-r" data-iconpos="right">Service Manager</a> <a href="#service-manager" data-role="button" data-icon="arrow-r" data-iconpos="right">${service_manager}</a>
<a href="#slide-controller" data-role="button" data-icon="arrow-r" data-iconpos="right">Slide Controller</a> <a href="#slide-controller" data-role="button" data-icon="arrow-r" data-iconpos="right">${slide_controller}</a>
<a href="#alerts" data-role="button" data-icon="arrow-r" data-iconpos="right">Alerts</a> <a href="#alerts" data-role="button" data-icon="arrow-r" data-iconpos="right">${alerts}</a>
<a href="#search" data-role="button" data-icon="arrow-r" data-iconpos="right">Search</a> <a href="#search" data-role="button" data-icon="arrow-r" data-iconpos="right">${search}</a>
</div> </div>
</div> </div>
</div> </div>
<div data-role="page" id="service-manager"> <div data-role="page" id="service-manager">
<div data-role="header"> <div data-role="header">
<a href="#" data-rel="back" data-icon="arrow-l">Back</a> <a href="#" data-rel="back" data-icon="arrow-l">${back}</a>
<h1>Service Manager</h1> <h1>${service_manager}</h1>
<a href="#" id="service-refresh" data-role="button" data-icon="refresh">Refresh</a> <a href="#" id="service-refresh" data-role="button" data-icon="refresh">${refresh}</a>
</div> </div>
<div data-role="content"> <div data-role="content">
<ul data-role="listview" data-inset="true"> <ul data-role="listview" data-inset="true">
</ul> </ul>
</div> </div>
<div data-role="footer" data-theme="b" class="ui-bar"> <div data-role="footer" data-theme="b" class="ui-bar">
<a href="#" id="service-blank" data-role="button" data-icon="blank">Blank</a> <a href="#" id="service-blank" data-role="button" data-icon="blank">${blank}</a>
<a href="#" id="service-unblank" data-role="button" data-icon="unblank">Show</a> <a href="#" id="service-unblank" data-role="button" data-icon="unblank">${show}</a>
<a href="#" id="service-previous" data-role="button" data-icon="arrow-l">Prev</a> <a href="#" id="service-previous" data-role="button" data-icon="arrow-l">${prev}</a>
<a href="#" id="service-next" data-role="button" data-icon="arrow-r" data-iconpos="right">Next</a> <a href="#" id="service-next" data-role="button" data-icon="arrow-r" data-iconpos="right">${next}</a>
</div> </div>
</div> </div>
<div data-role="page" id="slide-controller"> <div data-role="page" id="slide-controller">
<div data-role="header"> <div data-role="header">
<a href="#" data-rel="back" data-icon="arrow-l">Back</a> <a href="#" data-rel="back" data-icon="arrow-l">${back}</a>
<h1>Slide Controller</h1> <h1>${slide_controller}</h1>
<a href="#" id="controller-refresh" data-role="button" data-icon="refresh">Refresh</a> <a href="#" id="controller-refresh" data-role="button" data-icon="refresh">${refresh}</a>
</div> </div>
<div data-role="content"> <div data-role="content">
<ul data-role="listview" data-inset="true"> <ul data-role="listview" data-inset="true">
</ul> </ul>
</div> </div>
<div data-role="footer" data-theme="b" class="ui-bar"> <div data-role="footer" data-theme="b" class="ui-bar">
<a href="#" id="controller-blank" data-role="button" data-icon="blank">Blank</a> <a href="#" id="controller-blank" data-role="button" data-icon="blank">${blank}</a>
<a href="#" id="controller-unblank" data-role="button" data-icon="unblank">Show</a> <a href="#" id="controller-unblank" data-role="button" data-icon="unblank">${show}</a>
<a href="#" id="controller-previous" data-role="button" data-icon="arrow-l">Prev</a> <a href="#" id="controller-previous" data-role="button" data-icon="arrow-l">${prev}</a>
<a href="#" id="controller-next" data-role="button" data-icon="arrow-r" data-iconpos="right">Next</a> <a href="#" id="controller-next" data-role="button" data-icon="arrow-r" data-iconpos="right">${next}</a>
</div> </div>
</div> </div>
<div data-role="page" id="alerts"> <div data-role="page" id="alerts">
<div data-role="header"> <div data-role="header">
<a href="#" data-rel="back" data-icon="arrow-l">Back</a> <a href="#" data-rel="back" data-icon="arrow-l">${back}</a>
<h1>Alerts</h1> <h1>${alerts}</h1>
</div> </div>
<div data-role="content"> <div data-role="content">
<div data-role="fieldcontain"> <div data-role="fieldcontain">
<label for="alert-text">Text:</label> <label for="alert-text">${text}:</label>
<input type="text" name="alert-text" id="alert-text" value="" /> <input type="text" name="alert-text" id="alert-text" value="" />
</div> </div>
<a href="#" id="alert-submit" data-role="button">Show Alert</a> <a href="#" id="alert-submit" data-role="button">${show_alert}</a>
</div> </div>
</div> </div>
<div data-role="page" id="search"> <div data-role="page" id="search">
<div data-role="header"> <div data-role="header">
<a href="#" data-rel="back" data-icon="arrow-l">Back</a> <a href="#" data-rel="back" data-icon="arrow-l">${back}</a>
<h1>Search</h1> <h1>${search}</h1>
</div> </div>
<div data-role="content"> <div data-role="content">
<div data-role="fieldcontain"> <div data-role="fieldcontain">
<label for="search-plugin">Search:</label> <label for="search-plugin">${search}:</label>
<select name="search-plugin" id="search-plugin" data-native-menu="false"></select> <select name="search-plugin" id="search-plugin" data-native-menu="false"></select>
</div> </div>
<div data-role="fieldcontain"> <div data-role="fieldcontain">
<label for="search-text">Text:</label> <label for="search-text">${text}:</label>
<input type="search" name="search-text" id="search-text" value="" /> <input type="search" name="search-text" id="search-text" value="" />
</div> </div>
<a href="#" id="search-submit" data-role="button">Search</a> <a href="#" id="search-submit" data-role="button">${search}</a>
<ul data-role="listview" data-inset="true"> <ul data-role="listview" data-inset="true">
</div> </div>
</div> </div>
<div data-role="page" id="options">
<div data-role="header" data-position="inline" data-theme="b">
<h1>${options}</h1>
</div>
<div data-role="content">
<input type="hidden" id="selected-item" value="" />
<a href="#" id="go-live" data-role="button">${go_live}</a>
<a href="#" id="add-to-service" data-role="button">${add_to_service}</a>
</div>
</div>
</body> </body>
</html> </html>

0
openlp/plugins/remotes/html/json2.js Executable file → Normal file
View File

View File

@ -47,7 +47,7 @@ window.OpenLP = {
var select = $("#search-plugin"); var select = $("#search-plugin");
select.html(""); select.html("");
$.each(data.results.items, function (idx, value) { $.each(data.results.items, function (idx, value) {
select.append("<option value='" + value + "'>" + value + "</option>"); select.append("<option value='" + value[0] + "'>" + value[1] + "</option>");
}); });
select.selectmenu("refresh"); select.selectmenu("refresh");
} }
@ -215,16 +215,15 @@ window.OpenLP = {
var ul = $("#search > div[data-role=content] > ul[data-role=listview]"); var ul = $("#search > div[data-role=content] > ul[data-role=listview]");
ul.html(""); ul.html("");
if (data.results.items.length == 0) { if (data.results.items.length == 0) {
var li = $("<li data-icon=\"false\">").text('No results'); var li = $("<li data-icon=\"false\">").text(translationStrings["no_results"]);
ul.append(li); ul.append(li);
} }
else { else {
$.each(data.results.items, function (idx, value) { $.each(data.results.items, function (idx, value) {
var item = $("<li>").text(value[1]); ul.append($("<li>").append($("<a>").attr("href", "#options")
var golive = $("<a href=\"#\">Go Live</a>").attr("value", value[0]).click(OpenLP.goLive); .attr("data-rel", "dialog").attr("data-transition", "pop")
var additem = $("<a href=\"#\">Add To Service</a>").attr("value", value[0]).click(OpenLP.addToService); .attr("value", value[0]).click(OpenLP.showOptions)
item.append($("<ul>").append($("<li>").append(golive)).append($("<li>").append(additem))); .text(value[1])));
ul.append(item);
}); });
} }
ul.listview("refresh"); ul.listview("refresh");
@ -232,19 +231,23 @@ window.OpenLP = {
); );
return false; return false;
}, },
showOptions: function (event) {
var element = OpenLP.getElement(event);
console.log(element);
$("#selected-item").val(element.attr("value"));
},
goLive: function (event) { goLive: function (event) {
var item = OpenLP.getElement(event); var id = $("#selected-item").val();
var id = item.attr("value");
var text = JSON.stringify({"request": {"id": id}}); var text = JSON.stringify({"request": {"id": id}});
$.getJSON( $.getJSON(
"/api/" + $("#search-plugin").val() + "/live", "/api/" + $("#search-plugin").val() + "/live",
{"data": text}) {"data": text}
$.mobile.changePage("slide-controller"); );
$.mobile.changePage("#slide-controller");
return false; return false;
}, },
addToService: function (event) { addToService: function (event) {
var item = OpenLP.getElement(event); var id = $("#selected-item").val();
var id = item.attr("value");
var text = JSON.stringify({"request": {"id": id}}); var text = JSON.stringify({"request": {"id": id}});
$.getJSON( $.getJSON(
"/api/" + $("#search-plugin").val() + "/add", "/api/" + $("#search-plugin").val() + "/add",
@ -253,6 +256,7 @@ window.OpenLP = {
history.back(); history.back();
} }
); );
$("#options").dialog("close");
return false; return false;
} }
} }
@ -274,6 +278,8 @@ $("#controller-unblank").live("click", OpenLP.unblankDisplay);
$("#alert-submit").live("click", OpenLP.showAlert); $("#alert-submit").live("click", OpenLP.showAlert);
// Search // Search
$("#search-submit").live("click", OpenLP.search); $("#search-submit").live("click", OpenLP.search);
$("#go-live").live("click", OpenLP.goLive);
$("#add-to-service").live("click", OpenLP.addToService);
// Poll the server twice a second to get any updates. // Poll the server twice a second to get any updates.
OpenLP.getSearchablePlugins(); OpenLP.getSearchablePlugins();
$.ajaxSetup({ cache: false }); $.ajaxSetup({ cache: false });

View File

@ -6,8 +6,8 @@
# --------------------------------------------------------------------------- # # --------------------------------------------------------------------------- #
# Copyright (c) 2008-2011 Raoul Snyman # # Copyright (c) 2008-2011 Raoul Snyman #
# Portions copyright (c) 2008-2011 Tim Bentley, Jonathan Corwin, Michael # # Portions copyright (c) 2008-2011 Tim Bentley, Jonathan Corwin, Michael #
# Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, Armin Köhler, # # Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, Armin Köhler, #
# Andreas Preikschat, Mattias Põldaru, Christian Richter, Philip Ridout, # # Andreas Preikschat, Mattias Põldaru, Christian Richter, Philip Ridout, #
# Jeffrey Smith, Maikel Stuivenberg, Martin Thompson, Jon Tibble, Frode # # Jeffrey Smith, Maikel Stuivenberg, Martin Thompson, Jon Tibble, Frode #
# Woldsund # # Woldsund #
# --------------------------------------------------------------------------- # # --------------------------------------------------------------------------- #
@ -27,12 +27,13 @@
--> -->
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<title>OpenLP 2.0 Stage View</title> <title>${stage_title}</title>
<link rel="stylesheet" href="/files/stage.css" /> <link rel="stylesheet" href="/files/stage.css" />
<script type="text/javascript" src="/files/jquery.js"></script> <script type="text/javascript" src="/files/jquery.js"></script>
<script type="text/javascript" src="/files/stage.js"></script> <script type="text/javascript" src="/files/stage.js"></script>
</head> </head>
<body> <body>
<input type="hidden" id="next-text" value="${next}" />
<div id="right"> <div id="right">
<div id="clock"></div> <div id="clock"></div>
<div id="notes"></div> <div id="notes"></div>

View File

@ -100,24 +100,24 @@ window.OpenLP = {
$("#tag" + OpenLP.currentTags[OpenLP.currentSlide]).addClass("currenttag"); $("#tag" + OpenLP.currentTags[OpenLP.currentSlide]).addClass("currenttag");
var slide = OpenLP.currentSlides[OpenLP.currentSlide]; var slide = OpenLP.currentSlides[OpenLP.currentSlide];
var text = slide["text"]; var text = slide["text"];
text = text.replace(/\n/g, '<br />'); text = text.replace(/\n/g, "<br />");
$("#currentslide").html(text); $("#currentslide").html(text);
text = ""; text = "";
if (OpenLP.currentSlide < OpenLP.currentSlides.length - 1) { if (OpenLP.currentSlide < OpenLP.currentSlides.length - 1) {
for (var idx = OpenLP.currentSlide + 1; idx < OpenLP.currentSlides.length; idx++) { for (var idx = OpenLP.currentSlide + 1; idx < OpenLP.currentSlides.length; idx++) {
if (OpenLP.currentTags[idx] != OpenLP.currentTags[idx - 1]) if (OpenLP.currentTags[idx] != OpenLP.currentTags[idx - 1])
text = text + '<p class="nextslide">'; text = text + "<p class=\"nextslide\">";
text = text + OpenLP.currentSlides[idx]["text"]; text = text + OpenLP.currentSlides[idx]["text"];
if (OpenLP.currentTags[idx] != OpenLP.currentTags[idx - 1]) if (OpenLP.currentTags[idx] != OpenLP.currentTags[idx - 1])
text = text + '</p>'; text = text + "</p>";
else else
text = text + '<br />'; text = text + "<br />";
} }
text = text.replace(/\n/g, '<br />'); text = text.replace(/\n/g, "<br />");
$("#nextslide").html(text); $("#nextslide").html(text);
} }
else { else {
text = '<p class="nextslide">Next: ' + OpenLP.nextSong + '</p>'; text = "<p class=\"nextslide\">" + $("#next-text").val() + ": " + OpenLP.nextSong + "</p>";
$("#nextslide").html(text); $("#nextslide").html(text);
} }
}, },

View File

@ -115,7 +115,6 @@ import logging
import os import os
import urlparse import urlparse
import re import re
from pprint import pformat
try: try:
import json import json
@ -123,10 +122,11 @@ except ImportError:
import simplejson as json import simplejson as json
from PyQt4 import QtCore, QtNetwork from PyQt4 import QtCore, QtNetwork
from mako.template import Template
from openlp.core.lib import Receiver, PluginStatus from openlp.core.lib import Receiver, PluginStatus, StringContent
from openlp.core.ui import HideMode from openlp.core.ui import HideMode
from openlp.core.utils import AppLocation from openlp.core.utils import AppLocation, translate
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -261,6 +261,7 @@ class HttpConnection(object):
self.ready_read) self.ready_read)
QtCore.QObject.connect(self.socket, QtCore.SIGNAL(u'disconnected()'), QtCore.QObject.connect(self.socket, QtCore.SIGNAL(u'disconnected()'),
self.disconnected) self.disconnected)
self.translate()
def _get_service_items(self): def _get_service_items(self):
service_items = [] service_items = []
@ -280,6 +281,31 @@ class HttpConnection(object):
}) })
return service_items return service_items
def translate(self):
"""
Translate various strings in the mobile app.
"""
self.template_vars = {
'app_title': translate('RemotePlugin.Mobile', 'OpenLP 2.0 Remote'),
'stage_title': translate('RemotePlugin.Mobile', 'OpenLP 2.0 Stage View'),
'service_manager': translate('RemotePlugin.Mobile', 'Service Manager'),
'slide_controller': translate('RemotePlugin.Mobile', 'Slide Controller'),
'alerts': translate('RemotePlugin.Mobile', 'Alerts'),
'search': translate('RemotePlugin.Mobile', 'Search'),
'back': translate('RemotePlugin.Mobile', 'Back'),
'refresh': translate('RemotePlugin.Mobile', 'Refresh'),
'blank': translate('RemotePlugin.Mobile', 'Blank'),
'show': translate('RemotePlugin.Mobile', 'Show'),
'prev': translate('RemotePlugin.Mobile', 'Prev'),
'next': translate('RemotePlugin.Mobile', 'Next'),
'text': translate('RemotePlugin.Mobile', 'Text'),
'show_alert': translate('RemotePlugin.Mobile', 'Show Alert'),
'go_live': translate('RemotePlugin.Mobile', 'Go Live'),
'add_to_service': translate('RemotePlugin.Mobile', 'Add To Service'),
'no_results': translate('RemotePlugin.Mobile', 'No Results'),
'options': translate('RemotePlugin.Mobile', 'Options')
}
def ready_read(self): def ready_read(self):
""" """
Data has been sent from the client. Respond to it Data has been sent from the client. Respond to it
@ -327,8 +353,11 @@ class HttpConnection(object):
if not path.startswith(self.parent.html_dir): if not path.startswith(self.parent.html_dir):
return HttpResponse(code=u'404 Not Found') return HttpResponse(code=u'404 Not Found')
ext = os.path.splitext(filename)[1] ext = os.path.splitext(filename)[1]
html = None
if ext == u'.html': if ext == u'.html':
mimetype = u'text/html' mimetype = u'text/html'
variables = self.template_vars
html = Template(filename=path, input_encoding=u'utf-8', output_encoding=u'utf-8').render(**variables)
elif ext == u'.css': elif ext == u'.css':
mimetype = u'text/css' mimetype = u'text/css'
elif ext == u'.js': elif ext == u'.js':
@ -343,9 +372,12 @@ class HttpConnection(object):
mimetype = u'text/plain' mimetype = u'text/plain'
file_handle = None file_handle = None
try: try:
file_handle = open(path, u'rb') if html:
log.debug(u'Opened %s' % path) content = html
content = file_handle.read() else:
file_handle = open(path, u'rb')
log.debug(u'Opened %s' % path)
content = file_handle.read()
except IOError: except IOError:
log.exception(u'Failed to open %s' % path) log.exception(u'Failed to open %s' % path)
return HttpResponse(code=u'404 Not Found') return HttpResponse(code=u'404 Not Found')
@ -460,7 +492,8 @@ class HttpConnection(object):
for plugin in self.parent.plugin.pluginManager.plugins: for plugin in self.parent.plugin.pluginManager.plugins:
if plugin.status == PluginStatus.Active and \ if plugin.status == PluginStatus.Active and \
plugin.mediaItem and plugin.mediaItem.hasSearch: plugin.mediaItem and plugin.mediaItem.hasSearch:
searches.append(plugin.name) searches.append([plugin.name, unicode(
plugin.textStrings[StringContent.Name][u'plural'])])
return HttpResponse( return HttpResponse(
json.dumps({u'results': {u'items': searches}}), json.dumps({u'results': {u'items': searches}}),
{u'Content-Type': u'application/json'}) {u'Content-Type': u'application/json'})
@ -476,7 +509,7 @@ class HttpConnection(object):
plugin = self.parent.plugin.pluginManager.get_plugin_by_name(type) plugin = self.parent.plugin.pluginManager.get_plugin_by_name(type)
if plugin.status == PluginStatus.Active and \ if plugin.status == PluginStatus.Active and \
plugin.mediaItem and plugin.mediaItem.hasSearch: plugin.mediaItem and plugin.mediaItem.hasSearch:
results =plugin.mediaItem.search(text) results = plugin.mediaItem.search(text)
else: else:
results = [] results = []
return HttpResponse( return HttpResponse(