Serge Noiraud fd072528d2
Narrative web: Some improvements (#931)
* Narrative web: Some improvements

- Event type, Date and place in bold
- Family events shifted one column on the left
- ancestortree css file before narrative-screen to allow modification
- Adaptation for all themes

Fixes #11393

* Narrative web: forgot a comma during last merge

* Allow scrolling if the ancestor tree is too large

* Translation of alternate stylesheets name

* Crash when using the family map

* Translate only the css title, not the file name

* Some minor corrections to css files

* Narrative web: open layers optimizations

* Narrative web: open layers and link in popup

* Narrative web: some events missing in popup

* Narrative web: Reference date column too large.

Allow the place title to use the maximum of width

* NarrativeWeb: shift children from one column

- adapt the css files to the new table
- some inconsistencies between the source and the css

* Make the drop down menu button size usable

* NarrativeWeb: Incorrect rendering when use of

alternate place name

* NarWeb: removing the unused image heigth option

* Click on image link gives a not found URL.

If the image used in home, introduction or contact page
is not already associated by a filtered object, we have a 404 error

* NarWeb: Index images and thumbnails pages optional

* Narweb: Improper Notes subtitle in web pages

* Narweb: List index truncated after 999

* Narweb: NarrativeWeb usage enhancements

* Narweb: avoid duplicate files in archive.

* Narweb: Add an optional news and updates page:

When you have a big database and you make intensive updates, it's
useful to have a list of the last modified objects.
you can select the period to show and how many records to see per object type.

* Narweb: forgot to add the module updates.py

* Narweb: some minor changes (pylint, img index bug)

* Popups don't work with the last openlayers version

It only needs to move the scripts at the end of the html body.
Use addEventListener instead of onload in the html body statement.

* Narweb: some popup problems

* Narweb: better score for pylint
2019-12-14 11:55:16 +01:00

484 lines
21 KiB
Python

# -*- coding: utf-8 -*-
#!/usr/bin/env python
#
# Gramps - a GTK+/GNOME based genealogy program
#
# Copyright (C) 2000-2007 Donald N. Allingham
# Copyright (C) 2007 Johan Gonqvist <johan.gronqvist@gmail.com>
# Copyright (C) 2007-2009 Gary Burton <gary.burton@zen.co.uk>
# Copyright (C) 2007-2009 Stephane Charette <stephanecharette@gmail.com>
# Copyright (C) 2008-2009 Brian G. Matherly
# Copyright (C) 2008 Jason M. Simanek <jason@bohemianalps.com>
# Copyright (C) 2008-2011 Rob G. Healey <robhealey1@gmail.com>
# Copyright (C) 2010 Doug Blank <doug.blank@gmail.com>
# Copyright (C) 2010 Jakim Friant
# Copyright (C) 2010- Serge Noiraud
# Copyright (C) 2011 Tim G L Lyons
# Copyright (C) 2013 Benny Malengier
# Copyright (C) 2016 Allen Crider
#
# This program 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 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
"""
Narrative Web Page generator.
Classe:
PlacePage - Place index page and individual Place pages
"""
#------------------------------------------------
# python modules
#------------------------------------------------
from collections import defaultdict
from decimal import getcontext
import logging
#------------------------------------------------
# Gramps module
#------------------------------------------------
from gramps.gen.const import GRAMPS_LOCALE as glocale
from gramps.gen.lib import (PlaceType, Place)
from gramps.gen.plug.report import Bibliography
from gramps.plugins.lib.libhtml import Html
from gramps.gen.utils.place import conv_lat_lon
from gramps.gen.utils.location import get_main_location
#------------------------------------------------
# specific narrative web import
#------------------------------------------------
from gramps.plugins.webreport.basepage import BasePage
from gramps.plugins.webreport.common import (get_first_letters, first_letter,
alphabet_navigation, GOOGLE_MAPS,
primary_difference, _KEYPLACE,
get_index_letter, FULLCLEAR,
MARKER_PATH, OPENLAYER,
OSM_MARKERS, STAMEN_MARKERS,
MARKERS, html_escape,
sort_places)
_ = glocale.translation.sgettext
LOG = logging.getLogger(".NarrativeWeb")
getcontext().prec = 8
######################################################
# #
# Place Pages #
# #
######################################################
class PlacePages(BasePage):
"""
This class is responsible for displaying information about the 'Person'
database objects. It displays this information under the 'Events'
tab. It is told by the 'add_instances' call which 'Person's to display,
and remembers the list of persons. A single call to 'display_pages'
displays both the Event List (Index) page and all the Event
pages.
The base class 'BasePage' is initialised once for each page that is
displayed.
"""
def __init__(self, report):
"""
@param: report -- The instance of the main report class for
this report
"""
BasePage.__init__(self, report, title="")
self.place_dict = defaultdict(set)
self.placemappages = None
self.mapservice = None
self.person = None
self.familymappages = None
self.googlemapkey = None
self.stamenopts = None
# Place needs to display coordinates?
self.display_coordinates = report.options["coordinates"]
def display_pages(self, title):
"""
Generate and output the pages under the Place tab, namely the place
index and the individual place pages.
@param: title -- Is the title of the web page
"""
LOG.debug("obj_dict[Place]")
for item in self.report.obj_dict[Place].items():
LOG.debug(" %s", str(item))
message = _("Creating place pages")
with self.r_user.progress(_("Narrated Web Site Report"), message,
len(self.report.obj_dict[Place]) + 1
) as step:
index = 1
for place_handle in self.report.obj_dict[Place]:
step()
index += 1
self.placepage(self.report, title, place_handle)
step()
self.placelistpage(self.report, title,
self.report.obj_dict[Place].keys())
def placelistpage(self, report, title, place_handles):
"""
Create a place index
@param: report -- The instance of the main report class for
this report
@param: title -- Is the title of the web page
@param: place_handles -- The handle for the place to add
"""
BasePage.__init__(self, report, title)
output_file, sio = self.report.create_file("places")
result = self.write_header(self._("Places"))
placelistpage, dummy_head, dummy_body, outerwrapper = result
ldatec = 0
prev_letter = " "
# begin places division
with Html("div", class_="content", id="Places") as placelist:
outerwrapper += placelist
# place list page message
msg = self._("This page contains an index of all the places in the "
"database, sorted by their title. "
"Clicking on a place&#8217;s "
"title will take you to that place&#8217;s page.")
placelist += Html("p", msg, id="description")
# begin alphabet navigation
index_list = get_first_letters(self.r_db, place_handles,
_KEYPLACE, rlocale=self.rlocale)
alpha_nav = alphabet_navigation(index_list, self.rlocale)
if alpha_nav is not None:
placelist += alpha_nav
# begin places table and table head
with Html("table",
class_="infolist primobjlist placelist") as table:
placelist += table
# begin table head
thead = Html("thead")
table += thead
trow = Html("tr")
thead += trow
if self.display_coordinates:
trow.extend(
Html("th", label, class_=colclass, inline=True)
for (label, colclass) in [
[self._("Letter"), "ColumnLetter"],
[self._("Place Name | Name"), "ColumnName"],
[self._("State/ Province"), "ColumnState"],
[self._("Country"), "ColumnCountry"],
[self._("Latitude"), "ColumnLatitude"],
[self._("Longitude"), "ColumnLongitude"]
]
)
else:
trow.extend(
Html("th", label, class_=colclass, inline=True)
for (label, colclass) in [
[self._("Letter"), "ColumnLetter"],
[self._("Place Name | Name"), "ColumnName"],
[self._("State/ Province"), "ColumnState"],
[self._("Country"), "ColumnCountry"]
]
)
handle_list = sort_places(self.r_db, place_handles,
self.rlocale)
first = True
# begin table body
tbody = Html("tbody")
table += tbody
for (dummy_pname, place_handle) in handle_list:
place = self.r_db.get_place_from_handle(place_handle)
if place:
if place.get_change_time() > ldatec:
ldatec = place.get_change_time()
plc_title = self.report.obj_dict[Place][place_handle][1]
main_location = get_main_location(self.r_db, place)
if plc_title and plc_title != " ":
letter = get_index_letter(first_letter(plc_title),
index_list,
self.rlocale)
else:
letter = '&nbsp;'
trow = Html("tr")
tbody += trow
tcell = Html("td", class_="ColumnLetter", inline=True)
trow += tcell
if first or primary_difference(letter, prev_letter,
self.rlocale):
first = False
prev_letter = letter
trow.attr = 'class = "BeginLetter"'
ttle = self._("Places beginning "
"with letter %s") % letter
tcell += Html("a", letter, name=letter, title=ttle)
else:
tcell += "&nbsp;"
trow += Html("td",
self.place_link(
place.get_handle(),
plc_title, place.get_gramps_id()),
class_="ColumnName")
trow.extend(
Html("td", data or "&nbsp;", class_=colclass,
inline=True)
for (colclass, data) in [
["ColumnState",
main_location.get(PlaceType.STATE, '')],
["ColumnCountry",
main_location.get(PlaceType.COUNTRY, '')]
]
)
if self.display_coordinates:
tcell1 = Html("td", class_="ColumnLatitude",
inline=True)
tcell2 = Html("td", class_="ColumnLongitude",
inline=True)
trow += (tcell1, tcell2)
if place.lat and place.long:
latitude, longitude = conv_lat_lon(place.lat,
place.long,
"DEG")
tcell1 += latitude
tcell2 += longitude
else:
tcell1 += '&nbsp;'
tcell2 += '&nbsp;'
# add clearline for proper styling
# add footer section
footer = self.write_footer(ldatec)
outerwrapper += (FULLCLEAR, footer)
# send page out for processing
# and close the file
self.xhtml_writer(placelistpage, output_file, sio, ldatec)
def placepage(self, report, title, place_handle):
"""
Create a place page
@param: report -- The instance of the main report class for
this report
@param: title -- Is the title of the web page
@param: place_handle -- The handle for the place to add
"""
place = report.database.get_place_from_handle(place_handle)
if not place:
return
BasePage.__init__(self, report, title, place.get_gramps_id())
self.bibli = Bibliography()
place_name = self.report.obj_dict[Place][place_handle][1]
ldatec = place.get_change_time()
output_file, sio = self.report.create_file(place_handle, "plc")
self.uplink = True
self.page_title = place_name
(placepage, head, dummy_body,
outerwrapper) = self.write_header(_("Places"))
self.placemappages = self.report.options['placemappages']
self.mapservice = self.report.options['mapservice']
self.googlemapkey = self.report.options['googlemapkey']
self.stamenopts = self.report.options['stamenopts']
# begin PlaceDetail Division
with Html("div", class_="content", id="PlaceDetail") as placedetail:
outerwrapper += placedetail
if self.create_media:
media_list = place.get_media_list()
thumbnail = self.disp_first_img_as_thumbnail(media_list,
place)
if thumbnail is not None:
placedetail += thumbnail
# add section title
placedetail += Html("h3",
html_escape(place_name),
inline=True)
# begin summaryarea division and places table
with Html("div", id='summaryarea') as summaryarea:
placedetail += summaryarea
with Html("table", class_="infolist place") as table:
summaryarea += table
# list the place fields
self.dump_place(place, table)
# place gallery
if self.create_media:
placegallery = self.disp_add_img_as_gallery(media_list, place)
if placegallery is not None:
placedetail += placegallery
# place notes
notelist = self.display_note_list(place.get_note_list(), Place)
if notelist is not None:
placedetail += notelist
# place urls
urllinks = self.display_url_list(place.get_url_list())
if urllinks is not None:
placedetail += urllinks
# add place map here
# Link to Gramps marker
fname = "/".join(['images', 'marker.png'])
marker_path = self.report.build_url_image("marker.png",
"images", self.uplink)
if self.placemappages:
if place and (place.lat and place.long):
placetitle = place_name
# add narrative-maps CSS...
fname = "/".join(["css", "narrative-maps.css"])
url = self.report.build_url_fname(fname, None, self.uplink)
head += Html("link", href=url, type="text/css",
media="screen", rel="stylesheet")
# add MapService specific javascript code
src_js = GOOGLE_MAPS + "api/js?sensor=false"
if self.mapservice == "Google":
if self.googlemapkey:
src_js += "&key=" + self.googlemapkey
head += Html("script", type="text/javascript",
src=src_js, inline=True)
else: # OpenStreetMap, Stamen...
url = self.secure_mode
url += ("maxcdn.bootstrapcdn.com/bootstrap/3.3.7/"
"css/bootstrap.min.css")
head += Html("link", href=url, type="text/javascript",
rel="stylesheet")
src_js = self.secure_mode
src_js += ("ajax.googleapis.com/ajax/libs/jquery/1.9.1/"
"jquery.min.js")
head += Html("script", type="text/javascript",
src=src_js, inline=True)
src_js = "https://openlayers.org/en/latest/build/ol.js"
head += Html("script", type="text/javascript",
src=src_js, inline=True)
url = "https://openlayers.org/en/latest/css/ol.css"
head += Html("link", href=url, type="text/javascript",
rel="stylesheet")
src_js = self.secure_mode
src_js += ("maxcdn.bootstrapcdn.com/bootstrap/3.3.7/"
"js/bootstrap.min.js")
head += Html("script", type="text/javascript",
src=src_js, inline=True)
# section title
placedetail += Html("h4", self._("Place Map"), inline=True)
# begin map_canvas division
with Html("div", id="map_canvas", inline=True) as canvas:
placedetail += canvas
# add div for popups.
if self.mapservice == "Google":
with Html("div", id="popup", inline=True) as popup:
placedetail += popup
else:
with Html("div", id="popup", class_="ol-popup",
inline=True) as popup:
placedetail += popup
popup += Html("a", href="#", id="popup-closer",
class_="ol-popup-closer")
popup += Html("div", id="popup-content")
with Html("div", id="tooltip", class_="ol-popup",
inline=True) as tooltip:
placedetail += tooltip
tooltip += Html("div", id="tooltip-content")
# source references
srcrefs = self.display_ind_sources(place)
if srcrefs is not None:
placedetail += srcrefs
# References list
ref_list = self.display_bkref_list(Place, place_handle)
if ref_list is not None:
placedetail += ref_list
# Begin inline javascript code because jsc is a
# docstring, it does NOT have to be properly indented
if self.placemappages:
if place and (place.lat and place.long):
latitude, longitude = conv_lat_lon(place.get_latitude(),
place.get_longitude(),
"D.D8")
scripts = Html()
if self.mapservice == "Google":
with Html("script", type="text/javascript",
indent=False) as jsc:
scripts += jsc
# Google adds Latitude/ Longitude to its maps...
plce = placetitle.replace("'", "\\'")
jsc += MARKER_PATH % marker_path
jsc += MARKERS % ([[plce,
latitude,
longitude,
1, ""]],
latitude, longitude,
10)
elif self.mapservice == "OpenStreetMap":
with Html("script", type="text/javascript") as jsc:
scripts += jsc
jsc += MARKER_PATH % marker_path
jsc += OSM_MARKERS % ([[float(longitude),
float(latitude),
placetitle, ""]],
longitude, latitude, 10)
jsc += OPENLAYER
else: # STAMEN
with Html("script", type="text/javascript") as jsc:
scripts += jsc
jsc += MARKER_PATH % marker_path
jsc += STAMEN_MARKERS % ([[float(longitude),
float(latitude),
placetitle, ""]],
self.stamenopts,
longitude, latitude, 10)
jsc += OPENLAYER
placedetail += scripts
# add clearline for proper styling
# add footer section
footer = self.write_footer(ldatec)
outerwrapper += (FULLCLEAR, footer)
# send page out for processing
# and close the file
self.xhtml_writer(placepage, output_file, sio, ldatec)