Browse Source

progress on python port, passed the halfway point now.

sisyphus
Dustin Falgout 8 years ago
parent
commit
cbdb33eb97
  1. 2
      .gitignore
  2. 2
      meson.build
  3. 10
      web-greeter/bridge/Config.py
  4. 51
      web-greeter/bridge/Greeter.py
  5. 79
      web-greeter/bridge/ThemeUtils.py
  6. 32
      web-greeter/greeter.py
  7. 38
      web-greeter/interceptor.py
  8. 27
      web-greeter/meson.build
  9. 105
      web-greeter/resources/js/ThemeUtils.js
  10. 13874
      web-greeter/resources/js/_vendor/moment-with-locales.min.js
  11. 45
      web-greeter/resources/js/bootstrap.js
  12. 36
      web-greeter/resources/js/docs/Greeter.js
  13. 21
      web-greeter/resources/js/docs/GreeterConfig.js

2
.gitignore vendored

@ -1,3 +1,5 @@
bundle.js
resources.py
build/** build/**
!build/.gitignore !build/.gitignore
!build/ci/Dockerfile !build/ci/Dockerfile

2
meson.build

@ -1,4 +1,4 @@
project('lightdm-webkit2-greeter', 'c', version: '2.2.2', license: 'GPL-3') project('lightdm-webkit2-greeter', 'python', version: '3.0.0', license: 'GPL-3')
# ================================== # # ================================== #
# ------->>> Version Vars <<<------- # # ------->>> Version Vars <<<------- #

10
web-greeter/bridge/Config.py

@ -27,12 +27,10 @@
# along with Web Greeter; If not, see <http://www.gnu.org/licenses/>. # along with Web Greeter; If not, see <http://www.gnu.org/licenses/>.
# 3rd-Party Libs # 3rd-Party Libs
from PyQt5.QtCore import QVariant
# This Application
from whither.bridge import ( from whither.bridge import (
BridgeObject, BridgeObject,
bridge, bridge,
Variant,
) )
@ -41,12 +39,12 @@ class Config(BridgeObject):
def __init__(self, config, *args, **kwargs): def __init__(self, config, *args, **kwargs):
super().__init__(name='Config', *args, **kwargs) super().__init__(name='Config', *args, **kwargs)
self._branding, self._greeter = config.branding, config.greeter self._branding, self._greeter = config.branding.as_dict(), config.greeter.as_dict()
@bridge.prop(QVariant) @bridge.prop(Variant)
def branding(self): def branding(self):
return self._branding return self._branding
@bridge.prop(QVariant) @bridge.prop(Variant)
def greeter(self): def greeter(self):
return self._greeter return self._greeter

51
web-greeter/bridge/Greeter.py

@ -32,10 +32,10 @@
import gi import gi
gi.require_version('LightDM', '1') gi.require_version('LightDM', '1')
from gi.repository import LightDM from gi.repository import LightDM
from PyQt5.QtCore import QVariant
from whither.bridge import ( from whither.bridge import (
BridgeObject, BridgeObject,
bridge, bridge,
Variant,
) )
# This Application # This Application
@ -48,24 +48,33 @@ from . import (
LightDMGreeter = LightDM.Greeter() LightDMGreeter = LightDM.Greeter()
LightDMUserList = LightDM.UserList() LightDMUsers = LightDM.UserList()
class Greeter(BridgeObject): class Greeter(BridgeObject):
authentication_complete = bridge.signal() authentication_complete = bridge.signal(LightDM.Greeter)
autologin_timer_expired = bridge.signal() autologin_timer_expired = bridge.signal(LightDM.Greeter)
idle = bridge.signal() idle = bridge.signal(LightDM.Greeter)
reset = bridge.signal() reset = bridge.signal(LightDM.Greeter)
show_message = bridge.signal(str, str, arguments=('text', 'type')) show_message = bridge.signal((LightDM.Greeter, str, str), arguments=('text', 'type'))
show_prompt = bridge.signal(str, str, arguments=('text', 'type')) show_prompt = bridge.signal((LightDM.Greeter, str, str), arguments=('text', 'type'))
def __init__(self, *args, **kwargs): def __init__(self, themes_dir, *args, **kwargs):
super().__init__(name='LightDMGreeter', *args, **kwargs) super().__init__(name='LightDMGreeter', *args, **kwargs)
self._shared_data_directory = ''
self._themes_directory = themes_dir
LightDMGreeter.connect_to_daemon_sync() LightDMGreeter.connect_to_daemon_sync()
self._connect_signals() self._connect_signals()
self._determine_shared_data_directory_path()
def _determine_shared_data_directory_path(self):
user = LightDMUsers.get_users()[0]
user_data_dir = LightDMGreeter.ensure_shared_data_dir_sync(user.get_name())
self._shared_data_directory = user_data_dir.rpartition('/')[0]
def _connect_signals(self): def _connect_signals(self):
LightDMGreeter.connect('authentication-complete', self.authentication_complete.emit) LightDMGreeter.connect('authentication-complete', self.authentication_complete.emit)
@ -131,19 +140,19 @@ class Greeter(BridgeObject):
def is_authenticated(self): def is_authenticated(self):
return LightDMGreeter.get_is_authenticated() return LightDMGreeter.get_is_authenticated()
@bridge.prop(QVariant) @bridge.prop(Variant)
def language(self): def language(self):
return language_to_dict(LightDM.get_language()) return language_to_dict(LightDM.get_language())
@bridge.prop(list) @bridge.prop(Variant)
def languages(self): def languages(self):
return [language_to_dict(lang) for lang in LightDM.get_languages()] return [language_to_dict(lang) for lang in LightDM.get_languages()]
@bridge.prop(QVariant) @bridge.prop(Variant)
def layout(self): def layout(self):
return layout_to_dict(LightDM.get_layout()) return layout_to_dict(LightDM.get_layout())
@bridge.prop(list) @bridge.prop(Variant)
def layouts(self): def layouts(self):
return [layout_to_dict(layout) for layout in LightDM.get_layouts()] return [layout_to_dict(layout) for layout in LightDM.get_layouts()]
@ -151,7 +160,7 @@ class Greeter(BridgeObject):
def lock_hint(self): def lock_hint(self):
return LightDMGreeter.get_lock_hint() return LightDMGreeter.get_lock_hint()
@bridge.prop(list) @bridge.prop(Variant)
def remote_sessions(self): def remote_sessions(self):
return [session_to_dict(session) for session in LightDM.get_remote_sessions()] return [session_to_dict(session) for session in LightDM.get_remote_sessions()]
@ -163,10 +172,14 @@ class Greeter(BridgeObject):
def select_user_hint(self): def select_user_hint(self):
return LightDMGreeter.get_select_user_hint() or '' return LightDMGreeter.get_select_user_hint() or ''
@bridge.prop(QVariant) @bridge.prop(Variant)
def sessions(self): def sessions(self):
return [session_to_dict(session) for session in LightDM.get_sessions()] return [session_to_dict(session) for session in LightDM.get_sessions()]
@bridge.prop(str)
def shared_data_directory(self):
return self._shared_data_directory
@bridge.prop(bool) @bridge.prop(bool)
def show_manual_login_hint(self): def show_manual_login_hint(self):
return LightDMGreeter.get_show_manual_login_hint() return LightDMGreeter.get_show_manual_login_hint()
@ -175,9 +188,13 @@ class Greeter(BridgeObject):
def show_remote_login_hint(self): def show_remote_login_hint(self):
return LightDMGreeter.get_show_remote_login_hint() return LightDMGreeter.get_show_remote_login_hint()
@bridge.prop(QVariant) @bridge.prop(str)
def themes_directory(self):
return self._themes_directory
@bridge.prop(Variant)
def users(self): def users(self):
return [user_to_dict(user) for user in LightDMUserList.get_users()] return [user_to_dict(user) for user in LightDMUsers.get_users()]
@bridge.method(str) @bridge.method(str)
def authenticate(self, username): def authenticate(self, username):

79
web-greeter/bridge/ThemeUtils.py

@ -0,0 +1,79 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# ThemeUtils.py
#
# Copyright © 2017 Antergos
#
# This file is part of Web Greeter.
#
# Web Greeter is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# Web Greeter is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# The following additional terms are in effect as per Section 7 of the license:
#
# The preservation of all legal notices and author attributions in
# the material or in the Appropriate Legal Notices displayed
# by works containing it is required.
#
# You should have received a copy of the GNU General Public License
# along with Web Greeter; If not, see <http://www.gnu.org/licenses/>.
# Standard Lib
import os
import os.path as path
# 3rd-Party Libs
from whither.bridge import (
BridgeObject,
bridge,
Variant,
)
class ThemeUtils(BridgeObject):
def __init__(self, greeter, config, user_config, *args, **kwargs):
super().__init__(name='ThemeUtils', *args, **kwargs)
self._config = config
self._user_config = user_config
self._greeter = greeter
@bridge.method(Variant)
def dirlist(self, dir_path):
if not dir_path or not isinstance(dir_path, str):
return []
dir_path = path.realpath(path.normpath(dir_path))
if not path.isabs(dir_path) or not path.isdir(dir_path):
return []
allowed = False
allowed_dirs = (
self._config.themes_dir,
self._user_config.branding.background_images,
self._greeter.shared_data_directory,
'/tmp/'
)
for allowed_dir in allowed_dirs:
if dir_path.startswith(allowed_dir):
allowed = True
break
if not allowed:
return []
return (path.join(dir_path, f) for f in os.listdir(dir_path))

32
web-greeter/greeter.py

@ -35,8 +35,17 @@ from whither.app import App
from whither.base.data import AttributeDict from whither.base.data import AttributeDict
# This Application # This Application
from bridge.Greeter import Greeter try:
from bridge.Config import Config # PyCharm quirkiness
from .resources import *
from .bridge.Greeter import Greeter
from .bridge.Config import Config
from .bridge.ThemeUtils import ThemeUtils
except ImportError:
import resources
from bridge.Greeter import Greeter
from bridge.Config import Config
from bridge.ThemeUtils import ThemeUtils
BASE_DIR = os.path.dirname(os.path.realpath(__file__)) BASE_DIR = os.path.dirname(os.path.realpath(__file__))
@ -44,17 +53,22 @@ CONFIG_FILE = os.path.join(BASE_DIR, 'whither.yml')
class WebGreeter(App): class WebGreeter(App):
_user_config = AttributeDict({}) greeter = None
_greeter = None greeter_config = None
theme_utils = None
user_config = AttributeDict({})
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super().__init__('WebGreeter', config_file=CONFIG_FILE, debug=True, *args, **kwargs) super().__init__('WebGreeter', config_file=CONFIG_FILE, debug=True, *args, **kwargs)
self.get_and_save_user_config() self.get_and_save_user_config()
self._greeter = Greeter() self.greeter = Greeter(self.config.themes_dir)
self._web_container.bridge_objects = (self._greeter, Config(self._user_config)) self.greeter_config = Config(self.user_config)
self.theme_utils = ThemeUtils(self.greeter, self.config, self.user_config)
self._web_container.bridge_objects = (self.greeter, self.greeter_config, self.theme_utils)
self._web_container.initialize_bridge_objects() self._web_container.initialize_bridge_objects()
self._web_container.load_script(':/_greeter/js/bundle.js', 'Web Greeter Bundle')
self.load_theme() self.load_theme()
def get_and_save_user_config(self): def get_and_save_user_config(self):
@ -63,15 +77,15 @@ class WebGreeter(App):
config.read('/etc/lightdm/web-greeter.conf') config.read('/etc/lightdm/web-greeter.conf')
for section in config.sections(): for section in config.sections():
self._user_config[section] = {} self.user_config[section] = {}
for key in config[section]: for key in config[section]:
self._user_config[section][key] = config[section][key] self.user_config[section][key] = config[section][key]
def load_theme(self): def load_theme(self):
theme_url = 'file://{0}/{1}/index.html'.format( theme_url = 'file://{0}/{1}/index.html'.format(
self.config.themes_dir, self.config.themes_dir,
self._user_config.greeter.webkit_theme self.user_config.greeter.webkit_theme
) )
self._web_container.load(theme_url) self._web_container.load(theme_url)

38
web-greeter/interceptor.py

@ -0,0 +1,38 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
#
# interceptor.py
#
# Copyright © 2016-2017 Antergos
#
# This file is part of whither.
#
# whither is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# whither is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# The following additional terms are in effect as per Section 7 of the license:
#
# The preservation of all legal notices and author attributions in
# the material or in the Appropriate Legal Notices displayed
# by works containing it is required.
#
# You should have received a copy of the GNU General Public License
# along with whither; If not, see <http://www.gnu.org/licenses/>.
""" Url Request Interceptor """
# 3rd-Party Libs
from whither.bridge import UrlRequestInterceptor as Interceptor
class UrlRequestInterceptor(Interceptor):
def intercept_request(self, info):
self.interceptRequest(info)

27
src/meson.build → web-greeter/meson.build

@ -1,34 +1,15 @@
configure_file(output: 'config.h', configuration: conf)
with_webext_dir = get_option('with-webext-dir').split('"')
extdir = with_webext_dir[0]
# ================================ # # ================================ #
# ------->>> GResources <<<------- # # ------->>> Resources <<<-------- #
# ================================ # # ================================ #
gresources_dir = include_directories('gresource') resource_dir = include_directories('resource')
gnome = import('gnome') qt5 = import('qt5')
utils = '@0@/utils.sh'.format(meson.build_root()) utils = '@0@/utils.sh'.format(meson.build_root())
# Can't do it the right way until GLib 2.52 is released js_sources = run_command(utils, 'combine-js')
# js_sources = run_command(utils, 'get-js-files').stdout().split()
#js_sources_combined = custom_target(
# 'javascript_sources',
# input: files(js_sources),
# output: 'bundle.js',
# command: [utils, 'combine-js']
#)
js_sources = run_command(utils, 'combine-js')
gresources = gnome.compile_resources(
'greeter-resources',
'gresource/greeter-resources.gresource.xml',
source_dir: 'gresource',
c_name: 'greeter_resources'
)
# ======================================= # # ======================================= #
# ------->>> WebKit2 Extension <<<------- # # ------->>> WebKit2 Extension <<<------- #

105
web-greeter/resources/js/ThemeUtils.js

@ -25,16 +25,27 @@
* along with lightdm-webkit2-greeter; If not, see <http://www.gnu.org/licenses/>. * along with lightdm-webkit2-greeter; If not, see <http://www.gnu.org/licenses/>.
*/ */
if ( 'undefined' === typeof window.navigator.languages ) { /*if ( 'undefined' === typeof window.navigator.languages ) {
window.navigator.languages = [ window.navigator.language ]; window.navigator.languages = [ window.navigator.language ];
} }*/
moment.locale( window.navigator.languages );
let localized_invalid_date = moment('today', '!@#'), let localized_invalid_date = null,
time_language = null, time_language = null,
time_format = null, time_format = null,
allowed_dirs = null; allowed_dirs = null,
_ThemeUtils = null;
function _set_allowed_dirs() {
allowed_dirs = {
themes_dir: lightdm.themes_directory,
backgrounds_dir: greeter_config.branding.background_images,
lightdm_data_dir: lightdm.shared_data_directory,
tmpdir: '/tmp',
};
}
@ -46,6 +57,18 @@ let localized_invalid_date = moment('today', '!@#'),
* @memberOf LightDM * @memberOf LightDM
*/ */
class ThemeUtils { class ThemeUtils {
constructor( instance ) {
if ( null !== _ThemeUtils ) {
return _ThemeUtils;
}
moment.locale( window.navigator.languages );
localized_invalid_date = moment('today', '!@#');
_ThemeUtils = instance;
}
/** /**
* Binds `this` to class, `context`, for all of the class's methods. * Binds `this` to class, `context`, for all of the class's methods.
* *
@ -91,15 +114,15 @@ class ThemeUtils {
* @returns {string[]} List of abs paths for the files and directories found in `path`. * @returns {string[]} List of abs paths for the files and directories found in `path`.
*/ */
dirlist( path ) { dirlist( path ) {
let allowed = true; let allowed = false;
if ( '' === path || ! path instanceof String ) { if ( '' === path || 'string' !== typeof path ) {
console.log('[ERROR] theme_utils.dirlist(): path must be a non-empty string!'); console.log('[ERROR] theme_utils.dirlist(): path must be a non-empty string!');
allowed = false; return [];
} else if ( null !== path.match(/^[^/].+/) ) { } else if ( null !== path.match(/^[^/].+/) ) {
console.log('[ERROR] theme_utils.dirlist(): path must not include be absolute!'); console.log('[ERROR] theme_utils.dirlist(): path must be absolute!');
allowed = false; return[];
} }
if ( null !== path.match(/\/\.+(?=\/)/) ) { if ( null !== path.match(/\/\.+(?=\/)/) ) {
@ -108,29 +131,16 @@ class ThemeUtils {
} }
if ( null === allowed_dirs ) { if ( null === allowed_dirs ) {
let user = lightdm.users.pop(), _set_allowed_dirs();
user_data_dir = greeter_config.get_str( user.username, 'lightdm_data_dir' ),
lightdm_data_dir = user_data_dir.substr( 0, user_data_dir.lastIndexOf('/') );
allowed_dirs = {
themes_dir: greeter_config.get_str( 'greeter', 'themes_dir' ),
backgrounds_dir: greeter_config.get_str( 'branding', 'background_images' ),
lightdm_data_dir: lightdm_data_dir,
tmpdir: '/tmp',
};
} }
if ( ! Object.keys( allowed_dirs ).some( dir => path.startsWith( allowed_dirs[dir] ) ) ) { if ( ! Object.keys( allowed_dirs ).some( dir => path.startsWith( allowed_dirs[dir] ) ) ) {
console.log(`[ERROR] theme_utils.dirlist(): path is not allowed: ${path}`); console.log(`[ERROR] theme_utils.dirlist(): path is not allowed: ${path}`);
allowed = false;
}
if ( ! allowed ) {
return []; return [];
} }
try { try {
return __ThemeUtils.dirlist( path ); return _ThemeUtils.dirlist( path );
} catch( err ) { } catch( err ) {
console.log( `[ERROR] theme_utils.dirlist(): ${err}` ); console.log( `[ERROR] theme_utils.dirlist(): ${err}` );
@ -192,7 +202,7 @@ class ThemeUtils {
*/ */
txt2html( text ) { txt2html( text ) {
try { try {
return __ThemeUtils.txt2html( text ); return _ThemeUtils.esc_html( text );
} catch( err ) { } catch( err ) {
console.log( `[ERROR] theme_utils.dirlist(): ${err}` ); console.log( `[ERROR] theme_utils.dirlist(): ${err}` );
@ -200,46 +210,3 @@ class ThemeUtils {
} }
} }
} }
const __theme_utils = new Promise( (resolve, reject) => {
let waiting = 0;
const check_window_prop = () => {
if ( waiting > 15000 ) {
return reject( 'Timeout Reached!');
}
setTimeout( () => {
waiting += 1;
if ( '__ThemeUtils' in window ) {
return resolve( (() => new ThemeUtils())() );
}
check_window_prop();
}, 0 );
};
check_window_prop();
});
/**
* Theme Utils - various utility methods for use in greeter themes.
* @name theme_utils
* @type {LightDM.ThemeUtils}
* @memberOf window
*/
__theme_utils.then( result => {
window.theme_utils = result;
/**
* ***Deprecated!*** Use {@link window.theme_utils} instead.
* @name greeterutil
* @type {LightDM.ThemeUtils}
* @memberOf window
* @deprecated
*/
window.greeterutil = window.theme_utils;
} );

13874
web-greeter/resources/js/_vendor/moment-with-locales.min.js vendored

File diff suppressed because it is too large Load Diff

45
web-greeter/resources/js/bootstrap.js vendored

@ -26,15 +26,58 @@
*/ */
let _channel; let _channel;
const _ready_event = new Event( 'GreeterReady' );
function initialize() { function initialize() {
new QWebChannel( qt.webChannelTransport, channel => { new QWebChannel( qt.webChannelTransport, channel => {
_channel = channel; _channel = channel;
/**
* Greeter Instance
* @name lightdm
* @type {LightDM.Greeter}
* @memberOf window
*/
window.lightdm = _channel.objects.LightDMGreeter; window.lightdm = _channel.objects.LightDMGreeter;
/**
* Greeter Config - Access values from the greeter's config file.
* @name greeter_config
* @type {LightDM.GreeterConfig}
* @memberOf window
*/
window.greeter_config = _channel.objects.Config; window.greeter_config = _channel.objects.Config;
/**
* ***Deprecated!*** Use {@link window.greeter_config} instead.
* @name config
* @type {LightDM.GreeterConfig}
* @memberOf window
* @deprecated
*/
window.config = window.greeter_config;
/**
* Theme Utils - various utility methods for use in greeter themes.
* @name theme_utils
* @type {LightDM.ThemeUtils}
* @memberOf window
*/
window.theme_utils = new ThemeUtils( _channel.objects.ThemeUtils );
/**
* ***Deprecated!*** Use {@link window.theme_utils} instead.
* @name greeterutil
* @type {LightDM.ThemeUtils}
* @memberOf window
* @deprecated
*/
window.greeterutil = window.theme_utils;
window.dispatchEvent( _ready_event );
}); });
} }
$(window).on('load', initialize); setTimeout( initialize, 50 );

36
web-greeter/resources/js/docs/Greeter.js

@ -302,42 +302,6 @@ class Greeter {
} }
const __lightdm = new Promise( (resolve, reject) => {
let waiting = 0;
const check_window_prop = () => {
if ( waiting > 15000 ) {
return reject( 'Timeout Reached!');
}
setTimeout( () => {
waiting += 1;
if ( '__LightDMGreeter' in window ) {
return resolve( window.__LightDMGreeter );
}
check_window_prop();
}, 0 );
};
check_window_prop();
});
/**
* Greeter Instance
* @name lightdm
* @type {LightDM.Greeter}
* @memberOf window
*/
__lightdm.then( result => {
result.start_authentication = function( user ) { return this.authenticate( user ); };
result.start_authentication = result.start_authentication.bind(result);
window.lightdm = result
} );
/** /**
* Moment.js instance - Loaded and instantiated automatically by the greeter. * Moment.js instance - Loaded and instantiated automatically by the greeter.
* @name moment * @name moment

21
web-greeter/resources/js/docs/GreeterConfig.js

@ -35,7 +35,7 @@ function set_values( defaults, target_obj, method ) {
keys.forEach( prop => { keys.forEach( prop => {
try { try {
target_obj[prop] = method( 'greeter', prop ); target_obj[prop] = window;
} catch(err) { } catch(err) {
target_obj[prop] = defaults[prop]; target_obj[prop] = defaults[prop];
} }
@ -179,24 +179,5 @@ const __greeter_config = new Promise( (resolve, reject) => {
}); });
/**
* Greeter Config - Access values from the greeter's config file.
* @name greeter_config
* @type {LightDM.GreeterConfig}
* @memberOf window
*/
__greeter_config.then( result => {
window.greeter_config = result;
/**
* ***Deprecated!*** Use {@link window.greeter_config} instead.
* @name config
* @type {LightDM.GreeterConfig}
* @memberOf window
* @deprecated
*/
window.config = window.greeter_config;
} );

Loading…
Cancel
Save