# # Gramps - a GTK+/GNOME based genealogy program # # Copyright (C) 2000-2007 Donald N. Allingham # Copyright (C) 2007 Johan Gonqvist # Copyright (C) 2007 Gary Burton # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Pubilc License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # $Id$ """ Narrative Web Page generator. """ #------------------------------------------------------------------------ # # python modules # #------------------------------------------------------------------------ import cgi import os import md5 import time import locale import shutil import codecs import tarfile import operator from gettext import gettext as _ from cStringIO import StringIO from textwrap import TextWrapper from unicodedata import normalize #------------------------------------------------------------------------ # # Set up logging # #------------------------------------------------------------------------ import logging log = logging.getLogger(".WebPage") #------------------------------------------------------------------------ # # GRAMPS module # #------------------------------------------------------------------------ import gen.lib import const from GrampsCfg import get_researcher import Sort from PluginUtils import (register_report, FilterOption, EnumeratedListOption, PersonOption, BooleanOption, NumberOption, StringOption, DestinationOption, NoteOption, MediaOption) from ReportBase import (Report, ReportUtils, MenuReportOptions, CATEGORY_WEB, MODE_GUI, MODE_CLI, Bibliography) import Utils import ThumbNails import ImgManip import Mime from QuestionDialog import ErrorDialog, WarningDialog from BasicUtils import name_displayer as _nd from DateHandler import displayer as _dd from DateHandler import parser as _dp from gen.proxy import PrivateProxyDb, LivingProxyDb from gen.lib.eventroletype import EventRoleType #------------------------------------------------------------------------ # # constants # #------------------------------------------------------------------------ _NARRATIVE = "narrative.css" _NARRATIVEPRINT = "narrative-print.css" _NAME_COL = 3 _MAX_IMG_WIDTH = 800 # resize images that are wider than this _MAX_IMG_HEIGHT = 600 # resize images that are taller than this _WIDTH = 160 _HEIGHT = 50 _VGAP = 10 _HGAP = 30 _SHADOW = 5 _XOFFSET = 5 #This information defines the list of styles in the Narrative Web options dialog as well as the location of the corresponding SCREEN stylesheets. _CSS_FILES = [ [_("Basic - Ash"), 'NWeb-Screen_Basic-Ash.css'], [_("Basic - Cypress"), 'NWeb-Screen_Basic-Cypress.css'], [_("Basic - Lilac"), 'NWeb-Screen_Basic-Lilac.css'], [_("Basic - Peach"), 'NWeb-Screen_Basic-Peach.css'], [_("Basic - Spruce"), 'NWeb-Screen_Basic-Spruce.css'], [_("Mainz"), 'NWeb-Screen_Mainz.css'], [_("Nebraska"), 'NWeb-Screen_Nebraska.css'], [_("No style sheet"), ''], ] _CHARACTER_SETS = [ [_('Unicode (recommended)'), 'utf-8'], ['ISO-8859-1', 'iso-8859-1' ], ['ISO-8859-2', 'iso-8859-2' ], ['ISO-8859-3', 'iso-8859-3' ], ['ISO-8859-4', 'iso-8859-4' ], ['ISO-8859-5', 'iso-8859-5' ], ['ISO-8859-6', 'iso-8859-6' ], ['ISO-8859-7', 'iso-8859-7' ], ['ISO-8859-8', 'iso-8859-8' ], ['ISO-8859-9', 'iso-8859-9' ], ['ISO-8859-10', 'iso-8859-10' ], ['ISO-8859-13', 'iso-8859-13' ], ['ISO-8859-14', 'iso-8859-14' ], ['ISO-8859-15', 'iso-8859-15' ], ['koi8_r', 'koi8_r', ], ] _CC = [ '' 'Creative Commons License - By attribution', '' 'Creative Commons License - By attribution, No derivations', '' 'Creative Commons License - By attribution, Share-alike', '' 'Creative Commons License - By attribution, Non-commercial', '' 'Creative Commons License - By attribution, Non-commercial, No '
    'derivations', '' 'Creative Commons License - By attribution, Non-commerical, '
    'Share-alike' ] _COPY_OPTIONS = [ _('Standard copyright'), _('Creative Commons - By attribution'), _('Creative Commons - By attribution, No derivations'), _('Creative Commons - By attribution, Share-alike'), _('Creative Commons - By attribution, Non-commercial'), _('Creative Commons - By attribution, Non-commercial, No derivations'), _('Creative Commons - By attribution, Non-commercial, Share-alike'), _('No copyright notice'), ] wrapper = TextWrapper() wrapper.break_log_words = True wrapper.width = 20 #This list of characters defines which hexadecimal entity certain 'special characters' with be transformed into for valid HTML rendering. #The variety of quotes with spaces are to assist in appropriately typesetting curly quotes and apostrophes. html_escape_table = { "&": "&", ' "': " “", '" ': "” ", " '": " ‘", "' ": "’ ", "'s ": "’s ", '"': """, "'": "'", ">": ">", "<": "<", } #This command then defines the 'html_escape' option for escaping special characters for presentation in HTML based on the above list. def html_escape(text): """Produce entities within text.""" L=[] for c in text: L.append(html_escape_table.get(c,c)) return "".join(L) class BasePage: def __init__(self, title, options, archive, photo_list, gid): self.title_str = title self.gid = gid self.inc_download = options['incdownload'] self.html_dir = options['target'] self.copyright = options['cright'] self.options = options self.archive = archive self.ext = options['ext'] self.encoding = options['encoding'] self.css = options['css'] self.noid = options['nogid'] self.linkhome = options['linkhome'] self.showbirth = options['showbirth'] self.showdeath = options['showdeath'] self.showspouse = options['showspouse'] self.showparents = options['showparents'] self.showhalfsiblings = options['showhalfsiblings'] self.use_intro = options['intronote'] != u""\ or options['introimg'] != u"" self.use_contact = options['contactnote'] != u""\ or options['contactimg'] != u"" self.use_gallery = options['gallery'] self.header = options['headernote'] self.footer = options['footernote'] self.photo_list = photo_list self.usegraph = options['graph'] self.graphgens = options['graphgens'] self.use_home = self.options['homenote'] != "" or \ self.options['homeimg'] != "" self.page_title = "" self.warn_dir = True def store_file(self,archive, html_dir,from_path,to_path): if archive: archive.add(str(from_path),str(to_path)) else: dest = os.path.join(html_dir,to_path) dirname = os.path.dirname(dest) if not os.path.isdir(dirname): os.makedirs(dirname) if from_path != dest: shutil.copyfile(from_path,dest) elif self.warn_dir: WarningDialog( _("Possible destination error") + "\n" + _("You appear to have set your target directory " "to a directory used for data storage. This " "could create problems with file management. " "It is recommended that you consider using " "a different directory to store your generated " "web pages.")) self.warn_dir = False def copy_media(self,photo,store_ref=True): handle = photo.get_handle() if store_ref: lnk = (self.cur_name,self.page_title,self.gid) if self.photo_list.has_key(handle): if lnk not in self.photo_list[handle]: self.photo_list[handle].append(lnk) else: self.photo_list[handle] = [lnk] ext = os.path.splitext(photo.get_path())[1] real_path = "%s/%s" % (self.build_path(handle,'images'), handle+ext) thumb_path = "%s/%s.png" % (self.build_path(handle,'thumb'), handle) return (real_path,thumb_path) def create_file(self, name): self.cur_name = self.build_name("", name) if self.archive: self.string_io = StringIO() of = codecs.EncodedFile(self.string_io,'utf-8',self.encoding, 'xmlcharrefreplace') else: page_name = os.path.join(self.html_dir,self.cur_name) of = codecs.EncodedFile(open(page_name, "w"),'utf-8', self.encoding,'xmlcharrefreplace') return of def link_path(self, name,path): path = "%s/%s/%s" % (path, name[0].lower(), name[1].lower()) path = self.build_name(path, name) return path def create_link_file(self, name,path): self.cur_name = self.link_path(name,path) if self.archive: self.string_io = StringIO() of = codecs.EncodedFile(self.string_io,'utf-8', self.encoding,'xmlcharrefreplace') else: dirname = os.path.join(self.html_dir, path, name[0].lower(), name[1].lower()) if not os.path.isdir(dirname): os.makedirs(dirname) page_name = self.build_name(dirname, name) of = codecs.EncodedFile(open(page_name, "w"),'utf-8', self.encoding,'xmlcharrefreplace') return of def close_file(self, of): if self.archive: tarinfo = tarfile.TarInfo(self.cur_name) tarinfo.size = len(self.string_io.getvalue()) tarinfo.mtime = time.time() if os.sys.platform != "win32": tarinfo.uid = os.getuid() tarinfo.gid = os.getgid() self.string_io.seek(0) self.archive.addfile(tarinfo,self.string_io) of.close() else: of.close() def lnkfmt(self,text): return md5.new(text).hexdigest() def display_footer(self, of,db): of.write('\n\n') of.write('\n\n') of.write('\n') of.write('') def display_header(self, of,db,title,author="",up=False): self.up = up if up: path = "../../.." else: path = "" self.author = author of.write('\n') of.write('\n\n\n' % (xmllang,xmllang)) of.write('%s - %s\n' % (html_escape(self.title_str), html_escape(title))) of.write('\n' % self.encoding) # Link to narrative.css if path: of.write('\n') # Link to narrativePrint.css if path: of.write('\n') # Link to favicon.ico if path: of.write('\n' % path) else: of.write('\n') of.write('\n' % ('$','$')) of.write('\n\n') of.write('\n') of.write('\n\n') of.write('\n\n') # Give unique ID to 'content' div for styling specific sections separately # Because of how this script was originally written, the appropriate section ID is determined by looking for a directory or HTML file name to associate with that section if "index" in self.cur_name: of.write('
\n') elif "introduction" in self.cur_name: of.write('
\n') elif "surnames" in self.cur_name: of.write('
\n') elif "srn" in self.cur_name: of.write('
\n') elif "individuals" in self.cur_name: of.write('
\n') elif "ppl" in self.cur_name: of.write('
\n') elif "sources" in self.cur_name: of.write('
\n') elif "src" in self.cur_name: of.write('
\n') elif "places" in self.cur_name: of.write('
\n') elif "plc" in self.cur_name: of.write('
\n') elif "gallery" in self.cur_name: of.write('