hcornet 506716e703
Some checks failed
Deployment Verification / deploy-and-test (push) Failing after 29s
first sync
2025-03-04 07:59:21 +01:00

515 lines
19 KiB
JavaScript

var session_id = null ;
var collaborator = null ;
var buffer_dumped = false ;
var last_applied_change = null ;
var just_cleared_buffer = null ;
var from_sync = null;
var editor = ace.edit("editor_summary",
{
autoScrollEditorIntoView: true,
minLines: 4
});
var textarea = $('#case_summary');
function Collaborator( session_id ) {
this.collaboration_socket = io.connect() ;
this.channel = "case-" + session_id;
this.collaboration_socket.emit('join', { 'channel': this.channel });
this.collaboration_socket.on( "change", function(data) {
delta = JSON.parse( data.delta ) ;
console.log(delta);
last_applied_change = delta ;
$("#content_typing").text(data.last_change + " is typing..");
editor.getSession().getDocument().applyDeltas( [delta] ) ;
}.bind() ) ;
this.collaboration_socket.on( "clear_buffer", function() {
just_cleared_buffer = true ;
console.log( "setting editor empty" ) ;
editor.setValue( "" ) ;
}.bind() ) ;
this.collaboration_socket.on( "save", function(data) {
$("#content_last_saved_by").text("Last saved by " + data.last_saved);
sync_editor(true);
}.bind() ) ;
}
Collaborator.prototype.change = function( delta ) {
this.collaboration_socket.emit( "change", { 'delta': delta, 'channel': this.channel } ) ;
}
Collaborator.prototype.clear_buffer = function() {
this.collaboration_socket.emit( "clear_buffer", { 'channel': this.channel } ) ;
}
Collaborator.prototype.save = function() {
this.collaboration_socket.emit( "save", { 'channel': this.channel } ) ;
}
function body_loaded() {
collaborator = new Collaborator( get_caseid() ) ;
// registering change callback
from_sync = true;
editor.on( "change", function( e ) {
// TODO, we could make things more efficient and not likely to conflict by keeping track of change IDs
if( last_applied_change!=e && editor.curOp && editor.curOp.command.name) {
collaborator.change( JSON.stringify(e) ) ;
}
}, false );
editor.$blockScrolling = Infinity ;
document.getElementsByTagName('textarea')[0].focus() ;
last_applied_change = null ;
just_cleared_buffer = false ;
}
function handle_ed_paste(event) {
filename = null;
const { items } = event.originalEvent.clipboardData;
for (let i = 0; i < items.length; i += 1) {
const item = items[i];
if (item.kind === 'string') {
item.getAsString(function (s){
filename = $.trim(s.replace(/\t|\n|\r/g, '')).substring(0, 40);
});
}
if (item.kind === 'file') {
const blob = item.getAsFile();
if (blob !== null) {
const reader = new FileReader();
reader.onload = (e) => {
notify_success('The file is uploading in background. Don\'t leave the page');
if (filename === null) {
filename = random_filename(25);
}
upload_interactive_data(e.target.result, filename, function(data){
url = data.data.file_url + case_param();
event.preventDefault();
editor.insertSnippet(`\n![${filename}](${url} =40%x40%)\n`);
});
};
reader.readAsDataURL(blob);
} else {
notify_error('Unsupported direct paste of this item. Use datastore to upload.');
}
}
}
}
function report_template_selector() {
$('#modal_select_report').modal({ show: true });
}
function gen_report(safe) {
url = '/case/report/generate-investigation/' + $("#select_report option:selected").val() + case_param();
if (safe === true) {
url += '&safe=true';
}
window.open(url, '_blank');
}
function gen_act_report(safe) {
url = '/case/report/generate-activities/' + $("#select_report_act option:selected").val() + case_param();
if (safe === true) {
url += '&safe=true';
}
window.open(url, '_blank');
}
function act_report_template_selector() {
$('#modal_select_report_act').modal({ show: true });
}
function edit_case_summary() {
$('#container_editor_summary').toggle();
if ($('#container_editor_summary').is(':visible')) {
$('#ctrd_casesum').removeClass('col-md-12').addClass('col-md-6');
$('#summary_edition_btn').show(100);
$("#sum_refresh_btn").html('Save');
$("#sum_edit_btn").html('Close editor');
} else {
$('#ctrd_casesum').removeClass('col-md-6').addClass('col-md-12');
$('#summary_edition_btn').hide();
$("#sum_refresh_btn").html('Refresh');
$("#sum_edit_btn").html('Edit');
}
}
/* sync_editor
* Save the editor state.
* Check if there are external changes first.
* Copy local changes if conflict
*/
function sync_editor(no_check) {
$('#last_saved').text('Syncing..').addClass('badge-danger').removeClass('badge-success');
get_request_api('/case/summary/fetch')
.done((data) => {
if (data.status == 'success') {
if (no_check) {
// Set the content from remote server
from_sync = true;
editor.getSession().setValue(data.data.case_description);
// Set the CRC in page
$('#fetched_crc').val(data.data.crc32.toString());
$('#last_saved').text('Changes saved').removeClass('badge-danger').addClass('badge-success');
$('#content_last_sync').text("Last synced: " + new Date().toLocaleTimeString());
}
else {
// Check if content is different
st = editor.getSession().getValue();
if (data.data.crc32 != $('#fetched_crc').val()) {
// Content has changed remotely
// Check if we have changes locally
local_crc = crc32(st).toString();
console.log('Content changed. Local CRC is ' + local_crc);
console.log('Saved CRC is ' + $('#fetched_crc').val());
console.log('Remote CRC is ' + data.data.crc32);
if (local_crc == $('#fetched_crc').val()) {
// No local change, we can sync and update local CRC
editor.getSession().setValue(data.data.case_description);
$('#fetched_crc').val(data.data.crc32);
$('#last_saved').text('Changes saved').removeClass('badge-danger').addClass('badge-success');
$('#content_last_sync').text("Last synced: " + new Date().toLocaleTimeString());
} else {
// We have a conflict
$('#last_saved').text('Conflict !').addClass('badge-danger').removeClass('badge-success');
swal ( "Oh no !" ,
"We have a conflict with the remote content.\nSomeone may just have changed the description at the same time.\nThe local content will be copied into clipboard and content will be updated with remote." ,
"error"
).then((value) => {
// Old fashion trick
editor.selectAll();
editor.focus();
document.execCommand('copy');
editor.getSession().setValue(data.data.desc);
$('#fetched_crc').val(data.data.crc32);
notify_success('Content updated with remote. Local changes copied to clipboard.');
$('#content_last_sync').text("Last synced: " + new Date().toLocaleTimeString());
});
}
} else {
// Content did not change remotely
// Check local change
local_crc = crc32(st).toString();
if (local_crc != $('#fetched_crc').val()) {
console.log('Local change. Old CRC is ' + local_crc);
console.log('New CRC is ' + $('#fetched_crc').val());
var data = Object();
data['case_description'] = st;
data['csrf_token'] = $('#csrf_token').val();
// Local change detected. Update to remote
$.ajax({
url: '/case/summary/update' + case_param(),
type: "POST",
dataType: "json",
contentType: "application/json;charset=UTF-8",
data: JSON.stringify(data),
success: function (data) {
if (data.status == 'success') {
collaborator.save();
$('#content_last_sync').text("Last synced: " + new Date().toLocaleTimeString());
$('#fetched_crc').val(data.data);
$('#last_saved').text('Changes saved').removeClass('badge-danger').addClass('badge-success');
} else {
notify_error("Unable to save content to remote server");
$('#last_saved').text('Error saving !').addClass('badge-danger').removeClass('badge-success');
}
},
error: function(error) {
notify_error(error.responseJSON.message);
('#last_saved').text('Error saving !').addClass('badge-danger').removeClass('badge-success');
}
});
}
$('#content_last_sync').text("Last synced: " + new Date().toLocaleTimeString());
$('#last_saved').text('Changes saved').removeClass('badge-danger').addClass('badge-success');
}
}
}
});
}
is_typing = "";
function auto_remove_typing() {
if ($("#content_typing").text() == is_typing) {
$("#content_typing").text("");
} else {
is_typing = $("#content_typing").text();
}
}
function case_pipeline_popup() {
url = '/case/pipelines-modal' + case_param();
$('#info_case_modal_content').load(url, function (response, status, xhr) {
if (status !== "success") {
ajax_notify_error(xhr, url);
return false;
}
$('#modal_case_detail').modal({ show: true });
$("#update_pipeline_selector").selectpicker({
liveSearch: true,
style: "btn-outline-white"
})
$('#update_pipeline_selector').selectpicker("refresh");
$(".control-update-pipeline-args ").hide();
$('.control-update-pipeline-'+ $('#update_pipeline_selector').val() ).show();
$('#update_pipeline_selector').on('change', function(e){
$(".control-update-pipeline-args ").hide();
$('.control-update-pipeline-'+this.value).show();
});
$('[data-toggle="popover"]').popover();
});
}
async function do_case_review(action, reviewer_id) {
let data = Object();
data['csrf_token'] = $('#csrf_token').val();
data['action'] = action;
if (reviewer_id) {
data['reviewer_id'] = reviewer_id;
}
return post_request_api('/case/review/update', JSON.stringify(data));
}
function case_detail(case_id, edit_mode=false) {
url = '/case/details/' + case_id + case_param();
$('#info_case_modal_content').load(url, function (response, status, xhr) {
if (status !== "success") {
ajax_notify_error(xhr, url);
return false;
}
$('#modal_case_detail').modal({ show: true });
if (edit_mode) {
edit_case_info();
}
});
}
function manage_case(case_id) {
window.location = '/manage/cases?cid='+ case_id +'#view';
}
$(document).ready(function() {
if ($("#editor_summary").attr("data-theme") !== "dark") {
editor.setTheme("ace/theme/tomorrow");
} else {
editor.setTheme("ace/theme/iris_night");
}
editor.session.setMode("ace/mode/markdown");
editor.renderer.setShowGutter(true);
editor.setOption("showLineNumbers", true);
editor.setOption("showPrintMargin", false);
editor.setOption("displayIndentGuides", true);
editor.setOption("indentedSoftWrap", false);
editor.session.setUseWrapMode(true);
editor.setOption("maxLines", "Infinity")
editor.renderer.setScrollMargin(8, 5)
editor.setOption("enableBasicAutocompletion", true);
editor.commands.addCommand({
name: 'save',
bindKey: {win: "Ctrl-S", "mac": "Cmd-S"},
exec: function(editor) {
sync_editor(false);
}
})
editor.commands.addCommand({
name: 'bold',
bindKey: {win: "Ctrl-B", "mac": "Cmd-B"},
exec: function(editor) {
editor.insertSnippet('**${1:$SELECTION}**');
}
});
editor.commands.addCommand({
name: 'italic',
bindKey: {win: "Ctrl-I", "mac": "Cmd-I"},
exec: function(editor) {
editor.insertSnippet('*${1:$SELECTION}*');
}
});
editor.commands.addCommand({
name: 'head_1',
bindKey: {win: "Ctrl-Shift-1", "mac": "Cmd-Shift-1"},
exec: function(editor) {
editor.insertSnippet('# ${1:$SELECTION}');
}
});
editor.commands.addCommand({
name: 'head_2',
bindKey: {win: "Ctrl-Shift-2", "mac": "Cmd-Shift-2"},
exec: function(editor) {
editor.insertSnippet('## ${1:$SELECTION}');
}
});
editor.commands.addCommand({
name: 'head_3',
bindKey: {win: "Ctrl-Shift-3", "mac": "Cmd-Shift-3"},
exec: function(editor) {
editor.insertSnippet('### ${1:$SELECTION}');
}
});
editor.commands.addCommand({
name: 'head_4',
bindKey: {win: "Ctrl-Shift-4", "mac": "Cmd-Shift-4"},
exec: function(editor) {
editor.insertSnippet('#### ${1:$SELECTION}');
}
});
$('#editor_summary').on('paste', (event) => {
event.preventDefault();
handle_ed_paste(event);
});
var timer;
var timeout = 10000;
$('#editor_summary').keyup(function(){
if(timer) {
clearTimeout(timer);
}
timer = setTimeout(sync_editor, timeout);
});
//var textarea = $('#case_summary');
editor.getSession().on("change", function () {
//textarea.val(do_md_filter_xss(editor.getSession().getValue()));
$('#last_saved').text('Changes not saved').addClass('badge-danger').removeClass('badge-success');
let target = document.getElementById('targetDiv');
let converter = get_showdown_convert();
let html = converter.makeHtml(do_md_filter_xss(editor.getSession().getValue()));
target.innerHTML = do_md_filter_xss(html);
});
edit_case_summary();
body_loaded();
sync_editor(true);
setInterval(auto_remove_typing, 2000);
let review_state = $('#caseReviewState');
if (review_state.length > 0) {
let current_review_state = review_state.data('review-state');
if (current_review_state === 'Review in progress') {
$(".btn-start-review").hide();
$(".btn-confirm-review").show();
$(".btn-cancel-review").show();
$('#reviewSubtitle').text('You started this review. Press "Confirm review" when you are done.');
} else if (current_review_state === 'Review completed') {
$(".btn-start-review").hide();
$(".btn-confirm-review").hide();
$(".btn-cancel-review").hide();
} else if (current_review_state === 'Pending review') {
$(".btn-start-review").show();
$(".btn-confirm-review").hide();
$(".btn-cancel-review").hide();
}
$('.review-card').show();
}
$('.btn-start-review').on('click', function(e){
do_case_review('start').then(function(data) {
if (notify_auto_api(data)) {
location.reload();
}
});
});
$('.btn-confirm-review').on('click', function(e){
do_case_review('done').then(function(data) {
if (notify_auto_api(data)) {
location.reload();
}
});
});
$('.btn-cancel-review').on('click', function(e){
do_case_review('cancel').then(function(data) {
if (notify_auto_api(data)) {
location.reload();
}
});
});
$('#request_review').on('click', function(e){
let reviewer_id = $('#caseReviewState').data('reviewer-id');
let reviewer_name = $('#caseReviewState').data('reviewer-name');
if (reviewer_id !== "None") {
swal({
title: "Request review",
text: "Request a case review from " + reviewer_name + "?",
icon: "info",
buttons: true,
dangerMode: false,
}).then((willRequest) => {
if (willRequest) {
do_case_review('request', reviewer_id).then(function (data) {
if (notify_auto_api(data)) {
location.reload();
}
});
}
});
} else {
$('#reviewer_id').selectpicker({
liveSearch: true,
size: 10,
width: '100%'
});
get_request_api('/case/users/list')
.done((data) => {
if (notify_auto_api(data)) {
let users = data.data;
let options = '';
for (let i = 0; i < users.length; i++) {
if (users[i].user_access_level === 4) {
options += '<option value="' + users[i].user_id + '">' + filterXSS(users[i].user_name) + '</option>';
}
}
$('#reviewer_id').html(options);
$('#reviewer_id').selectpicker('refresh');
$('#modal_choose_reviewer').modal('show');
$('#submit_set_reviewer').off('click').on('click', function(e){
let reviewer_id = $('#reviewer_id').val();
do_case_review('request', reviewer_id).then(function (data) {
if (notify_auto_api(data)) {
location.reload();
}
});
});
}
});
}
});
});