diff --git a/data/interfaces/default/js/tables/newsletter_logs.js b/data/interfaces/default/js/tables/newsletter_logs.js index 166fc22e..6375bec3 100644 --- a/data/interfaces/default/js/tables/newsletter_logs.js +++ b/data/interfaces/default/js/tables/newsletter_logs.js @@ -48,7 +48,7 @@ newsletter_log_table_options = { $(td).html(cellData); } }, - "width": "10%", + "width": "5%", "className": "no-wrap" }, { @@ -67,43 +67,53 @@ newsletter_log_table_options = { "data": "subject_text", "createdCell": function (td, cellData, rowData, row, col) { if (cellData !== '') { - $(td).html('' + cellData + ''); + $(td).html(cellData); } }, - "width": "38%" + "width": "25%" }, { "targets": [5], + "data": "body_text", + "createdCell": function (td, cellData, rowData, row, col) { + if (cellData !== '') { + $(td).html(cellData); + } + }, + "width": "33%" + }, + { + "targets": [6], "data": "start_date", "createdCell": function (td, cellData, rowData, row, col) { if (cellData !== '') { $(td).html(cellData); } }, - "width": "10%" + "width": "5%" }, { - "targets": [6], + "targets": [7], "data": "end_date", "createdCell": function (td, cellData, rowData, row, col) { if (cellData !== '') { $(td).html(cellData); } }, - "width": "10%" - }, - { - "targets": [7], - "data": "uuid", - "createdCell": function (td, cellData, rowData, row, col) { - if (cellData !== '') { - $(td).html(cellData); - } - }, - "width": "10%" + "width": "5%" }, { "targets": [8], + "data": "uuid", + "createdCell": function (td, cellData, rowData, row, col) { + if (cellData !== '') { + $(td).html('' + cellData + ''); + } + }, + "width": "5%" + }, + { + "targets": [9], "data": "success", "createdCell": function (td, cellData, rowData, row, col) { if (cellData === 1) { diff --git a/data/interfaces/default/logs.html b/data/interfaces/default/logs.html index 38d88ab5..22a5df0c 100644 --- a/data/interfaces/default/logs.html +++ b/data/interfaces/default/logs.html @@ -152,6 +152,7 @@ Agent Action Subject Text + Body Text Start Date End Date UUID diff --git a/data/interfaces/default/newsletter_config.html b/data/interfaces/default/newsletter_config.html index c4f3aad3..0d2f783a 100644 --- a/data/interfaces/default/newsletter_config.html +++ b/data/interfaces/default/newsletter_config.html @@ -176,10 +176,10 @@

Use an existing Email notification agent or enter a new configuration below.

- +
- +

@@ -188,6 +188,19 @@ Note: You may include {server_name}, {start_date}, and {end_date} as parameters. The global date format under Settings > General will be used.

+
+ +
+
+ +
+
+

+ Optional: Enter a body line for the email. Leave blank for default. +
+ Note: You may include {server_name}, {start_date}, and {end_date} as parameters. The global date format under Settings > General will be used. +

+
% for item in newsletter['email_config_options']: diff --git a/plexpy/__init__.py b/plexpy/__init__.py index 4d0a491d..907521b2 100644 --- a/plexpy/__init__.py +++ b/plexpy/__init__.py @@ -638,14 +638,15 @@ def dbcheck(): 'CREATE TABLE IF NOT EXISTS newsletters (id INTEGER PRIMARY KEY AUTOINCREMENT, ' 'agent_id INTEGER, agent_name TEXT, agent_label TEXT, ' 'friendly_name TEXT, newsletter_config TEXT, email_config TEXT, ' - 'cron TEXT NOT NULL DEFAULT "0 0 * * 0", active INTEGER DEFAULT 0)' + 'subject TEXT, body TEXT, cron TEXT NOT NULL DEFAULT "0 0 * * 0", active INTEGER DEFAULT 0)' ) # newsletter_log table :: This is a table which logs newsletters sent c_db.execute( 'CREATE TABLE IF NOT EXISTS newsletter_log (id INTEGER PRIMARY KEY AUTOINCREMENT, timestamp INTEGER, ' 'newsletter_id INTEGER, agent_id INTEGER, agent_name TEXT, notify_action TEXT, ' - 'subject_text TEXT, start_date TEXT, end_date TEXT, uuid TEXT UNIQUE, success INTEGER DEFAULT 0)' + 'subject_text TEXT, body_text TEXT, start_date TEXT, end_date TEXT, ' + 'uuid TEXT UNIQUE, success INTEGER DEFAULT 0)' ) # poster_urls table :: This table keeps record of the notification poster urls diff --git a/plexpy/datafactory.py b/plexpy/datafactory.py index b2e56eb4..2608e99d 100644 --- a/plexpy/datafactory.py +++ b/plexpy/datafactory.py @@ -1573,6 +1573,7 @@ class DataFactory(object): 'newsletter_log.agent_name', 'newsletter_log.notify_action', 'newsletter_log.subject_text', + 'newsletter_log.body_text', 'newsletter_log.start_date', 'newsletter_log.end_date', 'newsletter_log.uuid', @@ -1606,6 +1607,7 @@ class DataFactory(object): 'agent_name': item['agent_name'], 'notify_action': item['notify_action'], 'subject_text': item['subject_text'], + 'body_text': item['body_text'], 'start_date': item['start_date'], 'end_date': item['end_date'], 'uuid': item['uuid'], diff --git a/plexpy/newsletter_handler.py b/plexpy/newsletter_handler.py index 1164db87..8f48cc98 100644 --- a/plexpy/newsletter_handler.py +++ b/plexpy/newsletter_handler.py @@ -65,19 +65,24 @@ def notify(newsletter_id=None, notify_action=None, **kwargs): if not newsletter_config: return + if notify_action in ('test', 'api'): + subject = kwargs.pop('subject', newsletter_config['subject']) + body = kwargs.pop('body', newsletter_config['subject']) + else: + subject = newsletter_config['subject'] + body = newsletter_config['body'] + newsletter_agent = newsletters.get_agent_class(agent_id=newsletter_config['agent_id'], config=newsletter_config['config'], - email_config=newsletter_config['email_config']) - - if notify_action in ('test', 'api'): - subject_string = kwargs.pop('subject', None) - if subject_string: - newsletter_agent.subject = newsletter_agent.format_subject(subject_string) + email_config=newsletter_config['email_config'], + subject=subject, + body=body) # Set the newsletter state in the db newsletter_log_id = set_notify_state(newsletter=newsletter_config, notify_action=notify_action, - subject=newsletter_agent.subject, + subject=newsletter_agent.subject_formatted, + body=newsletter_agent.body_formatted, start_date=newsletter_agent.start_date.format('YYYY-MM-DD'), end_date=newsletter_agent.end_date.format('YYYY-MM-DD'), newsletter_uuid=newsletter_agent.uuid) @@ -90,7 +95,7 @@ def notify(newsletter_id=None, notify_action=None, **kwargs): return True -def set_notify_state(newsletter, notify_action, subject, start_date, end_date, newsletter_uuid): +def set_notify_state(newsletter, notify_action, subject, body, start_date, end_date, newsletter_uuid): if newsletter and notify_action: db = database.MonitorDatabase() @@ -103,6 +108,7 @@ def set_notify_state(newsletter, notify_action, subject, start_date, end_date, n 'agent_name': newsletter['agent_name'], 'notify_action': notify_action, 'subject_text': subject, + 'body_text': body, 'start_date': start_date, 'end_date': end_date} @@ -149,3 +155,4 @@ def get_newsletter(newsletter_uuid): return "Newsletter no longer exists" else: return "Newsletter does not exist" + diff --git a/plexpy/newsletters.py b/plexpy/newsletters.py index e61e016d..d8daac2a 100644 --- a/plexpy/newsletters.py +++ b/plexpy/newsletters.py @@ -29,7 +29,7 @@ import logger import newsletter_handler import notification_handler import pmsconnect -from notification_handler import get_poster_info +from notification_handler import get_poster_info, CustomFormatter from notifiers import send_notification, EMAIL @@ -55,6 +55,7 @@ def available_notification_actions(): 'name': 'on_cron', 'description': 'Trigger a notification on a certain schedule.', 'subject': 'Tautulli Newsletter', + 'body': 'Tautulli Newsletter', 'icon': 'fa-calendar', 'media_types': ('newsletter',) } @@ -63,14 +64,17 @@ def available_notification_actions(): return actions -def get_agent_class(agent_id=None, config=None, email_config=None, start_date=None, end_date=None): +def get_agent_class(agent_id=None, config=None, email_config=None, start_date=None, end_date=None, + subject=None, body=None): if str(agent_id).isdigit(): agent_id = int(agent_id) kwargs = {'config': config, 'email_config': email_config, 'start_date': start_date, - 'end_date': end_date} + 'end_date': end_date, + 'subject': subject, + 'body': body} if agent_id == 0: return RecentlyAdded(**kwargs) @@ -131,13 +135,18 @@ def get_newsletter_config(newsletter_id=None): try: config = json.loads(result.pop('newsletter_config', '{}')) email_config = json.loads(result.pop('email_config', '{}')) - newsletter_agent = get_agent_class(agent_id=result['agent_id'], config=config, email_config=email_config) + subject = result.pop('subject') + body = result.pop('body') + newsletter_agent = get_agent_class(agent_id=result['agent_id'], config=config, email_config=email_config, + subject=subject, body=body) newsletter_config = newsletter_agent.return_config_options() newsletter_email_config = newsletter_agent.return_email_config_options() except Exception as e: logger.error(u"Tautulli Newsletters :: Failed to get newsletter config options: %s." % e) return + result['subject'] = newsletter_agent.subject + result['body'] = newsletter_agent.body result['config'] = config result['email_config'] = email_config result['config_options'] = newsletter_config @@ -169,7 +178,9 @@ def add_newsletter_config(agent_id=None, **kwargs): 'agent_label': agent['label'], 'friendly_name': '', 'newsletter_config': json.dumps(agent_class.config), - 'email_config': json.dumps(agent_class.email_config) + 'email_config': json.dumps(agent_class.email_config), + 'subject': agent_class.subject, + 'body': agent_class.body } db = database.MonitorDatabase() @@ -207,7 +218,11 @@ def set_newsletter_config(newsletter_id=None, agent_id=None, **kwargs): email_config = {k[len(email_config_prefix):]: kwargs.pop(k) for k in kwargs.keys() if k.startswith(email_config_prefix)} - agent_class = get_agent_class(agent_id=agent['id'], config=newsletter_config, email_config=email_config) + subject = kwargs.pop('subject') + body = kwargs.pop('body') + + agent_class = get_agent_class(agent_id=agent['id'], config=newsletter_config, email_config=email_config, + subject=subject, body=body) keys = {'id': newsletter_id} values = {'agent_id': agent['id'], @@ -216,6 +231,8 @@ def set_newsletter_config(newsletter_id=None, agent_id=None, **kwargs): 'friendly_name': kwargs.get('friendly_name', ''), 'newsletter_config': json.dumps(agent_class.config), 'email_config': json.dumps(agent_class.email_config), + 'subject': agent_class.subject, + 'body': agent_class.body, 'cron': kwargs.get('cron'), 'active': kwargs.get('active') } @@ -232,16 +249,15 @@ def set_newsletter_config(newsletter_id=None, agent_id=None, **kwargs): return False -def send_newsletter(newsletter_id=None, subject=None, notify_action='', newsletter_log_id=None, **kwargs): +def send_newsletter(newsletter_id=None, subject=None, body=None, newsletter_log_id=None, **kwargs): newsletter_config = get_newsletter_config(newsletter_id=newsletter_id) if newsletter_config: agent = get_agent_class(agent_id=newsletter_config['agent_id'], config=newsletter_config['config'], - email_config=newsletter_config['email_config']) - return agent.send(subject=subject, - action=notify_action.split('on_')[-1], - newsletter_log_id=newsletter_log_id, - **kwargs) + email_config=newsletter_config['email_config'], + subject=subject, + body=body) + return agent.send() else: logger.debug(u"Tautulli Newsletters :: Notification requested but no newsletter_id received.") @@ -279,15 +295,16 @@ class Newsletter(object): _DEFAULT_EMAIL_CONFIG = EMAIL().return_default_config() _DEFAULT_EMAIL_CONFIG['from_name'] = 'Tautulli Newsletter' _DEFAULT_EMAIL_CONFIG['notifier'] = 0 - _DEFAULT_EMAIL_CONFIG['subject'] = 'Tautulli Newsletter' + _DEFAULT_SUBJECT = 'Tautulli Newsletter' + _DEFAULT_BODY = 'Tautulli Newsletter' _TEMPLATE_MASTER = '' _TEMPLATE = '' - def __init__(self, config=None, email_config=None, start_date=None, end_date=None): + def __init__(self, config=None, email_config=None, start_date=None, end_date=None, subject=None, body=None): self.config = self.set_config(config=config, default=self._DEFAULT_CONFIG) self.email_config = self.set_config(config=email_config, default=self._DEFAULT_EMAIL_CONFIG) - date_format = helpers.momentjs_to_arrow(plexpy.CONFIG.DATE_FORMAT) + self.uuid = generate_newsletter_uuid() self.start_date = None self.end_date = None @@ -313,22 +330,19 @@ class Newsletter(object): self.end_time = self.end_date.timestamp self.start_time = self.start_date.timestamp - self.parameters = { - 'start_date': self.start_date.format(date_format), - 'end_date': self.end_date.format(date_format), - 'server_name': plexpy.CONFIG.PMS_NAME - } - - self.subject = self.format_subject(self.email_config['subject']) - - self.uuid = generate_newsletter_uuid() - - self.is_preview = False + self.parameters = self.build_params() + self.subject = subject or self._DEFAULT_SUBJECT + self.body = body or self._DEFAULT_BODY + self.subject_formatted, self.body_formatted = self.build_text() self.data = {} self.newsletter = None + self.is_preview = False + def set_config(self, config=None, default=None): + self._add_config() + return self._validate_config(config=config, default=default) def _validate_config(self, config=None, default=None): @@ -344,6 +358,9 @@ class Newsletter(object): return new_config + def _add_config(self): + pass + def retrieve_data(self): pass @@ -372,7 +389,7 @@ class Newsletter(object): return serve_template( templatename=template, - title=self.subject, + title=self.subject_formatted, parameters=self.parameters, data=self.data, preview=self.is_preview @@ -412,32 +429,62 @@ class Newsletter(object): if self.email_config['notifier']: return send_notification( notifier_id=self.email_config['notifier'], - subject=self.subject, + subject=self.subject_formatted, body=self.newsletter ) else: email = EMAIL(config=self.email_config) return email.notify( - subject=self.subject, + subject=self.subject_formatted, body=self.newsletter ) - def format_subject(self, subject=None): - subject = subject or self._DEFAULT_EMAIL_CONFIG['subject'] + def build_params(self): + parameters = self._build_params() + + return parameters + + def _build_params(self): + date_format = helpers.momentjs_to_arrow(plexpy.CONFIG.DATE_FORMAT) + + parameters = { + 'server_name': plexpy.CONFIG.PMS_NAME, + 'start_date': self.start_date.format(date_format), + 'end_date': self.end_date.format(date_format), + 'newsletter_days': self.config['last_days'], + 'newsletter_url': 'http://localhost:8181/dev'.rstrip('/') + '/newsletter/' + self.uuid, + 'newsletter_uuid': self.uuid + } + + return parameters + + def build_text(self): + custom_formatter = CustomFormatter() try: - subject = unicode(subject).format(**self.parameters) + subject = custom_formatter.format(unicode(self.subject), **self.parameters) except LookupError as e: logger.error( u"Tautulli Newsletter :: Unable to parse parameter %s in newsletter subject. Using fallback." % e) - subject = unicode(self._DEFAULT_EMAIL_CONFIG['subject']).format(**self.parameters) + subject = unicode(self._DEFAULT_SUBJECT).format(**self.parameters) except Exception as e: logger.error( u"Tautulli Newsletter :: Unable to parse custom newsletter subject: %s. Using fallback." % e) - subject = unicode(self._DEFAULT_EMAIL_CONFIG['subject']).format(**self.parameters) + subject = unicode(self._DEFAULT_SUBJECT).format(**self.parameters) - return subject + try: + body = custom_formatter.format(unicode(self.body), **self.parameters) + except LookupError as e: + logger.error( + u"Tautulli Newsletter :: Unable to parse parameter %s in newsletter body. Using fallback." % e) + body = unicode(self._DEFAULT_BODY).format(**self.parameters) + except Exception as e: + logger.error( + u"Tautulli Newsletter :: Unable to parse custom newsletter body: %s. Using fallback." % e) + body = unicode(self._DEFAULT_BODY).format(**self.parameters) + + return subject, body def return_config_options(self): config_options = [] @@ -455,23 +502,22 @@ class RecentlyAdded(Newsletter): _DEFAULT_CONFIG = {'last_days': 7, 'incl_libraries': None } + _DEFAULT_SUBJECT = 'Recently Added to {server_name}! ({end_date})' + _DEFAULT_BODY = 'View the newsletter here: {newsletter_url}' _TEMPLATE_MASTER = 'recently_added_master.html' _TEMPLATE = 'recently_added.html' - def __init__(self, config=None, email_config=None, start_date=None, end_date=None): + def __init__(self, config=None, email_config=None, start_date=None, end_date=None, subject=None, body=None): super(RecentlyAdded, self).__init__(config=config, email_config=email_config, - start_date=start_date, end_date=end_date) + start_date=start_date, end_date=end_date, + subject=subject, body=body) + def _add_config(self): if self.config['incl_libraries'] is None: self.config['incl_libraries'] = [] elif not isinstance(self.config['incl_libraries'], list): self.config['incl_libraries'] = [self.config['incl_libraries']] - self._DEFAULT_EMAIL_CONFIG['subject'] = 'Recently Added to Plex ({server_name})! ({end_date})' - - self.parameters['pms_identifier'] = plexpy.CONFIG.PMS_IDENTIFIER - self.parameters['pms_web_url'] = plexpy.CONFIG.PMS_WEB_URL - def _get_recently_added(self, media_type=None): pms_connect = pmsconnect.PmsConnect() @@ -656,6 +702,20 @@ class RecentlyAdded(Newsletter): sections[library_type] = group return sections + def build_params(self): + parameters = self._build_params() + + newsletter_libraries = [] + for s in self._get_sections(): + if str(s['section_id']) in self.config['incl_libraries']: + newsletter_libraries.append(s['section_name']) + + parameters['newsletter_libraries'] = ', '.join(sorted(newsletter_libraries)) + parameters['pms_identifier'] = plexpy.CONFIG.PMS_IDENTIFIER + parameters['pms_web_url'] = plexpy.CONFIG.PMS_WEB_URL + + return parameters + def return_config_options(self): config_option = [{'label': 'Number of Days', 'value': self.config['last_days'], diff --git a/plexpy/webserve.py b/plexpy/webserve.py index f840beae..63b78895 100644 --- a/plexpy/webserve.py +++ b/plexpy/webserve.py @@ -2495,6 +2495,7 @@ class WebInterface(object): ("agent_name", True, True), ("notify_action", True, True), ("subject_text", True, True), + ("body_text", True, True), ("start_date", True, True), ("end_date", True, True), ("uuid", True, True)] @@ -5482,13 +5483,11 @@ class WebInterface(object): "cron": "0 0 * * 1", "active": 1 "config": {"last_days": 7, - "incl_movies": 1, - "incl_shows": 1, - "incl_artists": 1, + "incl_libraries": [1, 2] }, + "email_config": {...}, "config_options": [{...}, ...], - "email_config_options": [{...}, ...], - "email_notifier": 0 + "email_config_options": [{...}, ...] } ``` """ @@ -5561,7 +5560,7 @@ class WebInterface(object): @cherrypy.expose @requireAuth(member_of("admin")) @addtoapi("notify_newsletter") - def send_newsletter(self, newsletter_id=None, subject='', notify_action='', **kwargs): + def send_newsletter(self, newsletter_id=None, subject='', body='', notify_action='', **kwargs): """ Send a newsletter using Tautulli. ``` @@ -5587,6 +5586,7 @@ class WebInterface(object): if newsletter_handler.notify(newsletter_id=newsletter_id, notify_action=notify_action, subject=subject, + body=body, **kwargs): return "Newsletter sent." else: