Merge lp:~raoul-snyman/openlp/bug-790382 into lp:openlp

Proposed by Raoul Snyman
Status: Merged
Approved by: Tim Bentley
Approved revision: 1604
Merged at revision: 1599
Proposed branch: lp:~raoul-snyman/openlp/bug-790382
Merge into: lp:openlp
Diff against target: 403 lines (+117/-59)
5 files modified
openlp/plugins/remotes/html/index.html (+47/-29)
openlp/plugins/remotes/html/openlp.js (+19/-13)
openlp/plugins/remotes/html/stage.html (+4/-3)
openlp/plugins/remotes/html/stage.js (+6/-6)
openlp/plugins/remotes/lib/httpserver.py (+41/-8)
To merge this branch: bzr merge lp:~raoul-snyman/openlp/bug-790382
Reviewer Review Type Date Requested Status
Andreas Preikschat (community) Approve
Tim Bentley Approve
Jonathan Corwin (community) Approve
Review via email: mp+63027@code.launchpad.net

This proposal supersedes a proposal from 2011-05-31.

Description of the change

Added translation of the web remote interface by using a templating engine.

BEWARE: You need "Mako" installed!

To post a comment you must log in.
Revision history for this message
Tim Bentley (trb143) : Posted in a previous version of this proposal
review: Approve
Revision history for this message
Jonathan Corwin (j-corwin) wrote : Posted in a previous version of this proposal

Line 203, id is only an integer for songs/customs, it is a string for the other plugins.

review: Needs Fixing
Revision history for this message
Tim Bentley (trb143) wrote : Posted in a previous version of this proposal

Ok with the python stuff. Js looks ok. Do we need a new bug for packaging and mako?

review: Approve
Revision history for this message
Jonathan Corwin (j-corwin) wrote : Posted in a previous version of this proposal

Also 234/235: My fault I know, but the Unicode conversion of the file seems to have corrupted the non-ascii characters.

Revision history for this message
Jonathan Corwin (j-corwin) :
review: Approve
Revision history for this message
Tim Bentley (trb143) :
review: Approve
Revision history for this message
Andreas Preikschat (googol-deactivatedaccount) wrote :

Nice on!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'openlp/plugins/remotes/html/index.html'
2--- openlp/plugins/remotes/html/index.html 2011-05-24 20:47:05 +0000
3+++ openlp/plugins/remotes/html/index.html 2011-05-31 18:24:33 +0000
4@@ -27,91 +27,109 @@
5 -->
6 <head>
7 <meta charset="utf-8" />
8- <title>OpenLP 2.0 Remote</title>
9+ <title>${app_title}</title>
10 <link rel="stylesheet" href="/files/jquery.mobile.css" />
11 <link rel="stylesheet" href="/files/openlp.css" />
12 <script type="text/javascript" src="/files/jquery.js"></script>
13 <script type="text/javascript" src="/files/openlp.js"></script>
14 <script type="text/javascript" src="/files/jquery.mobile.js"></script>
15+ <script type="text/javascript">
16+ translationStrings = {
17+ "go_live": "${go_live}",
18+ "add_to_service": "${add_to_service}",
19+ "no_results": "${no_results}",
20+ "back": "${back}"
21+ }
22+ </script>
23 </head>
24 <body>
25 <div data-role="page" id="home">
26 <div data-role="header">
27- <h1>OpenLP 2.0 Remote</h1>
28+ <h1>${app_title}</h1>
29 </div>
30 <div data-role="content">
31 <div data-role="controlgroup">
32- <a href="#service-manager" data-role="button" data-icon="arrow-r" data-iconpos="right">Service Manager</a>
33- <a href="#slide-controller" data-role="button" data-icon="arrow-r" data-iconpos="right">Slide Controller</a>
34- <a href="#alerts" data-role="button" data-icon="arrow-r" data-iconpos="right">Alerts</a>
35- <a href="#search" data-role="button" data-icon="arrow-r" data-iconpos="right">Search</a>
36+ <a href="#service-manager" data-role="button" data-icon="arrow-r" data-iconpos="right">${service_manager}</a>
37+ <a href="#slide-controller" data-role="button" data-icon="arrow-r" data-iconpos="right">${slide_controller}</a>
38+ <a href="#alerts" data-role="button" data-icon="arrow-r" data-iconpos="right">${alerts}</a>
39+ <a href="#search" data-role="button" data-icon="arrow-r" data-iconpos="right">${search}</a>
40 </div>
41 </div>
42 </div>
43 <div data-role="page" id="service-manager">
44 <div data-role="header">
45- <a href="#" data-rel="back" data-icon="arrow-l">Back</a>
46- <h1>Service Manager</h1>
47- <a href="#" id="service-refresh" data-role="button" data-icon="refresh">Refresh</a>
48+ <a href="#" data-rel="back" data-icon="arrow-l">${back}</a>
49+ <h1>${service_manager}</h1>
50+ <a href="#" id="service-refresh" data-role="button" data-icon="refresh">${refresh}</a>
51 </div>
52 <div data-role="content">
53 <ul data-role="listview" data-inset="true">
54 </ul>
55 </div>
56 <div data-role="footer" data-theme="b" class="ui-bar">
57- <a href="#" id="service-blank" data-role="button" data-icon="blank">Blank</a>
58- <a href="#" id="service-unblank" data-role="button" data-icon="unblank">Show</a>
59- <a href="#" id="service-previous" data-role="button" data-icon="arrow-l">Prev</a>
60- <a href="#" id="service-next" data-role="button" data-icon="arrow-r" data-iconpos="right">Next</a>
61+ <a href="#" id="service-blank" data-role="button" data-icon="blank">${blank}</a>
62+ <a href="#" id="service-unblank" data-role="button" data-icon="unblank">${show}</a>
63+ <a href="#" id="service-previous" data-role="button" data-icon="arrow-l">${prev}</a>
64+ <a href="#" id="service-next" data-role="button" data-icon="arrow-r" data-iconpos="right">${next}</a>
65 </div>
66 </div>
67 <div data-role="page" id="slide-controller">
68 <div data-role="header">
69- <a href="#" data-rel="back" data-icon="arrow-l">Back</a>
70- <h1>Slide Controller</h1>
71- <a href="#" id="controller-refresh" data-role="button" data-icon="refresh">Refresh</a>
72+ <a href="#" data-rel="back" data-icon="arrow-l">${back}</a>
73+ <h1>${slide_controller}</h1>
74+ <a href="#" id="controller-refresh" data-role="button" data-icon="refresh">${refresh}</a>
75 </div>
76 <div data-role="content">
77 <ul data-role="listview" data-inset="true">
78 </ul>
79 </div>
80 <div data-role="footer" data-theme="b" class="ui-bar">
81- <a href="#" id="controller-blank" data-role="button" data-icon="blank">Blank</a>
82- <a href="#" id="controller-unblank" data-role="button" data-icon="unblank">Show</a>
83- <a href="#" id="controller-previous" data-role="button" data-icon="arrow-l">Prev</a>
84- <a href="#" id="controller-next" data-role="button" data-icon="arrow-r" data-iconpos="right">Next</a>
85+ <a href="#" id="controller-blank" data-role="button" data-icon="blank">${blank}</a>
86+ <a href="#" id="controller-unblank" data-role="button" data-icon="unblank">${show}</a>
87+ <a href="#" id="controller-previous" data-role="button" data-icon="arrow-l">${prev}</a>
88+ <a href="#" id="controller-next" data-role="button" data-icon="arrow-r" data-iconpos="right">${next}</a>
89 </div>
90 </div>
91 <div data-role="page" id="alerts">
92 <div data-role="header">
93- <a href="#" data-rel="back" data-icon="arrow-l">Back</a>
94- <h1>Alerts</h1>
95+ <a href="#" data-rel="back" data-icon="arrow-l">${back}</a>
96+ <h1>${alerts}</h1>
97 </div>
98 <div data-role="content">
99 <div data-role="fieldcontain">
100- <label for="alert-text">Text:</label>
101+ <label for="alert-text">${text}:</label>
102 <input type="text" name="alert-text" id="alert-text" value="" />
103 </div>
104- <a href="#" id="alert-submit" data-role="button">Show Alert</a>
105+ <a href="#" id="alert-submit" data-role="button">${show_alert}</a>
106 </div>
107 </div>
108 <div data-role="page" id="search">
109 <div data-role="header">
110- <a href="#" data-rel="back" data-icon="arrow-l">Back</a>
111- <h1>Search</h1>
112+ <a href="#" data-rel="back" data-icon="arrow-l">${back}</a>
113+ <h1>${search}</h1>
114 </div>
115 <div data-role="content">
116 <div data-role="fieldcontain">
117- <label for="search-plugin">Search:</label>
118+ <label for="search-plugin">${search}:</label>
119 <select name="search-plugin" id="search-plugin" data-native-menu="false"></select>
120 </div>
121 <div data-role="fieldcontain">
122- <label for="search-text">Text:</label>
123+ <label for="search-text">${text}:</label>
124 <input type="search" name="search-text" id="search-text" value="" />
125 </div>
126- <a href="#" id="search-submit" data-role="button">Search</a>
127+ <a href="#" id="search-submit" data-role="button">${search}</a>
128 <ul data-role="listview" data-inset="true">
129 </div>
130 </div>
131+<div data-role="page" id="options">
132+ <div data-role="header" data-position="inline" data-theme="b">
133+ <h1>${options}</h1>
134+ </div>
135+ <div data-role="content">
136+ <input type="hidden" id="selected-item" value="" />
137+ <a href="#" id="go-live" data-role="button">${go_live}</a>
138+ <a href="#" id="add-to-service" data-role="button">${add_to_service}</a>
139+ </div>
140+</div>
141 </body>
142 </html>
143
144=== modified file 'openlp/plugins/remotes/html/json2.js' (properties changed: +x to -x)
145=== modified file 'openlp/plugins/remotes/html/openlp.js'
146--- openlp/plugins/remotes/html/openlp.js 2011-05-24 20:47:05 +0000
147+++ openlp/plugins/remotes/html/openlp.js 2011-05-31 18:24:33 +0000
148@@ -47,7 +47,7 @@
149 var select = $("#search-plugin");
150 select.html("");
151 $.each(data.results.items, function (idx, value) {
152- select.append("<option value='" + value + "'>" + value + "</option>");
153+ select.append("<option value='" + value[0] + "'>" + value[1] + "</option>");
154 });
155 select.selectmenu("refresh");
156 }
157@@ -215,16 +215,15 @@
158 var ul = $("#search > div[data-role=content] > ul[data-role=listview]");
159 ul.html("");
160 if (data.results.items.length == 0) {
161- var li = $("<li data-icon=\"false\">").text('No results');
162+ var li = $("<li data-icon=\"false\">").text(translationStrings["no_results"]);
163 ul.append(li);
164 }
165 else {
166 $.each(data.results.items, function (idx, value) {
167- var item = $("<li>").text(value[1]);
168- var golive = $("<a href=\"#\">Go Live</a>").attr("value", value[0]).click(OpenLP.goLive);
169- var additem = $("<a href=\"#\">Add To Service</a>").attr("value", value[0]).click(OpenLP.addToService);
170- item.append($("<ul>").append($("<li>").append(golive)).append($("<li>").append(additem)));
171- ul.append(item);
172+ ul.append($("<li>").append($("<a>").attr("href", "#options")
173+ .attr("data-rel", "dialog").attr("data-transition", "pop")
174+ .attr("value", value[0]).click(OpenLP.showOptions)
175+ .text(value[1])));
176 });
177 }
178 ul.listview("refresh");
179@@ -232,19 +231,23 @@
180 );
181 return false;
182 },
183+ showOptions: function (event) {
184+ var element = OpenLP.getElement(event);
185+ console.log(element);
186+ $("#selected-item").val(element.attr("value"));
187+ },
188 goLive: function (event) {
189- var item = OpenLP.getElement(event);
190- var id = item.attr("value");
191+ var id = $("#selected-item").val();
192 var text = JSON.stringify({"request": {"id": id}});
193 $.getJSON(
194 "/api/" + $("#search-plugin").val() + "/live",
195- {"data": text})
196- $.mobile.changePage("slide-controller");
197+ {"data": text}
198+ );
199+ $.mobile.changePage("#slide-controller");
200 return false;
201 },
202 addToService: function (event) {
203- var item = OpenLP.getElement(event);
204- var id = item.attr("value");
205+ var id = $("#selected-item").val();
206 var text = JSON.stringify({"request": {"id": id}});
207 $.getJSON(
208 "/api/" + $("#search-plugin").val() + "/add",
209@@ -253,6 +256,7 @@
210 history.back();
211 }
212 );
213+ $("#options").dialog("close");
214 return false;
215 }
216 }
217@@ -274,6 +278,8 @@
218 $("#alert-submit").live("click", OpenLP.showAlert);
219 // Search
220 $("#search-submit").live("click", OpenLP.search);
221+$("#go-live").live("click", OpenLP.goLive);
222+$("#add-to-service").live("click", OpenLP.addToService);
223 // Poll the server twice a second to get any updates.
224 OpenLP.getSearchablePlugins();
225 $.ajaxSetup({ cache: false });
226
227=== modified file 'openlp/plugins/remotes/html/stage.html'
228--- openlp/plugins/remotes/html/stage.html 2011-05-24 20:47:05 +0000
229+++ openlp/plugins/remotes/html/stage.html 2011-05-31 18:24:33 +0000
230@@ -6,8 +6,8 @@
231 # --------------------------------------------------------------------------- #
232 # Copyright (c) 2008-2011 Raoul Snyman #
233 # Portions copyright (c) 2008-2011 Tim Bentley, Jonathan Corwin, Michael #
234-# Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, Armin Köhler, #
235-# Andreas Preikschat, Mattias Põldaru, Christian Richter, Philip Ridout, #
236+# Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, Armin Köhler, #
237+# Andreas Preikschat, Mattias Põldaru, Christian Richter, Philip Ridout, #
238 # Jeffrey Smith, Maikel Stuivenberg, Martin Thompson, Jon Tibble, Frode #
239 # Woldsund #
240 # --------------------------------------------------------------------------- #
241@@ -27,12 +27,13 @@
242 -->
243 <head>
244 <meta charset="utf-8" />
245- <title>OpenLP 2.0 Stage View</title>
246+ <title>${stage_title}</title>
247 <link rel="stylesheet" href="/files/stage.css" />
248 <script type="text/javascript" src="/files/jquery.js"></script>
249 <script type="text/javascript" src="/files/stage.js"></script>
250 </head>
251 <body>
252+<input type="hidden" id="next-text" value="${next}" />
253 <div id="right">
254 <div id="clock"></div>
255 <div id="notes"></div>
256
257=== modified file 'openlp/plugins/remotes/html/stage.js'
258--- openlp/plugins/remotes/html/stage.js 2011-05-24 20:47:05 +0000
259+++ openlp/plugins/remotes/html/stage.js 2011-05-31 18:24:33 +0000
260@@ -100,24 +100,24 @@
261 $("#tag" + OpenLP.currentTags[OpenLP.currentSlide]).addClass("currenttag");
262 var slide = OpenLP.currentSlides[OpenLP.currentSlide];
263 var text = slide["text"];
264- text = text.replace(/\n/g, '<br />');
265+ text = text.replace(/\n/g, "<br />");
266 $("#currentslide").html(text);
267 text = "";
268 if (OpenLP.currentSlide < OpenLP.currentSlides.length - 1) {
269 for (var idx = OpenLP.currentSlide + 1; idx < OpenLP.currentSlides.length; idx++) {
270 if (OpenLP.currentTags[idx] != OpenLP.currentTags[idx - 1])
271- text = text + '<p class="nextslide">';
272+ text = text + "<p class=\"nextslide\">";
273 text = text + OpenLP.currentSlides[idx]["text"];
274 if (OpenLP.currentTags[idx] != OpenLP.currentTags[idx - 1])
275- text = text + '</p>';
276+ text = text + "</p>";
277 else
278- text = text + '<br />';
279+ text = text + "<br />";
280 }
281- text = text.replace(/\n/g, '<br />');
282+ text = text.replace(/\n/g, "<br />");
283 $("#nextslide").html(text);
284 }
285 else {
286- text = '<p class="nextslide">Next: ' + OpenLP.nextSong + '</p>';
287+ text = "<p class=\"nextslide\">" + $("#next-text").val() + ": " + OpenLP.nextSong + "</p>";
288 $("#nextslide").html(text);
289 }
290 },
291
292=== modified file 'openlp/plugins/remotes/lib/httpserver.py'
293--- openlp/plugins/remotes/lib/httpserver.py 2011-05-28 09:53:37 +0000
294+++ openlp/plugins/remotes/lib/httpserver.py 2011-05-31 18:24:33 +0000
295@@ -115,7 +115,6 @@
296 import os
297 import urlparse
298 import re
299-from pprint import pformat
300
301 try:
302 import json
303@@ -123,10 +122,11 @@
304 import simplejson as json
305
306 from PyQt4 import QtCore, QtNetwork
307+from mako.template import Template
308
309-from openlp.core.lib import Receiver, PluginStatus
310+from openlp.core.lib import Receiver, PluginStatus, StringContent
311 from openlp.core.ui import HideMode
312-from openlp.core.utils import AppLocation
313+from openlp.core.utils import AppLocation, translate
314
315 log = logging.getLogger(__name__)
316
317@@ -261,6 +261,7 @@
318 self.ready_read)
319 QtCore.QObject.connect(self.socket, QtCore.SIGNAL(u'disconnected()'),
320 self.disconnected)
321+ self.translate()
322
323 def _get_service_items(self):
324 service_items = []
325@@ -280,6 +281,31 @@
326 })
327 return service_items
328
329+ def translate(self):
330+ """
331+ Translate various strings in the mobile app.
332+ """
333+ self.template_vars = {
334+ 'app_title': translate('RemotePlugin.Mobile', 'OpenLP 2.0 Remote'),
335+ 'stage_title': translate('RemotePlugin.Mobile', 'OpenLP 2.0 Stage View'),
336+ 'service_manager': translate('RemotePlugin.Mobile', 'Service Manager'),
337+ 'slide_controller': translate('RemotePlugin.Mobile', 'Slide Controller'),
338+ 'alerts': translate('RemotePlugin.Mobile', 'Alerts'),
339+ 'search': translate('RemotePlugin.Mobile', 'Search'),
340+ 'back': translate('RemotePlugin.Mobile', 'Back'),
341+ 'refresh': translate('RemotePlugin.Mobile', 'Refresh'),
342+ 'blank': translate('RemotePlugin.Mobile', 'Blank'),
343+ 'show': translate('RemotePlugin.Mobile', 'Show'),
344+ 'prev': translate('RemotePlugin.Mobile', 'Prev'),
345+ 'next': translate('RemotePlugin.Mobile', 'Next'),
346+ 'text': translate('RemotePlugin.Mobile', 'Text'),
347+ 'show_alert': translate('RemotePlugin.Mobile', 'Show Alert'),
348+ 'go_live': translate('RemotePlugin.Mobile', 'Go Live'),
349+ 'add_to_service': translate('RemotePlugin.Mobile', 'Add To Service'),
350+ 'no_results': translate('RemotePlugin.Mobile', 'No Results'),
351+ 'options': translate('RemotePlugin.Mobile', 'Options')
352+ }
353+
354 def ready_read(self):
355 """
356 Data has been sent from the client. Respond to it
357@@ -327,8 +353,11 @@
358 if not path.startswith(self.parent.html_dir):
359 return HttpResponse(code=u'404 Not Found')
360 ext = os.path.splitext(filename)[1]
361+ html = None
362 if ext == u'.html':
363 mimetype = u'text/html'
364+ variables = self.template_vars
365+ html = Template(filename=path, input_encoding=u'utf-8', output_encoding=u'utf-8').render(**variables)
366 elif ext == u'.css':
367 mimetype = u'text/css'
368 elif ext == u'.js':
369@@ -343,9 +372,12 @@
370 mimetype = u'text/plain'
371 file_handle = None
372 try:
373- file_handle = open(path, u'rb')
374- log.debug(u'Opened %s' % path)
375- content = file_handle.read()
376+ if html:
377+ content = html
378+ else:
379+ file_handle = open(path, u'rb')
380+ log.debug(u'Opened %s' % path)
381+ content = file_handle.read()
382 except IOError:
383 log.exception(u'Failed to open %s' % path)
384 return HttpResponse(code=u'404 Not Found')
385@@ -460,7 +492,8 @@
386 for plugin in self.parent.plugin.pluginManager.plugins:
387 if plugin.status == PluginStatus.Active and \
388 plugin.mediaItem and plugin.mediaItem.hasSearch:
389- searches.append(plugin.name)
390+ searches.append([plugin.name, unicode(
391+ plugin.textStrings[StringContent.Name][u'plural'])])
392 return HttpResponse(
393 json.dumps({u'results': {u'items': searches}}),
394 {u'Content-Type': u'application/json'})
395@@ -476,7 +509,7 @@
396 plugin = self.parent.plugin.pluginManager.get_plugin_by_name(type)
397 if plugin.status == PluginStatus.Active and \
398 plugin.mediaItem and plugin.mediaItem.hasSearch:
399- results =plugin.mediaItem.search(text)
400+ results = plugin.mediaItem.search(text)
401 else:
402 results = []
403 return HttpResponse(