Merge pull request #1141 from ruwany/master

Notification center enhancements
4.x.x
Kamidu Sachith Punchihewa 7 years ago committed by GitHub
commit 48e67df534
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -209,4 +209,32 @@ public interface NotificationManagementService {
defaultValue = "1") defaultValue = "1")
@PathParam("id") @Max(45) @PathParam("id") @Max(45)
int id); int id);
@PUT
@Path("/clear-all")
@ApiOperation(
produces = MediaType.APPLICATION_JSON,
httpMethod = "PUT",
value = "Clear all notifications",
notes = "When a user needs to mark all the notifications as checked/read this " +
"function can be used to clear all notifications",
tags = "Device Notification Management",
extensions = {
@Extension(properties = {
@ExtensionProperty(name = Constants.SCOPE, value = "perm:notifications:mark-checked")
})
}
)
@ApiResponses(
value = {
@ApiResponse(
code = 200,
message = "OK"),
@ApiResponse(
code = 500,
message = "Error occurred while clearing notifications.")
}
)
Response clearAllNotifications();
} }

@ -20,6 +20,7 @@ package org.wso2.carbon.device.mgt.jaxrs.service.impl;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.wso2.carbon.context.CarbonContext;
import org.wso2.carbon.device.mgt.common.PaginationRequest; import org.wso2.carbon.device.mgt.common.PaginationRequest;
import org.wso2.carbon.device.mgt.common.PaginationResult; import org.wso2.carbon.device.mgt.common.PaginationResult;
import org.wso2.carbon.device.mgt.common.notification.mgt.Notification; import org.wso2.carbon.device.mgt.common.notification.mgt.Notification;
@ -102,4 +103,18 @@ public class NotificationManagementServiceImpl implements NotificationManagement
} }
} }
@Override
public Response clearAllNotifications() {
Notification.Status status = Notification.Status.CHECKED;
try {
int loggedinUserTenantId = CarbonContext.getThreadLocalCarbonContext()
.getTenantId();
DeviceMgtAPIUtils.getNotificationManagementService().updateAllNotifications(status, loggedinUserTenantId);
return Response.status(Response.Status.OK).build();
} catch (NotificationManagementException e) {
log.error("Error encountered while trying to clear all notifications.", e);
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build();
}
}
} }

@ -63,6 +63,16 @@ public interface NotificationManagementService {
boolean updateNotificationStatus(int notificationId, Notification.Status status) throws boolean updateNotificationStatus(int notificationId, Notification.Status status) throws
NotificationManagementException; NotificationManagementException;
/**
* Method for updating status all notifications.
*
* @return boolean status of the operation.
* @throws NotificationManagementException
* if something goes wrong while updating the Notification.
*/
boolean updateAllNotifications(Notification.Status status, int tenantID) throws
NotificationManagementException;
/** /**
* Method to fetch all the notifications in the database. * Method to fetch all the notifications in the database.
* *

@ -141,6 +141,28 @@ public class NotificationManagementServiceImpl implements NotificationManagement
return true; return true;
} }
@Override
public boolean updateAllNotifications(Notification.Status status, int tenantID) throws
NotificationManagementException {
if (log.isDebugEnabled()) {
log.debug("Attempting to clear all notifications");
}
try {
NotificationManagementDAOFactory.beginTransaction();
notificationDAO.updateAllNotifications(status, tenantID);
NotificationManagementDAOFactory.commitTransaction();
} catch (TransactionManagementException e) {
NotificationManagementDAOFactory.rollbackTransaction();
throw new NotificationManagementException("Error occurred while updating notification", e);
} finally {
NotificationManagementDAOFactory.closeConnection();
}
if (log.isDebugEnabled()) {
log.debug("All notifications updated successfully.");
}
return true;
}
@Override @Override
public List<Notification> getAllNotifications() throws NotificationManagementException { public List<Notification> getAllNotifications() throws NotificationManagementException {
try { try {

@ -61,6 +61,15 @@ public interface NotificationDAO {
int updateNotificationStatus(int notificationId, Notification.Status status) int updateNotificationStatus(int notificationId, Notification.Status status)
throws NotificationManagementException; throws NotificationManagementException;
/**
* Update status of all notifications.
*
* @return returns the no of updated records.
* @throws NotificationManagementException
*/
int updateAllNotifications(Notification.Status status, int tenantID) throws
NotificationManagementException;
/** /**
* This method is used to get all notifications based on tenant-id. * This method is used to get all notifications based on tenant-id.
* *

@ -142,6 +142,28 @@ public abstract class AbstractNotificationDAOImpl implements NotificationDAO {
return rows; return rows;
} }
@Override
public int updateAllNotifications(Notification.Status status, int tenantID)
throws NotificationManagementException {
Connection conn;
PreparedStatement stmt = null;
int rows;
try {
conn = NotificationManagementDAOFactory.getConnection();
String sql = "UPDATE DM_NOTIFICATION SET STATUS = ? WHERE TENANT_ID= ?";
stmt = conn.prepareStatement(sql);
stmt.setString(1, status.toString());
stmt.setInt(2, tenantID);
rows = stmt.executeUpdate();
} catch (Exception e) {
throw new NotificationManagementException("Error while trying to clear all " +
"notifications", e);
} finally {
NotificationDAOUtil.cleanupResources(stmt, null);
}
return rows;
}
@Override @Override
public List<Notification> getAllNotifications(int tenantId) throws NotificationManagementException { public List<Notification> getAllNotifications(int tenantId) throws NotificationManagementException {
Connection conn; Connection conn;

@ -216,5 +216,12 @@ public class NotificationManagementServiceImplTests {
" as added no. of records."); " as added no. of records.");
} }
@Test(dependsOnMethods = "updateStatusofAllNotification", description = "this tries to " +
"update the status of all notifications")
public void updateStatusOfAllNotifications() throws NotificationManagementException {
Assert.assertTrue(notificationManagementService.updateAllNotifications(Notification
.Status.CHECKED, -1234));
}
} }

@ -42,10 +42,8 @@
</a> </a>
</div> </div>
<div class="wr-hidden-operations-content col-lg-8" id="ast-container"> <div class="wr-hidden-operations-content col-lg-8" style="padding: 5px"
<div id="noNotificationtxt" class="panel-body"> id="ast-container">
No unread messages
</div>
</div> </div>
</div> </div>
</div> </div>
@ -55,8 +53,13 @@
</div> </div>
{{/zone}} {{/zone}}
{{#zone "bottomJs"}} {{#zone "bottomJs"}}
<script id="notification-listing" data-current-user="{{@user.username}}" <script id="notification-listing-new" data-current-user="{{@user.username}}"
data-image-resource="{{@unit.publicUri}}/images/" src="{{@unit.publicUri}}/templates/notification-listing.hbs" data-image-resource="{{@unit.publicUri}}/images/" src="{{@unit
.publicUri}}/templates/notification-listing-new.hbs"
type="text/x-handlebars-template"></script>
<script id="notification-listing-all" data-current-user="{{@user.username}}"
data-image-resource="{{@unit.publicUri}}/images/" src="{{@unit
.publicUri}}/templates/notification-listing-all.hbs"
type="text/x-handlebars-template"></script> type="text/x-handlebars-template"></script>
{{js "js/notification-listing.js"}} {{js "js/notification-listing.js"}}
{{/zone}} {{/zone}}

@ -17,6 +17,7 @@
*/ */
var deviceMgtAPIBaseURI = "/api/device-mgt/v1.0"; var deviceMgtAPIBaseURI = "/api/device-mgt/v1.0";
var notificationsAvailable = false;
/** /**
* Following function would execute * Following function would execute
@ -28,29 +29,74 @@ function InitiateViewOption() {
} }
function loadNotifications() { function loadNotifications() {
var deviceListing = $("#notification-listing"); var deviceListingNew = $("#notification-listing-new");
var deviceListingSrc = deviceListing.attr("src"); var deviceListingNewSrc = deviceListingNew.attr("src");
var currentUser = deviceListing.data("currentUser");
var deviceListingAll = $("#notification-listing-all");
var deviceListingAllSrc = deviceListingAll.attr("src");
$.template( $.template(
"notification-listing", "notification-listing-new",
deviceListingSrc, deviceListingNewSrc,
function (template) { function (template) {
invokerUtil.get( invokerUtil.get(
deviceMgtAPIBaseURI + "/notifications", deviceMgtAPIBaseURI + "/notifications?status=NEW",
// on success // on success
function (data, textStatus, jqXHR) { function (data, textStatus, jqXHR) {
if (jqXHR.status == 200 && data) { if (jqXHR.status == 200 && data) {
data = JSON.parse(data); data = JSON.parse(data);
if (data["notifications"] && data["notifications"].length > 0) { if (data["notifications"]) {
notificationsAvailable = true;
var viewModel = {}; var viewModel = {};
viewModel["notifications"] = data["notifications"]; viewModel["notifications"] = data["notifications"];
viewModel["appContext"] = context; viewModel["appContext"] = context;
var content = template(viewModel); var content = template(viewModel);
$("#ast-container").html(content); $("#ast-container").append(content);
var settings = { var settings = {
"sorting" : false "sorting" : false
}; };
$("#unread-notifications").datatables_extended(settings); $("#unread-notifications").datatables_extended(settings);
/**
* append advance operations to list table toolbar
*/
$('#unread-notifications_wrapper').find('.dataTablesTop' +
' .dataTables_toolbar').html(
"<a\ class=\"btn btn-primary\"" +
" data-click-event=\"clear-notification\">Clear All" +
" Notifications</a>"
);
}
}
},
// on error
function (jqXHR) {
console.log(jqXHR.status);
}
);
}
);
$.template(
"notification-listing-all",
deviceListingAllSrc,
function (template) {
invokerUtil.get(
deviceMgtAPIBaseURI + "/notifications",
// on success
function (data, textStatus, jqXHR) {
if (jqXHR.status == 200 && data) {
data = JSON.parse(data);
if (data["notifications"]) {
notificationsAvailable = true;
var viewModel = {};
viewModel["notifications"] = data["notifications"];
viewModel["appContext"] = context;
var content = template(viewModel);
$("#ast-container").append(content);
var settings = {
"sorting" : false
};
$("#all-notifications").datatables_extended(settings); $("#all-notifications").datatables_extended(settings);
} }
} }
@ -68,16 +114,9 @@ function loadNotifications() {
function showAdvanceOperation(operation, button) { function showAdvanceOperation(operation, button) {
$(button).addClass('selected'); $(button).addClass('selected');
$(button).siblings().removeClass('selected'); $(button).siblings().removeClass('selected');
if ($(button).attr("id") == 'allNotifications') {
$("#noNotificationtxt").html('You do not have any notifications ');
} else if ($(button).attr("id") == 'unReadNotifications') {
$("#noNotificationtxt").html('You do not have any unread notifications ');
} else {
$("#noNotificationtxt").html('You do not have any unread notifications ');
}
var hiddenOperation = ".wr-hidden-operations-content > div"; var hiddenOperation = ".wr-hidden-operations-content > div";
$(hiddenOperation + '[data-operation="' + operation + '"]').show();
$(hiddenOperation + '[data-operation="' + operation + '"]').siblings().hide(); $(hiddenOperation + '[data-operation="' + operation + '"]').siblings().hide();
$(hiddenOperation + '[data-operation="' + operation + '"]').show();
} }
$(document).ready(function () { $(document).ready(function () {
@ -119,4 +158,40 @@ $(document).ready(function () {
} }
); );
}); });
if(notificationsAvailable) {
$("#notification-clear-button").removeClass("hidden");
}
$("#ast-container").on("click", ".btn", function (e) {
var clickEvent = $(this).data('click-event');
if(clickEvent == "clear-notification"){
e.preventDefault();
var markAsReadNotificationsAPI = "/api/device-mgt/v1.0/notifications/clear-all";
var messageSideBar = ".sidebar-messages";
var clickEvent = $(this).data('click-event');
var eventHandler = $(this);
invokerUtil.put(
markAsReadNotificationsAPI,
null,
function (data) {
$('.message').remove();
$("#notification-bubble").html(0);
var undreadNotifications = $("#unread-notifications");
undreadNotifications.find("tbody").empty();
undreadNotifications.find("tbody").append("<tr><td colspan=''>No data" +
" available in table</td></tr>");
}, function () {
var content = "<li class='message message-danger'><h4><i class='icon fw fw-error'></i>Warning</h4>" +
"<p>Unexpected error occurred while loading notification. Please refresh the page and" +
" try again</p></li>";
$(messageSideBar).html(content);
}
);
}
});
}); });

@ -0,0 +1,42 @@
<!-- all -->
<div class="wr-hidden-operation" data-operation="all">
<div class="panel panel-default">
<div id="all-body" class="panel-collapse panel-body" role="tabpanel">
<div id="error-msg" class="alert alert-danger hidden" role="alert">
<i class="icon fw fw-error"></i><span></span>
</div>
<table class="table table-striped table-hover table-bordered display data-table" id="all-notifications">
<thead style="display: none;">
<tr class="sort-row">
<th>Description</th>
<th>Action</th>
</tr>
</thead>
<tbody>
{{#if notifications}}
{{#each notifications}}
<tr data-type="selectable" data-id="{{id}}">
<td data-display="{{description}}" data-grid-label="Description">{{description}}</td>
<td style="text-align: center;">
<a href="device/{{deviceType}}?id={{deviceIdentifier}}" data-click-event="remove-form">
<span class="fw-stack">
<i class="fw fw-circle-outline fw-stack-2x"></i>
<i class="fw fw-view fw-stack-1x"></i>
</span>
</a>
</td>
</tr>
{{/each}}
{{else}}
<div>
You do not have any notifications
</div>
{{/if}}
<br class="c-both" />
</tbody>
</table>
</div>
</div>
</div>
<!-- /all -->

@ -0,0 +1,44 @@
<!-- unread -->
<div class="wr-hidden-operation" data-operation="unread" style="display: block">
<div class="panel panel-default">
<div id="unread-body" class="panel-collapse panel-body" role="tabpanel">
<div id="error-msg" class="alert alert-danger hidden" role="alert">
<i class="icon fw fw-error"></i><span></span>
</div>
<table class="table table-striped table-hover table-bordered display data-table" id="unread-notifications">
<thead style="display: none;">
<tr class="sort-row">
<th>Description</th>
<th>Action</th>
</tr>
</thead>
<tbody>
{{#if notifications}}
{{#each notifications}}
{{#equal "NEW" status }}
<tr data-type="selectable" data-id="{{id}}">
<td data-display="{{description}}" data-grid-label="Description">{{description}}</td>
<td style="text-align: center;">
<a href="device/{{deviceType}}?id={{deviceIdentifier}}" data-id="{{id}}" data-url="device/{{deviceType}}?id={{deviceIdentifier}}" class="new-notification" data-click-event="remove-form">
<span class="fw-stack">
<i class="fw fw-circle-outline fw-stack-2x"></i>
<i class="fw fw-view fw-stack-1x"></i>
</span>
</a>
</td>
</tr>
{{/equal}}
{{/each}}
{{else}}
<div>
You do not have any unread notifications
</div>
{{/if}}
<br class="c-both" />
</tbody>
</table>
</div>
</div>
</div>
<!-- unread-->

@ -1,75 +0,0 @@
<!-- unread -->
<div class="wr-hidden-operation" data-operation="unread" style="display: block">
<div class="panel panel-default">
<div id="unread-body" class="panel-collapse panel-body" role="tabpanel">
<div id="error-msg" class="alert alert-danger hidden" role="alert">
<i class="icon fw fw-error"></i><span></span>
</div>
<table class="table table-striped table-hover table-bordered display data-table" id="unread-notifications">
<thead style="display: none;">
<tr class="sort-row">
<th>Description</th>
<th>Action</th>
</tr>
</thead>
<tbody>
{{#each notifications}}
{{#equal "NEW" status }}
<tr data-type="selectable" data-id="{{id}}">
<td data-display="{{description}}" data-grid-label="Description">{{description}}</td>
<td style="text-align: center;">
<a href="device/{{deviceType}}?id={{deviceIdentifier}}" data-id="{{id}}" data-url="device/{{deviceType}}?id={{deviceIdentifier}}" class="new-notification" data-click-event="remove-form">
<span class="fw-stack">
<i class="fw fw-circle-outline fw-stack-2x"></i>
<i class="fw fw-view fw-stack-1x"></i>
</span>
</a>
</td>
</tr>
{{/equal}}
{{/each}}
<br class="c-both" />
</tbody>
</table>
</div>
</div>
</div>
<!-- unread-->
<!-- all -->
<div class="wr-hidden-operation" data-operation="all">
<div class="panel panel-default">
<div id="all-body" class="panel-collapse panel-body" role="tabpanel">
<div id="error-msg" class="alert alert-danger hidden" role="alert">
<i class="icon fw fw-error"></i><span></span>
</div>
<table class="table table-striped table-hover table-bordered display data-table" id="all-notifications">
<thead style="display: none;">
<tr class="sort-row">
<th>Description</th>
<th>Action</th>
</tr>
</thead>
<tbody>
{{#each notifications}}
<tr data-type="selectable" data-id="{{id}}">
<td data-display="{{description}}" data-grid-label="Description">{{description}}</td>
<td style="text-align: center;">
<a href="device/{{deviceType}}?id={{deviceIdentifier}}" data-click-event="remove-form">
<span class="fw-stack">
<i class="fw fw-circle-outline fw-stack-2x"></i>
<i class="fw fw-view fw-stack-1x"></i>
</span>
</a>
</td>
</tr>
{{/each}}
<br class="c-both" />
</tbody>
</table>
</div>
</div>
</div>
<!-- /all -->

@ -158,7 +158,10 @@
data-side="right" data-width="320" data-sidebar-fixed="true" data-top="90" data-fixed-offset="90" data-offset-top="50" data-spy="affix"> data-side="right" data-width="320" data-sidebar-fixed="true" data-top="90" data-fixed-offset="90" data-offset-top="50" data-spy="affix">
<ul class="sidebar-messages"> <ul class="sidebar-messages">
</ul> </ul>
<div class="text-center"><a href="{{@app.context}}/notification-listing" class="btn btn-primary">Show All Notifications</a></div> <div class="text-center"><a href="{{@app.context}}/notification-listing" class="btn
btn-primary all-notifications">Show All Notifications</a></div>
<div id="right-side-pane-notifications-clear" class="text-center"><a class="btn
btn-primary" data-click-event="clear-notification">Clear All Notifications</a></div>
</div> </div>
{{/zone}} {{/zone}}
{{#zone "bottomJs"}} {{#zone "bottomJs"}}

@ -425,18 +425,24 @@ $.fn.collapse_nav_sub = function () {
$(document).ready(function () { $(document).ready(function () {
loadNotificationsPanel(); loadNotificationsPanel();
$("#right-sidebar").on("click", ".new-notification", function () { $("#right-sidebar").on("click", ".new-notification", function (e) {
var notificationId = $(this).data("id"); e.preventDefault();
var notificationId = $(this).parents('li').find('h4 a').data('id');
var redirectUrl = $(this).data("url"); var redirectUrl = $(this).data("url");
var markAsReadNotificationsAPI = "/api/device-mgt/v1.0/notifications/" + notificationId + "/mark-checked"; var markAsReadNotificationsAPI = "/api/device-mgt/v1.0/notifications/" + notificationId + "/mark-checked";
var messageSideBar = ".sidebar-messages"; var messageSideBar = ".sidebar-messages";
var clickEvent = $(this).data('click-event');
var eventHandler = $(this);
invokerUtil.put( invokerUtil.put(
markAsReadNotificationsAPI, markAsReadNotificationsAPI,
null, null,
function (data) { function (data) {
data = JSON.parse(data); data = JSON.parse(data);
if (data.statusCode == responseCodes["ACCEPTED"]) { if(clickEvent && clickEvent === 'remove-notification'){
$(eventHandler).parents('li').slideUp();
$("#notification-bubble").html(parseInt($("#notification-bubble").text()) - 1);
}else {
location.href = redirectUrl; location.href = redirectUrl;
} }
}, function () { }, function () {
@ -448,6 +454,34 @@ $(document).ready(function () {
); );
}); });
$("#right-sidebar").on("click", ".btn", function (e) {
var clickEvent = $(this).data('click-event');
if(clickEvent == "clear-notification"){
e.preventDefault();
var markAsReadNotificationsAPI = "/api/device-mgt/v1.0/notifications/clear-all";
var messageSideBar = ".sidebar-messages";
var clickEvent = $(this).data('click-event');
var eventHandler = $(this);
invokerUtil.put(
markAsReadNotificationsAPI,
null,
function (data) {
$('.message').remove();
$("#notification-bubble").html(0);
}, function () {
var content = "<li class='message message-danger'><h4><i class='icon fw fw-error'></i>Warning</h4>" +
"<p>Unexpected error occurred while loading notification. Please refresh the page and" +
" try again</p></li>";
$(messageSideBar).html(content);
}
);
}
});
if (typeof $.fn.collapse == 'function') { if (typeof $.fn.collapse == 'function') {
$('.navbar-collapse.tiles').on('shown.bs.collapse', function () { $('.navbar-collapse.tiles').on('shown.bs.collapse', function () {
$(this).collapse_nav_sub(); $(this).collapse_nav_sub();

@ -1,14 +1,27 @@
{{#each notifications}} {{#each notifications}}
<li class="message message-info" data-type="selectable" > <li class="message message-info" data-type="selectable" >
<table>
<td>
<h4> <h4>
<i class="icon fw fw-info"></i> <i class="icon fw fw-info"></i>
<a href="{{../context}}/device/{{deviceType}}?id={{deviceIdentifier}}" <a href="{{../context}}/device/{{deviceType}}?id={{deviceIdentifier}}"
data-id="{{id}}" data-id="{{id}}"
data-url="device?type={{deviceType}}&id={{deviceIdentifier}}" data-url="{{../context}}/device/{{deviceType}}?id={{deviceIdentifier}}"
class="new-notification" data-click-event="remove-form"> class="new-notification" data-click-event="remove-form">
{{deviceType}} : {{deviceName}} {{deviceType}} : {{deviceName}}
</a> </a>
</h4> </h4>
<p>{{description}}</p> <p>{{description}}</p>
</td>
<td>
<a class="new-notification" data-click-event="remove-notification">
<span class="fw-stack">
<i class="fw fw-circle-outline fw-stack-2x"></i>
<i class="fw fw-delete fw-stack-1x"></i>
</span>
</a>
</a>
</td>
</table>
</li> </li>
{{/each}} {{/each}}

@ -1178,6 +1178,10 @@ header .fw-user:before{
right: 0; right: 0;
} }
.all-notifications{
margin-bottom: 10px !important;
}
.sidebar-wrapper.toggled a.btn{ .sidebar-wrapper.toggled a.btn{
margin-bottom: 70px; margin-bottom: 70px;
} }

Loading…
Cancel
Save