Merge lp:~raoul-snyman/openlp/bug-790382 into lp:openlp
- bug-790382
- Merge into trunk
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 | ||||
Related bugs: |
|
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.
Commit message
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 | # |
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( |
Line 203, id is only an integer for songs/customs, it is a string for the other plugins.