Template the Datatables JS

Add Links to user screen
Add Public IP list to user screen
Add Watch history to user screen
This commit is contained in:
Tim 2015-06-20 22:27:12 +02:00
parent c4504d8be0
commit 6a026d510d
12 changed files with 536 additions and 335 deletions

View file

@ -25,7 +25,7 @@
<!-- <img src="interfaces/default/images/platforms/roku.png"> platform image --> <!-- <img src="interfaces/default/images/platforms/roku.png"> platform image -->
</div> </div>
<div class="dashboard-activity-metadata-user"> <div class="dashboard-activity-metadata-user">
${a['user']} is ${a['state']} <a href="user?user=${a['user']}">${a['user']}</a> is ${a['state']}
</div> </div>
<div class="dashboard-activity-metadata-title"> <div class="dashboard-activity-metadata-title">
% if a['type'] == 'episode': % if a['type'] == 'episode':

View file

@ -207,4 +207,14 @@ function getPlatformImagePath(platformName) {
} else { } else {
return 'interfaces/default/images/platforms/default.png'; return 'interfaces/default/images/platforms/default.png';
} }
}
function isPrivateIP(ip_address) {
var parts = ip_address.split('.');
if (parts[0] === '10' ||
(parts[0] === '172' && (parseInt(parts[1], 10) >= 16 && parseInt(parts[1], 10) <= 31)) ||
(parts[0] === '192' && parts[1] === '168')) {
return true;
}
return false;
} }

View file

@ -20,7 +20,7 @@ history_table_options = {
"infoFiltered":"(filtered from _MAX_ total entries)", "infoFiltered":"(filtered from _MAX_ total entries)",
"emptyTable": "No data in table", "emptyTable": "No data in table",
}, },
"stateSave": true, "stateSave": false,
"sPaginationType": "bootstrap", "sPaginationType": "bootstrap",
"processing": false, "processing": false,
"serverSide": true, "serverSide": true,
@ -48,7 +48,14 @@ history_table_options = {
}, },
{ {
"targets": [2], "targets": [2],
"data":"user" "data":"user",
"createdCell": function (td, cellData, rowData, row, col) {
if (cellData !== '') {
$(td).html('<a href="user?user=' + cellData + '">' + cellData + '</a>');
} else {
$(td).html(cellData);
}
},
}, },
{ {
"targets": [3], "targets": [3],

View file

@ -0,0 +1,41 @@
$('#log_table').dataTable( {
"processing": false,
"serverSide": true,
"ajax": {
"url": "getLog"
},
"sPaginationType": "bootstrap",
"order": [ 0, 'desc'],
"pageLength": 10,
"stateSave": true,
"language": {
"search":"Search: ",
"lengthMenu":"Show _MENU_ lines per page",
"emptyTable": "No log information available",
"info":"Showing _START_ to _END_ of _TOTAL_ lines",
"infoEmpty":"Showing 0 to 0 of 0 lines",
"infoFiltered":"(filtered from _MAX_ total lines)"},
"columnDefs": [
{
"targets": [0],
"width": "15%"
},
{
"targets": [1],
"width": "10%"
},
{
"targets": [2],
"width": "75%"
}
],
"drawCallback": function (settings) {
// Jump to top of page
$('html,body').scrollTop(0);
$('#ajaxMsg').addClass('success').fadeOut();
},
"preDrawCallback": function(settings) {
$('#ajaxMsg').html("<div class='msg'><span class='ui-icon ui-icon-check'></span>Fetching rows...</div>");
$('#ajaxMsg').addClass('success').fadeIn();
}
});

View file

@ -0,0 +1,105 @@
user_ip_table_options = {
"destroy": true,
"language": {
"search": "Search: ",
"lengthMenu":"Show _MENU_ entries per page",
"info":"Showing _START_ to _END_ of _TOTAL_ results",
"infoEmpty":"Showing 0 to 0 of 0 entries",
"infoFiltered":"(filtered from _MAX_ total entries)",
"emptyTable": "No data in table",
},
"stateSave": false,
"sPaginationType": "bootstrap",
"processing": false,
"serverSide": true,
"pageLength": 10,
"order": [ 0, 'desc'],
"autoWidth": false,
"columnDefs": [
{
"targets": [0],
"data":"last_seen",
"render": function ( data, type, full ) {
return moment(data, "X").fromNow();
},
"searchable": false,
"width": "15%"
},
{
"targets": [1],
"data":"ip_address",
"width": "15%",
"className": "modal-control",
"createdCell": function (td, cellData, rowData, row, col) {
if (isPrivateIP(cellData)) {
$(td).html(cellData);
} else {
$(td).html('<a href="#ip-info-modal" data-toggle="modal"><span data-toggle="ip-tooltip" data-placement="left" title="IP Address Info" id="ip-info"><i class="icon-map-marker icon-white"></i></span>&nbsp' + cellData +'</a>');
}
},
"width": "15%"
},
{
"targets": [2],
"data":"play_count",
"width": "10%"
},
{
"targets": [3],
"data":"platform",
"width": "15%"
},
{
"targets": [4],
"data":"last_watched",
"width": "30%"
}
],
"drawCallback": function (settings) {
// Jump to top of page
// $('html,body').scrollTop(0);
$('#ajaxMsg').addClass('success').fadeOut();
},
"preDrawCallback": function(settings) {
$('#ajaxMsg').html("<div class='msg'><span class='ui-icon ui-icon-check'></span>Fetching rows...</div>");
$('#ajaxMsg').addClass('success').fadeIn();
}
}
$('#user_ip_table').on('mouseenter', 'td.modal-control span', function () {
$(this).tooltip();
});
$('#user_ip_table').on('click', 'td.modal-control', function () {
var tr = $(this).parents('tr');
var row = user_ip_table.row( tr );
var rowData = row.data();
function getUserLocation(ip_address) {
if (isPrivateIP(ip_address)) {
return "n/a"
} else {
$.ajax({
url: 'http://ip-api.com/json/' + ip_address,
cache: true,
async: true,
type: 'GET',
dataType: 'json',
success: function(data) {
$('#ip_address').html(ip_address);
$('#country').html(data.country);
$('#city').html(data.city);
$('#region').html(data.regionName);
$('#timezone').html(data.timezone);
$('#lat').html(data.lat);
$('#lon').html(data.lon);
$('#isp').html(data.isp);
$('#org').html(data.org);
$('#as').html(data.as);
}
});
}
}
getUserLocation(rowData['ip_address']);
});

View file

@ -0,0 +1,77 @@
users_list_table_options = {
"language": {
"search": "Search: ",
"lengthMenu":"Show _MENU_ entries per page",
"info":"Showing _START_ to _END_ of _TOTAL_ active users",
"infoEmpty":"Showing 0 to 0 of 0 entries",
"infoFiltered":"",
"emptyTable": "No data in table",
},
"destroy": true,
"processing": false,
"serverSide": true,
"pageLength": 10,
"order": [ 0, 'asc'],
"ajax": {
"url": "get_user_list"
},
"bLengthChange": true,
"bInfo": true,
"bAutoWidth": true,
"aaSorting": [[ 0, "asc" ]],
"bStateSave": true,
"bSortClasses": true,
"sPaginationType": "bootstrap",
"columnDefs": [
{
"targets": [0],
"data": null,
"createdCell": function (td, cellData, rowData, row, col) {
//if (rowData['user_thumb'] === '') {
$(td).html('<img src="interfaces/default/images/gravatar-default-80x80.png" alt="User Logo"/>');
//} else {
// $(td).html('<img src="' + rowData['user_thumb'] + '" alt="User Logo"/>');
//}
},
"orderable": false,
"className": "users-poster-face",
"width": "40px"
},
{
"targets": [1],
"data": "user",
"createdCell": function (td, cellData, rowData, row, col) {
if (cellData !== '') {
$(td).html('<a href="user?user=' + cellData + '">' + cellData + '</a>');
} else {
$(td).html(cellData);
}
},
},
{
"targets": [2],
"data": "time",
"render": function ( data, type, full ) {
return moment(data, "X").fromNow();
}
},
{
"targets": [3],
"data": "ip_address",
"searchable": false
},
{
"targets": [4],
"data": "plays"
}
],
"drawCallback": function (settings) {
// Jump to top of page
$('html,body').scrollTop(0);
$('#ajaxMsg').addClass('success').fadeOut();
},
"preDrawCallback": function(settings) {
$('#ajaxMsg').html("<div class='msg'><span class='ui-icon ui-icon-check'></span>Fetching rows...</div>");
$('#ajaxMsg').addClass('success').fadeIn();
}
}

View file

@ -24,7 +24,12 @@
<div class="span12"> <div class="span12">
<div class="wellheader-bg"> <div class="wellheader-bg">
<div class="dashboard-wellheader-no-chevron"> <div class="dashboard-wellheader-no-chevron">
<h2><i class="fa fa-book"></i> Logs</h2> <div class="span9"><h2><i class="fa fa-book"></i> Logs</h2></div>
<div class="span3">
<div class="pull-right">
<h5><a id="menu_link_edit" href="clearLogs"><i class="fa fa-trash-o"></i> Clear log</a>
</div>
</div>
</div> </div>
</div> </div>
</div> </div>
@ -68,69 +73,23 @@
<%def name="javascriptIncludes()"> <%def name="javascriptIncludes()">
<script src="interfaces/default/js/jquery.dataTables.min.js"></script> <script src="interfaces/default/js/jquery.dataTables.min.js"></script>
<script src="interfaces/default/js/jquery.dataTables.bootstrap.pagination.integration.js"></script> <script src="interfaces/default/js/jquery.dataTables.bootstrap.pagination.integration.js"></script>
<script src="interfaces/default/js/tables/logs.js"></script>
<script> <script>
$(document).ready(function() { var timer;
function setRefresh()
$('#log_table').dataTable( { {
"processing": false, refreshrate = document.getElementById('refreshrate');
"serverSide": true, if(refreshrate != null)
"ajax": { {
"url": "getLog" if(timer)
}, {
"sPaginationType": "bootstrap", clearInterval(timer);
"order": [ 0, 'desc'],
"pageLength": 10,
"stateSave": true,
"language": {
"search":"Search: ",
"lengthMenu":"Show _MENU_ lines per page",
"emptyTable": "No log information available",
"info":"Showing _START_ to _END_ of _TOTAL_ lines",
"infoEmpty":"Showing 0 to 0 of 0 lines",
"infoFiltered":"(filtered from _MAX_ total lines)"},
"columnDefs": [
{
"targets": [0],
"width": "15%"
},
{
"targets": [1],
"width": "10%"
},
{
"targets": [2],
"width": "75%"
}
],
"drawCallback": function (settings) {
// Jump to top of page
$('html,body').scrollTop(0);
$('#ajaxMsg').addClass('success').fadeOut();
},
"preDrawCallback": function(settings) {
$('#ajaxMsg').html("<div class='msg'><span class='ui-icon ui-icon-check'></span>Fetching rows...</div>");
$('#ajaxMsg').addClass('success').fadeIn();
} }
}); if(refreshrate.value != 0)
}); {
</script> timer = setInterval("$('#log_table').dataTable().fnDraw()",1000*refreshrate.value);
<script> }
var timer; }
function setRefresh() }
{
refreshrate = document.getElementById('refreshrate');
if(refreshrate != null)
{
if(timer)
{
clearInterval(timer);
}
if(refreshrate.value != 0)
{
timer = setInterval("$('#log_table').dataTable().fnDraw()",1000*refreshrate.value);
}
}
}
</script> </script>
</%def> </%def>

View file

@ -7,218 +7,214 @@
<link rel="stylesheet" href="interfaces/default/css/plexwatch-tables.css"> <link rel="stylesheet" href="interfaces/default/css/plexwatch-tables.css">
</%def> </%def>
<%def name="body()"> % if user != None:
<div class="container-fluid"> <%def name="body()">
<div class="row-fluid"> <div class="container-fluid">
<div class="span12"> <div class="row-fluid">
<div class="user-info-wrapper"> <div class="span12">
<div class="user-info-poster-face"> <div class="user-info-wrapper">
<img src="interfaces/default/images/gravatar-default-80x80.png"> <div class="user-info-poster-face">
</div> <img src="interfaces/default/images/gravatar-default-80x80.png">
<div class="user-info-username">
Username
</div>
<div class="user-info-nav">
<ul class="user-info-nav">
<li class="active"><a href="#profile" data-toggle="tab">Profile</a></li>
<li><a id="ip-tab-btn" href="#userAddresses" data-toggle="tab">IP Addresses</a></li>
<li><a href="#userHistory" data-toggle="tab">History</a></li>
</ul>
</div>
</div>
</div>
</div>
</div>
<div class="tab-content">
<div class="tab-pane active" id="profile">
<div class="container-fluid">
<div class="row-fluid">
<div class="span12">
<div class="wellbg">
<div class="wellheader">
<div class="dashboard-wellheader">
<h3>Global Stats</h3>
</div>
</div>
<div id="user-time-stats" class="user-overview-stats-wrapper">
<div id="user-stats-spinner" class="spinner"></div>
</div>
</div> </div>
</div> <div class="user-info-username">
</div> ${user}
</div>
<div class="container-fluid">
<div class="row-fluid">
<div class="span12">
<div class="wellbg">
<div class="wellheader">
<div class="dashboard-wellheader">
<h3>Platform Stats</h3>
</div>
</div>
<div id="user-platform-stats" class="user-platforms">
<div id="user-platform-spinner" class="spinner"></div>
</div>
</div> </div>
</div> <div class="user-info-nav">
</div> <ul class="user-info-nav">
</div> <li class="active"><a href="#profile" data-toggle="tab">Profile</a></li>
<div class="container-fluid"> <li><a id="ip-tab-btn" href="#userAddresses" data-toggle="tab">IP Addresses</a></li>
<div class="row-fluid"> <li><a href="#userHistory" data-toggle="tab">History</a></li>
<div class="span12"> </ul>
<div class="wellbg">
<div class="wellheader">
<div class="dashboard-wellheader">
<h3>Recently watched</h3>
</div>
</div>
<div id="user-recently-watched" class="dashboard-recent-media-row">
<div id="user-watched-spinner" class="spinner"></div>
</div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div class="tab-pane" id="userAddresses"> <div class="tab-content">
<div class="container-fluid"> <div class="tab-pane active" id="profile">
<div class="row-fluid"> <div class="container-fluid">
<div class="span12"> <div class="row-fluid">
<div class="wellbg"> <div class="span12">
<div class="wellheader"> <div class="wellbg">
<div class="dashboard-wellheader"> <div class="wellheader">
<h3>Public IP Addresses for <strong> <div class="dashboard-wellheader">
Username <h3>Global Stats</h3>
</strong></h3> </div>
</div>
<div id="user-time-stats" class="user-overview-stats-wrapper">
<div id="user-stats-spinner" class="spinner"></div>
</div>
</div>
</div>
</div>
</div>
<div class="container-fluid">
<div class="row-fluid">
<div class="span12">
<div class="wellbg">
<div class="wellheader">
<div class="dashboard-wellheader">
<h3>Platform Stats</h3>
</div>
</div>
<div id="user-platform-stats" class="user-platforms">
<div id="user-platform-spinner" class="spinner"></div>
</div>
</div>
</div>
</div>
</div>
<div class="container-fluid">
<div class="row-fluid">
<div class="span12">
<div class="wellbg">
<div class="wellheader">
<div class="dashboard-wellheader">
<h3>Recently watched</h3>
</div>
</div>
<div id="user-recently-watched" class="dashboard-recent-media-row">
<div id="user-watched-spinner" class="spinner"></div>
</div> </div>
</div> </div>
<table id="tableUserIpAddresses" class="display" width="100%">
<thead>
<tr>
<th align="left"><i class="fa fa-sort"></i> Last seen</th>
<th align="left"><i class="fa fa-sort"></i> IP Address</th>
<th align="left"><i class="fa fa-sort"></i> Play Count</th>
<th align="left"><i class="fa fa-sort"></i> Platform (Last Seen)</th>
<th align="left"><i class="fa fa-sort"></i> Location</th>
<th align="left"><i class="fa fa-sort"></i> Location</th>
</tr>
</thead>
</table>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> <div class="tab-pane" id="userAddresses">
<div class="tab-pane" id="userHistory"> <div class="container-fluid">
<div class="container-fluid"> <div class="row-fluid">
<div class="row-fluid"> <div class="span12">
<div class="span12"> <div class="wellbg">
<div class="wellbg"> <div class="wellheader">
<div class="wellheader"> <div class="dashboard-wellheader">
<div class="dashboard-wellheader"> <h3>Public IP Addresses for <strong>
<h3>Watching History for <strong> ${user}
Username </strong></h3>
</strong></h3> </div>
</div> </div>
</div> <table id="user_ip_table" class="display" width="100%">
<table class="display" id="history_table" width="100%"> <thead>
<thead> <tr>
<tr> <th align="left"><i class="fa fa-sort"></i> Last seen</th>
<th align='left' id="id"><i class='fa fa-sort'></i> ID</th> <th align="left"><i class="fa fa-sort"></i> IP Address</th>
<th align='left' id="date"><i class='fa fa-sort'></i> Time</th> <th align="left"><i class="fa fa-sort"></i> Play Count</th>
<th align='left' id="user"><i class='fa fa-sort'></i> User</th> <th align="left"><i class="fa fa-sort"></i> Platform (Last Seen)</th>
<th align='left' id="platform"><i class='fa fa-sort'></i> Platform</th> <th align="left"> Last Watched</th>
<th align='left' id="ip_address"><i class='fa fa-sort'></i> IP Address</th> </tr>
<th align='left' id="title"><i class='fa fa-sort'></i> Title</th> </thead>
<th align='left' id="started"><i class='fa fa-sort'></i> Started</th> </table>
<th align='left' id="paused_counter"><i class='fa fa-sort'></i> Paused</th> <div id="ip-info-modal" class="modal hide fade" tabindex="-1" role="dialog" aria-labelledby="ip-info-modal" aria-hidden="true">
<th align='left' id="stopped"><i class='fa fa-sort'></i> Stopped</th> <div class="modal-header">
<th align='left' id="duration"><i class='fa fa-sort'></i> Duration</th> <button type="button" class="close" data-dismiss="modal" aria-hidden="true"><i class="fa fa-remove"></i></button>
<th align='left' id="percent_complete"> Completed</th> <h3 id="myModalLabel">IP Address: <strong><span id="ip_address"></span></strong></h3>
<th align='left' id="rating_key"> RatingKey</th> </div>
<th align='left' id="xml"></th> <div class="modal-body" id="modal-text">
</tr> <div class="span6">
</thead> <h4>Location Details</h4>
<tbody>
</tbody>
</table>
<div id="info-modal" class="modal hide fade" tabindex="-1" role="dialog" aria-labelledby="info-modal" aria-hidden="true">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true"><i class="fa fa-remove"></i></button>
<h3 id="myModalLabel">Stream Info: <strong><span id="modal-stream-info"></span></strong></h3>
</div>
<div class="modal-body" id="modal-text">
<div class="span4">
<h4>Stream Details</h4>
<ul> <ul>
<h5>Video</h5> <li>Country: <strong><span id="country"></span></strong></li>
<li>Stream Type: <strong><span id="transcode_video_dec"></span></strong></li> <li>City: <strong><span id="city"></span></strong></li>
<li>Video Resolution: <strong><span id="transcode_video_resolution"></span>p</strong></li> <li>Region: <strong><span id="region"></span></strong></li>
<li>Video Codec: <strong><span id="transcode_video_codec"></span></strong></li> <li>Timezone: <strong><span id="timezone"></span></strong></li>
<li>Video Width: <strong><span id="transcode_video_width"></span></strong></li> <li>Latitude: <strong><span id="lat"></span></strong></li>
<li>Video Height: <strong><span id="transcode_video_height"></span></strong></li> <li>Longitude: <strong><span id="lon"></span></strong></li>
</ul> </ul>
</div>
<div class="span6">
<h4>Connection Details</h4>
<ul> <ul>
<h5>Audio</h5> <li>ISP: <strong><span id="isp"></span></strong></li>
<li>Stream Type: <strong><span id="transcode_audio_dec"></span></strong></li> <li>Organization: <strong><span id="org"></span></strong></li>
<li>Audio Codec: <strong><span id="transcode_audio_codec"></span></strong></li> <li>AS: <strong><span id="as"></span></strong></li>
<li>Audio Channels: <strong><span id="transcode_audio_channels"></span></strong></li>
</ul> </ul>
</div>
</div> </div>
<div class="span4"> <div class="modal-footer"></div>
<h4>Media Source Details</h4> </div>
<li>Container: <strong><span id="media_container"></span></strong></li> </div>
<li>Resolution: <strong><span id="media_resolution"></span>p</strong></li> </div>
<li>Bitrate: <strong><span id="media_bitrate"></span> kbps</strong></li> </div>
</div>
</div>
<div class="tab-pane" id="userHistory">
<div class="container-fluid">
<div class="row-fluid">
<div class="span12">
<div class="wellbg">
<div class="wellheader">
<div class="dashboard-wellheader">
<h3>Watch History for <strong>
${user}
</strong></h3>
</div> </div>
<div class="span4"> </div>
<h4>Video Source Details</h4> <table class="display" id="history_table" width="100%">
<ul> <thead>
<li>Width: <strong><span id="video_width"></span></strong></li> <tr>
<li>Height: <strong><span id="video_height"></span></strong></li> <th align='left' id="id"><i class='fa fa-sort'></i> ID</th>
<li>Aspect Ratio: <strong><span id="video_aspect"></span></strong></li> <th align='left' id="date"><i class='fa fa-sort'></i> Time</th>
<li>Video Frame Rate: <strong><span id="video_framerate"></span></strong></li> <th align='left' id="user"><i class='fa fa-sort'></i> User</th>
<li>Video Codec: <strong><span id="video_codec"></span></strong></li> <th align='left' id="platform"><i class='fa fa-sort'></i> Platform</th>
</ul> <th align='left' id="ip_address"><i class='fa fa-sort'></i> IP Address</th>
<ul></ul> <th align='left' id="title"><i class='fa fa-sort'></i> Title</th>
<h4>Audio Source Details</h4> <th align='left' id="started"><i class='fa fa-sort'></i> Started</th>
<ul> <th align='left' id="paused_counter"><i class='fa fa-sort'></i> Paused</th>
<li>Audio Codec: <strong><span id="audio_codec"></span></strong></li> <th align='left' id="stopped"><i class='fa fa-sort'></i> Stopped</th>
<li>Audio Channels: <strong><span id="audio_channels"></span></strong></li> <th align='left' id="duration"><i class='fa fa-sort'></i> Duration</th>
</ul> <th align='left' id="percent_complete"> Completed</th>
</div> <th align='left' id="rating_key"> RatingKey</th>
</div> <th align='left' id="xml"></th>
<div class="modal-footer"> </tr>
</div> </thead>
</div> <tbody>
</tbody>
</table>
<div id="info-modal" class="modal hide fade" tabindex="-1" role="dialog" aria-labelledby="info-modal" aria-hidden="true">
</div>
</div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> <footer></footer>
<footer></footer> </%def>
</%def>
<%def name="javascriptIncludes()"> <%def name="javascriptIncludes()">
<script src="interfaces/default/js/jquery.dataTables.min.js"></script> <script src="interfaces/default/js/jquery.dataTables.min.js"></script>
<script src="interfaces/default/js/jquery.dataTables.bootstrap.pagination.integration.js"></script> <script src="interfaces/default/js/jquery.dataTables.bootstrap.pagination.integration.js"></script>
<script src="interfaces/default/js/moment-with-locale.js"></script> <script src="interfaces/default/js/moment-with-locale.js"></script>
<script src="interfaces/default/js/tables/history_table.js"></script>
<script src="interfaces/default/js/tables/history_table.js"></script> <script src="interfaces/default/js/tables/user_ips.js"></script>
<script> <script>
$(document).ready(function () { $(document).ready(function () {
history_table_options.ajax = { history_table_options.ajax = {
"url": "get_history", "url": "get_history",
"data": function(d) { "data": function(d) {
d.user = "drzoidberg33"; d.user = "${user}";
}
} }
} history_table = $('#history_table').DataTable(history_table_options);
history_table.column(2).visible(false); // Hide the title column
history_table = $('#history_table').DataTable(history_table_options); user_ip_table_options.ajax = {
"url": "get_user_ips",
// Hide the title column "data": function(d) {
// history_table.column(5).visible(false); d.user = "${user}";
}); }
</script> }
</%def> user_ip_table = $('#user_ip_table').DataTable(user_ip_table_options);
});
</script>
</%def>
% else:
<div class="clear"></div>
<div class="container-fluid">
<div class="row-fluid">
<div class="span10 offset1">
<h3>Error retrieving user information. Please see the logs for more details.</h3>
</div>
</div>
</div>
% endif

View file

@ -23,7 +23,7 @@
<div class='row-fluid'> <div class='row-fluid'>
<div class='span12'> <div class='span12'>
<div class='wellbg'> <div class='wellbg'>
<table id="usersTable" class="display" width="100%"> <table id="users_list_table" class="display" width="100%">
<thead> <thead>
<tr> <tr>
<th align="right" id="avatar"></th> <th align="right" id="avatar"></th>
@ -48,79 +48,8 @@
<script src="interfaces/default/js/jquery.dataTables.min.js"></script> <script src="interfaces/default/js/jquery.dataTables.min.js"></script>
<script src="interfaces/default/js/jquery.dataTables.bootstrap.pagination.integration.js"></script> <script src="interfaces/default/js/jquery.dataTables.bootstrap.pagination.integration.js"></script>
<script src="interfaces/default/js/moment-with-locale.js"></script> <script src="interfaces/default/js/moment-with-locale.js"></script>
<script src="interfaces/default/js/tables/users.js"></script>
<script> <script>
$(document).ready(function() { var users_list_table = $('#users_list_table').DataTable(users_list_table_options);
var oTable = $('#usersTable').dataTable({ </script>
"language": {
"search": "Search: ",
"lengthMenu":"Show _MENU_ entries per page",
"info":"Showing _START_ to _END_ of _TOTAL_ active users",
"infoEmpty":"Showing 0 to 0 of 0 entries",
"infoFiltered":"",
"emptyTable": "No data in table",
},
"destroy": true,
"processing": false,
"serverSide": true,
"pageLength": 10,
"order": [ 0, 'asc'],
"ajax": {
"url": "get_user_list"
},
"bLengthChange": true,
"bInfo": true,
"bAutoWidth": true,
"aaSorting": [[ 0, "asc" ]],
"bStateSave": true,
"bSortClasses": true,
"sPaginationType": "bootstrap",
"columnDefs": [
{
"targets": [0],
"data": null,
"createdCell": function (td, cellData, rowData, row, col) {
//if (rowData['user_thumb'] === '') {
$(td).html('<img src="interfaces/default/images/gravatar-default-80x80.png" alt="User Logo"/>');
//} else {
// $(td).html('<img src="' + rowData['user_thumb'] + '" alt="User Logo"/>');
//}
},
"orderable": false,
"className": "users-poster-face",
"width": "40px"
},
{
"targets": [1],
"data": "user"
},
{
"targets": [2],
"data": "time",
"render": function ( data, type, full ) {
return moment(data, "X").fromNow();
}
},
{
"targets": [3],
"data": "ip_address",
"searchable": false
},
{
"targets": [4],
"data": "plays"
}
],
"drawCallback": function (settings) {
// Jump to top of page
$('html,body').scrollTop(0);
$('#ajaxMsg').addClass('success').fadeOut();
},
"preDrawCallback": function(settings) {
$('#ajaxMsg').html("<div class='msg'><span class='ui-icon ui-icon-check'></span>Fetching rows...</div>");
$('#ajaxMsg').addClass('success').fadeIn();
}
});
});
</script>
</%def> </%def>

View file

@ -75,14 +75,14 @@ class DataTables(object):
% (column_data['column_string'], table_name, where, % (column_data['column_string'], table_name, where,
order, custom_where) order, custom_where)
# logger.debug(u"Query string: %s" % query)
filtered = self.ssp_db.select(query) filtered = self.ssp_db.select(query)
if search_value == '': if search_value == '':
totalcount = len(filtered) totalcount = len(filtered)
else: else:
totalcount = self.ssp_db.select('SELECT COUNT(*) from %s' % table_name)[0][0] totalcount = self.ssp_db.select('SELECT COUNT(*) from %s' % table_name)[0][0]
# logger.debug(u"Query string: %s" % query)
result = filtered[start:(start + length)] result = filtered[start:(start + length)]
output = {'result': result, output = {'result': result,
'filteredCount': len(filtered), 'filteredCount': len(filtered),

View file

@ -14,8 +14,8 @@
# along with PlexPy. If not, see <http://www.gnu.org/licenses/>. # along with PlexPy. If not, see <http://www.gnu.org/licenses/>.
from plexpy import logger, helpers, request, datatables, config, db from plexpy import logger, helpers, request, datatables, config, db
from xml.dom import minidom from xml.dom import minidom
import plexpy import plexpy
import json import json
@ -104,6 +104,70 @@ class PlexWatch(object):
return dict return dict
def get_user_unique_ips(self, start='', length='', kwargs=None, custom_where=''):
data_tables = datatables.DataTables()
start = int(start)
length = int(length)
filtered = []
totalcount = 0
search_value = ""
search_regex = ""
order_column = 0
order_dir = "desc"
if 'order[0][dir]' in kwargs:
order_dir = kwargs.get('order[0][dir]', "desc")
if 'order[0][column]' in kwargs:
order_column = kwargs.get('order[0][column]', 1)
if 'search[value]' in kwargs:
search_value = kwargs.get('search[value]', "")
if 'search[regex]' in kwargs:
search_regex = kwargs.get('search[regex]', "")
columns = ['time as last_seen',
'ip_address',
'COUNT(ip_address) as play_count',
'platform',
'user',
'orig_title as last_watched'
]
query = data_tables.ssp_query(table_name=self.get_user_table_name(),
columns=columns,
start=start,
length=length,
order_column=int(order_column),
order_dir=order_dir,
search_value=search_value,
search_regex=search_regex,
custom_where=custom_where,
group_by='ip_address',
kwargs=kwargs)
results = query['result']
rows = []
for item in results:
row = {"last_seen": item['last_seen'],
"ip_address": item['ip_address'],
"play_count": item['play_count'],
"platform": item['platform'],
"last_watched": item['last_watched']
}
rows.append(row)
dict = {'recordsFiltered': query['filteredCount'],
'recordsTotal': query['totalCount'],
'data': rows,
}
return dict
def get_history(self, start='', length='', kwargs=None, custom_where=''): def get_history(self, start='', length='', kwargs=None, custom_where=''):
data_tables = datatables.DataTables() data_tables = datatables.DataTables()

View file

@ -92,11 +92,11 @@ class WebInterface(object):
return serve_template(templatename="users.html", title="Users") return serve_template(templatename="users.html", title="Users")
@cherrypy.expose @cherrypy.expose
def user(self): def user(self, user=None):
return serve_template(templatename="user.html", title="User") return serve_template(templatename="user.html", title="User", user=user)
@cherrypy.expose @cherrypy.expose
def get_stream_data(self, row_id=None, user='', **kwargs): def get_stream_data(self, row_id=None, user=None, **kwargs):
plex_watch = plexwatch.PlexWatch() plex_watch = plexwatch.PlexWatch()
stream_data = plex_watch.get_stream_details(row_id) stream_data = plex_watch.get_stream_details(row_id)
@ -580,4 +580,17 @@ class WebInterface(object):
cherrypy.response.headers['Content-type'] = 'application/json' cherrypy.response.headers['Content-type'] = 'application/json'
return result return result
else: else:
logger.warn('Unable to retrieve data.') logger.warn('Unable to retrieve data.')
@cherrypy.expose
def get_user_ips(self, start=0, length=100, custom_where='', **kwargs):
if 'user' in kwargs:
user = kwargs.get('user', "")
custom_where = 'user = "%s"' % user
plex_watch = plexwatch.PlexWatch()
history = plex_watch.get_user_unique_ips(start, length, kwargs, custom_where)
cherrypy.response.headers['Content-type'] = 'application/json'
return json.dumps(history)