Add selectize for email input

This commit is contained in:
JonnyWong16 2018-01-10 00:29:35 -08:00
commit 5089ede207
4 changed files with 153 additions and 22 deletions

View file

@ -79,6 +79,13 @@ select.form-control {
padding: 1px 2px;
transition: background-color .3s;
}
.selectize-control.form-control .selectize-input {
display: flex;
align-items: center;
flex-wrap: wrap;
margin-bottom: 4px;
padding-left: 5px;
}
.react-selectize.root-node .react-selectize-control .react-selectize-placeholder {
color: #fff !important;
}
@ -95,7 +102,7 @@ select.form-control {
.react-selectize.root-node .simple-value span {
padding-bottom: 2px !important;
}
.react-selectize.root-node .react-selectize-control .react-selectize-search-field-and-selected-values .resizable-input{
.react-selectize.root-node .react-selectize-control .react-selectize-search-field-and-selected-values .resizable-input {
padding-top: 3px !important;
padding-bottom: 3px !important;
}
@ -110,7 +117,7 @@ select.form-control:focus,
}
.react-selectize.root-node.open .simple-value,
.selectize-control.multi .selectize-input.focus > div,
.selectize-control.multi .selectize-input > div.active{
.selectize-control.multi .selectize-input > div.active {
background: #efefef !important;
color: #333333 !important;
transition: background-color .3s;
@ -118,6 +125,28 @@ select.form-control:focus,
.react-selectize.root-node.open .react-selectize-control .react-selectize-toggle-button path {
fill: #999 !important;
}
.selectize-control .selectize-input > div .email {
opacity: 0.8;
font-size: 12px;
}
.selectize-control .selectize-input > div .user + .email {
margin-left: 5px;
}
.selectize-control .selectize-input > div .email:before {
content: '<';
opacity: 0.8;
font-size: 12px;
}
.selectize-control .selectize-input > div .email:after {
content: '>';
opacity: 0.8;
font-size: 12px;
}
.selectize-control .selectize-dropdown .caption {
font-size: 12px;
display: block;
color: #a0a0a0;
}
select.form-control option {
color: #555;
background-color: #fff;

View file

@ -1,6 +1,10 @@
<%!
from plexpy import helpers, notifiers
import json
from plexpy import helpers, notifiers, users
available_notification_actions = notifiers.available_notification_actions()
user_emails = [{'user': u['friendly_name'] or u['username'], 'email': u['email']} for u in users.Users().get_users() if u['email']]
sorted(user_emails, key=lambda u: u['user'])
%>
% if notifier:
<link href="${http_root}css/selectize.bootstrap3.css" rel="stylesheet" />
@ -39,7 +43,7 @@
<div class="form-group">
<label for="${item['name']}">${item['label']}</label>
<div class="row">
<div class="col-md-8">
<div class="col-md-12">
<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 ''}>
% if item['name'] == 'osx_notify_app':
<a href="javascript:void(0)" id="osxnotifyregister">Register</a>
@ -62,7 +66,7 @@
<div class="form-group">
<label for="${item['name']}">${item['label']}</label>
<div class="row">
<div class="col-md-8">
<div class="col-md-12">
<input type="button" class="btn btn-bright" id="${item['name']}" name="${item['name']}" value="${item['value']}">
</div>
</div>
@ -80,7 +84,7 @@
<div class="form-group">
<label for="${item['name']}">${item['label']}</label>
<div class="row">
<div class="col-md-8">
<div class="col-md-12">
<select class="form-control" id="${item['name']}" name="${item['name']}">
% for key, value in sorted(item['select_options'].iteritems()):
% if key == item['value']:
@ -101,7 +105,7 @@
<div class="form-group">
<label for="friendly_name">Description</label>
<div class="row">
<div class="col-md-8">
<div class="col-md-12">
<input type="text" class="form-control" id="friendly_name" name="friendly_name" value="${notifier['friendly_name']}" size="30">
</div>
</div>
@ -185,7 +189,7 @@
</div>
<div class="form-group">
<div class="row">
<div class="col-md-8">
<div class="col-md-12">
<input type="button" class="btn btn-bright notifier-text-preview" data-action="${action['name']}" value="Preview Arguments">
</div>
</div>
@ -212,7 +216,7 @@
</div>
<div class="form-group">
<div class="row">
<div class="col-md-8">
<div class="col-md-12">
<input type="button" class="btn btn-bright notifier-text-preview" data-action="${action['name']}" value="Preview Text">
</div>
</div>
@ -278,7 +282,7 @@
% endif
<div class="form-group">
<div class="row">
<div class="col-md-8">
<div class="col-md-12">
<input type="button" class="btn btn-bright" id="test_notifier" name="test_notifier" value="Test ${notifier['agent_label']}">
</div>
</div>
@ -465,6 +469,70 @@
var osx_notify_app = $('#osx_notify_app').val();
$.get('osxnotifyregister', { 'app': osx_notify_app }, function (data) { showMsg('<i class="fa fa-check"></i> ' + data, false, true, 3000); });
})
% elif notifier['agent_name'] == 'email':
var REGEX_EMAIL = '([a-z0-9!#$%&\'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&\'*+/=?^_`{|}~-]+)*@' +
'(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?)';
var $email_selectors = $('#email_to, #email_cc, #email_bcc').selectize({
plugins: ['remove_button'],
persist: false,
maxItems: null,
valueField: 'email',
labelField: 'user',
searchField: ['user', 'email'],
options: ${json.dumps(user_emails) | n},
render: {
item: function(item, escape) {
return '<div>' +
(item.user ? '<span class="user">' + escape(item.user) + '</span>' : '') +
(item.email ? '<span class="email">' + escape(item.email) + '</span>' : '') +
'</div>';
},
option: function(item, escape) {
var label = item.user || item.email;
var caption = item.user ? item.email : null;
return '<div>' +
escape(label) +
(caption ? '<span class="caption">' + escape(caption) + '</span>' : '') +
'</div>';
}
},
createFilter: function(input) {
var match, regex;
// email@address.com
regex = new RegExp('^' + REGEX_EMAIL + '$', 'i');
match = input.match(regex);
if (match) return !this.options.hasOwnProperty(match[0]);
// user <email@address.com>
regex = new RegExp('^([^<]*)\<' + REGEX_EMAIL + '\>$', 'i');
match = input.match(regex);
if (match) return !this.options.hasOwnProperty(match[2]);
return false;
},
create: function(input) {
if ((new RegExp('^' + REGEX_EMAIL + '$', 'i')).test(input)) {
return {email: input};
}
var match = input.match(new RegExp('^([^<]*)\<' + REGEX_EMAIL + '\>$', 'i'));
if (match) {
return {
email : match[2],
user : $.trim(match[1])
};
}
alert('Invalid email address.');
return false;
}
});
var email_to = $email_selectors[0].selectize;
var email_cc = $email_selectors[1].selectize;
var email_bcc = $email_selectors[2].selectize;
email_to.setValue(${json.dumps(next((c['value'] for c in notifier['config_options'] if c['name'] == 'email_to'), [])) | n});
email_cc.setValue(${json.dumps(next((c['value'] for c in notifier['config_options'] if c['name'] == 'email_cc'), [])) | n});
email_bcc.setValue(${json.dumps(next((c['value'] for c in notifier['config_options'] if c['name'] == 'email_bcc'), [])) | n});
% endif
function validateLogic() {

View file

@ -60,6 +60,7 @@ import logger
import mobile_app
import pmsconnect
import request
import users
from plexpy.config import _BLACKLIST_KEYS, _WHITELIST_KEYS
@ -1245,6 +1246,16 @@ class EMAIL(Notifier):
'html_support': 1
}
def __init__(self, config=None):
super(EMAIL, self).__init__(config=config)
if not isinstance(self.config['to'], list):
self.config['to'] = [x.strip() for x in self.config['to'].split(';')]
if not isinstance(self.config['cc'], list):
self.config['cc'] = [x.strip() for x in self.config['cc'].split(';')]
if not isinstance(self.config['bcc'], list):
self.config['bcc'] = [x.strip() for x in self.config['bcc'].split(';')]
def notify(self, subject='', body='', action='', **kwargs):
if not subject or not body:
return
@ -1259,13 +1270,10 @@ class EMAIL(Notifier):
msg['Subject'] = subject
msg['From'] = email.utils.formataddr((self.config['from_name'], self.config['from']))
msg['To'] = self.config['to']
msg['CC'] = self.config['cc']
msg['To'] = ','.join(self.config['to'])
msg['CC'] = ','.join(self.config['cc'])
recipients = [x.strip() for x in self.config['to'].split(';')] \
+ [x.strip() for x in self.config['cc'].split(';')] \
+ [x.strip() for x in self.config['bcc'].split(';')]
recipients = filter(None, recipients)
recipients = self.config['to'] + self.config['cc'] + self.config['bcc']
try:
mailserver = smtplib.SMTP(self.config['smtp_server'], self.config['smtp_port'])
@ -1289,6 +1297,8 @@ class EMAIL(Notifier):
return False
def return_config_options(self):
user_emails = {} # User selection set with selectize options
config_option = [{'label': 'From Name',
'value': self.config['from_name'],
'name': 'email_from_name',
@ -1304,20 +1314,23 @@ class EMAIL(Notifier):
{'label': 'To',
'value': self.config['to'],
'name': 'email_to',
'description': 'The email address(es) of the recipients, separated by semicolons (;).',
'input_type': 'text'
'description': 'The email address(es) of the recipients.',
'input_type': 'select',
'select_options': user_emails
},
{'label': 'CC',
'value': self.config['cc'],
'name': 'email_cc',
'description': 'The email address(es) to CC, separated by semicolons (;).',
'input_type': 'text'
'description': 'The email address(es) to CC.',
'input_type': 'select',
'select_options': user_emails
},
{'label': 'BCC',
'value': self.config['bcc'],
'name': 'email_bcc',
'description': 'The email address(es) to BCC, separated by semicolons (;).',
'input_type': 'text'
'description': 'The email address(es) to BCC.',
'input_type': 'select',
'select_options': user_emails
},
{'label': 'SMTP Server',
'value': self.config['smtp_server'],

View file

@ -580,6 +580,27 @@ class Users(object):
return recently_watched
def get_users(self):
monitor_db = database.MonitorDatabase()
try:
query = 'SELECT user_id, username, friendly_name, email FROM users WHERE deleted_user = 0'
result = monitor_db.select(query=query)
except Exception as e:
logger.warn(u"Tautulli Users :: Unable to execute database query for get_users: %s." % e)
return None
users = []
for item in result:
user = {'user_id': item['user_id'],
'username': item['username'],
'friendly_name': item['friendly_name'],
'email': item['email']
}
users.append(user)
return users
def delete_all_history(self, user_id=None):
monitor_db = database.MonitorDatabase()