Helpdesk da PluGzOne, baseado no osTicket
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

507 lines
19 KiB

/* Generic draft support for osTicket. The plugins supports draft retrieval
* automatically, along with draft autosave, and image uploading.
*
* Configuration:
* draftNamespace: namespace for the draft retrieval
* draftObjectId: extension to the namespace for draft retrieval
*
* Caveats:
* Login (staff only currently) is required server-side for drafts and image
* uploads. Furthermore, the id of the staff is considered for the drafts,
* so one user will not retrieve drafts for another user.
*/
(function(R$) {
// Monkey patch incorrect code in the inpection module
var stockInspectorParser = $R[$R.env['class']]['inspector.parser'];
R$.add('class', 'inspector.parser', $R.extend(stockInspectorParser.prototype, {
_getClosestUpNode: function(selector)
{
var $el = this.$el.parents(selector, '.redactor-in-' + this.uuid).last();
return ($el.length !== 0) ? $el.get() : false;
},
_getClosestNode: function(selector)
{
var $el = this.$el.closest(selector, '.redactor-in-' + this.uuid);
return ($el.length !== 0) ? $el.get() : false;
},
_getClosestElement: function(selector)
{
var $el = this.$el.closest(selector, '.redactor-in-' + this.uuid);
return ($el.length !== 0) ? $el : false;
},
}));
R$.add('plugin', 'draft', {
init: function (app) {
this.app = app;
this.$textarea = $(this.app.rootElement);
this.toolbar = this.app.toolbar;
this.opts = app.opts;
this.lastUpdate = 0;
this.statusbar = app.statusbar;
},
start: function() {
if (!this.opts.draftNamespace)
return;
var autosave_url = 'ajax.php/draft/' + this.opts.draftNamespace;
if (this.opts.draftObjectId)
autosave_url += '.' + this.opts.draftObjectId;
this.opts.autosave = this.autoCreateUrl = autosave_url;
this.opts.autosaveDelay = 10000;
if (this.opts.draftId) {
this.statusbar.add('draft', __('all changes saved'));
this._setup(this.opts.draftId);
}
else if (this.$textarea.hasClass('draft')) {
// Just upload the file. A draft will be created automatically
// and will be configured locally in the afterUpateDraft()
this.opts.clipboardUpload =
this.opts.imageUpload = this.autoCreateUrl + '/attach';
}
this.opts.autosaveData = {
'__CSRFToken__': $("meta[name=csrf_token]").attr("content")
};
if (autosave_url)
this.app.api('module.autosave.enable');
if (this.app.source.getCode())
this.app.broadcast('draft.recovered');
},
stop: function() {
this.app.statusbar.remove('draft');
},
_setup: function (draft_id) {
this.opts.draftId = draft_id;
this.opts.autosave = 'ajax.php/draft/' + draft_id;
this.opts.clipboardUpload =
this.opts.imageUpload =
'ajax.php/draft/' + draft_id + '/attach';
// Add [Delete Draft] button to the toolbar
if (this.opts.draftDelete) {
this.opts.draftSave = true;
var trash = this.deleteButton =
this.toolbar.addButton('deletedraft', {
title: __('Delete Draft'),
api: 'plugin.draft.deleteDraft',
icon: 'icon-trash',
});
trash.addClass('pull-right icon-trash');
}
// Add [Save Draft] button to the toolbar
if (this.opts.draftSave) {
var save = this.saveButton =
this.toolbar.addButton('savedraft', {
title: __('Save Draft'),
api: 'plugin.draft.saveDraft',
icon: 'icon-save',
});
save.addClass('pull-right icon-save');
}
},
onautosave: function(name, _, data) {
// If the draft was created, a draft_id will be sent back — update
// the URL to send updates in the future
if (!this.opts.draftId && data.draft_id) {
this._setup(data.draft_id);
$(this.app.rootElement).attr('data-draft-id', this.opts.draftId);
}
this.statusbar.add('draft', __('all changes saved'));
this.app.broadcast('draft.saved');
},
onautosaveSend: function() {
this.statusbar.add('draft', __('saving...'));
},
onautosaveError: function(error) {
if (error.code == 422)
// Unprocessable request (Empty message)
return;
this.displayError(error);
// Cancel autosave
this.app.api('module.autosave.disable');
this.statusbar.add('draft', '<span style="color:red">{}</span>'.replace('{}', __('save error')));
this.app.broadcast('draft.failed');
},
onimage: {
uploaded: function(image, response) {
this.onautosave(null, null, response);
},
uploadError: function (response) {
this.displayError(response);
}
},
displayError: function(json) {
$.sysAlert(json.error,
__('Unable to save draft.')
+ __('Refresh the current page to restore and continue your draft.'));
},
onchanged: function() {
this.statusbar.add('draft', __('unsaved'));
},
showDraftSaved: function() {
this.$draft_saved.show();
},
saveDraft: function() {
if (!this.opts.draftId)
return;
response = $(".draft").val()
if (response) {
var data = {
name: 'response',
response: response,
};
var self = this;
$.ajax('ajax.php/draft/'+this.opts.draftId, {
type: 'POST',
data: data,
dataType: 'json',
success: function() {
self.draft_id = self.opts.draftId;
self.opts.autosave = self.autoCreateUrl;
self.app.statusbar.add('draft', __('all changes saved'));
}
});
}
},
deleteDraft: function() {
if (!this.opts.draftId)
// Nothing to delete
return;
var self = this;
$.ajax('ajax.php/draft/'+this.opts.draftId, {
type: 'delete',
success: function() {
self.draft_id = self.opts.draftId = undefined;
self.app.statusbar.remove('draft');
self.app.source.setCode(self.opts.draftOriginal || '');
self.opts.autosave = self.autoCreateUrl;
self.opts.clipboardUpload =
self.opts.imageUpload = self.autoCreateUrl + '/attach';
self.deleteButton.hide();
self.saveButton.hide();
self.app.broadcast('draft.deleted');
}
});
}
});
// Monkey patch the autosave module to include an `autosaveBefore` signal
// and an delay option to limit calls to the backend.
var stockAutosave = $R[$R.env['module']]['autosave'];
R$.add('module', 'autosave', $R.extend(stockAutosave.prototype, {
onsynced: function() {
if (this.opts.autosave) {
// Don't send to backend if empty
if (!this.source.getCode())
return;
if (this.opts.autosaveDelay) {
if (this.delayTimer)
clearInterval(this.delayTimer);
this.delayTimer = setTimeout(this._sendDelayed.bind(this),
this.opts.autosaveDelay);
}
else {
this._sendDelayed();
}
}
},
_sendDelayed: function() {
this.app.broadcast('autosaveSend');
this._send.call(this);
},
}));
// Monkey patch the toolbar server to support adding buttons in an automatic
// position based on the `buttons` setting
var stockToolbar = $R[$R.env['service']]['toolbar'];
R$.add('service', 'toolbar', $R.extend(stockToolbar.prototype, {
addButtonAuto: function(name, btnObj) {
var pos = this.opts.buttons.indexOf(name);
if (pos === -1)
return this.addButton(name, btnObj);
if (pos === 0)
return this.addButtonFirst(name, btnObj);
return this.addButtonAfter(this.opts.buttons[pos - 1], name, btnObj);
},
}));
R$.add('plugin', 'autolock', {
init: function (app) {
this.app = app;
},
start: function () {
var root = $(this.app.rootElement),
code = root.closest('form').find('[name=lockCode]');
if (code.length)
this.lock = root.closest('[data-lock-object-id]');
},
onchanged: function(e) {
if (this.lock)
this.lock.exclusive('acquire');
}
});
R$.add('plugin', 'signature', {
init: function (app) {
this.app = app;
},
start: function() {
var $el = $R.dom(this.app.rootElement),
$box = this.app.editor.getElement(),
inner = $R.dom('<div class="inner"></div>'),
$form = $el.closest('form'),
signatureField = $el.data('signature-field');
if (signatureField) {
this.$signatureBox = $R.dom('<div class="selected-signature"></div>')
.append(inner);
this.app.editor.getElement().parent().find('.redactor-statusbar').before(this.$signatureBox);
if ($el.data('signature'))
inner.html($el.data('signature'));
else
this.$signatureBox.hide();
$R.dom('input[name='+signatureField+']', $form)
.on('change', this.updateSignature.bind(this));
// Expand on hover
var outer = this.$signatureBox,
inner = $('.inner', this.$signatureBox.get(0)).get(0),
originalHeight = outer.height(),
hoverTimeout = undefined,
originalShadow = this.$signatureBox.css('box-shadow');
this.$signatureBox.on('mouseenter', function() {
hoverTimeout = setTimeout(function() {
$(this).animate({
'height': inner.offsetHeight + 25
}, 'fast');
$(this).css('box-shadow', 'none', 'important');
}.bind(this), 250);
}).on('mouseleave', function() {
clearTimeout(hoverTimeout);
$(this).stop().animate({
'height': Math.min(inner.offsetHeight, originalHeight - 10)
}, 'fast');
$(this).css('box-shadow', originalShadow);
});
$el.find('.redactor-box').css('border-bottom-style', 'none', true);
}
},
updateSignature: function(e) {
var $el = $(this.app.rootElement),
signatureField = $el.data('signature-field'),
$form = $el.closest('form'),
selected = $(':input:checked[name='+signatureField+']', $form).val(),
type = $R.dom(e.target).val(),
dept = $R.dom(':input[name='+$el.data('dept-field')+']', $form).val(),
url = 'ajax.php/content/signature/',
inner = $R.dom('.inner', this.$signatureBox);
e.preventDefault && e.preventDefault();
if (selected == 'dept' && $el.data('dept-id'))
url += 'dept/' + $el.data('dept-id');
else if (selected == 'dept' && $el.data('dept-field')) {
if (dept)
url += 'dept/' + dept;
else
return inner.empty().parent().hide();
}
else if (selected == 'theirs' && $el.data('poster-id')) {
url += 'agent/' + $el.data('poster-id');
}
else if (type == 'none')
return inner.empty().parent().hide();
else
url += selected;
$R.ajax.get({
url: url,
success: function(html) {
inner.html(html).parent().show();
}
});
}
});
})(Redactor);
/* Redactor richtext init */
$(function() {
var captureImageSizes = function(html) {
$('img', this.$box).each(function(i, img) {
// TODO: Rewrite the entire <img> tag. Otherwise the @width
// and @height attributes will begin to accumulate
before = img.outerHTML;
if (img.clientWidth && img.clientHeight)
$(img).attr('width', img.clientWidth)
.attr('height',img.clientHeight);
html = html.replace(before, img.outerHTML);
});
return html;
},
redact = $.fn.redact = function(el, options) {
var el = $(el),
sizes = {'small': '75px', 'medium': '150px', 'large': '225px'},
selectedSize = sizes['medium'];
$.each(sizes, function(k, v) {
if (el.hasClass(k)) selectedSize = v;
});
var options = $.extend({
'air': el.hasClass('no-bar'),
'buttons': el.hasClass('no-bar')
? ['format', '|', 'bold', 'italic', 'underline', 'deleted', 'lists', 'link', 'image']
: ['html', 'format', 'fontcolor', 'fontfamily', 'bold',
'italic', 'underline', 'deleted', 'lists', 'image', 'video',
'file', 'table', 'link', 'line', 'fullscreen'],
'buttonSource': !el.hasClass('no-bar'),
'autoresize': !el.hasClass('no-bar') && !el.closest('.dialog').length,
'maxHeight': el.closest('.dialog').length ? selectedSize : false,
'minHeight': selectedSize,
'maxWidth': el.hasClass('fullscreen') ? '950px' : false,
'focus': false,
'plugins': el.hasClass('no-bar')
? ['imagemanager','definedlinks']
: ['imagemanager','table','video','definedlinks','autolock', 'fontcolor', 'fontfamily'],
'imageUpload': el.hasClass('draft'),
'imageManagerJson': 'ajax.php/draft/images/browse',
'imagePosition': true,
'imageUploadData': {
'__CSRFToken__': $("meta[name=csrf_token]").attr("content")
},
'imageResizable': true,
'syncBeforeCallback': captureImageSizes,
'tabFocus': false,
'toolbarFixed': true,
'callbacks': {
'start': function() {
var $element = $R.dom(this.rootElement),
$editor = this.editor.$editor;
if ($element.data('width'))
$editor.width($element.data('width'));
$editor.addClass('no-pjax');
$editor.attr('spellcheck', 'true');
var lang = $element.closest('[lang]').attr('lang');
if (lang)
$editor.attr('lang', lang);
// Fixup class for
$element.parent().closest(':not(.redactor-box)').addClass('-redactor-container')
},
},
'linkSize': 100000,
'definedlinks': 'ajax.php/config/links'
}, options||{});
if (el.data('redactor')) return;
var reset = $('input[type=reset]', el.closest('form'));
if (reset) {
reset.click(function() {
var file = $('.file', el.closest('form'));
if (file)
file.remove();
if (el.attr('data-draft-id')) {
el.redactor('plugin.draft.deleteDraft');
el.attr('data-draft-id', '');
}
else {
try {
el.redactor('source.setCode', '');
}
catch (error) {
el.redactor(); //reinitialize redactor
el.redactor('source.setCode', '');
}
}
});
}
if (!$.clientPortal) {
options['plugins'].push('signature');
}
if (el.hasClass('draft')) {
el.closest('form').append($('<input type="hidden" name="draft_id"/>'));
options['plugins'].push('draft');
options['plugins'].push('imageannotate');
options.draftDelete = el.hasClass('draft-delete');
options.draftSave = el.hasClass('draft-save');
}
if (true || 'scp') { // XXX: Add this to SCP only
options['plugins'].push('contexttypeahead');
}
if (el.hasClass('fullscreen'))
options['plugins'].push('fullscreen');
if (el.data('translateTag'))
options['plugins'].push('translatable');
if ($('#thread-items[data-thread-id]').length)
options['imageManagerJson'] += '?threadId=' + $('#thread-items').data('threadId');
getConfig().then(function(c) {
if (c.lang && c.lang.toLowerCase() != 'en_us' &&
Redactor.lang[c.short_lang])
options['lang'] = c.short_lang;
if (c.has_rtl)
options['plugins'].push('textdirection');
if (el.find('rtl').length)
options['direction'] = 'rtl';
if (c.editor_spacing == 'single')
options.breakline = true;
el.data('redactor', el.redactor(options));
});
},
findRichtextBoxes = function() {
$('.richtext').each(function(i,el) {
if ($(el).hasClass('ifhtml'))
// Check if html_thread is enabled first
getConfig().then(function(c) {
if (c.html_thread)
redact(el);
});
else
// Make a rich text editor immediately
redact(el);
});
},
cleanupRedactorElements = function() {
// Tear down redactor editors on this page
$('.richtext').each(function() {
var redactor = $(this).data('redactor');
if (redactor)
redactor.stop();
});
};
findRichtextBoxes();
$(document).ajaxStop(findRichtextBoxes);
$(document).on('pjax:start', cleanupRedactorElements);
});
$(document).on('focusout.redactor', 'div.redactor_richtext', function (e) {
alert('focusout.redactor');
$(this).siblings('textarea').trigger('change');
});
$(document).ajaxError(function(event, request, settings) {
if (settings.url.indexOf('ajax.php/draft') != -1
&& settings.type.toUpperCase() == 'POST') {
$('.richtext').each(function() {
var redactor = $(this).data('redactor');
if (redactor) {
redactor.autosave.disable();
}
});
$.sysAlert(__('Unable to save draft.'),
__('Refresh the current page to restore and continue your draft.'));
}
});