2014-01-25 23:13:56 +00:00
|
|
|
#!/usr/bin/python3
|
2013-07-12 17:56:24 +00:00
|
|
|
# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
|
2014-01-25 23:13:56 +00:00
|
|
|
# Mugshot - Lightweight user configuration utility
|
|
|
|
# Copyright (C) 2013-2014 Sean Davis <smd.seandavis@gmail.com>
|
2014-01-25 22:21:53 +00:00
|
|
|
#
|
2014-01-25 23:13:56 +00:00
|
|
|
# This program is free software: you can redistribute it and/or modify it
|
|
|
|
# under the terms of the GNU General Public License version 3, as published
|
|
|
|
# by the Free Software Foundation.
|
2014-01-25 22:21:53 +00:00
|
|
|
#
|
2014-01-25 23:13:56 +00:00
|
|
|
# This program is distributed in the hope that it will be useful, but
|
|
|
|
# WITHOUT ANY WARRANTY; without even the implied warranties of
|
|
|
|
# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
|
|
|
|
# PURPOSE. See the GNU General Public License for more details.
|
|
|
|
#
|
|
|
|
# You should have received a copy of the GNU General Public License along
|
|
|
|
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
2013-07-12 17:56:24 +00:00
|
|
|
|
|
|
|
from locale import gettext as _
|
|
|
|
|
2013-07-13 15:09:49 +00:00
|
|
|
import os
|
2013-07-14 14:22:51 +00:00
|
|
|
# Used for automating chfn
|
2013-07-13 15:09:49 +00:00
|
|
|
import pexpect
|
2013-07-14 14:22:51 +00:00
|
|
|
# Used for copying files to ~/.face
|
2013-07-13 15:09:49 +00:00
|
|
|
import shutil
|
2013-07-14 14:22:51 +00:00
|
|
|
# Used for which command and checking for running processes.
|
2013-07-13 15:09:49 +00:00
|
|
|
import subprocess
|
2013-07-14 14:22:51 +00:00
|
|
|
# DBUS interface is used to update pidgin buddyicon when pidgin is running.
|
2013-07-13 23:35:54 +00:00
|
|
|
import dbus
|
2013-07-13 15:09:49 +00:00
|
|
|
|
2014-03-02 16:41:30 +00:00
|
|
|
from gi.repository import Gtk, GdkPixbuf, GLib, Gio # pylint: disable=E0611
|
2013-07-12 17:56:24 +00:00
|
|
|
import logging
|
|
|
|
logger = logging.getLogger('mugshot')
|
|
|
|
|
2014-03-29 15:29:49 +00:00
|
|
|
from mugshot_lib import Window, SudoDialog, helpers
|
2013-07-15 23:12:55 +00:00
|
|
|
from mugshot.CameraMugshotDialog import CameraMugshotDialog
|
2013-07-12 17:56:24 +00:00
|
|
|
|
2014-03-02 15:59:38 +00:00
|
|
|
username = GLib.get_user_name()
|
|
|
|
home = GLib.get_home_dir()
|
|
|
|
libreoffice_prefs = os.path.join(GLib.get_user_config_dir(), 'libreoffice',
|
2014-04-02 01:42:05 +00:00
|
|
|
'4', 'user', 'registrymodifications.xcu')
|
2013-07-14 14:22:51 +00:00
|
|
|
pidgin_prefs = os.path.join(home, '.purple', 'prefs.xml')
|
2013-07-16 09:24:21 +00:00
|
|
|
faces_dir = '/usr/share/pixmaps/faces/'
|
2013-07-13 18:17:51 +00:00
|
|
|
|
2014-01-25 22:21:53 +00:00
|
|
|
|
2013-07-13 15:09:49 +00:00
|
|
|
def which(command):
|
|
|
|
'''Use the system command which to get the absolute path for the given
|
|
|
|
command.'''
|
2014-01-25 22:21:53 +00:00
|
|
|
command = subprocess.Popen(['which', command],
|
2014-04-02 01:42:05 +00:00
|
|
|
stdout=subprocess.PIPE).stdout.read().strip()
|
2014-01-26 00:44:26 +00:00
|
|
|
command = command.decode('utf-8')
|
2014-01-25 22:21:53 +00:00
|
|
|
if command == '':
|
2013-07-17 23:16:41 +00:00
|
|
|
logger.debug('Command "%s" could not be found.' % command)
|
|
|
|
return None
|
|
|
|
return command
|
2014-01-25 22:21:53 +00:00
|
|
|
|
|
|
|
|
2013-07-13 23:35:54 +00:00
|
|
|
def has_running_process(name):
|
2013-07-14 14:22:51 +00:00
|
|
|
"""Check for a running process, return True if any listings are found."""
|
2013-07-13 23:35:54 +00:00
|
|
|
command = 'ps -ef | grep " %s" | grep -v "grep" | wc -l' % name
|
2014-01-25 22:21:53 +00:00
|
|
|
n = subprocess.Popen(command, stdout=subprocess.PIPE,
|
2014-04-02 01:42:05 +00:00
|
|
|
shell=True).stdout.read().strip()
|
2013-07-13 23:35:54 +00:00
|
|
|
return int(n) > 0
|
2014-01-25 22:21:53 +00:00
|
|
|
|
|
|
|
|
2013-07-17 23:16:41 +00:00
|
|
|
def has_gstreamer_camerabin_support():
|
|
|
|
"""Return True if gstreamer1.0 camerabin element is available."""
|
2014-01-25 22:21:53 +00:00
|
|
|
process = subprocess.Popen(["gst-inspect-1.0", "camerabin"],
|
2014-04-02 01:42:05 +00:00
|
|
|
stdout=subprocess.PIPE,
|
|
|
|
stderr=subprocess.PIPE)
|
2013-07-17 23:16:41 +00:00
|
|
|
process.communicate()
|
|
|
|
has_support = process.returncode == 0
|
|
|
|
if not has_support:
|
2014-01-25 22:21:53 +00:00
|
|
|
element = 'camerabin'
|
|
|
|
plugin = 'gstreamer1.0-plugins-good'
|
|
|
|
logger.debug('%s element unavailable. '
|
|
|
|
'Do you have %s installed?' % (element, plugin))
|
2013-07-17 23:16:41 +00:00
|
|
|
return has_support
|
|
|
|
|
2014-01-25 22:21:53 +00:00
|
|
|
|
2013-07-17 23:16:41 +00:00
|
|
|
def has_gstreamer_camerasrc_support():
|
|
|
|
"""Return True if gstreamer1.0 v4l2src element is available."""
|
2014-01-25 22:21:53 +00:00
|
|
|
process = subprocess.Popen(["gst-inspect-1.0", "v4l2src"],
|
2014-04-02 01:42:05 +00:00
|
|
|
stdout=subprocess.PIPE,
|
|
|
|
stderr=subprocess.PIPE)
|
2013-07-17 23:16:41 +00:00
|
|
|
process.communicate()
|
|
|
|
has_support = process.returncode == 0
|
|
|
|
if not has_support:
|
2014-01-25 22:21:53 +00:00
|
|
|
element = 'v4l2src'
|
|
|
|
plugin = 'gstreamer1.0-plugins-good'
|
|
|
|
logger.debug('%s element unavailable. '
|
|
|
|
'Do you have %s installed?' % (element, plugin))
|
2013-07-17 23:16:41 +00:00
|
|
|
return has_support
|
2014-01-25 22:21:53 +00:00
|
|
|
|
|
|
|
|
2013-07-17 23:16:41 +00:00
|
|
|
def get_camera_installed():
|
|
|
|
"""Return True if /dev/video0 exists."""
|
|
|
|
if not os.path.exists('/dev/video0'):
|
|
|
|
logger.debug('Camera not detected at /dev/video0')
|
|
|
|
return False
|
|
|
|
return True
|
2014-01-25 22:21:53 +00:00
|
|
|
|
|
|
|
|
2013-07-17 23:16:41 +00:00
|
|
|
def get_has_camera_support():
|
|
|
|
"""Return True if cameras are fully supported by this application."""
|
|
|
|
if not get_camera_installed():
|
|
|
|
return False
|
|
|
|
if not which('gst-inspect-1.0'):
|
|
|
|
return False
|
|
|
|
if not has_gstreamer_camerabin_support():
|
|
|
|
return False
|
|
|
|
if not has_gstreamer_camerasrc_support():
|
|
|
|
return False
|
|
|
|
return True
|
2013-07-13 15:09:49 +00:00
|
|
|
|
2014-01-25 22:21:53 +00:00
|
|
|
|
2013-07-13 15:09:49 +00:00
|
|
|
def detach_cb(menu, widget):
|
|
|
|
'''Detach a widget from its attached widget.'''
|
|
|
|
menu.detach()
|
2014-01-25 22:21:53 +00:00
|
|
|
|
|
|
|
|
2013-07-13 18:17:51 +00:00
|
|
|
def get_entry_value(entry_widget):
|
|
|
|
"""Get the value from one of the Mugshot entries."""
|
|
|
|
# Get the text from an entry, changing none to ''
|
|
|
|
value = entry_widget.get_text().strip()
|
|
|
|
if value.lower() == 'none':
|
|
|
|
value = ''
|
|
|
|
return value
|
2014-01-25 22:21:53 +00:00
|
|
|
|
|
|
|
|
|
|
|
def get_confirmation_dialog(parent, primary_message, secondary_message,
|
2014-04-02 01:42:05 +00:00
|
|
|
icon_name=None):
|
2014-01-25 22:21:53 +00:00
|
|
|
"""Display a confirmation (yes/no) dialog configured with primary and
|
2013-07-14 14:22:51 +00:00
|
|
|
secondary messages, as well as a custom icon if requested."""
|
2014-01-25 22:44:45 +00:00
|
|
|
dialog = Gtk.MessageDialog(transient_for=parent, flags=0,
|
2014-01-25 23:13:56 +00:00
|
|
|
message_type=Gtk.MessageType.QUESTION,
|
2013-07-14 13:16:48 +00:00
|
|
|
buttons=Gtk.ButtonsType.YES_NO,
|
2014-01-25 23:13:56 +00:00
|
|
|
text=primary_message)
|
2013-07-14 13:16:48 +00:00
|
|
|
dialog.format_secondary_text(secondary_message)
|
|
|
|
if icon_name:
|
|
|
|
image = Gtk.Image.new_from_icon_name(icon_name, Gtk.IconSize.DIALOG)
|
|
|
|
dialog.set_image(image)
|
|
|
|
dialog.show_all()
|
|
|
|
response = dialog.run()
|
|
|
|
dialog.destroy()
|
|
|
|
return response == Gtk.ResponseType.YES
|
2014-01-25 22:21:53 +00:00
|
|
|
|
|
|
|
|
2013-07-13 15:09:49 +00:00
|
|
|
def menu_position(self, menu, data=None, something_else=None):
|
|
|
|
'''Position a menu at the bottom of its attached widget'''
|
|
|
|
widget = menu.get_attach_widget()
|
|
|
|
allocation = widget.get_allocation()
|
|
|
|
window_pos = widget.get_window().get_position()
|
2013-07-13 18:17:51 +00:00
|
|
|
# Align the left side of the menu with the left side of the button.
|
2013-07-13 15:09:49 +00:00
|
|
|
x = window_pos[0] + allocation.x
|
2013-07-13 18:17:51 +00:00
|
|
|
# Align the top of the menu with the bottom of the button.
|
2013-07-13 15:09:49 +00:00
|
|
|
y = window_pos[1] + allocation.y + allocation.height
|
|
|
|
return (x, y, True)
|
|
|
|
|
2014-01-25 22:21:53 +00:00
|
|
|
|
2013-07-12 17:56:24 +00:00
|
|
|
# See mugshot_lib.Window.py for more details about how this class works
|
|
|
|
class MugshotWindow(Window):
|
2014-01-25 22:21:53 +00:00
|
|
|
"""Mugshot GtkWindow"""
|
2013-07-12 17:56:24 +00:00
|
|
|
__gtype_name__ = "MugshotWindow"
|
2014-01-25 22:21:53 +00:00
|
|
|
|
|
|
|
def finish_initializing(self, builder): # pylint: disable=E1002
|
2013-07-12 17:56:24 +00:00
|
|
|
"""Set up the main window"""
|
|
|
|
super(MugshotWindow, self).finish_initializing(builder)
|
2013-07-18 02:12:38 +00:00
|
|
|
self.set_wmclass("Mugshot", "Mugshot")
|
2014-01-25 22:21:53 +00:00
|
|
|
|
2013-07-15 23:12:55 +00:00
|
|
|
self.CameraDialog = CameraMugshotDialog
|
2013-07-12 17:56:24 +00:00
|
|
|
|
2013-07-13 18:17:51 +00:00
|
|
|
# User Image widgets
|
|
|
|
self.image_button = builder.get_object('image_button')
|
|
|
|
self.user_image = builder.get_object('user_image')
|
|
|
|
self.image_menu = builder.get_object('image_menu')
|
|
|
|
self.image_menu.attach_to_widget(self.image_button, detach_cb)
|
2013-07-16 02:15:31 +00:00
|
|
|
self.image_from_camera = builder.get_object('image_from_camera')
|
2014-03-28 02:20:19 +00:00
|
|
|
self.image_from_stock = builder.get_object('image_from_stock')
|
|
|
|
self.image_from_stock.set_visible(os.path.exists(faces_dir) and
|
2014-04-02 01:42:05 +00:00
|
|
|
len(os.listdir(faces_dir)) > 0)
|
2014-03-28 02:20:19 +00:00
|
|
|
self.menuitem1 = builder.get_object('menuitem1')
|
|
|
|
self.image_remove = builder.get_object('image_remove')
|
2014-01-25 22:21:53 +00:00
|
|
|
|
2013-07-13 18:17:51 +00:00
|
|
|
# Entry widgets (chfn)
|
2013-07-13 15:09:49 +00:00
|
|
|
self.first_name_entry = builder.get_object('first_name')
|
|
|
|
self.last_name_entry = builder.get_object('last_name')
|
|
|
|
self.initials_entry = builder.get_object('initials')
|
|
|
|
self.office_phone_entry = builder.get_object('office_phone')
|
|
|
|
self.home_phone_entry = builder.get_object('home_phone')
|
2013-07-14 12:18:36 +00:00
|
|
|
self.email_entry = builder.get_object('email')
|
|
|
|
self.fax_entry = builder.get_object('fax')
|
2014-01-25 22:21:53 +00:00
|
|
|
|
2014-04-02 09:34:36 +00:00
|
|
|
# Users without sudo rights cannot change their name.
|
|
|
|
if not SudoDialog.check_sudo():
|
|
|
|
self.first_name_entry.set_sensitive(False)
|
|
|
|
self.last_name_entry.set_sensitive(False)
|
|
|
|
|
2013-07-13 18:17:51 +00:00
|
|
|
# Stock photo browser
|
2013-07-13 15:09:49 +00:00
|
|
|
self.stock_browser = builder.get_object('stock_browser')
|
2013-07-13 18:17:51 +00:00
|
|
|
self.iconview = builder.get_object('stock_iconview')
|
2014-01-25 22:21:53 +00:00
|
|
|
|
2013-07-18 01:28:32 +00:00
|
|
|
# File Chooser Dialog
|
|
|
|
self.chooser = builder.get_object('filechooserdialog')
|
|
|
|
self.crop_center = builder.get_object('crop_center')
|
|
|
|
self.crop_left = builder.get_object('crop_left')
|
|
|
|
self.crop_right = builder.get_object('crop_right')
|
|
|
|
self.file_chooser_preview = builder.get_object('file_chooser_preview')
|
|
|
|
# Add a filter for only image files.
|
|
|
|
image_filter = Gtk.FileFilter()
|
|
|
|
image_filter.set_name('Images')
|
|
|
|
image_filter.add_mime_type('image/*')
|
|
|
|
self.chooser.add_filter(image_filter)
|
2014-01-25 22:21:53 +00:00
|
|
|
|
2013-07-18 01:28:32 +00:00
|
|
|
self.tmpfile = None
|
2013-07-13 18:17:51 +00:00
|
|
|
|
|
|
|
# Populate all of the widgets.
|
|
|
|
self.init_user_details()
|
2014-01-25 22:21:53 +00:00
|
|
|
|
2013-07-13 18:17:51 +00:00
|
|
|
def init_user_details(self):
|
|
|
|
"""Initialize the user details entries and variables."""
|
|
|
|
# Check for .face and set profile image.
|
|
|
|
logger.debug('Checking for ~/.face profile image')
|
2014-03-02 15:59:38 +00:00
|
|
|
face = os.path.join(home, '.face')
|
2014-04-02 01:11:19 +00:00
|
|
|
logger.debug('Checking AccountsService for profile image')
|
|
|
|
image = self.accounts_service_get_user_image()
|
|
|
|
logger.debug('Found profile image: %s' % str(image))
|
|
|
|
|
2013-07-13 15:09:49 +00:00
|
|
|
if os.path.isfile(face):
|
2014-04-02 01:11:19 +00:00
|
|
|
if os.path.samefile(image, face):
|
|
|
|
self.updated_image = face
|
|
|
|
else:
|
|
|
|
self.updated_image = None
|
2013-07-13 15:09:49 +00:00
|
|
|
self.set_user_image(face)
|
2014-04-02 01:11:19 +00:00
|
|
|
elif os.path.isfile(image):
|
|
|
|
self.updated_image = image
|
|
|
|
self.set_user_image(image)
|
2013-07-13 15:09:49 +00:00
|
|
|
else:
|
2014-04-02 01:11:19 +00:00
|
|
|
self.updated_image = None
|
|
|
|
self.set_user_image(None)
|
2014-01-25 22:21:53 +00:00
|
|
|
|
2013-07-13 18:17:51 +00:00
|
|
|
# Search /etc/passwd for the current user's details.
|
|
|
|
logger.debug('Getting user details from /etc/passwd')
|
|
|
|
for line in open('/etc/passwd', 'r'):
|
|
|
|
if line.startswith(username + ':'):
|
|
|
|
logger.debug('Found details: %s' % line.strip())
|
|
|
|
details = line.split(':')[4]
|
|
|
|
name, office, office_phone, home_phone = details.split(',', 3)
|
|
|
|
break
|
2014-01-25 22:21:53 +00:00
|
|
|
|
2014-03-30 19:26:33 +00:00
|
|
|
# Expand the user's fullname into first and last.
|
2013-07-13 18:17:51 +00:00
|
|
|
try:
|
|
|
|
first_name, last_name = name.split(' ', 1)
|
|
|
|
except:
|
|
|
|
first_name = name
|
|
|
|
last_name = ''
|
2014-01-25 22:21:53 +00:00
|
|
|
|
2013-07-13 18:17:51 +00:00
|
|
|
# If the variables are defined as 'none', use blank for cleanliness.
|
2014-01-25 22:21:53 +00:00
|
|
|
if home_phone == 'none':
|
|
|
|
home_phone = ''
|
|
|
|
if office_phone == 'none':
|
|
|
|
office_phone = ''
|
|
|
|
|
2013-07-14 12:36:12 +00:00
|
|
|
# Get dconf settings
|
2013-07-14 15:45:10 +00:00
|
|
|
logger.debug('Getting initials, email, and fax from dconf')
|
2014-03-28 02:35:00 +00:00
|
|
|
initials = self.settings['initials']
|
2013-07-14 12:36:12 +00:00
|
|
|
email = self.settings['email']
|
|
|
|
fax = self.settings['fax']
|
2014-01-25 22:21:53 +00:00
|
|
|
|
2013-07-13 18:17:51 +00:00
|
|
|
# Set the class variables
|
|
|
|
self.first_name = first_name
|
|
|
|
self.last_name = last_name
|
|
|
|
self.initials = initials
|
|
|
|
self.home_phone = home_phone
|
|
|
|
self.office_phone = office_phone
|
2014-01-25 22:21:53 +00:00
|
|
|
|
2013-07-13 18:17:51 +00:00
|
|
|
# Populate the GtkEntries.
|
2013-07-14 15:45:10 +00:00
|
|
|
logger.debug('Populating entries')
|
2013-07-13 15:09:49 +00:00
|
|
|
self.first_name_entry.set_text(self.first_name)
|
|
|
|
self.last_name_entry.set_text(self.last_name)
|
|
|
|
self.initials_entry.set_text(self.initials)
|
|
|
|
self.office_phone_entry.set_text(self.office_phone)
|
|
|
|
self.home_phone_entry.set_text(self.home_phone)
|
2013-07-14 12:36:12 +00:00
|
|
|
self.email_entry.set_text(email)
|
|
|
|
self.fax_entry.set_text(fax)
|
2014-01-25 22:21:53 +00:00
|
|
|
|
2014-04-02 01:42:05 +00:00
|
|
|
# = Mugshot Window ====================================================== #
|
2013-07-13 15:09:49 +00:00
|
|
|
def set_user_image(self, filename=None):
|
2013-07-13 18:17:51 +00:00
|
|
|
"""Scale and set the user profile image."""
|
|
|
|
logger.debug("Setting user profile image to %s" % str(filename))
|
2013-07-13 15:09:49 +00:00
|
|
|
if filename:
|
|
|
|
pixbuf = GdkPixbuf.Pixbuf.new_from_file(filename)
|
|
|
|
scaled = pixbuf.scale_simple(128, 128, GdkPixbuf.InterpType.HYPER)
|
|
|
|
self.user_image.set_from_pixbuf(scaled)
|
2014-03-28 02:20:19 +00:00
|
|
|
# Show "Remove" menu item.
|
|
|
|
self.menuitem1.set_visible(True)
|
|
|
|
self.image_remove.set_visible(True)
|
2013-07-13 15:09:49 +00:00
|
|
|
else:
|
|
|
|
self.user_image.set_from_icon_name('avatar-default', 128)
|
2014-03-28 02:20:19 +00:00
|
|
|
# Hide "Remove" menu item.
|
|
|
|
self.menuitem1.set_visible(False)
|
|
|
|
self.image_remove.set_visible(False)
|
2013-07-14 12:18:36 +00:00
|
|
|
|
2014-03-30 19:26:33 +00:00
|
|
|
def suggest_initials(self, first_name, last_name):
|
|
|
|
"""Generate initials from first and last name."""
|
|
|
|
try:
|
|
|
|
initials = first_name[0] + last_name[0]
|
|
|
|
except:
|
|
|
|
if first_name:
|
|
|
|
initials = first_name[0]
|
|
|
|
else:
|
|
|
|
initials = ''
|
|
|
|
return initials
|
|
|
|
|
2013-07-14 12:18:36 +00:00
|
|
|
def filter_numbers(self, entry, *args):
|
|
|
|
"""Allow only numbers and + in phone entry fields."""
|
|
|
|
text = entry.get_text().strip()
|
|
|
|
entry.set_text(''.join([i for i in text if i in '+0123456789']))
|
2014-01-25 22:21:53 +00:00
|
|
|
|
2013-07-13 22:49:20 +00:00
|
|
|
def on_apply_button_clicked(self, widget):
|
2014-01-25 22:21:53 +00:00
|
|
|
"""When the window Apply button is clicked, commit any relevant
|
2013-07-13 22:49:20 +00:00
|
|
|
changes."""
|
2013-07-14 15:45:10 +00:00
|
|
|
logger.debug('Applying changes...')
|
2013-07-13 22:49:20 +00:00
|
|
|
if self.get_chfn_details_updated():
|
2014-03-02 21:01:53 +00:00
|
|
|
result = self.save_chfn_details()
|
|
|
|
if result != [0, 0]:
|
|
|
|
# Password was incorrect, complain.
|
|
|
|
primary = _("Authentication Failed")
|
|
|
|
secondary = _("User details were not updated.")
|
|
|
|
dialog = Gtk.MessageDialog(transient_for=self, flags=0,
|
2014-04-02 01:42:05 +00:00
|
|
|
message_type=
|
|
|
|
Gtk.MessageType.WARNING,
|
|
|
|
buttons=Gtk.ButtonsType.OK,
|
|
|
|
text=primary)
|
2014-03-02 21:01:53 +00:00
|
|
|
dialog.format_secondary_text(secondary)
|
|
|
|
dialog.run()
|
|
|
|
dialog.destroy()
|
|
|
|
return
|
2014-01-25 22:21:53 +00:00
|
|
|
|
2013-07-14 12:18:36 +00:00
|
|
|
if self.get_libreoffice_details_updated():
|
|
|
|
self.set_libreoffice_data()
|
2014-01-25 22:21:53 +00:00
|
|
|
|
2014-03-02 19:38:10 +00:00
|
|
|
if self.updated_image is not None:
|
2013-07-13 22:49:20 +00:00
|
|
|
self.save_image()
|
2014-01-25 22:21:53 +00:00
|
|
|
|
2013-07-14 12:36:12 +00:00
|
|
|
self.save_gsettings()
|
2013-07-25 10:26:30 +00:00
|
|
|
self.destroy()
|
2014-01-25 22:21:53 +00:00
|
|
|
|
2013-07-14 12:36:12 +00:00
|
|
|
def save_gsettings(self):
|
|
|
|
"""Save details to dconf (the ones not tracked by /etc/passwd)"""
|
2013-07-14 15:45:10 +00:00
|
|
|
logger.debug('Saving details to dconf: /apps/mugshot')
|
2014-01-25 22:21:53 +00:00
|
|
|
self.settings.set_string('initials',
|
2013-07-14 14:22:51 +00:00
|
|
|
get_entry_value(self.initials_entry))
|
2013-07-14 12:36:12 +00:00
|
|
|
self.settings.set_string('email', get_entry_value(self.email_entry))
|
|
|
|
self.settings.set_string('fax', get_entry_value(self.fax_entry))
|
2014-01-25 22:21:53 +00:00
|
|
|
|
2013-07-14 14:22:51 +00:00
|
|
|
def entry_focus_next(self, widget):
|
|
|
|
"""Focus the next available entry when pressing Enter."""
|
2013-07-14 15:45:10 +00:00
|
|
|
logger.debug('Entry activated, focusing next widget.')
|
2013-07-14 14:22:51 +00:00
|
|
|
vbox = widget.get_parent().get_parent().get_parent().get_parent()
|
|
|
|
vbox.child_focus(Gtk.DirectionType.TAB_FORWARD)
|
2014-01-25 22:21:53 +00:00
|
|
|
|
2014-03-28 02:35:00 +00:00
|
|
|
def initials_entry_focused(self, widget):
|
|
|
|
"""Paste initials into empty field."""
|
|
|
|
logger.debug('Initials field focused.')
|
2014-03-30 19:26:33 +00:00
|
|
|
first_name = get_entry_value(self.first_name_entry)
|
|
|
|
last_name = get_entry_value(self.last_name_entry)
|
2014-03-28 02:35:00 +00:00
|
|
|
if get_entry_value(self.initials_entry) == '':
|
2014-04-02 01:01:02 +00:00
|
|
|
self.initials_entry.set_text(self.suggest_initials(first_name,
|
|
|
|
last_name))
|
2014-01-25 22:21:53 +00:00
|
|
|
|
2013-07-13 22:49:20 +00:00
|
|
|
def on_cancel_button_clicked(self, widget):
|
|
|
|
"""When the window cancel button is clicked, close the program."""
|
2013-07-14 15:45:10 +00:00
|
|
|
logger.debug('Cancel clicked, goodbye.')
|
2013-07-13 22:49:20 +00:00
|
|
|
self.destroy()
|
2014-01-25 22:21:53 +00:00
|
|
|
|
2014-04-02 01:42:05 +00:00
|
|
|
# = Image Button and Menu =============================================== #
|
2013-07-13 18:17:51 +00:00
|
|
|
def on_image_button_clicked(self, widget):
|
2013-07-14 15:45:10 +00:00
|
|
|
"""When the menu button is clicked, display the photo menu."""
|
2013-07-13 18:17:51 +00:00
|
|
|
if widget.get_active():
|
2013-07-17 23:16:41 +00:00
|
|
|
logger.debug('Show photo menu')
|
|
|
|
self.image_from_camera.set_visible(get_has_camera_support())
|
2014-01-25 22:21:53 +00:00
|
|
|
self.image_menu.popup(None, None, menu_position,
|
2014-04-02 01:42:05 +00:00
|
|
|
self.image_menu, 3,
|
|
|
|
Gtk.get_current_event_time())
|
2014-01-25 22:21:53 +00:00
|
|
|
|
2013-07-13 18:17:51 +00:00
|
|
|
def on_image_menu_hide(self, widget):
|
|
|
|
"""Untoggle the image button when the menu is hidden."""
|
|
|
|
self.image_button.set_active(False)
|
2014-01-25 22:21:53 +00:00
|
|
|
|
2014-03-02 19:38:10 +00:00
|
|
|
def on_image_remove_activate(self, widget):
|
|
|
|
"""Remove the user's profile image."""
|
|
|
|
self.updated_image = ""
|
|
|
|
self.set_user_image(None)
|
|
|
|
|
2013-07-16 00:48:29 +00:00
|
|
|
def on_camera_dialog_apply(self, widget, data=None):
|
2014-01-25 22:21:53 +00:00
|
|
|
"""Commit changes when apply is clicked."""
|
2013-07-16 00:48:29 +00:00
|
|
|
self.updated_image = data
|
|
|
|
self.set_user_image(data)
|
2014-01-25 22:21:53 +00:00
|
|
|
|
2013-07-13 22:49:20 +00:00
|
|
|
def save_image(self):
|
|
|
|
"""Copy the updated image filename to ~/.face"""
|
|
|
|
# Check if the image has been updated.
|
2014-03-02 19:38:10 +00:00
|
|
|
if self.updated_image is None:
|
2013-07-14 15:45:10 +00:00
|
|
|
logger.debug('Photo not updated, not saving changes.')
|
2013-07-13 22:49:20 +00:00
|
|
|
return False
|
2014-01-25 22:21:53 +00:00
|
|
|
|
2014-04-02 01:11:19 +00:00
|
|
|
if not os.path.isfile(self.updated_image):
|
|
|
|
self.updated_image = ""
|
|
|
|
|
2014-03-02 15:59:38 +00:00
|
|
|
face = os.path.join(home, '.face')
|
2014-01-25 22:21:53 +00:00
|
|
|
|
2014-04-02 01:11:19 +00:00
|
|
|
if os.path.normpath(face) != os.path.normpath(self.updated_image):
|
|
|
|
# If the .face file already exists, remove it first.
|
|
|
|
logger.debug('Photo updated, saving ~/.face profile image.')
|
|
|
|
if os.path.isfile(face):
|
|
|
|
os.remove(face)
|
|
|
|
# Copy the new file to ~/.face
|
|
|
|
if os.path.isfile(self.updated_image):
|
|
|
|
shutil.copyfile(self.updated_image, face)
|
|
|
|
|
|
|
|
# Update AccountsService profile image
|
|
|
|
logger.debug('Photo updated, saving AccountsService profile image.')
|
|
|
|
self.accounts_service_set_user_image(self.updated_image)
|
|
|
|
|
|
|
|
# Update Pidgin buddy icon
|
|
|
|
self.set_pidgin_buddyicon(self.updated_image)
|
|
|
|
|
2013-07-13 22:49:20 +00:00
|
|
|
self.updated_image = None
|
|
|
|
return True
|
2014-01-25 22:21:53 +00:00
|
|
|
|
2014-04-02 01:11:19 +00:00
|
|
|
def accounts_service_get_user_image(self):
|
|
|
|
"""Get user profile image using AccountsService."""
|
|
|
|
bus = Gio.bus_get_sync(Gio.BusType.SYSTEM, None)
|
|
|
|
result = bus.call_sync('org.freedesktop.Accounts',
|
2014-04-02 01:42:05 +00:00
|
|
|
'/org/freedesktop/Accounts',
|
|
|
|
'org.freedesktop.Accounts',
|
|
|
|
'FindUserByName',
|
|
|
|
GLib.Variant('(s)', (username,)),
|
|
|
|
GLib.VariantType.new('(o)'),
|
|
|
|
Gio.DBusCallFlags.NONE,
|
|
|
|
-1,
|
|
|
|
None)
|
2014-04-02 01:11:19 +00:00
|
|
|
(path,) = result.unpack()
|
|
|
|
|
|
|
|
result = bus.call_sync('org.freedesktop.Accounts',
|
|
|
|
path,
|
|
|
|
'org.freedesktop.DBus.Properties',
|
|
|
|
'GetAll',
|
|
|
|
GLib.Variant('(s)',
|
2014-04-02 01:42:05 +00:00
|
|
|
('org.freedesktop.Accounts.User',)
|
|
|
|
),
|
2014-04-02 01:11:19 +00:00
|
|
|
GLib.VariantType.new('(a{sv})'),
|
|
|
|
Gio.DBusCallFlags.NONE,
|
|
|
|
-1,
|
|
|
|
None)
|
|
|
|
(props,) = result.unpack()
|
|
|
|
return props['IconFile']
|
|
|
|
|
2014-03-02 16:41:30 +00:00
|
|
|
def accounts_service_set_user_image(self, filename):
|
|
|
|
"""Set user profile image using AccountsService."""
|
2014-04-05 01:21:19 +00:00
|
|
|
try:
|
|
|
|
bus = Gio.bus_get_sync(Gio.BusType.SYSTEM, None)
|
|
|
|
result = bus.call_sync('org.freedesktop.Accounts',
|
|
|
|
'/org/freedesktop/Accounts',
|
|
|
|
'org.freedesktop.Accounts',
|
|
|
|
'FindUserByName',
|
|
|
|
GLib.Variant('(s)', (username,)),
|
|
|
|
GLib.VariantType.new('(o)'),
|
|
|
|
Gio.DBusCallFlags.NONE,
|
|
|
|
-1,
|
|
|
|
None)
|
|
|
|
(path,) = result.unpack()
|
|
|
|
|
|
|
|
bus.call_sync('org.freedesktop.Accounts',
|
|
|
|
path,
|
|
|
|
'org.freedesktop.Accounts.User',
|
|
|
|
'SetIconFile',
|
|
|
|
GLib.Variant('(s)', (filename,)),
|
|
|
|
GLib.VariantType.new('()'),
|
|
|
|
Gio.DBusCallFlags.NONE,
|
|
|
|
-1,
|
|
|
|
None)
|
|
|
|
except GLib.GError:
|
|
|
|
pass
|
2014-03-02 16:41:30 +00:00
|
|
|
|
2013-07-13 23:35:54 +00:00
|
|
|
def set_pidgin_buddyicon(self, filename=None):
|
|
|
|
"""Sets the pidgin buddyicon to filename (usually ~/.face).
|
2014-01-25 22:21:53 +00:00
|
|
|
|
2013-07-13 23:35:54 +00:00
|
|
|
If pidgin is running, use the dbus interface, otherwise directly modify
|
|
|
|
the XML file."""
|
2013-07-14 14:22:51 +00:00
|
|
|
if not os.path.exists(pidgin_prefs):
|
2013-07-14 15:45:10 +00:00
|
|
|
logger.debug('Pidgin not installed or never opened, not updating.')
|
2013-07-13 23:35:54 +00:00
|
|
|
return
|
2013-07-14 15:45:10 +00:00
|
|
|
logger.debug('Prompting user to update pidgin buddy icon')
|
2014-04-02 01:42:05 +00:00
|
|
|
primary = _("Update Pidgin buddy icon?")
|
|
|
|
secondary = _("Would you also like to update your Pidgin buddy icon?")
|
|
|
|
update_pidgin = get_confirmation_dialog(self, primary, secondary,
|
|
|
|
'pidgin')
|
2013-07-14 13:16:48 +00:00
|
|
|
if update_pidgin:
|
|
|
|
if has_running_process('pidgin'):
|
|
|
|
self.set_pidgin_buddyicon_dbus(filename)
|
|
|
|
else:
|
|
|
|
self.set_pidgin_buddyicon_xml(filename)
|
2013-07-14 15:45:10 +00:00
|
|
|
else:
|
|
|
|
logger.debug('Reject: Not updating pidgin buddy icon')
|
2014-01-25 22:21:53 +00:00
|
|
|
|
2013-07-13 23:35:54 +00:00
|
|
|
def set_pidgin_buddyicon_dbus(self, filename=None):
|
|
|
|
"""Set the pidgin buddy icon via dbus."""
|
2013-07-14 15:45:10 +00:00
|
|
|
logger.debug('Updating pidgin buddy icon via dbus')
|
2013-07-13 23:35:54 +00:00
|
|
|
bus = dbus.SessionBus()
|
2014-01-25 22:21:53 +00:00
|
|
|
obj = bus.get_object("im.pidgin.purple.PurpleService",
|
2013-07-13 23:35:54 +00:00
|
|
|
"/im/pidgin/purple/PurpleObject")
|
|
|
|
purple = dbus.Interface(obj, "im.pidgin.purple.PurpleInterface")
|
|
|
|
# To make the change instantly visible, set the icon to none first.
|
|
|
|
purple.PurplePrefsSetPath('/pidgin/accounts/buddyicon', '')
|
|
|
|
if filename:
|
|
|
|
purple.PurplePrefsSetPath('/pidgin/accounts/buddyicon', filename)
|
2014-01-25 22:21:53 +00:00
|
|
|
|
2013-07-13 23:35:54 +00:00
|
|
|
def set_pidgin_buddyicon_xml(self, filename=None):
|
|
|
|
"""Set the buddyicon used by pidgin to filename (via the xml file)."""
|
|
|
|
# This is hacky, but a working implementation for now...
|
2013-07-14 15:45:10 +00:00
|
|
|
logger.debug('Updating pidgin buddy icon via xml')
|
2013-07-14 14:22:51 +00:00
|
|
|
prefs_file = pidgin_prefs
|
2013-07-13 23:35:54 +00:00
|
|
|
tmp_buffer = []
|
|
|
|
if os.path.isfile(prefs_file):
|
|
|
|
for line in open(prefs_file):
|
|
|
|
if '<pref name=\'buddyicon\'' in line:
|
2014-01-25 22:21:53 +00:00
|
|
|
new = line.split('value=')[0]
|
2013-07-13 23:35:54 +00:00
|
|
|
if filename:
|
2014-01-25 22:21:53 +00:00
|
|
|
new = new + 'value=\'%s\'/>\n' % filename
|
2013-07-13 23:35:54 +00:00
|
|
|
else:
|
|
|
|
new = new + 'value=\'\'/>\n'
|
|
|
|
tmp_buffer.append(new)
|
|
|
|
else:
|
|
|
|
tmp_buffer.append(line)
|
|
|
|
write_prefs = open(prefs_file, 'w')
|
|
|
|
for line in tmp_buffer:
|
|
|
|
write_prefs.write(line)
|
|
|
|
write_prefs.close()
|
2014-01-25 22:21:53 +00:00
|
|
|
|
2013-07-13 22:49:20 +00:00
|
|
|
# = chfn functions ============================================ #
|
|
|
|
def get_chfn_details_updated(self):
|
|
|
|
"""Return True if chfn-related details have been modified."""
|
2013-07-14 15:45:10 +00:00
|
|
|
logger.debug('Checking if chfn details have been modified.')
|
2014-04-02 09:34:36 +00:00
|
|
|
modified = False
|
2014-04-02 01:42:05 +00:00
|
|
|
if self.first_name != self.first_name_entry.get_text().strip():
|
2014-04-02 09:34:36 +00:00
|
|
|
modified = True
|
2014-04-02 01:42:05 +00:00
|
|
|
if self.last_name != self.last_name_entry.get_text().strip():
|
2014-04-02 09:34:36 +00:00
|
|
|
modified = True
|
2014-04-02 01:42:05 +00:00
|
|
|
if self.home_phone != self.home_phone_entry.get_text().strip():
|
2014-04-02 09:34:36 +00:00
|
|
|
modified = True
|
2014-04-02 01:42:05 +00:00
|
|
|
if self.office_phone != self.office_phone_entry.get_text().strip():
|
2014-04-02 09:34:36 +00:00
|
|
|
modified = True
|
2014-04-02 01:42:05 +00:00
|
|
|
if modified:
|
2013-07-14 15:45:10 +00:00
|
|
|
logger.debug('chfn details have been modified.')
|
2013-07-13 18:17:51 +00:00
|
|
|
return True
|
2014-04-02 01:42:05 +00:00
|
|
|
else:
|
|
|
|
logger.debug('chfn details have NOT been modified.')
|
|
|
|
return False
|
2014-01-25 22:21:53 +00:00
|
|
|
|
2013-07-13 22:49:20 +00:00
|
|
|
def save_chfn_details(self):
|
|
|
|
"""Commit changes to chfn-related details. For full name, changes must
|
|
|
|
be performed as root. Other changes are done with the user password.
|
2014-01-25 22:21:53 +00:00
|
|
|
|
2013-07-13 22:49:20 +00:00
|
|
|
Return exit codes for 1) full name changes and 2) home/work phone
|
|
|
|
changes.
|
2014-01-25 22:21:53 +00:00
|
|
|
|
2013-07-13 22:49:20 +00:00
|
|
|
e.g. [0, 0] (both passed)"""
|
2013-07-13 15:09:49 +00:00
|
|
|
return_codes = []
|
2014-01-25 22:21:53 +00:00
|
|
|
|
2014-03-02 20:24:20 +00:00
|
|
|
# Get the password for sudo
|
|
|
|
sudo_dialog = SudoDialog.SudoDialog(
|
|
|
|
icon=None, name=_("Mugshot"), retries=3)
|
|
|
|
sudo_dialog.format_primary_text(_("Enter your password to change user "
|
|
|
|
"details."))
|
|
|
|
sudo_dialog.format_secondary_text(_("This is a security measure to "
|
|
|
|
"prevent unwanted updates\n"
|
|
|
|
"to your personal information."))
|
|
|
|
sudo_dialog.run()
|
|
|
|
sudo_dialog.hide()
|
|
|
|
password = sudo_dialog.get_password()
|
2014-03-02 21:01:53 +00:00
|
|
|
sudo_dialog.destroy()
|
2014-03-02 20:24:20 +00:00
|
|
|
|
2013-07-13 15:09:49 +00:00
|
|
|
if not password:
|
|
|
|
return return_codes
|
2014-01-25 22:21:53 +00:00
|
|
|
|
2014-01-26 00:44:26 +00:00
|
|
|
sudo = which('sudo')
|
2013-07-13 15:09:49 +00:00
|
|
|
chfn = which('chfn')
|
2014-01-25 22:21:53 +00:00
|
|
|
|
2013-07-13 15:09:49 +00:00
|
|
|
# Get each of the updated values.
|
2013-07-13 18:17:51 +00:00
|
|
|
first_name = get_entry_value(self.first_name_entry)
|
|
|
|
last_name = get_entry_value(self.last_name_entry)
|
2013-07-13 15:09:49 +00:00
|
|
|
full_name = "%s %s" % (first_name, last_name)
|
|
|
|
full_name = full_name.strip()
|
2013-07-13 18:17:51 +00:00
|
|
|
office_phone = get_entry_value(self.office_phone_entry)
|
2013-07-13 15:09:49 +00:00
|
|
|
if office_phone == '':
|
|
|
|
office_phone = 'none'
|
2013-07-13 18:17:51 +00:00
|
|
|
home_phone = get_entry_value(self.home_phone_entry)
|
2013-07-13 15:09:49 +00:00
|
|
|
if home_phone == '':
|
|
|
|
home_phone = 'none'
|
2014-01-25 22:21:53 +00:00
|
|
|
|
2013-07-13 15:09:49 +00:00
|
|
|
# Full name can only be modified by root. Try using sudo to modify.
|
2014-04-02 09:34:36 +00:00
|
|
|
if SudoDialog.check_sudo():
|
|
|
|
logger.debug('Attempting to set fullname with sudo chfn')
|
|
|
|
# Force the C language for guaranteed english strings in the script.
|
|
|
|
child = pexpect.spawn('%s %s %s' % (sudo, chfn, username),
|
|
|
|
env={"LANG": "C"})
|
|
|
|
child.timeout = 5
|
|
|
|
try:
|
|
|
|
child.expect([".*ssword.*", pexpect.EOF])
|
|
|
|
child.sendline(password)
|
|
|
|
child.expect(".*ame.*:")
|
|
|
|
child.sendline(full_name)
|
|
|
|
for i in range(5):
|
|
|
|
child.sendline('')
|
|
|
|
except pexpect.TIMEOUT:
|
|
|
|
# Password was incorrect, or sudo rights not granted
|
|
|
|
logger.warning('Timeout reached, password was incorrect or '
|
|
|
|
'sudo rights not granted.')
|
|
|
|
pass
|
|
|
|
child.close()
|
|
|
|
if child.exitstatus == 0:
|
|
|
|
self.first_name = first_name
|
|
|
|
self.last_name = last_name
|
|
|
|
return_codes.append(child.exitstatus)
|
|
|
|
else:
|
|
|
|
return_codes.append(0)
|
2013-07-13 15:09:49 +00:00
|
|
|
|
2013-07-13 18:17:51 +00:00
|
|
|
logger.debug('Attempting to set user details with chfn')
|
2014-03-09 04:16:54 +00:00
|
|
|
child = pexpect.spawn(chfn, env={"LANG": "C"})
|
2013-07-13 18:17:51 +00:00
|
|
|
child.timeout = 5
|
|
|
|
try:
|
2013-07-17 16:16:00 +00:00
|
|
|
child.expect([".*ssword.*", pexpect.EOF])
|
2013-07-13 18:17:51 +00:00
|
|
|
child.sendline(password)
|
2013-07-17 16:16:00 +00:00
|
|
|
child.expect(['Room Number.*:', 'Office.*:'])
|
2013-07-13 18:17:51 +00:00
|
|
|
child.sendline('')
|
2013-07-17 16:16:00 +00:00
|
|
|
child.expect(['Work Phone.*:', 'Office Phone.*:'])
|
2013-07-13 18:17:51 +00:00
|
|
|
child.sendline(office_phone)
|
|
|
|
child.expect('Home Phone.*:')
|
|
|
|
child.sendline(home_phone)
|
|
|
|
child.sendline(home_phone)
|
|
|
|
except pexpect.TIMEOUT:
|
2014-03-02 20:24:20 +00:00
|
|
|
logger.warning('Timeout reached, password was likely incorrect.')
|
2013-07-13 15:09:49 +00:00
|
|
|
child.close(True)
|
2013-07-13 18:17:51 +00:00
|
|
|
if child.exitstatus == 0:
|
|
|
|
self.office_phone = office_phone
|
|
|
|
self.home_phone = home_phone
|
2013-07-13 15:09:49 +00:00
|
|
|
return_codes.append(child.exitstatus)
|
|
|
|
return return_codes
|
2014-01-25 22:21:53 +00:00
|
|
|
|
2014-04-02 01:42:05 +00:00
|
|
|
# = LibreOffice ========================================================= #
|
2013-07-14 12:18:36 +00:00
|
|
|
def get_libreoffice_details_updated(self):
|
|
|
|
"""Return True if LibreOffice settings need to be updated."""
|
|
|
|
# Return False if there is no preferences file.
|
2013-07-14 14:22:51 +00:00
|
|
|
if not os.path.isfile(libreoffice_prefs):
|
2013-07-14 15:45:10 +00:00
|
|
|
logger.debug('LibreOffice is not installed or has not been opened.'
|
|
|
|
' Not updating.')
|
2013-07-14 12:18:36 +00:00
|
|
|
return False
|
|
|
|
# Compare the current entries to the existing LibreOffice data.
|
|
|
|
data = self.get_libreoffice_data()
|
|
|
|
if data['first_name'] != get_entry_value(self.first_name_entry):
|
|
|
|
return True
|
|
|
|
if data['last_name'] != get_entry_value(self.last_name_entry):
|
|
|
|
return True
|
|
|
|
if data['initials'] != get_entry_value(self.initials_entry):
|
|
|
|
return True
|
|
|
|
if data['email'] != get_entry_value(self.email_entry):
|
|
|
|
return True
|
|
|
|
if data['home_phone'] != get_entry_value(self.home_phone_entry):
|
|
|
|
return True
|
|
|
|
if data['office_phone'] != get_entry_value(self.office_phone_entry):
|
|
|
|
return True
|
|
|
|
if data['fax'] != get_entry_value(self.fax_entry):
|
|
|
|
return True
|
2013-07-14 15:45:10 +00:00
|
|
|
logger.debug('LibreOffice details do not need to be updated.')
|
2013-07-14 12:18:36 +00:00
|
|
|
return False
|
2014-01-25 22:21:53 +00:00
|
|
|
|
2013-07-14 12:18:36 +00:00
|
|
|
def get_libreoffice_data(self):
|
2014-04-02 01:42:05 +00:00
|
|
|
"""Get each of the preferences from the LibreOffice
|
|
|
|
registymodifications preferences file.
|
2014-01-25 22:21:53 +00:00
|
|
|
|
2013-07-14 12:18:36 +00:00
|
|
|
Return a dict with the details."""
|
2013-07-14 14:22:51 +00:00
|
|
|
prefs_file = libreoffice_prefs
|
2014-01-25 22:21:53 +00:00
|
|
|
data = {'first_name': '', 'last_name': '', 'initials': '', 'email': '',
|
2013-07-14 12:18:36 +00:00
|
|
|
'home_phone': '', 'office_phone': '', 'fax': ''}
|
|
|
|
if os.path.isfile(prefs_file):
|
2013-07-14 15:45:10 +00:00
|
|
|
logger.debug('Getting settings from %s' % prefs_file)
|
2013-07-14 12:18:36 +00:00
|
|
|
for line in open(prefs_file):
|
|
|
|
if "UserProfile/Data" in line:
|
2014-01-26 00:44:26 +00:00
|
|
|
try:
|
|
|
|
value = line.split('<value>')[1].split('</value>')[0]
|
|
|
|
except IndexError:
|
|
|
|
continue
|
2013-07-14 14:22:51 +00:00
|
|
|
value = value.strip()
|
2013-07-14 12:18:36 +00:00
|
|
|
# First Name
|
|
|
|
if 'name="givenname"' in line:
|
|
|
|
data['first_name'] = value
|
|
|
|
# Last Name
|
|
|
|
elif 'name="sn"' in line:
|
|
|
|
data['last_name'] = value
|
|
|
|
# Initials
|
|
|
|
elif 'name="initials"' in line:
|
|
|
|
data['initials'] = value
|
|
|
|
# Email
|
|
|
|
elif 'name="mail"' in line:
|
|
|
|
data['email'] = value
|
|
|
|
# Home Phone
|
|
|
|
elif 'name="homephone"' in line:
|
|
|
|
data['home_phone'] = value
|
|
|
|
# Office Phone
|
|
|
|
elif 'name="telephonenumber"' in line:
|
|
|
|
data['office_phone'] = value
|
|
|
|
# Fax Number
|
|
|
|
elif 'name="facsimiletelephonenumber"' in line:
|
|
|
|
data['fax'] = value
|
|
|
|
else:
|
|
|
|
pass
|
|
|
|
return data
|
2014-01-25 22:21:53 +00:00
|
|
|
|
2013-07-14 12:18:36 +00:00
|
|
|
def set_libreoffice_data(self):
|
|
|
|
"""Update the LibreOffice registymodifications preferences file."""
|
2013-07-14 14:22:51 +00:00
|
|
|
prefs_file = libreoffice_prefs
|
2013-07-14 12:18:36 +00:00
|
|
|
if os.path.isfile(prefs_file):
|
2013-07-14 15:45:10 +00:00
|
|
|
logger.debug('Prompting user to update LibreOffice details.')
|
2014-04-02 01:42:05 +00:00
|
|
|
update_libreoffice = \
|
|
|
|
get_confirmation_dialog(self,
|
|
|
|
_("Update LibreOffice user details?"),
|
|
|
|
_("Would you also like to update your "
|
|
|
|
"user details in LibreOffice?"),
|
|
|
|
'libreoffice-startcenter')
|
2013-07-14 15:45:10 +00:00
|
|
|
if update_libreoffice:
|
|
|
|
logger.debug('Confirm: Updating details.')
|
2013-07-17 16:16:00 +00:00
|
|
|
first_name = get_entry_value(self.first_name_entry)
|
|
|
|
first_name_updated = False
|
|
|
|
last_name = get_entry_value(self.last_name_entry)
|
|
|
|
last_name_updated = False
|
|
|
|
initials = get_entry_value(self.initials_entry)
|
|
|
|
initials_updated = False
|
|
|
|
email = get_entry_value(self.email_entry)
|
|
|
|
email_updated = False
|
|
|
|
home_phone = get_entry_value(self.home_phone_entry)
|
|
|
|
home_phone_updated = False
|
|
|
|
office_phone = get_entry_value(self.office_phone_entry)
|
|
|
|
office_phone_updated = False
|
|
|
|
fax = get_entry_value(self.fax_entry)
|
|
|
|
fax_updated = False
|
2013-07-14 15:45:10 +00:00
|
|
|
tmp_buffer = []
|
|
|
|
for line in open(prefs_file):
|
|
|
|
new = None
|
|
|
|
if "UserProfile/Data" in line:
|
|
|
|
new = line.split('<value>')[0]
|
|
|
|
# First Name
|
|
|
|
if 'name="givenname"' in line:
|
|
|
|
new = new + '<value>%s</value></prop></item>\n' % \
|
2013-07-17 16:16:00 +00:00
|
|
|
first_name
|
|
|
|
first_name_updated = True
|
2013-07-14 15:45:10 +00:00
|
|
|
# Last Name
|
|
|
|
elif 'name="sn"' in line:
|
|
|
|
new = new + '<value>%s</value></prop></item>\n' % \
|
2013-07-17 16:16:00 +00:00
|
|
|
last_name
|
|
|
|
last_name_updated = True
|
2013-07-14 15:45:10 +00:00
|
|
|
# Initials
|
|
|
|
elif 'name="initials"' in line:
|
|
|
|
new = new + '<value>%s</value></prop></item>\n' % \
|
2013-07-17 16:16:00 +00:00
|
|
|
initials
|
|
|
|
initials_updated = True
|
2013-07-14 15:45:10 +00:00
|
|
|
# Email
|
|
|
|
elif 'name="mail"' in line:
|
|
|
|
new = new + '<value>%s</value></prop></item>\n' % \
|
2013-07-17 16:16:00 +00:00
|
|
|
email
|
|
|
|
email_updated = True
|
2013-07-14 15:45:10 +00:00
|
|
|
# Home Phone
|
|
|
|
elif 'name="homephone"' in line:
|
|
|
|
new = new + '<value>%s</value></prop></item>\n' % \
|
2013-07-17 16:16:00 +00:00
|
|
|
home_phone
|
|
|
|
home_phone_updated = True
|
2013-07-14 15:45:10 +00:00
|
|
|
# Office Phone
|
|
|
|
elif 'name="telephonenumber"' in line:
|
|
|
|
new = new + '<value>%s</value></prop></item>\n' % \
|
2013-07-17 16:16:00 +00:00
|
|
|
office_phone
|
|
|
|
office_phone_updated = True
|
2013-07-14 15:45:10 +00:00
|
|
|
# Fax Number
|
|
|
|
elif 'name="facsimiletelephonenumber"' in line:
|
|
|
|
new = new + '<value>%s</value></prop></item>\n' % \
|
2013-07-17 16:16:00 +00:00
|
|
|
fax
|
|
|
|
fax_updated = True
|
2013-07-14 15:45:10 +00:00
|
|
|
else:
|
|
|
|
new = line
|
|
|
|
tmp_buffer.append(new)
|
2013-07-17 16:16:00 +00:00
|
|
|
elif '</oor:items>' in line:
|
|
|
|
pass
|
2013-07-14 12:18:36 +00:00
|
|
|
else:
|
2013-07-14 15:45:10 +00:00
|
|
|
tmp_buffer.append(line)
|
|
|
|
open_prefs = open(prefs_file, 'w')
|
|
|
|
for line in tmp_buffer:
|
|
|
|
open_prefs.write(line)
|
2014-01-25 22:21:53 +00:00
|
|
|
|
2013-07-17 16:16:00 +00:00
|
|
|
if not first_name_updated:
|
2014-01-25 22:21:53 +00:00
|
|
|
string = \
|
2014-04-02 01:42:05 +00:00
|
|
|
'<item oor:path="/org.openoffice.UserProfile/Data">'
|
2014-01-25 22:21:53 +00:00
|
|
|
'<prop oor:name="givenname" oor:op="fuse">'
|
|
|
|
'<value>%s</value></prop></item>\n' % first_name
|
2013-07-17 16:16:00 +00:00
|
|
|
open_prefs.write(string)
|
|
|
|
if not last_name_updated:
|
2014-01-25 22:21:53 +00:00
|
|
|
string = \
|
2014-04-02 01:42:05 +00:00
|
|
|
'<item oor:path="/org.openoffice.UserProfile/Data">'
|
2014-01-25 22:21:53 +00:00
|
|
|
'<prop oor:name="sn" oor:op="fuse">'
|
|
|
|
'<value>%s</value></prop></item>\n' % last_name
|
2013-07-17 16:16:00 +00:00
|
|
|
open_prefs.write(string)
|
|
|
|
if not initials_updated:
|
2014-01-25 22:21:53 +00:00
|
|
|
string = \
|
2014-04-02 01:42:05 +00:00
|
|
|
'<item oor:path="/org.openoffice.UserProfile/Data">'
|
2014-01-25 22:21:53 +00:00
|
|
|
'<prop oor:name="initials" oor:op="fuse">'
|
|
|
|
'<value>%s</value></prop></item>\n' % initials
|
2013-07-17 16:16:00 +00:00
|
|
|
open_prefs.write(string)
|
|
|
|
if not email_updated:
|
2014-01-25 22:21:53 +00:00
|
|
|
string = \
|
2014-04-02 01:42:05 +00:00
|
|
|
'<item oor:path="/org.openoffice.UserProfile/Data">'
|
2014-01-25 22:21:53 +00:00
|
|
|
'<prop oor:name="mail" oor:op="fuse">'
|
|
|
|
'<value>%s</value></prop></item>\n' % email
|
2013-07-17 16:16:00 +00:00
|
|
|
open_prefs.write(string)
|
|
|
|
if not home_phone_updated:
|
2014-01-25 22:21:53 +00:00
|
|
|
string = \
|
2014-04-02 01:42:05 +00:00
|
|
|
'<item oor:path="/org.openoffice.UserProfile/Data">'
|
2014-01-25 22:21:53 +00:00
|
|
|
'<prop oor:name="homephone" oor:op="fuse">'
|
|
|
|
'<value>%s</value></prop></item>\n' % home_phone
|
2013-07-17 16:16:00 +00:00
|
|
|
open_prefs.write(string)
|
|
|
|
if not office_phone_updated:
|
2014-01-25 22:21:53 +00:00
|
|
|
string = \
|
2014-04-02 01:42:05 +00:00
|
|
|
'<item oor:path="/org.openoffice.UserProfile/Data">'
|
2014-01-25 22:21:53 +00:00
|
|
|
'<prop oor:name="telephonenumber" oor:op="fuse">'
|
|
|
|
'<value>%s</value></prop></item>\n' % office_phone
|
2013-07-17 16:16:00 +00:00
|
|
|
open_prefs.write(string)
|
|
|
|
if not fax_updated:
|
2014-01-25 22:21:53 +00:00
|
|
|
string = \
|
2014-04-02 01:42:05 +00:00
|
|
|
'<item oor:path="/org.openoffice.UserProfile/Data">'
|
2014-01-25 22:21:53 +00:00
|
|
|
'<prop oor:name="facsimiletelephonenumber" oor:op="fuse">'
|
|
|
|
'<value>%s</value></prop></item>\n' % fax
|
2013-07-17 16:16:00 +00:00
|
|
|
open_prefs.write(string)
|
|
|
|
open_prefs.write('</oor:items>')
|
2013-07-14 15:45:10 +00:00
|
|
|
open_prefs.close()
|
|
|
|
else:
|
|
|
|
logger.debug('Reject: Not updating.')
|
2014-01-25 22:21:53 +00:00
|
|
|
|
2014-04-02 01:42:05 +00:00
|
|
|
# = Stock Browser ======================================================= #
|
2013-07-13 22:49:20 +00:00
|
|
|
def on_image_from_stock_activate(self, widget):
|
2014-01-25 22:21:53 +00:00
|
|
|
"""When the 'Select image from stock' menu item is clicked, load and
|
2013-07-13 22:49:20 +00:00
|
|
|
display the stock photo browser."""
|
|
|
|
self.load_stock_browser()
|
|
|
|
self.stock_browser.show_all()
|
2014-01-25 22:21:53 +00:00
|
|
|
|
2013-07-13 22:49:20 +00:00
|
|
|
def load_stock_browser(self):
|
|
|
|
"""Load the stock photo browser."""
|
|
|
|
# Check if the photos have already been loaded.
|
|
|
|
model = self.iconview.get_model()
|
|
|
|
if len(model) != 0:
|
|
|
|
logger.debug("Stock browser already loaded.")
|
|
|
|
return
|
2014-01-25 22:21:53 +00:00
|
|
|
|
2014-02-28 19:02:53 +00:00
|
|
|
# If they have not, load each photo from faces_dir.
|
2013-07-13 22:49:20 +00:00
|
|
|
logger.debug("Loading stock browser photos.")
|
2014-02-28 19:02:53 +00:00
|
|
|
for filename in os.listdir(faces_dir):
|
|
|
|
full_path = os.path.join(faces_dir, filename)
|
2013-07-13 22:49:20 +00:00
|
|
|
if os.path.isfile(full_path):
|
|
|
|
pixbuf = GdkPixbuf.Pixbuf.new_from_file(full_path)
|
2014-04-02 01:42:05 +00:00
|
|
|
scaled = pixbuf.scale_simple(90, 90,
|
|
|
|
GdkPixbuf.InterpType.HYPER)
|
2013-07-13 22:49:20 +00:00
|
|
|
model.append([full_path, scaled])
|
2014-01-25 22:21:53 +00:00
|
|
|
|
2013-07-14 14:22:51 +00:00
|
|
|
def on_stock_iconview_selection_changed(self, widget):
|
|
|
|
"""Enable stock submission only when an item is selected."""
|
|
|
|
selected_items = self.iconview.get_selected_items()
|
2014-04-02 01:42:05 +00:00
|
|
|
is_sensitive = len(selected_items) > 0
|
|
|
|
self.builder.get_object('stock_ok').set_sensitive(is_sensitive)
|
2014-01-25 22:21:53 +00:00
|
|
|
|
2013-07-13 22:49:20 +00:00
|
|
|
def on_stock_browser_delete_event(self, widget, event):
|
|
|
|
"""Hide the stock browser instead of deleting it."""
|
|
|
|
widget.hide()
|
2013-07-13 15:09:49 +00:00
|
|
|
return True
|
2014-01-25 22:21:53 +00:00
|
|
|
|
2013-07-13 22:49:20 +00:00
|
|
|
def on_stock_cancel_clicked(self, widget):
|
|
|
|
"""Hide the stock browser when Cancel is clicked."""
|
|
|
|
self.stock_browser.hide()
|
2014-01-25 22:21:53 +00:00
|
|
|
|
2013-07-13 22:49:20 +00:00
|
|
|
def on_stock_ok_clicked(self, widget):
|
2014-01-25 22:21:53 +00:00
|
|
|
"""When the stock browser OK button is clicked, get the currently
|
2013-07-13 22:49:20 +00:00
|
|
|
selected photo and set it to the user profile image."""
|
|
|
|
selected_items = self.iconview.get_selected_items()
|
|
|
|
if len(selected_items) != 0:
|
|
|
|
# Get the filename from the stock browser iconview.
|
|
|
|
path = int(selected_items[0].to_string())
|
|
|
|
filename = self.iconview.get_model()[path][0]
|
|
|
|
logger.debug("Selected %s" % filename)
|
2014-01-25 22:21:53 +00:00
|
|
|
|
2013-07-13 22:49:20 +00:00
|
|
|
# Update variables and widgets, then hide.
|
|
|
|
self.set_user_image(filename)
|
|
|
|
self.updated_image = filename
|
|
|
|
self.stock_browser.hide()
|
2014-01-25 22:21:53 +00:00
|
|
|
|
2013-07-13 22:49:20 +00:00
|
|
|
def on_stock_iconview_item_activated(self, widget, path):
|
2013-07-14 14:22:51 +00:00
|
|
|
"""Allow selecting a stock photo with Enter."""
|
2013-07-13 22:49:20 +00:00
|
|
|
self.on_stock_ok_clicked(widget)
|
2014-01-25 22:21:53 +00:00
|
|
|
|
2014-04-02 01:42:05 +00:00
|
|
|
# = Image Browser ======================================================= #
|
2013-07-13 22:49:20 +00:00
|
|
|
def on_image_from_browse_activate(self, widget):
|
|
|
|
"""Browse for a user profile image."""
|
|
|
|
# Run the dialog, grab the filename if confirmed, then hide the dialog.
|
2013-07-18 01:28:32 +00:00
|
|
|
response = self.chooser.run()
|
|
|
|
if response == Gtk.ResponseType.APPLY:
|
2013-07-13 22:49:20 +00:00
|
|
|
# Update the user image, store the path for committing later.
|
2014-03-29 15:29:49 +00:00
|
|
|
self.updated_image = helpers.new_tempfile('browse')
|
2014-01-25 22:21:53 +00:00
|
|
|
self.filechooser_preview_pixbuf.savev(self.updated_image, "png",
|
2014-04-02 01:42:05 +00:00
|
|
|
[], [])
|
2013-07-13 22:49:20 +00:00
|
|
|
logger.debug("Selected %s" % self.updated_image)
|
|
|
|
self.set_user_image(self.updated_image)
|
2013-07-18 01:28:32 +00:00
|
|
|
self.chooser.hide()
|
2014-01-25 22:21:53 +00:00
|
|
|
|
2013-07-18 01:28:32 +00:00
|
|
|
def on_filechooserdialog_update_preview(self, widget):
|
2014-01-25 22:21:53 +00:00
|
|
|
"""Update the preview image used in the file chooser."""
|
2013-07-18 01:28:32 +00:00
|
|
|
filename = widget.get_filename()
|
|
|
|
if not filename:
|
|
|
|
self.file_chooser_preview.set_from_icon_name('folder', 128)
|
|
|
|
return
|
|
|
|
if not os.path.isfile(filename):
|
|
|
|
self.file_chooser_preview.set_from_icon_name('folder', 128)
|
|
|
|
return
|
|
|
|
filechooser_pixbuf = GdkPixbuf.Pixbuf.new_from_file(filename)
|
2014-01-25 22:21:53 +00:00
|
|
|
|
2013-07-18 01:28:32 +00:00
|
|
|
# Get the image dimensions.
|
|
|
|
height = filechooser_pixbuf.get_height()
|
|
|
|
width = filechooser_pixbuf.get_width()
|
|
|
|
start_x = 0
|
|
|
|
start_y = 0
|
2014-01-25 22:21:53 +00:00
|
|
|
|
2013-07-18 01:28:32 +00:00
|
|
|
if self.crop_center.get_active():
|
|
|
|
# Calculate a balanced center.
|
|
|
|
if width > height:
|
2014-01-25 22:21:53 +00:00
|
|
|
start_x = (width - height) / 2
|
2013-07-18 01:28:32 +00:00
|
|
|
width = height
|
|
|
|
else:
|
2014-01-25 22:21:53 +00:00
|
|
|
start_y = (height - width) / 2
|
2013-07-18 01:28:32 +00:00
|
|
|
height = width
|
2014-01-25 22:21:53 +00:00
|
|
|
|
2013-07-18 01:28:32 +00:00
|
|
|
elif self.crop_left.get_active():
|
|
|
|
start_x = 0
|
|
|
|
if width > height:
|
|
|
|
width = height
|
|
|
|
else:
|
2014-01-25 22:21:53 +00:00
|
|
|
start_y = (height - width) / 2
|
2013-07-18 01:28:32 +00:00
|
|
|
height = width
|
|
|
|
elif self.crop_right.get_active():
|
|
|
|
if width > height:
|
2014-01-25 22:21:53 +00:00
|
|
|
start_x = width - height
|
2013-07-18 01:28:32 +00:00
|
|
|
width = height
|
|
|
|
else:
|
2014-01-25 22:21:53 +00:00
|
|
|
start_y = (height - width) / 2
|
2013-07-18 01:28:32 +00:00
|
|
|
height = width
|
2014-01-25 22:21:53 +00:00
|
|
|
|
2013-07-18 01:28:32 +00:00
|
|
|
# Create a new cropped pixbuf.
|
2014-01-25 22:21:53 +00:00
|
|
|
self.filechooser_preview_pixbuf = \
|
|
|
|
filechooser_pixbuf.new_subpixbuf(start_x, start_y, width, height)
|
|
|
|
|
2014-04-02 01:42:05 +00:00
|
|
|
scaled = self.filechooser_preview_pixbuf.scale_simple(
|
|
|
|
128, 128,
|
|
|
|
GdkPixbuf.InterpType.HYPER)
|
2013-07-18 01:28:32 +00:00
|
|
|
self.file_chooser_preview.set_from_pixbuf(scaled)
|
2014-01-25 22:21:53 +00:00
|
|
|
|
2013-07-18 01:28:32 +00:00
|
|
|
def on_crop_changed(self, widget, data=None):
|
2014-01-25 22:21:53 +00:00
|
|
|
"""Update the preview image when crop style is modified."""
|
2013-07-18 01:28:32 +00:00
|
|
|
if widget.get_active():
|
|
|
|
self.on_filechooserdialog_update_preview(self.chooser)
|