1 | #! /usr/bin/python3 |
---|
2 | # -*- coding:utf-8 -*- |
---|
3 | # |
---|
4 | # Copyright 2012-2013 "Korora Project" <dev@kororaproject.org> |
---|
5 | # Copyright 2013 "Manjaro Linux" <support@manjaro.org> |
---|
6 | # Copyright 2014 Antergos |
---|
7 | # Copyright 2015-2016 Martin Wimpress <code@flexion.org> |
---|
8 | # Copyright 2015-2016 Luke Horwell <lukehorwell37+code@gmail.com> |
---|
9 | # |
---|
10 | # Ubuntu MATE Welcome is free software: you can redistribute it and/or modify |
---|
11 | # it under the temms of the GNU General Public License as published by |
---|
12 | # the Free Software Foundation, either version 3 of the License, or |
---|
13 | # (at your option) any later version. |
---|
14 | # |
---|
15 | # Ubuntu MATE Welcome is distributed in the hope that it will be useful, |
---|
16 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
17 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
---|
18 | # GNU General Public License for more details. |
---|
19 | # |
---|
20 | # You should have received a copy of the GNU General Public License |
---|
21 | # along with Ubuntu MATE Welcome. If not, see <http://www.gnu.org/licenses/>. |
---|
22 | # |
---|
23 | |
---|
24 | """ Welcome screen for LliureX MATE """ |
---|
25 | |
---|
26 | import gi |
---|
27 | gi.require_version("Gdk", "3.0") |
---|
28 | gi.require_version("Gtk", "3.0") |
---|
29 | gi.require_version("Notify", "0.7") |
---|
30 | gi.require_version("WebKit", "3.0") |
---|
31 | |
---|
32 | import apt |
---|
33 | import errno |
---|
34 | import gettext |
---|
35 | import glob |
---|
36 | import inspect |
---|
37 | import json |
---|
38 | import locale |
---|
39 | import logging |
---|
40 | import os |
---|
41 | import platform |
---|
42 | import random |
---|
43 | import shutil |
---|
44 | import signal |
---|
45 | import socket |
---|
46 | import subprocess |
---|
47 | import sys |
---|
48 | import tempfile |
---|
49 | import urllib.request |
---|
50 | import webbrowser |
---|
51 | import xmlrpc.client |
---|
52 | import ssl |
---|
53 | |
---|
54 | import urllib.error |
---|
55 | import urllib.parse |
---|
56 | import urllib.request |
---|
57 | from aptdaemon.client import AptClient |
---|
58 | from aptdaemon.gtk3widgets import AptErrorDialog, AptConfirmDialog, \ |
---|
59 | AptProgressDialog |
---|
60 | import aptdaemon.errors |
---|
61 | |
---|
62 | from aptdaemon.enums import * |
---|
63 | from gi.repository import GLib, Gio, GObject, Gdk, Gtk, Notify, WebKit |
---|
64 | from ctypes import cdll, byref, create_string_buffer |
---|
65 | from threading import Thread |
---|
66 | from getpass import getuser |
---|
67 | |
---|
68 | # i18n - if no translation is available use the inline strings |
---|
69 | # t = gettext.translation("lliurex-mate-welcome", "./locale", fallback=True) |
---|
70 | t = gettext.translation("lliurex-mate-welcome", "/usr/share/locale", fallback=True) |
---|
71 | _ = t.gettext |
---|
72 | |
---|
73 | |
---|
74 | #Issues with the certificate of localhost |
---|
75 | ssl._create_default_https_context = ssl._create_unverified_context |
---|
76 | client=xmlrpc.client.ServerProxy("https://localhost:9779") |
---|
77 | |
---|
78 | def goodbye(a=None, b=None): |
---|
79 | # NOTE: _a_ and _b_ are passed via the close window 'delete-event'. |
---|
80 | ''' Closing the program ''' |
---|
81 | |
---|
82 | # Refuse to quit if operations are in progress. |
---|
83 | if dynamicapps.operations_busy: |
---|
84 | print('[Welcome] WARNING: Software changes are in progress!') |
---|
85 | title = _("Software Boutique") |
---|
86 | text_busy = _('Software changes are in progress. Please allow them to complete before closing Welcome.') |
---|
87 | ok_label = _("OK") |
---|
88 | messagebox = subprocess.Popen(['zenity', |
---|
89 | '--error', |
---|
90 | '--title=' + title, |
---|
91 | "--text=" + text_busy, |
---|
92 | "--ok-label=" + ok_label, |
---|
93 | '--window-icon=error', |
---|
94 | '--timeout=9']) |
---|
95 | return 1 |
---|
96 | |
---|
97 | else: |
---|
98 | print('[Welcome] Application Closed.') |
---|
99 | Gtk.main_quit() |
---|
100 | # Be quite forceful, particularly those child screenshot windows. |
---|
101 | exit() |
---|
102 | |
---|
103 | |
---|
104 | def set_proc_title(name=None): |
---|
105 | '''Set the process title''' |
---|
106 | |
---|
107 | if not name: |
---|
108 | name = os.path.basename(sys.argv[0]) |
---|
109 | |
---|
110 | libc = cdll.LoadLibrary('libc.so.6') |
---|
111 | buff = create_string_buffer(len(name)+1) |
---|
112 | buff.value = name.encode("UTF-8") |
---|
113 | ret = libc.prctl(15, byref(buff), 0, 0, 0) |
---|
114 | |
---|
115 | if ret != 0: |
---|
116 | print("Failed to set process title") |
---|
117 | |
---|
118 | return ret |
---|
119 | |
---|
120 | |
---|
121 | class SimpleApt(object): |
---|
122 | def __init__(self, packages, action): |
---|
123 | self._timeout = 100 |
---|
124 | self.packages = packages |
---|
125 | self.action = action |
---|
126 | self.source_to_update = None |
---|
127 | self.update_cache = False |
---|
128 | self.loop = GLib.MainLoop() |
---|
129 | self.client = AptClient() |
---|
130 | |
---|
131 | def on_error(self, error): |
---|
132 | dynamicapps.operations_busy = False |
---|
133 | if isinstance(error, aptdaemon.errors.NotAuthorizedError): |
---|
134 | # Silently ignore auth failures |
---|
135 | return |
---|
136 | elif not isinstance(error, aptdaemon.errors.TransactionFailed): |
---|
137 | # Catch internal errors of the client |
---|
138 | error = aptdaemon.errors.TransactionFailed(ERROR_UNKNOWN, |
---|
139 | str(error)) |
---|
140 | error_dialog = AptErrorDialog(error) |
---|
141 | error_dialog.run() |
---|
142 | error_dialog.hide() |
---|
143 | |
---|
144 | def on_finished_fix_incomplete_install(self, transaction, status): |
---|
145 | dynamicapps.operations_busy = False |
---|
146 | self.loop.quit() |
---|
147 | if status == 'exit-success': |
---|
148 | Notify.init(_('Fixing incomplete install succeeded')) |
---|
149 | apt_notify=Notify.Notification.new(_('Successfully fixed an incomplete install.'), _('Fixing the incomplete install was successful.'), 'dialog-information') |
---|
150 | apt_notify.show() |
---|
151 | return True |
---|
152 | else: |
---|
153 | Notify.init(_('Fixing incomplete install failed')) |
---|
154 | apt_notify=Notify.Notification.new(_('Failed to fix incomplete install.'), _('Fixing the incomplete install failed.'), 'dialog-error') |
---|
155 | apt_notify.show() |
---|
156 | return False |
---|
157 | |
---|
158 | def on_finished_fix_broken_depends(self, transaction, status): |
---|
159 | dynamicapps.operations_busy = False |
---|
160 | self.loop.quit() |
---|
161 | if status == 'exit-success': |
---|
162 | Notify.init(_('Fixing broken dependencies succeeded')) |
---|
163 | apt_notify=Notify.Notification.new(_('Successfully fixed broken dependencies.'), _('Fixing the broken dependencies was successful.'), 'dialog-information') |
---|
164 | apt_notify.show() |
---|
165 | return True |
---|
166 | else: |
---|
167 | Notify.init(_('Fixing broken dependencies failed')) |
---|
168 | apt_notify=Notify.Notification.new(_('Failed to fix broken dependencies.'), _('Fixing the broken dependencies failed.'), 'dialog-error') |
---|
169 | apt_notify.show() |
---|
170 | return False |
---|
171 | |
---|
172 | def on_finished_update(self, transaction, status): |
---|
173 | dynamicapps.operations_busy = False |
---|
174 | # If the action is only to update do not display notifcations |
---|
175 | if self.action == 'update': |
---|
176 | self.loop.quit() |
---|
177 | if status == 'exit-success': |
---|
178 | return True |
---|
179 | else: |
---|
180 | return False |
---|
181 | elif self.action == 'install': |
---|
182 | if status != 'exit-success': |
---|
183 | self.do_notify(status) |
---|
184 | self.loop.quit() |
---|
185 | return False |
---|
186 | |
---|
187 | GLib.timeout_add(self._timeout,self.do_install) |
---|
188 | return True |
---|
189 | elif self.action == 'upgrade': |
---|
190 | if status != 'exit-success': |
---|
191 | self.do_notify(status) |
---|
192 | self.loop.quit() |
---|
193 | return False |
---|
194 | |
---|
195 | GLib.timeout_add(self._timeout,self.do_upgrade) |
---|
196 | return True |
---|
197 | |
---|
198 | def on_finished_install(self, transaction, status): |
---|
199 | dynamicapps.operations_busy = False |
---|
200 | self.loop.quit() |
---|
201 | if status != 'exit-success': |
---|
202 | return False |
---|
203 | else: |
---|
204 | self.do_notify(status) |
---|
205 | |
---|
206 | def on_finished_remove(self, transaction, status): |
---|
207 | dynamicapps.operations_busy = False |
---|
208 | self.loop.quit() |
---|
209 | if status != 'exit-success': |
---|
210 | return False |
---|
211 | else: |
---|
212 | self.do_notify(status) |
---|
213 | |
---|
214 | def on_finished_upgrade(self, transaction, status): |
---|
215 | dynamicapps.operations_busy = False |
---|
216 | self.loop.quit() |
---|
217 | if status != 'exit-success': |
---|
218 | return False |
---|
219 | else: |
---|
220 | self.do_notify(status) |
---|
221 | |
---|
222 | def do_notify(self, status): |
---|
223 | print('Status: ' + status) |
---|
224 | if self.action == 'install': |
---|
225 | title = _('Install') |
---|
226 | noun = _('Installation of ') |
---|
227 | action = _('installed.') |
---|
228 | elif self.action == 'remove': |
---|
229 | title = _('Remove') |
---|
230 | noun = _('Removal of ') |
---|
231 | action = _('removed.') |
---|
232 | elif self.action == 'upgrade': |
---|
233 | title = _('Upgrade') |
---|
234 | noun = _('Upgrade of ') |
---|
235 | action = _('upgraded.') |
---|
236 | |
---|
237 | # Do not show notifications when updating the cache |
---|
238 | if self.action != 'update': |
---|
239 | if status == 'exit-success': |
---|
240 | Notify.init(title + ' ' + _('complete')) |
---|
241 | apt_notify=Notify.Notification.new(title + ' ' + _('complete'), ', '.join(self.packages) + ' ' + _('has been successfully ') +action, 'dialog-information') |
---|
242 | elif status == 'exit-cancelled': |
---|
243 | Notify.init(title + ' ' + _('cancelled')) |
---|
244 | apt_notify=Notify.Notification.new(title + ' ' + _('cancelled'), noun + ', '.join(self.packages) + ' ' + _('was cancelled.'), 'dialog-information') |
---|
245 | else: |
---|
246 | Notify.init(title + ' ' + _('failed')) |
---|
247 | apt_notify=Notify.Notification.new(title + ' ' + _('failed'), noun + ', '.join(self.packages) + ' ' + _('failed.'), 'dialog-error') |
---|
248 | |
---|
249 | apt_notify.show() |
---|
250 | |
---|
251 | def do_fix_incomplete_install(self): |
---|
252 | dynamicapps.operations_busy = True |
---|
253 | # Corresponds to: dpkg --configure -a |
---|
254 | apt_fix_incomplete = self.client.fix_incomplete_install() |
---|
255 | apt_fix_incomplete.connect("finished",self.on_finished_fix_incomplete_install) |
---|
256 | |
---|
257 | fix_incomplete_dialog = AptProgressDialog(apt_fix_incomplete) |
---|
258 | fix_incomplete_dialog.run(close_on_finished=True, show_error=True, |
---|
259 | reply_handler=lambda: True, |
---|
260 | error_handler=self.on_error, |
---|
261 | ) |
---|
262 | return False |
---|
263 | dynamicapps.operations_busy = False |
---|
264 | |
---|
265 | def do_fix_broken_depends(self): |
---|
266 | dynamicapps.operations_busy = True |
---|
267 | # Corresponds to: apt-get --fix-broken install |
---|
268 | apt_fix_broken = self.client.fix_broken_depends() |
---|
269 | apt_fix_broken.connect("finished",self.on_finished_fix_broken_depends) |
---|
270 | |
---|
271 | fix_broken_dialog = AptProgressDialog(apt_fix_broken) |
---|
272 | fix_broken_dialog.run(close_on_finished=True, show_error=True, |
---|
273 | reply_handler=lambda: True, |
---|
274 | error_handler=self.on_error, |
---|
275 | ) |
---|
276 | return False |
---|
277 | dynamicapps.operations_busy = False |
---|
278 | |
---|
279 | def do_update(self): |
---|
280 | if self.source_to_update: |
---|
281 | apt_update = self.client.update_cache(self.source_to_update) |
---|
282 | else: |
---|
283 | apt_update = self.client.update_cache() |
---|
284 | apt_update.connect("finished",self.on_finished_update) |
---|
285 | |
---|
286 | update_dialog = AptProgressDialog(apt_update) |
---|
287 | update_dialog.run(close_on_finished=True, show_error=True, |
---|
288 | reply_handler=lambda: True, |
---|
289 | error_handler=self.on_error, |
---|
290 | ) |
---|
291 | return False |
---|
292 | |
---|
293 | def do_install(self): |
---|
294 | apt_install = self.client.install_packages(self.packages) |
---|
295 | apt_install.connect("finished", self.on_finished_install) |
---|
296 | |
---|
297 | install_dialog = AptProgressDialog(apt_install) |
---|
298 | install_dialog.run(close_on_finished=True, show_error=True, |
---|
299 | reply_handler=lambda: True, |
---|
300 | error_handler=self.on_error, |
---|
301 | ) |
---|
302 | return False |
---|
303 | |
---|
304 | def do_remove(self): |
---|
305 | apt_remove = self.client.remove_packages(self.packages) |
---|
306 | apt_remove.connect("finished", self.on_finished_remove) |
---|
307 | |
---|
308 | remove_dialog = AptProgressDialog(apt_remove) |
---|
309 | remove_dialog.run(close_on_finished=True, show_error=True, |
---|
310 | reply_handler=lambda: True, |
---|
311 | error_handler=self.on_error, |
---|
312 | ) |
---|
313 | return False |
---|
314 | |
---|
315 | def do_upgrade(self): |
---|
316 | apt_upgrade = self.client.upgrade_system(True) |
---|
317 | apt_upgrade.connect("finished", self.on_finished_upgrade) |
---|
318 | |
---|
319 | upgrade_dialog = AptProgressDialog(apt_upgrade) |
---|
320 | upgrade_dialog.run(close_on_finished=True, show_error=True, |
---|
321 | reply_handler=lambda: True, |
---|
322 | error_handler=self.on_error, |
---|
323 | ) |
---|
324 | return False |
---|
325 | |
---|
326 | def install_packages(self): |
---|
327 | dynamicapps.operations_busy = True |
---|
328 | if self.update_cache: |
---|
329 | GLib.timeout_add(self._timeout,self.do_update) |
---|
330 | else: |
---|
331 | GLib.timeout_add(self._timeout,self.do_install) |
---|
332 | self.loop.run() |
---|
333 | dynamicapps.operations_busy = False |
---|
334 | |
---|
335 | def remove_packages(self): |
---|
336 | dynamicapps.operations_busy = True |
---|
337 | GLib.timeout_add(self._timeout,self.do_remove) |
---|
338 | self.loop.run() |
---|
339 | dynamicapps.operations_busy = False |
---|
340 | |
---|
341 | def upgrade_packages(self): |
---|
342 | dynamicapps.operations_busy = True |
---|
343 | if self.update_cache: |
---|
344 | GLib.timeout_add(self._timeout,self.do_update) |
---|
345 | else: |
---|
346 | GLib.timeout_add(self._timeout,self.do_upgrade) |
---|
347 | self.loop.run() |
---|
348 | dynamicapps.operations_busy = False |
---|
349 | |
---|
350 | def fix_incomplete_install(self): |
---|
351 | dynamicapps.operations_busy = True |
---|
352 | GLib.timeout_add(self._timeout,self.do_fix_incomplete_install) |
---|
353 | self.loop.run() |
---|
354 | dynamicapps.operations_busy = False |
---|
355 | |
---|
356 | def fix_broken_depends(self): |
---|
357 | dynamicapps.operations_busy = True |
---|
358 | GLib.timeout_add(self._timeout,self.do_fix_broken_depends) |
---|
359 | self.loop.run() |
---|
360 | dynamicapps.operations_busy = False |
---|
361 | |
---|
362 | def update_repos(): |
---|
363 | transaction = SimpleApt('', 'update') |
---|
364 | transaction.update_cache = True |
---|
365 | transaction.do_update() |
---|
366 | |
---|
367 | def fix_incomplete_install(): |
---|
368 | transaction = SimpleApt('', 'fix-incomplete-install') |
---|
369 | transaction.fix_incomplete_install() |
---|
370 | |
---|
371 | def fix_broken_depends(): |
---|
372 | transaction = SimpleApt('', 'fix-broken-depends') |
---|
373 | transaction.fix_broken_depends() |
---|
374 | |
---|
375 | def mkdir_p(path): |
---|
376 | try: |
---|
377 | os.makedirs(path) |
---|
378 | except OSError as exc: # Python >2.5 |
---|
379 | if exc.errno == errno.EEXIST and os.path.isdir(path): |
---|
380 | pass |
---|
381 | else: |
---|
382 | raise |
---|
383 | |
---|
384 | def get_aacs_db(): |
---|
385 | home_dir = GLib.get_home_dir() |
---|
386 | key_url = 'http://www.labdv.com/aacs/KEYDB.cfg' |
---|
387 | key_db = os.path.join(home_dir, '.config', 'aacs', 'KEYDB.cfg') |
---|
388 | mkdir_p(os.path.join(home_dir, '.config', 'aacs')) |
---|
389 | print('[AACS] Getting ' + key_url + ' and saving as ' + key_db) |
---|
390 | |
---|
391 | # Download the file from `key_url` and save it locally under `file_name`: |
---|
392 | try: |
---|
393 | with urllib.request.urlopen(key_url) as response, open(key_db, 'wb') as out_file: |
---|
394 | data = response.read() # a `bytes` object |
---|
395 | out_file.write(data) |
---|
396 | |
---|
397 | Notify.init(_('Blu-ray AACS database install succeeded')) |
---|
398 | aacs_notify=Notify.Notification.new(_('Successfully installed the Blu-ray AACS database.'), _('Installation of the Blu-ray AACS database was successful.'), 'dialog-information') |
---|
399 | aacs_notify.show() |
---|
400 | except: |
---|
401 | Notify.init(_('Blu-ray AACS database install failed')) |
---|
402 | aacs_notify=Notify.Notification.new(_('Failed to install the Blu-ray AACS database.'), _('Installation of the Blu-ray AACS database failed.'), 'dialog-error') |
---|
403 | aacs_notify.show() |
---|
404 | |
---|
405 | class PreInstallation(object): |
---|
406 | # |
---|
407 | # See the JSON Structure in the `DynamicApps` class on |
---|
408 | # how to specify pre-configuration actions in `applications.json` |
---|
409 | # |
---|
410 | |
---|
411 | def __init__(self): |
---|
412 | # Always ensure we have the correct variables. |
---|
413 | self.os_version = client.lliurex_version("", "LliurexVersion")[1].split(", ")[-1] |
---|
414 | self.codename = platform.dist()[2] |
---|
415 | arg.print_verbose("Pre-Install", "System is running Ubuntu " + self.os_version + " (" + self.codename + ")") |
---|
416 | |
---|
417 | def process_packages(self, program_id, action): |
---|
418 | simulating = arg.simulate_software_changes |
---|
419 | print(' ') |
---|
420 | |
---|
421 | # Get category for this program, which can be used to retrieve data later. |
---|
422 | category = dynamicapps.get_attribute_for_app(program_id, 'category') |
---|
423 | |
---|
424 | try: |
---|
425 | preconfig = dynamicapps.index[category][program_id]['pre-install'] |
---|
426 | except: |
---|
427 | print('[Pre-Install] Missing pre-configuration data for "' + program_id + '". Refusing to continue.') |
---|
428 | return |
---|
429 | |
---|
430 | try: |
---|
431 | if action == 'install': |
---|
432 | packages = dynamicapps.index[category][program_id]['install-packages'] |
---|
433 | print('[Apps] Packages to be installed:\n ' + packages) |
---|
434 | elif action == 'remove': |
---|
435 | packages = dynamicapps.index[category][program_id]['remove-packages'] |
---|
436 | print('[Apps] Packages to be removed:\n ' + packages) |
---|
437 | elif action == 'upgrade': |
---|
438 | packages = dynamicapps.index[category][program_id]['upgrade-packages'] |
---|
439 | print('[Apps] Packages to be upgraded:\n ' + packages) |
---|
440 | else: |
---|
441 | print('[Apps] ERROR: Invalid action was requested.') |
---|
442 | return |
---|
443 | except: |
---|
444 | print('[Apps] ERROR: No packages retrieved for requested action.') |
---|
445 | return |
---|
446 | |
---|
447 | # Validate that we have packages to work with. |
---|
448 | if len(packages): |
---|
449 | packages = packages.split(',') |
---|
450 | else: |
---|
451 | print('[Apps] ERROR: No package(s) supplied for "' + program_id + '".') |
---|
452 | return |
---|
453 | transaction = SimpleApt(packages, action) |
---|
454 | |
---|
455 | # Function to run privileged commands. |
---|
456 | def run_task(function): |
---|
457 | subprocess.call(['pkexec', '/usr/lib/ubuntu-mate/lliurex-mate-welcome-repository-installer', os.path.abspath(os.path.join(app._data_path, 'js/applications.json')), function, category, program_id, target]) |
---|
458 | |
---|
459 | # Determine if any pre-configuration is specific to a codename. |
---|
460 | try: |
---|
461 | preinstall = dynamicapps.index[category][program_id]['pre-install'] |
---|
462 | codenames = list(preinstall.keys()) |
---|
463 | except: |
---|
464 | print('[Pre-Install] No pre-install data specified for "' + program_id + '". This application entry is invalid.') |
---|
465 | return |
---|
466 | arg.print_verbose('Pre-Install','Available configurations: ' + str(codenames)) |
---|
467 | target = None |
---|
468 | for name in codenames: |
---|
469 | if name == self.codename: |
---|
470 | target = name |
---|
471 | break |
---|
472 | if not target: |
---|
473 | target = 'all' |
---|
474 | arg.print_verbose('Pre-Install','Using "all" pre-configuration.') |
---|
475 | else: |
---|
476 | arg.print_verbose('Pre-Install','Using configuration for: "' + target + '".') |
---|
477 | |
---|
478 | methods = preinstall[target]['method'].split('+') |
---|
479 | if not methods: |
---|
480 | print('[Pre-Install] No pre-install method was specified. The index is invalid.') |
---|
481 | else: |
---|
482 | arg.print_verbose('Pre-Install','Configuration changes: ' + str(methods)) |
---|
483 | |
---|
484 | # Perform any pre-configuration, if necessary. |
---|
485 | if action == 'install' or action == 'upgrade': |
---|
486 | for method in methods: |
---|
487 | if method == 'skip': |
---|
488 | arg.print_verbose('Pre-Install','No need! The package is already in the archives.') |
---|
489 | continue |
---|
490 | |
---|
491 | elif method == 'partner-repo': |
---|
492 | print('[Pre-Install] Enabling the Ubuntu partner repository.') |
---|
493 | if not simulating: |
---|
494 | run_task('enable_partner_repository') |
---|
495 | transaction.update_cache = True |
---|
496 | |
---|
497 | elif method == 'ppa': |
---|
498 | try: |
---|
499 | ppa = preinstall[target]['enable-ppa'] |
---|
500 | except: |
---|
501 | print('[Pre-Install] Missing "enable-ppa" attribute. Cannot add PPA as requested.') |
---|
502 | return |
---|
503 | print('[Pre-Install] Adding PPA: "' + ppa + '" and updating cache.') |
---|
504 | if not simulating: |
---|
505 | run_task('enable_ppa') |
---|
506 | transaction.update_cache = True |
---|
507 | try: |
---|
508 | source_file = preinstall[target]['source-file'].replace('OSVERSION',self.os_version).replace('CODENAME',self.codename) |
---|
509 | print('[Pre-Install] Updating Apt Source: "' + source_file + '.list"') |
---|
510 | if not simulating: |
---|
511 | transaction.source_to_update = source_file + '.list' |
---|
512 | except: |
---|
513 | arg.print_verbose('Pre-Install','Updating entire cache as no source file was specified.') |
---|
514 | |
---|
515 | elif method == 'manual': |
---|
516 | # Do we get the apt key from a URL? |
---|
517 | try: |
---|
518 | apt_key_url = preinstall[target]['apt-key-url'] |
---|
519 | print('[Pre-Install] Getting Apt key from URL: "' + apt_key_url + '"') |
---|
520 | if not simulating: |
---|
521 | run_task('add_apt_key_from_url') |
---|
522 | except: |
---|
523 | arg.print_verbose('Pre-Install', 'No apt key to retrieve from a URL.') |
---|
524 | |
---|
525 | # Do we get the apt key from the server? |
---|
526 | try: |
---|
527 | apt_key_server = preinstall[target]['apt-key-server'][0] |
---|
528 | apt_key_key = preinstall[target]['apt-key-server'][1] |
---|
529 | print('[Pre-Install] Getting key "' + apt_key_key + '" from keyserver: "' + apt_key_server + '"') |
---|
530 | if not simulating: |
---|
531 | run_task('add_apt_key_from_keyserver') |
---|
532 | except: |
---|
533 | arg.print_verbose('Pre-Install', 'No apt key to retrieve from a key server.') |
---|
534 | |
---|
535 | # Do we need to add an apt source file? |
---|
536 | try: |
---|
537 | source = preinstall[target]['apt-sources'] |
---|
538 | source_file = preinstall[target]['source-file'].replace('OSVERSION',self.os_version).replace('CODENAME',self.codename) |
---|
539 | print('[Pre-Install] Writing source file: ' + source_file + '.list') |
---|
540 | print(' -------- Start of file ------') |
---|
541 | for line in source: |
---|
542 | print(' ' + line.replace('OSVERSION',self.os_version).replace('CODENAME',self.codename)) |
---|
543 | print(' -------- End of file ------') |
---|
544 | try: |
---|
545 | print('[Pre-Install] Updating Apt Source: ' + source_file + '.list') |
---|
546 | if not simulating: |
---|
547 | run_task('add_apt_sources') |
---|
548 | transaction.source_to_update = source_file + '.list' |
---|
549 | transaction.update_cache = True |
---|
550 | except: |
---|
551 | arg.print_verbose('Pre-Install','Failed to add apt sources!') |
---|
552 | except: |
---|
553 | arg.print_verbose('Pre-Install','No source data or source file to write.') |
---|
554 | |
---|
555 | elif action == 'remove': |
---|
556 | try: |
---|
557 | # The function uses wild cards, so we don't need to worry about being explict. |
---|
558 | listname = preinstall[target]['source-file'].replace('CODENAME','').replace('OSVERSION','') |
---|
559 | if simulating: |
---|
560 | print('[Simulation] Deleting Apt Source: ' + listname) |
---|
561 | else: |
---|
562 | run_task('del_apt_sources') |
---|
563 | except: |
---|
564 | print('[Pre-Install]', 'No apt source specified, so none will be removed.') |
---|
565 | |
---|
566 | |
---|
567 | # Pre-configuration complete. Now perform the operations, unless this was just a simulation. |
---|
568 | if simulating: |
---|
569 | print('[Pre-Install] Simulation flag active. No changes will be performed.') |
---|
570 | return |
---|
571 | else: |
---|
572 | if transaction.action == 'install': |
---|
573 | transaction.install_packages() |
---|
574 | elif transaction.action == 'remove': |
---|
575 | transaction.remove_packages() |
---|
576 | elif transaction.action == 'upgrade': |
---|
577 | transaction.upgrade_packages() |
---|
578 | |
---|
579 | |
---|
580 | class WelcomeConfig(object): |
---|
581 | """ Manages Welcome configuration """ |
---|
582 | def __init__(self): |
---|
583 | # store our base architecture |
---|
584 | self.os_version = client.lliurex_version("", "LliurexVersion")[1].split(", ")[-1] |
---|
585 | self.os_codename = platform.dist()[2] |
---|
586 | self.os_title = 'LliureX 16 ' + self.os_version |
---|
587 | self._arch = systemstate.arch |
---|
588 | |
---|
589 | # store full path to our binary |
---|
590 | self._welcome_bin_path = os.path.abspath(inspect.getfile(inspect.currentframe())) |
---|
591 | |
---|
592 | # directory for the configuration |
---|
593 | self._config_dir = os.path.expanduser('~/.config/lliurex-mate/welcome/') |
---|
594 | |
---|
595 | # autostart directory |
---|
596 | self._autostart_dir = os.path.expanduser('~/.config/autostart/') |
---|
597 | |
---|
598 | # full path to the autostart symlink |
---|
599 | self._autostart_path = os.path.expanduser(os.path.join(self._autostart_dir, 'lliurex-mate-welcome.desktop')) |
---|
600 | |
---|
601 | # ensure our config and autostart directories exists |
---|
602 | for _dir in [self._config_dir, self._autostart_dir]: |
---|
603 | if not os.path.exists(_dir): |
---|
604 | try: |
---|
605 | os.makedirs(_dir) |
---|
606 | except OSError as err: |
---|
607 | print(err) |
---|
608 | pass |
---|
609 | |
---|
610 | # does autostart symlink exist |
---|
611 | self._autostart = os.path.exists(self._autostart_path) |
---|
612 | |
---|
613 | @property |
---|
614 | def autostart(self): |
---|
615 | return self._autostart |
---|
616 | |
---|
617 | @autostart.setter |
---|
618 | def autostart(self, state): |
---|
619 | if state and not os.path.exists(self._autostart_path): |
---|
620 | # create the autostart symlink |
---|
621 | try: |
---|
622 | os.symlink('/usr/share/applications/lliurex-mate-welcome.desktop', self._autostart_path) |
---|
623 | except OSError as err: |
---|
624 | print(err) |
---|
625 | pass |
---|
626 | elif not state and os.path.exists(self._autostart_path): |
---|
627 | # remove the autostart symlink |
---|
628 | try: |
---|
629 | os.unlink(self._autostart_path) |
---|
630 | except OSError as err: |
---|
631 | print(err) |
---|
632 | pass |
---|
633 | |
---|
634 | # determine autostart state based on absence of the disable file |
---|
635 | self._autostart = os.path.exists(self._autostart_path) |
---|
636 | |
---|
637 | |
---|
638 | class AppView(WebKit.WebView): |
---|
639 | def __init__(self, slide_list = None): |
---|
640 | """ |
---|
641 | Args: |
---|
642 | slide_list : A list of tuples containing the filenames of translated html files |
---|
643 | or, if no translation is available, the filename of the original |
---|
644 | untranslated slide |
---|
645 | """ |
---|
646 | WebKit.WebView.__init__(self) |
---|
647 | WebKit.WebView.__init__(self) |
---|
648 | self._config = WelcomeConfig() |
---|
649 | self._apt_cache = apt.Cache() |
---|
650 | self.connect('load-finished', self._load_finished_cb) |
---|
651 | self.connect('navigation-policy-decision-requested', self._nav_request_policy_decision_cb) |
---|
652 | self.l_uri = None |
---|
653 | self._slide_list = slide_list |
---|
654 | |
---|
655 | self.set_zoom_level(systemstate.zoom_level) |
---|
656 | print('[Welcome] Setting zoom level to: ' + str(systemstate.zoom_level)) |
---|
657 | |
---|
658 | # Disable right-click context menu as it isn't needed. |
---|
659 | self.props.settings.props.enable_default_context_menu = False |
---|
660 | |
---|
661 | # Perform a smooth transition for footer icons. |
---|
662 | self.do_smooth_footer = False |
---|
663 | self.tutorial = TutorialApp() |
---|
664 | |
---|
665 | def _push_config(self): |
---|
666 | ### Global - On all pages ### |
---|
667 | self.execute_script("$('#os_title').html('%s')" % self._config.os_title) |
---|
668 | self.execute_script("$('#os_version').html('%s')" % self._config.os_version) |
---|
669 | self.execute_script("$('#autostart').toggleClass('fa-check-square', %s).toggleClass('fa-square', %s)" % (json.dumps(self._config.autostart), json.dumps(not self._config.autostart))) |
---|
670 | |
---|
671 | # If this is a Live session (booted from ISO) show the |
---|
672 | # 'Install OS' button, if running on an installed system show |
---|
673 | # the 'Install Software' button. |
---|
674 | if systemstate.session_type == 'live': |
---|
675 | self.execute_script("$('#install').show();") |
---|
676 | self.execute_script("$('#software').hide();") |
---|
677 | self.execute_script("$('.live-session./').hide();") |
---|
678 | self.execute_script("$('.live-session-only').show();") |
---|
679 | else: |
---|
680 | self.execute_script("$('#install').hide();") |
---|
681 | self.execute_script("$('#software').show();") |
---|
682 | self.execute_script("$('.live-session').show();") |
---|
683 | self.execute_script("$('.live-session-only').hide();") |
---|
684 | |
---|
685 | # If started from a Raspberry Pi. |
---|
686 | if systemstate.session_type == 'pi': |
---|
687 | self.execute_script("$('.rpi-only').show();") |
---|
688 | else: |
---|
689 | self.execute_script("$('.rpi-only').hide();") |
---|
690 | |
---|
691 | # check if which flavour is the cd to customize de icons |
---|
692 | # and the options |
---|
693 | |
---|
694 | if systemstate.session_flavour == 'server' and not systemstate.session_type == 'live': |
---|
695 | self.execute_script("$('.server-only').show();") |
---|
696 | else: |
---|
697 | self.execute_script("$('.server-only').hide();") |
---|
698 | |
---|
699 | if systemstate.session_flavour == 'music': |
---|
700 | self.execute_script("$('#mate-blur').removeClass('menu-bkgnd').addClass('menu-bkgnd-music');") |
---|
701 | self.execute_script("$('#main-menu-logo').attr('src', 'img/welcome/flavour-music.svg');") |
---|
702 | |
---|
703 | if systemstate.session_flavour == 'infantil': |
---|
704 | self.execute_script("$('#mate-blur').removeClass('menu-bkgnd').addClass('menu-bkgnd-infantil');") |
---|
705 | self.execute_script("$('#main-menu-logo').attr('src', 'img/welcome/flavour-infantil.svg');") |
---|
706 | |
---|
707 | # Display warnings if the user is not connected to the internet. |
---|
708 | if systemstate.is_online: |
---|
709 | self.execute_script("$('.offline').hide();") |
---|
710 | self.execute_script("$('.online').show();") |
---|
711 | else: |
---|
712 | self.execute_script("$('.offline').show();") |
---|
713 | self.execute_script("$('.online').hide();") |
---|
714 | |
---|
715 | ## Social Links ## |
---|
716 | footer_left = '<div id="social" class="pull-left"> \ |
---|
717 | <a href="cmd://link?https://twitter.com/lliurex" title="Twitter"><img src="img/social/twitter.svg"></a> \ |
---|
718 | <a href="cmd://link?http://mestreacasa.gva.es/web/lliurex/" title="lliurex"><img src="img/humanity/website.svg"></a> \ |
---|
719 | <a href="cmd://link?http://wiki.lliurex.net/Inicio" title="Wiki de LliureX"><img src="img/humanity/gtk-info.svg"></a> \ |
---|
720 | </div>' |
---|
721 | |
---|
722 | # Do not show footer links on splash or software page. |
---|
723 | if not arg.jump_software_page: |
---|
724 | if not self.current_page == 'splash.html' and not self.current_page == 'software.html': |
---|
725 | self.execute_script("$('#footer-global-left').html('" + footer_left + "');") |
---|
726 | |
---|
727 | # Show the button depending on context. |
---|
728 | footer_close = '<a href="cmd://quit" class="btn btn-inverse">' + _("Close") + '</a>' |
---|
729 | footer_skip = '<a onclick="continueToPage(true)" class="btn btn-inverse">' + _("Skip") + '</a>' |
---|
730 | |
---|
731 | if self.current_page == 'splash.html': |
---|
732 | self.execute_script("$('#footer-global-right').html('" + footer_skip + "');") |
---|
733 | self.execute_script("$('.splash-version').html('%s')" % self._config.os_version) |
---|
734 | elif arg.jump_software_page: |
---|
735 | # Do not show a "Close" button for the Boutique. |
---|
736 | pass |
---|
737 | else: |
---|
738 | self.execute_script("$('#footer-global-right').html('" + footer_close + "');") |
---|
739 | |
---|
740 | # Smoothly fade in the footer links between pages. |
---|
741 | # splash → index |
---|
742 | # index ← → software |
---|
743 | if self.do_smooth_footer or self.current_page == 'software.html': |
---|
744 | self.do_smooth_footer = False |
---|
745 | self.execute_script("$('#footer-left').hide();") |
---|
746 | self.execute_script("$('#footer-left').fadeIn();") |
---|
747 | |
---|
748 | # Individual Page Actions |
---|
749 | ### Main Menu ### |
---|
750 | if self.current_page == 'index.html': |
---|
751 | if systemstate.session_type == 'guest': |
---|
752 | # Disable features that are unavailable to guests. |
---|
753 | self.execute_script("$('#gettingstarted').hide();") |
---|
754 | self.execute_script("$('#software').hide();") |
---|
755 | self.execute_script("$('#introduction').addClass('btn-success');") |
---|
756 | self.execute_script("$('#community').addClass('btn-success');") |
---|
757 | |
---|
758 | # Check whether the system is subscribed for receiving more up-to-date versions of Welcome. |
---|
759 | self.execute_script('$("#update-subscribing").hide()') |
---|
760 | if not systemstate.updates_subscribed: |
---|
761 | if systemstate.is_online: |
---|
762 | self.execute_script('$("#update-notification").fadeIn("slow")') |
---|
763 | else: |
---|
764 | self.execute_script('$("#update-notification").hide()') |
---|
765 | |
---|
766 | # Disable confetti on machines that may suffer performance issues. |
---|
767 | if systemstate.arch == 'armhf': |
---|
768 | self.execute_script('var disable_confetti = true;') |
---|
769 | elif systemstate.arch == 'powerpc': |
---|
770 | self.execute_script('var disable_confetti = true;') |
---|
771 | else: |
---|
772 | self.execute_script('var disable_confetti = false;') |
---|
773 | |
---|
774 | """# Special event strings. |
---|
775 | self.execute_script('var days_in = "in"') |
---|
776 | self.execute_script('var future_days = "days."') |
---|
777 | self.execute_script('var days_ago = "days ago."') |
---|
778 | self.execute_script('var yesterday = "yesterday."') |
---|
779 | self.execute_script('var tomorrow = "tomorrow."') |
---|
780 | self.execute_script('var years_ago = "years ago today."') |
---|
781 | self.execute_script('var today_string = "today."') |
---|
782 | self.execute_script('var years_old = "years old"') |
---|
783 | |
---|
784 | self.execute_script('var flavour_anniversary_future = "Ubuntu MATE\'s official flavour anniversary"') |
---|
785 | self.execute_script('var flavour_anniversary_present = "Ubuntu MATE become an official flavour"') |
---|
786 | self.execute_script('var flavour_anniversary_past = "Ubuntu MATE\'s official flavour anniversary was"') |
---|
787 | |
---|
788 | self.execute_script('var project_birthday_future = "Ubuntu MATE will be"') |
---|
789 | self.execute_script('var project_birthday_present = "Ubuntu MATE is"') |
---|
790 | self.execute_script('var project_birthday_past = "Ubuntu MATE turned"') |
---|
791 | |
---|
792 | self.execute_script('var project_birthday = "Happy Birthday!"') |
---|
793 | self.execute_script('var celebrate_new_year = "Happy New Year from Ubuntu MATE!"') |
---|
794 | |
---|
795 | self.execute_script('var project_release_future = "will be released"') |
---|
796 | self.execute_script('var project_release_present = "is released today!"') |
---|
797 | self.execute_script('var project_release_past = "was released"') |
---|
798 | self.execute_script('var project_release_thanks = "Thank you for testing"') |
---|
799 | |
---|
800 | self.execute_script('checkDates();')""" |
---|
801 | |
---|
802 | ### Splash ### |
---|
803 | if self.current_page == 'splash.html': |
---|
804 | self.do_smooth_footer = True |
---|
805 | # Determine which screen to show after the splash screen. |
---|
806 | if systemstate.session_type == 'live': |
---|
807 | self.execute_script('var splashNextPage = "hellolive"') |
---|
808 | elif systemstate.session_type == 'guest': |
---|
809 | self.execute_script('var splashNextPage = "helloguest"') |
---|
810 | else: |
---|
811 | self.execute_script('var splashNextPage = "index"') |
---|
812 | |
---|
813 | # Smoothly fade footer when entering main menu. |
---|
814 | self.splash_finished = True |
---|
815 | |
---|
816 | ### Getting Started Page ### |
---|
817 | if self.current_page == 'gettingstarted.html': |
---|
818 | # Display information tailored to graphics vendor (Getting Started / Drivers) |
---|
819 | self.execute_script('var graphicsVendor = "' + systemstate.graphics_vendor + '";') |
---|
820 | self.execute_script('var graphicsGrep = "' + systemstate.graphics_grep + '";') |
---|
821 | self.execute_script('$("#boot-mode").html("' + systemstate.boot_mode + '")') |
---|
822 | |
---|
823 | # Update any applications featured on these pages. |
---|
824 | dynamicapps.update_app_status(self, 'hardinfo') |
---|
825 | dynamicapps.update_app_status(self, 'gparted') |
---|
826 | dynamicapps.update_app_status(self, 'gnome-disk-utility') |
---|
827 | dynamicapps.update_app_status(self, 'mate-disk-usage-analyzer') |
---|
828 | dynamicapps.update_app_status(self, 'mate-system-monitor') |
---|
829 | dynamicapps.update_app_status(self, 'psensor') |
---|
830 | dynamicapps.update_app_status(self, 'boot-repair') |
---|
831 | dynamicapps.update_app_status(self, 'codecs') |
---|
832 | dynamicapps.update_app_status(self, 'firmware') |
---|
833 | dynamicapps.update_app_status(self, 'hp-printer') |
---|
834 | dynamicapps.update_app_status(self, 'keyboard-chinese') |
---|
835 | dynamicapps.update_app_status(self, 'keyboard-japanese') |
---|
836 | dynamicapps.update_app_status(self, 'keyboard-korean') |
---|
837 | |
---|
838 | ### Software Page ### |
---|
839 | if self.current_page == 'software.html': |
---|
840 | dynamicapps.hide_non_free = False |
---|
841 | self.do_smooth_footer = True |
---|
842 | |
---|
843 | # If loading a minimal "Get More Software" only page. |
---|
844 | if arg.jump_software_page: |
---|
845 | self.execute_script('$("#menu-button").hide()') |
---|
846 | self.execute_script('$("#navigation-title").html("<span id=\'navigation-sub-title\'>Curated software collection</span>")') |
---|
847 | self.execute_script('$("#navigation-sub-title").css("color","#DED9CB")') |
---|
848 | |
---|
849 | # Pass 'Servers' variable used for one-click server links. |
---|
850 | self.execute_script('var server_string = "' + _("Servers") + '"') |
---|
851 | |
---|
852 | # Dynamically load application lists. |
---|
853 | dynamicapps.populate_categories(self) |
---|
854 | dynamicapps.update_all_app_status(self) |
---|
855 | dynamicapps.populate_featured_apps(self) |
---|
856 | |
---|
857 | ### Raspberry Pi Page ### |
---|
858 | if self.current_page == 'rpi.html': |
---|
859 | # Check file system resize flag. |
---|
860 | systemstate.rpi_resize('check', self) |
---|
861 | |
---|
862 | def _load_finished_cb(self, view, frame): |
---|
863 | self._push_config() |
---|
864 | |
---|
865 | def _nav_request_policy_decision_cb(self, view, frame, net_req, nav_act, pol_dec): |
---|
866 | uri = net_req.get_uri() |
---|
867 | self.current_page = uri.rsplit('/', 1)[1] |
---|
868 | |
---|
869 | try: |
---|
870 | if uri.index('#') > 0: |
---|
871 | uri = uri[:uri.index('#')] |
---|
872 | except ValueError: |
---|
873 | pass |
---|
874 | |
---|
875 | if uri == self.l_uri: |
---|
876 | pol_dec.use() |
---|
877 | return True |
---|
878 | |
---|
879 | if uri.startswith('cmd://'): |
---|
880 | self._do_command(uri) |
---|
881 | return True |
---|
882 | |
---|
883 | self.l_uri = uri |
---|
884 | |
---|
885 | if self._slide_list is None: |
---|
886 | # no translations have to been specified, so we can just load the specified page.. |
---|
887 | page = urllib.request.urlopen(uri) |
---|
888 | else: |
---|
889 | # find the slide in slide_list |
---|
890 | head, slide_name = os.path.split(uri) |
---|
891 | found = False |
---|
892 | for slide in self._slide_list: |
---|
893 | head, trans_slide_name = os.path.split(slide) |
---|
894 | if slide_name == trans_slide_name: |
---|
895 | found = True |
---|
896 | # load the translated html |
---|
897 | trans_uri = urllib.parse.urljoin('file:', urllib.request.pathname2url(slide)) |
---|
898 | page = urllib.request.urlopen(trans_uri) |
---|
899 | break |
---|
900 | |
---|
901 | if not found: |
---|
902 | # this should never happen, but if it does, recover by loading the originally specified page |
---|
903 | arg.print_verbose('Translation','Couldn''t find slide %s when getting translation' %uri) |
---|
904 | page = urllib.request.urlopen(uri) |
---|
905 | |
---|
906 | # use UTF-8 encoding as fix for   chars in translated html |
---|
907 | # |
---|
908 | # When loading the html, for the base_uri, use the uri of the originally specified |
---|
909 | # page (which will be in _data_path) rather than the uri of any translated html we may be using instead. |
---|
910 | # Doing this allows the js, css, fonts etc. directories to be located by the translated page, |
---|
911 | frame.load_string(page.read().decode(), "text/html", "UTF-8", uri); |
---|
912 | |
---|
913 | pol_dec.ignore() |
---|
914 | return True |
---|
915 | |
---|
916 | ################################################################################################################3 |
---|
917 | |
---|
918 | |
---|
919 | |
---|
920 | ################################################################################################################ |
---|
921 | |
---|
922 | def _do_command(self, uri): |
---|
923 | if uri.startswith('cmd://'): |
---|
924 | uri = uri[6:] |
---|
925 | try: |
---|
926 | if uri.startswith('install-appid?'): |
---|
927 | dynamicapps.modify_app(self, 'install', uri[14:]) |
---|
928 | elif uri.startswith('remove-appid?'): |
---|
929 | dynamicapps.modify_app(self, 'remove', uri[13:]) |
---|
930 | elif uri.startswith('upgrade-appid?'): |
---|
931 | dynamicapps.modify_app(self, 'upgrade', uri[14:]) |
---|
932 | elif uri.startswith('launch-appid?'): |
---|
933 | dynamicapps.launch_app(uri[13:]) |
---|
934 | elif uri.startswith('filter-apps?'): |
---|
935 | filter_name = uri.split('?')[1] |
---|
936 | nonfree_toggle = uri.split('?')[2] |
---|
937 | if nonfree_toggle == 'toggle': |
---|
938 | dynamicapps.apply_filter(self, filter_name, True) |
---|
939 | else: |
---|
940 | dynamicapps.apply_filter(self, filter_name) |
---|
941 | elif uri.startswith('app-info-show?'): |
---|
942 | appid = uri.split('?')[1] |
---|
943 | self.execute_script('$("#info-show-' + appid + '").hide()') |
---|
944 | self.execute_script('$("#info-hide-' + appid + '").show()') |
---|
945 | self.execute_script('$("#details-' + appid + '").fadeIn("fast")') |
---|
946 | elif uri.startswith('app-info-hide?'): |
---|
947 | appid = uri.split('?')[1] |
---|
948 | self.execute_script('$("#info-show-' + appid + '").show()') |
---|
949 | self.execute_script('$("#info-hide-' + appid + '").hide()') |
---|
950 | self.execute_script('$("#details-' + appid + '").fadeOut("fast")') |
---|
951 | elif uri.startswith('screenshot?'): |
---|
952 | filename = uri.split('?')[1] |
---|
953 | dynamicapps.show_screenshot(filename) |
---|
954 | elif uri == 'apt-update': |
---|
955 | update_repos() |
---|
956 | self._apt_cache.close() |
---|
957 | self._apt_cache = apt.Cache() |
---|
958 | self._push_config() |
---|
959 | elif uri == 'fix-incomplete-install': |
---|
960 | fix_incomplete_install() |
---|
961 | self._apt_cache.close() |
---|
962 | self._apt_cache = apt.Cache() |
---|
963 | self._push_config() |
---|
964 | elif uri == 'fix-broken-depends': |
---|
965 | fix_broken_depends() |
---|
966 | self._apt_cache.close() |
---|
967 | self._apt_cache = apt.Cache() |
---|
968 | self._push_config() |
---|
969 | elif uri == 'get-aacs-db': |
---|
970 | self.execute_script('$(".bluray-applying").show()') |
---|
971 | get_aacs_db() |
---|
972 | self.execute_script('$(".bluray-applying").hide()') |
---|
973 | elif uri == 'autostart': |
---|
974 | self._config.autostart ^= True |
---|
975 | self._push_config() |
---|
976 | elif uri == 'install': |
---|
977 | subprocess.Popen(['ubiquity','gtk_ui']) |
---|
978 | elif uri == 'backup': |
---|
979 | subprocess.Popen(['lliurex-backup']) |
---|
980 | elif uri == 'control': |
---|
981 | subprocess.Popen(['mate-control-center']) |
---|
982 | elif uri == 'drivers': |
---|
983 | subprocess.Popen(['software-properties-gtk','--open-tab=4']) |
---|
984 | elif uri == 'firewall': |
---|
985 | subprocess.Popen(['gufw']) |
---|
986 | elif uri == 'language': |
---|
987 | subprocess.Popen(['gnome-language-selector']) |
---|
988 | elif uri == 'users': |
---|
989 | subprocess.Popen(['users-admin']) |
---|
990 | elif uri == 'quit': |
---|
991 | goodbye() |
---|
992 | elif uri == 'tweak': |
---|
993 | subprocess.Popen(['mate-tweak']) |
---|
994 | elif uri == 'update': |
---|
995 | subprocess.Popen(['update-manager']) |
---|
996 | elif uri == 'printers': |
---|
997 | subprocess.Popen(['system-config-printer']) |
---|
998 | elif uri == 'gparted': |
---|
999 | subprocess.Popen(['gparted-pkexec']) |
---|
1000 | elif uri == 'sysmonitor': |
---|
1001 | subprocess.Popen(['mate-system-monitor']) |
---|
1002 | elif uri.startswith('run?'): |
---|
1003 | subprocess.Popen([uri[4:]]) |
---|
1004 | elif uri.startswith('link?'): |
---|
1005 | webbrowser.open_new_tab(uri[5:]) |
---|
1006 | elif uri == 'checkInternetConnection': |
---|
1007 | systemstate.check_internet_connection() |
---|
1008 | if systemstate.is_online: |
---|
1009 | self.execute_script("$('.offline').hide();") |
---|
1010 | self.execute_script("$('.online').show();") |
---|
1011 | else: |
---|
1012 | self.execute_script("$('.offline').show();") |
---|
1013 | self.execute_script("$('.online').hide();") |
---|
1014 | elif uri == 'resize-rpi': |
---|
1015 | systemstate.rpi_resize('do-resize', self) |
---|
1016 | elif uri == 'reboot-rpi': |
---|
1017 | systemstate.rpi_resize('reboot') |
---|
1018 | elif uri == 'subscribe-updates': |
---|
1019 | print('[Welcome] Subscribing to Ubuntu MATE Welcome Updates...') |
---|
1020 | self.execute_script("$('#update-notification').hide()") |
---|
1021 | self.execute_script("$('#update-subscribing').show()") |
---|
1022 | dynamicapps.modify_app(self, 'install', 'ubuntu-mate-welcome') |
---|
1023 | # Verify if the PPA was successfully added. |
---|
1024 | if os.path.exists(systemstate.welcome_ppa_file): |
---|
1025 | if os.path.getsize(systemstate.welcome_ppa_file) > 0: |
---|
1026 | print('[Welcome] Success, PPA added! Application restarting...') |
---|
1027 | os.execv(__file__, sys.argv) |
---|
1028 | else: |
---|
1029 | print('[Welcome] Failed, PPA not detected!') |
---|
1030 | self.execute_script('$("#update-subscribing").hide()') |
---|
1031 | self.execute_script('$("#update-notification").show()') |
---|
1032 | elif uri == 'init-system-info': |
---|
1033 | systemstate.get_system_info(self) |
---|
1034 | #Information of a LLiureX Server |
---|
1035 | elif uri == 'init-server-info': |
---|
1036 | systemstate.get_server_info(self) |
---|
1037 | elif uri == 'zero-lliurex-backup': |
---|
1038 | subprocess.Popen(['lliurex-backup']) |
---|
1039 | elif uri == 'zero-center': |
---|
1040 | subprocess.Popen(['zero-center']) |
---|
1041 | elif uri == 'dr-valentin': |
---|
1042 | subprocess.Popen(['dr-valentin']) |
---|
1043 | elif uri == 'llum': |
---|
1044 | subprocess.Popen(['llum']) |
---|
1045 | elif uri == 'lliurex-up': |
---|
1046 | subprocess.Popen(["gksudo","lliurex-up"]) |
---|
1047 | elif uri == 'iniciar-servidor': |
---|
1048 | subprocess.Popen(["gksudo","zero-server-wizard"]) |
---|
1049 | elif uri == 'lliurex-mirror': |
---|
1050 | subprocess.Popen(["gksudo","lliurex-mirror-gui"]) |
---|
1051 | elif uri == 'lmd': |
---|
1052 | subprocess.Popen(["lmd-manager"]) |
---|
1053 | elif uri == 'network_boot': |
---|
1054 | subprocess.Popen(["node-webkit","/usr/share/llx-bootmanager-gui"]) |
---|
1055 | elif uri == 'pmb': |
---|
1056 | subprocess.Popen(["/usr/share/zero-center/zmds/zero-lliurex-pmb-installer.zmd"]) |
---|
1057 | elif uri == 'windows-selector': |
---|
1058 | subprocess.Popen(["mate-tweak"]) |
---|
1059 | elif uri == 'windows-selector-compiz': |
---|
1060 | subprocess.Popen(['compiz','--replace']) |
---|
1061 | else: |
---|
1062 | print('[Error] Unknown command: ', uri) |
---|
1063 | except Exception as e: |
---|
1064 | print('[Error] Failed to execute command: ', uri) |
---|
1065 | print('[Error] Exception: ', e) |
---|
1066 | |
---|
1067 | |
---|
1068 | class WelcomeApp(object): |
---|
1069 | def __init__(self): |
---|
1070 | # establish our location |
---|
1071 | self._location = os.path.dirname( os.path.abspath(inspect.getfile(inspect.currentframe())) ) |
---|
1072 | |
---|
1073 | # check for relative path |
---|
1074 | if( os.path.exists( os.path.join(self._location, 'data/' ) ) ): |
---|
1075 | print('[Debug] Using relative path for data source. Non-production testing.') |
---|
1076 | self._data_path = os.path.join(self._location, 'data/') |
---|
1077 | elif( os.path.exists('/usr/share/lliurex-mate-welcome/') ): |
---|
1078 | print('Using /usr/share/lliurex-mate-welcome/ path.') |
---|
1079 | self._data_path = '/usr/share/lliurex-mate-welcome/' |
---|
1080 | else: |
---|
1081 | print('Unable to source the lliurex-mate-welcome data directory.') |
---|
1082 | sys.exit(1) |
---|
1083 | |
---|
1084 | self._build_app() |
---|
1085 | |
---|
1086 | def _get_translated_slides(self): |
---|
1087 | """ If a locale has been specified on the command line, get translated slides |
---|
1088 | for that. If not, get translated slides for the current locale |
---|
1089 | |
---|
1090 | Do not assume that every slide has a translation, check each file individually |
---|
1091 | |
---|
1092 | Returns: |
---|
1093 | a list of filenames of translated slides - if there is no translated version |
---|
1094 | of a slide, the filename of the untranslated version from the _data_path directory |
---|
1095 | is used instead |
---|
1096 | |
---|
1097 | """ |
---|
1098 | |
---|
1099 | if (arg.locale is not None): |
---|
1100 | locale_to_use = arg.locale |
---|
1101 | else: |
---|
1102 | locale_to_use = str(locale.getlocale()[0]) |
---|
1103 | |
---|
1104 | def set_locale_dir(this_locale): |
---|
1105 | # check for relative path |
---|
1106 | if (os.path.exists(os.path.join(self._location, 'i18n', this_locale))): |
---|
1107 | locale_dir = os.path.join(self._location, 'i18n', this_locale) |
---|
1108 | print('[i18n] Using ' + this_locale + '. Non-production testing.') |
---|
1109 | elif (os.path.exists(os.path.join('/usr/share/lliurex-mate-welcome/i18n/', this_locale))): |
---|
1110 | locale_dir = os.path.join('/usr/share/lliurex-mate-welcome/i18n/', this_locale) |
---|
1111 | print('[i18n] Using ' + this_locale) |
---|
1112 | else: |
---|
1113 | locale_dir = '' |
---|
1114 | print('[i18n] Locale ' + this_locale + ' not available.') |
---|
1115 | |
---|
1116 | return locale_dir |
---|
1117 | |
---|
1118 | # if no locale exists, try a generic locale. |
---|
1119 | locale_dir = set_locale_dir(locale_to_use) |
---|
1120 | if locale_dir == '': |
---|
1121 | locale_generic = locale_to_use.split('_')[0] |
---|
1122 | print('[i18n] Trying ' + locale_generic + '...') |
---|
1123 | locale_dir = set_locale_dir(locale_generic) |
---|
1124 | |
---|
1125 | results = [] |
---|
1126 | |
---|
1127 | slides = glob.glob(os.path.join(self._data_path, '*.html')) |
---|
1128 | for slide in slides: |
---|
1129 | # get the slide name and see if a translated version exists |
---|
1130 | head, slide_name = os.path.split(slide) |
---|
1131 | |
---|
1132 | trans_slide = os.path.join(locale_dir, slide_name) |
---|
1133 | if os.path.exists(trans_slide): |
---|
1134 | results.append(trans_slide) |
---|
1135 | arg.print_verbose("i18n","Will use %s translation of %s" %(locale_to_use, slide_name)) |
---|
1136 | else: |
---|
1137 | results.append(slide) |
---|
1138 | arg.print_verbose("i18n","No %s translation of %s found. Will use version in _data_path" %(locale_to_use, slide)) |
---|
1139 | |
---|
1140 | return results |
---|
1141 | |
---|
1142 | def _build_app(self): |
---|
1143 | |
---|
1144 | # Slightly different attributes if "--software-only" is activated. |
---|
1145 | if arg.jump_software_page: |
---|
1146 | title = _("Software Boutique") |
---|
1147 | width = 900 |
---|
1148 | height = 600 |
---|
1149 | load_file = 'software-only.html' |
---|
1150 | else: |
---|
1151 | title = _("LliureX 16") |
---|
1152 | width = 800 |
---|
1153 | height = 552 |
---|
1154 | load_file = 'splash.html' |
---|
1155 | |
---|
1156 | # Enlarge the window should the text be any larger. |
---|
1157 | if systemstate.zoom_level == 1.1: |
---|
1158 | width = width + 20 |
---|
1159 | height = height + 20 |
---|
1160 | elif systemstate.zoom_level == 1.2: |
---|
1161 | width = width + 60 |
---|
1162 | height = height + 40 |
---|
1163 | elif systemstate.zoom_level == 1.3: |
---|
1164 | width = width + 100 |
---|
1165 | height = height + 60 |
---|
1166 | elif systemstate.zoom_level == 1.4: |
---|
1167 | width = width + 130 |
---|
1168 | height = height + 100 |
---|
1169 | elif systemstate.zoom_level == 1.5: |
---|
1170 | width = width + 160 |
---|
1171 | height = height + 120 |
---|
1172 | |
---|
1173 | # Jump to a specific page for testing purposes. |
---|
1174 | if arg.jump_to: |
---|
1175 | load_file = arg.jump_to + '.html' |
---|
1176 | |
---|
1177 | # build window |
---|
1178 | w = Gtk.Window() |
---|
1179 | w.set_position(Gtk.WindowPosition.CENTER) |
---|
1180 | w.set_wmclass('Ubuntu MATE Welcome', 'Ubuntu MATE Welcome') |
---|
1181 | w.set_title(title) |
---|
1182 | |
---|
1183 | # http://askubuntu.com/questions/153549/how-to-detect-a-computers-physical-screen-size-in-gtk |
---|
1184 | s = Gdk.Screen.get_default() |
---|
1185 | if s.get_height() <= 600: |
---|
1186 | w.set_size_request(768, 528) |
---|
1187 | else: |
---|
1188 | w.set_size_request(width, height) |
---|
1189 | |
---|
1190 | icon_dir = os.path.join(self._data_path, 'img', 'welcome', 'ubuntu-mate-icon.svg') |
---|
1191 | w.set_icon_from_file(icon_dir) |
---|
1192 | |
---|
1193 | #get the translated slide list |
---|
1194 | trans_slides = self._get_translated_slides() |
---|
1195 | # build webkit container |
---|
1196 | mv = AppView(trans_slides) |
---|
1197 | |
---|
1198 | # load our index file |
---|
1199 | file = os.path.abspath(os.path.join(self._data_path, load_file)) |
---|
1200 | |
---|
1201 | uri = 'file://' + urllib.request.pathname2url(file) |
---|
1202 | mv.open(uri) |
---|
1203 | |
---|
1204 | # build scrolled window widget and add our appview container |
---|
1205 | sw = Gtk.ScrolledWindow() |
---|
1206 | sw.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC) |
---|
1207 | sw.add(mv) |
---|
1208 | |
---|
1209 | # build an autoexpanding box and add our scrolled window |
---|
1210 | b = Gtk.VBox(homogeneous=False, spacing=0) |
---|
1211 | b.pack_start(sw, expand=True, fill=True, padding=0) |
---|
1212 | |
---|
1213 | # add the box to the parent window and show |
---|
1214 | w.add(b) |
---|
1215 | w.connect('delete-event', goodbye) |
---|
1216 | w.show_all() |
---|
1217 | |
---|
1218 | self._window = w |
---|
1219 | self._appView = mv |
---|
1220 | |
---|
1221 | def run(self): |
---|
1222 | signal.signal(signal.SIGINT, signal.SIG_DFL) |
---|
1223 | Gtk.main() |
---|
1224 | |
---|
1225 | def close(self, p1, p2): |
---|
1226 | Gtk.main_quit(p1, p2); |
---|
1227 | |
---|
1228 | |
---|
1229 | ###############################################3 |
---|
1230 | |
---|
1231 | class TutorialApp(object): |
---|
1232 | def inicia(self): |
---|
1233 | # establish our location |
---|
1234 | self._location = os.path.dirname( os.path.abspath(inspect.getfile(inspect.currentframe())) ) |
---|
1235 | |
---|
1236 | # check for relative path |
---|
1237 | if( os.path.exists( os.path.join(self._location, 'data/' ) ) ): |
---|
1238 | print('[Debug] Using relative path for data source. Non-production testing.') |
---|
1239 | self._data_path = os.path.join(self._location, 'data/') |
---|
1240 | elif( os.path.exists('/usr/share/LliureX-mate-welcome/') ): |
---|
1241 | print('Using /usr/share/lliurex-mate-welcome/ path.') |
---|
1242 | self._data_path = '/usr/share/lliurex-mate-welcome/' |
---|
1243 | else: |
---|
1244 | print('Unable to source the lliurex-mate-welcome data directory.') |
---|
1245 | sys.exit(1) |
---|
1246 | |
---|
1247 | self._build_app() |
---|
1248 | |
---|
1249 | def _get_translated_slides(self): |
---|
1250 | """ If a locale has been specified on the command line, get translated slides |
---|
1251 | for that. If not, get translated slides for the current locale |
---|
1252 | |
---|
1253 | Do not assume that every slide has a translation, check each file individually |
---|
1254 | |
---|
1255 | Returns: |
---|
1256 | a list of filenames of translated slides - if there is no translated version |
---|
1257 | of a slide, the filename of the untranslated version from the _data_path directory |
---|
1258 | is used instead |
---|
1259 | |
---|
1260 | """ |
---|
1261 | |
---|
1262 | if (arg.locale is not None): |
---|
1263 | locale_to_use = arg.locale |
---|
1264 | else: |
---|
1265 | locale_to_use = str(locale.getlocale()[0]) |
---|
1266 | |
---|
1267 | def set_locale_dir(this_locale): |
---|
1268 | # check for relative path |
---|
1269 | if (os.path.exists(os.path.join(self._location, 'i18n', this_locale))): |
---|
1270 | locale_dir = os.path.join(self._location, 'i18n', this_locale) |
---|
1271 | print('[i18n] Using ' + this_locale + '. Non-production testing.') |
---|
1272 | elif (os.path.exists(os.path.join('/usr/share/lliurex-mate-welcome/i18n/', this_locale))): |
---|
1273 | locale_dir = os.path.join('/usr/share/lliurex-mate-welcome/i18n/', this_locale) |
---|
1274 | print('[i18n] Using ' + this_locale) |
---|
1275 | else: |
---|
1276 | locale_dir = '' |
---|
1277 | print('[i18n] Locale ' + this_locale + ' not available.') |
---|
1278 | |
---|
1279 | return locale_dir |
---|
1280 | |
---|
1281 | # if no locale exists, try a generic locale. |
---|
1282 | locale_dir = set_locale_dir(locale_to_use) |
---|
1283 | if locale_dir == '': |
---|
1284 | locale_generic = locale_to_use.split('_')[0] |
---|
1285 | print('[i18n] Trying ' + locale_generic + '...') |
---|
1286 | locale_dir = set_locale_dir(locale_generic) |
---|
1287 | |
---|
1288 | results = [] |
---|
1289 | |
---|
1290 | slides = glob.glob(os.path.join(self._data_path, '*.html')) |
---|
1291 | for slide in slides: |
---|
1292 | # get the slide name and see if a translated version exists |
---|
1293 | head, slide_name = os.path.split(slide) |
---|
1294 | |
---|
1295 | trans_slide = os.path.join(locale_dir, slide_name) |
---|
1296 | if os.path.exists(trans_slide): |
---|
1297 | results.append(trans_slide) |
---|
1298 | arg.print_verbose("i18n","Will use %s translation of %s" %(locale_to_use, slide_name)) |
---|
1299 | else: |
---|
1300 | results.append(slide) |
---|
1301 | arg.print_verbose("i18n","No %s translation of %s found. Will use version in _data_path" %(locale_to_use, slide)) |
---|
1302 | |
---|
1303 | return results |
---|
1304 | |
---|
1305 | def _build_app(self): |
---|
1306 | |
---|
1307 | # Slightly different attributes if "--software-only" is activated. |
---|
1308 | |
---|
1309 | title = _("LliureX 16") |
---|
1310 | width = 800 |
---|
1311 | height = 552 |
---|
1312 | load_file = 'foros2.html' |
---|
1313 | |
---|
1314 | # Enlarge the window should the text be any larger. |
---|
1315 | if systemstate.zoom_level == 1.1: |
---|
1316 | width = width + 20 |
---|
1317 | height = height + 20 |
---|
1318 | elif systemstate.zoom_level == 1.2: |
---|
1319 | width = width + 60 |
---|
1320 | height = height + 40 |
---|
1321 | elif systemstate.zoom_level == 1.3: |
---|
1322 | width = width + 100 |
---|
1323 | height = height + 60 |
---|
1324 | elif systemstate.zoom_level == 1.4: |
---|
1325 | width = width + 130 |
---|
1326 | height = height + 100 |
---|
1327 | elif systemstate.zoom_level == 1.5: |
---|
1328 | width = width + 160 |
---|
1329 | height = height + 120 |
---|
1330 | |
---|
1331 | # build window |
---|
1332 | w = Gtk.Window() |
---|
1333 | w.set_position(Gtk.WindowPosition.CENTER) |
---|
1334 | w.set_wmclass('Ubuntu MATE Welcome', 'Ubuntu MATE Welcome') |
---|
1335 | w.set_title(title) |
---|
1336 | |
---|
1337 | # http://askubuntu.com/questions/153549/how-to-detect-a-computers-physical-screen-size-in-gtk |
---|
1338 | s = Gdk.Screen.get_default() |
---|
1339 | if s.get_height() <= 600: |
---|
1340 | w.set_size_request(768, 528) |
---|
1341 | else: |
---|
1342 | w.set_size_request(width, height) |
---|
1343 | |
---|
1344 | icon_dir = os.path.join(self._data_path, 'img', 'welcome', 'ubuntu-mate-icon.svg') |
---|
1345 | w.set_icon_from_file(icon_dir) |
---|
1346 | |
---|
1347 | #get the translated slide list |
---|
1348 | trans_slides = self._get_translated_slides() |
---|
1349 | # build webkit container |
---|
1350 | mv = AppView(trans_slides) |
---|
1351 | |
---|
1352 | # load our index file |
---|
1353 | file = os.path.abspath(os.path.join(self._data_path, load_file)) |
---|
1354 | |
---|
1355 | uri = 'file://' + urllib.request.pathname2url(file) |
---|
1356 | mv.open(uri) |
---|
1357 | |
---|
1358 | # build scrolled window widget and add our appview container |
---|
1359 | sw = Gtk.ScrolledWindow() |
---|
1360 | sw.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC) |
---|
1361 | sw.add(mv) |
---|
1362 | |
---|
1363 | # build an autoexpanding box and add our scrolled window |
---|
1364 | b = Gtk.VBox(homogeneous=False, spacing=0) |
---|
1365 | b.pack_start(sw, expand=True, fill=True, padding=0) |
---|
1366 | |
---|
1367 | # add the box to the parent window and show |
---|
1368 | w.add(b) |
---|
1369 | w.connect('delete-event', goodbye) |
---|
1370 | w.show_all() |
---|
1371 | |
---|
1372 | self._window = w |
---|
1373 | self._appView = mv |
---|
1374 | |
---|
1375 | def run(self): |
---|
1376 | signal.signal(signal.SIGINT, signal.SIG_DFL) |
---|
1377 | Gtk.main() |
---|
1378 | |
---|
1379 | def close(self, p1, p2): |
---|
1380 | Gtk.main_quit(p1, p2); |
---|
1381 | |
---|
1382 | |
---|
1383 | |
---|
1384 | |
---|
1385 | |
---|
1386 | |
---|
1387 | |
---|
1388 | |
---|
1389 | |
---|
1390 | |
---|
1391 | |
---|
1392 | ################################################# |
---|
1393 | |
---|
1394 | |
---|
1395 | class SystemState(object): |
---|
1396 | def __init__(self): |
---|
1397 | # Set initial variables |
---|
1398 | self.is_online = False |
---|
1399 | self.user_name = getuser() |
---|
1400 | self.updates_subscribed = False |
---|
1401 | self.welcome_version = 'Unknown' |
---|
1402 | self.rpi_resize_pending = False |
---|
1403 | self.start_from_menu = False |
---|
1404 | |
---|
1405 | # Get current architecture of system. |
---|
1406 | # Outputs 'i386', 'amd64', etc - Based on packages instead of kernel (eg. i686, x86_64). |
---|
1407 | self.arch = str(subprocess.Popen(['dpkg','--print-architecture'], stdout=subprocess.PIPE).communicate()[0]).strip('\\nb\'') |
---|
1408 | |
---|
1409 | # Get current codename of Ubuntu MATE in use. |
---|
1410 | # Uses first word in lowercase, such as : trusty, wily, xenial |
---|
1411 | self.codename = platform.dist()[2] |
---|
1412 | |
---|
1413 | def run_external_command(command, with_shell=False): |
---|
1414 | if with_shell: |
---|
1415 | raw = str(subprocess.Popen(command, stdout=subprocess.PIPE, shell=True).communicate()[0]) |
---|
1416 | else: |
---|
1417 | raw = str(subprocess.Popen(command, stdout=subprocess.PIPE).communicate()[0]) |
---|
1418 | output = raw.replace("b'","").replace('b"',"").replace("\\n'","").replace("\\n","") |
---|
1419 | return output |
---|
1420 | |
---|
1421 | # Determine which type of session we are in. |
---|
1422 | |
---|
1423 | if os.path.exists('/usr/share/glib-2.0/schemas/zubuntu-mate-live.gschema.override'): |
---|
1424 | self.session_type = 'live' |
---|
1425 | elif self.user_name[:6] == 'guest-': |
---|
1426 | self.session_type = 'guest' |
---|
1427 | elif os.path.isfile(os.path.join('/','boot/','kernel7.img')): |
---|
1428 | self.session_type = 'pi' |
---|
1429 | else: |
---|
1430 | self.session_type = 'normal' |
---|
1431 | |
---|
1432 | # Determine which type of flavour we are in. |
---|
1433 | |
---|
1434 | self.session_flavour = str(run_external_command(['lliurex-version','-f'])) |
---|
1435 | |
---|
1436 | |
---|
1437 | #print('[SystemState] Lliurex Session: ' + self.session_type) |
---|
1438 | #print('[SystemState] Lliurex Flavour: ' + self.session_flavour) |
---|
1439 | |
---|
1440 | # To inform the user if they are running in BIOS or UEFI mode. |
---|
1441 | if os.path.exists("/sys/firmware/efi"): |
---|
1442 | self.boot_mode = 'UEFI' |
---|
1443 | elif self.session_type == 'pi': |
---|
1444 | self.boot_mode = 'Raspberry Pi' |
---|
1445 | elif self.arch == 'powerpc': |
---|
1446 | self.boot_mode = 'Yaboot' |
---|
1447 | else: |
---|
1448 | self.boot_mode = 'BIOS' |
---|
1449 | |
---|
1450 | # Create, then spawn threads |
---|
1451 | thread1 = Thread(target=self.check_internet_connection) |
---|
1452 | thread2 = Thread(target=self.detect_graphics) |
---|
1453 | thread1.start() |
---|
1454 | thread2.start() |
---|
1455 | |
---|
1456 | # Check whether Welcome is subscribed for updates. |
---|
1457 | self.welcome_ppa_file = '/etc/apt/sources.list.d/ubuntu-mate-dev-ubuntu-welcome-' + self.codename + '.list' |
---|
1458 | if os.path.exists(self.welcome_ppa_file): |
---|
1459 | if os.path.getsize(self.welcome_ppa_file) > 0: |
---|
1460 | self.updates_subscribed = True |
---|
1461 | |
---|
1462 | # Accessibility - Enlarge/shrink text based on Font DPI set by the user. |
---|
1463 | if arg.font_dpi_override: |
---|
1464 | font_dpi = arg.font_dpi_override |
---|
1465 | else: |
---|
1466 | try: |
---|
1467 | font_gsettings = Gio.Settings.new('org.mate.font-rendering') |
---|
1468 | font_value = font_gsettings.get_value('dpi') |
---|
1469 | font_dpi = int(float(str(font_value))) |
---|
1470 | arg.print_verbose('Welcome', 'Font DPI is: ' + str(font_dpi)) |
---|
1471 | except: |
---|
1472 | font_dpi = 96 |
---|
1473 | print('[Welcome] Couldn\'t retrieve font DPI. Using default value of ' + str(font_dpi)) |
---|
1474 | |
---|
1475 | if font_dpi < 50: |
---|
1476 | print('[Welcome] DPI below 50. Out of range..') |
---|
1477 | font_dpi = 96 |
---|
1478 | elif font_dpi > 500: |
---|
1479 | print('[Welcome] DPI over 500. Out of range.') |
---|
1480 | font_dpi = 96 |
---|
1481 | |
---|
1482 | zoom_level = 1.0 |
---|
1483 | if font_dpi <= 80: |
---|
1484 | zoom_level = 0.75 |
---|
1485 | elif font_dpi <= 87: |
---|
1486 | zoom_level = 0.85 |
---|
1487 | elif font_dpi <= 94: |
---|
1488 | zoom_level = 0.9 |
---|
1489 | elif font_dpi <= 101: |
---|
1490 | zoom_level = 1.0 # Default DPI is usually 96. |
---|
1491 | elif font_dpi <= 108: |
---|
1492 | zoom_level = 1.1 |
---|
1493 | elif font_dpi <= 115: |
---|
1494 | zoom_level = 1.2 |
---|
1495 | elif font_dpi <= 122: |
---|
1496 | zoom_level = 1.3 |
---|
1497 | elif font_dpi <= 129: |
---|
1498 | zoom_level = 1.4 |
---|
1499 | elif font_dpi >= 130: |
---|
1500 | zoom_level = 1.5 |
---|
1501 | |
---|
1502 | self.dpi = font_dpi |
---|
1503 | self.zoom_level = zoom_level |
---|
1504 | |
---|
1505 | def check_internet_connection(self): |
---|
1506 | print('[Network Test] Checking for internet connectivity... ') |
---|
1507 | url = "http://www.lliurex.net" |
---|
1508 | |
---|
1509 | if arg.simulate_no_connection: |
---|
1510 | print('[Network Test] Simulation argument override. Retrying will reset this.') |
---|
1511 | arg.simulate_no_connection = False |
---|
1512 | self.is_online = False |
---|
1513 | return |
---|
1514 | |
---|
1515 | if arg.simulate_force_connection: |
---|
1516 | print('[Network Test] Simulation argument override. Forcing connection presence.') |
---|
1517 | print('[Network Test] WARNING: Do not attempt to install/remove software offline as this may lead to errors later!') |
---|
1518 | arg.simulate_connection = False |
---|
1519 | self.is_online = True |
---|
1520 | return |
---|
1521 | |
---|
1522 | try: |
---|
1523 | response = urllib.request.urlopen(url, timeout=2).read().decode('utf-8') |
---|
1524 | except socket.timeout: |
---|
1525 | print("[Network Test] -- Socket timed out to URL {0}".format(url)) |
---|
1526 | self.is_online = False |
---|
1527 | except: |
---|
1528 | print("[Network Test] -- Could not establish a connection to '{0}'. ".format(url)) |
---|
1529 | self.is_online = False |
---|
1530 | else: |
---|
1531 | print("[Network Test] Successfully pinged '{0}' ".format(url)) |
---|
1532 | self.is_online = True |
---|
1533 | |
---|
1534 | def detect_graphics(self): |
---|
1535 | # If we're the Raspberry Pi, there is nothing to output. |
---|
1536 | if self.session_type == 'pi': |
---|
1537 | self.graphics_grep = 'Raspberry Pi' |
---|
1538 | self.graphics_vendor = 'Raspberry Pi' |
---|
1539 | return |
---|
1540 | |
---|
1541 | # TODO: Support dual graphic cards. |
---|
1542 | arg.print_verbose('Graphics','Detecting graphics vendor... ') |
---|
1543 | try: |
---|
1544 | output = subprocess.Popen('lspci | grep VGA', stdout=subprocess.PIPE, shell='True').communicate()[0] |
---|
1545 | output = output.decode(encoding='UTF-8') |
---|
1546 | except: |
---|
1547 | # When 'lspci' does not find a VGA controller (this is the case for the RPi 2) |
---|
1548 | arg.print_verbose("Graphics","Couldn't detect a VGA Controller on this system.") |
---|
1549 | output = 'Unknown' |
---|
1550 | |
---|
1551 | # Scan for and set known brand name. |
---|
1552 | if output.find('NVIDIA') != -1: |
---|
1553 | self.graphics_vendor = 'NVIDIA' |
---|
1554 | elif output.find('AMD') != -1: |
---|
1555 | self.graphics_vendor = 'AMD' |
---|
1556 | elif output.find('Intel') != -1: |
---|
1557 | self.graphics_vendor = 'Intel' |
---|
1558 | elif output.find('VirtualBox') != -1: |
---|
1559 | self.graphics_vendor = 'VirtualBox' |
---|
1560 | else: |
---|
1561 | self.graphics_vendor = 'Unknown' |
---|
1562 | |
---|
1563 | self.graphics_grep = repr(output) |
---|
1564 | self.graphics_grep = self.graphics_grep.split("controller: ",1)[1] |
---|
1565 | self.graphics_grep = self.graphics_grep.split("\\n",1)[0] |
---|
1566 | arg.print_verbose("Graphics","Detected: {0}".format(self.graphics_grep)) |
---|
1567 | |
---|
1568 | |
---|
1569 | |
---|
1570 | |
---|
1571 | def get_server_info(self, webkit): |
---|
1572 | print('[LliureX Specs] Gathering LliureX Server information...') |
---|
1573 | |
---|
1574 | msg_srv_started = _('The server is started: ') |
---|
1575 | msg_no_srv_started = _(" You don\'t have the server started yet. You must start the server before starting the next step.") |
---|
1576 | msg_no_srv_started_2 = _(" This step is essential, so you have to do it before anything else") |
---|
1577 | msg_mirror_date = _(' The mirror was done at: ') |
---|
1578 | msg_mirror_not_done = _(' The mirror is not done. Before continue you must do it.') |
---|
1579 | msg_image_done = _(' It seems you have an image done.') |
---|
1580 | msg_image_not_done =_(' It seems you have no image done. Before procedure with the next step you should do one.') |
---|
1581 | |
---|
1582 | # Collect basic system information |
---|
1583 | def run_external_command(command, with_shell=False): |
---|
1584 | if with_shell: |
---|
1585 | raw = str(subprocess.Popen(command, stdout=subprocess.PIPE, shell=True).communicate()[0]) |
---|
1586 | else: |
---|
1587 | raw = str(subprocess.Popen(command, stdout=subprocess.PIPE).communicate()[0]) |
---|
1588 | output = raw.replace("b'","").replace('b"',"").replace("\\n'","").replace("\\n","") |
---|
1589 | return output |
---|
1590 | |
---|
1591 | #Check if Sever is started############################################################################### |
---|
1592 | try: |
---|
1593 | server_started = client.get_variable("", "VariablesManager", "SRV_IP") |
---|
1594 | webkit.execute_script('$("#servidor_iniciat,#servidor_iniciat2").html("'+ msg_srv_started +' '+ server_started +'")') |
---|
1595 | webkit.execute_script('$("#servidor_iniciat,#servidor_iniciat2").addClass("alert alert-info")') |
---|
1596 | except: |
---|
1597 | webkit.execute_script('$("#text_servidor_iniciat").html("' + msg_no_srv_started_2+ '")') |
---|
1598 | webkit.execute_script('$("#text_servidor_iniciat2").html("' + msg_no_srv_started+ '")') |
---|
1599 | webkit.execute_script('$("#icon_servidor_iniciat,#icon_servidor_iniciat2").addClass("fa fa-warning")') |
---|
1600 | webkit.execute_script('$("#servidor_iniciat,#servidor_iniciat2").addClass("alert alert-warning")') |
---|
1601 | print('[LliureX Specs] Failed to retrieve data: servidor_iniciat') |
---|
1602 | |
---|
1603 | #Check mirror in LliureX############################################################################### |
---|
1604 | try: |
---|
1605 | mirror_done = client.get_variable("", "VariablesManager", "LLIUREXMIRROR") |
---|
1606 | print ("[LliureX Specs] The mirror server in LliureX is: " + mirror_done['llx16']['status_mirror']) |
---|
1607 | if mirror_done['llx16']['status_mirror'] == 'Ok': |
---|
1608 | webkit.execute_script('$("#mirror_fet").html("'+ msg_mirror_date +' '+ mirror_done['llx16']['last_mirror_date'] +'")') |
---|
1609 | webkit.execute_script('$("#mirror_fet").addClass("alert alert-info")') |
---|
1610 | else: |
---|
1611 | webkit.execute_script('$("#text_mirror_fet").html("'+ msg_mirror_not_done +'")') |
---|
1612 | webkit.execute_script('$("#icon_mirror_fet").addClass("fa fa-warning")') |
---|
1613 | webkit.execute_script('$("#mirror_fet").addClass("alert alert-warning")') |
---|
1614 | print('[LliureX Specs] Failed to retrieve data: mirror_fet') |
---|
1615 | except: |
---|
1616 | webkit.execute_script('$("#text_mirror_fet").html("'+ msg_mirror_not_done +'")') |
---|
1617 | webkit.execute_script('$("#icon_mirror_fet").addClass("fa fa-warning")') |
---|
1618 | webkit.execute_script('$("#mirror_fet").addClass("alert alert-warning")') |
---|
1619 | print('[LliureX Specs] Failed to retrieve data: mirror_fet') |
---|
1620 | |
---|
1621 | #Are there any client done in the Server?############################################################################### |
---|
1622 | try: |
---|
1623 | imatge_feta = client.getClientList("", "LmdClientManager") |
---|
1624 | if imatge_feta != "[]": |
---|
1625 | webkit.execute_script('$("#imatge_feta").html("'+ msg_image_done +'")') |
---|
1626 | webkit.execute_script('$("#imatge_feta").addClass("alert alert-info")') |
---|
1627 | else: |
---|
1628 | webkit.execute_script('$("#text_imatge_feta").html("'+ msg_image_not_done +'")') |
---|
1629 | webkit.execute_script('$("#icon_imatge_feta").addClass("fa fa-warning")') |
---|
1630 | webkit.execute_script('$("#imatge_feta").addClass("alert alert-warning")') |
---|
1631 | except: |
---|
1632 | webkit.execute_script('$("#text_imatge_feta").html("'+ msg_image_not_done +'")') |
---|
1633 | webkit.execute_script('$("#icon_imatge_feta").addClass("fa fa-warning")') |
---|
1634 | webkit.execute_script('$("#imatge_feta").addClass("alert alert-warning")') |
---|
1635 | print('[LliureX Specs] Failed to retrieve data: imatge_feta') |
---|
1636 | ############################################################################################## |
---|
1637 | |
---|
1638 | |
---|
1639 | |
---|
1640 | def get_system_info(self, webkit): |
---|
1641 | print('[System Specs] Gathering system specifications...') |
---|
1642 | |
---|
1643 | # Prefixes for translation |
---|
1644 | mb_prefix = _("MB") |
---|
1645 | mib_prefix = _("MiB") |
---|
1646 | gb_prefix = _("GB") |
---|
1647 | gib_prefix = _("GiB") |
---|
1648 | |
---|
1649 | # Start collecting advanced system information in the background. |
---|
1650 | # (Python can do other things while this command completes) |
---|
1651 | arg.print_verbose('System Specs', 'Running "inxi" for advanced system information...') |
---|
1652 | try: |
---|
1653 | inxi_raw = subprocess.Popen(['inxi','-c','0','-v','5','-p','-d','-xx'], stdout=subprocess.PIPE) |
---|
1654 | except: |
---|
1655 | print('[System Specs] Failed to execute collect advanced information. Is "inxi" no longer installed?') |
---|
1656 | |
---|
1657 | # Append a failure symbol beforehand in event something goes horribly wrong. |
---|
1658 | stat_error_msg = _("Could not gather data.") |
---|
1659 | html_tag = '<a data-toggle=\'tooltip\' data-placement=\'top\' title=\'' + stat_error_msg + '\'><span class=\'fa fa-warning specs-error\'></span></a>' |
---|
1660 | for element in ['distro', 'kernel', 'motherboard', 'boot-mode', 'cpu-model', 'cpu-speed', 'arch-use', |
---|
1661 | 'arch-supported', 'memory', 'graphics', 'filesystem', 'capacity', 'allocated-space', 'free-space']: |
---|
1662 | webkit.execute_script('$("#spec-' + element + '").html("' + html_tag + '")') |
---|
1663 | |
---|
1664 | # Collect basic system information |
---|
1665 | def run_external_command(command, with_shell=False): |
---|
1666 | if with_shell: |
---|
1667 | raw = str(subprocess.Popen(command, stdout=subprocess.PIPE, shell=True).communicate()[0]) |
---|
1668 | else: |
---|
1669 | raw = str(subprocess.Popen(command, stdout=subprocess.PIPE).communicate()[0]) |
---|
1670 | output = raw.replace("b'","").replace('b"',"").replace("\\n'","").replace("\\n","") |
---|
1671 | return output |
---|
1672 | |
---|
1673 | ## Distro |
---|
1674 | try: |
---|
1675 | arg.print_verbose('System Specs', 'Gathering data: Distribution') |
---|
1676 | distro_description = run_external_command(['lliurex-version']) |
---|
1677 | distro_codename = run_external_command(['lsb_release','-c','-s']) |
---|
1678 | webkit.execute_script('$("#spec-distro").html("Lliurex: ' + distro_description + '")') |
---|
1679 | except: |
---|
1680 | print('[System Specs] Failed to retrieve data: Distribution') |
---|
1681 | |
---|
1682 | ## Kernel |
---|
1683 | try: |
---|
1684 | arg.print_verbose('System Specs', 'Gathering data: Kernel') |
---|
1685 | kernel = run_external_command(['uname','-r']) |
---|
1686 | webkit.execute_script('$("#spec-kernel").html("' + kernel + '")') |
---|
1687 | except: |
---|
1688 | print('[System Specs] Failed to retrieve data: Kernel') |
---|
1689 | |
---|
1690 | ## Motherboard |
---|
1691 | try: |
---|
1692 | arg.print_verbose('System Specs', 'Gathering data: Motherboard') |
---|
1693 | motherboard_name = run_external_command(['cat','/sys/devices/virtual/dmi/id/board_name']) |
---|
1694 | webkit.execute_script('$("#spec-motherboard").html("' + motherboard_name + '")') |
---|
1695 | except: |
---|
1696 | print('[System Specs] Failed to retrieve data: Motherboard') |
---|
1697 | |
---|
1698 | ## CPU Details |
---|
1699 | arg.print_verbose('System Specs', 'Gathering data: CPU') |
---|
1700 | try: |
---|
1701 | cpu_model = run_external_command(['lscpu | grep "name"'], True).split(': ')[1] |
---|
1702 | webkit.execute_script('$("#spec-cpu-model").html("' + cpu_model + '")') |
---|
1703 | except: |
---|
1704 | print('[System Specs] Failed to retrieve data: CPU Model') |
---|
1705 | try: |
---|
1706 | try: |
---|
1707 | # Try obtaining the maximum speed first. |
---|
1708 | cpu_speed = int(run_external_command(['lscpu | grep "max"'], True).split(': ')[1].strip(' ').split('.')[0]) |
---|
1709 | except: |
---|
1710 | # Otherwise, fetch the CPU's MHz. |
---|
1711 | cpu_speed = int(run_external_command(['lscpu | grep "CPU MHz"'], True).split(': ')[1].strip(' ').split('.')[0]) |
---|
1712 | |
---|
1713 | webkit.execute_script('$("#spec-cpu-speed").html("' + str(cpu_speed) + ' MHz")') |
---|
1714 | except: |
---|
1715 | print('[System Specs] Failed to retrieve data: CPU Speed') |
---|
1716 | |
---|
1717 | try: |
---|
1718 | if self.arch == 'i386': |
---|
1719 | cpu_arch_used = '32-bit' |
---|
1720 | elif self.arch == 'amd64': |
---|
1721 | cpu_arch_used = '64-bit' |
---|
1722 | else: |
---|
1723 | cpu_arch_used = self.arch |
---|
1724 | webkit.execute_script('$("#spec-arch-use").html("' + cpu_arch_used + '")') |
---|
1725 | except: |
---|
1726 | print('[System Specs] Failed to retrieve data: CPU Arch in Use') |
---|
1727 | |
---|
1728 | try: |
---|
1729 | cpu_arch_supported = run_external_command(['lscpu | grep "modo"'], True).split(':')[1] |
---|
1730 | webkit.execute_script('$("#spec-arch-supported").html("' + cpu_arch_supported + '")') |
---|
1731 | except: |
---|
1732 | print('[System Specs] Failed to retrieve data: CPU Supported Arch') |
---|
1733 | |
---|
1734 | ## Root partition (where Ubuntu MATE is installed) and the rest of that disk. |
---|
1735 | try: |
---|
1736 | if self.session_type == 'live': |
---|
1737 | webkit.execute_script('$(".specs-hide-live-session").hide()') |
---|
1738 | else: |
---|
1739 | arg.print_verbose('System Specs', 'Gathering data: Storage') |
---|
1740 | ## Gather entire disk data |
---|
1741 | root_partition = run_external_command(['mount | grep "on / "'], True).split(' ')[0] |
---|
1742 | if root_partition[:-2] == "/dev/sd": # /dev/sdXY |
---|
1743 | root_dev = root_partition[:-1] |
---|
1744 | if root_partition[:-2] == "/dev/hd": # /dev/hdXY |
---|
1745 | root_dev = root_partition[:-1] |
---|
1746 | if root_partition[:-3] == "/dev/mmcblk": # /dev/mmcblkXpY |
---|
1747 | root_dev = root_partition[:-2] |
---|
1748 | else: |
---|
1749 | root_dev = root_partition[:-1] # Generic |
---|
1750 | disk_dev_name = root_dev.split('/')[2] |
---|
1751 | arg.print_verbose('System Specs', 'LliureX 16 is installed on disk: ' + root_dev) |
---|
1752 | rootfs = os.statvfs('/') |
---|
1753 | root_size = rootfs.f_blocks * rootfs.f_frsize |
---|
1754 | root_free = rootfs.f_bavail * rootfs.f_frsize |
---|
1755 | root_used = root_size - root_free |
---|
1756 | entire_disk = run_external_command(['lsblk -b | grep "' + disk_dev_name + '" | grep "disk"'], True) |
---|
1757 | entire_disk = int(entire_disk.split()[3]) |
---|
1758 | |
---|
1759 | ## Perform calculations across units |
---|
1760 | capacity_GB = round(entire_disk/1000/1000/1000,1) |
---|
1761 | capacity_GiB = round(entire_disk/1024/1024/1024,1) |
---|
1762 | allocated_GB = round(root_size/1000/1000/1000,1) |
---|
1763 | allocated_GiB = round(root_size/1024/1024/1024,1) |
---|
1764 | used_GB = round(root_used/1000/1000/1000,1) |
---|
1765 | used_GiB = round(root_used/1024/1024/1024,1) |
---|
1766 | free_GB = round(root_free/1000/1000/1000,1) |
---|
1767 | free_GiB = round(root_free/1024/1024/1024,1) |
---|
1768 | other_GB = round((entire_disk-root_size)/1000/1000/1000,1) |
---|
1769 | other_GiB = round((entire_disk-root_size)/1024/1024/1024,1) |
---|
1770 | |
---|
1771 | # Show megabytes/mebibytes (in red) if gigabytes are too small. |
---|
1772 | if capacity_GB <= 1: |
---|
1773 | capacity_GB = str(round(entire_disk/1000/1000,1)) + ' ' + mb_prefix |
---|
1774 | capacity_GiB = str(round(entire_disk/1024/1024,1)) + ' ' + mib_prefix |
---|
1775 | else: |
---|
1776 | capacity_GB = str(capacity_GB) + ' ' + gb_prefix |
---|
1777 | capacity_GiB = str(capacity_GiB) + ' ' + gib_prefix |
---|
1778 | |
---|
1779 | if allocated_GB <= 1: |
---|
1780 | allocated_GB = str(round(root_size/1000/1000,1)) + ' ' + mb_prefix |
---|
1781 | allocated_GiB = str(round(root_size/1024/1024,1)) + ' ' + mib_prefix |
---|
1782 | else: |
---|
1783 | allocated_GB = str(allocated_GB) + ' ' + gb_prefix |
---|
1784 | allocated_GiB = str(allocated_GiB) + ' ' + gib_prefix |
---|
1785 | |
---|
1786 | if used_GB <= 1: |
---|
1787 | used_GB = str(round(root_used/1000/1000,1)) + ' ' + mb_prefix |
---|
1788 | used_GiB = str(round(root_used/1024/1024,1)) + ' ' + mib_prefix |
---|
1789 | else: |
---|
1790 | used_GB = str(used_GB) + ' ' + gb_prefix |
---|
1791 | used_GiB = str(used_GiB) + ' ' + gib_prefix |
---|
1792 | |
---|
1793 | if free_GB <= 1: |
---|
1794 | free_GB = str(round(root_free/1000/1000,1)) + ' ' + mb_prefix |
---|
1795 | free_GiB = str(round(root_free/1024/1024,1)) + ' ' + mib_prefix |
---|
1796 | webkit.execute_script('$("#spec-free-space").addClass("specs-error")') |
---|
1797 | else: |
---|
1798 | free_GB = str(free_GB) + ' ' + gb_prefix |
---|
1799 | free_GiB = str(free_GiB) + ' ' + gib_prefix |
---|
1800 | |
---|
1801 | if other_GB <= 1: |
---|
1802 | other_GB = str(round((entire_disk-root_size)/1000/1000,1)) + ' ' + mb_prefix |
---|
1803 | other_GiB = str(round((entire_disk-root_size)/1024/1024,1)) + ' ' + mib_prefix |
---|
1804 | else: |
---|
1805 | other_GB = str(other_GB) + ' ' + gb_prefix |
---|
1806 | other_GiB = str(other_GiB) + ' ' + gib_prefix |
---|
1807 | |
---|
1808 | ## Append data to HTML. |
---|
1809 | webkit.execute_script('$("#spec-filesystem").html("' + root_partition + '")') |
---|
1810 | webkit.execute_script('$("#spec-capacity").html("' + capacity_GB + ' <span class=\'secondary-value\'>(' + capacity_GiB + ')</span>")') |
---|
1811 | webkit.execute_script('$("#spec-allocated-space").html("' + allocated_GB + ' <span class=\'secondary-value\'>(' + allocated_GiB + ')</span>")') |
---|
1812 | webkit.execute_script('$("#spec-used-space").html("' + used_GB + ' <span class=\'secondary-value\'>(' + used_GiB + ')</span>")') |
---|
1813 | webkit.execute_script('$("#spec-free-space").html("' + free_GB + ' <span class=\'secondary-value\'>(' + free_GiB + ')</span>")') |
---|
1814 | webkit.execute_script('$("#spec-other-space").html("' + other_GB + ' <span class=\'secondary-value\'>(' + other_GiB + ')</span>")') |
---|
1815 | |
---|
1816 | ## Calculate representation across physical disk |
---|
1817 | disk_percent_UM_used = int(round(root_used / entire_disk * 100)) * 2 |
---|
1818 | disk_percent_UM_free = int(round(root_free / entire_disk * 100)) * 2 |
---|
1819 | disk_percent_other = (200 - disk_percent_UM_used - disk_percent_UM_free) |
---|
1820 | arg.print_verbose('System Specs', ' --- Disk: ' + root_dev) |
---|
1821 | arg.print_verbose('System Specs', ' --- * OS Used: ' + str(root_used) + ' bytes (' + str(disk_percent_UM_used/2) + '%)') |
---|
1822 | arg.print_verbose('System Specs', ' --- * OS Free: ' + str(root_free) + ' bytes (' + str(disk_percent_UM_free/2) + '%)') |
---|
1823 | arg.print_verbose('System Specs', ' --- = Other Partitions: ' + str(entire_disk - root_size) + ' bytes (' + str(disk_percent_other/2) + '%)') |
---|
1824 | webkit.execute_script("$('#disk-used').width('" + str(disk_percent_UM_used) + "px');") |
---|
1825 | webkit.execute_script("$('#disk-free').width('" + str(disk_percent_UM_free) + "px');") |
---|
1826 | webkit.execute_script("$('#disk-other').width('" + str(disk_percent_other) + "px');") |
---|
1827 | except: |
---|
1828 | print('[System Specs] Failed to retrieve data: Storage') |
---|
1829 | |
---|
1830 | ## RAM |
---|
1831 | try: |
---|
1832 | arg.print_verbose('System Specs', 'Gathering Data: RAM') |
---|
1833 | ram_bytes = run_external_command(['free -b | grep "Memoria:" '], True) |
---|
1834 | ram_bytes = float(ram_bytes.split()[1]) |
---|
1835 | if round(ram_bytes / 1024 / 1024) < 1024: |
---|
1836 | ram_xb = str(round(ram_bytes / 1000 / 1000, 1)) + ' ' + mb_prefix |
---|
1837 | ram_xib = str(round(ram_bytes / 1024 / 1024, 1)) + ' ' + mib_prefix |
---|
1838 | else: |
---|
1839 | ram_xb = str(round(ram_bytes / 1000 / 1000 / 1000, 1)) + ' ' + gb_prefix |
---|
1840 | ram_xib = str(round(ram_bytes / 1024 / 1024 / 1024, 1)) + ' ' + gib_prefix |
---|
1841 | ram_string = ram_xb + ' <span class=\'secondary-value\'>(' + ram_xib + ')</span>' |
---|
1842 | webkit.execute_script('$("#spec-memory").html("' + ram_string + '")') |
---|
1843 | except: |
---|
1844 | print('[System Specs] Failed to retrieve data: RAM (Memory)') |
---|
1845 | |
---|
1846 | ## Graphics |
---|
1847 | webkit.execute_script('$("#spec-graphics").html("' + self.graphics_grep + '")') |
---|
1848 | |
---|
1849 | ## Collect missing data differently for some architectures. |
---|
1850 | if systemstate.arch == 'powerpc': |
---|
1851 | ## Motherboard & Revision |
---|
1852 | try: |
---|
1853 | arg.print_verbose('System Specs', 'Gathering alternate data: PowerPC Motherboard') |
---|
1854 | mb_model = run_external_command(['grep','motherboard','/proc/cpuinfo']).split(': ')[1] |
---|
1855 | mb_rev = run_external_command(['grep','revision','/proc/cpuinfo']).split(': ')[1] |
---|
1856 | webkit.execute_script('$("#spec-motherboard").html("' + mb_model + ' ' + mb_rev + '")') |
---|
1857 | except: |
---|
1858 | arg.print_verbose('System Specs', 'Failed to gather data: PowerPC Motherboard') |
---|
1859 | |
---|
1860 | ## CPU and Clock Speed |
---|
1861 | try: |
---|
1862 | arg.print_verbose('System Specs', 'Gathering alternate data: PowerPC CPU') |
---|
1863 | cpu_model = run_external_command(['grep','cpu','/proc/cpuinfo']).split(': ')[1] |
---|
1864 | cpu_speed = run_external_command(['grep','clock','/proc/cpuinfo']).split(': ')[1] |
---|
1865 | webkit.execute_script('$("#spec-cpu-model").html("' + cpu_model + '")') |
---|
1866 | webkit.execute_script('$("#spec-cpu-speed").html("' + str(cpu_speed) + '")') |
---|
1867 | except: |
---|
1868 | arg.print_verbose('System Specs', 'Failed to gather data: PowerPC CPU') |
---|
1869 | |
---|
1870 | ## Device Name |
---|
1871 | try: |
---|
1872 | arg.print_verbose('System Specs', 'Gathering alternate data: PowerPC Model Name') |
---|
1873 | mb_name = run_external_command(['grep','detected','/proc/cpuinfo']).split(': ')[1] |
---|
1874 | webkit.execute_script('$("#spec-motherboard").append(" / ' + mb_name + '")') |
---|
1875 | except: |
---|
1876 | arg.print_verbose('System Specs', 'Failed to gather data: PowerPC Model Name') |
---|
1877 | |
---|
1878 | ## Boot Mode / PowerMac Generation |
---|
1879 | try: |
---|
1880 | arg.print_verbose('System Specs', 'Gathering alternate data: PowerMac Generation') |
---|
1881 | mac_generation = run_external_command(['grep','pmac-generation','/proc/cpuinfo']).split(': ')[1] |
---|
1882 | webkit.execute_script('$("#spec-boot-mode").html("Yaboot (' + mac_generation + ')")') |
---|
1883 | except: |
---|
1884 | arg.print_verbose('System Specs', 'Failed to gather data: PowerMac Generation') |
---|
1885 | |
---|
1886 | # Append advanced system information |
---|
1887 | try: |
---|
1888 | arg.print_verbose('System Specs', 'Waiting for inxi process to finish...') |
---|
1889 | inxi_output = str(inxi_raw.communicate()[0]) |
---|
1890 | inxi_output = inxi_output.replace("b'","").replace("\\n","\n") |
---|
1891 | webkit.execute_script("$('#specs-inxi').html('')") |
---|
1892 | for line in inxi_output.split('\n'): |
---|
1893 | webkit.execute_script("$('#specs-inxi').append('" + line.strip('"').strip("'") + "<br>')") |
---|
1894 | print('[System Specs] Successfully appended advanced system information.') |
---|
1895 | except: |
---|
1896 | print('[System Specs] Failed to append advanced system information or communicate with "inxi" process.') |
---|
1897 | |
---|
1898 | # Check internet connectivity status. |
---|
1899 | if self.is_online: |
---|
1900 | webkit.execute_script('$("#specs-has-net").show()') |
---|
1901 | webkit.execute_script('$("#specs-has-no-net").hide()') |
---|
1902 | else: |
---|
1903 | webkit.execute_script('$("#specs-has-net").hide()') |
---|
1904 | webkit.execute_script('$("#specs-has-no-net").show()') |
---|
1905 | |
---|
1906 | # Change icon depending on what type of device we are using. |
---|
1907 | if self.session_type == 'pi': |
---|
1908 | webkit.execute_script('$("#specs-device-rpi").show()') |
---|
1909 | webkit.execute_script('$(".specs-hide-pi").hide()') |
---|
1910 | elif self.arch == 'powerpc': |
---|
1911 | webkit.execute_script('$("#specs-device-powerpc").show()') |
---|
1912 | webkit.execute_script('$(".specs-hide-ppc").hide()') |
---|
1913 | elif self.graphics_vendor == 'VirtualBox': |
---|
1914 | webkit.execute_script('$("#specs-device-vbox").show()') |
---|
1915 | webkit.execute_script('$(".specs-hide-vbox").hide()') |
---|
1916 | elif self.session_type == 'live': |
---|
1917 | webkit.execute_script('$("#specs-live-session").show()') |
---|
1918 | webkit.execute_script('$(".specs-hide-live").hide()') |
---|
1919 | else: |
---|
1920 | webkit.execute_script('$("#specs-device-normal").show()') |
---|
1921 | |
---|
1922 | # Display UEFI/BIOS boot mode. |
---|
1923 | if systemstate.arch == 'i386' or systemstate.arch == 'amd64': |
---|
1924 | webkit.execute_script('$("#spec-boot-mode").html("' + self.boot_mode + '")') |
---|
1925 | |
---|
1926 | # Hide root storage info if in a live session. |
---|
1927 | if self.session_type == 'live': |
---|
1928 | webkit.execute_script('$(".spec-3").hide()') |
---|
1929 | |
---|
1930 | if self.session_flavour == 'music': |
---|
1931 | webkit.execute_script('$(".spec-3").hide()') |
---|
1932 | |
---|
1933 | # Data cached, ready to display. |
---|
1934 | webkit.execute_script('$("#specs-loading").fadeOut("fast")') |
---|
1935 | webkit.execute_script('$("#specs-tabs").fadeIn("fast")') |
---|
1936 | webkit.execute_script('$("#specs-basic").fadeIn("medium")') |
---|
1937 | webkit.execute_script('setCursorNormal()') |
---|
1938 | |
---|
1939 | |
---|
1940 | |
---|
1941 | def rpi_resize(self, action, webkit=None): |
---|
1942 | if action == 'do-resize': |
---|
1943 | subprocess.call(['pkexec', '/usr/lib/lliurex-mate/lliurex-mate-welcome-rpi2-partition-resize']) |
---|
1944 | |
---|
1945 | def notify(subject, body, icon): |
---|
1946 | Notify.init(_('Raspberry Pi Partition Resize')) |
---|
1947 | resize_notify=Notify.Notification.new(subject, body, icon) |
---|
1948 | resize_notify.show() |
---|
1949 | |
---|
1950 | try: |
---|
1951 | with open('/tmp/notify_rpi_status') as status_file: |
---|
1952 | status_code = int(status_file.read()) |
---|
1953 | except: |
---|
1954 | status_code = 0 |
---|
1955 | |
---|
1956 | try: |
---|
1957 | with open('/tmp/notify_rpi_text') as misc_file: |
---|
1958 | misc_text = misc_file.read() |
---|
1959 | except: |
---|
1960 | misc_text = "" |
---|
1961 | |
---|
1962 | if status_code == 1: |
---|
1963 | notify( _("Root partition has been resized."), _("The filesystem will be enlarged upon the next reboot."), 'dialog-information' ) |
---|
1964 | self.rpi_resize_pending = True |
---|
1965 | webkit.execute_script('$("#rpi-resized").hide()') |
---|
1966 | webkit.execute_script('$("#rpi-not-resized").hide()') |
---|
1967 | webkit.execute_script('$("#rpi-restart-now").show()') |
---|
1968 | elif status_code == 2: |
---|
1969 | notify( _("Don't know how to expand."), misc_text + ' ' + _("does not exist or is not a symlink."), 'dialog-error' ) |
---|
1970 | elif status_code == 3: |
---|
1971 | notify( _("Don't know how to expand."), misc_text + ' ' + _("is not an SD card."), 'dialog-error' ) |
---|
1972 | elif status_code == 4: |
---|
1973 | notify( _("Don't know how to expand."), _("Your partition layout is not currently supported by this tool."), 'dialog-error' ) |
---|
1974 | elif status_code == 5: |
---|
1975 | notify( _("Don't know how to expand."), misc_text + ' ' + _("is not the last partition."), 'dialog-error' ) |
---|
1976 | else: |
---|
1977 | notify( _("Failed to run resize script."), _("The returned error code is:") + str(status_code), 'dialog-error' ) |
---|
1978 | print('[Welcome] Unrecognised return code for Raspberry Pi resize: ' + str(status_code)) |
---|
1979 | |
---|
1980 | app._appView._push_config() |
---|
1981 | |
---|
1982 | elif action == 'check': |
---|
1983 | if os.path.exists('/.resized'): |
---|
1984 | resized = True |
---|
1985 | else: |
---|
1986 | resized = False |
---|
1987 | |
---|
1988 | if resized: |
---|
1989 | webkit.execute_script('$("#rpi-resized").show()') |
---|
1990 | webkit.execute_script('$("#rpi-not-resized").hide()') |
---|
1991 | else: |
---|
1992 | webkit.execute_script('$("#rpi-resized").hide()') |
---|
1993 | webkit.execute_script('$("#rpi-not-resized").show()') |
---|
1994 | |
---|
1995 | if self.rpi_resize_pending: |
---|
1996 | webkit.execute_script('$("#rpi-resized").hide()') |
---|
1997 | webkit.execute_script('$("#rpi-not-resized").hide()') |
---|
1998 | webkit.execute_script('$("#rpi-restart-now").show()') |
---|
1999 | |
---|
2000 | elif action == 'reboot': |
---|
2001 | subprocess.call(['lliurex-session-save','--shutdown-dialog']) |
---|
2002 | |
---|
2003 | |
---|
2004 | class DynamicApps(object): |
---|
2005 | def __init__(self): |
---|
2006 | # Load JSON Index into Memory |
---|
2007 | self.reload_index() |
---|
2008 | |
---|
2009 | # Variables to remember common details. |
---|
2010 | self.all_categories = ['Accessories', 'Education', 'Games', 'Graphics', 'Internet', 'Office', 'Programming', 'Media', 'SysTools', 'UnivAccess', 'Servers', 'MoreApps', 'Lliurex'] |
---|
2011 | self.hide_non_free = True |
---|
2012 | |
---|
2013 | # Reading the apt cache later. |
---|
2014 | self._apt_cache = apt.Cache() |
---|
2015 | |
---|
2016 | # Indicate that operations are in progress. |
---|
2017 | self.operations_busy = False |
---|
2018 | |
---|
2019 | # Get the version of Welcome in use. |
---|
2020 | for pkgname in self._apt_cache.keys(): |
---|
2021 | if 'lliurex-mate-welcome' in pkgname: |
---|
2022 | systemstate.welcome_version = self._apt_cache['lliurex-mate-welcome'].installed.version |
---|
2023 | break |
---|
2024 | print('[Welcome] Version: ' + systemstate.welcome_version) |
---|
2025 | |
---|
2026 | |
---|
2027 | ###### JSON Index Structure |
---|
2028 | # |
---|
2029 | # ===== Structure Overview ===== |
---|
2030 | # { |
---|
2031 | # "Category" { - Application category. |
---|
2032 | # "application-id" { - Unique string identifier for application, apps sorted A-Z. Hyphens preferred. |
---|
2033 | # "variable": "data", - Variable containing single data. |
---|
2034 | # "list": ["This is a line.", |
---|
2035 | # "The same line."] - Variable containing a 'list' of data. |
---|
2036 | # "group": { "variable": "data" } - Group containing data. |
---|
2037 | # } |
---|
2038 | # } |
---|
2039 | # } |
---|
2040 | # |
---|
2041 | # ** Standard JSON rules apply. Watch out for the commas. |
---|
2042 | # ** Important!! Use ’ instead of ' for an apostrophe character. |
---|
2043 | |
---|
2044 | # ===== Variable Index ===== |
---|
2045 | # Variable Type Required? Description |
---|
2046 | # ----------------------- ---------- ---------- --------------------------------------------- |
---|
2047 | # name string Yes Name of the application as displayed to the user. |
---|
2048 | # img string Yes Name of image. Excluding ".png" extension. |
---|
2049 | # main-package string Yes Package used to detect if it's installed. |
---|
2050 | # launch-command string No Command to launch the installed program. Can be ignored for no launch option. |
---|
2051 | # install-packages string * Packages to install/reinstall. Comma separated. |
---|
2052 | # remove-packages string * Packages to remove. Comma separated. |
---|
2053 | # upgradable boolean * This package is only for upgrading. |
---|
2054 | # upgrade-packages string * Packages to upgrade. Comma separated. |
---|
2055 | # description list Yes Description of the application. Use usual HTML tags for formatting. Can be left blank if unlisted. |
---|
2056 | # alternate-to string No If the app is similar or has an alternate. Can be ignored. |
---|
2057 | # subcategory string Yes Used for filtering applications within the category. Eg. "Partitioning", "Audio Production". |
---|
2058 | # open-source boolean Yes Proprietary or Open Source? |
---|
2059 | # url-info string Yes URL to the web page for more information. |
---|
2060 | # url-android string No URL if there is an associated Android app. Can be ignored. |
---|
2061 | # url-ios string No URL if there is an associated Android app. Can be ignored. |
---|
2062 | # arch string Yes Supported architectures for this app. Comma seperated. |
---|
2063 | # releases string Yes Supported versions of Ubuntu MATE to show this application. Comma seperated. |
---|
2064 | # working boolean Yes Show/hide visibility of this application. |
---|
2065 | # notes string No Optional developer notes for the application. |
---|
2066 | # |
---|
2067 | #### * Only if applicable to application. |
---|
2068 | |
---|
2069 | # ===== Pre-installation Index ===== |
---|
2070 | # |
---|
2071 | # "pre-install": { - Required group of data containing pre-installation procedures. |
---|
2072 | # "trusty": { - Different releases may have different operations. |
---|
2073 | # "variable": "data" - See table below for possible operations. |
---|
2074 | # }, |
---|
2075 | # "all": { - Use "all" to specify all other releases. This should be last. |
---|
2076 | # "variable": "data" If there is only one instruction, |
---|
2077 | # } |
---|
2078 | # } |
---|
2079 | # |
---|
2080 | # method string Yes Pre-configuration methods. Multiple can be specified with a plus '+'. |
---|
2081 | # "skip" = Package is already in archive. |
---|
2082 | # "ppa" = Add a PPA. Specify (2), optionally (1). |
---|
2083 | # "partner-repo" = Add the Ubuntu Partners Repo. |
---|
2084 | # "manual" = Get keys and write a sources.list file. (3) |
---|
2085 | # source-file (1) string No Source file to update, excluding the ".list" extension. |
---|
2086 | # enable-ppa (2) string * Name of PPA to add, eg. "ppa:somebody/someapp". |
---|
2087 | # apt-key-url (3) string * Retrieve the key from URL. |
---|
2088 | # apt-key-server (3) list * Retrieve the key from a server. |
---|
2089 | # "server-address" = Eg. "keyserver.ubuntu.com" |
---|
2090 | # "key" = Eg. "D2C19886" |
---|
2091 | # apt-sources (3) list * Contents for the sources file. Each variable is a new line. |
---|
2092 | # |
---|
2093 | #### These keys words can be given as placeholders: |
---|
2094 | # |
---|
2095 | # CODENAME = Current Ubuntu release, eg. "xenial". |
---|
2096 | # OSVERSION = Current Ubuntu version, eg "16.04". |
---|
2097 | # |
---|
2098 | |
---|
2099 | def reload_index(self): |
---|
2100 | try: |
---|
2101 | print('[Apps] Reading index...') |
---|
2102 | json_path = os.path.abspath(os.path.join(app._data_path, 'js/applications.json')) |
---|
2103 | with open(json_path) as data_file: |
---|
2104 | self.index = json.load(data_file) |
---|
2105 | print('[Apps] Successfully loaded index.') |
---|
2106 | except Exception as e: |
---|
2107 | self.index = None |
---|
2108 | print("[Apps] ERROR: Software Index JSON is invalid or missing!") |
---|
2109 | print("------------------------------------------------------------") |
---|
2110 | print("Exception:") |
---|
2111 | print(str(e)) |
---|
2112 | print("------------------------------------------------------------") |
---|
2113 | |
---|
2114 | def set_app_info(self, category, program_id): |
---|
2115 | self.app_name = self.index[category][program_id]['name'] |
---|
2116 | self.app_img = self.index[category][program_id]['img'] |
---|
2117 | self.app_main_package = self.index[category][program_id]['main-package'] |
---|
2118 | self.app_launch_command = self.index[category][program_id]['launch-command'] |
---|
2119 | self.app_upgrade_only = False |
---|
2120 | try: |
---|
2121 | if self.index[category][program_id]['upgradable']: |
---|
2122 | self.app_upgrade_only = True |
---|
2123 | self.app_upgrade_packages = self.index[category][program_id]['upgrade-packages'] |
---|
2124 | except: |
---|
2125 | self.app_upgrade_only = False |
---|
2126 | |
---|
2127 | if not self.app_upgrade_only: |
---|
2128 | self.app_install_packages = self.index[category][program_id]['install-packages'] |
---|
2129 | self.app_remove_packages = self.index[category][program_id]['remove-packages'] |
---|
2130 | self.app_description = '' |
---|
2131 | for line in self.index[category][program_id]['description']: |
---|
2132 | self.app_description = self.app_description + ' ' + line |
---|
2133 | self.app_alternate_to = self.index[category][program_id]['alternate-to'] |
---|
2134 | self.app_subcategory = self.index[category][program_id]['subcategory'] |
---|
2135 | self.app_open_source = self.index[category][program_id]['open-source'] |
---|
2136 | self.app_url_info = self.index[category][program_id]['url-info'] |
---|
2137 | self.app_url_android = self.index[category][program_id]['url-android'] |
---|
2138 | self.app_url_ios = self.index[category][program_id]['url-ios'] |
---|
2139 | self.app_arch = self.index[category][program_id]['arch'] |
---|
2140 | self.app_releases = self.index[category][program_id]['releases'] |
---|
2141 | self.app_working = self.index[category][program_id]['working'] |
---|
2142 | |
---|
2143 | def populate_categories(self, webkit): |
---|
2144 | ''' List all of the applications supported on the current architecture. ''' |
---|
2145 | total_added = 0 |
---|
2146 | total_skipped = 0 |
---|
2147 | total_unsupported = 0 |
---|
2148 | |
---|
2149 | # Don't attempt to continue if the index is missing/incorrectly parsed. |
---|
2150 | if not self.index: |
---|
2151 | print('[Apps] ERROR: Application index not loaded. Cannot populate categories.') |
---|
2152 | return |
---|
2153 | |
---|
2154 | # Strings |
---|
2155 | str_nothing_here = _("Sorry, Welcome could not feature any software for this category that is compatible on this system.") |
---|
2156 | str_upgraded = _("LliureX: ") |
---|
2157 | str_alternate_to = _('Alternative to:') |
---|
2158 | str_hide = _("Hide") |
---|
2159 | str_show = _("Show") |
---|
2160 | str_install = _("Install") |
---|
2161 | str_reinstall = _("Reinstall") |
---|
2162 | str_remove = _("Remove") |
---|
2163 | str_upgrade = _("Upgrade") |
---|
2164 | str_launch = _("Launch") |
---|
2165 | str_license = _("License") |
---|
2166 | str_platform = _("Platform") |
---|
2167 | str_category = _("Category") |
---|
2168 | str_website = _("Website") |
---|
2169 | str_screenshot = _("Screenshot") |
---|
2170 | str_source = _("Source") |
---|
2171 | str_source_ppa = '<span class="fa fa-cube"></span> ' |
---|
2172 | str_source_manual = '<span class="fa fa-globe"></span></a> ' |
---|
2173 | str_source_partner = '<img src="img/logos/ubuntu-mono.png" width="16px" height="16px"/> ' + _('Canonical Partner Repository') |
---|
2174 | str_source_skip = '<img src="img/logos/ubuntu-mono.png" width="16px" height="16px"/> ' + _('Ubuntu Repository') |
---|
2175 | str_unknown = _('Unknown') |
---|
2176 | |
---|
2177 | # Get the app data from each category and list them. |
---|
2178 | for category in self.all_categories: |
---|
2179 | arg.print_verbose('Apps', ' ------ Processing: ' + category + ' ------') |
---|
2180 | |
---|
2181 | # Convert to a list to work with. Sort alphabetically. |
---|
2182 | category_items = list(self.index[category].keys()) |
---|
2183 | category_items.sort() |
---|
2184 | |
---|
2185 | # Keep a count of apps in case there are none to list. |
---|
2186 | apps_here = 0 |
---|
2187 | |
---|
2188 | # Keep track of the subcategories of the apps in this category so we can filter them. |
---|
2189 | subcategories = [] |
---|
2190 | |
---|
2191 | # Enumerate each program in this category. |
---|
2192 | for program_id in category_items: |
---|
2193 | self.set_app_info(category, program_id) |
---|
2194 | |
---|
2195 | # Only list the program if it's working. |
---|
2196 | if not self.app_working: |
---|
2197 | arg.print_verbose('Apps', ' Unlisted: ' + self.app_name) |
---|
2198 | total_skipped = total_skipped + 1 |
---|
2199 | continue |
---|
2200 | |
---|
2201 | # Only list the program if it supports the current architecture in use. |
---|
2202 | supported = False |
---|
2203 | supported_arch = False |
---|
2204 | supported_release = False |
---|
2205 | |
---|
2206 | for architecture in self.app_arch.split(','): |
---|
2207 | if architecture == systemstate.arch: |
---|
2208 | supported_arch = True |
---|
2209 | |
---|
2210 | # Only list the program if it's available for the current release. |
---|
2211 | for release in self.app_releases.split(','): |
---|
2212 | if release == systemstate.codename: |
---|
2213 | supported_release = True |
---|
2214 | |
---|
2215 | if supported_arch and supported_release: |
---|
2216 | supported = True |
---|
2217 | |
---|
2218 | if not supported: |
---|
2219 | arg.print_verbose('Apps', ' Unsupported: ' + self.app_name + ' (Only for architectures: ' + self.app_arch + ' and releases: ' + self.app_releases + ')' ) |
---|
2220 | total_unsupported = total_unsupported + 1 |
---|
2221 | continue |
---|
2222 | |
---|
2223 | # If the app has made it this far, it can be added to the grid. |
---|
2224 | # CSS breaks with dots (.), so any must become hyphens (-). |
---|
2225 | arg.print_verbose('Apps', ' Added: ' + self.app_name) |
---|
2226 | subcategories.append(self.app_subcategory) |
---|
2227 | html_buffer = '' |
---|
2228 | css_class = program_id.replace('.','-') |
---|
2229 | css_subcategory = self.app_subcategory.replace(' ','-') |
---|
2230 | |
---|
2231 | # "Normal" packages that can be installed/removed by the user. |
---|
2232 | if self.app_open_source: |
---|
2233 | html_buffer = html_buffer + '<div id="' + css_class + '" class="app-entry filter-' + css_subcategory + '">' |
---|
2234 | else: |
---|
2235 | html_buffer = html_buffer + '<div id="' + css_class + '" class="app-entry filter-' + css_subcategory + ' proprietary">' |
---|
2236 | html_buffer = html_buffer + '<div class="row-fluid">' |
---|
2237 | html_buffer = html_buffer + '<div class="span2 center-inside">' |
---|
2238 | html_buffer = html_buffer + '<img src="img/applications/' + self.app_img + '.png">' |
---|
2239 | html_buffer = html_buffer + '<span class="fa fa-check-circle fa-2x installed-check ' + css_class + '-remove"></span>' |
---|
2240 | html_buffer = html_buffer + '</div><div class="span10">' |
---|
2241 | html_buffer = html_buffer + '<p><b class="' + css_class + '-text">' + self.app_name + '</b></p>' |
---|
2242 | html_buffer = html_buffer + '<p class="' + css_class + '-text">' + self.app_description + '</p>' |
---|
2243 | |
---|
2244 | # Check any "Upgrade" packages if the PPA has already been added. |
---|
2245 | upgraded = False |
---|
2246 | if self.app_upgrade_only: |
---|
2247 | try: |
---|
2248 | listname = dynamicapps.index[category][program_id]['pre-install']['all']['source-file'] |
---|
2249 | listname = listname.replace('OSVERSION',preinstallation.os_version).replace('CODENAME',preinstallation.codename) |
---|
2250 | if os.path.exists(os.path.join('/', 'etc', 'apt', 'sources.list.d', listname+'.list')): |
---|
2251 | upgraded = True |
---|
2252 | html_buffer = html_buffer + '<h5 class="' + css_class + '-text"><span class="fa fa-check-circle"></span> ' + str_upgraded + '</h5>' |
---|
2253 | except: |
---|
2254 | pass |
---|
2255 | |
---|
2256 | if not self.app_alternate_to == None: |
---|
2257 | html_buffer = html_buffer + '<ul><li class="' + css_class + '-text"><b>' + str_alternate_to + ' </b><i>' + self.app_alternate_to + '</i></li></ul>' |
---|
2258 | html_buffer = html_buffer + '<p class="text-right">' |
---|
2259 | html_buffer = html_buffer + '<a id="info-show-' + css_class + '" class="btn" href="cmd://app-info-show?' + css_class + '"><span class="fa fa-chevron-down"></span> ' + str_show + '</a> ' |
---|
2260 | html_buffer = html_buffer + '<a hidden id="info-hide-' + css_class + '" class="btn" href="cmd://app-info-hide?' + css_class + '"><span class="fa fa-chevron-up"></span> ' + str_hide + '</a> ' |
---|
2261 | |
---|
2262 | # "Regular" packages - can be installed or removed with one-click by the user. |
---|
2263 | if not self.app_upgrade_only: |
---|
2264 | html_buffer = html_buffer + '<span class="' + css_class + '-applying"> <span class="' + css_class + '-applying-status"></span> <img src="img/welcome/processing.gif" width="24px" height="24px"/></span>' |
---|
2265 | html_buffer = html_buffer + '<a class="' + css_class + '-install btn btn-success" href="cmd://install-appid?' + program_id + '"><span class="fa fa-download"></span> ' + str_install + '</a> ' |
---|
2266 | html_buffer = html_buffer + '<a class="' + css_class + '-reinstall btn btn-warning" href="cmd://install-appid?' + program_id + '" data-toggle="tooltip" data-placement="top" title="' + str_reinstall + '"><span class="fa fa-refresh"></span></a> ' |
---|
2267 | html_buffer = html_buffer + '<a class="' + css_class + '-remove btn btn-danger" href="cmd://remove-appid?' + program_id + '" data-toggle="tooltip" data-placement="top" title="' + str_remove + '"><span class="fa fa-trash"></span></a> ' |
---|
2268 | |
---|
2269 | # "Upgradable" packages - usually pre-installed but have a more up-to-date repository. |
---|
2270 | if self.app_upgrade_only: |
---|
2271 | arg.print_verbose('Apps', 'Upgrade: ' + self.app_name) |
---|
2272 | if not upgraded: |
---|
2273 | html_buffer = html_buffer + '<a class="' + css_class + '-upgrade btn btn-warning" href="cmd://upgrade-appid?' + program_id + '"><span class="fa fa-level-up"></span> ' + str_upgrade + '</a> ' |
---|
2274 | |
---|
2275 | if not self.app_launch_command == None: |
---|
2276 | html_buffer = html_buffer + '<a class="' + css_class + '-launch btn btn-inverse" href="cmd://launch-appid?' + program_id + '"><img src="img/applications/' + self.app_img + '.png" width="20px" height="20px" /> ' + str_launch + '</a> ' |
---|
2277 | |
---|
2278 | # More details section. |
---|
2279 | html_buffer = html_buffer + '</p><div hidden id="details-' + css_class + '">' |
---|
2280 | |
---|
2281 | ## Determine string for license |
---|
2282 | if self.app_open_source: |
---|
2283 | license_string = _('Open Source') |
---|
2284 | else: |
---|
2285 | license_string = _('Proprietary') |
---|
2286 | |
---|
2287 | ## Determine supported platforms |
---|
2288 | platform_string = '' |
---|
2289 | for arch in self.app_arch.split(','): |
---|
2290 | if arch == 'i386': |
---|
2291 | platform_string = platform_string + '<span class="i386"><span class="i386 fa fa-laptop"></span> 32-bit</span> ' |
---|
2292 | elif arch =='amd64': |
---|
2293 | platform_string = platform_string + '<span class="amd64"><span class="fa fa-laptop"></span> 64-bit</span> ' |
---|
2294 | elif arch =='armhf': |
---|
2295 | platform_string = platform_string + '<span class="armhf"><span class="fa fa-tv"></span> aarch32 (ARMv7)</span> ' |
---|
2296 | elif arch =='powerpc': |
---|
2297 | platform_string = platform_string + '<span class="powerpc"><span class="fa fa-desktop"></span> PowerPC</span> ' |
---|
2298 | |
---|
2299 | ## Add Android / iOS app links if necessary. |
---|
2300 | if not self.app_url_android == None: |
---|
2301 | platform_string = platform_string + '<a href="cmd://link?' + self.app_url_android + '"><span class="fa fa-android"></span> Android</a> ' |
---|
2302 | |
---|
2303 | if not self.app_url_ios == None: |
---|
2304 | platform_string = platform_string + '<a href="cmd://link?' + self.app_url_ios + '"><span class="fa fa-apple"></span> iOS</a> ' |
---|
2305 | |
---|
2306 | ## Add details about the source of this file. |
---|
2307 | try: |
---|
2308 | preinstall = dynamicapps.index[category][program_id]['pre-install'] |
---|
2309 | codenames = list(preinstall.keys()) |
---|
2310 | target = None |
---|
2311 | for name in codenames: |
---|
2312 | if name == systemstate.codename: |
---|
2313 | target = name |
---|
2314 | break |
---|
2315 | if not target: |
---|
2316 | target = 'all' |
---|
2317 | |
---|
2318 | methods = preinstall[target]['method'].split('+') |
---|
2319 | self.source_info = [] |
---|
2320 | if len(methods) > 1: |
---|
2321 | multiple_sources = True |
---|
2322 | else: |
---|
2323 | multiple_sources = False |
---|
2324 | |
---|
2325 | for method in methods: |
---|
2326 | if method == 'skip': |
---|
2327 | self.source_info.insert(0, str_source_skip) |
---|
2328 | |
---|
2329 | elif method == 'partner-repo': |
---|
2330 | self.source_info.insert(0, str_source_partner) |
---|
2331 | |
---|
2332 | elif method == 'ppa': |
---|
2333 | ppa = preinstall[target]['enable-ppa'] |
---|
2334 | ppa_author = ppa.split(':')[1].split('/')[0] |
---|
2335 | ppa_archive = ppa.split(':')[1].split('/')[1] |
---|
2336 | self.source_info.insert(0, str_source_ppa + ' <a href="cmd://link?https://launchpad.net/~' + ppa_author + '/+archive/ubuntu/' + ppa_archive + '">' + ppa + '</a>') |
---|
2337 | |
---|
2338 | elif method == 'manual': |
---|
2339 | apt_source = ''.join(preinstall[target]['apt-sources']) |
---|
2340 | manual_text = str_source_manual + ' ' + str_unknown |
---|
2341 | for substring in apt_source.split(' '): |
---|
2342 | if substring[:4] == 'http': |
---|
2343 | apt_source = substring.replace('OSVERSION',preinstallation.os_version).replace('CODENAME',preinstallation.codename) |
---|
2344 | manual_text = str_source_manual + ' ' + apt_source |
---|
2345 | break |
---|
2346 | self.source_info.insert(0, manual_text) |
---|
2347 | |
---|
2348 | except: |
---|
2349 | print('[Apps] WARNING: Error occurred while processing pre-configuration! Skipped Source: ' + program_id) |
---|
2350 | self.source_info = [str_unknown] |
---|
2351 | |
---|
2352 | ## Write contents of the table. |
---|
2353 | html_buffer = html_buffer + '<table class="more-details table table-striped">' |
---|
2354 | html_buffer = html_buffer + '<tr><th>' + str_license + '</th><td>' + license_string + '</td></tr>' |
---|
2355 | html_buffer = html_buffer + '<tr><th>' + str_platform + '</th><td>' + platform_string + '</td></tr>' |
---|
2356 | html_buffer = html_buffer + '<tr><th>' + str_category + '</th><td>' + self.app_subcategory + '</td></tr>' |
---|
2357 | |
---|
2358 | ## Add a website URL if there is one. |
---|
2359 | if self.app_url_info: |
---|
2360 | html_buffer = html_buffer + '<tr><th>' + str_website + '</th><td><a href="cmd://link?' + self.app_url_info + '">' + self.app_url_info + '</a></td></tr>' |
---|
2361 | |
---|
2362 | ## Add the source for this application. |
---|
2363 | if multiple_sources: |
---|
2364 | html_buffer = html_buffer + '<tr><th>' + str_source + '</th><td><ul>' |
---|
2365 | for item in self.source_info: |
---|
2366 | html_buffer = html_buffer + '<li>' + item + '</li>' |
---|
2367 | html_buffer = html_buffer + '</td></tr></ul>' |
---|
2368 | else: |
---|
2369 | html_buffer = html_buffer + '<tr><th>' + str_source + '</th><td>' + self.source_info[0] + '</td></tr>' |
---|
2370 | |
---|
2371 | ## Add a screenshot if there is any. |
---|
2372 | ## Images should be labelled the same as 'img' and increment starting at 1. |
---|
2373 | screenshots = 1 |
---|
2374 | screenshots_end = False |
---|
2375 | screenshot_buffer = '' |
---|
2376 | while not screenshots_end: |
---|
2377 | screenshot_path = os.path.join(app._data_path + 'img/applications/screenshots/' + self.app_img + '-' + str(screenshots) + '.jpg') |
---|
2378 | if os.path.exists(screenshot_path): |
---|
2379 | screenshot_buffer = screenshot_buffer + '<a class="screenshot-link" href="cmd://screenshot?' + self.app_img + '-' + str(screenshots) + '"><img src="' + screenshot_path + '" class="screenshot"/></a>' |
---|
2380 | screenshots = screenshots + 1 |
---|
2381 | else: |
---|
2382 | screenshots_end = True |
---|
2383 | |
---|
2384 | if not screenshots == 1: |
---|
2385 | html_buffer = html_buffer + '<tr><th>' + str_screenshot + '</th><td>' + screenshot_buffer + '</td></tr>' |
---|
2386 | |
---|
2387 | html_buffer = html_buffer + '</table>' |
---|
2388 | |
---|
2389 | # End the div's for this application. |
---|
2390 | html_buffer = html_buffer + '</div><br><hr class="soften"></div></div></div>' |
---|
2391 | |
---|
2392 | # Append buffer to page |
---|
2393 | webkit.execute_script('$("#' + category + '").append(\'' + html_buffer + '\')') |
---|
2394 | webkit.execute_script('$("#info-hide-' + css_class + '").hide()') |
---|
2395 | |
---|
2396 | # Keep track of how many apps added. |
---|
2397 | apps_here = apps_here + 1 |
---|
2398 | total_added = total_added + 1 |
---|
2399 | |
---|
2400 | # Display a message if there is nothing for this category. |
---|
2401 | if apps_here == 0: |
---|
2402 | webkit.execute_script('$("#' + category + '").append("<p class=\'center\'><span class=\'fa fa-warning\'></span> ' + str_nothing_here + '</p>")') |
---|
2403 | |
---|
2404 | # Post actions to page |
---|
2405 | ## Colour the architecture currently in use. |
---|
2406 | webkit.execute_script('$(".' + systemstate.arch + '").addClass("arch-in-use")') |
---|
2407 | |
---|
2408 | # Process filters for this category. |
---|
2409 | filters = list(set(subcategories)) |
---|
2410 | filters.sort() |
---|
2411 | for string in filters: |
---|
2412 | css_subcategory = string.replace(' ','-') |
---|
2413 | webkit.execute_script('$("#Filter-' + category + '").append(\'<option value="' + css_subcategory + '">' + string + '</option>\')') |
---|
2414 | |
---|
2415 | # "Stats for nerds" |
---|
2416 | total_apps = total_added + total_skipped + total_unsupported |
---|
2417 | arg.print_verbose('Apps','------------------') |
---|
2418 | arg.print_verbose('Apps','Applications added: ' + str(total_added)) |
---|
2419 | arg.print_verbose('Apps','Applications unsupported on this architecture: ' + str(total_unsupported)) |
---|
2420 | arg.print_verbose('Apps','Applications that are broken or not suitable for inclusion: ' + str(total_skipped)) |
---|
2421 | arg.print_verbose('Apps','Total number of applications: ' + str(total_apps)) |
---|
2422 | arg.print_verbose('Apps','------------------') |
---|
2423 | |
---|
2424 | def populate_featured_apps(self, webkit): |
---|
2425 | arg.print_verbose('Apps', '---- Populating Featured Apps Grid ----') |
---|
2426 | # Randomly generate a list of apps to feature if supported on this architecture. |
---|
2427 | possible_apps = [] |
---|
2428 | for category in self.all_categories: |
---|
2429 | category_items = list(self.index[category].keys()) |
---|
2430 | for program_id in category_items: |
---|
2431 | if systemstate.arch in self.index[category][program_id]['arch']: |
---|
2432 | possible_apps.append(self.index[category][program_id]['img']) |
---|
2433 | |
---|
2434 | random.shuffle(possible_apps) |
---|
2435 | for no in range(0,17): |
---|
2436 | arg.print_verbose('Apps', str(no) + '. ' + possible_apps[no]) |
---|
2437 | webkit.execute_script("addToGrid('" + possible_apps[no] + "');") |
---|
2438 | webkit.execute_script("initGrid();") |
---|
2439 | arg.print_verbose('Apps','------------------') |
---|
2440 | |
---|
2441 | def modify_app(self, webkit, action, program_id): |
---|
2442 | ''' Installs, removes or upgrades an application. ''' |
---|
2443 | # Indicate changes are in progress. |
---|
2444 | css_class = program_id.replace('.','-') |
---|
2445 | webkit.execute_script("$('." + css_class + "-applying').show();") |
---|
2446 | webkit.execute_script("$('." + css_class + "-launch').hide();") |
---|
2447 | webkit.execute_script("$('." + css_class + "-install').hide();") |
---|
2448 | webkit.execute_script("$('." + css_class + "-reinstall').hide();") |
---|
2449 | webkit.execute_script("$('." + css_class + "-remove').hide();") |
---|
2450 | webkit.execute_script("$('." + css_class + "-upgrade').hide();") |
---|
2451 | webkit.execute_script("$('." + css_class + "-text').css('color','#000');") |
---|
2452 | |
---|
2453 | # Text to display when applying changes. |
---|
2454 | install_text = _("Installing...") |
---|
2455 | remove_text = _("Removing...") |
---|
2456 | upgrade_text = _("Upgrading...") |
---|
2457 | |
---|
2458 | # Asynchronous apt process |
---|
2459 | if action == 'install': |
---|
2460 | webkit.execute_script("$('." + css_class + "-applying-status').html('" + install_text + "');") |
---|
2461 | preinstallation.process_packages(program_id, 'install') |
---|
2462 | elif action == 'remove': |
---|
2463 | webkit.execute_script("$('." + css_class + "-applying-status').html('" + remove_text + "');") |
---|
2464 | preinstallation.process_packages(program_id, 'remove') |
---|
2465 | elif action == 'upgrade': |
---|
2466 | webkit.execute_script("$('." + css_class + "-applying-status').html('" + upgrade_text + "');") |
---|
2467 | preinstallation.process_packages(program_id, 'upgrade') |
---|
2468 | else: |
---|
2469 | print('[Apps] An unknown action was requested.') |
---|
2470 | |
---|
2471 | # Refresh the page to reflect changes (if any). |
---|
2472 | self._apt_cache.close() |
---|
2473 | self._apt_cache = apt.Cache() |
---|
2474 | self.update_app_status(webkit, program_id) |
---|
2475 | |
---|
2476 | def update_app_status(self, webkit, program_id): |
---|
2477 | ''' Update the web page for an individual application. ''' |
---|
2478 | |
---|
2479 | # Don't attempt to continue if the index is missing/incorrectly parsed. |
---|
2480 | if not self.index: |
---|
2481 | print('[Apps] ERROR: Application index not loaded. Cannot update application status.') |
---|
2482 | return |
---|
2483 | |
---|
2484 | # Check whether the application is installed or not. |
---|
2485 | main_package = self.get_attribute_for_app(program_id, 'main-package') |
---|
2486 | try: |
---|
2487 | if self._apt_cache[main_package].is_installed: |
---|
2488 | this_installed = True |
---|
2489 | arg.print_verbose('Apps', ' Installed: ' + main_package) |
---|
2490 | else: |
---|
2491 | this_installed = False |
---|
2492 | arg.print_verbose('Apps', 'Not present: ' + main_package) |
---|
2493 | except: |
---|
2494 | this_installed = False |
---|
2495 | arg.print_verbose('Apps', 'Not present: ' + main_package) |
---|
2496 | |
---|
2497 | # Replace any dots with dashes, as they are unsupported in CSS. |
---|
2498 | css_class = program_id.replace('.','-') |
---|
2499 | |
---|
2500 | # Update appearance on this page. |
---|
2501 | webkit.execute_script("$('." + css_class + "-applying').hide();") |
---|
2502 | if this_installed: |
---|
2503 | webkit.execute_script("$('." + css_class + "-launch').show();") |
---|
2504 | webkit.execute_script("$('." + css_class + "-install').hide();") |
---|
2505 | webkit.execute_script("$('." + css_class + "-reinstall').show();") |
---|
2506 | webkit.execute_script("$('." + css_class + "-remove').show();") |
---|
2507 | webkit.execute_script("$('." + css_class + "-upgrade').show();") |
---|
2508 | else: |
---|
2509 | webkit.execute_script("$('." + css_class + "-launch').hide();") |
---|
2510 | webkit.execute_script("$('." + css_class + "-install').show();") |
---|
2511 | webkit.execute_script("$('." + css_class + "-reinstall').hide();") |
---|
2512 | webkit.execute_script("$('." + css_class + "-remove').hide();") |
---|
2513 | webkit.execute_script("$('." + css_class + "-upgrade').hide();") |
---|
2514 | |
---|
2515 | def update_all_app_status(self, webkit): |
---|
2516 | ''' Update the webpage whether all indexed applications are installed or not. ''' |
---|
2517 | |
---|
2518 | # Don't attempt to continue if the index is missing/incorrectly parsed. |
---|
2519 | if not self.index: |
---|
2520 | print('[Apps] ERROR: Application index not loaded. Cannot update page.') |
---|
2521 | return |
---|
2522 | |
---|
2523 | # Enumerate each program and check each one from the index. |
---|
2524 | arg.print_verbose('Apps', '---- Checking cache for installed applications ----') |
---|
2525 | for category in self.all_categories: |
---|
2526 | category_items = list(self.index[category].keys()) |
---|
2527 | for program_id in category_items: |
---|
2528 | main_package = self.index[category][program_id]['main-package'] |
---|
2529 | # Only check if it's supported on this architecture. |
---|
2530 | if systemstate.arch in self.index[category][program_id]['arch']: |
---|
2531 | self.update_app_status(webkit, program_id) |
---|
2532 | else: |
---|
2533 | continue |
---|
2534 | |
---|
2535 | arg.print_verbose('Apps', '----------------------------------------') |
---|
2536 | |
---|
2537 | def get_attribute_for_app(self, requested_id, attribute): |
---|
2538 | ''' Retrieves a specific attribute from a listed application, |
---|
2539 | without specifying its category. ''' |
---|
2540 | for category in list(self.index.keys()): |
---|
2541 | category_items = list(self.index[category].keys()) |
---|
2542 | for program_id in category_items: |
---|
2543 | if program_id == requested_id: |
---|
2544 | if not attribute == 'category': |
---|
2545 | return self.index[category][program_id][attribute] |
---|
2546 | else: |
---|
2547 | return category |
---|
2548 | |
---|
2549 | def launch_app(self, appid): |
---|
2550 | ''' Launch an application directly from Welcome ''' |
---|
2551 | program_name = self.get_attribute_for_app(appid, 'name') |
---|
2552 | program_command = self.get_attribute_for_app(appid, 'launch-command') |
---|
2553 | print('[Apps] Launched "' + program_name + '" (Command: "' + program_command + '").') |
---|
2554 | try: |
---|
2555 | subprocess.Popen(program_command.split(' ')) |
---|
2556 | except: |
---|
2557 | print('[Apps] Failed to launch command: ' + program_command) |
---|
2558 | title = _("Software Boutique") |
---|
2559 | ok_label = _("OK") |
---|
2560 | text_error = _("An error occurred while launching PROGRAM_NAME. Please consider re-installing the application.").replace('PROGRAM_NAME', program_name) + \ |
---|
2561 | '\n\n' + _("Command:") + ' "' + program_command + '"' |
---|
2562 | messagebox = subprocess.Popen(['zenity', |
---|
2563 | '--error', |
---|
2564 | '--title=' + title, |
---|
2565 | "--text=" + text_error, |
---|
2566 | "--ok-label=" + ok_label, |
---|
2567 | '--window-icon=error', |
---|
2568 | '--timeout=15']) |
---|
2569 | |
---|
2570 | def apply_filter(self, webkit, filter_value, nonfree_toggle=False): |
---|
2571 | sub_css_class = 'filter-' + filter_value |
---|
2572 | |
---|
2573 | # Toggle visibility of non-free software. |
---|
2574 | if nonfree_toggle: |
---|
2575 | if self.hide_non_free: |
---|
2576 | self.hide_non_free = False |
---|
2577 | webkit.execute_script('$("#nonFreeCheckBox").addClass("fa-square");') |
---|
2578 | webkit.execute_script('$("#nonFreeCheckBox").removeClass("fa-check-square");') |
---|
2579 | else: |
---|
2580 | self.hide_non_free = True |
---|
2581 | webkit.execute_script('$("#nonFreeCheckBox").removeClass("fa-square");') |
---|
2582 | webkit.execute_script('$("#nonFreeCheckBox").addClass("fa-check-square");') |
---|
2583 | |
---|
2584 | if filter_value == 'none': |
---|
2585 | arg.print_verbose('Apps','Filter reset.') |
---|
2586 | webkit.execute_script('$(".app-entry").show();') |
---|
2587 | if self.hide_non_free: |
---|
2588 | arg.print_verbose('Apps','Hiding all proprietary software.') |
---|
2589 | webkit.execute_script('$(".proprietary").hide();') |
---|
2590 | return |
---|
2591 | else: |
---|
2592 | arg.print_verbose('Apps','Applying filter: ' + filter_value) |
---|
2593 | webkit.execute_script('$(".app-entry").hide();') |
---|
2594 | |
---|
2595 | for category in self.all_categories: |
---|
2596 | category_items = list(self.index[category].keys()) |
---|
2597 | for program_id in category_items: |
---|
2598 | app_subcategory = self.index[category][program_id]['subcategory'].replace(' ','-') |
---|
2599 | app_open_source = self.index[category][program_id]['open-source'] |
---|
2600 | |
---|
2601 | # If the application is closed source and we're told to hide it. |
---|
2602 | if not app_open_source and self.hide_non_free: |
---|
2603 | webkit.execute_script('$("#' + program_id.replace('.','-') + '").hide();') |
---|
2604 | continue |
---|
2605 | |
---|
2606 | # Only show if subcategory matches. |
---|
2607 | if app_subcategory.replace(' ','-') == filter_value: |
---|
2608 | webkit.execute_script('$("#' + program_id.replace('.','-') + '").show();') |
---|
2609 | |
---|
2610 | def show_screenshot(self, filename): |
---|
2611 | ssw = ScreenshotWindow(filename) |
---|
2612 | |
---|
2613 | |
---|
2614 | class ScreenshotWindow(Gtk.Window): |
---|
2615 | ''' Displays a simple window when enlarging a screenshot. ''' |
---|
2616 | |
---|
2617 | # FIXME: Destroy this window when finished as it prevents the app from closing via the "Close" button and bloats memory. |
---|
2618 | |
---|
2619 | def __init__(self, filename): |
---|
2620 | # Strings for this child window. |
---|
2621 | title_string = 'Preview Screenshot' |
---|
2622 | close_string = 'Close' |
---|
2623 | path = app._data_path + '/img/applications/screenshots/' + filename + '.jpg' |
---|
2624 | |
---|
2625 | # Build a basic pop up window containing the screenshot at its full dimensions. |
---|
2626 | Gtk.Window.__init__(self, title=title_string) |
---|
2627 | self.overlay = Gtk.Overlay() |
---|
2628 | self.add(self.overlay) |
---|
2629 | self.background = Gtk.Image.new_from_file(path) |
---|
2630 | self.overlay.add(self.background) |
---|
2631 | self.grid = Gtk.Grid() |
---|
2632 | self.overlay.add_overlay(self.grid) |
---|
2633 | self.connect('button-press-event', self.destroy_window) # Click anywhere to close the window. |
---|
2634 | self.connect('delete-event', Gtk.main_quit) |
---|
2635 | self.set_position(Gtk.WindowPosition.CENTER) |
---|
2636 | self.set_resizable(False) |
---|
2637 | # FIXME: Set the cursor to a hand, like it was a link. |
---|
2638 | #~ self.get_root_window().set_cursor(Gdk.Cursor(Gdk.CursorType.HAND1)) |
---|
2639 | self.show_all() |
---|
2640 | Gtk.main() |
---|
2641 | |
---|
2642 | def destroy_window(self, widget, dummy=None): |
---|
2643 | self.destroy() |
---|
2644 | |
---|
2645 | |
---|
2646 | class Arguments(object): |
---|
2647 | '''Check arguments passed the application.''' |
---|
2648 | |
---|
2649 | def __init__(self): |
---|
2650 | self.verbose_enabled = False |
---|
2651 | self.simulate_arch = None |
---|
2652 | self.simulate_session = None |
---|
2653 | self.simulate_session_flavour = None |
---|
2654 | self.simulate_codename = None |
---|
2655 | self.simulate_no_connection = False |
---|
2656 | self.simulate_force_connection = False |
---|
2657 | self.jump_software_page = False |
---|
2658 | self.simulate_software_changes = False |
---|
2659 | self.locale = None |
---|
2660 | self.jump_to = None |
---|
2661 | self.font_dpi_override = None |
---|
2662 | self.start_from_menu = False |
---|
2663 | |
---|
2664 | for arg in sys.argv: |
---|
2665 | if arg == '--help': |
---|
2666 | print('\LliureX Welcome Parameters\n Intended for debugging and testing purposes only!\n') |
---|
2667 | print('\nUsage: lliurex-mate-welcome [arguments]') |
---|
2668 | print(' -v --verbose Show more details.') |
---|
2669 | print(' --force-arch=<ARCH> Simulate a specific architecture.') |
---|
2670 | print(' "i386", "amd64" or "armhf" or "powerpc"') |
---|
2671 | print(' --force-session=<TYPE> Simulate a specific architecture.') |
---|
2672 | print(' "guest", "live", "pi", "vbox"') |
---|
2673 | print(' --force-codename=<NAME> Simulate a specific LliureX codename release.') |
---|
2674 | print(' Examples: "trusty", "wily" or "xenial"') |
---|
2675 | print(' --force-flavour=<TYPE> Simulate a specific Session in LliureX.') |
---|
2676 | print(' "server", "desktop", "client", "infantil", "music", "pime"') |
---|
2677 | print(' --force-no-net Simulate no internet connection.') |
---|
2678 | print(' --force-net Simulate a working internet connection.') |
---|
2679 | print(' --software-only Open Welcome only for the software selections.') |
---|
2680 | print(' --simulate-changes Simulate software package changes without modifying the system.') |
---|
2681 | print(' --locale=<LOCALE> Locale to use e.g. fr_FR.') |
---|
2682 | print(' --jump-to=<page> Open a specific page, excluding html extension.') |
---|
2683 | print(' --font-dpi=<number> Override the font size by specifying a font DPI.') |
---|
2684 | print('') |
---|
2685 | exit() |
---|
2686 | |
---|
2687 | if arg == '--verbose' or arg == '-v': |
---|
2688 | print('[Debug] Verbose mode enabled.') |
---|
2689 | self.verbose_enabled = True |
---|
2690 | |
---|
2691 | if arg.startswith('--force-arch'): |
---|
2692 | try: |
---|
2693 | self.simulate_arch = arg.split('--force-arch=')[1] |
---|
2694 | if not self.simulate_arch == 'i386' and not self.simulate_arch == 'amd64' and not self.simulate_arch == 'armhf' and not self.simulate_arch == 'powerpc': |
---|
2695 | print('[Debug] Unrecognised architecture: ' + self.simulate_arch) |
---|
2696 | exit() |
---|
2697 | else: |
---|
2698 | print('[Debug] Simulating architecture: ' + self.simulate_arch) |
---|
2699 | except: |
---|
2700 | print('[Debug] Invalid arguments for "--force-arch"') |
---|
2701 | exit() |
---|
2702 | |
---|
2703 | if arg.startswith('--force-session'): |
---|
2704 | try: |
---|
2705 | self.simulate_session = arg.split('--force-session=')[1] |
---|
2706 | if not self.simulate_session == 'guest' and not self.simulate_session == 'live' and not self.simulate_session == 'pi' and not self.simulate_session == 'vbox': |
---|
2707 | print('[Debug] Unrecognised session type: ' + self.simulate_session) |
---|
2708 | exit() |
---|
2709 | else: |
---|
2710 | print('[Debug] Simulating session: ' + self.simulate_session) |
---|
2711 | except: |
---|
2712 | print('[Debug] Invalid arguments for "--force-session"') |
---|
2713 | exit() |
---|
2714 | |
---|
2715 | if arg.startswith('--force-flavour'): |
---|
2716 | try: |
---|
2717 | self.simulate_session_flavour = arg.split('--force-flavour=')[1] |
---|
2718 | if not self.simulate_session_flavour == 'music' and not self.simulate_session_flavour == 'infantil' and not self.simulate_session_flavour == 'pime' and not self.simulate_session_flavour == 'desktop' and not self.simulate_session_flavour == 'client' and not self.simulate_session_flavour == 'server': |
---|
2719 | print('[Debug] Unrecognised session flavour: ' + self.simulate_session_flavour) |
---|
2720 | exit() |
---|
2721 | else: |
---|
2722 | print('[Debug] Simulating flavour: ' + self.simulate_session_flavour) |
---|
2723 | except: |
---|
2724 | print('[Debug] Invalid arguments for "--force-flavour"') |
---|
2725 | exit() |
---|
2726 | |
---|
2727 | if arg.startswith('--force-codename'): |
---|
2728 | self.simulate_codename = arg.split('--force-codename=')[1] |
---|
2729 | print('[Debug] Simulating LliureX release: ' + self.simulate_codename) |
---|
2730 | |
---|
2731 | if arg == '--force-no-net': |
---|
2732 | print('[Debug] Simulating the application without an internet connection.') |
---|
2733 | self.simulate_no_connection = True |
---|
2734 | |
---|
2735 | if arg == '--force-net': |
---|
2736 | print('[Debug] Forcing the application to think we\'re connected with an internet connection.') |
---|
2737 | self.simulate_force_connection = True |
---|
2738 | |
---|
2739 | if arg == '--software-only': |
---|
2740 | print('[Welcome] Starting in software selections only mode.') |
---|
2741 | self.jump_software_page = True |
---|
2742 | |
---|
2743 | if arg == '--simulate-changes': |
---|
2744 | print('[Debug] Any changes to software will be simulated without modifying the actual system.') |
---|
2745 | self.simulate_software_changes = True |
---|
2746 | |
---|
2747 | if arg.startswith('--locale='): |
---|
2748 | self.locale = arg.split('--locale=')[1] |
---|
2749 | print('[Debug] Setting locale to: ' + self.locale) |
---|
2750 | |
---|
2751 | if arg.startswith('--jump-to='): |
---|
2752 | self.jump_to = arg.split('--jump-to=')[1] |
---|
2753 | print('[Debug] Opening page: ' + self.jump_to + '.html') |
---|
2754 | |
---|
2755 | if arg.startswith('--force'): |
---|
2756 | print('[Debug] Start from menu') |
---|
2757 | self.start_from_menu = True |
---|
2758 | |
---|
2759 | if arg.startswith('--font-dpi='): |
---|
2760 | try: |
---|
2761 | self.font_dpi_override = int(arg.split('--font-dpi=')[1]) |
---|
2762 | except: |
---|
2763 | print('[Debug] Invalid Override Font DPI specified. Ignoring.') |
---|
2764 | return |
---|
2765 | print('[Debug] Overriding font DPI to ' + str(self.font_dpi_override) + '.') |
---|
2766 | |
---|
2767 | def print_verbose(self, feature, text): |
---|
2768 | if self.verbose_enabled: |
---|
2769 | print('[' + feature + '] ' + text) |
---|
2770 | |
---|
2771 | def override_arch(self): |
---|
2772 | if not self.simulate_arch == None: |
---|
2773 | systemstate.arch = self.simulate_arch |
---|
2774 | |
---|
2775 | def override_session(self): |
---|
2776 | if not self.simulate_session == None: |
---|
2777 | if self.simulate_session == 'vbox': |
---|
2778 | systemstate.graphics_vendor = 'VirtualBox' |
---|
2779 | systemstate.graphics_grep = 'VirtualBox' |
---|
2780 | else: |
---|
2781 | systemstate.session_type = self.simulate_session |
---|
2782 | |
---|
2783 | def override_flavour(self): |
---|
2784 | if not self.simulate_session_flavour == None: |
---|
2785 | systemstate.session_flavour = self.simulate_session_flavour |
---|
2786 | |
---|
2787 | def override_codename(self): |
---|
2788 | if not self.simulate_codename == None: |
---|
2789 | systemstate.codename = self.simulate_codename |
---|
2790 | |
---|
2791 | def check_start_menu(self): |
---|
2792 | if not self.start_from_menu == False: |
---|
2793 | systemstate.start_from_menu = self.start_from_menu |
---|
2794 | |
---|
2795 | |
---|
2796 | |
---|
2797 | if __name__ == "__main__": |
---|
2798 | |
---|
2799 | user = os.environ.get('USER') |
---|
2800 | groups = os.getgroups() |
---|
2801 | # Process any parameters passed to the program. |
---|
2802 | arg = Arguments() |
---|
2803 | |
---|
2804 | # Application Initialization |
---|
2805 | set_proc_title() |
---|
2806 | systemstate = SystemState() |
---|
2807 | app = WelcomeApp() |
---|
2808 | dynamicapps = DynamicApps() |
---|
2809 | preinstallation = PreInstallation() |
---|
2810 | |
---|
2811 | # Argument Overrides |
---|
2812 | arg.override_arch() |
---|
2813 | arg.override_session() |
---|
2814 | arg.override_flavour() |
---|
2815 | arg.override_codename() |
---|
2816 | arg.check_start_menu() |
---|
2817 | print('[Welcome] Application Started.') |
---|
2818 | print('[SystemState] Lliurex Session: ' + systemstate.session_type) |
---|
2819 | print('[SystemState] Lliurex Flavour: ' + systemstate.session_flavour) |
---|
2820 | if 10004 in groups or user == 'netadmin' and systemstate.start_from_menu == False: |
---|
2821 | print('[Init] Netadmin or member of students') |
---|
2822 | exit() |
---|
2823 | else: |
---|
2824 | app.run() |
---|