a0e18ef963
Document Template import status and Device Enrolment status fixes. Attachment download fixes for SignalR foreverFrame transport. Database queries for Devices, Jobs and Users updated. Device attributes (model, profile, batch) now shown in various places.
580 lines
25 KiB
Plaintext
580 lines
25 KiB
Plaintext
@model Disco.Web.Models.Job.ShowModel
|
|
@{
|
|
Authorization.RequireAny(Claims.Job.ShowLogs, Claims.Job.ShowAttachments);
|
|
|
|
var canShowLogs = Authorization.Has(Claims.Job.ShowLogs);
|
|
var canShowAttachments = Authorization.Has(Claims.Job.ShowAttachments);
|
|
|
|
var canAddLogs = Authorization.Has(Claims.Job.Actions.AddLogs);
|
|
var canRemoveAnyLogs = Authorization.Has(Claims.Job.Actions.RemoveAnyLogs);
|
|
var canRemoveOwnLogs = Authorization.Has(Claims.Job.Actions.RemoveOwnLogs);
|
|
|
|
var canAddAttachments = Authorization.Has(Claims.Job.Actions.AddAttachments);
|
|
var canRemoveAnyAttachments = Authorization.Has(Claims.Job.Actions.RemoveAnyAttachments);
|
|
var canRemoveOwnAttachments = Authorization.Has(Claims.Job.Actions.RemoveOwnAttachments);
|
|
|
|
if (canShowAttachments)
|
|
{
|
|
Html.BundleDeferred("~/Style/Shadowbox");
|
|
Html.BundleDeferred("~/ClientScripts/Modules/Shadowbox");
|
|
}
|
|
Html.BundleDeferred("~/ClientScripts/Modules/jQuery-SignalR");
|
|
}
|
|
<table id="jobShowResources">
|
|
<tr class="@(canShowLogs ? "canShowLogs" : "cannotShowLogs") @(canShowAttachments ? "canShowAttachments" : "cannotShowAttachments")">
|
|
@if (canShowLogs)
|
|
{
|
|
<td id="Comments" class="@(canAddLogs ? "canAddLogs" : "cannotAddLogs")">
|
|
<div class="commentOutput">
|
|
@foreach (var jl in Model.Job.JobLogs.OrderBy(m => m.Timestamp))
|
|
{
|
|
<div data-logid="@jl.Id">
|
|
<span class="author">@jl.TechUser.ToStringFriendly()</span>@if (canRemoveAnyLogs || (canRemoveOwnLogs && jl.TechUserId == CurrentUser.UserId))
|
|
{<text><span class="remove fa fa-times-circle"></span></text>}<span class="timestamp" data-livestamp="@(jl.Timestamp.ToUnixEpoc())" title="@jl.Timestamp.ToFullDateTime()">@jl.Timestamp.ToFullDateTime()</span>
|
|
<span class="comment">@jl.Comments.ToMultilineJobRefString()</span>
|
|
</div>
|
|
}
|
|
</div>
|
|
@if (canAddLogs)
|
|
{
|
|
<div class="commentInput clearfix">
|
|
<textarea class="commentInput" placeholder="write comment..." accesskey="l"></textarea>
|
|
<span class="action post commentInputPost fa fa-comment" title="Post Comment"></span>
|
|
</div>
|
|
}
|
|
</td>
|
|
}
|
|
@if (canShowAttachments)
|
|
{
|
|
<td id="Attachments" class="@(canAddAttachments ? "canAddAttachments" : "cannotAddAttachments")">
|
|
<div class="attachmentOutput">
|
|
@foreach (var ja in Model.Job.JobAttachments)
|
|
{
|
|
<a href="@Url.Action(MVC.API.Job.AttachmentDownload(ja.Id))" data-attachmentid="@ja.Id" data-mimetype="@ja.MimeType">
|
|
<span class="icon" title="@ja.Filename">
|
|
<img alt="Attachment Thumbnail" src="@(Url.Action(MVC.API.Job.AttachmentThumbnail(ja.Id)))" /></span>
|
|
<span class="comments" title="@ja.Comments">
|
|
@{if (!string.IsNullOrEmpty(ja.DocumentTemplateId))
|
|
{ @ja.DocumentTemplate.Description}
|
|
else
|
|
{ @ja.Comments }}
|
|
</span><span class="author">@ja.TechUser.ToStringFriendly()</span>@if (canRemoveAnyAttachments || (canRemoveOwnAttachments && ja.TechUserId == CurrentUser.UserId))
|
|
{<text><span class="remove fa fa-times-circle"></span></text>}<span class="timestamp" data-livestamp="@(ja.Timestamp.ToUnixEpoc())" title="@ja.Timestamp.ToFullDateTime()">@ja.Timestamp.ToFullDateTime()</span>
|
|
</a>
|
|
}
|
|
</div>
|
|
@if (canAddAttachments)
|
|
{
|
|
<div class="attachmentInput clearfix">
|
|
<span class="action upload fa fa-upload" title="Attach File"></span><span class="action photo fa fa-camera" title="Capture Image"></span>
|
|
</div>
|
|
}
|
|
</td>
|
|
}
|
|
</tr>
|
|
</table>
|
|
@if (canShowLogs && (canRemoveAnyLogs || canRemoveOwnLogs))
|
|
{
|
|
<div id="dialogRemoveLog" class="dialog" title="Remove this Comment?">
|
|
<p>
|
|
<i class="fa fa-exclamation-triangle fa-lg"></i> Are you sure?
|
|
</p>
|
|
</div>
|
|
}
|
|
@if (canShowAttachments && canAddAttachments)
|
|
{
|
|
<div id="dialogUpload" class="dialog" title="Upload Attachment">
|
|
<div id="silverlightHostUploadAttachment">
|
|
</div>
|
|
</div>
|
|
}
|
|
@if (canShowAttachments && (canRemoveAnyAttachments || canRemoveOwnAttachments))
|
|
{
|
|
<div id="dialogRemoveAttachment" class="dialog" title="Remove this Attachment?">
|
|
<p>
|
|
<i class="fa fa-exclamation-triangle fa-lg"></i> Are you sure?
|
|
</p>
|
|
</div>
|
|
}
|
|
@if (canShowLogs)
|
|
{
|
|
<script>
|
|
if (!document.DiscoFunctions) {
|
|
document.DiscoFunctions = {};
|
|
}
|
|
|
|
$(function () {
|
|
var jobId = parseInt('@(Model.Job.Id)');
|
|
|
|
//#region Comments
|
|
var $Comments = $('#Comments');
|
|
var $CommentOutput = $Comments.find('.commentOutput');
|
|
var $CommentOutputContainer = $Comments.find('.commentOutputContainer');
|
|
|
|
window.setTimeout(function () {
|
|
$CommentOutput[0].scrollTop = $CommentOutput[0].scrollHeight; // Scroll to Bottom
|
|
}, 0);
|
|
$('#jobDetailTabs').on('tabsactivate', function (event, ui) {
|
|
if (ui.newPanel && ui.newPanel.is('#jobDetailTab-Resources')) {
|
|
$CommentOutput[0].scrollTop = $CommentOutput[0].scrollHeight; // Scroll to Bottom
|
|
}
|
|
});
|
|
|
|
@if (canAddLogs)
|
|
{<text>
|
|
//#region Add Logs
|
|
|
|
var $CommentInput = $Comments.find('textarea.commentInput');
|
|
|
|
$Comments.find('.commentInputPost').click(postComment);
|
|
$CommentInput.keypress(function (e) {
|
|
if (e.which == 13 && !e.shiftKey) {
|
|
postComment();
|
|
return false;
|
|
}
|
|
});
|
|
|
|
function postComment() {
|
|
var comment = $CommentInput.val();
|
|
if (comment != '') {
|
|
var data = { comment: comment }
|
|
$.ajax({
|
|
url: '@Url.Action(MVC.API.Job.CommentPost(Model.Job.Id, null))',
|
|
dataType: 'json',
|
|
data: data,
|
|
success: function (d) {
|
|
if (d.Result == 'OK') {
|
|
// Should be added via Repository Notifications
|
|
// addComment(d.Comment, false);
|
|
$CommentInput.val('').attr('disabled', false).focus();
|
|
} else {
|
|
alert('Unable to post comment: ' + d.Result);
|
|
$CommentInput.attr('disabled', false);
|
|
}
|
|
},
|
|
error: function (jqXHR, textStatus, errorThrown) {
|
|
alert('Unable to post comment: ' + textStatus);
|
|
$CommentInput.attr('disabled', false);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
//#endregion
|
|
</text>}
|
|
@if (canRemoveAnyLogs || canRemoveOwnLogs)
|
|
{<text>
|
|
//#region Remove Logs
|
|
|
|
var $dialogRemoveLog;
|
|
|
|
$CommentOutput.find('span.remove').click(removePost);
|
|
|
|
function removePost() {
|
|
$this = $(this);
|
|
var data = { id: $this.closest('div').attr('data-logid') };
|
|
|
|
if (!$dialogRemoveLog) {
|
|
$dialogRemoveLog = $('#dialogRemoveLog').dialog({
|
|
resizable: false,
|
|
height: 140,
|
|
modal: true,
|
|
autoOpen: false
|
|
});
|
|
}
|
|
|
|
$dialogRemoveLog.dialog("enable").dialog('option', 'buttons', {
|
|
"Remove": function () {
|
|
$dialogRemoveLog.dialog("disable");
|
|
$dialogRemoveLog.dialog("option", "buttons", null);
|
|
$.ajax({
|
|
url: '@Url.Action(MVC.API.Job.CommentRemove())',
|
|
dataType: 'json',
|
|
data: data,
|
|
success: function (d) {
|
|
if (d == 'OK') {
|
|
// Should be removed via Repository Notifications
|
|
//$this.closest('div').slideUp(300).delay(300).queue(function () {
|
|
// $(this).remove();
|
|
//});
|
|
} else {
|
|
alert('Unable to remove comment: ' + d);
|
|
}
|
|
$dialogRemoveLog.dialog("close");
|
|
},
|
|
error: function (jqXHR, textStatus, errorThrown) {
|
|
alert('Unable to remove comment: ' + textStatus);
|
|
$dialogRemoveLog.dialog("close");
|
|
}
|
|
});
|
|
},
|
|
"Cancel": function () {
|
|
$dialogRemoveLog.dialog("close");
|
|
}
|
|
}).dialog('open');
|
|
|
|
return false;
|
|
}
|
|
|
|
//#endregion
|
|
</text>}
|
|
|
|
function loadLiveComment(key) {
|
|
$.ajax({
|
|
url: '@Url.Action(MVC.API.Job.Comment())',
|
|
dataType: 'json',
|
|
data: { id: key },
|
|
success: function (d) {
|
|
if (d && d.JobId == jobId) {
|
|
@if (canRemoveAnyLogs)
|
|
{<text>addComment(d, false, true);</text>}
|
|
else if (canRemoveOwnLogs)
|
|
{<text>addComment(d, false, (d.AuthorId === '@(CurrentUser.UserId)'));</text>}
|
|
else
|
|
{<text>addComment(d, false, false);</text>}
|
|
|
|
}
|
|
},
|
|
error: function (jqXHR, textStatus, errorThrown) {
|
|
alert('Unable to load live comment ' + id + ': ' + textStatus);
|
|
}
|
|
});
|
|
}
|
|
function liveRemoveComment(key) {
|
|
$CommentOutput.children('div[data-logid="' + key + '"]').slideUp(300).delay(300).queue(function () {
|
|
var $this = $(this);
|
|
$this.find('.timestamp').livestamp('destroy');
|
|
$this.remove();
|
|
});
|
|
}
|
|
function addComment(c, quick, canRemove) {
|
|
var t = '<div><span class="author" />';
|
|
if (canRemove)
|
|
t += '<span class="remove fa fa-times-circle" />';
|
|
t += '<span class="timestamp" /><span class="comment" /></div>';
|
|
|
|
var e = $(t);
|
|
e.attr('data-logid', c.Id);
|
|
e.find('.author').text(c.Author);
|
|
e.find('.timestamp').text(c.TimestampFull).attr('title', c.TimestampFull).livestamp(c.TimestampUnixEpoc);
|
|
if (canRemove)
|
|
e.find('.remove').click(removePost);
|
|
var eComment = e.find('.comment').text(c.Comments);
|
|
var commentHtml = eComment.text().replace(/\r\n|\r|\n/g, '<br />');
|
|
commentHtml = commentHtml.replace(/\#(\d+)/g, '<a href="@Url.Action(MVC.Job.Show(null))?id=$1">#$1</a>');
|
|
eComment.html(commentHtml);
|
|
|
|
$CommentOutput.append(e);
|
|
|
|
if (!quick) {
|
|
e.animate({ backgroundColor: '#ffff99' }, 500, function () {
|
|
e.animate({ backgroundColor: '#fafafa' }, 500, function () {
|
|
e.css('background-color', '');
|
|
});
|
|
});
|
|
$CommentOutput.animate({ scrollTop: $CommentOutput[0].scrollHeight }, 250)
|
|
}
|
|
}
|
|
|
|
// Add Globally Available Functions
|
|
document.DiscoFunctions.liveLoadComment = loadLiveComment;
|
|
document.DiscoFunctions.liveRemoveComment = liveRemoveComment;
|
|
//#endregion
|
|
});
|
|
</script>
|
|
}
|
|
@if (canShowAttachments)
|
|
{
|
|
<script>
|
|
Shadowbox.init({
|
|
skipSetup: true,
|
|
modal: true
|
|
});
|
|
|
|
if (!document.DiscoFunctions) {
|
|
document.DiscoFunctions = {};
|
|
}
|
|
|
|
$(function () {
|
|
var jobId = parseInt('@(Model.Job.Id)');
|
|
|
|
//#region Attachments
|
|
var $Attachments = $('#Attachments');
|
|
var $attachmentOutput = $Attachments.find('.attachmentOutput');
|
|
|
|
@if (canAddAttachments)
|
|
{<text>
|
|
//#region Add Attachments
|
|
|
|
// For Silverlight Backwards-compatibility
|
|
// - Repository notifications now handles this
|
|
document.DiscoFunctions.addAttachment = function () { };
|
|
|
|
var $dialogUpload;
|
|
var onLoadNavigation = null;
|
|
var isLoaded = null;
|
|
|
|
var $attachmentInput = $Attachments.find('.attachmentInput');
|
|
$attachmentInput.find('.photo').click(function () {
|
|
showDialog('/WebCam');
|
|
});
|
|
$attachmentInput.find('.upload').click(function () {
|
|
showDialog('/File');
|
|
});
|
|
|
|
function showDialog(navigationPath) {
|
|
if (!$dialogUpload) {
|
|
Silverlight.createObject(
|
|
'@(Links.ClientBin.Disco_Silverlight_AttachmentUpload_xap)',
|
|
$('#silverlightHostUploadAttachment').get(0),
|
|
'silverlightUploadAttachment',
|
|
{ width: '840px', height: '500px', background: 'white', version: '4.0.60310.0' },
|
|
{
|
|
onLoad: function () {
|
|
if (onLoadNavigation) {
|
|
$('#silverlightUploadAttachment').get(0).content.Navigator.Navigate(onLoadNavigation);
|
|
isLoaded = true;
|
|
}
|
|
}
|
|
},
|
|
'UploadUrl=@(Url.Action(MVC.API.Job.AttachmentUpload(Model.Job.Id, null)))');
|
|
|
|
$dialogUpload = $('#dialogUpload').dialog({
|
|
autoOpen: false,
|
|
draggable: false,
|
|
modal: true,
|
|
resizable: false,
|
|
width: 860,
|
|
height: 550,
|
|
close: function () {
|
|
var sl = $('#silverlightUploadAttachment').get(0);
|
|
if (sl.content)
|
|
sl.content.Navigator.Navigate('/Hidden');
|
|
}
|
|
});
|
|
|
|
}
|
|
$dialogUpload.dialog('open');
|
|
if (isLoaded) {
|
|
$('#silverlightUploadAttachment').get(0).content.Navigator.Navigate(navigationPath);
|
|
} else {
|
|
onLoadNavigation = navigationPath;
|
|
}
|
|
};
|
|
|
|
//#endregion
|
|
</text>}
|
|
|
|
@if (canRemoveAnyAttachments || canRemoveOwnAttachments)
|
|
{<text>
|
|
//#region Remove Attachments
|
|
|
|
var $dialogRemoveAttachment;
|
|
|
|
$attachmentOutput.find('span.remove').click(removeLocalAttachment);
|
|
|
|
function removeLocalAttachment() {
|
|
$this = $(this).closest('a');
|
|
|
|
var data = { id: $this.attr('data-attachmentid') };
|
|
|
|
if (!$dialogRemoveAttachment) {
|
|
$dialogRemoveAttachment = $('#dialogRemoveAttachment').dialog({
|
|
resizable: false,
|
|
height: 140,
|
|
modal: true,
|
|
autoOpen: false
|
|
});
|
|
}
|
|
|
|
$dialogRemoveAttachment.dialog("enable").dialog('option', 'buttons', {
|
|
"Remove": function () {
|
|
$dialogRemoveAttachment.dialog("disable");
|
|
$dialogRemoveAttachment.dialog("option", "buttons", null);
|
|
$.ajax({
|
|
url: '@Url.Action(MVC.API.Job.AttachmentRemove())',
|
|
dataType: 'json',
|
|
data: data,
|
|
success: function (d) {
|
|
if (d == 'OK') {
|
|
// Should be removed via Repository Notifications
|
|
} else {
|
|
alert('Unable to remove attachment: ' + d);
|
|
}
|
|
$dialogRemoveAttachment.dialog("close");
|
|
},
|
|
error: function (jqXHR, textStatus, errorThrown) {
|
|
alert('Unable to remove attachment: ' + textStatus);
|
|
$dialogRemoveAttachment.dialog("close");
|
|
}
|
|
});
|
|
},
|
|
"Cancel": function () {
|
|
$dialogRemoveAttachment.dialog("close");
|
|
}
|
|
}).dialog('open');
|
|
|
|
return false;
|
|
}
|
|
|
|
//#endregion
|
|
|
|
</text>}
|
|
|
|
function addAttachment(key, quick) {
|
|
var data = { id: key };
|
|
$.ajax({
|
|
url: '@Url.Action(MVC.API.Job.Attachment())',
|
|
dataType: 'json',
|
|
data: data,
|
|
success: function (d) {
|
|
if (d.Result == 'OK') {
|
|
var a = d.Attachment;
|
|
@if (canRemoveAnyAttachments)
|
|
{
|
|
<text>buildAttachment(a, true, quick);</text>
|
|
}
|
|
else if (canRemoveOwnAttachments)
|
|
{
|
|
<text>buildAttachment(a, (a.AuthorId === '@(CurrentUser.UserId)'), quick);</text>
|
|
}
|
|
else
|
|
{
|
|
<text>buildAttachment(a, false, quick);</text>
|
|
}
|
|
} else {
|
|
alert('Unable to add attachment: ' + d.Result);
|
|
}
|
|
},
|
|
error: function (jqXHR, textStatus, errorThrown) {
|
|
alert('Unable to add attachment: ' + textStatus);
|
|
}
|
|
});
|
|
}
|
|
function buildAttachment(a, canRemove, quick) {
|
|
if (parseInt(a.ParentId) == jobId) {
|
|
var t = '<a><span class="icon"><img alt="Attachment Thumbnail" /></span><span class="comments"></span><span class="author"></span>';
|
|
if (canRemove)
|
|
t += '<span class="remove fa fa-times-circle"></span>';
|
|
t += '<span class="timestamp"></span></a>';
|
|
|
|
var e = $(t);
|
|
|
|
e.attr('data-attachmentid', a.Id).attr('data-mimetype', a.MimeType).attr('href', '@(Url.Action(MVC.API.Job.AttachmentDownload()))/' + a.Id);
|
|
e.find('.icon img').attr('src', '@(Url.Action(MVC.API.Job.AttachmentThumbnail()))/' + a.Id);
|
|
e.find('.comments').text(a.Description);
|
|
e.find('.author').text(a.Author);
|
|
e.find('.timestamp').text(a.TimestampFull).attr('title', a.TimestampFull).livestamp(a.TimestampUnixEpoc);
|
|
if (canRemove)
|
|
e.find('.remove').click(removeLocalAttachment);
|
|
if (!quick)
|
|
e.hide();
|
|
$attachmentOutput.append(e);
|
|
document.DiscoFunctions.liveAfterUpdate();
|
|
if (!quick)
|
|
e.show('slow');
|
|
if (a.MimeType.toLowerCase().indexOf('image/') == 0)
|
|
e.shadowbox({ gallery: 'attachments', player: 'img', title: a.Description });
|
|
else
|
|
e.click(onDownload);
|
|
}
|
|
}
|
|
|
|
function onDownload() {
|
|
var $this = $(this);
|
|
var url = $this.attr('href');
|
|
|
|
if ($.connection && $.connection.hub && $.connection.hub.transport &&
|
|
$.connection.hub.transport.name == 'foreverFrame') {
|
|
// SignalR active with foreverFrame transport - use popup window
|
|
window.open(url, '_blank', 'height=150,width=250,location=no,menubar=no,resizable=no,scrollbars=no,status=no,toolbar=no');
|
|
} else {
|
|
// use iFrame
|
|
if (!$attachmentDownloadHost) {
|
|
$attachmentDownloadHost = $('<iframe>')
|
|
.attr({ 'src': url, 'title': 'Attachment Download Host' })
|
|
.addClass('hidden')
|
|
.appendTo('body')
|
|
.contents();
|
|
} else {
|
|
$attachmentDownloadHost[0].location.href = url;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
function removeAttachment(key) {
|
|
var $element = $attachmentOutput.find('a[data-attachmentid="' + key + '"]');
|
|
if ($element.length > 0) {
|
|
$element.hide(300).delay(300).queue(function () {
|
|
if ($element.attr('data-mimetype').toLowerCase().indexOf('image/') == 0)
|
|
Shadowbox.removeCache(this);
|
|
$element.remove();
|
|
document.DiscoFunctions.liveAfterUpdate();
|
|
});
|
|
}
|
|
}
|
|
|
|
$attachmentOutput.children('a').each(function () {
|
|
$this = $(this);
|
|
if ($this.attr('data-mimetype').toLowerCase().indexOf('image/') == 0)
|
|
$this.shadowbox({ gallery: 'attachments', player: 'img', title: $this.find('.comments').text() });
|
|
else
|
|
$this.click(onDownload);
|
|
});
|
|
|
|
// Add Globally Available Functions
|
|
document.DiscoFunctions.liveAddAttachment = addAttachment;
|
|
document.DiscoFunctions.liveRemoveAttachment = removeAttachment;
|
|
|
|
//#endregion
|
|
});
|
|
|
|
|
|
</script>
|
|
}
|
|
@if (canShowLogs || canShowAttachments)
|
|
{
|
|
<script>
|
|
$(function () {
|
|
var jobId = parseInt('@(Model.Job.Id)');
|
|
|
|
//#region LiveEvents
|
|
var hub = $.connection.jobUpdates;
|
|
|
|
// Map Functions
|
|
@if (canShowLogs)
|
|
{<text>
|
|
hub.client.addLog = document.DiscoFunctions.liveLoadComment;
|
|
hub.client.removeLog = document.DiscoFunctions.liveRemoveComment;
|
|
</text>}
|
|
@if (canShowAttachments)
|
|
{<text>
|
|
hub.client.addAttachment = document.DiscoFunctions.liveAddAttachment;
|
|
hub.client.removeAttachment = document.DiscoFunctions.liveRemoveAttachment;
|
|
|
|
document.DiscoFunctions.liveAfterUpdate = function () {
|
|
var tabLink = $('#jobDetailTab-ResourcesLink');
|
|
var attachmentCount = $('#Attachments').find('.attachmentOutput').children('a').length;
|
|
|
|
var tabHeading = tabLink.text();
|
|
tabHeading = tabHeading.substr(0, tabHeading.indexOf('[') + 1) + attachmentCount + ']';
|
|
tabLink.text(tabHeading);
|
|
}
|
|
</text>}
|
|
|
|
$.connection.hub.qs = { JobId: jobId };
|
|
$.connection.hub.error(onHubError);
|
|
|
|
// Start Connection
|
|
$.connection.hub.start().fail(onHubError);
|
|
|
|
function onHubError(error) {
|
|
alert('Live-update Error: ' + error);
|
|
}
|
|
|
|
//#endregion
|
|
});
|
|
</script>
|
|
} |