Merge branch 'ux-release-1'

This commit is contained in:
Luka Murn 2018-05-28 15:59:53 +02:00
commit cc5b4b75cc
458 changed files with 13085 additions and 14671 deletions

View file

@ -1,8 +1,16 @@
env:
- DOCKER_COMPOSE_VERSION=1.13.0
sudo: required
language: ruby
install: true
services:
- docker
before_script:
before_install:
- sudo rm /usr/local/bin/docker-compose
- curl -L https://github.com/docker/compose/releases/download/${DOCKER_COMPOSE_VERSION}/docker-compose-`uname -s`-`uname -m` > docker-compose
- chmod +x docker-compose
- sudo mv docker-compose /usr/local/bin
- make docker
script:
- make tests-ci

View file

@ -9,11 +9,10 @@ gem 'pg', '~> 0.18'
gem 'devise', '~> 4.3.0'
gem 'devise_invitable'
gem 'simple_token_authentication', '~> 1.15.1' # Token authentication for Devise
gem 'bootstrap-sass', '~> 3.3.5'
gem 'bootstrap-sass', '~> 3.3.7'
gem 'sass-rails', '~> 5.0.6'
gem 'bootstrap_form'
gem 'yomu'
gem 'font-awesome-rails', '~> 4.7.0.2'
gem 'recaptcha', require: 'recaptcha/rails'
gem 'sanitize', '~> 4.4'
gem 'omniauth'
@ -27,7 +26,7 @@ gem 'momentjs-rails', '~> 2.17.1'
# JS datetime picker
gem 'bootstrap3-datetimepicker-rails', '~> 4.15.35'
# Select elements for Bootstrap
gem 'bootstrap-select-rails', '~> 1.6.3'
gem 'bootstrap-select-rails', '~> 1.12.4'
gem 'uglifier', '>= 1.3.0'
# jQuery & plugins
gem 'jquery-turbolinks'
@ -36,7 +35,6 @@ gem 'jquery-ui-rails'
gem 'jquery-scrollto-rails',
git: 'https://github.com/biosistemika/jquery-scrollto-rails'
gem 'hammerjs-rails'
gem 'introjs-rails' # Create quick tutorials
gem 'js_cookie_rails' # Simple JS API for cookies
gem 'spinjs-rails'
gem 'autosize-rails' # jQuery autosize plugin
@ -79,6 +77,8 @@ gem 'devise-async',
git: 'https://github.com/mhfs/devise-async.git',
branch: 'devise-4.x'
gem 'discard', '~> 1.0'
gem 'ruby-graphviz', '~> 1.2' # Graphviz for rails
gem 'tinymce-rails', '~> 4.6.4' # Rich text editor

View file

@ -1,10 +1,11 @@
GIT
remote: https://github.com/biosistemika/canaid
revision: f2000c19b75e66ea929a44cb0575262b7f5fc13e
revision: 943ae9b9801819fd2513f6ab9e1143ad8de523ce
branch: master
specs:
canaid (1.0.1)
canaid (1.0.2)
devise (>= 3.4.1)
docile (>= 1.1.0)
rails (>= 4)
GIT
@ -82,7 +83,7 @@ GEM
activemodel (= 5.1.1)
activesupport (= 5.1.1)
arel (~> 8.0)
activerecord-import (0.22.0)
activerecord-import (0.23.0)
activerecord (>= 3.2)
activesupport (5.1.1)
concurrent-ruby (~> 1.0, >= 1.0.2)
@ -127,7 +128,7 @@ GEM
bootstrap-sass (3.3.7)
autoprefixer-rails (>= 5.2.1)
sass (>= 3.3.4)
bootstrap-select-rails (1.6.3)
bootstrap-select-rails (1.12.4)
bootstrap3-datetimepicker-rails (4.15.35)
momentjs-rails (>= 2.8.1)
bootstrap_form (2.7.0)
@ -136,7 +137,7 @@ GEM
activesupport (>= 3.0.0)
uniform_notifier (~> 1.11.0)
byebug (9.1.0)
capybara (2.17.0)
capybara (2.18.0)
addressable
mini_mime (>= 0.1.3)
nokogiri (>= 1.3.3)
@ -203,6 +204,8 @@ GEM
actionmailer (>= 4.1.0)
devise (>= 4.0.0)
diff-lcs (1.3)
discard (1.0.0)
activerecord (>= 4.2, < 6)
docile (1.1.5)
erubi (1.7.0)
execjs (2.7.0)
@ -218,8 +221,6 @@ GEM
ffi (1.9.18)
figaro (1.1.1)
thor (~> 0.14)
font-awesome-rails (4.7.0.3)
railties (>= 3.2, < 5.2)
gherkin (5.0.0)
globalid (0.4.1)
activesupport (>= 4.2.0)
@ -229,9 +230,6 @@ GEM
concurrent-ruby (~> 1.0)
i18n-js (3.0.3)
i18n (~> 0.6, >= 0.6.6)
introjs-rails (1.0.0)
sass-rails (>= 3.2)
thor (~> 0.14)
jbuilder (2.7.0)
activesupport (>= 4.2.0)
multi_json (>= 1.2)
@ -297,7 +295,7 @@ GEM
rails (>= 3.2.0)
newrelic_rpm (4.7.1.340)
nio4r (2.2.0)
nokogiri (1.8.1)
nokogiri (1.8.2)
mini_portile2 (~> 2.3.0)
nokogumbo (1.4.13)
nokogiri
@ -343,9 +341,9 @@ GEM
pry (~> 0.10)
pry-rails (0.3.6)
pry (>= 0.10.4)
public_suffix (3.0.1)
public_suffix (3.0.2)
puma (3.11.2)
rack (2.0.3)
rack (2.0.4)
rack-test (0.6.3)
rack (>= 1.0)
rails (5.1.1)
@ -532,8 +530,8 @@ DEPENDENCIES
bcrypt (~> 3.1.10)
better_errors
binding_of_caller
bootstrap-sass (~> 3.3.5)
bootstrap-select-rails (~> 1.6.3)
bootstrap-sass (~> 3.3.7)
bootstrap-select-rails (~> 1.12.4)
bootstrap3-datetimepicker-rails (~> 4.15.35)
bootstrap_form
bullet
@ -551,13 +549,12 @@ DEPENDENCIES
devise-async!
devise_invitable
devise_security_extension!
discard (~> 1.0)
factory_bot_rails
faker
figaro
font-awesome-rails (~> 4.7.0.2)
hammerjs-rails
i18n-js (~> 3.0)
introjs-rails
jbuilder
jquery-rails
jquery-scrollto-rails!

View file

@ -1,6 +1,6 @@
Copyright (c) 2016 BioSistemika USA, LLC <info@biosistemika.com>
sciNote is licensed under the following license:
SciNote is licensed under the following license:
Mozilla Public License Version 2.0
==================================

View file

@ -7,7 +7,6 @@ SECRET_KEY_BASE=$(shell openssl rand -hex 64)
PAPERCLIP_HASH_SECRET=$(shell openssl rand -base64 128 | tr -d '\n')
DATABASE_URL=postgresql://postgres@db/scinote_production
PAPERCLIP_STORAGE=filesystem
ENABLE_TUTORIAL=true
ENABLE_RECAPTCHA=false
ENABLE_USER_CONFIRMATION=false
ENABLE_USER_REGISTRATION=true
@ -83,7 +82,10 @@ integration-tests:
@$(MAKE) rails cmd="bundle exec cucumber"
tests-ci:
@docker-compose run -e ENABLE_EMAIL_CONFIRMATIONS=false -e MAILER_PORT=$MAILER_PORT -e SMTP_DOMAIN=$SMTP_DOMAIN -e SMTP_USERNAME=$SMTP_USERNAME -e SMTP_PASSWORD=$SMTP_PASSWORD -e SMTP_ADDRESS=$SMTP_ADDRESS -e PAPERCLIP_HASH_SECRET=PAPERCLIP_HASH_SECRET -e MAIL_SERVER_URL=localhost -e PAPERCLIP_STORAGE=filesystem -e ENABLE_TUTORIAL=false -e ENABLE_RECAPTCHA=false -e ENABLE_USER_CONFIRMATION=false -e ENABLE_USER_REGISTRATION=true --rm web bash -c "bundle install && rake db:create db:migrate && rake db:migrate RAILS_ENV=test && yarn install && bundle exec rspec && bundle exec cucumber"
@docker-compose run --rm web bash -c "bundle install && npm install"
@docker-compose up -d webpack
@docker-compose ps
@docker-compose run -e ENABLE_EMAIL_CONFIRMATIONS=false -e MAILER_PORT=$MAILER_PORT -e SMTP_DOMAIN=$SMTP_DOMAIN -e SMTP_USERNAME=$SMTP_USERNAME -e SMTP_PASSWORD=$SMTP_PASSWORD -e SMTP_ADDRESS=$SMTP_ADDRESS -e PAPERCLIP_HASH_SECRET=PAPERCLIP_HASH_SECRET -e MAIL_SERVER_URL=localhost -e PAPERCLIP_STORAGE=filesystem -e ENABLE_RECAPTCHA=false -e ENABLE_USER_CONFIRMATION=false -e ENABLE_USER_REGISTRATION=true --rm web bash -c "rake db:create db:migrate && rake db:migrate RAILS_ENV=test && npm install && bundle exec rspec && bundle exec cucumber"
console:
@$(MAKE) rails cmd="rails console"

View file

@ -1,10 +1,10 @@
# sciNote
# SciNote
![sciNote logo](http://scinote.net/wp-content/uploads/2015/10/logo_sciNote_final.png)
![SciNote logo](http://scinote.net/wp-content/uploads/2015/10/logo_sciNote_final.png)
## About
[sciNote is an open source electronic lab notebook](https://scinote.net) ([ELN](https://en.wikipedia.org/wiki/Electronic_lab_notebook)) that helps you manage your laboratory work and stores all your experimental data in one place. sciNote is specifically designed for life science students, researchers, lab technicians and group leaders.
[SciNote is an open source electronic lab notebook](https://scinote.net) ([ELN](https://en.wikipedia.org/wiki/Electronic_lab_notebook)) that helps you manage your laboratory work and stores all your experimental data in one place. SciNote is specifically designed for life science students, researchers, lab technicians and group leaders.
## Build & run
@ -20,6 +20,6 @@ See [Wiki/Contributing & Collaboration](https://github.com/biosistemika/scinote-
## License
sciNote is developed and maintained by BioSistemika USA, LLC, under [Mozilla Public License Version 2.0](LICENSE.txt).
SciNote is developed and maintained by BioSistemika USA, LLC, under [Mozilla Public License Version 2.0](LICENSE.txt).
See [LICENSE-3RD-PARTY.txt](LICENSE-3RD-PARTY.txt) for licenses of included third-party libraries.

View file

Before

Width:  |  Height:  |  Size: 531 KiB

After

Width:  |  Height:  |  Size: 531 KiB

View file

Before

Width:  |  Height:  |  Size: 41 KiB

After

Width:  |  Height:  |  Size: 41 KiB

View file

Before

Width:  |  Height:  |  Size: 530 KiB

After

Width:  |  Height:  |  Size: 530 KiB

View file

Before

Width:  |  Height:  |  Size: 474 KiB

After

Width:  |  Height:  |  Size: 474 KiB

View file

Before

Width:  |  Height:  |  Size: 183 KiB

After

Width:  |  Height:  |  Size: 183 KiB

View file

Before

Width:  |  Height:  |  Size: 51 KiB

After

Width:  |  Height:  |  Size: 51 KiB

View file

Before

Width:  |  Height:  |  Size: 62 KiB

After

Width:  |  Height:  |  Size: 62 KiB

View file

Before

Width:  |  Height:  |  Size: 52 KiB

After

Width:  |  Height:  |  Size: 52 KiB

View file

Before

Width:  |  Height:  |  Size: 73 KiB

After

Width:  |  Height:  |  Size: 73 KiB

View file

Before

Width:  |  Height:  |  Size: 80 KiB

After

Width:  |  Height:  |  Size: 80 KiB

View file

@ -1,4 +1,4 @@
# Content for tables used in tutorial (demo project).
# Content for tables used in demo project.
module1:
experimental_design: '{"data":[["","","",null,null],["","","",null,null],["group/time","1 dpi","6 dpi","",""],["PVYNTN","1","1","",""],["mock","1","1","",""],[null,null,null,null,null]]}'

View file

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 991 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.8 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

View file

@ -0,0 +1,19 @@
'use strict';
(function() {
function initActivitiesButton() {
$(document).ready(function() {
$(document).find('.btn-more-activities')
.on('ajax:success', function(e, data) {
$(data.html).insertAfter($('#list-activities li').last());
if(data.more_url) {
$(this).attr('href', data.more_url);
} else {
$(this).remove();
}
});
});
}
initActivitiesButton();
})();

View file

@ -11,10 +11,10 @@
//= require jquery-ui/widgets/draggable
//= require jquery-ui/widgets/droppable
//= require jquery.ui.touch-punch.min
//= require jquery-ui/effects/effect-slide
//= require jquery.caret.min
//= require jquery.atwho.min
//= require hammer
//= require introjs
//= require js.cookie
//= require spin
//= require jquery.spin
@ -30,11 +30,11 @@
//= require tinymce-jquery
//= require jsPlumb-2.0.4-min
//= require jsnetworkx
//= require bootstrap-select
//= require_directory ./sitewide
//= require jquery.dataTables.yadcf
//= require datatables
//= require dataTables.noSearchHidden
//= require bootstrap-select
//= require ajax-bootstrap-select.min
//= require underscore
//= require i18n.js
//= require i18n/translations
@ -197,17 +197,20 @@ var HelperModule = (function(){
window.setTimeout(function () {
flash.fadeTo(500, 0).slideUp(500, function () {
$(this).remove();
if($('.alert').length <= 0) {
$('#content-wrapper').removeClass('alert-shown');
}
});
}, 5000);
}, 3000);
}
}
helpers.dismissAlert = function() {
$('#alert-flash').on('click', function() {
$('#alert-flash').alert('close');
})
}
helpers.flashAlertMsg = function(message, type) {
var alertType;
var glyphSign;
var alertType, glyphSign;
$('#notifications').html('');
if (type === 'success') {
alertType = ' alert-success ';
@ -222,34 +225,38 @@ var HelperModule = (function(){
alertType = ' alert-warning ';
glyphSign = ' glyphicon-exclamation-sign ';
}
var htmlSnippet = '<div class="alert alert' + alertType +
var htmlSnippet = '<div id="alert-flash" class="alert alert' + alertType +
'alert-dismissable alert-floating">' +
'<div class="container">' +
'<button type="button" class="close" ' +
'data-dismiss="alert" aria-label="Close">' +
'<div>' +
'<button type="button" class="close" ' +
'data-dismiss="alert" aria-label="Close">' +
'<span aria-hidden="true">×</span></button>' +
'<span class="glyphicon' + glyphSign + '"></span>' +
'<span class="glyphicon' + glyphSign + '"></span>&nbsp;' +
'<span>' + message + '</span>' +
'</div>' +
'</div>';
$('#notifications').html(htmlSnippet);
$('#content-wrapper').addClass('alert-shown');
helpers.hideFlashMsg();
helpers.dismissAlert();
}
$( document ).ready(function() {
helpers.treeLinkTruncation();
helpers.hideFlashMsg();
helpers.dismissAlert();
});
return helpers;
})();
// initialize code markup in rich text fields
(function() {
$(document).ready(function() {
// initialize code markup in rich text fields
$('[class^=language]').each(function(i, block) {
hljs.highlightBlock(block);
});
// fix dropdown-menu style throughout the app
$('.dropdown-header').parent('ul').addClass('custom-dropdown-menu');
});
})();

View file

@ -25,29 +25,22 @@ function setupAssetsLoading() {
$el.next().hide();
$el.html("");
if (data.type === "image") {
if (data.type === 'image') {
$el.html(
"<a class='image-preview-link' id='modal_link" +
"<a class='file-preview-link' id='modal_link" +
data['asset-id'] + "' data-status='asset-present' " +
"href='" + data['download-url'] + "'>" +
"<img src='" + data['image-tag-url'] + "' data-preview-url='" +
data['preview-url'] + "'><p>" +
"href='" + data['download-url'] + "' data-preview-url='" +
data['preview-url'] + "'>" +
"<img src='" + data['image-tag-url'] + "'><p>" +
data.filename + '</p></a>'
);
} else if (data.type === 'wopi') {
if (data['wopi-edit']) {
wopiBtns = data['wopi-file-name'] +
data['wopi-view'] +
data['wopi-edit'];
} else {
wopiBtns = data['wopi-file-name'] +
data['wopi-view'];
}
$el.html(wopiBtns);
} else {
$el.html(
"<a href='" + data['download-url'] + "'><p>" +
data.filename + "</p></a>"
"<a class='file-preview-link' id='modal_link" +
data['asset-id'] + "' data-status='asset-present' " +
"href='" + data['download-url'] + "' data-preview-url='" +
data['preview-url'] + "'><p>" +
data.filename + '</p></a>'
);
}
animateSpinner(null, false);

View file

@ -17,7 +17,6 @@ function init() {
initImport();
Comments.bindNewElement();
Comments.initialize();
initTutorial();
setupAssetsLoading();
}
@ -113,31 +112,6 @@ function bindEditDueDateAjax() {
});
}
/**
* Initializes tutorial
*/
function initTutorial() {
var stepNum = parseInt(Cookies.get('current_tutorial_step'), 10);
if (stepNum >= 12 && stepNum <= 14) {
var resultsTab = $('#results-nav-tab');
var nextPage = resultsTab.find('a').attr('href');
var steps = [{
intro: I18n.t('tutorial.module_protocols_html'),
}, {
element: $('#protocol-copy-to-repository')[0],
intro: I18n.t('tutorial.module_protocols_save_html'),
position: 'right'
}, {
element: resultsTab[0],
intro: I18n.t('tutorial.module_protocols_click_results_html'),
position: 'right'
}];
initPageTutorialSteps(12, 14, nextPage, function() {}, function() {},
steps);
}
}
function initCopyToRepository() {
var link = $("[data-action='copy-to-repository']");
var modal = $("#copy-to-repository-modal");
@ -320,6 +294,9 @@ function initLoadFromRepositoryTable(content) {
{ data: "5" },
{ data: "6" }
],
oLanguage: {
sSearch: I18n.t('general.filter')
},
rowCallback: function(row, data, dataIndex) {
// Get row ID
var rowId = data["DT_RowId"];

View file

@ -15,7 +15,6 @@
function init() {
initHandsOnTables($(document));
_expandAllResults();
_initTutorial();
applyCollapseLinkCallBack();
Comments.bindNewElement();
@ -130,28 +129,6 @@
}
}
/**
* Initializes tutorial
*/
function _initTutorial() {
var stepNum = parseInt(Cookies.get('current_tutorial_step'), 10);
if (stepNum >= 15 && stepNum <= 16) {
var samplesTab = $('#module-samples-nav-tab');
var nextPage = samplesTab.find('a').attr('href');
var steps = [{
element: $('#results-toolbar')[0],
intro: I18n.t('tutorial.module_results_html')
}, {
element: samplesTab[0],
intro: I18n.t('tutorial.module_results_click_samples_html'),
position: 'left'
}];
initPageTutorialSteps(15, 16, nextPage, function() {}, function() {},
steps);
}
}
function processResult(ev, resultTypeEnum, editMode) {
var $form = $(ev.target.form);
$form.clearFormErrors();

View file

@ -94,67 +94,9 @@
});
}
function focusSearchInput() {
var searchIco = $('#search-ico');
searchIco
.on('shown.bs.dropdown', function() {
searchIco
.find('input.form-control')
.focus();
});
}
function initActivitiesButton() {
$(document.body).ready(function() {
// Activity feed modal in main navigation menu
var activityModal = $('#activity-modal');
var activityModalBody = activityModal.find('.modal-body');
var initMoreBtn = function() {
activityModalBody.find('.btn-more-activities')
.on('ajax:success', function(e, data) {
$(data.html).insertBefore($(this).parents('li'));
if(data.more_url) {
$(this).attr('href', data.more_url);
} else {
$(this).remove();
}
});
};
notificationAlertClose();
$('#main-menu .btn-activity')
.on('ajax:before', function() {
activityModal.modal('show');
})
.on('ajax:success', function(e, data) {
activityModalBody.html(data.html);
initMoreBtn();
});
activityModal.on('hidden.bs.modal', function() {
activityModalBody.html('');
});
});
$(document).ajaxComplete(function() {
notificationAlertClose();
});
function notificationAlertClose() {
$('#notifications .alert').on('closed.bs.alert', function() {
$('#content-wrapper')
.addClass('alert-hidden')
.removeClass('alert-shown');
});
}
}
// init
loadDropdownNotifications();
loadUnseenNotificationsNumber();
toggleNotificationBellPosition();
focusSearchInput();
initGlobalSwitchForm();
initActivitiesButton();
})();

View file

@ -116,9 +116,6 @@ var ignoreUnsavedWorkAlert;
// Global variable for hammer js
var hammertime;
// Cookie data for tutorial
var tutorialData;
/*
* As a guideline, all module elements should contain
* the following attributes:
@ -146,7 +143,6 @@ function init() {
bindWindowResizeEvent();
initializeGraph(".diagram .module-large");
initializeFullZoom();
initTutorial();
}
jsPlumb.ready(function () {
@ -174,7 +170,7 @@ function initializeEdit() {
// Hide sidebar & also its toggle button
$("#wrapper").addClass("hidden2");
$("#wrapper").find(".sidebar-header-toggle").hide();
$("#toggle-sidebar-btn").addClass("hidden2");
$(".navbar-secondary").addClass("navbar-without-sidebar");
// Also, hide zoom levels button group
@ -331,7 +327,6 @@ function destroyFullZoom() {
$(".module-large .buttons-container [role=tab]").off("ajax:before ajax:success ajax:error");
$("div.module-large").off("mouseenter mouseleave");
$("div.module-large a.due-date-link").off("ajax:success ajax:error");
$("#manage-module-description-modal [data-action='submit']").off("click");
$("#manage-module-due-date-modal [data-action='submit']").off("click");
$("div.module-large a.edit-tags-link").off("ajax:before ajax:success");
$("li[data-module-group]").off("mouseenter mouseleave");
@ -597,46 +592,6 @@ function bindWindowResizeEvent() {
function bindFullZoomAjaxTabs() {
var manageUsersModal = null;
var manageUsersModalBody = null;
var editDescriptionModal = null;
var editDescriptionModalBody = null;
// Initialize edit description modal window
function initEditDescription($el) {
$el.find(".description-link")
.on("ajax:success", function(ev, data, status) {
var descriptionLink = $(this);
var descriptionTab = descriptionLink.closest(".tab-pane");
// Set modal body & title
editDescriptionModalBody.html(data.html);
editDescriptionModal
.find("#manage-module-description-modal-label")
.text(data.title);
editDescriptionModalBody.find("form")
.on("ajax:success", function(ev2, data2, status2) {
// Update module's description in the tab
descriptionTab.find(".description-label")
.html(data2.description_label);
// Close modal
editDescriptionModal.modal("hide");
})
.on("ajax:error", function(ev2, data2, status2) {
// Display errors if needed
$(this).renderFormErrors("my_module", data2.responseJSON);
});
// Disable canvas dragging events
toggleCanvasEvents(false);
// Show modal
editDescriptionModal.modal("show");
})
.on("ajax:error", function(ev, data, status) {
// TODO
});
}
// Initialize users editing modal remote loading.
function initUsersEditLink($el) {
@ -680,8 +635,6 @@ function bindFullZoomAjaxTabs() {
manageUsersModal = $("#manage-module-users-modal");
manageUsersModalBody = manageUsersModal.find(".modal-body");
editDescriptionModal = $("#manage-module-description-modal");
editDescriptionModalBody = editDescriptionModal.find(".modal-body");
// Reload users tab HTML element when modal is closed
manageUsersModal.on("hide.bs.modal", function () {
@ -709,21 +662,6 @@ function bindFullZoomAjaxTabs() {
manageUsersModalBody.html("");
});
// When clicking on description modal "Update" button,
// submit its inner-lying form
editDescriptionModal.find("[data-action='submit']").click(function() {
editDescriptionModalBody.find("form").submit();
});
// Remove description modal content when window is closed
editDescriptionModal.on("hidden.bs.modal", function() {
$(this).find("form").off("ajax:success ajax:error");
editDescriptionModalBody.html("");
// Re-activate canvas dragging events
toggleCanvasEvents(true);
});
// initialize my_module tab remote loading
var elements = $(".module-large .buttons-container [role=tab]");
elements.on("ajax:before", function (e) {
@ -1189,6 +1127,7 @@ function updateModuleHtml(module, id, name, gridDistX, gridDistY) {
var dropdownMenu = document.createElement("ul");
$(dropdownMenu)
.addClass("dropdown-menu")
.addClass("custom-dropdown-menu")
.addClass("no-scale")
.attr("aria-labelledby", id + "_options")
.appendTo(dropdown);
@ -1667,7 +1606,7 @@ function initMoveModules() {
})
.on("shown.bs.modal", function(event) {
// Focus the text element
$(this).find(".selectpicker").focus();
$(this).find(".selectpicker").selectpicker().focus();
})
.on("hide.bs.modal", function (event) {
// When hiding modal, re-enable events
@ -1766,7 +1705,7 @@ function initMoveModuleGroups() {
})
.on("shown.bs.modal", function(event) {
// Focus the text element
$(this).find(".selectpicker").focus();
$(this).find(".selectpicker").selectpicker().focus();
})
.on("hide.bs.modal", function (event) {
// When hiding modal, re-enable events
@ -2999,117 +2938,6 @@ function initJsPlumb(containerSel, containerChildSel, modulesSel, params) {
}
})();
/**
* Initializes tutorial
*/
function initTutorial() {
var tutorialData = Cookies.get('tutorial_data');
if (tutorialData) {
tutorialData = JSON.parse(tutorialData);
var stepNum = parseInt(Cookies.get('current_tutorial_step'), 10);
if (stepNum >= 6 && stepNum <= 7) {
var nextPage = window.location.pathname;
var steps = [{
element: $('#canvas-container')[0],
intro: I18n.t('tutorial.canvas_overview_html'),
position: 'left'
}, {
element: $('#edit-canvas-button')[0],
intro: I18n.t('tutorial.canvas_click_edit_workflow_html'),
position: 'left'
}];
initPageTutorialSteps(6, 7, nextPage, tutorialBeforeCb, tutorialAfterCb,
steps);
} else if (stepNum >= 8 && stepNum <= 9) {
// Go into edit canvas mode
$('.introjs-overlay').remove();
$('.introjs-helperLayer').remove();
$('.introjs-tooltipReferenceLayer').remove();
$('#edit-canvas-button').click();
setTimeout(function() {
var nextPage = window.location.pathname;
var steps = [{
intro: I18n.t('tutorial.edit_workflow_html'),
element: $('#canvas-new-module')[0],
position: 'right'
}, {
intro: I18n.t('tutorial.edit_workflow_click_save_html'),
element: $('#canvas-save')[0],
position: 'right'
}];
initPageTutorialSteps(8, 9, nextPage, tutorialBeforeCb, function() {
// Go out of edit canvas mode
$('.cancel-edit-canvas').click();
tutorialAfterCb();
}, steps);
}, 1000);
} else if (stepNum >= 10 && stepNum <= 11) {
var qpcrModuleLeaf =
$("li.leaf[data-module-id='" + tutorialData[0].qpcr_module + "']");
var nextPage = qpcrModuleLeaf.find('a.module-link').attr('href');
var steps = [{
element: $('#slide-panel .tree')[0],
intro: I18n.t('tutorial.sidebar_html'),
position: 'right'
}, {
element: qpcrModuleLeaf[0],
intro: I18n.t('tutorial.sidebar_click_module_html'),
position: 'right'
}];
initPageTutorialSteps(10, 11, nextPage, tutorialBeforeCb,
tutorialAfterCb, steps);
}
}
}
/**
* Callback to be executed before tutorial starts
*/
function tutorialBeforeCb() {
$('#slide-panel')
.css({'pointer-events': 'none'});
$('#canvas-new-module')
.css({'pointer-events': 'none'});
$.each($('.panel-default'), function(i, el) {
$(el)
.find('.tab-pane')
.css({'pointer-events': 'none'});
$(el)
.find('.edit-tags-link')
.css({'pointer-events': 'none'});
$(el)
.find('.panel-heading')
.css({'pointer-events': 'none'});
});
}
/**
* Callback to be executed after tutorial exits
*/
function tutorialAfterCb() {
$('#slide-panel')
.css({'pointer-events': 'auto'});
$('#canvas-new-module')
.css({'pointer-events': 'auto'});
$.each($('.panel-default'), function(i, el) {
$(el)
.find('.tab-pane')
.css({'pointer-events': 'auto'});
$(el)
.find('.edit-tags-link')
.css({'pointer-events': 'auto'});
$(el)
.find('.panel-heading')
.css({'pointer-events': 'auto'});
});
}
/** prevent reload page */
var preventCanvasReloadOnSave = (function() {
'use strict';

View file

@ -102,8 +102,6 @@
.on('ajax:error', function() {
// TODO
});
initTutorial();
}
/**
@ -335,131 +333,5 @@
initUserRoleForms();
}
/**
* Initializes tutorial
*/
function initTutorial() {
var tutorialData = Cookies.get('tutorial_data');
if (tutorialData) {
tutorialData = JSON.parse(tutorialData);
var stepNum = parseInt(Cookies.get('current_tutorial_step'), 10);
if (isNaN(stepNum)) {
// Cookies data initialization
stepNum = 1;
Cookies.set('current_tutorial_step', stepNum);
tutorialData[0].backPagesPaths = [];
Cookies.set('tutorial_data', tutorialData);
}
var demoProjectId = tutorialData[0].project;
var demoProject = $('#' + demoProjectId);
if (stepNum >= 1 && stepNum <= 3) {
var thirdStepPos = (demoProject.offset().top > window.innerHeight / 2) ?
'top' : 'bottom';
var nextPage = $('#' + demoProjectId + '-project-canvas-link')
.attr('href');
var steps = [{
element: $('#projects-toolbar')[0],
intro: I18n.t('tutorial.tutorial_welcome_title_html'),
position: 'bottom'
}, {
element: $('#new-project-btn')[0],
intro: I18n.t('tutorial.create_project_html'),
position: 'left'
}, {
element: demoProject[0],
intro: I18n.t('tutorial.project_options_html'),
position: thirdStepPos
}];
initPageTutorialSteps(1, 3, nextPage, tutorialBeforeCb,
tutorialAfterCb, steps);
} else if (stepNum === 22) {
var protocolLink = $('#protocol-link');
var nextPage = protocolLink.attr('href');
var steps = [{
element: protocolLink[0],
intro: I18n.t('tutorial.protocols_link_html'),
position: 'left'
}];
initPageTutorialSteps(22, 22, nextPage, function() {}, function() {},
steps);
} else if (stepNum >= 25 && stepNum <= 26) {
var firstStepPos = 'right';
if (demoProject.offset().left > window.innerWidth / 2 ||
window.innerWidth < demoProject.width() + 100) {
if (demoProject.offset().top > 500 && demoProject.offset().top >
window.innerHeight / 2) {
firstStepPos = 'top';
} else {
firstStepPos = 'bottom';
}
}
var nextPage = $('#new-report-btn').attr('href');
var steps = [{
element: demoProject[0],
intro: I18n.t('tutorial.archive_project_html'),
position: firstStepPos
}, {
element: $('.avatar')[0],
intro: I18n.t('tutorial.goodbye_message'),
position: 'left'
}];
initPageTutorialSteps(25, 26, nextPage, function() {}, function() {},
steps);
}
}
}
/**
* Callback to be executed before tutorial starts
*/
function tutorialBeforeCb() {
$.each($('.panel'), function(i, el){
$(el)
.find('.panel-title')
.css({ 'pointer-events': 'none' });
$(el)
.find('.tab-content')
.css({ 'pointer-events': 'none' });
$(el)
.find('.form-submit-link')
.css({
'pointer-events': 'none',
'color': '<%= Constants::COLOR_ALTO %>'});
$(el)
.find("[data-action='edit']")
.css({
'pointer-events': 'none',
'color': '<%= Constants::COLOR_ALTO %>'});
});
}
/**
* Callback to be executed after tutorial exits
*/
function tutorialAfterCb() {
$.each($('.panel'), function(i, el){
$(el)
.find('.tab-content')
.css({ 'pointer-events': 'auto' });
$(el)
.find('.panel-title')
.css({ 'pointer-events': 'auto' });
$(el)
.find('.form-submit-link')
.css({
'pointer-events': 'auto',
'color': '<%= Constants::COLOR_NERO %>'});
$(el)
.find("[data-action='edit']")
.css({
'pointer-events': 'auto',
'color': '<%= Constants::COLOR_NERO %>'});
});
}
init();
}());

View file

@ -6,8 +6,6 @@
var that = $(this);
initProjectExperiment(that);
});
initTutorial();
}
function initProjectExperiment(element){
@ -64,58 +62,5 @@
});
}
/**
* Initializes tutorial
*/
function initTutorial() {
var stepNum = parseInt(Cookies.get('current_tutorial_step'), 10);
if (stepNum >= 4 && stepNum <= 5) {
var nextPage = $('[data-canvas-link]').data('canvasLink');
var steps = [{
element: $('#new-experiment')[0],
intro: I18n.t('tutorial.tutorial_welcome_title_html'),
position: 'left'
}, {
element: $('.experiment-panel')[0],
intro: I18n.t('tutorial.edit_experiment_html'),
position: 'right'
}];
initPageTutorialSteps(4, 5, nextPage, tutorialBeforeCb, tutorialAfterCb,
steps);
}
}
/**
* Callback to be executed before tutorial starts
*/
function tutorialBeforeCb() {
$.each( $(".panel-title"), function(){
$(this).css({ "pointer-events": "none" });
});
$.each( $(".workflowimg-container"), function(){
$(this).css({ "pointer-events": "none" });
});
$.each( $(".dropdown-experiment-actions").find("li"),
function(){
$(this).css({ "pointer-events": "none" });
});
}
/**
* Callback to be executed after tutorial exits
*/
function tutorialAfterCb() {
$.each( $(".panel-title"), function(){
$(this).css({ "pointer-events": "auto" });
});
$.each( $(".workflowimg-container"), function(){
$(this).css({ "pointer-events": "auto" });
});
$.each( $(".dropdown-experiment-actions").find("li"),
function(){
$(this).css({ "pointer-events": "auto" });
});
}
init();
})();

View file

@ -19,7 +19,6 @@ function init() {
initCreateNewModal();
initModals();
initImport();
initTutorial();
}
// Initialize protocols DataTable
@ -66,6 +65,9 @@ function initProtocolsTable() {
{ data: "5" },
{ data: "6" }
],
oLanguage: {
sSearch: I18n.t('general.filter')
},
rowCallback: function(row, data, dataIndex) {
// Get row ID
var rowId = data["DT_RowId"];
@ -770,25 +772,4 @@ function initImport() {
});
}
/**
* Initializes tutorial
*/
function initTutorial() {
var stepNum = parseInt(Cookies.get('current_tutorial_step'), 10);
if (stepNum >= 23 && stepNum <= 24) {
var nextPage = $('.navbar-brand').attr('href');
var steps = [{
element: $('.nav-settings')[0],
intro: I18n.t('tutorial.protocols_index_html'),
position: 'bottom'
}, {
element: $('#import-export-protocols')[0],
intro: I18n.t('tutorial.protocols_import_export_html'),
position: 'bottom'
}];
initPageTutorialSteps(23, 24, nextPage, function() {}, function() {},
steps);
}
}
init();

View file

@ -43,7 +43,7 @@
var task_button = $("[data-action='complete-task']");
task_button.attr('data-action', 'uncomplete-task');
task_button.find('.btn')
.removeClass('btn-primary').addClass('btn-greyed');
.removeClass('btn-toggle').addClass('btn-default');
$('.task-due-date').html(data.module_header_due_date_label);
$('.task-state-label').html(data.module_state_label);
task_button
@ -80,7 +80,8 @@
button = step.find("[data-action='complete-step']");
button.attr("data-action", "uncomplete-step");
button.find(".btn").removeClass("btn-primary").addClass("btn-default");
button.find(".btn").removeClass("btn-toggle").addClass("btn-default");
button.find("button").html('<span class="glyphicon glyphicon-remove"></span>&nbsp;' + data.new_title);
if (data.task_ready_to_complete) {
$('#completed-task-modal').modal('show');
@ -92,10 +93,9 @@
button = step.find("[data-action='uncomplete-step']");
button.attr("data-action", "complete-step");
button.find(".btn").removeClass("btn-default").addClass("btn-primary");
button.find(".btn").removeClass("btn-default").addClass("btn-toggle");
button.find("button").html('<span class="glyphicon glyphicon-ok"></span>&nbsp;' + data.new_title);
}
button.find("button").html(data.new_title);
},
error: function (data) {
console.log ("error");
@ -462,7 +462,7 @@
function enableCheckboxSorting(el) {
Sortable.create(el, {
draggable: 'fieldset',
handle: '.glyphicon-chevron-right',
handle: '.fa-circle',
onUpdate: function () {
reorderCheckboxData(el);
}
@ -595,6 +595,7 @@
animateSpinner(null, false);
setupAssetsLoading();
DragNDropSteps.clearFiles();
initPreviewModal();
},
error: function(xhr) {
if (xhr.responseJSON['assets.file']) {

View file

@ -1,186 +0,0 @@
(function () {
var newReportModal = null;
var newReportModalBody = null;
var newReportCreateButton = null;
var deleteReportsModal = null;
var deleteReportsInput = null;
var newReportButton = null;
var editReportButton = null;
var deleteReportsButton = null;
var checkAll = null;
var allChecks = null;
var allRows = null;
var checkedReports = [];
/**
* Initializes page
*/
function init() {
// Initialize selectors
newReportModal = $('#new-report-modal');
newReportModalBody = newReportModal.find('.modal-body');
newReportCreateButton = $('#create-new-report-btn');
deleteReportsModal = $('#delete-reports-modal');
deleteReportsInput = $('#report-ids');
newReportButton = $('#new-report-btn');
editReportButton = $('#edit-report-btn');
deleteReportsButton = $('#delete-reports-btn');
checkAll = $('.check-all-reports');
allChecks = $('.check-report');
allRows = $('.report-row');
initNewReportModal();
initCheckboxesAndEditing();
updateButtons();
initEditReport();
initDeleteReports();
initTutorial();
}
/**
* Initialize the new report modal.
*/
function initNewReportModal() {
// TEMPORARY DISABLED
/**
// Remove modal content when modal window is closed.
newReportModal.on("hidden.bs.modal", function () {
newReportModalBody.html("");
});
// Populate modal content when AJAX call is complete
newReportButton
.on("ajax:before", function () {
newReportModal.modal('show');
})
.on("ajax:success", function (e, data) {
newReportModalBody.html(data.html);
});
// Before redirecting, pass parameters
newReportCreateButton.click(function(event){
var url = $(this).closest("form").attr("action");
event.preventDefault();
// Copy the GET params
var val = newReportModalBody.find(".btn-primary.active > input[type='radio']").attr("value");
url += "/" + val;
$(location).attr("href", url);
return false;
});
*/
}
/**
* Initialize interaction between checkboxes, editing and deleting.
*/
function initCheckboxesAndEditing() {
checkAll.click(function() {
allChecks.prop("checked", this.checked);
checkedReports = [];
if (this.checked) {
_.each(allRows, function(row) {
checkedReports.push($(row).data("id"));
});
}
updateButtons();
});
allChecks.click(function() {
checkAll.prop("checked", false);
var id = $(this).closest(".report-row").data("id");
if (this.checked) {
if (_.indexOf(checkedReports, id) === -1) {
checkedReports.push(id);
}
} else {
var idx = _.indexOf(checkedReports, id);
if (idx !== -1) {
checkedReports.splice(idx, 1);
}
}
updateButtons();
});
}
/**
* Update edit & delete buttons depending on checking of reports.
*/
function updateButtons() {
if (checkedReports.length === 0) {
editReportButton.addClass("disabled");
deleteReportsButton.addClass("disabled");
} else if (checkedReports.length === 1) {
editReportButton.removeClass("disabled");
deleteReportsButton.removeClass("disabled");
} else {
editReportButton.addClass("disabled");
deleteReportsButton.removeClass("disabled");
}
}
/**
* Initialize the edit report functionality.
*/
function initEditReport() {
editReportButton.click(function(e) {
animateLoading();
if (checkedReports.length === 1) {
var id = checkedReports[0];
var row = $(".report-row[data-id='" + id + "']");
var url = row.data("edit-link");
$(location).attr("href", url);
}
return false;
});
}
/**
* Initialize the deleting of reports.
*/
function initDeleteReports() {
deleteReportsButton.click(function(e) {
if (checkedReports.length > 0) {
// Copy the checked IDs into the hidden input
deleteReportsInput.attr("value", "[" + checkedReports + "]");
// Show modal
deleteReportsModal.modal("show");
}
});
$("#confirm-delete-reports-btn").click(function(e) {
animateLoading();
});
}
/**
* Initializes tutorial
*/
function initTutorial() {
var stepNum = parseInt(Cookies.get('current_tutorial_step'), 10);
if (stepNum === 19) {
var newReportBtn = $('#new-report-btn');
var nextPage = newReportBtn.attr('href');
var steps = [{
element: newReportBtn[0],
intro: I18n.t('tutorial.reports_index_click_new_report_html'),
position: 'right'
}];
initPageTutorialSteps(19, 19, nextPage, function() {}, function() {},
steps);
}
}
$(document).ready(init);
}());

View file

@ -5,7 +5,6 @@ $.fn.findWithSelf = function(selector) {
};
var REPORT_CONTENT = "#report-content";
var SIDEBAR_PARENT_TREE = "#report-sidebar-tree";
var ADD_CONTENTS_FORM_ID = "#add-contents-form";
var SAVE_REPORT_FORM_ID = "#save-report-form";
@ -31,13 +30,14 @@ var ignoreUnsavedWorkAlert;
initializeSaveToPdf();
initializeSaveReport();
initializeAddContentsModal();
initializeSidebarNavigation();
initializeUnsavedWorkDialog();
initTutorial();
$('.report-nav-link').each(function() {
truncateLongString($(this), <%= Constants::NAME_TRUNCATION_LENGTH %>);
});
// Automatically display the "Add content" modal
$('.new-element.initial').click();
}
/**
@ -239,9 +239,6 @@ function initializeNewElement(newEl) {
} else if (data.status == 200) {
// Add elements
addElements(el, data.responseJSON.elements);
// Update sidebar
initializeSidebarNavigation();
}
})
.on("ajax:error", function(e, xhr, settings, error) {
@ -487,140 +484,6 @@ function initializeUnsavedWorkDialog() {
$(document).on('page:before-change', beforeUnload);
}
/**
* SIDEBAR CODE
*/
/**
* Get the sidebar <li> element for the specified report element.
* @param reportEl - The .report-element in the report.
* @return The corresponding sidebar <li>.
*/
function getSidebarEl(reportEl) {
var type = reportEl.data("type");
var scrollId = reportEl.data("scroll-id");
return $(SIDEBAR_PARENT_TREE).find(
"li" +
"[data-type='" + type + "']" +
"[data-scroll-id='" + scrollId + "']"
);
}
/**
* Get the report <div.report-element> element for the specified
* sidebar element.
* @param sidebarEl - The <li> sidebar element.
* @return The corresponding report element.
*/
function getReportEl(sidebarEl) {
var type = sidebarEl.data("type");
var scrollId = sidebarEl.data("scroll-id");
return $(REPORT_CONTENT).find(
"div.report-element" +
"[data-type='" + type + "']" +
"[data-scroll-id='" + scrollId + "']"
);
}
/**
* Initialize the sidebar navigation pane.
*/
function initializeSidebarNavigation() {
var reportContent = $(REPORT_CONTENT);
var treeParent = $(SIDEBAR_PARENT_TREE);
// Remove existing contents (also remove click listeners)
treeParent.find(".report-nav-link").off("click");
treeParent.children().remove();
// Re-populate the sidebar
_.each(reportContent.children(".report-element"), function(child) {
var li = initSidebarElement($(child));
li.appendTo(treeParent);
});
// Add click listener on all links
treeParent.find(".report-nav-link").click(function(e) {
var el = $(this).closest("li");
scrollToElement(el);
e.preventDefault();
e.stopPropagation();
return false;
});
// Call to sidebar function to re-initialize tree functionality
setupSidebarTree();
}
/**
* Recursive call to initialize sidebar elements.
* @param reportEl - The report element for which to
* generate the sidebar.
* @return A <li> jQuery element containing sidebar entry.
*/
function initSidebarElement(reportEl) {
var elChildrenContainer = reportEl.children(".report-element-children");
var type = reportEl.data("type");
var name = reportEl.data("name");
var scrollId = reportEl.data("scroll-id");
var iconClass = reportEl.data("icon-class");
// Generate list element
var newLi = $(document.createElement("li"));
newLi
.attr("data-type", type)
.attr("data-scroll-id", scrollId);
var newSpan = $(document.createElement("span"));
newSpan.appendTo(newLi);
var newI = $(document.createElement("i"));
newI.appendTo(newSpan);
var newHref = $(document.createElement("a"));
newHref
.attr("href", "")
.addClass("report-nav-link")
.text(name)
.appendTo(newSpan);
var newIcon = $(document.createElement("span"));
newIcon.addClass(iconClass).prependTo(newHref);
if (elChildrenContainer.length && elChildrenContainer.length > 0) {
var elChildren = elChildrenContainer.children(".report-element");
if (elChildren.length && elChildren.length > 0) {
var newUl = $(document.createElement("ul"));
newUl.appendTo(newLi);
_.each(elChildren, function(child) {
var li = initSidebarElement($(child));
li.appendTo(newUl);
});
}
}
return newLi;
}
/**
* Scroll to the specified element in the report.
* @param sidebarEl - The sidebar element.
*/
function scrollToElement(sidebarEl) {
var el = getReportEl(sidebarEl);
if (el.length && el.length == 1) {
var content = $("body");
content.scrollTo(
el,
{
axis: 'y',
duration: 500,
offset: -150
}
);
}
}
/**
* INDIVIDUAL ELEMENTS SORTING/MODIFYING FUNCTIONS
*/
@ -684,8 +547,6 @@ function sortWholeReport(asc) {
sortElementChildren($(el), asc, true);
});
// Reinitialize sidebar
initializeSidebarNavigation();
animateLoading(false);
}
@ -735,22 +596,6 @@ function sortElementChildren(el, asc, recursive) {
sortElementChildren($(child), asc, true);
}
});
// Update sidebar
var prevEl = null;
_.each(children, function(child) {
var sidebarEl = getSidebarEl($(child));
if (sidebarEl.length && sidebarEl.length == 1) {
var sidebarParent = sidebarEl.closest("ul");
sidebarEl.detach();
if (prevEl === null) {
sidebarParent.prepend(sidebarEl);
} else {
prevEl.after(sidebarEl);
}
prevEl = sidebarEl;
}
});
}
/**
@ -845,38 +690,24 @@ function moveElement(el, up) {
return;
}
var sidebarEl;
if (up) {
var prevEl = prevNewEl.prev();
if (!prevEl.length || !prevEl.hasClass("report-element")) {
return;
}
// Move sidebar element up
sidebarEl = getSidebarEl(el);
var sidebarPrev = sidebarEl.prev();
sidebarEl.detach();
sidebarPrev.before(sidebarEl);
el.detach();
nextNewEl.detach();
prevEl.before(el);
prevEl.before(nextNewEl);
updateElementControls(prevEl);
} else {
var nextEl = nextNewEl.next();
if (!nextEl.length || !nextEl.hasClass("report-element")) {
return;
}
// Move sidebar element up
sidebarEl = getSidebarEl(el);
var sidebarNext = sidebarEl.next();
sidebarEl.detach();
sidebarNext.after(sidebarEl);
prevNewEl.detach();
el.detach();
nextEl.after(el);
@ -906,10 +737,6 @@ function removeElement(el) {
// TODO Remove event listeners
// Remove sidebar entry
var sidebarEl = getSidebarEl(el);
sidebarEl.remove();
prevNewEl.remove();
el.remove();
@ -934,10 +761,6 @@ function removeResultCommentsElement(el) {
// TODO Remove event listeners
// Remove sidebar entry
var sidebarEl = getSidebarEl(el);
sidebarEl.remove();
// Remove element, show the new element container
el.remove();
parent.children(".new-element").removeClass("hidden");
@ -1126,52 +949,6 @@ function constructElementContentsJson(el) {
return jsonEl;
}
/**
* Binds listeners to sidebar
* that truncate long strings
*/
function initializeReportSidebartruncation() {
var target = document.getElementById("report-sidebar-tree");
var observer = new MutationObserver(
function() {
$.each($("a.report-nav-link"),
function(){
truncateLongString($(this),
<%= Constants::NAME_TRUNCATION_LENGTH %>);
});
}
);
var config = { childList: true };
observer.observe(target, config);
}
/**
* Initializes tutorial
*/
function initTutorial() {
var stepNum = parseInt(Cookies.get('current_tutorial_step'), 10);
if (stepNum >= 20 && stepNum <= 21) {
ignoreUnsavedWorkAlert = true;
var nextPage = $('.navbar-brand').attr('href');
var steps = [{
element: $('.new-element')[0],
intro: I18n.t('tutorial.new_report_html'),
position: 'left'
}, {
element: $('#team-link')[0],
intro: I18n.t('tutorial.new_report_click_team_html',
{ private_team: $('#team-link span').last().text() }),
position: 'left'
}];
initPageTutorialSteps(20, 21, nextPage, function() {}, function() {
ignoreUnsavedWorkAlert = false;
},
steps);
}
}
$(document).ready(function() {
// Check if we are actually at new report page
if ($(REPORT_CONTENT).length) {

View file

@ -0,0 +1,202 @@
(function(global) {
'use strict';
var DATATABLE;
var CHECKED_REPORTS = [];
function tableDrowCallback() {
checkboxToggleCallback();
initToggleAllCheckboxes();
updateButtons();
}
function initSelectPicker() {
$('.selectpicker').selectpicker({liveSearch: true})
.ajaxSelectPicker({
ajax: {
url: '<%= Rails.application.routes.url_helpers.reports_visible_projects_path %>',
type: 'POST',
dataType: 'json',
data: function () {
return { q: '{{{q}}}' };
}
},
locale: {
emptyTitle: 'Nothing selected'
},
preprocessData: appendSearchResults,
emptyRequest: true,
clearOnEmpty: false,
preserveSelected: false
}).on('change.bs.select', function(el) {
$('#new-report-reports-btn').attr('data-new-report-path', el.target.value);
}).on('loaded.bs.select', function(el) {
$('#new-report-reports-btn').attr('data-new-report-path', el.target.value);
});
}
function appendSearchResults(data) {
var items = [];
if(data.hasOwnProperty('projects')){
$.each(data.projects, function(index, el) {
items.push(
{
'value': el.path,
'text': el.name,
'disabled': false
}
)
});
}
return items;
}
function initRedirectToNewReportPage() {
$('#new-report-reports-btn').on('click', function() {
animateSpinner();
var url = $(this).attr('data-new-report-path');
$(location).attr('href', url);
});
}
function initToggleAllCheckboxes() {
$('input[name="select_all"]').change(function() {
if($(this).is(':checked')) {
$("[data-action='toggle']").prop('checked', true);
addAllItems();
} else {
$("[data-action='toggle']").prop('checked', false);
removeAllItems();
}
updateButtons();
});
}
function addAllItems() {
$.each($("[data-action='toggle']"), function(i, el) {
CHECKED_REPORTS.push($(el).attr('data-report-id'));
})
}
function removeAllItems() {
CHECKED_REPORTS = [];
}
function renderCheckboxHTML(data) {
var html;
html = "<input data-action='toggle' data-report-id='";
html += data + "' type='checkbox'>";
return html;
}
function appendEditPathToRow(row, data) {
$(row).addClass('report-row')
.attr('data-edit-path', data['edit'])
.attr('data-id', data['0']);
}
function checkboxToggleCallback() {
$("[data-action='toggle']").change(function() {
var id = $(this).attr('data-report-id');
if($(this).is(':checked')) {
CHECKED_REPORTS.push(id);
} else {
var index = CHECKED_REPORTS.indexOf(id);
if(index != -1) {
CHECKED_REPORTS.splice(index, 1);
}
}
updateButtons();
});
}
function updateButtons() {
var editReportButton = $('#edit-report-btn');
var deleteReportsButton = $('#delete-reports-btn');
if (CHECKED_REPORTS.length === 0) {
editReportButton.addClass("disabled");
deleteReportsButton.addClass("disabled");
} else if (CHECKED_REPORTS.length === 1) {
editReportButton.removeClass("disabled");
deleteReportsButton.removeClass("disabled");
} else {
editReportButton.addClass("disabled");
deleteReportsButton.removeClass("disabled");
}
}
// INIT
function initDatatable() {
var $table = $('#reports-table')
DATATABLE = $table.dataTable({
'order': [[2, 'desc']],
'processing': true,
'serverSide': true,
'ajax': $table.data('source'),
'pagingType': 'simple_numbers',
'colReorder': {
'fixedColumnsLeft': 1000000 // Disable reordering
},
'columnDefs': [{
'targets': 0,
'searchable': false,
'orderable': false,
'className': 'dt-body-center',
'sWidth': '1%',
'render': renderCheckboxHTML
}],
'oLanguage': {
'sSearch': I18n.t('general.filter')
},
'fnDrawCallback': tableDrowCallback,
'createdRow': appendEditPathToRow
});
}
function initEditReport() {
$('#edit-report-btn').click(function(e) {
e.preventDefault();
animateSpinner();
if (CHECKED_REPORTS.length === 1) {
var id = CHECKED_REPORTS[0];
var row = $(".report-row[data-id='" + id + "']");
var url = row.attr('data-edit-path');
$(location).attr('href', url);
}
});
}
function initDeleteReports() {
$('#delete-reports-btn').click(function(e) {
if (CHECKED_REPORTS.length > 0) {
$('#report-ids').attr("value", "[" + CHECKED_REPORTS + "]");
$('#delete-reports-modal').modal("show");
}
});
$("#confirm-delete-reports-btn").click(function(e) {
animateLoading();
});
}
function initNewReportModal() {
$('#new-report-btn').on('click', function() {
$('#new-report-modal').modal('show').promise().done(function() {
initSelectPicker();
initRedirectToNewReportPage();
});
});
}
function init() {
$(document).ready(function() {
initDatatable();
initEditReport();
initDeleteReports();
initNewReportModal();
});
}
init();
})(window);

View file

@ -0,0 +1,282 @@
(function() {
'use strict';
var INVENTORY_PICKER, COLUMN_PICKER, ITEM_PICKER;
var SELECTED_IDS = {
repository_id: null,
respository_column_id: null,
repository_item_id: null,
};
function clearErrors() {
var $columnsAlertSection = $('#save-PDF-to-inventory-column-warnings');
var $itemsAlertSection = $('#save-PDF-to-inventory-warnings');
$itemsAlertSection.empty();
$columnsAlertSection.empty();
}
function toggleHasFileErrorMessage(value) {
var element = $('#selectInventoryItem [value="' + value + '"]');
var $alertSection = $('#save-PDF-to-inventory-warnings');
$alertSection.empty();
if(element.data('hasfile')) {
$alertSection.append(
'<div class="alert alert-danger">' +
'<%=I18n.t("projects.reports.new.save_PDF_to_inventory_modal.asset_present_warning_html") %>' +
'</div>'
)
}
}
function appendSearchResults(data) {
var items = [];
if(data.hasOwnProperty('results')){
$.each(data.results, function(index, el) {
items.push(
{
value: el.id,
text: el.name,
disabled: false
}
)
});
}
return items;
}
function appendSearchResultsForItems(data) {
var items = [];
if(data.hasOwnProperty('results')){
$('#selectInventoryItem').parent().find('button').removeAttr('disabled');
$.each(data.results, function(index, el) {
items.push(
{
value: el.id,
text: el.name,
disabled: false,
data: {
hasFile: el.hasOwnProperty('has_file_attached') ?
el.has_file_attached :
null
}
}
)
});
} else {
$('#selectInventoryItem').parent().find('button').attr('disabled', true);
clearErrors();
$('#save-PDF-to-inventory-warnings').append(
'<strong class="danger">' + data.no_items + '</strong>'
)
}
return items;
}
function appendSearchResultsForColumns(data) {
var items = [];
if(data.hasOwnProperty('results')){
$('#selectInventoryColumn').parent().find('button').removeAttr('disabled');
$.each(data.results, function(index, el) {
items.push(
{
value: el.id,
text: el.name,
disabled: false
}
)
});
} else {
$('#selectInventoryColumn').parent().find('button').attr('disabled', true);
clearErrors();
$('#save-PDF-to-inventory-column-warnings').append(
'<div class="save-PDF-to-inventory-alerts"><strong class="danger">' +
data.no_items + '</strong></div>'
)
}
return items;
}
function submitButtonEnableToggle(status) {
var button = $('#savePDFtoInventorySubmit');
if(status) {
button.removeAttr('disabled');
} else {
button.attr('disabled', true);
}
}
function deselect(element) {
if(element) {
element.selectpicker('val', null)
element.selectpicker('render');
element.attr('disabled', true);
}
}
// triggers first request and cleans the dropdown selectpicker
function clearDropdownResultsCallback(element) {
element.parent().find('button').on('click', function(el) {
$(this).parent().find('input').trigger('keyup');
});
}
function initInventoryItemSelectPicker() {
ITEM_PICKER =
$('#selectInventoryItem')
.removeAttr('disabled')
.selectpicker({liveSearch: true})
.ajaxSelectPicker({
ajax: {
url: '<%= Rails.application.routes.url_helpers.available_rows_path %>',
type: 'POST',
dataType: 'json',
data: function () {
return {
q: '{{{q}}}',
repository_id: SELECTED_IDS.repository_id,
repository_column_id: SELECTED_IDS.respository_column_id
};
}
},
locale: {
emptyTitle: '<%= I18n.t('projects.reports.new.save_PDF_to_inventory_modal.nothing_selected') %>'
},
preprocessData: appendSearchResultsForItems,
emptyRequest: true,
clearOnEmpty: true,
cache: false,
preserveSelected: false
}).on('change.bs.select', function(el) {
var value = el.target.value;
toggleHasFileErrorMessage(value);
submitButtonEnableToggle(true);
SELECTED_IDS.repository_item_id = value;
});
clearDropdownResultsCallback(ITEM_PICKER);
}
function initInventoryColumnSelectPicker() {
COLUMN_PICKER =
$('#selectInventoryColumn')
.removeAttr('disabled')
.selectpicker({liveSearch: true})
.ajaxSelectPicker({
ajax: {
url: '<%= Rails.application.routes.url_helpers.available_asset_type_columns_path %>',
type: 'POST',
dataType: 'json',
data: function () {
return {
q: '{{{q}}}',
repository_id: SELECTED_IDS.repository_id
};
}
},
locale: {
emptyTitle: '<%= I18n.t('projects.reports.new.save_PDF_to_inventory_modal.nothing_selected') %>'
},
preprocessData: appendSearchResultsForColumns,
emptyRequest: true,
clearOnEmpty: true,
cache: false,
preserveSelected: false
}).on('change.bs.select', function(el) {
SELECTED_IDS.respository_column_id = el.target.value;
deselect(ITEM_PICKER);
submitButtonEnableToggle(false);
initInventoryItemSelectPicker();
// refresh the dropdown state
$('#selectInventoryItem').parent().find('input').trigger('keyup');
clearErrors();
});
clearDropdownResultsCallback(COLUMN_PICKER);
}
function initInventoriesSelectPicker() {
INVENTORY_PICKER =
$('#selectInventory')
.selectpicker({liveSearch: true})
.ajaxSelectPicker({
ajax: {
url: '<%= Rails.application.routes.url_helpers.reports_available_repositories_path %>',
type: 'POST',
dataType: 'json',
data: function () {
return { q: '{{{q}}}' };
}
},
locale: {
emptyTitle: '<%= I18n.t('projects.reports.new.save_PDF_to_inventory_modal.nothing_selected') %>'
},
preprocessData: appendSearchResults,
emptyRequest: true,
clearOnEmpty: false,
cache: false,
preserveSelected: false
}).on('change.bs.select', function(el) {
SELECTED_IDS.repository_id = el.target.value;
deselect(COLUMN_PICKER);
deselect(ITEM_PICKER);
submitButtonEnableToggle(false);
initInventoryColumnSelectPicker();
clearErrors();
// refresh the dropdown state
$('#selectInventoryColumn').parent().find('input').trigger('keyup');
});
clearDropdownResultsCallback(INVENTORY_PICKER);
}
function initializeSubmitAction() {
$('#savePDFtoInventorySubmit').off().on('click', function() {
animateSpinner();
$.ajax({
url: '<%= Rails.application.routes.url_helpers.reports_save_pdf_to_inventory_item_path %>',
data: Object.assign(SELECTED_IDS, { html: $(REPORT_CONTENT).html() }, {}),
type: 'POST',
success: function(data) {
animateSpinner(null, false);
HelperModule.flashAlertMsg(data.message, 'success');
$('#savePDFtoInventory').modal('hide');
},
error: function(xhr) {
animateSpinner(null, false);
HelperModule.flashAlertMsg(xhr.responseJSON.message, 'danger');
$('#savePDFtoInventory').modal('hide');
}
});
});
}
/*
* INITIALIZERS
*/
function initializeSavePDFtoInventoryModal() {
$('#savePDFtoInventory').off().on('shown.bs.modal', function() {
initInventoriesSelectPicker();
initializeSubmitAction();
clearErrors();
// refresh the dropdown state
$('#selectInventory').parent().find('input').trigger('keyup');
}).on('hidden.bs.modal', function() {
// clear ids
SELECTED_IDS = {
repository_id: null,
respository_column_id: null,
repository_item_id: null,
}
// clear select picker objects
if(COLUMN_PICKER) {
deselect(COLUMN_PICKER);
}
if(ITEM_PICKER) {
deselect(ITEM_PICKER);
}
submitButtonEnableToggle(false);
});
}
$(document).ready(initializeSavePDFtoInventoryModal);
})();

View file

@ -66,7 +66,7 @@
type: "GET",
dataType: "json",
success: function (data) {
var tabBody = $(pane.context.hash).find(".tab-content-body");
var tabBody = $(pane.context.hash).find(".tab-content-body");
tabBody.html(data.html);
pane.tab('show').promise().done(function(el) {
initImportRecordsModal();
@ -96,9 +96,8 @@
})
}
$('.create-repository').initializeModal('#create-repo-modal');
$(document).ready(function() {
$('#create-new-repository').initializeModal('#create-repo-modal');
loadRepositoryTab();
initImportRecordsModal();
});

View file

@ -0,0 +1,38 @@
(function() {
'use strict';
function initImportRecordsModal() {
$('#importRecordsButton').off().on('click', function() {
$('#modal-import-records').modal('show');
_initParseRecordsModal();
});
}
function _initParseRecordsModal() {
$('#form-records-file').on('ajax:success', function(ev, data) {
$('#modal-import-records').modal('hide');
$(data.html).appendTo('body').promise().done(function() {
$('#parse-records-modal')
.modal('show')
.on('hidden.bs.modal', function() {
animateSpinner();
location.reload();
});
repositoryRecordsImporter();
});
}).on('ajax:error', function(ev, data) {
$(this).find('.form-group').addClass('has-error');
$(this).find('.form-group').find('.help-block').remove();
$(this).find('.form-group').append("<span class='help-block'>" +
data.responseJSON.message + '</span>');
});
}
function initialzerRepositoryTable() {
initImportRecordsModal();
RepositoryDatatable.destroy();
RepositoryDatatable.init('#' + $('.repository-table table').attr('id'));
}
$(document).ready(initialzerRepositoryTable);
})()

View file

@ -0,0 +1,230 @@
(function() {
'use strict';
// @TODO refactor that eventually
function initEditCoumnModal() {
var modalID = '#manageRepositoryColumn';
var colRadID = '#repository_column_data_type_repositorylistvalue';
var tagsInputID = '[data-role="tagsinput"]';
var formID = '[data-role="manage-repository-column-form"]';
$('[data-action="edit"]').off('click').on('click', function() {
var editUrl = $(this).closest('li').attr('data-edit-url');
$.get(editUrl, function(data) {
$(data.html).appendTo('body').promise().done(function() {
$(modalID).modal('show').promise().done(function() {
$(modalID).on('hidden.bs.modal', function () {
// remove edit modal window
$(modalID).remove();
$('.modal-backdrop').remove();
});
_initTagInput();
setTimeout(function() {
$('#repository_column_name').focus();
}, 500)
if($(modalID).attr('data-edit-type') === 'RepositoryListValue') {
var values = JSON.parse($(tagsInputID).attr('data-value'));
$(colRadID).click().promise().done(function() {
$.each(values, function(index, element) {
$(tagsInputID).tagsinput('add', element);
});
});
}
$('[data-action="save"]').on('click', function() {
if($(colRadID).is(':checked')) {
$('#list_items').val($(tagsInputID).val());
}
_processResponse($(formID), 'update', modalID);
});
});
});
}).fail(function(error) {
HelperModule.flashAlertMsg(
"<%= I18n.t("libraries.repository_columns.no_permissions") %>",
'danger');
});
});
}
function initDeleteColumnModal() {
$('[data-action="destroy"]').off('click').on('click', function() {
var element = $(this);
var modal_html = $("#deleteRepositoryColumn");
$.get(element.closest('li').attr('data-destroy-url'), function(data) {
modal_html.find('.modal-body').html(data.html)
.promise()
.done(function() {
modal_html.modal('show');
_initSubmitAction(modal_html, $(modal_html.find('form')));
});
}).fail(function(error) {
HelperModule.flashAlertMsg(
"<%= I18n.t("libraries.repository_columns.no_permissions") %>",
'danger');
});
});
}
// @TODO refactor that eventually
function initNewColumnModal() {
var modalID = '#manageRepositoryColumn';
$('[data-action="new-column-modal"]').off('click').on('click', function() {
var modalUrl = $(this).attr('data-modal-url');
$.get(modalUrl, function(data) {
$(data.html).appendTo('body').promise().done(function() {
$(modalID).modal('show').promise().done(function() {
$(modalID).on('hidden.bs.modal', function () {
// remove create new modal window
$(modalID).remove();
$('.modal-backdrop').remove();
});
_initTagInput();
setTimeout(function() {
$('#repository_column_name').focus();
}, 500);
$('[data-action="save"]').on('click', function() {
var colRad = '#repository_column_data_type_repositorylistvalue';
if($(colRad).is(':checked')) {
$('#list_items')
.val($('[data-role="tagsinput"]').val());
}
var form = $('[data-role="manage-repository-column-form"]');
_processResponse(form, 'create', modalID);
});
});
});
});
});
}
/* *********************************
Helper methods
********************************* */
function _insertNewListItem(column) {
// remove element if already persent
$('[data-id="' + column.id + '"]').remove();
var html = '<li class="list-group-item" data-id="' + column.id + '" ';
html += 'data-destroy-url="' + column.destroy_html_url + '"';
html += 'data-edit-url="' + column.edit_url + '">';
html += '<span class="pull-left">' + column.name + '</span>';
html += '<span class="controlls pull-right">';
html += '<button class="btn btn-default" data-action="edit">';
html += '<span class="fas fa-pencil-alt"></span>&nbsp;';
html += '<%= I18n.t "libraries.repository_columns.index.edit_column" %></button>&nbsp;';
html += '<button class="btn btn-default" data-action="destroy">';
html += '<span class="glyphicon glyphicon-trash"></span>&nbsp;';
html += '<%= I18n.t "libraries.repository_columns.index.delete_column" %>';
html += '</button></span></li>';
$(html).insertBefore('.repository-columns-body ul li:first')
.promise()
.done(function() {
initDeleteColumnModal();
initEditCoumnModal();
// remove create new modal window
$('#manageRepositoryColumn').remove();
$('.modal-backdrop').remove();
});
// remove 'no column' list item
$('[data-attr="no-columns"]').remove();
}
function _replaceListItem(column) {
$('.list-group-item[data-id="' + column.id + '"]')
.find('span.pull-left').text(column.name);
}
function _initTagInput() {
$('[name="repository_column[data_type]"]')
.on('click', function() {
var listValueId = 'repository_column_data_type_repositorylistvalue';
if($(this).attr('id') === listValueId) {
$('[data-role="tagsinput"]').tagsinput({
maxChars: <%= Constants::NAME_MAX_LENGTH %>,
trimValue: true
});
$('.bootstrap-tagsinput').show();
$('[data-role="tagsimput-label"]').show();
} else {
$('.bootstrap-tagsinput').hide();
$('[data-role="tagsimput-label"]').hide();
}
});
}
function _removeElementFromDom(column) {
$('.list-group-item[data-id="' + column.id + '"]').remove();
if($('.list-group-item').length === 0) {
location.reload();
}
}
function _initSubmitAction(modal, form) {
modal.find('[data-action="delete"]').on('click', function() {
form.submit();
modal.modal('hide')
animateSpinner();
_processResponse(form, 'destroy');
});
}
function _processResponse(form, action, modalID) {
form.on('ajax:success', function(e, data) {
switch(action) {
case 'destroy':
_removeElementFromDom(data);
break;
case 'create':
_insertNewListItem(data);
break;
case 'update':
_replaceListItem(data);
break;
default:
location.reload();
}
HelperModule.flashAlertMsg(data.message, 'success');
animateSpinner(null, false);
if (modalID) {
$(modalID).modal('hide');
}
}).on('ajax:error', function(e, xhr) {
animateSpinner(null, false);
if (modalID) {
if(xhr.responseJSON.message.hasOwnProperty('repository_list_items')) {
var message = xhr.responseJSON.message['repository_list_items'];
$('.dnd-error').remove();
$('#manageRepositoryColumn ').find('.bootstrap-tagsinput').after(
"<i class='dnd-error'>" + message + "</i>"
);
} else {
var field = { "name": xhr.responseJSON.message }
$(form).renderFormErrors('repository_column', field, true, e);
}
} else {
HelperModule.flashAlertMsg(xhr.responseJSON.message, 'danger');
}
});
if (modalID) {
form.submit();
}
}
/* *********************************
Initializers
********************************* */
$(document).ready(function() {
initEditCoumnModal();
initDeleteColumnModal();
initNewColumnModal();
});
})();

View file

@ -271,10 +271,6 @@ setTimeout(function () {
table.columns.adjust();
}, 10);
// Enables noSearchHidden plugin
$.fn.dataTable.defaults.noSearchHidden = true;
// Updates "Select all" control in a data table
function updateDataTableSelectAllCtrl(table) {
var $table = table.table().node();
@ -1081,7 +1077,7 @@ function changeToEditMode() {
var editable = $(el).is('[data-editable]');
var deletable = $(el).is('[data-deletable]');
var visClass = (visible) ? 'glyphicon-eye-open' : 'glyphicon-eye-close';
var visClass = (visible) ? 'fas fa-eye' : 'glyphicon-eye-close';
var visLi = (visible) ? '' : 'col-invisible';
var editClass = (editable) ? '' : 'disabled';
var delClass = (deletable) ? '' : 'disabled';
@ -1109,7 +1105,7 @@ function changeToEditMode() {
'<span class="ok glyphicon glyphicon-ok" style="display: none;" title="<%= I18n.t("general.save") %>"></span>' +
'<span class="cancel glyphicon glyphicon-remove" style="display: none;" title="<%= I18n.t("general.cancel") %>"></span>' +
'<span class="vis glyphicon ' + visClass + '" title="<%= I18n.t("samples.columns_visibility") %>"></span> ' +
'<span class="edit glyphicon glyphicon-pencil ' + editClass + '" title="<%= I18n.t("general.edit") %>"></span>' +
'<span class="edit fas fa-pencil-alt ' + editClass + '" title="<%= I18n.t("general.edit") %>"></span>' +
'<span class="del glyphicon glyphicon-trash ' + delClass + '" title="<%= I18n.t("samples.columns_delete") %>"></span>' +
'</span><br></span></li>';
dropdownList.append(html);
@ -1145,12 +1141,12 @@ function changeToEditMode() {
if (column.visible()) {
self.addClass('glyphicon-eye-close');
self.removeClass('glyphicon-eye-open');
self.removeClass('fas fa-eye');
li.addClass('col-invisible');
column.visible(false);
table.setColumnSearchable(column.index(), false);
} else {
self.addClass('glyphicon-eye-open');
self.addClass('fas fa-eye');
self.removeClass('glyphicon-eye-close');
li.removeClass('col-invisible');
column.visible(true);

View file

@ -87,25 +87,3 @@ function updateSamplesTypesandGroups() {
}
});
}
/**
* Initializes tutorial
*/
function initTutorial() {
var stepNum = parseInt(Cookies.get('current_tutorial_step'), 10);
if (stepNum >= 17 && stepNum <= 18) {
var nextPage = $('#reports-nav-tab a').attr('href');
var steps = [{
element: $('#importSamplesButton')[0],
intro: I18n.t('tutorial.samples_html'),
position: 'right'
}, {
element: $('#secondary-menu')[0],
intro: I18n.t('tutorial.breadcrumbs_html')
}];
initPageTutorialSteps(17, 18, nextPage, function() {}, function() {},
steps);
}
}
initTutorial();

View file

@ -4,7 +4,6 @@
*/
var STORAGE_TREE_KEY = "scinote-sidebar-tree-collapsed-ids";
var STORAGE_TOGGLE_KEY = "scinote-sidebar-toggled";
/**
* Get all collapsed sidebar elements.
@ -84,33 +83,6 @@ function recalculateElementsPositions(ids, item, elements) {
sessionStorage.setItem(STORAGE_TREE_KEY, JSON.stringify(ids));
}
}
/**
* Get the session stored toggled boolean or null value if
* sidebar toggle state was not changed by user. It allow for
* automatic toggling for small devices.
*
* @return True if sidebar is toggled; false otherwise.
*/
function sessionIsSidebarToggled() {
var val = sessionStorage.getItem(STORAGE_TOGGLE_KEY);
if (val === null) {
return null;
}
return val === "toggled";
}
/**
* Store the sidebar toggled boolean to session storage.
*/
function sessionToggleSidebar() {
if (sessionIsSidebarToggled()) {
sessionStorage.setItem(STORAGE_TOGGLE_KEY, "not_toggled");
} else {
sessionStorage.setItem(STORAGE_TOGGLE_KEY, "toggled");
}
}
/**
* Setup the sidebar collapsing & expanding functionality.
@ -128,7 +100,7 @@ function setupSidebarTree() {
}
el
.find(" > span i")
.attr("title", "Expand this branch")
.attr("title", I18n.t('sidebar.branch_expand'))
.removeClass("expanded");
} else {
if (animate) {
@ -138,7 +110,7 @@ function setupSidebarTree() {
}
el
.find(" > span i")
.attr("title", "Collapse this branch")
.attr("title", I18n.t('sidebar.branch_collapse'))
.addClass("expanded");
}
}
@ -147,9 +119,10 @@ function setupSidebarTree() {
$(".tree li:has(ul)")
.addClass("parent_li")
.find(" > span i")
.attr("title", "Collapse this branch");
.attr("title", I18n.t('sidebar.branch_collapse'));
$(".tree li.parent_li ")
.find("> span i")
.removeClass("no-arrow")
.addClass("glyphicon glyphicon-triangle-right expanded");
// Add IDs to all parent <lis>
@ -169,7 +142,7 @@ function setupSidebarTree() {
// Get the session-stored elements
var collapsedIds = sessionGetCollapsedSidebarElements();
// Get the current project stered elements
// Get the current project stored elements
var currentProjectIds = _.findWhere(collapsedIds, { prid: project });
if ( currentProjectIds ){
currentProjectIds.ids = _.filter(currentProjectIds.ids,
@ -241,62 +214,33 @@ function setupSidebarTree() {
e.stopPropagation();
return false;
});
}
/**
* Initialize the show/hide toggling of sidebar.
*/
function initializeSidebarToggle() {
var wrapper = $("#wrapper");
var toggled = sessionIsSidebarToggled();
// Add bold style to all levels of selected element
$(".tree li.active ")
.parents('.parent_li[data-parent="candidate"]')
.find("> span a")
.css("font-weight", "bold");
if (toggled || toggled === null && $(window).width() <
<%= Constants::SCREEN_WIDTH_LARGE %>) {
wrapper.addClass("no-animation");
wrapper.addClass("toggled");
// Cause reflow of the wrapper element
wrapper[0].offsetHeight;
wrapper.removeClass("no-animation");
$(".navbar-secondary").addClass("navbar-without-sidebar");
}
$("#toggle-sidebar-link").on("click", function() {
$("#wrapper").toggleClass("toggled");
sessionToggleSidebar();
$(".navbar-secondary").toggleClass("navbar-without-sidebar", sessionIsSidebarToggled());
return false;
});
// Add custom borders to tree links
$(".tree li span.tree-link ").after("<div class='border-custom'></div>");
}
// Resize the sidebar to accomodate to the page size
function resizeSidebarContents() {
var wrapper = $("#wrapper");
var tree = $("#sidebar-wrapper .tree");
var toggled = sessionIsSidebarToggled();
var navbar = $(".navbar-secondary");
// Set vertical scrollbar on navigation tree
if (tree.length && tree.length == 1) {
tree.css(
"height",
($(window).height() - tree.position().top - 50) + "px"
);
}
// Automatic toggling of sidebar for smaller devices
if (toggled === null) {
if ($(window).width() < <%= Constants::SCREEN_WIDTH_LARGE %>) {
wrapper.addClass("toggled");
navbar.addClass("navbar-without-sidebar");
} else {
wrapper.removeClass("toggled");
navbar.removeClass("navbar-without-sidebar");
}
}
}
(function () {
// Initialize click listeners
setupSidebarTree();
initializeSidebarToggle();
// Actually display wrapper, which is, up to now,
// hidden

View file

@ -0,0 +1,51 @@
(function(global) {
'use strict';
global.SideBarToggle = (function() {
function show() {
$('#wrapper').removeClass('hidden2');
$('#sidebar-wrapper').show(
'slide', { direction: 'right', easing: 'linear' }, 400
);
$('#wrapper').css('paddingLeft', '280px');
$('.navbar-secondary').css(
{ 'margin-left': '-280px', 'padding-left': '294px' }
);
$('#toggle-sidebar-btn').attr('data-shown', '');
}
function hide() {
$('#wrapper').addClass('hidden2');
$('#sidebar-wrapper').hide(
'slide', { direction: 'left', easing: 'linear'}, 400
);
$('#wrapper').css('paddingLeft', '0');
$('.navbar-secondary').css({
'margin-left': '0',
'padding-left': '14px'
});
$('#toggle-sidebar-btn').removeAttr('data-shown');
}
function toggle() {
var btn = $('#toggle-sidebar-btn');
if (btn.is('[data-shown]')) {
hide();
} else {
show();
}
}
function isShown() {
var btn = $('#toggle-sidebar-btn');
return btn.is('[data-shown]');
}
return Object.freeze({
show: show,
hide: hide,
toggle: toggle,
isShown: isShown
})
})();
})(window);

View file

@ -39,16 +39,18 @@ var SmartAnnotation = (function() {
dataUrl: $(document.body).attr('data-atwho-project-url')},
EXPERIMENT: {tag: "exp",
dataUrl: $(document.body).attr('data-atwho-experiment-url')},
SAMPLE: {tag: "sam",
dataUrl: $(document.body).attr('data-atwho-sample-url')},
REPOSITORY: {tag: "rep",
dataUrl: $(document.body).attr('data-atwho-rep-items-url')},
MENU: {tag: "menu",
dataUrl: $(document.body).attr('data-atwho-menu-items')}
});
var prevAt,
// Default selected filter when using '#'
DEFAULT_SEARCH_FILTER = FilterTypeEnum.SAMPLE,
DEFAULT_SEARCH_FILTER = FilterTypeEnum.REPOSITORY,
atWhoUpdating = false;
var defaultRepId;
// helper methods for AtWho callback
function _templateEval(_tpl, map) {
var res;
@ -109,7 +111,7 @@ var SmartAnnotation = (function() {
}
// Initialize or update dropdown header buttons
function updateHeaderButtons(query, filterTypeTag) {
function updateHeaderButtons(query, filterType) {
var $currentAtWho = $('.atwho-view[style]');
initDismissButton($currentAtWho);
@ -117,15 +119,25 @@ var SmartAnnotation = (function() {
$currentAtWho.find('[data-filter]')
.removeClass('btn-primary')
.addClass('btn-default');
$currentAtWho.find('[data-filter="' + filterTypeTag + '"]')
.removeClass('btn-default')
.addClass('btn-primary');
if(filterType.tag === 'rep') {
$currentAtWho.find('[data-rep-id="' + filterType.repository_id + '"]')
.removeClass('btn-default')
.addClass('btn-primary');
} else {
$currentAtWho.find('[data-filter="' + filterType.tag + '"]')
.removeClass('btn-default')
.addClass('btn-primary');
}
// Update the selected filter button when clicking on one of them
$currentAtWho.find('[data-filter]').off()
.on('click', function(e) {
if($(this).hasClass('btn-primary')) {
return;
}
var $selectedBtn = $(this);
var $prevBtn = $selectedBtn.closest('.title').children('.btn-primary');
var $prevBtn = $selectedBtn.closest('.atwho-header-res')
.children('.btn-primary');
$selectedBtn.removeClass('btn-default').addClass('btn-primary');
$prevBtn.removeClass('btn-primary').addClass('btn-default');
@ -136,16 +148,31 @@ var SmartAnnotation = (function() {
// Generates suggestion dropdown filter
function generateFilterMenu(active, res_data) {
var rep_buttons = '';
$.ajax({
async: false,
dataType: 'json',
url: $(document.body).attr('data-atwho-repositories-url'),
success: function(data) {
$.each(data['repositories'], function(id, name) {
if(defaultRepId === undefined){
defaultRepId = id;
}
rep_buttons += '<button data-filter="rep" data-rep-id="' + id +
'" class="btn btn-xs btn-primary">' + name + '</button>';
});
}
});
var header = '<div class="atwho-header-res">' +
'<div class="title">' +
'<button data-filter="prj" class="btn btn-xs ' +
(active === 'prj' ? 'btn-primary' : 'btn-default') + '">project#</button>' +
'<button data-filter="exp" class="btn btn-xs ' +
(active === 'exp' ? 'btn-primary' : 'btn-default') + '">experiment#</button>' +
'<button data-filter="tsk" class="btn btn-xs ' +
(active === 'tsk' ? 'btn-primary' : 'btn-default') + '">task#</button>' +
'<button data-filter="sam" class="btn btn-xs ' +
(active === 'sam' ? 'btn-primary' : 'btn-default') + '">sample#</button>' +
'<button data-filter="prj" class="btn btn-xs btn-primary' +
'">Projects</button>' +
'<button data-filter="exp" class="btn btn-xs btn-primary' +
'">Experiments</button>' +
'<button data-filter="tsk" class="btn btn-xs btn-primary' +
'">Tasks</button>' +
rep_buttons +
'<div class="dismiss">' +
'<span class="glyphicon glyphicon-remove"></span>' +
'</div>' +
'<div class="help">' +
'<div>' +
@ -159,9 +186,6 @@ var SmartAnnotation = (function() {
'<strong><%= I18n.t("atwho.users.dismiss_1") %></strong> ' +
'<%= I18n.t("atwho.users.dismiss_2") %>' +
'</div>' +
'<div class="dismiss">' +
'<span class="glyphicon glyphicon-remove"></span>' +
'</div>' +
'</div>' +
'</div>';
@ -193,8 +217,9 @@ var SmartAnnotation = (function() {
case 'exp':
res += '<span data-type class="res-type">' + map.type + '</span>';
break;
case 'sam':
res += '<span class="glyphicon glyphicon-tint"></span>';
case 'rep_item':
res += '<span data-type class="res-type">' +
map.repository_tag + '</span>';
break;
}
@ -287,9 +312,21 @@ var SmartAnnotation = (function() {
$currentAtWho.removeAttr("style");
}
var params = { query: query };
if(filterType.tag === 'rep') {
params.repository_id = $currentAtWho
.find('.btn-primary')
.data('rep-id');
if(params.repository_id === undefined) {
params.repository_id = defaultRepId;
}
filterType.repository_id = params.repository_id;
}
$.getJSON(
filterType.dataUrl,
{query: query},
params,
function(data) {
// Updates dropdown
if (data.res.length < 1) {
@ -298,7 +335,7 @@ var SmartAnnotation = (function() {
callback(data.res);
}
updateHeaderButtons(query, filterType.tag);
updateHeaderButtons(query, filterType);
}
);
});
@ -389,7 +426,7 @@ var SmartAnnotation = (function() {
},
headerTpl:
'<div class="atwho-header-res">' +
'<div class="title title-user"><%= I18n.t("atwho.users.title") %></div>' +
'<div class="title-user"><%= I18n.t("atwho.users.title") %></div>' +
'<div class="help">' +
'<div>' +
'<strong><%= I18n.t("atwho.users.navigate_1") %></strong> ' +
@ -403,21 +440,21 @@ var SmartAnnotation = (function() {
'<strong><%= I18n.t("atwho.users.dismiss_1") %></strong> ' +
'<%= I18n.t("atwho.users.dismiss_2") %>' +
'</div>' +
'</div>' +
'<div class="dismiss">' +
'<span class="glyphicon glyphicon-remove"></span>' +
'</div>' +
'</div>' +
'</div>',
limit: <%= Constants::ATWHO_SEARCH_LIMIT %>,
startsWithSpace: true,
acceptSpaceBar: true,
displayTimeout: 120000
})
.atwho(atWhoSettings('#', DEFAULT_SEARCH_FILTER));
.atwho(atWhoSettings('#', DEFAULT_SEARCH_FILTER))
// .atwho(atWhoSettings('task#', FilterTypeEnum.TASK)) Waiting for better times
// .atwho(atWhoSettings('project#', FilterTypeEnum.PROJECT))
// .atwho(atWhoSettings('experiment#', FilterTypeEnum.EXPERIMENT))
// .atwho(atWhoSettings('sample#', FilterTypeEnum.SAMPLE));
// .atwho(atWhoSettings('sample#', FilterTypeEnum.REPOSITORY));
}
return {

View file

@ -137,8 +137,8 @@
html += 'placeholder="<%= I18n.t('assets.from_clipboard.file_name_placeholder') %>" aria-describedby="image-name">';
html += '<span class="input-group-addon" id="image-name"></span></div>';
html += '</div><div class="modal-footer">';
html += '<button type="button" class="btn btn-primary" data-action="addImageFormClipboard"><%= I18n.t('assets.from_clipboard.add_image') %></button>';
html += '<button type="button" class="btn btn-default" data-dismiss="modal"><%= I18n.t('general.cancel') %></button>'
html += '<button type="button" class="btn btn-default" data-dismiss="modal"><%= I18n.t('general.cancel') %></button>';
html += '<button type="button" class="btn btn-success" data-action="addImageFormClipboard"><%= I18n.t('assets.from_clipboard.add_image') %></button>';
html += '</div></div></div></div><!-- /.modal -->';
return $(html).appendTo($('body')).promise().done(function() {
// display modal

View file

@ -0,0 +1,106 @@
(function(global) {
'use strict';
global.initPreviewModal = function initPreviewModal() {
var name;
var url;
var downloadUrl;
$('.file-preview-link').off('click');
$('.file-preview-link').click(function(e) {
e.preventDefault();
name = $(this).find('p').text();
url = $(this).data('preview-url');
downloadUrl = $(this).attr('href');
openPreviewModal(name, url, downloadUrl);
});
}
function openPreviewModal(name, url, downloadUrl) {
var modal = $('#filePreviewModal');
$.ajax({
url: url,
type: 'GET',
dataType: 'json',
success: function(data) {
modal.find('.file-preview-container').empty();
modal.find('.file-wopi-controls').empty();
if (data.hasOwnProperty('wopi-controls')) {
modal.find('.file-wopi-controls').html(data['wopi-controls']);
}
var link = modal.find('.file-download-link');
link.attr('href', downloadUrl);
link.attr('data-no-turbolink', true);
link.attr('data-status', 'asset-present');
if (data['type'] === 'image') {
if(data['processing']) {
animateSpinner('.file-preview-container', true);
} else {
animateSpinner('.file-preview-container', false);
modal.find('.file-preview-container')
.append($('<img>')
.attr('src', data['large-preview-url'])
.attr('alt', name)
.click(function(ev) {
ev.stopPropagation();
})
);
}
} else {
modal.find('.file-preview-container').html(data['preview-icon']);
}
if(data['processing']) {
checkFileReady(url, modal);
}
modal.find('.file-name').text(name);
modal.find('.modal-body').click(function() {
modal.modal('hide');
});
modal.modal();
$('.modal-backdrop').last().css('z-index', modal.css('z-index') - 1);
},
error: function(ev) {
// TODO
}
});
}
function checkFileReady(url, modal) {
$.get(url, function(data) {
if(data['processing']) {
$('.file-download-link')
.addClass('disabled-with-click-events')
.attr('title',
'<%= I18n.t('general.file.processing')%>')
.click(function(ev) {
ev.preventDefault();
ev.stopPropagation();
});
setTimeout(function() {
checkFileReady(url, modal);
}, 10000);
} else {
if(data['type'] === 'image') {
modal.find('.file-preview-container').empty();
modal.find('.file-preview-container')
.append($('<img>')
.attr('src', data['large-preview-url'])
.attr('alt', data['filename'])
.click(function(ev) {
ev.stopPropagation();
})
);
modal.find('.file-name').text(data['filename']);
modal.find('.modal-body').click(function() {
modal.modal('hide');
});
modal.modal();
$('.modal-backdrop').last().css('z-index', modal.css('z-index') - 1);
}
$('.file-download-link')
.removeClass('disabled-with-click-events')
.removeAttr('title')
.off();
}
})
}
})(window);

View file

@ -1,48 +0,0 @@
(function(global) {
'use strict';
global.initPreviewModal = function initPreviewModal() {
var name, url, downloadUrl, description;
$('.image-preview-link').off();
$('.image-preview-link').click(function(e) {
e.preventDefault();
name = $(this).find('p').text();
url = $(this).find('img').data('preview-url');
downloadUrl = $(this).attr('href');
description = $(this).data('description');
openPreviewModal(name, url, downloadUrl, description);
});
}
function openPreviewModal(name, url, downloadUrl, description) {
var modal = $('#imagePreviewModal');
$.ajax({
url: url,
type: 'GET',
dataType: 'json',
success: function(data) {
modal.find('.modal-body img').remove();
modal.find('.image-name').text(name);
var link = modal.find('.image-download-link');
link.attr('href', downloadUrl);
link.attr('data-no-turbolink', true);
link.attr('data-status', 'asset-present');
modal.find('.modal-body').append($('<img>')
.attr('src', data['large-preview-url'])
.attr('alt', name)
.click(function(ev) {
ev.stopPropagation();
}));
modal.find('.modal-footer .image-description').text(description);
modal.find('.modal-body').click(function() {
modal.modal('hide');
});
modal.modal();
$('.modal-backdrop').last().css('z-index', modal.css('z-index') - 1);
},
error: function(ev) {
// TODO
}
});
}
})(window);

View file

@ -0,0 +1,44 @@
(function() {
'use strict';
$(document).on('click', '.record-info-link', function(e) {
var that = $(this);
$.ajax({
method: 'GET',
url: that.attr('href'),
dataType: 'json'
}).done(function(xhr, settings, data) {
$('body').append($.parseHTML(data.responseJSON.html));
$('#modal-info-repository-row').modal('show', {
backdrop: true,
keyboard: false
}).on('hidden.bs.modal', function() {
$(this).find('.modal-body #repository_row-info-table').DataTable().destroy();
$(this).remove();
});
initPreviewModal();
$('#repository_row-info-table').DataTable({
dom: 'RBltpi',
stateSave: false,
buttons: [],
processing: true,
colReorder: {
fixedColumnsLeft: 1000000 // Disable reordering
},
columnDefs: [{
targets: 0,
searchable: false,
orderable: false
}],
fnDrawCallback: function(settings, json) {
animateSpinner(this, false);
},
preDrawCallback: function(settings) {
animateSpinner(this);
}
});
});
e.preventDefault();
return false;
});
})();

View file

@ -36,7 +36,6 @@ $(document).ready(function () {
$("#hide-alert").click(function(ev) {
$(this).closest("div.alert").addClass("alert-hidden");
$("#content-wrapper").addClass("alert-hidden");
$("#content-wrapper").removeClass("alert-shown");
ev.preventDefault();
ev.stopPropagation();

View file

@ -26,165 +26,6 @@ $.fn.onAjaxComplete = function (cb) {
});
}
// Number of all tutorial steps
var TUTORIAL_STEPS_CNT = 26;
/**
* Initializes tutorial steps for the current page.
* NOTE: You can specify steps manually in JS with steps parameter (preferred
* way), or hardcode them in HTML
* NOTE: If some steps edit page, then this function needs to be called several
* times for the same page, but for different steps. The same goes if the page
* has discontinuous tutorial steps. In such cases, use steps branching, e.g.:
* @example
* var tutorialData = Cookies.get('tutorial_data');
* if (tutorialData) {
* tutorialData = JSON.parse(tutorialData);
* var stepNum = parseInt(Cookies.get('current_tutorial_step'), 10);
*
* if (stepNum >= 6 && stepNum <= 7) {
* ...
* } else if ...
* NOTE: If an element the popup is pointing at is of lesser horizontal length
* than the popup itself, then it will not be positioned correctly if it's
* position is top or bottom, so set/change the step's position to either left
* or right (and don't use any custom styling!), e.g.:
* @example
* var steps = [
* {
* ...
* position: 'right'
* },
* {
* ...
* ];
* NOTE: If only one page step is needed, then make pageFirstStepN ==
* pageLastStepN (both represent the one and only step number)
*
* @param {number} pageFirstStepN Page's first step number
* @param {number} pageLastStepN Page's last step number
* @param {string} nextPagePath Next page absolute path
* @param {function} beforeCb Callback called before the tutorial starts. Mainly
* used for setting 'pointer-events: none' on the elements the page's steps
* highlight.
* @param {function} endCb Callback called after the tutorial ends. Mainly used
* for setting 'pointer-events: auto' on the elements the page's steps
* highlight.
* @param {object} steps JSON containing intro.js steps.
*/
function initPageTutorialSteps(pageFirstStepN, pageLastStepN, nextPagePath,
beforeCb, endCb, steps) {
var tutorialData = Cookies.get('tutorial_data');
if (tutorialData) {
tutorialData = JSON.parse(tutorialData);
var stepNum = parseInt(Cookies.get('current_tutorial_step'), 10);
if (isNaN(stepNum)) {
// Cookies data initialization
stepNum = 1;
Cookies.set('current_tutorial_step', stepNum);
tutorialData[0].backPagesPaths = [];
Cookies.set('tutorial_data', tutorialData);
}
var thisPagePath = window.location.pathname;
beforeCb();
// Initialize tutorial for the current pages' steps
var doneLabel;
if (pageLastStepN == TUTORIAL_STEPS_CNT) {
doneLabel = I18n.t('tutorial.finish_tutorial');
} else {
doneLabel = I18n.t('tutorial.skip_tutorial');
// Add extra fake step, so that next button on last step of current page
// gets focused. Also, if current page has only one step, this adds back
// and next buttons to the popup.
steps.push({});
}
introJs()
.setOptions({
overlayOpacity: '0.2',
prevLabel: I18n.t('tutorial.back'),
nextLabel: I18n.t('tutorial.next'),
skipLabel: I18n.t('tutorial.skip_tutorial'),
doneLabel: doneLabel,
showBullets: false,
showStepNumbers: false,
exitOnOverlayClick: false,
exitOnEsc: false,
disableInteraction: true,
keyboardNavigation: false,
tooltipClass: 'custom next-page-link',
steps: steps
})
.goToStep(stepNum - (pageFirstStepN - 1))
.onexit(function() {
Cookies.remove('tutorial_data');
Cookies.remove('current_tutorial_step');
location.reload();
})
.oncomplete(function() {
Cookies.remove('tutorial_data');
Cookies.remove('current_tutorial_step');
location.reload();
})
.start();
// Page navigation when coming to this page from previous or from next page
$(function() {
if (stepNum === pageFirstStepN && stepNum > 1) {
$('.introjs-prevbutton').removeClass('introjs-disabled');
} else if (stepNum === pageLastStepN && stepNum < TUTORIAL_STEPS_CNT) {
$('.introjs-nextbutton').removeClass('introjs-disabled');
}
});
// Page navigation when already on this page
$('.introjs-skipbutton').click(function() {
Cookies.remove('current_tutorial_step');
Cookies.remove('tutorial_data');
endCb();
});
$('.introjs-prevbutton').click(function() {
if (stepNum > 1) {
Cookies.set('current_tutorial_step', --stepNum);
if (stepNum === pageFirstStepN && stepNum > 1) {
$('.introjs-prevbutton').removeClass('introjs-disabled');
} else if (stepNum < pageFirstStepN) {
// Go to previous page;
var prevPagePath = tutorialData[0].backPagesPaths.pop();
Cookies.set('tutorial_data', tutorialData);
$('.introjs-prevbutton').attr('href', prevPagePath);
introJs().exit();
endCb();
}
}
});
$('.introjs-nextbutton').click(function() {
if (stepNum < TUTORIAL_STEPS_CNT) {
Cookies.set('current_tutorial_step', ++stepNum);
if (stepNum === pageLastStepN && stepNum < TUTORIAL_STEPS_CNT) {
$('.introjs-nextbutton').removeClass('introjs-disabled');
} else if (stepNum > pageLastStepN) {
// Go to next page
tutorialData[0].backPagesPaths.push(thisPagePath);
Cookies.set('tutorial_data', tutorialData);
$('.introjs-nextbutton').attr('href', nextPagePath);
introJs().exit();
endCb();
}
}
});
}
}
/**
* Checkbox on/off logic. For each checkbox hierarchy add 'checkbox-tree' class
* to a parent 'div' surrounding the checkbox hierarchy, represented with 'ul',

View file

@ -64,35 +64,9 @@
$(this).renderFormErrors("user", data.responseJSON);
});
var repeatTutorialModal = $("#repeat-tutorial-modal");
var repeatTutorialModalBody = repeatTutorialModal.find(".modal-body");
initRepeatTutorialModal();
notificationsSettings();
initNotificationSettingsForm();
$("#reset-tutorial-btn")
.on("ajax:before", function () {
repeatTutorialModal.modal('show');
})
.on("ajax:success", function (e, data) {
initRepeatTutorialModalBody(data);
});
function initRepeatTutorialModal() {
// Remove modal content when modal window is closed.
repeatTutorialModal.on("hidden.bs.modal", function () {
repeatTutorialModalBody.html("");
});
}
// Initialize ajax listeners and elements style on modal body. This
// function must be called when modal body is changed.
function initRepeatTutorialModalBody(data) {
repeatTutorialModalBody.html(data.html);
repeatTutorialModalBody.find(".selectpicker").selectpicker();
}
// Setup notification checkbox buttons
function notificationsSettings() {
var notification_settings = [ "recent_notification",
@ -101,10 +75,10 @@
for (var i = 0; i < notification_settings.length; i++ ) {
var setting = $('[name="' + notification_settings[i] + '"]');
var dependant = $('[name="' + notification_settings[i] + '_email"]');
dependant.checkboxpicker({ onActiveCls: 'btn-primary' });
dependant.checkboxpicker({ onActiveCls: 'btn-toggle', offActiveCls: 'btn-toggle' });
setting
.checkboxpicker({
onActiveCls: 'btn-primary'
onActiveCls: 'btn-toggle', offActiveCls: 'btn-toggle'
}).change(function() {
if ( $(this).prop('checked') ) {
enableDependant($('[name="' + $(this).attr('name') + '_email"]'));
@ -126,7 +100,7 @@
function setEmailSwitch(setting) {
setting
.checkboxpicker({
onActiveCls: 'btn-primary'
onActiveCls: 'btn-toggle', offActiveCls: 'btn-toggle'
});
if ( setting.attr('value') === 'true' ) {
setting.prop('checked', true);
@ -149,7 +123,7 @@
var system_message_notification = $('[name="system_message_notification"]');
system_message_notification
.checkboxpicker({
onActiveCls: 'btn-primary'
onActiveCls: 'btn-toggle', offActiveCls: 'btn-toggle'
});
system_message_notification.prop('checked', true);
system_message_notification.prop('disabled', true);
@ -158,7 +132,7 @@
var system_message_notification_mail = $('[name="system_message_notification_email"]');
system_message_notification_mail
.checkboxpicker({
onActiveCls: 'btn-primary'
onActiveCls: 'btn-toggle', offActiveCls: 'btn-toggle'
});
system_message_notification_mail.prop(
'checked',

View file

@ -0,0 +1,18 @@
(function() {
'use strict';
$(".tree li.parent_li ").find("> span i").on("click", function (e) {
e.stopPropagation();
var el = $(this).closest("li.parent_li");
if (el.find(" > ul.accountNavigation").is(":visible")) {
el.find(" > span > i.triangleDown").hide();
el.find(" > span > i.triangleRight").show();
el.find(" > ul.accountNavigation").hide();
} else {
el.find(" > span > i.triangleDown").show();
el.find(" > span > i.triangleRight").hide();
el.find(" > ul.accountNavigation").show();
}
});
})();

View file

@ -120,7 +120,10 @@ function initUsersTable() {
{ data: "3" },
{ data: "4" },
{ data: "5" }
]
],
oLanguage: {
sSearch: I18n.t('general.filter')
}
});
}

View file

@ -6,9 +6,10 @@
*= require rails_bootstrap_forms
*= require bootstrap-select
*= require constants
*= require introjs
*= stub reports_pdf
*/
@import "constants";
@import "bootstrap-sprockets";
@import "bootstrap";
@import "bootstrap-datetimepicker";
@ -16,6 +17,6 @@
@import "bootstrap-tagsinput";
@import "bootstrap-tagsinput-typeahead";
@import "handsontable.full.min";
@import "ajax-bootstrap-select.min";
@import "extend/bootstrap";
@import "font-awesome";
@import "themes/scinote";

View file

@ -1,51 +1,35 @@
@import url(https://fonts.googleapis.com/css?family=Open+Sans:400,600,700,400italic&subset=latin,latin-ext);
//==============================================================================
// Colors
//==============================================================================
// Theme colors
$color-theme-primary: #37a0d9;
$color-theme-secondary: #8fd13f;
$color-theme-dark: #6d6e71;
// Grayscale colors
$color-white: #fff;
$color-alabaster: #fcfcfc;
$color-snow: #f9f9f9;
$color-wild-sand: #f5f5f5;
$color-concrete: #f2f2f2;
$color-gallery: #eee;
$color-gainsboro: #e3e3e3;
$color-alto: #d2d2d2;
$color-silver: #c5c5c5;
$color-dark-gray: #adadad;
$color-silver-chalice: #a0a0a0;
$color-gray: #909088;
$color-dove-gray: #666;
$color-emperor: #555;
$color-mine-shaft: #333;
$color-nero: #262626;
$color-black: #000;
$color-cloud: rgba(0, 0, 0, .1);
$color-gray-light-yadcf: #ccc;
$color-gray-dark-yadcf: #a9a9a9;
// Miscelaneous colors
$color-mystic: #eaeff2;
$color-candlelight: #ffda23;
$color-orange: #ff900b;
$color-saturated-green: #008600;
$color-blue-yadcf: #337ab7;
// Theme colors
$brand-default: $color-white;
$brand-primary: #37a0d9;
$brand-success: #2dbe61;
$brand-info: #5bc0de;
$brand-warning: #f0ad4e;
$brand-danger: #e54e42;
$brand-other: #8fd13f;
$brand-extra: #34495e;
$brand-primary-light: #dcedf6;
$brand-success-light: #e2eed8;
$brand-warning-light: #fcf7e4;
$brand-danger-light: #efdfdf;
// Red colors
$color-mojo: #cf4b48;
$color-apple-blossom: #a94442;
$color-milano-red: #a70b05;
// Colors for specific intents
$color-visited-link: #23527c;
// Overlay shade for drag'n dropdown
$color-drag-overlay: rgba(0, 0, 0, .4);
//==============================================================================
// Other
@ -53,3 +37,460 @@ $color-drag-overlay: rgba(0, 0, 0, .4);
// Some big value which is still supported by all browsers
$infinity: 9999999;
//==============================================================================
// Bootstrap
//==============================================================================
// Grayscale Colors in bootstrap
$gray-darker: $color-black;
$gray-dark: $color-emperor;
$gray: $color-emperor;
$gray-light: $color-dove-gray;
$gray-lighter: $color-concrete;
// Scaffolding
$body-bg: $color-concrete;
$text-color: $color-emperor;
$link-color: $brand-primary;
$link-hover-color: darken($link-color, 15%);
$link-hover-decoration: underline;
// Typography
$font-family-lato: lato, "Open Sans", Arial, Helvetica, sans-serif;
$font-family-sans-serif: "Open Sans", Arial, Helvetica, sans-serif;
$font-family-serif: Georgia, "Times New Roman", Times, serif;
$font-family-monospace: Menlo, Monaco, Consolas, "Courier New", monospace;
$font-family-base: $font-family-lato;
$font-size-base: 14px;
$font-size-large: ceil(($font-size-base * 1.1)); //16px
$font-size-small: ceil(($font-size-base * .9)); //13px
$font-size-h1: floor(($font-size-base * 2.6)); //36px
$font-size-h2: floor(($font-size-base * 2.15)); //30px
$font-size-h3: ceil(($font-size-base * 1.7)); //24px
$font-size-h4: ceil(($font-size-base * 1.25)); //18px
$font-size-h5: $font-size-base; //14px
$font-size-h6: ceil(($font-size-base * .85)); //12px
$line-height-base: 1.428571429;
$line-height-computed: floor(($font-size-base * $line-height-base));
$headings-font-family: inherit;
$headings-font-weight: 500;
$headings-line-height: 1.1;
$headings-color: inherit;
// // Iconography
// $icon-font-path: "../fonts/";
// $icon-font-name: "glyphicons-halflings-regular";
// $icon-font-svg-id: "glyphicons_halflingsregular";
// Components
$padding-base-vertical: 6px;
$padding-base-horizontal: 12px;
$padding-large-vertical: 10px;
$padding-large-horizontal: 16px;
$padding-small-vertical: 5px;
$padding-small-horizontal: 10px;
$padding-xs-vertical: 1px;
$padding-xs-horizontal: 5px;
$line-height-large: 1.3333333;
$line-height-small: 1.5;
$border-radius-base: 4px;
$border-radius-large: 6px;
$border-radius-small: 3px;
$component-active-color: $color-white;
$component-active-bg: $brand-primary;
$caret-width-base: 4px;
$caret-width-large: 5px;
// // Tables
// $table-cell-padding: 8px;
// $table-condensed-cell-padding: 5px;
// $table-bg: transparent;
// $table-bg-accent: #f9f9f9;
// $table-bg-hover: #f5f5f5;
// $table-bg-active: $table-bg-hover;
// $table-border-color: #ddd;
// Buttons
$btn-font-weight: normal;
$btn-default-color: $gray-dark;
$btn-default-bg: $brand-default;
$btn-default-border: $color-silver;
$btn-primary-color: $color-white;
$btn-primary-bg: $brand-success;
$btn-primary-border: darken($btn-primary-bg, 5%);
$btn-success-color: $color-white;
$btn-success-bg: $brand-success;
$btn-success-border: darken($btn-success-bg, 5%);
$btn-info-color: $color-white;
$btn-info-bg: $brand-info;
$btn-info-border: darken($btn-info-bg, 5%);
$btn-warning-color: $color-white;
$btn-warning-bg: $brand-warning;
$btn-warning-border: darken($btn-warning-bg, 5%);
$btn-danger-color: $color-white;
$btn-danger-bg: $brand-danger;
$btn-danger-border: darken($btn-danger-bg, 5%);
$btn-toggle-color: $color-white;
$btn-toggle-bg: $brand-primary;
$btn-toggle-border: darken($btn-toggle-bg, 5%);
$btn-link-disabled-color: $gray-light;
$btn-border-radius-base: $border-radius-base;
$btn-border-radius-large: $border-radius-large;
$btn-border-radius-small: $border-radius-small;
// // Forms
// $input-bg: #fff;
// $input-bg-disabled: $gray-lighter;
// $input-color: $gray;
// $input-border: #ccc;
// $input-border-radius: $border-radius-base;
// $input-border-radius-large: $border-radius-large;
// $input-border-radius-small: $border-radius-small;
// $input-border-focus: #66afe9;
// $input-color-placeholder: #999;
// $input-height-base: ($line-height-computed + ($padding-base-vertical * 2) + 2);
// $input-height-large: (ceil($font-size-large * $line-height-large) + ($padding-large-vertical * 2) + 2);
// $input-height-small: (floor($font-size-small * $line-height-small) + ($padding-small-vertical * 2) + 2);
// $form-group-margin-bottom: 15px;
// $legend-color: $gray-dark;
// $legend-border-color: #e5e5e5;
// $input-group-addon-bg: $gray-lighter;
// $input-group-addon-border-color: $input-border;
// $cursor-disabled: not-allowed;
//
// // Dropdowns
// $dropdown-bg: #fff;
// $dropdown-border: rgba(0,0,0,.15);
// $dropdown-fallback-border: #ccc;
// $dropdown-divider-bg: #e5e5e5;
// $dropdown-link-color: $gray-dark;
// $dropdown-link-hover-color: darken($gray-dark, 5%);
// $dropdown-link-hover-bg: #f5f5f5;
// $dropdown-link-active-color: $component-active-color;
// $dropdown-link-active-bg: $component-active-bg;
// $dropdown-link-disabled-color: $gray-light;
// $dropdown-header-color: $gray-light;
// $dropdown-caret-color: #000;
//
// // Media queries breakpoints
// $screen-xs: 480px;
// $screen-xs-min: $screen-xs;
// $screen-phone: $screen-xs-min;
// $screen-sm: 768px;
// $screen-sm-min: $screen-sm;
// $screen-tablet: $screen-sm-min;
// $screen-md: 992px;
// $screen-md-min: $screen-md;
// $screen-desktop: $screen-md-min;
// $screen-lg: 1200px;
// $screen-lg-min: $screen-lg;
// $screen-lg-desktop: $screen-lg-min;
// $screen-xs-max: ($screen-sm-min - 1);
// $screen-sm-max: ($screen-md-min - 1);
// $screen-md-max: ($screen-lg-min - 1);
//
// // Grid system
// $grid-columns: 12;
// $grid-gutter-width: 30px;
// $grid-float-breakpoint: $screen-sm-min;
// $grid-float-breakpoint-max: ($grid-float-breakpoint - 1);
// Container sizes
$container-tablet: auto;
$container-sm: auto;
$container-desktop: auto;
$container-md: auto;
$container-large-desktop: auto;
$container-lg: auto;
// // Navbar
// $navbar-height: 50px;
// $navbar-margin-bottom: $line-height-computed;
// $navbar-border-radius: $border-radius-base;
// $navbar-padding-horizontal: floor(($grid-gutter-width / 2));
// $navbar-padding-vertical: (($navbar-height - $line-height-computed) / 2);
// $navbar-collapse-max-height: 340px;
// $navbar-default-color: #777;
// $navbar-default-bg: #f8f8f8;
// $navbar-default-border: darken($navbar-default-bg, 6.5%);
// $navbar-default-link-color: #777;
// $navbar-default-link-hover-color: #333;
// $navbar-default-link-hover-bg: transparent;
// $navbar-default-link-active-color: #555;
// $navbar-default-link-active-bg: darken($navbar-default-bg, 6.5%);
// $navbar-default-link-disabled-color: #ccc;
// $navbar-default-link-disabled-bg: transparent;
// $navbar-default-brand-color: $navbar-default-link-color;
// $navbar-default-brand-hover-color: darken($navbar-default-brand-color, 10%);
// $navbar-default-brand-hover-bg: transparent;
// $navbar-default-toggle-hover-bg: #ddd;
// $navbar-default-toggle-icon-bar-bg: #888;
// $navbar-default-toggle-border-color: #ddd;
//
// // Inverted navbar
// $navbar-inverse-color: lighten($gray-light, 15%);
// $navbar-inverse-bg: #222;
// $navbar-inverse-border: darken($navbar-inverse-bg, 10%);
// $navbar-inverse-link-color: lighten($gray-light, 15%);
// $navbar-inverse-link-hover-color: #fff;
// $navbar-inverse-link-hover-bg: transparent;
// $navbar-inverse-link-active-color: $navbar-inverse-link-hover-color;
// $navbar-inverse-link-active-bg: darken($navbar-inverse-bg, 10%);
// $navbar-inverse-link-disabled-color: #444;
// $navbar-inverse-link-disabled-bg: transparent;
// $navbar-inverse-brand-color: $navbar-inverse-link-color;
// $navbar-inverse-brand-hover-color: #fff;
// $navbar-inverse-brand-hover-bg: transparent;
// $navbar-inverse-toggle-hover-bg: #333;
// $navbar-inverse-toggle-icon-bar-bg: #fff;
// $navbar-inverse-toggle-border-color: #333;
//
// // Navs
// $nav-link-padding: 10px 15px;
// $nav-link-hover-bg: $gray-lighter;
// $nav-disabled-link-color: $gray-light;
// $nav-disabled-link-hover-color: $gray-light;
//
// // Tabs
// $nav-tabs-border-color: #ddd;
// $nav-tabs-link-hover-border-color: $gray-lighter;
// $nav-tabs-active-link-hover-bg: $body-bg;
// $nav-tabs-active-link-hover-color: $gray;
// $nav-tabs-active-link-hover-border-color: #ddd;
// $nav-tabs-justified-link-border-color: #ddd;
// $nav-tabs-justified-active-link-border-color: $body-bg;
//
// // Pills
// $nav-pills-border-radius: $border-radius-base;
// $nav-pills-active-link-hover-bg: $component-active-bg;
// $nav-pills-active-link-hover-color: $component-active-color;
//
// // Pagination
// $pagination-color: $link-color;
// $pagination-bg: #fff;
// $pagination-border: #ddd;
// $pagination-hover-color: $link-hover-color;
// $pagination-hover-bg: $gray-lighter;
// $pagination-hover-border: #ddd;
// $pagination-active-color: #fff;
// $pagination-active-bg: $brand-primary;
// $pagination-active-border: $brand-primary;
// $pagination-disabled-color: $gray-light;
// $pagination-disabled-bg: #fff;
// $pagination-disabled-border: #ddd;
//
// // Pager
// $pager-bg: $pagination-bg;
// $pager-border: $pagination-border;
// $pager-border-radius: 15px;
// $pager-hover-bg: $pagination-hover-bg;
// $pager-active-bg: $pagination-active-bg;
// $pager-active-color: $pagination-active-color;
// $pager-disabled-color: $pagination-disabled-color;
//
// // Jumbotron
// $jumbotron-padding: 30px;
// $jumbotron-color: inherit;
// $jumbotron-bg: $gray-lighter;
// $jumbotron-heading-color: inherit;
// $jumbotron-font-size: ceil(($font-size-base * 1.5));
// $jumbotron-heading-font-size: ceil(($font-size-base * 4.5));
// Form states and alerts
$state-success-text: #3c763d;
$state-success-bg: #d5f2df;
$state-success-border: adjust-hue(darken($state-success-bg, 5%), -10);
$state-info-text: #31708f;
$state-info-bg: #d7ecf7;
$state-info-border: adjust-hue(darken($state-info-bg, 7%), -10);
$state-warning-text: #8a6d3b;
$state-warning-bg: #fcf8e3;
$state-warning-border: adjust-hue(darken($state-warning-bg, 5%), -10);
$state-danger-text: #a94442;
$state-danger-bg: #fadcd9;
$state-danger-border: adjust-hue(darken($state-danger-bg, 5%), -10);
// // Tooltips
// $tooltip-max-width: 200px;
// $tooltip-color: #fff;
// $tooltip-bg: #000;
// $tooltip-opacity: .9;
// $tooltip-arrow-width: 5px;
// $tooltip-arrow-color: $tooltip-bg;
//
// // Popovers
// $popover-bg: #fff;
// $popover-max-width: 276px;
// $popover-border-color: rgba(0,0,0,.2);
// $popover-fallback-border-color: #ccc;
// $popover-title-bg: darken($popover-bg, 3%);
// $popover-arrow-width: 10px;
// $popover-arrow-color: $popover-bg;
// $popover-arrow-outer-width: ($popover-arrow-width + 1);
// $popover-arrow-outer-color: fadein($popover-border-color, 5%);
// $popover-arrow-outer-fallback-color: darken($popover-fallback-border-color, 20%);
//
// // Labels
// $label-default-bg: $gray-light;
// $label-primary-bg: $brand-primary;
// $label-success-bg: $brand-success;
// $label-info-bg: $brand-info;
// $label-warning-bg: $brand-warning;
// $label-danger-bg: $brand-danger;
// $label-color: #fff;
// $label-link-hover-color: #fff;
//
// // Modals
// $modal-inner-padding: 15px;
// $modal-title-padding: 15px;
// $modal-title-line-height: $line-height-base;
// $modal-content-bg: #fff;
// $modal-content-border-color: rgba(0,0,0,.2);
// $modal-content-fallback-border-color: #999;
// $modal-backdrop-bg: #000;
// $modal-backdrop-opacity: .5;
// $modal-header-border-color: #e5e5e5;
// $modal-footer-border-color: $modal-header-border-color;
// $modal-lg: 900px;
// $modal-md: 600px;
// $modal-sm: 300px;
//
// // Alerts
// $alert-padding: 15px;
// $alert-border-radius: $border-radius-base;
// $alert-link-font-weight: bold;
// $alert-success-bg: $state-success-bg;
// $alert-success-text: $state-success-text;
// $alert-success-border: $state-success-border;
// $alert-info-bg: $state-info-bg;
// $alert-info-text: $state-info-text;
// $alert-info-border: $state-info-border;
// $alert-warning-bg: $state-warning-bg;
// $alert-warning-text: $state-warning-text;
// $alert-warning-border: $state-warning-border;
// $alert-danger-bg: $state-danger-bg;
// $alert-danger-text: $state-danger-text;
// $alert-danger-border: $state-danger-border;
//
// // Progress bars
// $progress-bg: #f5f5f5;
// $progress-bar-color: #fff;
// $progress-border-radius: $border-radius-base;
// $progress-bar-bg: $brand-primary;
// $progress-bar-success-bg: $brand-success;
// $progress-bar-warning-bg: $brand-warning;
// $progress-bar-danger-bg: $brand-danger;
// $progress-bar-info-bg: $brand-info;
//
// // List group
// $list-group-bg: #fff;
// $list-group-border: #ddd;
// $list-group-border-radius: $border-radius-base;
// $list-group-hover-bg: #f5f5f5;
// $list-group-active-color: $component-active-color;
// $list-group-active-bg: $component-active-bg;
// $list-group-active-border: $list-group-active-bg;
// $list-group-active-text-color: lighten($list-group-active-bg, 40%);
// $list-group-disabled-color: $gray-light;
// $list-group-disabled-bg: $gray-lighter;
// $list-group-disabled-text-color: $list-group-disabled-color;
// $list-group-link-color: #555;
// $list-group-link-hover-color: $list-group-link-color;
// $list-group-link-heading-color: #333;
//
// // Panels
// $panel-bg: #fff;
// $panel-body-padding: 15px;
// $panel-heading-padding: 10px 15px;
// $panel-footer-padding: $panel-heading-padding;
// $panel-border-radius: $border-radius-base;
// $panel-inner-border: #ddd;
// $panel-footer-bg: #f5f5f5;
// $panel-default-text: $gray-dark;
// $panel-default-border: #ddd;
// $panel-default-heading-bg: #f5f5f5;
// $panel-primary-text: #fff;
// $panel-primary-border: $brand-primary;
// $panel-primary-heading-bg: $brand-primary;
// $panel-success-text: $state-success-text;
// $panel-success-border: $state-success-border;
// $panel-success-heading-bg: $state-success-bg;
// $panel-info-text: $state-info-text;
// $panel-info-border: $state-info-border;
// $panel-info-heading-bg: $state-info-bg;
// $panel-warning-text: $state-warning-text;
// $panel-warning-border: $state-warning-border;
// $panel-warning-heading-bg: $state-warning-bg;
// $panel-danger-text: $state-danger-text;
// $panel-danger-border: $state-danger-border;
// $panel-danger-heading-bg: $state-danger-bg;
//
// // Thumbnails
// $thumbnail-padding: 4px;
// $thumbnail-bg: $body-bg;
// $thumbnail-border: #ddd;
// $thumbnail-border-radius: $border-radius-base;
// $thumbnail-caption-color: $text-color;
// $thumbnail-caption-padding: 9px;
//
// // Wells
// $well-bg: #f5f5f5;
// $well-border: darken($well-bg, 7%);
//
// // Badges
// $badge-color: #fff;
// $badge-link-hover-color: #fff;
// $badge-bg: $gray-light;
// $badge-active-color: $link-color;
// $badge-active-bg: #fff;
// $badge-font-weight: bold;
// $badge-line-height: 1;
// $badge-border-radius: 10px;
//
// // Breadcrumbs
// $breadcrumb-padding-vertical: 8px;
// $breadcrumb-padding-horizontal: 15px;
// $breadcrumb-bg: #f5f5f5;
// $breadcrumb-color: #ccc;
// $breadcrumb-active-color: $gray-light;
// $breadcrumb-separator: "/";
//
// // Carousel
// $carousel-text-shadow: 0 1px 2px rgba(0,0,0,.6);
// $carousel-control-color: #fff;
// $carousel-control-width: 15%;
// $carousel-control-opacity: .5;
// $carousel-control-font-size: 20px;
// $carousel-indicator-active-bg: #fff;
// $carousel-indicator-border-color: #fff;
// $carousel-caption-color: #fff;
//
// // Close
// $close-font-weight: bold;
// $close-color: #000;
// $close-text-shadow: 0 1px 0 #fff;
//
// // Code
// $code-color: #c7254e;
// $code-bg: #f9f2f4;
// $kbd-color: #fff;
// $kbd-bg: #333;
// $pre-bg: #f5f5f5;
// $pre-color: $gray-dark;
// $pre-border-color: #ccc;
// $pre-scrollable-max-height: 340px;
//
// // Type
// $component-offset-horizontal: 180px;
// $text-muted: $gray-light;
// $abbr-border-color: $gray-light;
// $headings-small-color: $gray-light;
// $blockquote-small-color: $gray-light;
// $blockquote-font-size: ($font-size-base * 1.25);
// $blockquote-border-color: $gray-lighter;
// $page-header-border-color: $gray-lighter;
// $dl-horizontal-offset: $component-offset-horizontal;
// $dl-horizontal-breakpoint: $grid-float-breakpoint;
// $hr-border: $gray-lighter;

View file

@ -14,7 +14,7 @@
// Active tab with error should retain error color if clicked on again
.nav-tabs > li.active.has-error > a {
color: $color-apple-blossom;
color: $brand-danger;
}
@media (max-width: 886px) {
@ -58,7 +58,7 @@
float: none;
}
@media (max-width: 1000px) {
@media (max-width: 1188px) {
.navbar-header {
float: none;
}

View file

@ -8,15 +8,15 @@
.task-due-date,
.task-state-label {
.alert-green {
color: $color-saturated-green;
color: $brand-success;
}
.alert-yellow {
color: $color-candlelight;
color: $brand-warning;
}
.alert-red {
color: $color-milano-red;
color: $brand-danger;
}
}

View file

@ -31,7 +31,7 @@
padding-top: 10px;
&:hover {
background-color: $color-mystic;
background-color: $color-gainsboro;
}
&.no-notifications {
@ -40,7 +40,7 @@
}
.unseen {
border-left: 4px solid $color-theme-primary;
border-left: 4px solid $brand-primary;
}
.text-center {
@ -49,54 +49,39 @@
}
.assignment {
background-color: $color-theme-primary;
background-color: $brand-primary;
border-radius: 50%;
color: $color-wild-sand;
color: $color-concrete;
display: inline-block;
font-size: 13px;
font-size: $font-size-small;
height: 30px;
padding-top: 5px;
width: 30px;
}
.system-message {
background-color: $color-theme-secondary;
background-color: $brand-success;
border-radius: 50%;
color: $color-wild-sand;
color: $color-concrete;
display: inline-block;
font-size: 13px;
font-size: $font-size-small;
height: 30px;
padding-top: 5px;
width: 30px;
}
.deliver {
background-color: $color-orange;
background-color: $brand-warning;
border-radius: 50%;
color: $color-wild-sand;
color: $color-concrete;
display: inline-block;
font-size: 13px;
font-size: $font-size-small;
height: 30px;
padding-top: 5px;
width: 30px;
}
}
.notifications-footer {
background-color: $color-mystic;
.btn-more-notifications {
border-bottom: 1px solid $color-alto;
border-left: 1px solid $color-alto;
border-right: 1px solid $color-alto;
margin: 0;
padding: 8px;
text-align: center;
&:hover {
background-color: $color-mystic;
}
}
.btn-more-notifications {
margin-top: 15px;
}

View file

@ -22,71 +22,26 @@ $toggle-btn-size: 50px;
transition: all 0.5s ease;
#sidebar-wrapper {
background-color: $color-alto;
z-index: 1000;
position: fixed;
width: $wrapper-width;
left: $wrapper-width;
background-color: $color-white;
box-shadow: 1px 3px 6px $color-alto;
height: 100%;
margin-left: -$wrapper-width;
-webkit-transition: all 0.5s ease;
position: fixed;
-moz-transition: all 0.5s ease;
-o-transition: all 0.5s ease;
-webkit-transition: all 0.5s ease;
transition: all 0.5s ease;
width: $wrapper-width;
z-index: 999;
#slide-panel {
height: 100%;
.sidebar-header {
height: $toggle-btn-size;
background: $color-theme-primary;
border-bottom: 2px solid darken($color-theme-primary, 10%);
.sidebar-header-title {
width: inherit;
color: $color-white;
display: inline-block;
margin-left: 15px;
margin-top: 6px;
text-transform: uppercase;
max-width: ($wrapper-width - $toggle-btn-size);
overflow: hidden;
text-overflow: ellipsis;
opacity: 1;
// Animations
@include transition(opacity 0.5s ease);
}
}
.sidebar-header-toggle {
height: $toggle-btn-size;
width: $toggle-btn-size;
margin-left: ($wrapper-width - $toggle-btn-size);
margin-top: -$toggle-btn-size;
font-size: 20pt;
background: $color-theme-primary;
border-left: 2px solid darken($color-theme-primary, 10%);
border-bottom: 2px solid darken($color-theme-primary, 10%);
// Animations
@include transition(margin-left 0.5s ease);
span {
margin: 10px;
color: $color-white;
// Animations
@include rotate-animation(0.5s, 180deg);
@include transition(color 0.5s ease);
}
}
.tree {
overflow-y: auto;
margin-bottom: 0;
padding-top: 15px;
opacity: 1;
overflow-y: auto;
padding: 20px 0;
// Animations
@include transition(opacity 0.5s ease);
@ -99,32 +54,10 @@ $toggle-btn-size: 50px;
padding-left: 0;
#sidebar-wrapper {
margin-left: 0;
width: 0;
#slide-panel {
.sidebar-header .sidebar-header-title {
width: 0;
opacity: 0;
@include transition(width 0.5s ease);
@include transition(opacity 0.5s ease);
}
.sidebar-header-toggle {
margin-left: 0;
background: none;
border: none;
@include transition(margin-left 0.5s ease);
span {
color: darken($color-theme-primary, 10%);
@include rotate-animation(0.5s, 0deg);
@include transition(color 0.5s ease);
}
}
.tree {
opacity: 0;

View file

@ -1,65 +1,105 @@
@import 'constants';
@import "constants";
@import "mixins";
.tree {
height: 100%;
padding-bottom: 30px;
}
.tree > ul {
margin-bottom: 0;
}
.tree ul {
padding-left: 0;
}
.tree li {
list-style-type: none;
margin: 0;
padding: 5px 5px 5px 15px;
position: relative;
&.active > span {
ul {
padding-left: 0;
}
> ul {
margin-bottom: 0;
}
.no-indent {
background-color: $color-white;
border: 1px solid $color-white;
border-radius: 4px;
font-weight: bold;
}
&.active:not span.tree-link:hover {
text-decoration: underline;
.first-indent {
background-color: $color-concrete;
padding-left: 30px;
}
&.leaf {
padding-left: 10px;
.tree-link::before {
content: "\25B8";
.second-indent {
background-color: $color-concrete;
padding-left: 60px;
}
li {
list-style-type: none;
margin: 0;
position: relative;
span {
display: block;
padding: 15px;
&.my-module-group-element {
border: 0;
padding: 0;
}
&.glyphicon-map-marker {
border: 0;
display: inline-block;
padding: 0;
}
}
&.active > span {
background-color: $brand-primary;
color: $color-white;
font-weight: bold;
&.first-indent > span,
&.no-indent > span {
border: 0;
display: inline-block;
padding: 0;
}
}
i.no-arrow {
padding-left: 15px;
}
i.glyphicon {
cursor: pointer;
font-size: 9pt;
&.expanded {
@include rotate(90deg);
}
}
// Links are recolored
a {
color: $color-emperor;
&:hover {
color: $brand-primary;
}
}
.border-custom {
box-shadow: 0 -.25px 0 .25px $color-alto;
margin: 0 15px;
}
}
& i.glyphicon {
font-size: 9pt;
#settings {
.glyphicon-triangle-right {
margin-left: -15px;
}
&.expanded {
@include rotate(45deg);
.no-indent {
padding-left: 30px;
}
.first-indent {
padding-left: 45px;
}
}
/* Links are recolored */
a {
color: $color-emperor;
&:hover {
color: $color-theme-primary;
}
}
span {
display:inline-block;
padding:3px 8px;
}
}
.tree li.parent_li>span {
display: block;
}
.tree li:last-child::before {
height:30px;
}

View file

@ -2,8 +2,18 @@
@import "mixins";
// Some color definitions
$color-group-hover: $color-theme-primary;
$color-module-hover: $color-theme-secondary;
$color-group-hover: $color-alabaster;
$color-module-hover: $brand-primary;
#new-project-modal,
#edit-project-modal,
#copy-to-repository-modal {
.btn-group label.btn-toggle:not(.active) {
background-color: $color-white;
border-color: $color-silver;
color: $color-emperor;
}
}
/* Canvas index page */
@ -32,8 +42,6 @@ $color-module-hover: $color-theme-secondary;
}
#canvas-new-module {
margin-left: 10px;
.hbtn-default {
opacity: 1;
width: 0;
@ -61,14 +69,13 @@ $color-module-hover: $color-theme-secondary;
}
#diagram-container {
/* for IE10+ touch devices */
touch-action: none;
height: 650px;
background: $color-dove-gray;
@include box-shadow(0px 0px 2px 1px $color-dove-gray);
overflow: hidden;
@include box-shadow(0 0 2px 1px $color-silver);
background: $color-silver;
cursor: move;
height: 650px;
overflow: hidden;
// for IE10+ touch devices
touch-action: none;
}
.diagram {
@ -112,7 +119,7 @@ $color-module-hover: $color-theme-secondary;
cursor: pointer;
&:hover {
color: $color-theme-primary;
color: $brand-primary;
padding: 2px 9px 4px 9px;
}
}
@ -122,7 +129,7 @@ $color-module-hover: $color-theme-secondary;
border: 2px solid green;
}
.jsplumb-drag .title {
background-color: $color-theme-primary !important;
background-color: $brand-primary !important;
color: $color-white !important;
}
path, ._jsPlumb_endpoint {
@ -132,7 +139,7 @@ path, ._jsPlumb_endpoint {
fill: $color-white;
}
.ep-hover svg * {
fill: $color-theme-primary;
fill: $brand-primary;
}
/* EDIT MODE MODULE */
@ -140,16 +147,16 @@ path, ._jsPlumb_endpoint {
opacity: 0.7;
}
.module.dragged > .panel-heading {
background-color: $color-theme-primary;
background-color: $brand-primary;
color: $color-white;
}
.module.collided {
.overlay {
display: block;
z-index: 21;
background-color: $color-milano-red;
border: 1px solid $color-milano-red;
@include box-shadow(0 0 0 1pt $color-milano-red);
background-color: $brand-danger;
border: 1px solid $brand-danger;
@include box-shadow(0 0 0 1pt $brand-danger);
border-radius: 4px;
position: absolute;
top: 0;
@ -223,28 +230,28 @@ path, ._jsPlumb_endpoint {
}
&.alert-green .panel-body {
color: $color-saturated-green;
color: $brand-success;
font-weight: bold;
.due-date-link {
color: $color-saturated-green;
color: $brand-success;
}
}
&.alert-yellow .panel-body {
color: $color-candlelight;
color: $brand-warning;
font-weight: bold;
.due-date-link {
color: $color-candlelight;
color: $brand-warning;
}
}
&.alert-red .panel-body {
color: $color-milano-red;
color: $brand-danger;
font-weight: bold;
.due-date-link {
color: $color-milano-red;
color: $brand-danger;
}
}
}
@ -265,18 +272,18 @@ path, ._jsPlumb_endpoint {
}
&.alert-green {
border-color: $color-saturated-green;
border-color: $brand-success;
border-radius: 8px;
border-width: 4px;
}
&.alert-yellow {
border-color: $color-candlelight;
border-color: $brand-warning;
border-width: 4px;
border-radius: 8px;
}
&.alert-red {
border-color: $color-milano-red;
border-color: $brand-danger;
border-width: 4px;
border-radius: 8px;
}
@ -326,13 +333,13 @@ path, ._jsPlumb_endpoint {
span {
font-weight: bold;
font-size: 16px;
font-size: $font-size-large;
text-transform: uppercase;
display: block;
margin-top: 20%;
a {
color: $color-mine-shaft;
color: $color-emperor;
}
}
@ -344,14 +351,14 @@ path, ._jsPlumb_endpoint {
}
&.alert-green {
border-color: $color-saturated-green;
border-color: $brand-success;
}
&.alert-yellow {
border-color: $color-candlelight;
border-color: $brand-warning;
}
&.alert-red {
border-color: $color-milano-red;
border-color: $brand-danger;
}
}
@ -362,7 +369,7 @@ li.group-hover {
}
li.module-hover {
a {
color: $color-theme-primary;
color: $brand-primary;
text-decoration: underline;
}
}
@ -400,7 +407,7 @@ li.module-hover {
margin-top: 5px;
font-family: 'Glyphicons Halflings';
color: $color-white;
font-size: 12pt;
font-size: $font-size-large;
&:before {
content: "\e221";
@ -410,9 +417,9 @@ li.module-hover {
}
}
.glyphicon {
.glyphicon, .fas {
color: $color-white;
font-size: 12pt;
font-size: $font-size-h6;
}
a.btn-link {
@ -430,6 +437,7 @@ li.module-hover {
.create-new-tag-btn {
margin-right: 15px;
margin-top: 10px;
}
}
@ -463,7 +471,7 @@ li.module-hover {
.experiment-no-description {
color: $color-alto;
display: block;
font-size: 18px;
font-size: $font-size-h4;
font-weight: bold;
text-align: center;
@ -508,7 +516,7 @@ li.module-hover {
}
.big-plus {
color: $color-mystic;
color: $color-gainsboro;
display: block;
font-size: 250px;
margin: 20px 0;

View file

@ -0,0 +1,62 @@
@import 'constants';
.btn-open-file {
position: relative;
overflow: hidden;
& > input[type=file] {
position: absolute;
top: 0;
right: 0;
min-width: 100%;
min-height: 100%;
font-size: 100px;
text-align: right;
filter: alpha(opacity=0);
opacity: 0;
outline: none;
background: white;
cursor: inherit;
display: block;
}
}
#protocols-index,
#load-from-repository-modal {
padding: 0;
.nav-tabs > li {
text-transform: uppercase;
a {
color: $color-silver-chalice;
padding: 15px 20px;
}
a:hover {
border-color: $color-white;
}
}
.nav > li > a:hover,
.nav > li > a:focus {
background-color: $color-white;
color: $color-emperor;
}
.nav-tabs > li.active > a,
.nav-tabs > li.active > a:hover,
.nav-tabs > li.active > a:focus {
background-color: $color-white;
border: 0;
box-shadow: 0 4px 0 $brand-primary;
color: $color-emperor;
font-weight: bold;
margin-bottom: 4px;
}
.tab-pane-settings {
border: 0;
padding: 25px 20px;
}
}

View file

@ -8,27 +8,30 @@
/* New page navbar */
.navbar-report {
border-left: none;
border-top: none;
border-right: none;
border-bottom: 4px solid $color-silver;
background: $color-concrete !important;
margin-bottom: 0;
background: $color-white;
border-bottom: 1px solid $color-gainsboro;
border-left: 0;
border-right: 0;
border-top: 15px solid $color-concrete;
margin-bottom: 15px;
min-width: 320px;
padding: 0 15px;
padding: 25px 20px;
position: sticky;
top: 50px;
z-index: 500;
position: fixed;
width: 100%;
div.row {
margin-right: 0;
}
#report-menu {
form {
display: inline-block;
}
.form-group {
margin-bottom: 0;
}
}
& > div.row {
@ -54,43 +57,6 @@ label {
}
}
/* New page sidebar */
.report-sidebar-wrapper {
background-color: $color-white !important;
}
// Some additional styling on the treeview
.report-tree {
li {
padding: 0 0 0 15px;
a.report-nav-link:visited {
text-decoration: none;
}
a.report-nav-link:hover {
text-decoration: none;
}
[data-type='step']:not(.parent_li) {
padding-left: 27px;
}
}
}
.report-sidebar-panel-description {
margin: 10px 10px 0 10px;
}
.report-item-elements {
margin-top: 10px !important;
margin-left: 15px !important;
li {
margin: 5px 5px 5px 15px;
}
ul {
padding-left: 15px !important;
}
}
/**
* Global fix for handsontable
@ -108,26 +74,34 @@ label {
/* New page content */
#report-new {
margin-top: -15px;
}
.report-body {
background: $color-dove-gray;
background: $color-silver;
}
.report-container {
background: $color-silver;
box-shadow: 0 0 2px 1px $color-silver;
overflow-x: auto;
overflow-y: auto;
padding-top: 30px;
padding-bottom: 30px;
padding: 30px;
padding-left: 0;
width: auto;
}
#report-content {
color: $color-black;
@include box-shadow(0 0 58px -10px $color-black);
background: $color-white;
@include box-shadow(0px 0px 58px -10px $color-black);
max-width: 800px;
min-width: 230px;
min-height: 1200px;
color: $color-black;
margin-left: auto;
margin-right: auto;
margin-top: -15px;
max-width: 800px;
min-height: 1200px;
min-width: 230px;
padding: 45px;
}
@ -148,7 +122,7 @@ label {
opacity: 0.7;
padding: 15px;
border-radius: 5px;
border: 4px $color-theme-primary solid;
border: 4px $brand-primary solid;
.plus-icon {
bottom: 16px !important;
@ -166,7 +140,7 @@ label {
.filler {
display: block;
height: 4px;
background-color: $color-theme-primary;
background-color: $brand-primary;
border-radius: 1px;
margin-top: 8px;
margin-bottom: 8px;
@ -183,7 +157,7 @@ label {
}
.plus-icon {
color: $color-theme-primary;
color: $brand-primary;
display: block;
text-align: center;
width: 40px;
@ -201,7 +175,7 @@ label {
opacity: 1.0;
.filler {
background-color: $color-theme-primary;
background-color: $brand-primary;
.plus-icon span {
font-weight: bold;
@ -223,7 +197,7 @@ label {
}
.controls {
margin-right: 15px;
font-size: 12pt;
font-size: $font-size-h6;
opacity: 0.05;
}
}
@ -240,8 +214,8 @@ label {
}
&:hover {
background-color: $color-mystic;
@include box-shadow(0 0 2px 15px $color-mystic);
background-color: $color-gainsboro;
@include box-shadow(0 0 2px 15px $color-gainsboro);
& > .report-element-header {
@ -266,7 +240,7 @@ label {
}
&:hover > .report-element-body .project-name {
color: $color-theme-primary;
color: $brand-primary;
}
}
@ -297,7 +271,7 @@ label {
}
&:hover > .report-element-body .module-name {
color: $color-theme-primary;
color: $brand-primary;
}
}
@ -329,7 +303,7 @@ label {
}
&:hover > .report-element-header {
color: $color-theme-primary;
color: $brand-primary;
}
.report-element-body {
@ -368,7 +342,7 @@ label {
/** Step element style */
.report-step-element {
&:hover > .report-element-body .step-name {
color: $color-theme-primary;
color: $brand-primary;
}
}
@ -388,7 +362,7 @@ label {
&:hover > .report-element-header {
.attachment-icon {
color: $color-theme-primary;
color: $brand-primary;
}
}
}
@ -403,7 +377,7 @@ label {
&:hover > .report-element-header {
.table-name {
color: $color-theme-primary;
color: $brand-primary;
}
}
}
@ -417,7 +391,7 @@ label {
}
&:hover > .report-element-header .file-name {
color: $color-theme-primary;
color: $brand-primary;
}
}
@ -438,7 +412,7 @@ label {
}
&:hover > .report-element-header .checklist-name {
color: $color-theme-primary;
color: $brand-primary;
}
}
@ -475,7 +449,7 @@ label {
&:hover > .report-element-header {
.comments-icon,.comments-name {
color: $color-theme-primary;
color: $brand-primary;
}
}
}
@ -504,7 +478,7 @@ label {
&:hover > .report-element-header {
.samples-icon,.samples-name {
color: $color-theme-primary;
color: $brand-primary;
}
}
}
@ -524,7 +498,7 @@ label {
&:hover > .report-element-header {
.repository-icon,
.repository-name {
color: $color-theme-primary;
color: $brand-primary;
}
}
}
@ -559,7 +533,17 @@ label {
&:hover > .report-element-header {
.activity-icon,.activity-name {
color: $color-theme-primary;
color: $brand-primary;
}
}
}
#save-PDF-to-inventory-warnings {
margin-top: 30px;
}
.save-PDF-to-inventory-alerts {
.danger {
color: $brand-danger;
}
}

View file

@ -1,3 +1,5 @@
@import "constants";
.repositories-dropdown-menu {
height: auto;
max-height: 400px;
@ -24,3 +26,8 @@
float: right;
text-align: inherit;
}
.breadcrumb.breadcrumb-repository {
background-color: $color-concrete;
margin-bottom: 15px;
}

View file

@ -45,7 +45,7 @@
}
.glyphicon-ok {
color: $color-theme-secondary;
color: $brand-default;
}
.glyphicon {

View file

@ -9,5 +9,5 @@
padding-top: 20px;
}
.search-asset-text-data{
font-size: 14px;
font-size: $font-size-base;
}

View file

@ -11,8 +11,7 @@
margin-bottom: 10px;
& > div > span.pull-left {
margin-top: 8px;
font-size: 1.2em;
margin-top: 10px;
}
}
}

View file

@ -0,0 +1,364 @@
@import 'constants';
@import "mixins";
#main-nav {
box-shadow: 0 3px 6px $color-alto;
margin-bottom: 0;
}
#notifications-dropdown {
.fa-bell {
font-size: $font-size-large;
position: absolute;
}
#count-notifications {
background-color: $brand-primary;
border-radius: 5px;
color: $color-concrete;
display: none;
font-size: 11px;
font-weight: bold;
margin-left: 12px;
padding: 1px 6px;
position: relative;
z-index: 1;
}
}
.navbar {
border-radius: 0;
padding-right: 15px;
}
.navbar-default {
background-color: $color-white;
border-color: $color-alto;
}
.navbar-default .navbar-brand {
& > img {
margin-top: -7px;
max-width: 55px;
max-height: 38px;
}
}
.dropdown-notifications {
max-height: 500px;
overflow-x: hidden;
overflow-y: scroll;
padding-bottom: 0;
padding-top: 0;
width: 450px;
word-wrap: break-word;
.notifications-no-recent {
padding-bottom: 10px;
padding-left: 10px;
padding-top: 10px;
}
.notification {
border-bottom: 1px solid $color-alto;
padding-bottom: 10px;
padding-top: 10px;
&:hover {
background-color: $color-concrete;
}
}
.unseen {
border-left: 4px solid $brand-primary;
}
.text-center {
margin-left: 12px;
}
.avatar {
top: 0px;
margin-top: 5px;
height: 45px;
width: 45px;
}
.assignment {
background-color: $brand-primary;
border-radius: 50%;
color: $color-concrete;
display: block;
font-size: $font-size-h3;
height: 45px;
padding-top: 5px;
width: 45px;
}
.deliver {
background-color: $brand-warning;
border-radius: 50%;
color: $color-concrete;
display: block;
font-size: $font-size-h3;
height: 45px;
padding-top: 5px;
width: 45px;
}
.system-message {
background-color: $brand-success;
border-radius: 50%;
color: $color-concrete;
display: block;
font-size: $font-size-h3;
height: 45px;
padding-top: 8px;
width: 45px;
}
.notifications-dropdown-footer {
background-color: $color-gainsboro;
padding: 8px;
text-align: center;
a:hover {
background-color: $color-gainsboro;
}
}
}
.notification {
padding-right: 8px;
word-wrap: break-word;
}
#search-menu {
padding-right: 0;
.nav {
position: relative;
z-index: 1000;
}
}
#search-content {
padding-left: 0;
}
#search-container {
padding-left: 45px;
}
/** Search */
.nav-search {
li.disabled {
opacity: .8;
.badge {
background-color: $color-emperor;
opacity: .8;
}
}
.repositories-team {
padding: 10px 15px;
&.active {
color: $brand-primary;
}
}
.repository-search {
padding-left: 15px;
}
}
.notification-settings-container {
margin-bottom: 50px;
margin-top: 50px;
h4 {
font-weight: 600;
margin-bottom: 2rem;
}
.col-sm-4 {
padding-left: 5rem;
padding-top: .5rem;
word-break: break-word;
}
.col-sm-2 {
padding-left: 3rem;
padding-top: .7rem;
}
@media (max-width: 768px) {
.col-sm-4 {
margin-bottom: 1rem;
padding-left: 1.8rem;
}
.col-sm-2 {
padding-left: 1.8rem;
}
}
.btn-group {
margin-bottom: 15px;
}
.assignment {
background-color: $brand-primary;
border-radius: 50%;
color: $color-concrete;
display: block !important;
font-size: $font-size-base;
height: 30px;
margin-right: 15px;
padding: 7px;
padding-bottom: 5px;
padding-top: 5px;
width: 30px;
}
.system-message {
background-color: $brand-success;
border-radius: 50%;
color: $color-concrete;
display: block !important;
font-size: $font-size-base;
height: 30px;
margin-right: 15px;
padding: 8px;
padding-bottom: 5px;
padding-top: 5px;
width: 30px;
}
.img-circle {
margin-right: 15px;
}
}
// Global team switch
#team-switch {
word-wrap: break-word;
.team-name {
margin-left: 17px;
}
.glyphicon-ok-sign {
color: $brand-primary;
margin-left: -2px;
margin-right: 2px;
}
.team-name-item {
border-bottom: 1px solid $color-gainsboro;
padding-bottom: 8px;
padding-top: 5px;
}
li:last-child {
border-bottom: 0;
}
.btn-default:not(.dropdown-toggle) {
margin-left: 15px;
text-align: right;
width: 300px;
}
.btn-group {
margin-top: 8px;
}
.dropdown-menu {
border-radius: 0;
margin-top: 0;
padding-top: 0;
width: 100%;
}
i {
margin-right: 5px;
}
li {
display: block;
text-align: left;
word-wrap: break-word;
&:hover {
background-color: $color-concrete;
}
a {
color: $color-emperor;
display: block;
line-height: 1.6em;
padding: 3px 20px;
text-align: left;
text-decoration: none;
word-wrap: break-word;
}
}
}
#nav-team-switch {
margin-left: 30px;
}
.custom-nav-dropdown {
border: 1px solid $color-alto;
padding: 10px 0 10px 30px;
}
// Alert
.alert {
border-radius: 0;
margin-bottom: 0;
opacity: .86;
width: 100%;
&.alert-hidden {
display: none;
}
a#hide-alert {
margin-left: 15px;
}
&.alert-floating {
position: fixed;
top: 50px;
z-index: 1000;
}
}
#content-wrapper {
margin-top: 50px;
margin-left: 83px;
}
#search-bar {
padding-right: 0;
}
@media (max-width: 768px) {
#search-bar {
padding: 10px 30px;
}
}
// reset margins on small screens
@media (max-width: 1188px) {
#nav-team-switch {
margin-left: 0;
}
}

View file

@ -0,0 +1,92 @@
@import "constants";
@import "mixins";
.menu-bar {
background-color: $color-white;
box-shadow: 1px 3px 6px 0 $color-alto;
height: 100%;
left: 0;
overflow-x: hidden;
position: fixed;
width: 83px;
z-index: 1001;
padding-bottom: 50px;
.scroll-wrapper {
height: 100%;
padding-top: 16px;
width: 83px;
overflow-x: hidden;
}
ul.nav > li {
& > a {
color: $color-silver-chalice;
display: grid;
font-size: $font-size-h6;
margin-left: auto;
margin-right: auto;
padding: 10px;
text-align: center;
word-break: normal;
& > span {
padding-top: 4px;
}
}
&.active {
@include box-shadow(-4px 0 0 $brand-primary);
background-color: $color-gainsboro;
margin-left: 4px;
& > a {
color: $color-emperor;
}
}
}
ul.nav-bottom {
bottom: 0;
padding-bottom: 16px;
position: fixed;
width: inherit;
}
.dropup {
.dropdown-menu {
bottom: 0;
left: 99%;
margin-bottom: 0;
}
&.open > a {
color: $color-emperor;
}
}
#toggle-sidebar-btn > span {
@include rotate-animation(.5s, 0deg);
}
#toggle-sidebar-btn[data-shown] > span {
@include rotate-animation(.5s, 180deg);
}
#toggle-sidebar-btn:hover {
background-color: $color-concrete;
cursor: pointer;
}
#toggle-sidebar-btn.hidden2 {
visibility: hidden;
}
}
@media(max-height:510px) {
.menu-bar .nav-bottom {
position: relative;
width: auto;
}
}

View file

@ -0,0 +1,45 @@
@import "constants";
.repository-columns-body {
margin-top: 50px;
.list-group-item {
padding: 28px;
.controlls {
margin-top: -18px;
}
span {
margin-top: -10px;
}
}
}
#repository-columns-dropdown {
float: right;
}
.new-repository-button {
float: left;
margin-right: 5px;
}
.custom-alert-info {
background-color: $state-info-bg;
border: 1px solid $state-info-border;
color: $state-info-text;
margin: 10px 0;
opacity: .86;
padding: 15px;
width: 100%;
}
.repository-input-file-field {
display: inline-flex;
a {
color: $brand-danger;
margin-left: 5px;
}
}

File diff suppressed because it is too large Load diff

View file

@ -2,15 +2,17 @@ class ActivitiesController < ApplicationController
include ActivityHelper
def index
@vars = local_vars
respond_to do |format|
format.json do
render json: {
more_url: local_vars.fetch(:more_activities_url),
html: render_to_string(
partial: 'index.html.erb', locals: local_vars
partial: 'list.html.erb', locals: @vars
)
}
end
format.html
end
end

View file

@ -7,7 +7,6 @@ class ApplicationController < ActionController::Base
protect_from_forgery with: :exception, prepend: true
before_action :authenticate_user!
helper_method :current_team
before_action :generate_intro_tutorial, if: :is_current_page_root?
before_action :update_current_team, if: :user_signed_in?
around_action :set_time_zone, if: :current_user
layout 'main'
@ -66,19 +65,6 @@ class ApplicationController < ActionController::Base
private
def generate_intro_tutorial
if Rails.configuration.x.enable_tutorial &&
current_user.no_tutorial_done? &&
current_user.teams.where(created_by: current_user).count > 0 then
demo_cookie = seed_demo_data current_user
cookies[:tutorial_data] = {
value: demo_cookie,
expires: 1.week.from_now
}
current_user.update(tutorial_status: 1)
end
end
def update_current_team
if current_user.current_team_id.blank? &&
current_user.teams.count > 0

View file

@ -7,7 +7,6 @@ class AssetsController < ApplicationController
include ActionView::Context
include InputSanitizeHelper
include FileIconsHelper
include WopiHelper
before_action :load_vars
before_action :check_read_permission, except: :file_present
@ -31,32 +30,62 @@ class AssetsController < ApplicationController
render json: {
'asset-id' => @asset.id,
'image-tag-url' => @asset.url(:medium),
'preview-url' => large_image_url_asset_path(@asset),
'preview-url' => asset_file_preview_path(@asset),
'filename' => truncate(@asset.file_file_name,
length:
Constants::FILENAME_TRUNCATION_LENGTH),
'download-url' => download_asset_path(@asset),
'type' => asset_data_type(@asset),
'wopi-file-name' => wopi_asset_file_name(@asset, true),
'wopi-edit' => (wopi_asset_edit_button(@asset) if wopi_file?(@asset)),
'wopi-view' => (wopi_asset_view_button(@asset) if wopi_file?(@asset))
'type' => asset_data_type(@asset)
}, status: 200
end
end
end
end
def large_image_url
def file_preview
response_json = {
'type' => (@asset.is_image? ? 'image' : 'file'),
'filename' => truncate(@asset.file_file_name,
length:
Constants::FILENAME_TRUNCATION_LENGTH),
'download-url' => download_asset_path(@asset)
}
if @asset.is_image?
response_json.merge!(
'processing' => @asset.file.processing?,
'large-preview-url' => @asset.url(:large),
'processing-url' => image_tag('medium/processing.gif')
)
else
response_json.merge!(
'processing' => @asset.file.processing?,
'preview-icon' => render_to_string(
partial: 'shared/file_preview_icon.html.erb',
locals: { asset: @asset }
)
)
end
if wopi_file?(@asset)
can_edit =
if @assoc.class == Step
can_manage_protocol_in_module?(@protocol) ||
can_manage_protocol_in_repository?(@protocol)
elsif @assoc.class == Result
can_manage_module?(@my_module)
elsif @assoc.class == RepositoryCell
can_manage_repository_rows?(@repository.team)
end
response_json['wopi-controls'] = render_to_string(
partial: 'shared/file_wopi_controlls.html.erb',
locals: { asset: @asset, can_edit: can_edit }
)
end
respond_to do |format|
format.json do
render json: {
'large-preview-url' => @asset.url(:large),
'filename' => truncate(@asset.file_file_name,
length:
Constants::FILENAME_TRUNCATION_LENGTH),
'download-url' => download_asset_path(@asset),
'type' => (@asset.is_image? ? 'image' : 'file')
}
render json: response_json
end
end
end
@ -103,13 +132,17 @@ class AssetsController < ApplicationController
step_assoc = @asset.step
result_assoc = @asset.result
repository_cell_assoc = @asset.repository_cell
@assoc = step_assoc unless step_assoc.nil?
@assoc = result_assoc unless result_assoc.nil?
@assoc = repository_cell_assoc unless repository_cell_assoc.nil?
if @assoc.class == Step
@protocol = @asset.step.protocol
else
elsif @assoc.class == Result
@my_module = @assoc.my_module
elsif @assoc.class == RepositoryCell
@repository = @assoc.repository_column.repository
end
end
@ -119,6 +152,8 @@ class AssetsController < ApplicationController
can_read_protocol_in_repository?(@protocol)
elsif @assoc.class == Result
render_403 and return unless can_read_experiment?(@my_module.experiment)
elsif @assoc.class == RepositoryCell
render_403 and return unless can_read_team?(@repository.team)
end
end
@ -128,6 +163,8 @@ class AssetsController < ApplicationController
can_manage_protocol_in_repository?(@protocol)
elsif @assoc.class == Result
render_403 and return unless can_manage_module?(@my_module)
elsif @assoc.class == RepositoryCell
render_403 and return unless can_manage_repository_rows?(@repository.team)
end
end

View file

@ -29,12 +29,29 @@ class AtWhoController < ApplicationController
end
end
def samples
def rep_items
res = SmartAnnotation.new(current_user, current_team, @query)
repository = Repository.find_by_id(params[:repository_id])
render_403 && return unless repository && can_read_team?(repository.team)
respond_to do |format|
format.json do
render json: {
res: res.samples,
res: res.repository_rows(repository),
status: :ok
}
end
end
end
def repositories
repositories =
@team.repositories.limit(Rails.configuration.x.repositories_limit)
respond_to do |format|
format.json do
render json: {
repositories: repositories.map do |r|
[r.id, r.name.truncate(Constants::ATWHO_REP_NAME_LIMIT)]
end.to_h,
status: :ok
}
end

View file

@ -7,19 +7,19 @@ class ExperimentsController < ApplicationController
include Rails.application.routes.url_helpers
before_action :set_experiment,
except: [:new, :create]
except: %i(new create)
before_action :set_project,
only: [:new, :create, :samples_index, :samples, :module_archive,
:clone_modal, :move_modal, :delete_samples]
only: %i(new create samples_index samples module_archive
clone_modal move_modal delete_samples)
before_action :load_projects_by_teams, only: %i(canvas samples module_archive)
before_action :check_view_permissions,
only: [:canvas, :module_archive]
only: %i(canvas module_archive)
before_action :check_manage_permissions, only: :edit
before_action :check_archive_permissions, only: :archive
before_action :check_clone_permissions, only: %i(clone_modal clone)
before_action :check_move_permissions, only: %i(move_modal move)
# except parameter could be used but it is not working.
layout :choose_layout
layout 'fluid'.freeze
# Action defined in SampleActions
DELETE_SAMPLES = 'Delete'.freeze
@ -348,6 +348,11 @@ class ExperimentsController < ApplicationController
params.require(:experiment).permit(:name, :description, :archived)
end
def load_projects_by_teams
@projects_by_teams = current_user.projects_by_teams(current_team.id,
nil, false)
end
def check_view_permissions
render_403 unless can_read_experiment?(@experiment)
end
@ -368,10 +373,6 @@ class ExperimentsController < ApplicationController
render_403 unless can_move_experiment?(@experiment)
end
def choose_layout
action_name.in?(%w(index archive)) ? 'main' : 'fluid'
end
def experiment_annotation_notification(old_text = nil)
smart_annotation_notification(
old_text: old_text,

View file

@ -6,20 +6,36 @@ class MyModulesController < ApplicationController
include ActionView::Helpers::UrlHelper
include ApplicationHelper
before_action :load_vars
before_action :load_vars_nested, only: %I[new create]
before_action :load_repository, only: %I[assign_repository_records
unassign_repository_records]
before_action :check_manage_permissions, only:
%i(destroy description due_date)
before_action :load_vars,
only: %i(show update destroy description due_date protocols
results samples activities activities_tab
assign_samples unassign_samples delete_samples
toggle_task_state samples_index archive
complete_my_module repository repository_index
assign_repository_records unassign_repository_records
unassign_repository_records_modal
assign_repository_records_modal)
before_action :load_vars_nested, only: %i(new create)
before_action :load_repository, only: %i(assign_repository_records
unassign_repository_records
unassign_repository_records_modal
assign_repository_records_modal
repository_index)
before_action :load_projects_by_teams, only: %i(protocols results activities
samples repository archive)
before_action :check_manage_permissions_archive, only: %i(update destroy)
before_action :check_manage_permissions, only: %i(description due_date)
before_action :check_view_permissions, only:
%i(show activities activities_tab protocols results samples samples_index
archive)
before_action :check_complete_module_permission, only: :complete_my_module
before_action :check_assign_repository_records_permissions, only:
%i(assign_repository_records unassign_repository_records)
before_action :check_assign_samples_permissions, only: %i(assign_samples
unassign_samples)
before_action :check_assign_repository_records_permissions,
only: %i(unassign_repository_records_modal
assign_repository_records_modal
assign_repository_records
unassign_repository_records
assign_samples
unassign_samples)
layout 'fluid'.freeze
@ -62,7 +78,7 @@ class MyModulesController < ApplicationController
@last_activity_id = params[:from].to_i || 0
@per_page = 10
@activities = @my_module.last_activities(@last_activity_id, @per_page +1 )
@activities = @my_module.last_activities(@last_activity_id, @per_page + 1)
@more_activities_url = ""
@overflown = @activities.length > @per_page
@ -128,12 +144,6 @@ class MyModulesController < ApplicationController
end
def update
render_403 && return unless if my_module_params[:archived] == 'false'
can_restore_module?(@my_module)
else
can_manage_module?(@my_module)
end
@my_module.assign_attributes(my_module_params)
@my_module.last_modified_by = current_user
description_changed = @my_module.description_changed?
@ -364,38 +374,51 @@ class MyModulesController < ApplicationController
# AJAX actions
def repository_index
@repository = Repository.find_by_id(params[:repository_id])
if @repository.nil? || !can_read_team?(@repository.team)
render_403
else
respond_to do |format|
format.html
format.json do
render json: ::RepositoryDatatable.new(view_context,
@repository,
@my_module,
current_user)
end
end
end
@draw = params[:draw].to_i
per_page = params[:length] == '-1' ? 100 : params[:length].to_i
page = (params[:start].to_i / per_page) + 1
records = RepositoryDatatableService.new(@repository,
params,
current_user,
@my_module)
@assigned_rows = records.assigned_rows
@repository_row_count = records.repository_rows.count
@columns_mappings = records.mappings
@repository_rows = records.repository_rows.page(page).per(per_page)
render 'repository_rows/index.json'
end
# Submit actions
def assign_repository_records
if params[:selected_rows].present? && params[:repository_id].present?
records_names = []
downstream = ActiveModel::Type::Boolean.new.cast(params[:downstream])
params[:selected_rows].each do |id|
record = RepositoryRow.find_by_id(id)
next if !record || @my_module.repository_rows.include?(record)
record.last_modified_by = current_user
record.save
records_names << record.name
MyModuleRepositoryRow.create!(
my_module: @my_module,
repository_row: record,
assigned_by: current_user
)
RepositoryRow
.where(id: params[:selected_rows],
repository_id: params[:repository_id])
.find_each do |record|
unless @my_module.repository_rows.include?(record)
record.last_modified_by = current_user
record.save
MyModuleRepositoryRow.create!(
my_module: @my_module,
repository_row: record,
assigned_by: current_user
)
records_names << record.name
end
next unless downstream
@my_module.downstream_modules.each do |my_module|
next if my_module.repository_rows.include?(record)
MyModuleRepositoryRow.create!(
my_module: my_module,
repository_row: record,
assigned_by: current_user
)
end
end
if records_names.any?
@ -432,17 +455,29 @@ class MyModulesController < ApplicationController
def unassign_repository_records
if params[:selected_rows].present? && params[:repository_id].present?
records = []
downstream = ActiveModel::Type::Boolean.new.cast(params[:downstream])
params[:selected_rows].each do |id|
record = RepositoryRow.find_by_id(id)
next unless record && @my_module.repository_rows.include?(record)
record.last_modified_by = current_user
record.save
records << record
end
records = RepositoryRow.assigned_on_my_module(params[:selected_rows],
@my_module)
@my_module.repository_rows.destroy(records & @my_module.repository_rows)
if downstream
@my_module.downstream_modules.each do |my_module|
assigned_records = RepositoryRow.assigned_on_my_module(
params[:selected_rows],
my_module
)
my_module.repository_rows.destroy(
assigned_records & my_module.repository_rows
)
assigned_records.update_all(last_modified_by_id: current_user.id)
end
end
# update last last_modified_by
records.update_all(last_modified_by_id: current_user.id)
if records.any?
Activity.create(
type_of: :unassign_repository_record,
@ -475,6 +510,28 @@ class MyModulesController < ApplicationController
end
end
def unassign_repository_records_modal
selected_rows = params[:selected_rows]
modal = render_to_string(
partial: 'my_modules/modals/unassign_repository_records_modal.html.erb',
locals: { my_module: @my_module,
repository: @repository,
selected_rows: selected_rows }
)
render json: { html: modal }, status: :ok
end
def assign_repository_records_modal
selected_rows = params[:selected_rows]
modal = render_to_string(
partial: 'my_modules/modals/assign_repository_records_modal.html.erb',
locals: { my_module: @my_module,
repository: @repository,
selected_rows: selected_rows }
)
render json: { html: modal }, status: :ok
end
# Complete/uncomplete task
def toggle_task_state
respond_to do |format|
@ -596,11 +653,25 @@ class MyModulesController < ApplicationController
def load_repository
@repository = Repository.find_by_id(params[:repository_id])
render_404 unless @repository && can_read_team?(@repository.team)
render_404 unless @repository
render_403 unless can_read_team?(@repository.team)
end
def load_projects_by_teams
@projects_by_teams = current_user.projects_by_teams(current_team.id,
nil, false)
end
def check_manage_permissions
render_403 unless can_manage_module?(@my_module)
render_403 && return unless can_manage_module?(@my_module)
end
def check_manage_permissions_archive
render_403 && return unless if my_module_params[:archived] == 'false'
can_restore_module?(@my_module)
else
can_manage_module?(@my_module)
end
end
def check_view_permissions

View file

@ -4,20 +4,22 @@ class ProjectsController < ApplicationController
include TeamsHelper
include InputSanitizeHelper
before_action :load_vars, only: [:show, :edit, :update,
:notifications, :reports,
:samples, :experiment_archive,
:delete_samples, :samples_index]
before_action :generate_intro_demo, only: :index
before_action :load_vars, only: %i(show edit update
notifications reports
samples experiment_archive
delete_samples samples_index)
before_action :load_projects_by_teams, only: %i(index show samples archive
experiment_archive)
before_action :load_archive_vars, only: :archive
before_action :check_view_permissions, only: %i(show reports notifications
samples experiment_archive
samples_index)
before_action :check_create_permissions, only: [ :new, :create ]
before_action :check_create_permissions, only: %i(new create)
before_action :check_manage_permissions, only: :edit
@filter_by_archived = false
# except parameter could be used but it is not working.
layout :choose_layout
layout 'fluid'
# Action defined in SampleActions
DELETE_SAMPLES = 'Delete'.freeze
@ -27,18 +29,6 @@ class ProjectsController < ApplicationController
current_team_switch(Team.find_by_id(params[:team]))
end
if current_user.teams.any?
@current_team_id = current_team.id if current_team
@current_team_id ||= current_user.teams.first.id
@current_sort = params[:sort].to_s
@projects_by_teams = current_user.projects_by_teams(@current_team_id,
@current_sort,
@filter_by_archived)
else
@projects_by_teams = []
end
@teams = current_user.teams
# New project for create new project modal
@ -46,7 +36,6 @@ class ProjectsController < ApplicationController
end
def archive
@filter_by_archived = true
index
end
@ -306,6 +295,12 @@ class ProjectsController < ApplicationController
private
def generate_intro_demo
return unless current_user.sign_in_count == 1
team = current_user.teams.where(created_by: current_user).first
seed_demo_data(current_user, team) if team && team.projects.blank?
end
def project_params
params.require(:project).permit(:name, :team_id, :visibility, :archived)
end
@ -318,6 +313,29 @@ class ProjectsController < ApplicationController
end
end
def load_projects_by_teams
if current_user.teams.any?
@current_team_id = current_team.id if current_team
@current_team_id ||= current_user.teams.first.id
@current_sort = params[:sort].to_s
@projects_by_teams = current_user.projects_by_teams(@current_team_id,
@current_sort,
false)
else
@projects_by_teams = []
end
end
def load_archive_vars
if current_user.teams.any?
@archived_projects_by_teams =
current_user.projects_by_teams(@current_team_id, @current_sort, true)
else
@projects_by_teams = []
end
end
def check_view_permissions
render_403 unless can_read_project?(@project)
end
@ -329,8 +347,4 @@ class ProjectsController < ApplicationController
def check_manage_permissions
render_403 unless can_manage_project?(@project)
end
def choose_layout
action_name.in?(['index', 'archive']) ? 'main' : 'fluid'
end
end

View file

@ -5,37 +5,11 @@ class ReportsController < ApplicationController
# used via target='_blank')
protect_from_forgery with: :exception, except: :generate
before_action :load_vars, only: [
:edit,
:update
]
before_action :load_vars_nested, only: [
:index,
:new,
:create,
:edit,
:update,
:generate,
:destroy,
:save_modal,
:project_contents_modal,
:experiment_contents_modal,
:module_contents_modal,
:step_contents_modal,
:result_contents_modal,
:project_contents,
:module_contents,
:step_contents,
:result_contents
]
before_action :check_view_permissions, only: :index
before_action :check_manage_permissions, only: %i(
BEFORE_ACTION_METHODS = %i(
new
create
edit
update
destroy
generate
save_modal
project_contents_modal
@ -47,12 +21,29 @@ class ReportsController < ApplicationController
module_contents
step_contents
result_contents
)
).freeze
layout 'fluid'
before_action :load_vars, only: %i(edit update)
before_action :load_vars_nested, only: BEFORE_ACTION_METHODS
before_action :load_visible_projects, only: %i(index visible_projects)
before_action :load_available_repositories,
only: %i(new edit available_repositories)
before_action :check_manage_permissions, only: BEFORE_ACTION_METHODS
# Index showing all reports of a single project
def index
def index; end
def datatable
respond_to do |format|
format.json do
render json: ::ReportDatatable.new(
view_context,
current_user,
current_team.datatables_reports.visible_by(current_user, current_team)
)
end
end
end
# Report grouped by modules
@ -72,6 +63,7 @@ class ReportsController < ApplicationController
@report = Report.new(report_params)
@report.project = @project
@report.user = current_user
@report.team = current_team
@report.last_modified_by = current_user
if continue && @report.save_with_contents(report_contents)
@ -88,7 +80,7 @@ class ReportsController < ApplicationController
)
respond_to do |format|
format.json do
render json: { url: project_reports_path(@project) }, status: :ok
render json: { url: reports_path }, status: :ok
end
end
else
@ -133,7 +125,7 @@ class ReportsController < ApplicationController
)
respond_to do |format|
format.json do
render json: { url: project_reports_path(@project) }, status: :ok
render json: { url: reports_path }, status: :ok
end
end
else
@ -155,7 +147,7 @@ class ReportsController < ApplicationController
report_ids.each do |report_id|
report = Report.find_by_id(report_id)
next unless report.present?
next unless report.present? && can_manage_reports?(current_team)
# record an activity
Activity.create(
type_of: :delete_report,
@ -170,24 +162,46 @@ class ReportsController < ApplicationController
report.destroy
end
redirect_to project_reports_path(@project)
redirect_to reports_path
end
# Generation action
# Currently, only .PDF is supported
def generate
content = params[:html]
content = I18n.t('projects.reports.new.no_content_for_PDF_html') if content.blank?
respond_to do |format|
format.pdf do
@html = params[:html]
@html = '<h1>No content</h1>' if @html.blank?
render pdf: 'report',
header: { right: '[page] of [topage]' },
template: 'reports/report.pdf.erb',
disable_javascript: true
render pdf: 'report', header: { right: '[page] of [topage]' },
locals: { content: content },
template: 'reports/report.pdf.erb',
disable_javascript: true
end
end
end
def save_pdf_to_inventory_item
save_pdf_to_inventory_item = ReportActions::SavePdfToInventoryItem.new(
current_user, current_team, save_PDF_params
)
if save_pdf_to_inventory_item.save
render json: {
message: I18n.t(
'projects.reports.new.save_PDF_to_inventory_modal.success_flash'
)
}, status: :ok
else
render json: { message: save_pdf_to_inventory_item.error_messages },
status: :unprocessable_entity
end
rescue ReportActions::RepositoryPermissionError => error
render json: { message: error },
status: :forbidden
rescue Exception => error
render json: { message: error.message },
status: :internal_server_error
end
# Modal for saving the existsing/new report
def save_modal
# Assume user is updating existing report
@ -434,8 +448,20 @@ class ReportsController < ApplicationController
end
end
def visible_projects
render json: { projects: @visible_projects }, status: :ok
end
def available_repositories
render json: { results: @available_repositories }, status: :ok
end
private
include StringUtility
VisibleProject = Struct.new(:path, :name)
AvailableRepository = Struct.new(:id, :name)
def load_vars
@report = Report.find_by_id(params[:id])
render_404 unless @report
@ -446,16 +472,45 @@ class ReportsController < ApplicationController
render_404 unless @project
end
def check_view_permissions
render_403 unless can_read_project?(@project)
def check_manage_permissions
render_403 unless can_manage_reports?(@project.team)
end
def check_manage_permissions
render_403 unless can_manage_reports?(@project)
def load_visible_projects
render_404 unless current_team
projects = current_team.projects.visible_from_user_by_name(
current_user, current_team, search_params[:q]
).limit(Constants::SEARCH_LIMIT).select(:id, :name)
@visible_projects = projects.collect do |project|
VisibleProject.new(new_project_reports_path(project),
ellipsize(project.name, 50, 40))
end
end
def load_available_repositories
repositories = current_team.repositories
.name_like(search_params[:q])
.limit(Constants::SEARCH_LIMIT)
.select(:id, :name)
@available_repositories = repositories.collect do |repository|
AvailableRepository.new(repository.id,
ellipsize(repository.name, 75, 50))
end
end
def report_params
params.require(:report)
.permit(:name, :description, :grouped_by, :report_contents)
end
def search_params
params.permit(:q)
end
def save_PDF_params
params.permit(:repository_id,
:respository_column_id,
:repository_item_id,
:html)
end
end

View file

@ -5,27 +5,22 @@ class RepositoriesController < ApplicationController
%i(repository_table_index export_repository parse_sheet import_records)
before_action :check_team, only: %i(parse_sheet import_records)
before_action :check_view_all_permissions, only: :index
before_action :check_view_permissions, only: :export_repository
before_action :check_view_permissions, only: %i(export_repository show)
before_action :check_manage_permissions, only:
%i(destroy destroy_modal rename_modal update)
before_action :check_create_permissions, only:
%i(create_new_modal create copy_modal copy)
%i(create_modal create copy_modal copy)
layout 'fluid'
def index
render('repositories/index')
unless @repositories.length.zero? && current_team
redirect_to repository_path(@repositories.first) and return
end
render 'repositories/index'
end
def show_tab
respond_to do |format|
format.json do
render json: {
html: render_to_string(
partial: 'repositories/repository.html.erb',
locals: { repository: @repository }
)
}
end
end
def show
end
def create_modal
@ -53,7 +48,7 @@ class RepositoriesController < ApplicationController
if @repository.save
flash[:success] = t('repositories.index.modal_create.success_flash',
name: @repository.name)
render json: { url: team_repositories_path(repository: @repository) },
render json: { url: repository_path(@repository) },
status: :ok
else
render json: @repository.errors,
@ -78,7 +73,8 @@ class RepositoriesController < ApplicationController
def destroy
flash[:success] = t('repositories.index.delete_flash',
name: @repository.name)
@repository.destroy
@repository.discard
ClearDiscardedRepositoriesJob.perform_later
redirect_to team_repositories_path
end
@ -261,7 +257,7 @@ class RepositoriesController < ApplicationController
def export_repository
if params[:row_ids] && params[:header_ids]
generate_zip
RepositoryZipExport.generate_zip(params, @repository, current_user)
else
flash[:alert] = t('zip_export.export_error')
end
@ -287,7 +283,7 @@ class RepositoriesController < ApplicationController
end
def load_parent_vars
@team = Team.find_by_id(params[:team_id])
@team = current_team
render_404 unless @team
@repositories = @team.repositories.order(created_at: :asc)
end
@ -305,8 +301,10 @@ class RepositoriesController < ApplicationController
end
def check_create_permissions
render_403 unless can_create_repositories?(@team) ||
@team.repositories.count < Constants::REPOSITORIES_LIMIT
unless can_create_repositories?(@team) ||
@team.repositories.count < Rails.configuration.x.repositories_limit
render_403
end
end
def check_manage_permissions
@ -333,66 +331,4 @@ class RepositoriesController < ApplicationController
end
end
end
def generate_zip
# Fetch rows in the same order as in the currently viewed datatable
ordered_row_ids = params[:row_ids]
id_row_map = RepositoryRow.where(id: ordered_row_ids,
repository: @repository)
.index_by(&:id)
ordered_rows = ordered_row_ids.collect { |id| id_row_map[id.to_i] }
zip = ZipExport.create(user: current_user)
zip.generate_exportable_zip(
current_user,
to_csv(ordered_rows, params[:header_ids]),
:repositories
)
end
def to_csv(rows, column_ids)
require 'csv'
# Parse column names
csv_header = []
column_ids.each do |c_id|
csv_header << case c_id.to_i
when -1, -2
next
when -3
I18n.t('repositories.table.row_name')
when -4
I18n.t('repositories.table.added_by')
when -5
I18n.t('repositories.table.added_on')
else
column = RepositoryColumn.find_by_id(c_id)
column ? column.name : nil
end
end
CSV.generate do |csv|
csv << csv_header
rows.each do |row|
csv_row = []
column_ids.each do |c_id|
csv_row << case c_id.to_i
when -1, -2
next
when -3
row.name
when -4
row.created_by.full_name
when -5
I18n.l(row.created_at, format: :full)
else
cell = row.repository_cells
.find_by(repository_column_id: c_id)
cell ? cell.value.data : nil
end
end
csv << csv_row
end
end
end
end

View file

@ -1,39 +1,67 @@
class RepositoryColumnsController < ApplicationController
include InputSanitizeHelper
before_action :load_vars, except: :create
before_action :load_vars_nested, only: :create
ACTIONS = %i(create index create_html available_asset_type_columns).freeze
before_action :load_vars,
except: ACTIONS
before_action :load_vars_nested,
only: ACTIONS
before_action :check_create_permissions, only: :create
before_action :check_manage_permissions, except: :create
before_action :check_manage_permissions,
except: ACTIONS
before_action :load_repository_columns, only: :index
before_action :load_asset_type_columns, only: :available_asset_type_columns
def index; end
def create_html
@repository_column = RepositoryColumn.new
respond_to do |format|
format.json do
render json: {
html: render_to_string(
partial: 'repository_columns/manage_column_modal.html.erb'
)
}
end
end
end
def create
@repository_column = RepositoryColumn.new(repository_column_params)
@repository_column.repository = @repository
@repository_column.created_by = current_user
@repository_column.data_type = :RepositoryTextValue
respond_to do |format|
if @repository_column.save
format.json do
render json: {
id: @repository_column.id,
name: escape_input(@repository_column.name),
edit_url:
edit_repository_repository_column_path(@repository,
@repository_column),
update_url:
repository_repository_column_path(@repository,
@repository_column),
destroy_html_url:
repository_columns_destroy_html_path(
@repository, @repository_column
)
},
status: :ok
end
else
format.json do
render json: @repository_column.errors.to_json,
format.json do
if @repository_column.save
if generate_repository_list_items(params[:list_items])
render json: {
id: @repository_column.id,
name: escape_input(@repository_column.name),
message: t('libraries.repository_columns.create.success_flash',
name: @repository_column.name),
edit_url:
edit_repository_repository_column_path(@repository,
@repository_column),
update_url:
repository_repository_column_path(@repository,
@repository_column),
destroy_html_url:
repository_columns_destroy_html_path(@repository,
@repository_column)
},
status: :ok
else
render json: {
message: {
repository_list_items:
t('libraries.repository_columns.repository_list_items_limit',
limit: Constants::REPOSITORY_LIST_ITEMS_PER_COLUMN)
}
}, status: :unprocessable_entity
end
else
render json: { message: @repository_column.errors.full_messages },
status: :unprocessable_entity
end
end
@ -43,7 +71,11 @@ class RepositoryColumnsController < ApplicationController
def edit
respond_to do |format|
format.json do
render json: { status: :ok }
render json: {
html: render_to_string(
partial: 'repository_columns/manage_column_modal.html.erb'
)
}
end
end
end
@ -53,9 +85,24 @@ class RepositoryColumnsController < ApplicationController
format.json do
@repository_column.update_attributes(repository_column_params)
if @repository_column.save
render json: { status: :ok }
if update_repository_list_items(params[:list_items])
render json: {
id: @repository_column.id,
name: escape_input(@repository_column.name),
message: t('libraries.repository_columns.update.success_flash',
name: @repository_column.name)
}, status: :ok
else
render json: {
message: {
repository_list_items:
t('libraries.repository_columns.repository_list_items_limit',
limit: Constants::REPOSITORY_LIST_ITEMS_PER_COLUMN)
}
}, status: :unprocessable_entity
end
else
render json: @repository_column.errors.to_json,
render json: { message: @repository_column.errors.full_messages },
status: :unprocessable_entity
end
end
@ -67,8 +114,7 @@ class RepositoryColumnsController < ApplicationController
format.json do
render json: {
html: render_to_string(
partial: 'repositories/delete_column_modal_body.html.erb',
locals: { column_index: params[:column_index] }
partial: 'repository_columns/delete_column_modal_body.html.erb'
)
}
end
@ -76,25 +122,44 @@ class RepositoryColumnsController < ApplicationController
end
def destroy
@del_repository_column = @repository_column.dup
column_id = @repository_column.id
column_name = @repository_column.name
respond_to do |format|
format.json do
if @repository_column.destroy
RepositoryTableState.update_state(
@del_repository_column,
params[:repository_column][:column_index],
current_user
)
render json: { status: :ok }
render json: {
message: t('libraries.repository_columns.destroy.success_flash',
name: column_name),
id: column_id,
status: :ok
}
else
render json: { status: :unprocessable_entity }
render json: {
message: t('libraries.repository_columns.destroy.error_flash'),
status: :unprocessable_entity
}
end
end
end
end
def available_asset_type_columns
if @asset_columns.empty?
render json: {
no_items: t(
'projects.reports.new.save_PDF_to_inventory_modal.no_columns'
)
}, status: :ok
else
render json: { results: @asset_columns }, status: :ok
end
end
private
include StringUtility
AvailableRepositoryColumn = Struct.new(:id, :name)
def load_vars
@repository = Repository.find_by_id(params[:repository_id])
render_404 unless @repository
@ -107,6 +172,16 @@ class RepositoryColumnsController < ApplicationController
render_404 unless @repository
end
def load_repository_columns
@repository_columns = @repository.repository_columns
.order(created_at: :desc)
end
def load_asset_type_columns
render_403 unless can_read_team?(@repository.team)
@asset_columns = load_asset_columns(search_params[:q])
end
def check_create_permissions
render_403 unless can_create_repository_columns?(@repository.team)
end
@ -116,6 +191,80 @@ class RepositoryColumnsController < ApplicationController
end
def repository_column_params
params.require(:repository_column).permit(:name)
params.require(:repository_column).permit(:name, :data_type)
end
def search_params
params.permit(:q, :repository_id)
end
def load_asset_columns(query)
@repository.repository_columns
.asset_type.name_like(query)
.limit(Constants::SEARCH_LIMIT)
.select(:id, :name)
.collect do |column|
AvailableRepositoryColumn.new(
column.id,
ellipsize(column.name, 75, 50)
)
end
end
def generate_repository_list_items(item_names)
return true unless @repository_column.data_type == 'RepositoryListValue'
column_items = @repository_column.repository_list_items.size
success = true
item_names.split(',').uniq.each do |name|
if column_items >= Constants::REPOSITORY_LIST_ITEMS_PER_COLUMN
success = false
next
end
RepositoryListItem.create(
repository: @repository,
repository_column: @repository_column,
data: name,
created_by: current_user,
last_modified_by: current_user
)
column_items += 1
end
success
end
def update_repository_list_items(item_names)
return true unless @repository_column.data_type == 'RepositoryListValue'
column_items = @repository_column.repository_list_items.size
items_list = item_names.split(',').uniq
existing = @repository_column.repository_list_items.pluck(:data)
existing.each do |name|
next if items_list.include? name
list_item_id = @repository_column.repository_list_items
.find_by_data(name)
.destroy
.id
RepositoryCell.where(
'value_type = ? AND value_id = ?',
'RepositoryListValue',
list_item_id
).destroy_all
end
success = true
items_list.each do |name|
next if @repository_column.repository_list_items.find_by_data(name)
if column_items >= Constants::REPOSITORY_LIST_ITEMS_PER_COLUMN
success = false
next
end
RepositoryListItem.create(
repository: @repository,
repository_column: @repository_column,
data: name,
created_by: current_user,
last_modified_by: current_user
)
column_items += 1
end
success
end
end

View file

@ -0,0 +1,28 @@
class RepositoryListItemsController < ApplicationController
before_action :load_vars, only: :search
def search
column_list_items = @repository_column.repository_list_items
.where('data ILIKE ?',
"%#{search_params[:q]}%")
.limit(Constants::SEARCH_LIMIT)
.select(:id, :data)
render json: { list_items: column_list_items }, status: :ok
end
private
def search_params
params.permit(:q, :column_id)
end
def load_vars
@repository_column = RepositoryColumn.find_by_id(search_params[:column_id])
repository = @repository_column.repository if @repository_column
unless @repository_column&.data_type == 'RepositoryListValue'
render_404 and return
end
render_403 unless can_manage_repository_rows?(repository.team)
end
end

View file

@ -3,10 +3,30 @@ class RepositoryRowsController < ApplicationController
include ActionView::Helpers::TextHelper
include ApplicationHelper
before_action :load_info_modal_vars, only: :show
before_action :load_vars, only: %i(edit update)
before_action :load_repository, only: %i(create delete_records)
before_action :load_repository,
only: %i(create
delete_records
index
copy_records
available_rows)
before_action :check_create_permissions, only: :create
before_action :check_manage_permissions, only: %i(edit update delete_records)
before_action :check_manage_permissions,
only: %i(edit update delete_records copy_records)
def index
@draw = params[:draw].to_i
per_page = params[:length] == '-1' ? 100 : params[:length].to_i
page = (params[:start].to_i / per_page) + 1
records = RepositoryDatatableService.new(@repository,
params,
current_user)
@assigned_rows = records.assigned_rows
@repository_row_count = records.repository_rows.count
@columns_mappings = records.mappings
@repository_rows = records.repository_rows.page(page).per(per_page)
end
def create
record = RepositoryRow.new(repository: @repository,
@ -16,29 +36,11 @@ class RepositoryRowsController < ApplicationController
repository_cells: [] }
record.transaction do
record.name = record_params[:name] unless record_params[:name].blank?
record.name = record_params[:repository_row_name] unless record_params[:repository_row_name].blank?
errors[:default_fields] = record.errors.messages unless record.save
if cell_params
cell_params.each do |key, value|
column = @repository.repository_columns.detect do |c|
c.id == key.to_i
end
cell_value = RepositoryTextValue.new(
data: value,
created_by: current_user,
last_modified_by: current_user,
repository_cell_attributes: {
repository_row: record,
repository_column: column
}
)
if cell_value.save
record_annotation_notification(record, cell_value.repository_cell)
else
errors[:repository_cells] << {
"#{column.id}": cell_value.errors.messages
}
end
next if create_cell_value(record, key, value, errors).nil?
end
end
raise ActiveRecord::Rollback if errors[:repository_cells].any?
@ -60,19 +62,40 @@ class RepositoryRowsController < ApplicationController
end
end
def show
respond_to do |format|
format.json do
render json: {
html: render_to_string(
partial: 'repositories/repository_row_info_modal.html.erb'
)
}
end
end
end
def edit
json = {
repository_row: {
name: escape_input(@record.name),
repository_cells: {}
repository_cells: {},
repository_column_items: fetch_columns_list_items
}
}
# Add custom cells ids as key (easier lookup on js side)
@record.repository_cells.each do |cell|
if cell.value_type == 'RepositoryAssetValue'
cell_value = cell.value.asset if cell.value_type == 'RepositoryAssetValue'
else
cell_value = escape_input(cell.value.data)
end
json[:repository_row][:repository_cells][cell.repository_column_id] = {
repository_cell_id: cell.id,
value: escape_input(cell.value.data)
value: cell_value,
type: cell.value_type,
list_items: fetch_list_items(cell)
}
end
@ -89,7 +112,7 @@ class RepositoryRowsController < ApplicationController
}
@record.transaction do
@record.name = record_params[:name].blank? ? nil : record_params[:name]
@record.name = record_params[:repository_row_name].blank? ? nil : record_params[:repository_row_name]
errors[:default_fields] = @record.errors.messages unless @record.save
if cell_params
cell_params.each do |key, value|
@ -98,42 +121,48 @@ class RepositoryRowsController < ApplicationController
end
if existing
# Cell exists and new value present, so update value
existing.value.data = value
if existing.value.save
record_annotation_notification(@record, existing)
if existing.value_type == 'RepositoryListValue'
item = RepositoryListItem.where(
repository_column: existing.repository_column
).find(value) unless value == '-1'
if item
existing.value.update_attribute(
:repository_list_item_id, item.id
)
else
existing.delete
end
elsif existing.value_type == 'RepositoryAssetValue'
next if value.blank?
if existing.value.asset.update(file: value)
existing.value.asset.created_by = current_user
existing.value.asset.last_modified_by = current_user
existing.value.asset.post_process_file(current_team)
else
errors[:repository_cells] << {
"#{existing.repository_column_id}": { data: existing.value.asset.errors.messages[:file].first }
}
end
else
errors[:repository_cells] << {
"#{existing.repository_column_id}":
existing.value.errors.messages
}
existing.value.data = value
if existing.value.save
record_annotation_notification(@record, existing)
else
errors[:repository_cells] << {
"#{existing.repository_column_id}":
existing.value.errors.messages
}
end
end
else
# Looks like it is a new cell, so we need to create new value, cell
# will be created automatically
column = @repository.repository_columns.detect do |c|
c.id == key.to_i
end
cell_value = RepositoryTextValue.new(
data: value,
created_by: current_user,
last_modified_by: current_user,
repository_cell_attributes: {
repository_row: @record,
repository_column: column
}
)
if cell_value.save
record_annotation_notification(@record,
cell_value.repository_cell)
else
errors[:repository_cells] << {
"#{column.id}": cell_value.errors.messages
}
end
next if create_cell_value(@record, key, value, errors).nil?
end
end
# Clean up empty cells, not present in updated record
@record.repository_cells.each do |cell|
next if cell.value_type == 'RepositoryListValue'
cell.value.destroy unless cell_params
.key?(cell.repository_column_id.to_s)
end
@ -165,6 +194,72 @@ class RepositoryRowsController < ApplicationController
end
end
def create_cell_value(record, key, value, errors)
column = @repository.repository_columns.detect do |c|
c.id == key.to_i
end
save_successful = false
if column.data_type == 'RepositoryListValue'
return if value == '-1'
# check if item exists else revert the transaction
list_item = RepositoryListItem.where(repository_column: column)
.find(value)
cell_value = RepositoryListValue.new(
repository_list_item_id: list_item.id,
created_by: current_user,
last_modified_by: current_user,
repository_cell_attributes: {
repository_row: record,
repository_column: column
}
)
save_successful = list_item && cell_value.save
elsif column.data_type == 'RepositoryAssetValue'
return if value.blank?
asset = Asset.new(file: value,
created_by: current_user,
last_modified_by: current_user,
team: current_team)
if asset.save
asset.post_process_file(current_team)
else
errors[:repository_cells] << {
"#{column.id}": { data: asset.errors.messages[:file].first }
}
end
cell_value = RepositoryAssetValue.new(
asset: asset,
created_by: current_user,
last_modified_by: current_user,
repository_cell_attributes: {
repository_row: record,
repository_column: column
}
)
save_successful = cell_value.save
else
cell_value = RepositoryTextValue.new(
data: value,
created_by: current_user,
last_modified_by: current_user,
repository_cell_attributes: {
repository_row: record,
repository_column: column
}
)
if (save_successful = cell_value.save)
record_annotation_notification(record,
cell_value.repository_cell)
end
end
unless save_successful
errors[:repository_cells] << {
"#{column.id}": cell_value.errors.messages
}
end
end
def delete_records
deleted_count = 0
if selected_params
@ -201,8 +296,47 @@ class RepositoryRowsController < ApplicationController
end
end
def copy_records
duplicate_service = RepositoryActions::DuplicateRows.new(
current_user, @repository, params[:selected_rows]
)
duplicate_service.call
render json: {
flash: t('repositories.copy_records_report',
number: duplicate_service.number_of_duplicated_items)
}, status: :ok
end
def available_rows
if @repository.repository_rows.empty?
no_items_string =
"#{t('projects.reports.new.save_PDF_to_inventory_modal.no_items')} " \
"#{link_to(t('projects.reports.new.save_PDF_to_inventory_modal.here'),
repository_path(@repository),
data: { 'no-turbolink' => true })}"
render json: { no_items: no_items_string },
status: :ok
else
render json: { results: load_available_rows(search_params[:q]) },
status: :ok
end
end
private
include StringUtility
AvailableRepositoryRow = Struct.new(:id, :name, :has_file_attached)
def load_info_modal_vars
@repository_row = RepositoryRow.eager_load(:created_by, repository: [:team])
.find_by_id(params[:id])
@assigned_modules = MyModuleRepositoryRow.eager_load(
my_module: [{ experiment: :project }]
).where(repository_row: @repository_row)
render_404 and return unless @repository_row
render_403 unless can_read_team?(@repository_row.repository.team)
end
def load_vars
@repository = Repository.eager_load(:repository_columns)
.find_by_id(params[:repository_id])
@ -214,6 +348,7 @@ class RepositoryRowsController < ApplicationController
def load_repository
@repository = Repository.find_by_id(params[:repository_id])
render_404 unless @repository
render_403 unless can_read_team?(@repository.team)
end
def check_create_permissions
@ -225,7 +360,7 @@ class RepositoryRowsController < ApplicationController
end
def record_params
params.require(:repository_row).permit(:name).to_h
params.permit(:repository_row_name).to_h
end
def cell_params
@ -236,6 +371,27 @@ class RepositoryRowsController < ApplicationController
params.permit(selected_rows: []).to_h[:selected_rows]
end
def load_available_rows(query)
@repository.repository_rows
.includes(:repository_cells)
.name_like(search_params[:q])
.limit(Constants::SEARCH_LIMIT)
.select(:id, :name)
.collect do |row|
with_asset_cell = row.repository_cells.where(
'repository_cells.repository_column_id = ?',
search_params[:repository_column_id]
)
AvailableRepositoryRow.new(row.id,
ellipsize(row.name, 75, 50),
with_asset_cell.present?)
end
end
def search_params
params.permit(:q, :repository_id, :repository_column_id)
end
def record_annotation_notification(record, cell, old_text = nil)
table_url = params.fetch(:request_url) { :request_url_must_be_present }
smart_annotation_notification(
@ -251,4 +407,28 @@ class RepositoryRowsController < ApplicationController
column: link_to(cell.repository_column.name, table_url))
)
end
def fetch_list_items(cell)
return [] if cell.value_type != 'RepositoryListValue'
RepositoryListItem.where(repository: @repository)
.where(repository_column: cell.repository_column)
.limit(Constants::SEARCH_LIMIT)
.pluck(:id, :data)
end
def fetch_columns_list_items
collection = []
@repository.repository_columns
.list_type
.preload(:repository_list_items)
.each do |column|
collection << {
column_id: column.id,
list_items: column.repository_list_items
.limit(Constants::SEARCH_LIMIT)
.pluck(:id, :data)
}
end
collection
end
end

View file

@ -4,6 +4,8 @@ class SearchController < ApplicationController
def index
redirect_to new_search_path unless @search_query
@search_id = params[:search_id] ? params[:search_id] : generate_search_id
count_search_results
search_projects if @search_category == :projects
@ -15,7 +17,6 @@ class SearchController < ApplicationController
search_protocols if @search_category == :protocols
search_steps if @search_category == :steps
search_checklists if @search_category == :checklists
search_samples if @search_category == :samples
if @search_category == :repositories && params[:repository]
search_repository
end
@ -86,6 +87,10 @@ class SearchController < ApplicationController
protected
def generate_search_id
SecureRandom.urlsafe_base64(32)
end
def search_by_name(model)
model.search(current_user,
true,
@ -109,53 +114,70 @@ class SearchController < ApplicationController
end
def count_by_repository
count_total = 0
search_results = Repository.search(current_user,
true,
@search_query,
Constants::SEARCH_NO_LIMIT,
nil,
match_case: @search_case,
whole_word: @search_whole_word,
whole_phrase: @search_whole_phrase)
@repository_search_count = {}
current_user.teams.includes(:repositories).each do |team|
team_results = {}
team_results[:count] = 0
team_results[:repositories] = {}
team.repositories.each do |repository|
repository_results = {}
repository_results[:id] = repository.id
repository_results[:count] = 0
search_results.each do |result|
if repository.id == result.id
count_total += result.counter
repository_results[:count] += result.counter
@repository_search_count =
Rails.cache.fetch("#{@search_id}/repository_search_count",
expires_in: 5.minutes) do
search_count = {}
search_results = Repository.search(current_user,
@search_query,
Constants::SEARCH_NO_LIMIT,
nil,
match_case: @search_case,
whole_word: @search_whole_word,
whole_phrase: @search_whole_phrase)
current_user.teams.includes(:repositories).each do |team|
team_results = {}
team_results[:count] = 0
team_results[:repositories] = {}
team.repositories.each do |repository|
repository_results = {}
repository_results[:id] = repository.id
repository_results[:count] = 0
search_results.each do |result|
if repository.id == result.id
repository_results[:count] += result.counter
end
end
team_results[:repositories][repository.name] = repository_results
team_results[:count] += repository_results[:count]
end
search_count[team.name] = team_results
end
team_results[:repositories][repository.name] = repository_results
team_results[:count] += repository_results[:count]
search_count
end
@repository_search_count[team.name] = team_results
count_total = 0
@repository_search_count.each_value do |team_results|
count_total += team_results[:count]
end
count_total
end
def current_repository_search_count
@repository_search_count.each_value do |counter|
res = counter[:repositories].values.detect do |rep|
rep[:id] == @repository.id
end
return res[:count] if res && res[:count]
end
end
def count_search_results
@project_search_count = count_by_name Project
@experiment_search_count = count_by_name Experiment
@module_search_count = count_by_name MyModule
@result_search_count = count_by_name Result
@tag_search_count = count_by_name Tag
@report_search_count = count_by_name Report
@protocol_search_count = count_by_name Protocol
@step_search_count = count_by_name Step
@checklist_search_count = count_by_name Checklist
@sample_search_count = count_by_name Sample
@project_search_count = fetch_cached_count Project
@experiment_search_count = fetch_cached_count Experiment
@module_search_count = fetch_cached_count MyModule
@result_search_count = fetch_cached_count Result
@tag_search_count = fetch_cached_count Tag
@report_search_count = fetch_cached_count Report
@protocol_search_count = fetch_cached_count Protocol
@step_search_count = fetch_cached_count Step
@checklist_search_count = fetch_cached_count Checklist
@sample_search_count = fetch_cached_count Sample
@repository_search_count_total = count_by_repository
@asset_search_count = count_by_name Asset
@table_search_count = count_by_name Table
@comment_search_count = count_by_name Comment
@asset_search_count = fetch_cached_count Asset
@table_search_count = fetch_cached_count Table
@comment_search_count = fetch_cached_count Comment
@search_results_count = @project_search_count
@search_results_count += @experiment_search_count
@ -173,6 +195,15 @@ class SearchController < ApplicationController
@search_results_count += @comment_search_count
end
def fetch_cached_count(type)
exp = 5.minutes
Rails.cache.fetch(
"#{@search_id}/#{type.name.underscore}_search_count", expires_in: exp
) do
count_by_name type
end
end
def search_projects
@project_results = []
@project_results = search_by_name(Project) if @project_search_count > 0
@ -231,24 +262,19 @@ class SearchController < ApplicationController
@search_count = @checklist_search_count
end
def search_samples
@sample_results = []
@sample_results = search_by_name(Sample) if @sample_search_count > 0
@search_count = @sample_search_count
end
def search_repository
@repository = Repository.find_by_id(params[:repository])
render_403 unless can_read_team?(@repository.team)
@repository_results = []
if @repository_search_count_total > 0
@repository_results =
RepositoryRow.search(@repository, @search_query, @search_page,
match_case: @search_case,
whole_word: @search_whole_word,
whole_phrase: @search_whole_phrase)
Repository.search(current_user, @search_query, @search_page,
@repository,
match_case: @search_case,
whole_word: @search_whole_word,
whole_phrase: @search_whole_phrase)
end
@search_count = @repository_search_count_total
@search_count = current_repository_search_count
end
def search_assets

View file

@ -2,15 +2,8 @@ class UserRepositoriesController < ApplicationController
before_action :load_vars
def save_table_state
table_state = RepositoryTableState.where(user: current_user,
repository: @repository).first
if table_state
table_state.update(state: params[:state])
else
RepositoryTableState.create(user: current_user,
repository: @repository,
state: params[:state])
end
service = RepositoryTableStateService.new(current_user, @repository)
service.update_state(params[:state])
respond_to do |format|
format.json do
render json: {
@ -21,13 +14,13 @@ class UserRepositoriesController < ApplicationController
end
def load_table_state
table_state = RepositoryTableState.load_state(current_user,
@repository).first
service = RepositoryTableStateService.new(current_user, @repository)
state = service.load_state.state
respond_to do |format|
if table_state
if state
format.json do
render json: {
state: table_state
state: state
}
end
end

View file

@ -4,6 +4,7 @@ class Users::RegistrationsController < Devise::RegistrationsController
only: %i(new create new_with_provider create_with_provider)
before_action :sign_up_with_provider_enabled?,
only: %i(new_with_provider create_with_provider)
layout :layout
def avatar
user = User.find_by_id(params[:id]) || current_user
@ -291,6 +292,10 @@ class Users::RegistrationsController < Devise::RegistrationsController
private
def layout
'fluid' if action_name == 'edit'
end
def check_captcha
if Rails.configuration.x.enable_recaptcha
unless verify_recaptcha

View file

@ -2,6 +2,7 @@ module Users
module Settings
module Account
class AddonsController < ApplicationController
layout 'fluid'
end
end
end

View file

@ -5,10 +5,9 @@ module Users
before_action :load_user, only: [
:index,
:update,
:tutorial,
:reset_tutorial,
:notifications_settings
]
layout 'fluid'
def index
end
@ -31,51 +30,6 @@ module Users
end
end
def tutorial
@teams =
@user
.user_teams
.includes(team: :users)
.where(role: 1..2)
.order(created_at: :asc)
.map(&:team)
@member_of = @teams.count
respond_to do |format|
format.json do
render json: {
status: :ok,
html: render_to_string(
partial: 'users/settings/account/preferences/' \
'repeat_tutorial_modal_body.html.erb'
)
}
end
end
end
def reset_tutorial
if @user.update(tutorial_status: 0) && params[:team][:id].present?
@user.update(current_team_id: params[:team][:id])
cookies.delete :tutorial_data
cookies.delete :current_tutorial_step
cookies[:repeat_tutorial_team_id] = {
value: params[:team][:id],
expires: 1.day.from_now
}
flash[:notice] = t(
'users.settings.account.preferences.tutorial.tutorial_reset_flash'
)
redirect_to root_path
else
flash[:alert] = t(
'users.settings.account.preferences.tutorial.tutorial_reset_error'
)
redirect_back(fallback_location: root_path)
end
end
def notifications_settings
@user.assignments_notification =
params[:assignments_notification] ? true : false

View file

@ -22,6 +22,8 @@ module Users
before_action :check_create_team_permission,
only: %i(new create)
layout 'fluid'
def index
@user_teams =
@user

Some files were not shown because too many files have changed in this diff Show more