Initial newsletter support

This commit is contained in:
JonnyWong16 2018-01-06 22:27:49 -08:00
commit 0f39201774
15 changed files with 2454 additions and 123 deletions

View file

@ -2973,6 +2973,9 @@ a .home-platforms-list-cover-face:hover
.stacked-configs > li.new-notification-agent,
.stacked-configs > li.notification-agent,
.stacked-configs > li.add-notification-agent,
.stacked-configs > li.new-newsletter-agent,
.stacked-configs > li.newsletter-agent,
.stacked-configs > li.add-newsletter-agent,
.stacked-configs > li.mobile-device,
.stacked-configs > li.add-mobile-device {
cursor: pointer;
@ -3657,38 +3660,58 @@ a:hover .overlay-refresh-image:hover {
}
#plexpy-notifiers-table .friendly_name,
#notifier-config-modal span.notifier_id,
#plexpy-newsletters-table .friendly_name,
#newsletter-config-modal span.newsletter_id,
#plexpy-mobile-devices-table .friendly_name,
#mobile-device-config-modal span.notifier_id {
color: #777;
}
#notifier-config-modal .nav-tabs {
#notifier-config-modal .nav-tabs,
#newsletter-config-modal .nav-tabs {
margin-bottom: 10px;
padding-left: 15px;
border-bottom: 1px solid #444;
}
#notifier-config-modal .nav-tabs > li {
#notifier-config-modal .nav-tabs > li,
#newsletter-config-modal .nav-tabs > li {
margin: 0 0 -1px 0;
}
#notifier-config-modal .nav-tabs > li > a {
#notifier-config-modal .nav-tabs > li > a,
#newsletter-config-modal .nav-tabs > li > a {
padding: 5px 10px;
color: #737373;
}
#notifier-config-modal .nav-tabs > li > a:hover {
#notifier-config-modal .nav-tabs > li > a:hover,
#newsletter-config-modal .nav-tabs > li > a:hover {
border-color: #444;
background: #222;
}
#notifier-config-modal .nav-tabs > li.active > a,
#notifier-config-modal .nav-tabs > li.active > a:hover,
#notifier-config-modal .nav-tabs > li.active > a:focus {
#notifier-config-modal .nav-tabs > li.active > a:focus,
#newsletter-config-modal .nav-tabs > li.active > a,
#newsletter-config-modal .nav-tabs > li.active > a:hover,
#newsletter-config-modal .nav-tabs > li.active > a:focus {
color: #fff;
background: #222;
}
#notifier-config-modal .nav-tabs > li.active > a,
#notifier-config-modal .nav-tabs > li.active > a:hover,
#notifier-config-modal .nav-tabs > li.active > a:focus {
#notifier-config-modal .nav-tabs > li.active > a:focus,
#newsletter-config-modal .nav-tabs > li.active > a,
#newsletter-config-modal .nav-tabs > li.active > a:hover,
#newsletter-config-modal .nav-tabs > li.active > a:focus {
border: 1px solid #444;
border-bottom-color: transparent;
}
#newsletter-config-modal #cron-widget select.cron-select {
width: initial;
display: inline;
}
#newsletter-config-modal #cron-widget select.cron-select[name=cron-period] option[value=minute],
#newsletter-config-modal #cron-widget select.cron-select[name=cron-period] option[value=hour] {
display: none;
}
.git-group input.form-control {
width: 50%;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,306 @@
<%!
from plexpy import helpers
%>
% if newsletter:
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true"><i class="fa fa-remove"></i></button>
<h4 class="modal-title" id="newsletter-config-modal-header">${newsletter['agent_label']} Newsletter Settings &nbsp;<small><span class="newsletter_id">(Newsletter ID: ${newsletter['id']})</span></small></h4>
</div>
<div class="modal-body">
<div class="container-fluid">
<div class="row">
<ul class="nav nav-tabs list-unstyled" role="tablist">
<li role="presentation" class="active"><a href="#tabs-config" aria-controls="tabs-config" role="tab" data-toggle="tab">Configuration</a></li>
<li role="presentation"><a href="#tabs-test_newsletter" aria-controls="tabs-test_newsletter" role="tab" data-toggle="tab">Test Newsletter</a></li>
</ul>
</div>
<form action="set_newsletter_config" method="post" class="form" id="set_newsletter_config" data-parsley-validate>
<div class="tab-content">
<div role="tabpanel" class="tab-pane active" id="tabs-config">
<div class="row">
<div class="col-md-12" style="margin-bottom: 10px; padding-bottom: 0x; border-bottom: 1px solid #444;">
<div class="checkbox" style="margin-bottom: 20px;">
<label>
<input type="checkbox" data-id="active_value" class="checkboxes" value="1" ${helpers.checked(newsletter['active'])}> Enable the newsletter
</label>
<input type="hidden" id="active_value" name="active" value="${newsletter['active']}">
</div>
<div class="form-group">
<label for="cron">Schedule</label>
<div class="row">
<div class="col-md-12">
<div id="cron-widget"></div>
<input type="hidden" id="cron_value" name="cron" />
</div>
</div>
<p class="help-block">Set the schedule for the newsletter</p>
</div>
</div>
<div class="col-md-12">
<input type="hidden" id="newsletter_id" name="newsletter_id" value="${newsletter['id']}" />
<input type="hidden" id="agent_id" name="agent_id" value="${newsletter['agent_id']}" />
% for item in newsletter['config_options']:
% if item['input_type'] == 'help':
<div class="form-group">
<label>${item['label']}</label>
<p class="help-block">${item['description'] | n}</p>
</div>
% elif item['input_type'] == 'text' or item['input_type'] == 'password':
<div class="form-group">
<label for="${item['name']}">${item['label']}</label>
<div class="row">
<div class="col-md-8">
<input type="${item['input_type']}" class="form-control" id="${item['name']}" name="${item['name']}" value="${item['value']}" size="30" ${'readonly' if item.get('readonly') else ''}>
</div>
</div>
<p class="help-block">${item['description'] | n}</p>
</div>
% elif item['input_type'] == 'number':
<div class="form-group">
<label for="${item['name']}">${item['label']}</label>
<div class="row">
<div class="col-md-3">
<input type="${item['input_type']}" class="form-control" id="${item['name']}" name="${item['name']}" value="${item['value']}" size="30">
</div>
</div>
<p class="help-block">${item['description'] | n}</p>
</div>
% elif item['input_type'] == 'button':
<div class="form-group">
<label for="${item['name']}">${item['label']}</label>
<div class="row">
<div class="col-md-8">
<input type="button" class="btn btn-bright" id="${item['name']}" name="${item['name']}" value="${item['value']}">
</div>
</div>
<p class="help-block">${item['description'] | n}</p>
</div>
% elif item['input_type'] == 'checkbox':
<div class="checkbox">
<label>
<input type="checkbox" data-id="${item['name']}" class="checkboxes" value="1" ${helpers.checked(item['value'])}> ${item['label']}
</label>
<p class="help-block">${item['description'] | n}</p>
<input type="hidden" id="${item['name']}" name="${item['name']}" value="${item['value']}">
</div>
% elif item['input_type'] == 'select':
<div class="form-group">
<label for="${item['name']}">${item['label']}</label>
<div class="row">
<div class="col-md-8">
<select class="form-control" id="${item['name']}" name="${item['name']}">
% for key, value in sorted(item['select_options'].iteritems()):
% if key == item['value']:
<option value="${key}" selected>${value}</option>
% else:
<option value="${key}">${value}</option>
% endif
% endfor
</select>
</div>
</div>
<p class="help-block">${item['description'] | n}</p>
</div>
% endif
% endfor
</div>
<div class="col-md-12" style="margin-top: 10px; padding-top: 10px; border-top: 1px solid #444;">
<div class="form-group">
<label for="friendly_name">Description</label>
<div class="row">
<div class="col-md-8">
<input type="text" class="form-control" id="friendly_name" name="friendly_name" value="${newsletter['friendly_name']}" size="30">
</div>
</div>
<p class="help-block">Optional: Enter a description to help identify this newsletter in the newsletters list.</p>
</div>
</div>
</div>
</div>
<div role="tabpanel" class="tab-pane" id="tabs-test_newsletter">
<label>Preview Newsletter</label>
<p class="help-block">
Preview the ${newsletter['agent_label']} newsletter.
</p>
<div class="form-group">
<div class="row">
<div class="col-md-8">
<input type="button" class="btn btn-bright" id="preview_newsletter" name="preview_newsletter" value="Preview ${newsletter['agent_label']} Newsletter">
</div>
</div>
</div>
<label>Test Newsletter</label>
<p class="help-block">
Test if the ${newsletter['agent_label']} newsletter is working. Check the <a href="logs">logs</a> for troubleshooting.
</p>
<div class="form-group">
<div class="row">
<div class="col-md-8">
<input type="button" class="btn btn-bright" id="test_newsletter" name="test_newsletter" value="Test ${newsletter['agent_label']} Newsletter">
</div>
</div>
</div>
</div>
</div>
</form>
</div>
</div>
<div class="modal-footer">
<input type="button" id="delete-newsletter-item" class="btn btn-danger btn-edit" style="float:left;" value="Delete">
<input type="button" id="duplicate-newsletter-item" class="btn btn-dark btn-edit" style="float:left;" value="Duplicate">
<input type="button" id="save-newsletter-item" class="btn btn-bright" value="Save">
</div>
</div>
</div>
<script src="${http_root}js/jquery-cron-min.js"></script>
<script>
$('#newsletter-config-modal').unbind('hidden.bs.modal');
$('#cron-widget').cron({
initial: "${newsletter['cron']}",
classes: "form-control cron-select",
onChange: function() {
$("#cron_value").val($(this).cron("value"));
}
}); // apply cron with default options
function reloadModal() {
$.ajax({
url: 'get_newsletter_config_modal',
data: { newsletter_id: '${newsletter["id"]}' },
cache: false,
async: true,
complete: function (xhr, status) {
$('#newsletter-config-modal').html(xhr.responseText);
}
});
}
function saveCallback(jqXHR) {
if (jqXHR) {
var result = $.parseJSON(jqXHR.responseText);
var msg = result.message;
if (result.result == 'success') {
showMsg('<i class="fa fa-check"></i> ' + msg, false, true, 5000)
} else {
showMsg('<i class="fa fa-times"></i> ' + msg, false, true, 5000, true)
}
}
getNewslettersTable();
}
function deleteCallback() {
$('#newsletter-config-modal').modal('hide');
getNewslettersTable();
}
function duplicateCallback(result) {
// Set new newsletter id
$('#newsletter_id').val(result.newsletter_id);
// Clear friendly name
$('#friendly_name').val("");
saveNewsletter();
$('#newsletter-config-modal').on('hidden.bs.modal', function () {
loadNewsletterConfig(result.newsletter_id);
});
$('#newsletter-config-modal').modal('hide');
}
function saveNewsletter() {
// Trim all text inputs before saving
$('input[type=text]').val(function(_, value) {
return $.trim(value);
});
doAjaxCall('set_newsletter_config', $(this), 'tabs', true, true, saveCallback);
}
$('#delete-newsletter-item').click(function () {
var msg = 'Are you sure you want to delete this <strong>${newsletter["agent_label"]}</strong> newsletter?';
var url = 'delete_newsletter';
confirmAjaxCall(url, msg, { newsletter_id: '${newsletter["id"]}' }, null, deleteCallback);
});
$('#duplicate-newsletter-item').click(function() {
var msg = 'Are you sure you want to duplicate this <strong>${newsletter["agent_label"]}</strong> newsletter?';
var url = 'add_newsletter_config';
confirmAjaxCall(url, msg, { agent_id: '${newsletter["agent_id"]}' }, null, duplicateCallback);
});
$('#save-newsletter-item').click(function () {
saveNewsletter();
});
$('#preview_newsletter').click(function () {
doAjaxCall('set_newsletter_config', $(this), 'tabs', true, false, previewNewsletter);
});
$('#test_newsletter').click(function () {
doAjaxCall('set_newsletter_config', $(this), 'tabs', true, false, sendTestNewsletter);
});
function previewNewsletter() {
window.open('preview_newsletter?newsletter_id=${newsletter["id"]}');
}
function sendTestNewsletter() {
$.ajax({
url: 'send_newsletter',
data: {
newsletter_id: '${newsletter["id"]}',
test: true
},
cache: false,
async: true,
complete: function (xhr, status) {
if (xhr.responseText.indexOf('sent') > -1) {
var msg = '<i class="fa fa-check"></i>&nbsp; ' + xhr.responseText;
showMsg(msg, false, true, 2000);
} else {
var msg = '<i class="fa fa-times"></i>&nbsp; ' + xhr.responseText;
showMsg(msg, false, true, 2000, true);
}
}
});
}
$("${', '.join(['#' + c['name'] for c in newsletter['config_options'] if c.get('refresh')])}").on('change', function () {
// Reload modal to update certain fields
doAjaxCall('set_newsletter_config', $(this), 'tabs', true, false, reloadModal);
return false;
});
// Never send checkbox values directly, always substitute value in hidden input.
$('.checkboxes').click(function () {
var configToggle = $(this).data('id');
if ($(this).is(':checked')) {
$('#'+configToggle).val(1);
} else {
$('#'+configToggle).val(0);
}
});
</script>
% else:
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true"><i class="fa fa-remove"></i></button>
<h4 class="modal-title" id="newsletter-config-modal-header">Error</h4>
</div>
<div class="modal-body" style="text-align: center">
<strong>
<i class="fa fa-exclamation-circle"></i> Failed to retrieve newsletter configuration. Check the <a href="logs">logs</a> for more info.
</strong>
</div>
<div class="modal-footer">
</div>
</div>
</div>
% endif

View file

@ -0,0 +1,42 @@
<%doc>
USAGE DOCUMENTATION :: PLEASE LEAVE THIS AT THE TOP OF THIS FILE
For Mako templating syntax documentation please visit: http://docs.makotemplates.org/en/latest/
Filename: newsletters_table.html
Version: 0.1
DOCUMENTATION :: END
</%doc>
<ul class="stacked-configs list-unstyled">
% for newsletter in sorted(newsletters_list, key=lambda k: (k['agent_label'], k['friendly_name'], k['id'])):
<li class="newsletter-agent" data-id="${newsletter['id']}">
<span>
<span class="toggle-left trigger-tooltip ${'active' if newsletter['active'] else ''}" data-toggle="tooltip" data-placement="top" title="Newsletter ${'active' if newsletter['active'] else 'inactive'}"><i class="fa fa-lg fa-newspaper-o"></i></span>
% if newsletter['friendly_name']:
${newsletter['agent_label']} &nbsp;<span class="friendly_name">(${newsletter['id']} - ${newsletter['friendly_name']})</span>
% else:
${newsletter['agent_label']} &nbsp;<span class="friendly_name">(${newsletter['id']})</span>
% endif
<span class="toggle-right"><i class="fa fa-lg fa-cog"></i></span>
</span>
</li>
% endfor
<li class="add-newsletter-agent" id="add-newsletter-agent" data-target="#add-newsletter-modal" data-toggle="modal">
<span>
<span class="toggle-left"><i class="fa fa-lg fa-newspaper-o"></i></span> Add a new newsletter agent
<span class="toggle-right"><i class="fa fa-lg fa-plus"></i></span>
</span>
</li>
</ul>
<script>
// Load newsletter config modal
$(".newsletter-agent").click(function () {
var newsletter_id = $(this).data('id');
loadNewsletterConfig(newsletter_id);
});
$('.trigger-tooltip').tooltip();
</script>

View file

@ -7,8 +7,6 @@
sorted(user_emails, key=lambda u: u['user'])
%>
% if notifier:
<link href="${http_root}css/selectize.bootstrap3.css" rel="stylesheet" />
<link href="${http_root}css/selectize.min.css" rel="stylesheet" />
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
@ -167,7 +165,7 @@
<a href="#notify-text-sub-modal" data-toggle="modal">Click here</a> for a description of all the parameters.
</p>
<div id="condition-widget"></div>
<input type="hidden" name="custom_conditions" id="custom_conditions" />
<input type="hidden" id="custom_conditions" name="custom_conditions" />
<div class="form-group">
<label for="custom_conditions_logic">Condition Logic</label>
@ -425,7 +423,7 @@
$('#duplicate-notifier-item').click(function() {
var msg = 'Are you sure you want to duplicate this <strong>${notifier["agent_label"]}</strong> notification agent?';
var url = 'add_notifier_config';
confirmAjaxCall(url, msg, { agent_id: "${notifier['agent_id']}" }, null, duplicateCallback);
confirmAjaxCall(url, msg, { agent_id: '${notifier["agent_id"]}' }, null, duplicateCallback);
});
$('#save-notifier-item').click(function () {
@ -767,10 +765,10 @@
async: true,
complete: function (xhr, status) {
if (xhr.responseText.indexOf('sent') > -1) {
msg = '<i class="fa fa-check"></i>&nbsp; ' + xhr.responseText;
var msg = '<i class="fa fa-check"></i>&nbsp; ' + xhr.responseText;
showMsg(msg, false, true, 2000);
} else {
msg = '<i class="fa fa-times"></i>&nbsp; ' + xhr.responseText;
var msg = '<i class="fa fa-times"></i>&nbsp; ' + xhr.responseText;
showMsg(msg, false, true, 2000, true);
}
}

View file

@ -4,10 +4,11 @@
import sys
import plexpy
from plexpy import common, notifiers
from plexpy import common, notifiers, newsletters
from plexpy.helpers import anon_url, checked
available_notification_agents = sorted(notifiers.available_notification_agents(), key=lambda k: k['label'])
available_newsletter_agents = sorted(newsletters.available_newsletter_agents(), key=lambda k: k['label'])
%>
<%def name="headIncludes()">
</%def>
@ -51,6 +52,7 @@
<li role="presentation"><a href="#tabs-plex_media_server" aria-controls="tabs-plex_media_server" role="tab" data-toggle="tab">Plex Media Server</a></li>
<li role="presentation"><a href="#tabs-notifications" aria-controls="tabs-notifications" role="tab" data-toggle="tab">Notifications</a></li>
<li role="presentation"><a href="#tabs-notification_agents" aria-controls="tabs-notification_agents" role="tab" data-toggle="tab">Notification Agents</a></li>
<li role="presentation"><a href="#tabs-newsletter_agents" aria-controls="tabs-newsletter_agents" role="tab" data-toggle="tab">Newsletter Agents</a></li>
<li role="presentation"><a href="#tabs-import_backups" aria-controls="tabs-import_backups" role="tab" data-toggle="tab">Import & Backups</a></li>
<li role="presentation"><a href="#tabs-android_app" aria-controls="tabs-android_app" role="tab" data-toggle="tab">Tautulli Remote Android App <sup><small>beta</small></sup></a></li>
</ul>
@ -977,6 +979,23 @@
</div>
<div role="tabpanel" class="tab-pane" id="tabs-newsletter_agents">
<div class="padded-header">
<h3>Newsletter Agents</h3>
</div>
<p class="help-block">
Add a new newsletter agent, or configure an existing newsletter agent by clicking the settings icon on the right.
</p>
<br />
<div id="plexpy-newsletters-table">
<div class='text-muted'><i class="fa fa-refresh fa-spin"></i> Loading newsletter agents...</div>
<br>
</div>
</div>
<div role="tabpanel" class="tab-pane" id="tabs-import_backups">
<div class="padded-header">
@ -1246,7 +1265,36 @@
</div>
</div>
</div>
<div id="add-newsletter-modal" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="add-newsletter-modal">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true"><i class="fa fa-remove"></i></button>
<h4 class="modal-title">Add a Newsletter Agent</h4>
</div>
<div class="modal-body">
<div class="container-fluid">
<div class="row">
<div class="col-md-12">
<ul class="stacked-configs list-unstyled">
% for agent in available_newsletter_agents:
<li class="new-newsletter-agent" data-id="${agent['id']}">
<span>${agent['label']}</span>
</li>
% endfor
</ul>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<input type="button" class="btn btn-bright" data-dismiss="modal" value="Cancel">
</div>
</div>
</div>
</div>
<div id="notifier-config-modal" class="modal fade wide" tabindex="-1" role="dialog" aria-labelledby="notifier-config-modal"></div>
<div id="newsletter-config-modal" class="modal fade wide" tabindex="-1" role="dialog" aria-labelledby="newsletter-config-modal"></div>
<div id="notify-text-sub-modal" class="modal fade wide" tabindex="-1" role="dialog" aria-labelledby="notify-text-sub-modal">
<div class="modal-dialog" role="document">
<div class="modal-content">
@ -1536,6 +1584,29 @@
});
}
function getNewslettersTable() {
$.ajax({
url: 'get_newsletters_table',
cache: false,
async: true,
complete: function(xhr, status) {
$("#plexpy-newsletters-table").html(xhr.responseText);
}
});
}
function loadNewsletterConfig(newsletter_id) {
$.ajax({
url: 'get_newsletter_config_modal',
data: { newsletter_id: newsletter_id },
cache: false,
async: true,
complete: function (xhr, status) {
$("#newsletter-config-modal").html(xhr.responseText).modal('show');
}
});
}
function getMobileDevicesTable() {
$.ajax({
url: 'get_mobile_devices_table',
@ -1608,6 +1679,7 @@ $(document).ready(function() {
getConfigurationTable();
getSchedulerTable();
getNotifiersTable();
getNewslettersTable();
getMobileDevicesTable();
loadUpdateDistros();
settingsChanged = false;
@ -1691,6 +1763,7 @@ $(document).ready(function() {
getConfigurationTable();
getSchedulerTable();
getNotifiersTable();
getNewslettersTable();
getMobileDevicesTable();
$('#changelog-modal-link').on('click', function (e) {
@ -2304,6 +2377,28 @@ $(document).ready(function() {
});
});
// Add a new newsletter agent
$('.new-newsletter-agent').click(function () {
$.ajax({
url: 'add_newsletter_config',
data: { agent_id: $(this).data('id') },
cache: false,
async: true,
complete: function (xhr, status) {
var result = $.parseJSON(xhr.responseText);
var msg = result.message;
$('#add-newsletter-modal').modal('hide');
if (result.result == 'success') {
showMsg('<i class="fa fa-check"></i> ' + msg, false, true, 5000);
loadNewsletterConfig(result.newsletter_id);
} else {
showMsg('<i class="fa fa-times"></i> ' + msg, false, true, 5000, true);
}
getNewslettersTable();
}
});
});
function apiEnabled() {
var api_enabled = $('#api_enabled').prop('checked');
$('#app_api_msg').toggle(!(api_enabled));