2 # -*- coding: utf-8 -*-
4 # (c) Copyright 2003-2009 Hewlett-Packard Development Company, L.P.
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 2 of the License, or
9 # (at your option) any later version.
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 __title__ = "Fax Address Book"
26 __doc__ = "A simple fax address book for HPLIP."
35 from base import utils, tui, module
38 # Console class (from ASPN Python Cookbook)
39 # Author: James Thiele
42 # Location: http://www.eskimo.com/~jet/python/examples/cmd/
43 # Copyright (c) 2004, James Thiele
44 class Console(cmd.Cmd):
47 cmd.Cmd.__init__(self)
48 self.intro = "Type 'help' for a list of commands. Type 'exit' or 'quit' to quit."
49 self.db = fax.FaxAddressBook() # database instance
50 self.prompt = log.bold("hp-fab > ")
53 def do_hist(self, args):
54 """Print a list of commands that have been entered"""
57 def do_exit(self, args):
58 """Exits from the console"""
61 def do_quit(self, args):
62 """Exits from the console"""
65 # Command definitions to support Cmd object functionality
66 def do_EOF(self, args):
67 """Exit on system end of file character"""
68 return self.do_exit(args)
70 def do_help(self, args):
71 """Get help on commands
72 'help' or '?' with no arguments prints a list of commands for which help is available
73 'help <command>' or '? <command>' gives help on <command>
75 # The only reason to define this method is for the help text in the doc string
76 cmd.Cmd.do_help(self, args)
78 # Override methods in Cmd object
80 """Initialization before prompting user for commands.
81 Despite the claims in the Cmd documentaion, Cmd.preloop() is not a stub.
83 cmd.Cmd.preloop(self) # sets up command completion
84 self._hist = [] # No history yet
85 self._locals = {} # Initialize execution namespace for user
91 """Take care of any unfinished business.
92 Despite the claims in the Cmd documentaion, Cmd.postloop() is not a stub.
94 cmd.Cmd.postloop(self) # Clean up command completion
97 def precmd(self, line):
98 """ This method is called after the line has been input but before
99 it has been interpreted. If you want to modifdy the input line
100 before execution (for example, variable substitution) do it here.
102 self._hist += [line.strip()]
105 def postcmd(self, stop, line):
106 """If you want to stop the console, return something that evaluates to true.
107 If you want to do some post command processing, do it here.
112 """Do nothing on empty input line"""
115 def default(self, line):
116 log.error("Unrecognized command. Use 'help' to list commands.")
118 def get_nickname(self, args, fail_if_match=True, alt_text=False):
122 nickname = raw_input(log.bold("Enter the name to add to the group (<enter>=done*, c=cancel) ? ")).strip()
124 nickname = raw_input(log.bold("Enter name (c=cancel) ? ")).strip()
126 if nickname.lower() == 'c':
127 print log.red("Canceled")
134 log.error("Name must not be blank.")
139 if self.db.get(nickname) is not None:
140 log.error("Name already exists. Please choose a different name.")
144 if self.db.get(nickname) is None:
145 log.error("Name not found. Please enter a different name.")
151 nickname = args.strip()
154 if self.db.get(nickname) is not None:
155 log.error("Name already exists. Please choose a different name.")
159 if self.db.get(nickname) is None:
160 log.error("Name not found. Please enter a different name.")
166 def get_groupname(self, args, fail_if_match=True, alt_text=False):
167 all_groups = self.db.get_all_groups()
172 groupname = raw_input(log.bold("Enter the group to join (<enter>=done*, c=cancel) ? ")).strip()
174 groupname = raw_input(log.bold("Enter the group (c=cancel) ? ")).strip()
177 if groupname.lower() == 'c':
178 print log.red("Canceled")
185 log.error("The group name must not be blank.")
188 if groupname == 'All':
189 print "Cannot specify group 'All'. Please choose a different group."
193 if groupname in all_groups:
194 log.error("Group already exists. Please choose a different group.")
198 if groupname not in all_groups:
199 log.error("Group not found. Please enter a different group.")
205 groupname = args.strip()
208 if groupname in all_groups:
209 log.error("Group already exists. Please choose a different group.")
213 if groupname not in all_groups:
214 log.error("Group not found. Please enter a different group.")
219 def do_list(self, args):
221 List names and/or groups.
222 list [names|groups|all|]
223 dir [names|groups|all|]
227 scope = args.strip().split()[0]
229 if args.startswith('nam'):
233 elif args.startswith('gro'):
242 def do_names(self, args):
247 all_entries = self.db.get_all_records()
248 log.debug(all_entries)
250 print log.bold("\nNames:\n")
251 if len(all_entries) > 0:
254 f.header = ("Name", "Fax Number", "Notes", "Member of Group(s)")
255 for name, e in all_entries.items():
256 if not name.startswith('__'):
257 f.add((name, e['fax'], e['notes'], ', '.join(e['groups'])))
266 def do_groups(self, args):
271 all_groups = self.db.get_all_groups()
272 log.debug(all_groups)
274 print log.bold("\nGroups:\n")
278 f.header = ("Group", "Members")
279 for group in all_groups:
280 f.add((group, ', '.join([x for x in self.db.group_members(group) if not x.startswith('__')])))
289 def do_edit(self, args):
295 nickname = self.get_nickname(args, fail_if_match=False)
296 if not nickname: return
298 e = self.db.get(nickname)
301 print log.bold("\nEdit/modify information for %s:\n" % nickname)
303 # save_title = e['title']
304 # title = raw_input(log.bold("Title (<enter>='%s', c=cancel) ? " % save_title)).strip()
306 # if title.lower() == 'c':
307 # print log.red("Canceled")
313 # save_firstname = e['firstname']
314 # firstname = raw_input(log.bold("First name (<enter>='%s', c=cancel) ? " % save_firstname)).strip()
316 # if firstname.lower() == 'c':
317 # print log.red("Canceled")
321 # firstname = save_firstname
323 # save_lastname = e['lastname']
324 # lastname = raw_input(log.bold("Last name (<enter>='%s', c=cancel) ? " % save_lastname)).strip()
326 # if lastname.lower() == 'c':
327 # print log.red("Canceled")
331 # lastname = save_lastname
337 save_faxnum = e['fax']
339 faxnum = raw_input(log.bold("Fax Number (<enter>='%s', c=cancel) ? " % save_faxnum)).strip()
341 if faxnum.lower() == 'c':
342 print log.red("Canceled")
345 if not faxnum and not save_faxnum:
346 log.error("Fax number must not be empty.")
354 if c not in '0123456789-(+) *#':
355 log.error("Invalid characters in fax number. Fax number may only contain '0123456789-(+) '")
362 save_notes = e['notes']
363 notes = raw_input(log.bold("Notes (<enter>='%s', c=cancel) ? " % save_notes)).strip()
365 if notes.lower() == 'c':
366 print log.red("Canceled")
373 print "\nLeave or Stay in a Group:\n"
376 for g in e['groups']:
380 ok, ans = tui.enter_yes_no("Stay in group %s " % g,
381 choice_prompt="(y=yes* (stay), n=no (leave), c=cancel) ? ")
384 print log.red("Canceled")
390 print "\nJoin New Group(s):\n"
393 add_group = self.get_groupname('', fail_if_match=False, alt_text=True)
395 if add_group.lower() == 'c':
396 print log.red("Canceled")
402 all_groups = self.db.get_all_groups()
404 if add_group not in all_groups:
405 log.warn("Group not found.")
406 ok, ans = tui.enter_yes_no("Is this a new group",
407 choice_prompt="(y=yes* (new), n=no, c=cancel) ? ")
410 print log.red("Canceled")
416 if add_group in e['groups']:
417 log.error("Group already specified. Choose a different group name or press <enter> to continue.")
420 new_groups.append(add_group)
422 self.db.set(nickname, title, firstname, lastname, faxnum, new_groups, notes)
423 self.do_show(nickname)
430 def do_editgrp(self, args):
436 group = self.get_groupname(args, fail_if_match=False)
439 old_entries = self.db.group_members(group)
443 print "\nExisting Names in Group:\n"
445 for e in old_entries:
446 if not e.startswith('__'):
447 ok, ans = tui.enter_yes_no("Should '%s' stay in this group " % e,
448 choice_prompt="(y=yes* (stay), n=no (leave), c=cancel) ? ")
453 print log.red("Canceled")
457 new_entries.append(e)
459 print "\nAdd New Names to Group:\n"
462 nickname = self.get_nickname('', fail_if_match=False, alt_text=True)
464 if nickname.lower() == 'c':
465 print log.red("Canceled")
468 if not nickname.lower():
471 new_entries.append(nickname)
473 self.db.update_groups(group, new_entries)
477 do_modifygrp = do_editgrp
480 def do_add(self, args):
486 nickname = self.get_nickname(args, fail_if_match=True)
487 if not nickname: return
489 print log.bold("\nEnter information for %s:\n" % nickname)
491 # title = raw_input(log.bold("Title (c=cancel) ? ")).strip()
493 # if title.lower() == 'c':
494 # print log.red("Canceled")
497 # firstname = raw_input(log.bold("First name (c=cancel) ? ")).strip()
499 # if firstname.lower() == 'c':
500 # print log.red("Canceled")
503 # lastname = raw_input(log.bold("Last name (c=cancel) ? ")).strip()
505 # if lastname.lower() == 'c':
506 # print log.red("Canceled")
514 faxnum = raw_input(log.bold("Fax Number (c=cancel) ? ")).strip()
516 if faxnum.lower() == 'c':
517 print log.red("Canceled")
521 log.error("Fax number must not be empty.")
526 if c not in '0123456789-(+) *#':
527 log.error("Invalid characters in fax number. Fax number may only contain '0123456789-(+) *#'")
534 notes = raw_input(log.bold("Notes (c=cancel) ? ")).strip()
536 if notes.strip().lower() == 'c':
537 print log.red("Canceled")
541 all_groups = self.db.get_all_groups()
543 add_group = raw_input(log.bold("Member of group (<enter>=done*, c=cancel) ? " )).strip()
545 if add_group.lower() == 'c':
546 print log.red("Canceled")
552 if add_group == 'All':
553 print log.red("Cannot specify 'All'.")
556 if add_group not in all_groups:
557 log.warn("Group not found.")
560 user_input = raw_input(log.bold("Is this a new group (y=yes*, n=no) ? ")).lower().strip()
562 if user_input not in ['', 'n', 'y']:
563 log.error("Please enter 'y', 'n' or press <enter> for 'yes'.")
568 if user_input == 'n':
571 if add_group in groups:
572 log.error("Group already specified. Choose a different group name or press <enter> to continue.")
575 groups.append(add_group)
579 self.db.set(nickname, title, firstname, lastname, faxnum, groups, notes)
580 self.do_show(nickname)
586 def do_addgrp(self, args):
592 group = self.get_groupname(args, fail_if_match=True)
597 nickname = self.get_nickname('', fail_if_match=False, alt_text=True)
599 if nickname.lower() == 'c':
600 print log.red("Canceled")
603 if not nickname.lower():
606 entries.append(nickname)
608 self.db.update_groups(group, entries)
612 do_newgrp = do_addgrp
615 def do_view(self, args):
620 all_entries = self.db.get_all_records()
621 log.debug(all_entries)
623 print log.bold("\nView all Data:\n")
624 if len(all_entries) > 0:
627 f.header = ("Name", "Fax", "Notes", "Member of Group(s)")
629 for name, e in all_entries.items():
630 if not name.startswith('__'):
631 f.add((name, e['fax'], e['notes'], ', '.join(e['groups'])))
639 def do_show(self, args):
641 Show a name (all details).
645 name = self.get_nickname(args, fail_if_match=False)
648 e = self.db.get(name)
651 f.header = ("Key", "Value")
652 f.add(("Name:", name))
653 #f.add(("Title:", e['title']))
654 #f.add(("First Name:", e['firstname']))
655 #f.add(("Last Name:", e['lastname']))
656 f.add(("Fax Number:", e['fax']))
657 f.add(("Notes:", e['notes']))
658 f.add(("Member of Group(s):", ', '.join(e['groups'])))
663 log.error("Name not found. Use the 'names' command to view all names.")
669 def do_rm(self, args):
675 nickname = self.get_nickname(args, fail_if_match=False)
676 if not nickname: return
678 self.db.delete(nickname)
684 def do_rmgrp(self, args):
690 group = self.get_groupname(args, fail_if_match=False)
693 self.db.delete_group(group)
700 def do_about(self, args):
702 utils.log_title(__title__, __version__)
704 def do_import(self, args):
707 import <filename> [type]
708 [type] = vcf|ldif|auto
710 args = args.strip().split()
713 log.error("You must specify a filename to import from.")
719 typ = args[1].lower()
723 if typ not in ('auto', 'ldif', 'vcf', 'vcard'):
724 log.error("Invalid type: %s" % typ)
727 if not os.path.exists(filename):
728 log.error("File %s not found." % filename)
732 ext = os.path.splitext(filename)[1].lower()
738 head = file(filename, 'r').read(1024).lower()
739 if 'begin:vcard' in head:
745 print "Importing from LDIF file %s..." % filename
746 ok, error_str = self.db.import_ldif(filename)
748 elif typ in ('vcard', 'vcf'):
749 print "Importing from VCF file %s..." % filename
750 ok, error_str = self.db.import_vcard(filename)
762 mod = module.Module(__mod__, __title__, __version__, __doc__, None,
763 (GUI_MODE, INTERACTIVE_MODE),
764 (UI_TOOLKIT_QT3, UI_TOOLKIT_QT4))
766 mod.setUsage(module.USAGE_FLAG_NONE)
768 opts, device_uri, printer_name, mode, ui_toolkit, loc = \
769 mod.parseStdOpts(handle_device_printer=False)
771 if ui_toolkit == 'qt3':
772 if not utils.canEnterGUIMode():
773 log.error("%s GUI mode requires GUI support (try running with --qt4). Entering interactive mode." % __mod__)
774 mode = INTERACTIVE_MODE
776 if not utils.canEnterGUIMode4():
777 log.error("%s GUI mode requires GUI support (try running with --qt3). Entering interactive mode." % __mod__)
778 mode = INTERACTIVE_MODE
782 if ui_toolkit == 'qt3':
783 log.set_module("hp-fab(qt3)")
786 from ui.faxaddrbookform import FaxAddrBookForm
788 log.error("Unable to load Qt3 support. Is it installed?")
793 # create the main application object
794 app = QApplication(sys.argv)
797 loc = user_conf.get('ui', 'loc', 'system')
798 if loc.lower() == 'system':
799 loc = str(QTextCodec.locale())
800 log.debug("Using system locale: %s" % loc)
802 if loc.lower() != 'c':
805 l, x = loc.split('.')
806 loc = '.'.join([l, e])
809 loc = '.'.join([loc, e])
811 log.debug("Trying to load .qm file for %s locale." % loc)
812 trans = QTranslator(None)
814 qm_file = 'hplip_%s.qm' % l
815 log.debug("Name of .qm file: %s" % qm_file)
816 loaded = trans.load(qm_file, prop.localization_dir)
819 app.installTranslator(trans)
824 log.debug("Using default 'C' locale")
826 log.debug("Using locale: %s" % loc)
827 QLocale.setDefault(QLocale(loc))
830 locale.setlocale(locale.LC_ALL, locale.normalize(loc))
834 addrbook = FaxAddrBookForm()
836 app.setMainWidget(addrbook)
839 log.debug("Starting GUI loop...")
841 except KeyboardInterrupt:
848 from PyQt4.QtGui import QApplication
849 from ui4.fabwindow import FABWindow
851 log.error("Unable to load Qt4 support. Is it installed?")
854 log.set_module("hp-fab(qt4)")
857 app = QApplication(sys.argv)
859 fab = FABWindow(None)
863 log.debug("Starting GUI loop...")
865 except KeyboardInterrupt:
870 else: # INTERACTIVE_MODE
874 # This can fail on Python < 2.3 due to the datetime module
875 log.error("Fax address book disabled - Python 2.3+ required.")
882 except KeyboardInterrupt:
883 log.error("User exit.")