diff --git a/Gemfile b/Gemfile index 9b77f0893..d545da3ae 100644 --- a/Gemfile +++ b/Gemfile @@ -14,6 +14,7 @@ gem 'bootstrap_form' gem 'yomu' gem 'font-awesome-rails', '~> 4.6' gem 'recaptcha', require: 'recaptcha/rails' +gem 'sanitize', '~> 4.4' # JS datetime library, requirement of datetime picker gem 'momentjs-rails', '>= 2.9.0' diff --git a/Gemfile.lock b/Gemfile.lock index 080015b5c..f0a7e09df 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -103,6 +103,7 @@ GEM colorize (0.8.1) commit_param_routing (0.0.1) concurrent-ruby (1.0.0) + crass (1.0.2) debug_inspector (0.0.2) deface (1.0.2) colorize (>= 0.5.8) @@ -192,6 +193,8 @@ GEM nokogiri (1.6.8) mini_portile2 (~> 2.1.0) pkg-config (~> 1.1.7) + nokogumbo (1.4.10) + nokogiri oj (2.17.4) orm_adapter (0.5.0) paperclip (4.3.2) @@ -266,6 +269,10 @@ GEM ruby-graphviz (1.2.2) ruby-progressbar (1.8.1) rubyzip (1.1.7) + sanitize (4.4.0) + crass (~> 1.0.2) + nokogiri (>= 1.4.4) + nokogumbo (~> 1.4.1) sass (3.4.23) sass-rails (5.0.4) railties (>= 4.0.0, < 5.0) @@ -387,6 +394,7 @@ DEPENDENCIES rubocop ruby-graphviz (~> 1.2) rubyzip + sanitize (~> 4.4) sass-rails (~> 5.0) scss_lint sdoc (~> 0.4.0) diff --git a/app/assets/javascripts/protocols/steps.js.erb b/app/assets/javascripts/protocols/steps.js.erb index ffcd6e459..a536e33be 100644 --- a/app/assets/javascripts/protocols/steps.js.erb +++ b/app/assets/javascripts/protocols/steps.js.erb @@ -544,14 +544,12 @@ $("[data-action='new-step']").on("ajax:success", function(e, data) { toggleButtons(false); initializeCheckboxSorting(); - $("#step_name").focus(); $("#new-step-main-tab a").on("shown.bs.tab", function() { $("#step_name").focus(); }); TinyMCE.refresh(); - }); // Needed because server-side validation failure clears locations of diff --git a/app/assets/javascripts/results/result_assets.js b/app/assets/javascripts/results/result_assets.js index cf9f0e456..93493a8a2 100644 --- a/app/assets/javascripts/results/result_assets.js +++ b/app/assets/javascripts/results/result_assets.js @@ -25,7 +25,8 @@ Results.toggleResultEditButtons(false); $('#result_name').focus(); }, - error: function() { + error: function(xhr, status, e) { + $(this).renderFormErrors('result', xhr.responseJSON, true, e); animateSpinner(null, false); initNewResultAsset(); } @@ -74,15 +75,8 @@ initPreviewModal(); Comments.initialize(); initNewResultAsset(); - }).on('ajax:error', function(e, data) { - // This check is here only because of remotipart bug, which returns - // HTML instead of JSON, go figure - var errors = ''; - if (data.errors) - errors = data.errors; - else - errors = data.responseJSON.errors; - $form.renderFormErrors('result', errors, true, e); + }).on('ajax:error', function(xhr, status, e) { + $form.renderFormErrors('result', xhr.responseJSON, true, e); animateSpinner(null, false); }); } diff --git a/app/assets/javascripts/samples/sample_datatable.js.erb b/app/assets/javascripts/samples/sample_datatable.js.erb index c0bbff776..a53a1ccad 100644 --- a/app/assets/javascripts/samples/sample_datatable.js.erb +++ b/app/assets/javascripts/samples/sample_datatable.js.erb @@ -154,7 +154,10 @@ function dataTableInit() { fnInitComplete: function(oSettings, json) { // Reload correct column order and visibility (if you refresh page) for (var i = 0; i < table.columns()[0].length; i++) { - var visibility = myData.columns[i].visible; + var visibility = false; + if (myData.columns[i]) { + visibility = myData.columns[i].visible; + } if (typeof (visibility) === 'string') { visibility = (visibility === 'true'); } @@ -976,7 +979,6 @@ function changeToEditMode() { // Add number of columns $('#samples').data('num-columns', $('#samples').data('num-columns') + 1); - // Add column to table (=table header) originalHeader.append( ''); + var $errSpan = "' + errorText + ''; + $formGroup.append($errSpan); + } + + var $parent; + var $tab = $(input).closest('.tab-pane'); + if ($tab.length) { + // Mark error tab + tabsPropagateErrorClass($form); + $parent = $tab; + } else { + $parent = $form; + } + + // Focus and scroll to the error if it is the first (most upper) one + if ($parent.find('.form-group.has-error').length === 1 || + _.isUndefined(errMsgs)) { + goToFormElement(input); + } + + if (!_.isUndefined(ev)) { + // Don't submit form + ev.preventDefault(); + ev.stopPropagation(); + } +}; + /* * Render errors specified in JSON format for many form elements. */ -$.fn.renderFormErrors = function (modelName, errors, clear, ev) { - clear = (typeof clear !== 'undefined') ? clear : true; +$.fn.renderFormErrors = function(modelName, errors, clear, ev) { + clear = ((typeof clear) === 'undefined') ? true : clear; if (clear || _.isUndefined(clear)) { this.clearFormErrors(); } - var form = $(this); - $.each(errors, function (field, messages) { - $input = $(_.filter(form.find('input, select, textarea'), function (el) { + $.each(errors, function(field, messages) { + // Special exception for file uploads in steps and results + if (field === 'assets.file') { + field = 'assets_attributes'; + } else if (field === 'asset.file') { + field = 'asset_attribute'; + } + var types = 'input, file, select, textarea'; + var $input = $(_.filter(form.find(types), function(el) { var name = $(el).attr('name'); if (name) { return name.match(new RegExp(modelName + '\\[' + field + '\\(?')); @@ -25,80 +92,22 @@ $.fn.renderFormErrors = function (modelName, errors, clear, ev) { }); }; -/* - * Render errors specified in array of strings format (or string if - * just one error) for a single form element. - * - * Show error message/s and mark error input (if errMsgs is defined) - * and, if present, mark and show the tab where the error occured and - * focus/scroll to the error input, if it is the first one to be - * specified or if errMsgs is undefined. - * - * @param {string} errAttributes Span element (error) attributes - * @param {boolean} clearErr Set clearErr to true if this is the only - * error that can happen/show. - */ -var renderFormError = function (ev, input, errMsgs, clearErr, errAttributes) { - clearErr = _.isUndefined(clearErr) ? false : clearErr; - errAttributes = _.isUndefined(errAttributes) ? "" : " " + errAttributes; - $form = $(input).closest("form"); - - if (!_.isUndefined(errMsgs)) { - if (clearErr) { - $form.clearFormErrors(); - } - - // Mark error form group - $formGroup = $(input).closest(".form-group"); - if (!$formGroup.hasClass("has-error")) { - $formGroup.addClass("has-error"); - } - - // Add error message/s - error_text = ($.makeArray(errMsgs).map(function (m) { - return m.strToErrorFormat(); - })).join("
"); - $errSpan = "" + error_text + ""; - $formGroup.append($errSpan); - } - - $tab = $(input).closest(".tab-pane"); - if ($tab.length) { - // Mark error tab - tabsPropagateErrorClass($form); - $parent = $tab; - } else { - $parent = $form; - } - - // Focus and scroll to the error if it is the first (most upper) one - if ($parent.find(".form-group.has-error").length === 1 || _.isUndefined(errMsgs)) { - goToFormElement(input); - } - - if(!_.isUndefined(ev)) { - // Don't submit form - ev.preventDefault(); - ev.stopPropagation(); - } -} - /* * If any of form tabs (if exist) has errors, mark it and * and show the first erroneous tab. */ function tabsPropagateErrorClass($form) { - var $contents = $form.find("div.tab-pane"); - _.each($contents, function (tab) { + var $contents = $form.find('div.tab-pane'); + _.each($contents, function(tab) { var $tab = $(tab); - var $errorFields = $tab.find(".has-error"); + var $errorFields = $tab.find('.has-error'); if ($errorFields.length) { - var id = $tab.attr("id"); + var id = $tab.attr('id'); var navLink = $form.find("a[href='#" + id + "'][data-toggle='tab']"); if (navLink.parent().length) { - navLink.parent().addClass("has-error"); + navLink.parent().addClass('has-error'); } } }); - $form.find(".nav-tabs .has-error:first > a", $form).tab("show"); + $form.find('.nav-tabs .has-error:first > a', $form).tab('show'); } diff --git a/app/assets/stylesheets/projects.scss b/app/assets/stylesheets/projects.scss index e7899e24d..fa92a85e4 100644 --- a/app/assets/stylesheets/projects.scss +++ b/app/assets/stylesheets/projects.scss @@ -200,7 +200,7 @@ path, ._jsPlumb_endpoint { cursor: pointer; display: block; position: absolute; - width: 294px; + width: 300px; z-index: 5; .panel-body .due-date-link { diff --git a/app/assets/stylesheets/themes/scinote.scss b/app/assets/stylesheets/themes/scinote.scss index 06a2feca9..057e3cd70 100644 --- a/app/assets/stylesheets/themes/scinote.scss +++ b/app/assets/stylesheets/themes/scinote.scss @@ -282,7 +282,9 @@ a { .badge-indicator, .btn .badge-indicator { + font-size: 9px; margin-left: -8px; + padding: 3px 5px; top: 3px; } diff --git a/app/controllers/concerns/report_actions.rb b/app/controllers/concerns/report_actions.rb index 50deeeedc..6b0653738 100644 --- a/app/controllers/concerns/report_actions.rb +++ b/app/controllers/concerns/report_actions.rb @@ -52,7 +52,7 @@ module ReportActions def generate_experiment_contents_json(experiment, selected_modules) res = [] - experiment.my_modules.each do |my_module| + experiment.my_modules.order(:workflow_order).each do |my_module| next unless selected_modules.include?(my_module.id) res << generate_new_el(false) @@ -72,9 +72,9 @@ module ReportActions ReportExtends::MODULE_CONTENTS.each do |contents| protocol = contents.element == :step ? my_module.protocol.present? : true next unless in_params?("module_#{contents.element}".to_sym) && protocol - res << generate_new_el(false) if contents.children contents.collection(my_module).each do |report_el| + res << generate_new_el(false) el = generate_el( "reports/elements/my_module_#{contents .element @@ -82,9 +82,9 @@ module ReportActions .singularize}_element.html.erb", contents.parse_locals([report_el]) ) - if contents.element == :step + if contents.locals.first == :step el[:children] = generate_step_contents_json(report_el) - elsif contents.element == :result + elsif contents.locals.first == :result el[:children] = generate_result_contents_json(report_el) end res << el @@ -92,6 +92,7 @@ module ReportActions else file_name = contents.file_name file_name = contents.element if contents.element == :samples + res << generate_new_el(false) res << generate_el( "reports/elements/my_module_#{file_name}_element.html.erb", contents.parse_locals([my_module, :asc]) @@ -105,7 +106,7 @@ module ReportActions def generate_step_contents_json(step) res = [] if in_params? :step_checklists - step.checklists.each do |checklist| + step.checklists.asc.each do |checklist| res << generate_new_el(false) res << generate_el( 'reports/elements/step_checklist_element.html.erb', diff --git a/app/controllers/experiments_controller.rb b/app/controllers/experiments_controller.rb index 41388f209..513229aee 100644 --- a/app/controllers/experiments_controller.rb +++ b/app/controllers/experiments_controller.rb @@ -185,6 +185,8 @@ class ExperimentsController < ApplicationController cloned_experiment = @experiment.deep_clone_to_project(current_user, project) success = cloned_experiment.valid? + # Create workflow image + cloned_experiment.delay.generate_workflow_img if success else success = false end diff --git a/app/controllers/result_assets_controller.rb b/app/controllers/result_assets_controller.rb index 75247bb05..7bae4741b 100644 --- a/app/controllers/result_assets_controller.rb +++ b/app/controllers/result_assets_controller.rb @@ -72,11 +72,7 @@ class ResultAssetsController < ApplicationController }, status: :ok end else - # This response is sent as 200 OK due to IE security error when - # accessing iframe content. - format.json do - render json: { status: 'error', errors: @result.errors } - end + format.json { render json: @result.errors, status: :bad_request } end end end diff --git a/app/controllers/steps_controller.rb b/app/controllers/steps_controller.rb index 8b1a340b0..a9cbf6409 100644 --- a/app/controllers/steps_controller.rb +++ b/app/controllers/steps_controller.rb @@ -97,11 +97,6 @@ class StepsController < ApplicationController status: :ok end else - # On error, delete the newly added files from S3, as they were - # uploaded on client-side (in case of client-side hacking of - # asset's signature response) - Asset.destroy_all(new_assets) - format.json { render json: { html: render_to_string(partial: 'new.html.erb') diff --git a/app/controllers/teams_controller.rb b/app/controllers/teams_controller.rb index b0fbdb42a..3472610cb 100644 --- a/app/controllers/teams_controller.rb +++ b/app/controllers/teams_controller.rb @@ -53,6 +53,7 @@ class TeamsController < ApplicationController ) if @temp_file.save + @temp_file.destroy_obsolete # format.html format.json { render :json => { diff --git a/app/controllers/users/invitations_controller.rb b/app/controllers/users/invitations_controller.rb index bac1b5707..21cf02977 100644 --- a/app/controllers/users/invitations_controller.rb +++ b/app/controllers/users/invitations_controller.rb @@ -1,5 +1,6 @@ module Users class InvitationsController < Devise::InvitationsController + include InputSanitizeHelper include UsersGenerator prepend_before_action :check_captcha, only: [:update] @@ -175,8 +176,8 @@ module Users message = "#{I18n.t('search.index.team')} #{team.name}" notification = Notification.create( type_of: :assignment, - title: ActionController::Base.helpers.sanitize(title), - message: ActionController::Base.helpers.sanitize(message) + title: sanitize_input(title), + message: sanitize_input(message) ) if target_user.assignments_notification diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index cad28e101..0b6c0b199 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -92,10 +92,8 @@ module ApplicationHelper def generate_annotation_notification(target_user, title, message) notification = Notification.create( type_of: :assignment, - title: - ActionController::Base.helpers.sanitize(title), - message: - ActionController::Base.helpers.sanitize(message) + title: sanitize_input(title), + message: sanitize_input(message) ) if target_user.assignments_notification UserNotification.create(notification: notification, user: target_user) @@ -122,11 +120,13 @@ module ApplicationHelper project = Project.find_by_id(match[3].base62_decode) next unless project if project.archived? - "#{sanitize(match[2])} " \ + "" \ + "#{sanitize_input(match[2])} " \ "#{link_to project.name, projects_archive_path} #{I18n.t('atwho.res.archived')}" else - "#{sanitize(match[2])} " \ + "" \ + "#{sanitize_input(match[2])} " \ "#{link_to project.name, project_path(project)}" end @@ -134,12 +134,14 @@ module ApplicationHelper experiment = Experiment.find_by_id(match[3].base62_decode) next unless experiment if experiment.archived? - "#{sanitize(match[2])} " \ + "" \ + "#{sanitize_input(match[2])} " \ "#{link_to experiment.name, experiment_archive_project_path(experiment.project)} " \ "#{I18n.t('atwho.res.archived')}" else - "#{sanitize(match[2])} " \ + ""\ + "#{sanitize_input(match[2])} " \ "#{link_to experiment.name, canvas_experiment_path(experiment)}" end @@ -147,12 +149,14 @@ module ApplicationHelper my_module = MyModule.find_by_id(match[3].base62_decode) next unless my_module if my_module.archived? - "#{sanitize(match[2])} " \ + "" \ + "#{sanitize_input(match[2])} " \ "#{link_to my_module.name, module_archive_experiment_path(my_module.experiment)} " \ "#{I18n.t('atwho.res.archived')}" else - "#{sanitize(match[2])} " \ + "" \ + "#{sanitize_input(match[2])} " \ "#{link_to my_module.name, protocols_my_module_path(my_module)}" end @@ -213,19 +217,20 @@ module ApplicationHelper user_description += %(

) end - raw(image_tag(user_avatar_absolute_url(user, :icon_small), - class: 'atwho-user-img-popover')) + - raw('') + user.full_name + raw('') + raw("") + + raw('') + user.full_name + raw('') end def user_avatar_absolute_url(user, style) unless user.avatar(style) == '/images/icon_small/missing.png' return user.avatar(style) end - URI.join(root_url, "/images/#{style}/missing.png").to_s + URI.join(Rails.application.routes.url_helpers.root_url, + "/images/#{style}/missing.png").to_s end end diff --git a/app/helpers/input_sanitize_helper.rb b/app/helpers/input_sanitize_helper.rb index ce58cc235..b8ba5b5d6 100644 --- a/app/helpers/input_sanitize_helper.rb +++ b/app/helpers/input_sanitize_helper.rb @@ -1,14 +1,17 @@ +require 'sanitize' + module InputSanitizeHelper - def sanitize_input( - text, - tags = [], - attributes = [] - ) - ActionController::Base.helpers.sanitize( - text, - tags: Constants::WHITELISTED_TAGS + tags, - attributes: Constants::WHITELISTED_ATTRIBUTES + attributes - ) + # Rails default ActionController::Base.helpers.sanitize method call + # the ActiveRecord connecton method on the caller object which in + # our cases throws an error when called from not ActiveRecord objects + # such as SamplesDatatables + def sanitize_input(html, tags = [], attributes = []) + Sanitize.fragment( + html, + elements: Constants::WHITELISTED_TAGS + tags, + attributes: { all: Constants::WHITELISTED_ATTRIBUTES + attributes }, + css: Constants::WHITELISTED_CSS_ATTRIBUTES + ).html_safe end def escape_input(text) @@ -16,15 +19,13 @@ module InputSanitizeHelper end def custom_auto_link(text, options = {}) - simple_format = options.fetch(:simple_format) { true } - team = options.fetch(:team) { nil }, + simple_f = options.fetch(:simple_format) { true } + team = options.fetch(:team) { nil } wrapper_tag = options.fetch(:wrapper_tag) { {} } tags = options.fetch(:tags) { [] } - text = if simple_format - simple_format(sanitize_input(text), {}, wrapper_tag) - else - sanitize_input(text, tags) - end + format_opt = wrapper_tag.merge(sanitize: false) + text = sanitize_input(text, tags) + text = simple_format(sanitize_input(text), {}, format_opt) if simple_f auto_link( smart_annotation_parser(text, team), link: :urls, diff --git a/app/helpers/notifications_helper.rb b/app/helpers/notifications_helper.rb index e21066a54..f41eb90ac 100644 --- a/app/helpers/notifications_helper.rb +++ b/app/helpers/notifications_helper.rb @@ -40,10 +40,8 @@ module NotificationsHelper notification = Notification.create( type_of: :assignment, - title: - ActionController::Base.helpers.sanitize(title), - message: - ActionController::Base.helpers.sanitize(message) + title: sanitize_input(title), + message: sanitize_input(message) ) if target_user.assignments_notification diff --git a/app/helpers/search_helper.rb b/app/helpers/search_helper.rb index 8c266803e..479907bbb 100644 --- a/app/helpers/search_helper.rb +++ b/app/helpers/search_helper.rb @@ -11,10 +11,11 @@ module SearchHelper if search_team != current_team link_to text, path, - data: { confirm: t('users.settings.changed_team_in_search', + data: { no_turbolink: true, + confirm: t('users.settings.changed_team_in_search', team: search_team.name) } else - link_to text, path + link_to text, path, data: { no_turbolink: true } end end diff --git a/app/helpers/tiny_mce_helper.rb b/app/helpers/tiny_mce_helper.rb index 73d76cadf..0b9da187e 100644 --- a/app/helpers/tiny_mce_helper.rb +++ b/app/helpers/tiny_mce_helper.rb @@ -23,7 +23,9 @@ module TinyMceHelper match = el.match(regex) img = TinyMceAsset.find_by_id(match[1]) next unless img - image_tag img.url, data: { token: Base62.encode(img.id) } + image_tag img.url, + class: 'img-responsive', + data: { token: Base62.encode(img.id) } end end diff --git a/app/models/activity.rb b/app/models/activity.rb index 47541a78f..99dec64e0 100644 --- a/app/models/activity.rb +++ b/app/models/activity.rb @@ -1,4 +1,6 @@ class Activity < ActiveRecord::Base + include InputSanitizeHelper + after_create :generate_notification enum type_of: [ @@ -104,15 +106,12 @@ class Activity < ActiveRecord::Base notification = Notification.create( type_of: notification_type, - title: - ActionController::Base.helpers.sanitize(message, tags: %w(strong a)), - message: - ActionController::Base - .helpers.sanitize( - "#{I18n.t('search.index.project')} - #{project_m} #{experiment_m} #{task_m}", - tags: %w(strong a) - ), + title: sanitize_input(message, %w(strong a)), + message: sanitize_input( + "#{I18n.t('search.index.project')} + #{project_m} #{experiment_m} #{task_m}", + %w(strong a) + ), generator_user_id: user.id ) diff --git a/app/models/checklist.rb b/app/models/checklist.rb index 8e4b0e021..2d06bfe61 100644 --- a/app/models/checklist.rb +++ b/app/models/checklist.rb @@ -22,6 +22,8 @@ class Checklist < ActiveRecord::Base reject_if: :all_blank, allow_destroy: true + scope :asc, -> { order('checklists.created_at ASC') } + def self.search(user, include_archived, query = nil, diff --git a/app/models/experiment.rb b/app/models/experiment.rb index cabc7d54b..3ad412694 100644 --- a/app/models/experiment.rb +++ b/app/models/experiment.rb @@ -1,5 +1,6 @@ class Experiment < ActiveRecord::Base - include ArchivableModel, SearchableModel + include ArchivableModel + include SearchableModel belongs_to :project, inverse_of: :experiments belongs_to :created_by, foreign_key: :created_by_id, class_name: 'User' @@ -354,7 +355,7 @@ class Experiment < ActiveRecord::Base i += 1 while experiment_names.include?(format(format, i, name)) clone = Experiment.new( - name: format(format, i, name), + name: format(format, i, name).truncate(Constants::NAME_MAX_LENGTH), description: description, created_by: current_user, last_modified_by: current_user, @@ -371,10 +372,6 @@ class Experiment < ActiveRecord::Base m.deep_clone_to_experiment(current_user, clone) end clone.save - - # Create workflow image - clone.delay.generate_workflow_img - clone end diff --git a/app/models/project.rb b/app/models/project.rb index 0ba2f943c..0baab5d64 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -189,4 +189,12 @@ class Project < ActiveRecord::Base .distinct end end + + def notifications_count(user) + res = 0 + assigned_modules(user).find_each do |t| + res += 1 if t.is_overdue? || t.is_one_day_prior? + end + res + end end diff --git a/app/models/protocol.rb b/app/models/protocol.rb index dc8270bef..8f4049561 100644 --- a/app/models/protocol.rb +++ b/app/models/protocol.rb @@ -268,7 +268,7 @@ class Protocol < ActiveRecord::Base step2.save # Copy checklists - step.checklists.each do |checklist| + step.checklists.asc.each do |checklist| checklist2 = Checklist.new( name: checklist.name, step: step2 @@ -372,7 +372,7 @@ class Protocol < ActiveRecord::Base end def completed_steps - steps.select(&:completed) + steps.where(completed: true) end def space_taken diff --git a/app/models/temp_file.rb b/app/models/temp_file.rb index 90fbbcc3d..b351f5f2a 100644 --- a/app/models/temp_file.rb +++ b/app/models/temp_file.rb @@ -3,4 +3,11 @@ class TempFile < ActiveRecord::Base has_attached_file :file do_not_validate_attachment_file_type :file + + def destroy_obsolete + destroy! if self + end + + handle_asynchronously :destroy_obsolete, + run_at: proc { 7.days.from_now } end diff --git a/app/models/tiny_mce_asset.rb b/app/models/tiny_mce_asset.rb index 6898c1ea5..b38004a11 100644 --- a/app/models/tiny_mce_asset.rb +++ b/app/models/tiny_mce_asset.rb @@ -25,7 +25,7 @@ class TinyMceAsset < ActiveRecord::Base # When using S3 file upload, we can limit file accessibility with url signing def presigned_url(style = :large, download: false, - timeout: Constants::URL_SHORT_EXPIRE_TIME) + timeout: Constants::URL_LONG_EXPIRE_TIME) if stored_on_s3? if download download_arg = 'attachment; filename=' + URI.escape(image_file_name) @@ -46,7 +46,7 @@ class TinyMceAsset < ActiveRecord::Base image.options[:storage].to_sym == :s3 end - def url(style = :large, timeout: Constants::URL_SHORT_EXPIRE_TIME) + def url(style = :large, timeout: Constants::URL_LONG_EXPIRE_TIME) if image.is_stored_on_s3? presigned_url(style, timeout: timeout) else diff --git a/app/views/canvas/full_zoom/_my_module.html.erb b/app/views/canvas/full_zoom/_my_module.html.erb index 5a80aabc9..3a3fcbdc0 100644 --- a/app/views/canvas/full_zoom/_my_module.html.erb +++ b/app/views/canvas/full_zoom/_my_module.html.erb @@ -44,14 +44,14 @@