epicyon/webapp_calendar.py

477 lines
20 KiB
Python
Raw Normal View History

2020-11-09 20:15:17 +00:00
__filename__ = "webapp_calendar.py"
__author__ = "Bob Mottram"
__license__ = "AGPL3+"
2021-01-26 10:07:42 +00:00
__version__ = "1.2.0"
2020-11-09 20:15:17 +00:00
__maintainer__ = "Bob Mottram"
2021-09-10 16:14:50 +00:00
__email__ = "bob@libreserver.org"
2020-11-09 20:15:17 +00:00
__status__ = "Production"
2021-06-15 15:08:12 +00:00
__module_group__ = "Calendar"
2020-11-09 20:15:17 +00:00
import os
from datetime import datetime
from datetime import date
2021-12-27 21:59:07 +00:00
from utils import get_display_name
2021-12-26 14:08:58 +00:00
from utils import get_config_param
2021-12-27 22:19:18 +00:00
from utils import get_nickname_from_actor
2021-12-27 19:05:25 +00:00
from utils import get_domain_from_actor
2021-12-26 20:36:08 +00:00
from utils import locate_post
2021-12-26 15:13:34 +00:00
from utils import load_json
2021-12-26 20:52:11 +00:00
from utils import week_day_of_month_start
2021-12-26 18:32:02 +00:00
from utils import get_alt_path
2021-12-26 18:17:37 +00:00
from utils import remove_domain_port
2021-12-26 12:02:29 +00:00
from utils import acct_dir
2021-12-26 10:19:59 +00:00
from utils import local_actor_url
2021-12-26 17:21:37 +00:00
from utils import replace_users_with_at
2021-12-29 21:55:09 +00:00
from happening import get_todays_events
from happening import get_calendar_events
from webapp_utils import set_custom_background
from webapp_utils import html_header_with_external_style
from webapp_utils import html_footer
from webapp_utils import html_hide_from_screen_reader
from webapp_utils import html_keyboard_navigation
def html_calendar_delete_confirm(css_cache: {}, translate: {}, base_dir: str,
path: str, http_prefix: str,
2022-01-03 22:00:38 +00:00
domain_full: str, post_id: str,
post_time: str,
year: int, month_number: int,
day_number: int, calling_domain: str) -> str:
2020-11-09 20:15:17 +00:00
"""Shows a screen asking to confirm the deletion of a calendar event
"""
2021-12-27 22:19:18 +00:00
nickname = get_nickname_from_actor(path)
2021-12-26 10:19:59 +00:00
actor = local_actor_url(http_prefix, nickname, domain_full)
2022-01-03 22:00:38 +00:00
domain, _ = get_domain_from_actor(actor)
message_id = actor + '/statuses/' + post_id
2020-11-09 20:15:17 +00:00
2022-01-03 22:00:38 +00:00
post_filename = locate_post(base_dir, nickname, domain, message_id)
2021-12-26 23:41:34 +00:00
if not post_filename:
2020-11-09 20:15:17 +00:00
return None
2021-12-26 23:41:34 +00:00
post_json_object = load_json(post_filename)
2021-12-25 22:09:19 +00:00
if not post_json_object:
2020-11-09 20:15:17 +00:00
return None
2022-01-03 22:00:38 +00:00
delete_post_str = None
2021-12-31 21:18:12 +00:00
css_filename = base_dir + '/epicyon-profile.css'
2021-12-25 16:17:53 +00:00
if os.path.isfile(base_dir + '/epicyon.css'):
2021-12-31 21:18:12 +00:00
css_filename = base_dir + '/epicyon.css'
2020-11-09 20:15:17 +00:00
2022-01-03 22:00:38 +00:00
instance_title = \
2021-12-26 14:08:58 +00:00
get_config_param(base_dir, 'instanceTitle')
2022-01-03 22:00:38 +00:00
delete_post_str = \
html_header_with_external_style(css_filename, instance_title, None)
delete_post_str += \
'<center><h1>' + post_time + ' ' + str(year) + '/' + \
str(month_number) + \
'/' + str(day_number) + '</h1></center>'
delete_post_str += '<center>'
delete_post_str += ' <p class="followText">' + \
2020-11-12 17:05:38 +00:00
translate['Delete this event'] + '</p>'
2022-01-03 22:00:38 +00:00
post_actor = get_alt_path(actor, domain_full, calling_domain)
delete_post_str += \
' <form method="POST" action="' + post_actor + '/rmpost">\n'
delete_post_str += ' <input type="hidden" name="year" value="' + \
2020-11-12 17:05:38 +00:00
str(year) + '">\n'
2022-01-03 22:00:38 +00:00
delete_post_str += ' <input type="hidden" name="month" value="' + \
str(month_number) + '">\n'
delete_post_str += ' <input type="hidden" name="day" value="' + \
str(day_number) + '">\n'
delete_post_str += \
2020-11-12 17:05:38 +00:00
' <input type="hidden" name="pageNumber" value="1">\n'
2022-01-03 22:00:38 +00:00
delete_post_str += \
2020-11-12 17:05:38 +00:00
' <input type="hidden" name="messageId" value="' + \
2022-01-03 22:00:38 +00:00
message_id + '">\n'
delete_post_str += \
2020-11-12 17:05:38 +00:00
' <button type="submit" class="button" name="submitYes">' + \
translate['Yes'] + '</button>\n'
2022-01-03 22:00:38 +00:00
delete_post_str += \
2020-11-12 17:05:38 +00:00
' <a href="' + actor + '/calendar?year=' + \
str(year) + '?month=' + \
2022-01-03 22:00:38 +00:00
str(month_number) + '"><button class="button">' + \
2020-11-12 17:05:38 +00:00
translate['No'] + '</button></a>\n'
2022-01-03 22:00:38 +00:00
delete_post_str += ' </form>\n'
delete_post_str += '</center>\n'
delete_post_str += html_footer()
return delete_post_str
2020-11-09 20:15:17 +00:00
2021-12-29 21:55:09 +00:00
def _html_calendar_day(person_cache: {}, css_cache: {}, translate: {},
base_dir: str, path: str,
2022-01-03 22:00:38 +00:00
year: int, month_number: int, day_number: int,
nickname: str, domain: str, day_events: [],
month_name: str, actor: str) -> str:
2020-11-09 20:15:17 +00:00
"""Show a day within the calendar
"""
2022-01-03 22:00:38 +00:00
account_dir = acct_dir(base_dir, nickname, domain)
calendar_file = account_dir + '/.newCalendar'
if os.path.isfile(calendar_file):
try:
2022-01-03 22:00:38 +00:00
os.remove(calendar_file)
2021-11-25 18:42:38 +00:00
except OSError:
2022-01-03 22:00:38 +00:00
print('EX: _html_calendar_day unable to delete ' + calendar_file)
2020-11-09 20:15:17 +00:00
2021-12-31 21:18:12 +00:00
css_filename = base_dir + '/epicyon-calendar.css'
2021-12-25 16:17:53 +00:00
if os.path.isfile(base_dir + '/calendar.css'):
2021-12-31 21:18:12 +00:00
css_filename = base_dir + '/calendar.css'
2020-11-09 20:15:17 +00:00
2022-01-03 22:00:38 +00:00
cal_actor = actor
2020-11-09 20:15:17 +00:00
if '/users/' in actor:
2022-01-03 22:00:38 +00:00
cal_actor = '/users/' + actor.split('/users/')[1]
instance_title = get_config_param(base_dir, 'instanceTitle')
calendar_str = \
html_header_with_external_style(css_filename, instance_title, None)
calendar_str += '<main><table class="calendar">\n'
calendar_str += '<caption class="calendar__banner--month">\n'
calendar_str += \
' <a href="' + cal_actor + '/calendar?year=' + str(year) + \
'?month=' + str(month_number) + '">\n'
calendar_str += \
' <h1>' + str(day_number) + ' ' + month_name + \
2020-11-09 20:15:17 +00:00
'</h1></a><br><span class="year">' + str(year) + '</span>\n'
2022-01-03 22:00:38 +00:00
calendar_str += '</caption>\n'
calendar_str += '<tbody>\n'
if day_events:
for event_post in day_events:
event_time = None
event_description = None
event_place = None
2021-12-26 19:47:06 +00:00
post_id = None
2022-01-03 22:00:38 +00:00
sender_name = ''
sender_actor = None
event_is_public = False
2020-11-09 20:15:17 +00:00
# get the time place and description
2022-01-03 22:00:38 +00:00
for evnt in event_post:
if evnt['type'] == 'Event':
if evnt.get('post_id'):
post_id = evnt['post_id']
if evnt.get('startTime'):
event_date = \
datetime.strptime(evnt['startTime'],
2020-11-09 20:15:17 +00:00
"%Y-%m-%dT%H:%M:%S%z")
2022-01-03 22:00:38 +00:00
event_time = event_date.strftime("%H:%M").strip()
if 'public' in evnt:
if evnt['public'] is True:
event_is_public = True
if evnt.get('sender'):
2021-03-06 18:20:10 +00:00
# get display name from sending actor
2022-01-03 22:00:38 +00:00
if evnt.get('sender'):
sender_actor = evnt['sender']
disp_name = \
get_display_name(base_dir, sender_actor,
2021-12-27 21:59:07 +00:00
person_cache)
2022-01-03 22:00:38 +00:00
if disp_name:
sender_name = \
'<a href="' + sender_actor + '">' + \
disp_name + '</a>: '
if evnt.get('name'):
event_description = evnt['name'].strip()
elif evnt['type'] == 'Place':
if evnt.get('name'):
event_place = evnt['name']
2020-11-09 20:15:17 +00:00
2021-03-06 18:48:16 +00:00
# prepend a link to the sender of the calendar item
2022-01-03 22:00:38 +00:00
if sender_name and event_description:
2021-03-06 19:41:39 +00:00
# if the sender is also mentioned within the event
# description then this is a reminder
2022-01-03 22:00:38 +00:00
sender_actor2 = replace_users_with_at(sender_actor)
if sender_actor not in event_description and \
sender_actor2 not in event_description:
event_description = sender_name + event_description
2021-03-06 19:04:41 +00:00
else:
2022-01-03 22:00:38 +00:00
event_description = \
translate['Reminder'] + ': ' + event_description
2021-03-06 18:48:16 +00:00
2022-01-03 22:00:38 +00:00
delete_button_str = ''
2021-12-26 19:47:06 +00:00
if post_id:
2022-01-03 22:00:38 +00:00
delete_button_str = \
'<td class="calendar__day__icons"><a href="' + \
cal_actor + \
2021-12-26 19:47:06 +00:00
'/eventdelete?eventid=' + post_id + \
'?year=' + str(year) + \
2022-01-03 22:00:38 +00:00
'?month=' + str(month_number) + \
'?day=' + str(day_number) + \
'?time=' + event_time + \
2020-11-09 20:15:17 +00:00
'">\n<img class="calendardayicon" loading="lazy" alt="' + \
translate['Delete this event'] + ' |" title="' + \
translate['Delete this event'] + '" src="/' + \
2020-12-09 13:08:26 +00:00
'icons/delete.png" /></a></td>\n'
2020-11-09 20:15:17 +00:00
2022-01-03 22:00:38 +00:00
event_class = 'calendar__day__event'
cal_item_class = 'calItem'
if event_is_public:
event_class = 'calendar__day__event__public'
cal_item_class = 'calItemPublic'
if event_time and event_description and event_place:
calendar_str += \
'<tr class="' + cal_item_class + '">' + \
'<td class="calendar__day__time"><b>' + event_time + \
'</b></td><td class="' + event_class + '">' + \
2020-11-09 20:15:17 +00:00
'<span class="place">' + \
2022-01-03 22:00:38 +00:00
event_place + '</span><br>' + event_description + \
'</td>' + delete_button_str + '</tr>\n'
elif event_time and event_description and not event_place:
calendar_str += \
'<tr class="' + cal_item_class + '">' + \
'<td class="calendar__day__time"><b>' + event_time + \
'</b></td><td class="' + event_class + '">' + \
event_description + '</td>' + delete_button_str + '</tr>\n'
elif not event_time and event_description and not event_place:
calendar_str += \
'<tr class="' + cal_item_class + '">' + \
'<td class="calendar__day__time">' + \
2022-01-03 22:00:38 +00:00
'</td><td class="' + event_class + '">' + \
event_description + '</td>' + delete_button_str + '</tr>\n'
elif not event_time and event_description and event_place:
calendar_str += \
'<tr class="' + cal_item_class + '">' + \
'<td class="calendar__day__time"></td>' + \
2022-01-03 22:00:38 +00:00
'<td class="' + event_class + '"><span class="place">' + \
event_place + '</span><br>' + event_description + \
'</td>' + delete_button_str + '</tr>\n'
elif event_time and not event_description and event_place:
calendar_str += \
'<tr class="' + cal_item_class + '">' + \
'<td class="calendar__day__time"><b>' + event_time + \
'</b></td><td class="' + event_class + '">' + \
2020-11-09 20:15:17 +00:00
'<span class="place">' + \
2022-01-03 22:00:38 +00:00
event_place + '</span></td>' + \
delete_button_str + '</tr>\n'
2020-11-09 20:15:17 +00:00
2022-01-03 22:00:38 +00:00
calendar_str += '</tbody>\n'
calendar_str += '</table></main>\n'
calendar_str += html_footer()
2020-11-09 20:15:17 +00:00
2022-01-03 22:00:38 +00:00
return calendar_str
2020-11-09 20:15:17 +00:00
2021-12-29 21:55:09 +00:00
def html_calendar(person_cache: {}, css_cache: {}, translate: {},
base_dir: str, path: str,
http_prefix: str, domain_full: str,
2021-12-31 21:18:12 +00:00
text_mode_banner: str, access_keys: {}) -> str:
2020-11-09 20:15:17 +00:00
"""Show the calendar for a person
"""
2021-12-26 18:17:37 +00:00
domain = remove_domain_port(domain_full)
2020-11-09 20:15:17 +00:00
2022-01-03 22:00:38 +00:00
month_number = 0
day_number = None
2020-11-09 20:15:17 +00:00
year = 1970
2021-12-26 10:00:46 +00:00
actor = http_prefix + '://' + domain_full + path.replace('/calendar', '')
2020-11-09 20:15:17 +00:00
if '?' in actor:
first = True
2022-01-03 22:00:38 +00:00
for part in actor.split('?'):
2020-11-09 20:15:17 +00:00
if not first:
2022-01-03 22:00:38 +00:00
if '=' in part:
if part.split('=')[0] == 'year':
num_str = part.split('=')[1]
if num_str.isdigit():
year = int(num_str)
elif part.split('=')[0] == 'month':
num_str = part.split('=')[1]
if num_str.isdigit():
month_number = int(num_str)
elif part.split('=')[0] == 'day':
num_str = part.split('=')[1]
if num_str.isdigit():
day_number = int(num_str)
2020-11-09 20:15:17 +00:00
first = False
actor = actor.split('?')[0]
2022-01-03 22:00:38 +00:00
curr_date = datetime.now()
if year == 1970 and month_number == 0:
year = curr_date.year
month_number = curr_date.month
2020-11-09 20:15:17 +00:00
2021-12-27 22:19:18 +00:00
nickname = get_nickname_from_actor(actor)
2020-11-09 20:15:17 +00:00
2021-12-29 21:55:09 +00:00
set_custom_background(base_dir, 'calendar-background',
'calendar-background')
2020-11-09 20:15:17 +00:00
2021-11-03 16:44:34 +00:00
months = (
'January', 'February', 'March', 'April', 'May', 'June',
'July', 'August', 'September', 'October', 'November', 'December'
)
2022-01-03 22:00:38 +00:00
month_name = translate[months[month_number - 1]]
2020-11-09 20:15:17 +00:00
2022-01-03 22:00:38 +00:00
if day_number:
day_events = None
2020-11-09 20:15:17 +00:00
events = \
2021-12-29 21:55:09 +00:00
get_todays_events(base_dir, nickname, domain,
2022-01-03 22:00:38 +00:00
year, month_number, day_number)
2020-11-09 20:15:17 +00:00
if events:
2022-01-03 22:00:38 +00:00
if events.get(str(day_number)):
day_events = events[str(day_number)]
2021-12-29 21:55:09 +00:00
return _html_calendar_day(person_cache, css_cache,
translate, base_dir, path,
2022-01-03 22:00:38 +00:00
year, month_number, day_number,
nickname, domain, day_events,
month_name, actor)
2020-11-09 20:15:17 +00:00
events = \
2022-01-03 22:00:38 +00:00
get_calendar_events(base_dir, nickname, domain, year, month_number)
prev_year = year
prev_month_number = month_number - 1
if prev_month_number < 1:
prev_month_number = 12
prev_year = year - 1
next_year = year
next_month_number = month_number + 1
if next_month_number > 12:
next_month_number = 1
next_year = year + 1
print('Calendar year=' + str(year) + ' month=' + str(month_number) +
' ' + str(week_day_of_month_start(month_number, year)))
if month_number < 12:
days_in_month = \
(date(year, month_number + 1, 1) -
date(year, month_number, 1)).days
2020-11-09 20:15:17 +00:00
else:
2022-01-03 22:00:38 +00:00
days_in_month = \
(date(year + 1, 1, 1) - date(year, month_number, 1)).days
# print('days_in_month ' + str(month_number) + ': ' + str(days_in_month))
2020-11-09 20:15:17 +00:00
2021-12-31 21:18:12 +00:00
css_filename = base_dir + '/epicyon-calendar.css'
2021-12-25 16:17:53 +00:00
if os.path.isfile(base_dir + '/calendar.css'):
2021-12-31 21:18:12 +00:00
css_filename = base_dir + '/calendar.css'
2020-11-09 20:15:17 +00:00
2022-01-03 22:00:38 +00:00
cal_actor = actor
2020-11-09 20:15:17 +00:00
if '/users/' in actor:
2022-01-03 22:00:38 +00:00
cal_actor = '/users/' + actor.split('/users/')[1]
2020-11-09 20:15:17 +00:00
2022-01-03 22:00:38 +00:00
instance_title = \
2021-12-26 14:08:58 +00:00
get_config_param(base_dir, 'instanceTitle')
2022-01-03 22:00:38 +00:00
header_str = \
html_header_with_external_style(css_filename, instance_title, None)
2021-02-12 15:13:07 +00:00
# the main graphical calendar as a table
2022-01-03 22:00:38 +00:00
calendar_str = '<main><table class="calendar">\n'
calendar_str += '<caption class="calendar__banner--month">\n'
calendar_str += \
' <a href="' + cal_actor + '/calendar?year=' + str(prev_year) + \
'?month=' + str(prev_month_number) + '" ' + \
2021-12-31 21:18:12 +00:00
'accesskey="' + access_keys['Page up'] + '">'
2022-01-03 22:00:38 +00:00
calendar_str += \
2020-11-09 20:15:17 +00:00
' <img loading="lazy" alt="' + translate['Previous month'] + \
2020-12-09 13:08:26 +00:00
'" title="' + translate['Previous month'] + '" src="/icons' + \
2020-11-09 20:15:17 +00:00
'/prev.png" class="buttonprev"/></a>\n'
2022-01-03 22:00:38 +00:00
calendar_str += ' <a href="' + cal_actor + '/inbox" title="'
calendar_str += translate['Switch to timeline view'] + '" ' + \
2021-12-31 21:18:12 +00:00
'accesskey="' + access_keys['menuTimeline'] + '">'
2022-01-03 22:00:38 +00:00
calendar_str += ' <h1>' + month_name + '</h1></a>\n'
calendar_str += \
' <a href="' + cal_actor + '/calendar?year=' + str(next_year) + \
'?month=' + str(next_month_number) + '" ' + \
2021-12-31 21:18:12 +00:00
'accesskey="' + access_keys['Page down'] + '">'
2022-01-03 22:00:38 +00:00
calendar_str += \
2020-11-09 20:15:17 +00:00
' <img loading="lazy" alt="' + translate['Next month'] + \
2020-12-09 13:08:26 +00:00
'" title="' + translate['Next month'] + '" src="/icons' + \
2020-11-09 20:15:17 +00:00
'/prev.png" class="buttonnext"/></a>\n'
2022-01-03 22:00:38 +00:00
calendar_str += '</caption>\n'
calendar_str += '<thead>\n'
calendar_str += '<tr>\n'
2021-07-05 19:35:57 +00:00
days = ('Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat')
2022-01-03 22:00:38 +00:00
for day in days:
calendar_str += ' <th scope="col" class="calendar__day__header">' + \
translate[day] + '</th>\n'
calendar_str += '</tr>\n'
calendar_str += '</thead>\n'
calendar_str += '<tbody>\n'
2020-11-09 20:15:17 +00:00
2021-02-12 15:13:07 +00:00
# beginning of the links used for accessibility
2021-12-31 21:18:12 +00:00
nav_links = {}
2022-01-03 22:00:38 +00:00
timeline_link_str = html_hide_from_screen_reader('🏠') + ' ' + \
2021-02-12 15:13:07 +00:00
translate['Switch to timeline view']
2022-01-03 22:00:38 +00:00
nav_links[timeline_link_str] = cal_actor + '/inbox'
2021-02-12 15:13:07 +00:00
2022-01-03 22:00:38 +00:00
day_of_month = 0
dow = week_day_of_month_start(month_number, year)
for week_of_month in range(1, 7):
if day_of_month == days_in_month:
2020-11-09 20:15:17 +00:00
continue
2022-01-03 22:00:38 +00:00
calendar_str += ' <tr>\n'
for day_number in range(1, 8):
if (week_of_month > 1 and day_of_month < days_in_month) or \
(week_of_month == 1 and day_number >= dow):
day_of_month += 1
is_today = False
if year == curr_date.year:
if curr_date.month == month_number:
if day_of_month == curr_date.day:
is_today = True
if events.get(str(day_of_month)):
url = cal_actor + '/calendar?year=' + \
2020-11-09 20:15:17 +00:00
str(year) + '?month=' + \
2022-01-03 22:00:38 +00:00
str(month_number) + '?day=' + str(day_of_month)
day_description = month_name + ' ' + str(day_of_month)
day_link = '<a href="' + url + '" ' + \
'title="' + day_description + '">' + \
str(day_of_month) + '</a>'
2021-02-12 15:13:07 +00:00
# accessibility menu links
2022-01-03 22:00:38 +00:00
menu_option_str = \
2021-12-29 21:55:09 +00:00
html_hide_from_screen_reader('📅') + ' ' + \
2022-01-03 22:00:38 +00:00
day_description
nav_links[menu_option_str] = url
2020-11-09 20:15:17 +00:00
# there are events for this day
2022-01-03 22:00:38 +00:00
if not is_today:
calendar_str += \
2020-11-09 20:15:17 +00:00
' <td class="calendar__day__cell" ' + \
'data-event="">' + \
2022-01-03 22:00:38 +00:00
day_link + '</td>\n'
2020-11-09 20:15:17 +00:00
else:
2022-01-03 22:00:38 +00:00
calendar_str += \
2020-11-09 20:15:17 +00:00
' <td class="calendar__day__cell" ' + \
'data-today-event="">' + \
2022-01-03 22:00:38 +00:00
day_link + '</td>\n'
2020-11-09 20:15:17 +00:00
else:
# No events today
2022-01-03 22:00:38 +00:00
if not is_today:
calendar_str += \
2020-11-09 20:15:17 +00:00
' <td class="calendar__day__cell">' + \
2022-01-03 22:00:38 +00:00
str(day_of_month) + '</td>\n'
2020-11-09 20:15:17 +00:00
else:
2022-01-03 22:00:38 +00:00
calendar_str += \
2020-11-09 20:15:17 +00:00
' <td class="calendar__day__cell" ' + \
2022-01-03 22:00:38 +00:00
'data-today="">' + str(day_of_month) + '</td>\n'
2020-11-09 20:15:17 +00:00
else:
2022-01-03 22:00:38 +00:00
calendar_str += ' <td class="calendar__day__cell"></td>\n'
calendar_str += ' </tr>\n'
2020-11-09 20:15:17 +00:00
2022-01-03 22:00:38 +00:00
calendar_str += '</tbody>\n'
calendar_str += '</table></main>\n'
2021-02-12 15:13:07 +00:00
# end of the links used for accessibility
2022-01-03 22:00:38 +00:00
next_month_str = \
2021-12-29 21:55:09 +00:00
html_hide_from_screen_reader('') + ' ' + translate['Next month']
2022-01-03 22:00:38 +00:00
nav_links[next_month_str] = \
cal_actor + '/calendar?year=' + str(next_year) + \
'?month=' + str(next_month_number)
prev_month_str = \
2021-12-29 21:55:09 +00:00
html_hide_from_screen_reader('') + ' ' + translate['Previous month']
2022-01-03 22:00:38 +00:00
nav_links[prev_month_str] = \
cal_actor + '/calendar?year=' + str(prev_year) + \
'?month=' + str(prev_month_number)
2021-12-31 21:18:12 +00:00
nav_access_keys = {
2021-04-22 11:51:19 +00:00
}
2022-01-03 22:00:38 +00:00
screen_reader_cal = \
2021-12-31 21:18:12 +00:00
html_keyboard_navigation(text_mode_banner, nav_links, nav_access_keys,
2022-01-03 22:00:38 +00:00
month_name)
2021-02-12 15:13:07 +00:00
2022-01-03 22:00:38 +00:00
new_event_str = \
2021-11-03 16:43:41 +00:00
'<br><center>\n<p>\n' + \
2022-01-03 22:00:38 +00:00
'<a href="' + cal_actor + '/newreminder"> ' + \
2021-11-03 16:41:23 +00:00
translate['Add to the calendar'] + '</a>\n</p>\n</center>\n'
2022-01-03 22:00:38 +00:00
cal_str = \
header_str + screen_reader_cal + calendar_str + \
new_event_str + html_footer()
2021-11-03 16:41:23 +00:00
2022-01-03 22:00:38 +00:00
return cal_str