Merge branch 'develop' into features/sso-improvements

This commit is contained in:
Andrej 2024-04-03 13:32:15 +02:00
commit 6e95ba13f9
36 changed files with 180 additions and 92 deletions

View file

@ -603,7 +603,7 @@ GEM
rb-fsevent (0.11.2)
rb-inotify (0.10.1)
ffi (~> 1.0)
rdoc (6.3.3)
rdoc (6.3.4.1)
recaptcha (5.14.0)
regexp_parser (2.8.1)
responders (3.1.1)

View file

@ -1 +1 @@
1.31.0.1
1.32.0

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View file

@ -31,7 +31,6 @@ var CommentsSidebar = (function() {
// Replace the number in comment element
commentsCounter.text(commentsCounter.text().replace(/[\d\\+]+/g, commentsAmount));
commentsCounter.removeClass('hidden');
commentsCounter.css('display', 'flex');
}
}

View file

@ -22,20 +22,6 @@
}
}
.btn-beta-icon {
&::after {
background-color: var(--sn-coral);
border-radius: 1px;
color: var(--sn-white);
content: "BETA";
font-size: .5rem;
left: 7rem;
padding: .125rem .25rem;
position: absolute;
top: .1rem;
}
}
.dropdown-menu {
border: 0;
}

View file

@ -74,6 +74,7 @@ class ProjectsController < ApplicationController
end
def update
default_public_user_role_name_before_update = @project.default_public_user_role&.name
@project.assign_attributes(project_update_params)
return_error = false
flash_error = t('projects.update.error_flash', name: escape_input(@project.name))
@ -127,7 +128,7 @@ class ProjectsController < ApplicationController
log_activity(:project_remove_access_from_all_team_members,
@project,
{ visibility: message_visibility,
role: @project.default_public_user_role.name,
role: default_public_user_role_name_before_update,
team: @project.team.id })
end

View file

@ -129,13 +129,13 @@ class ProtocolsController < ApplicationController
project_folder = record.my_module.experiment.project.project_folder
{
my_module_name: record.my_module.name,
experiment_name: record.my_module.experiment.name,
project_name: record.my_module.experiment.project.name,
my_module_name: record.my_module.name_with_label,
experiment_name: record.my_module.experiment.name_with_label,
project_name: record.my_module.experiment.project.name_with_label,
my_module_url: protocols_my_module_path(record.my_module),
experiment_url: my_modules_path(experiment_id: record.my_module.experiment.id),
project_url: experiments_path(project_id: record.my_module.experiment.project.id),
project_folder_name: project_folder.present? ? project_folder.name : nil,
project_folder_name: project_folder.present? ? project_folder.name_with_label : nil,
project_folder_url: project_folder.present? ? project_folder_projects_url(project_folder) : nil
}
},

View file

@ -1,11 +1,12 @@
<template>
<div class="px-3 pt-3 pb-4 rounded border-solid border border-sn-gray flex flex-col h-56"
<div class="px-3 pt-3 pb-4 rounded border-solid border border-sn-gray flex flex-col"
:class="{ 'bg-sn-light-grey': dtComponent.currentViewMode === 'archived', [cardMinWidth]: true}">
<div class="flex items-center gap-4 mb-2">
<div class="sci-checkbox-container">
<input
type="checkbox"
class="sci-checkbox"
:checked="cardSelected"
@change="itemSelected"
/>
<label :for="params.id" class="sci-checkbox-label"></label>
@ -16,7 +17,7 @@
<a :href="params.urls.show"
:title="params.name"
:class="{'pointer-events-none text-sn-grey': !params.urls.show}"
class="font-bold mb-auto text-sn-blue hover:no-underline line-clamp-2 hover:text-sn-blue h-10">
class="font-bold mb-4 shrink-0 text-sn-blue hover:no-underline line-clamp-2 hover:text-sn-blue h-10">
{{ params.name }}
</a>
<div class="flex gap-4 mb-2.5">

View file

@ -199,7 +199,7 @@ export default {
label: this.i18n.t('filters_modal.created_on.label')
},
{
key: 'created_at',
key: 'updated_on',
type: 'DateRange',
label: this.i18n.t('filters_modal.updated_on.label')
}

View file

@ -1,6 +1,12 @@
<template>
<div class="group relative flex items-center group-hover:marker text-xs">
<div class="flex items-end gap-2">
<div class="group relative flex items-center group-hover:marker text-xs h-full w-full"
:style="{ lineHeight: 'unset' }">
<div class="flex gap-2"
:style="{ lineHeight: 'unset' }"
:class="{
'items-center text-sm': params.dtComponent.currentViewRender === 'table',
'items-end text-xs': params.dtComponent.currentViewRender === 'cards'
}">
<span v-if="shouldTruncateText"
class="cursor-pointer grow"
:class="{
@ -11,7 +17,14 @@
v-html="params.data.sa_description">
</span>
<span v-else class="grow" v-html="params.data.sa_description"></span>
<span v-if="shouldTruncateText" @click.stop="showDescriptionModal" class="text-sn-blue cursor-pointer shrink-0 text-xs">{{ i18n.t('experiments.card.more') }}</span>
<span v-if="shouldTruncateText" @click.stop="showDescriptionModal" class="text-sn-blue cursor-pointer shrink-0 inline-block"
:style="{ lineHeight: 'unset' }"
:class="{
'text-xs': params.dtComponent.currentViewRender === 'cards',
'text-sm': params.dtComponent.currentViewRender === 'table'
}">
{{ i18n.t('experiments.card.more') }}
</span>
</div>
</div>
</template>

View file

@ -126,7 +126,8 @@ export default {
field: 'due_date',
headerName: this.i18n.t('experiments.table.column.due_date_html'),
sortable: true,
cellRenderer: DueDateRenderer
cellRenderer: DueDateRenderer,
minWidth: 200
},
{
field: 'results',
@ -143,7 +144,8 @@ export default {
field: 'status',
headerName: this.i18n.t('experiments.table.column.status_html'),
sortable: true,
cellRenderer: this.statusRenderer
cellRenderer: this.statusRenderer,
minWidth: 120
}
];

View file

@ -1,6 +1,6 @@
<template>
<div v-if="params.data.designated_users.length > 0 || params.data.permissions.manage_designated_users">
<GeneralDropdown @open="loadUsers" @close="closeFlyout">
<GeneralDropdown @open="loadUsers" @close="closeFlyout" position="right">
<template v-slot:field>
<div v-if="!params.data.folder" class="flex items-center gap-1 cursor-pointer h-9" @click="openAccessModal">
<div v-for="(user, i) in visibleUsers" :key="i" :title="user.full_name">
@ -17,7 +17,7 @@
</div>
</template>
<template v-slot:flyout>
<div v-if="canManage" class="sci-input-container-v2 left-icon mb-1">
<div v-if="canManage" class="sci-input-container-v2 left-icon mb-1 -mx-2.5">
<input type="text"
v-model="query"
class="sci-input-field"
@ -25,7 +25,7 @@
:placeholder="i18n.t('general.search')" />
<i class="sn-icon sn-icon-search"></i>
</div>
<perfect-scrollbar class="flex flex-col relative max-h-96 overflow-y-auto max-w-[280px] pr-4 pl-2 gap-y-px">
<perfect-scrollbar class="flex flex-col relative max-h-80 overflow-y-auto max-w-[280px] pt-1 -mx-2.5 pr-4 pl-2 gap-y-px">
<div v-for="user in allUsers"
:key="user.value"
@click="selectUser(user)"

View file

@ -1,12 +1,13 @@
<template>
<div v-if="!params.folder"
:class="{ 'bg-sn-light-grey': dtComponent.currentViewMode === 'archived', [cardMinWidth]: true }"
class="px-3 pt-3 pb-4 rounded border-solid border border-sn-gray flex flex-col h-56" >
class="px-3 pt-3 pb-4 rounded border-solid border border-sn-gray flex flex-col" >
<div class="flex items-center gap-4 mb-2">
<div class="sci-checkbox-container">
<input
type="checkbox"
class="sci-checkbox"
:checked="cardSelected"
@change="itemSelected"
/>
<label :for="params.id" class="sci-checkbox-label"></label>
@ -17,7 +18,7 @@
<a :href="params.urls.show"
:title="params.name"
:class="{'pointer-events-none text-sn-grey': !params.urls.show}"
class="font-bold mb-4 text-sn-blue hover:no-underline line-clamp-3 hover:text-sn-blue h-[60px]">
class="font-bold mb-4 text-sn-blue shrink-0 hover:no-underline line-clamp-3 hover:text-sn-blue h-[60px]">
{{ params.name }}
</a>
<div class="grid gap-x-2 gap-y-3 grid-cols-[90px_auto] mt-auto text-xs">
@ -49,6 +50,7 @@
<input
type="checkbox"
class="sci-checkbox"
:checked="cardSelected"
@change="itemSelected"
/>
<label :for="params.id" class="sci-checkbox-label"></label>

View file

@ -337,7 +337,7 @@ export default {
project_folder_ids: rows.filter((row) => row.folder).map((row) => row.id)
}).then((response) => {
this.reloadingTable = true;
HelperModule.flashAlertMsg(response.data.message, 'success');
HelperModule.flashAlertMsg(response.data.flash, 'success');
}).catch((error) => {
HelperModule.flashAlertMsg(error.response.data.error, 'danger');
});

View file

@ -86,9 +86,9 @@
@click="openCommentsSidebar"
:data-object-id="step.id">
<i class="sn-icon sn-icon-comments"></i>
<span class="comments-counter" v-show="step.attributes.comments_count"
<span class="comments-counter"
:id="`comment-count-${step.id}`"
:class="{'unseen': step.attributes.unseen_comments}"
:class="{'unseen': step.attributes.unseen_comments, 'hidden': !step.attributes.comments_count}"
>
{{ step.attributes.comments_count }}
</span>

View file

@ -211,7 +211,10 @@ export default {
if (this.docxParserEnabled) {
importMenu.menuItems.push({
emit: 'import_docx',
text: this.i18n.t('protocols.index.import_docx')
text: `<span>${this.i18n.t('protocols.index.import_docx')}</span>
<span class="bg-sn-coral text-sn-white text-[8px] absolute leading-none p-1 top-px rounded-[1px] right-px">
${this.i18n.t('protocols.index.beta')}
</span>`
});
}

View file

@ -25,7 +25,7 @@
{{ i18n.t('projects.reports.index.docx') }}
</a>
</template>
<a v-else class="hidden group-hover:!block" href="#" @click.prevent="generate">
<a v-else class="opacity-0 group-hover:opacity-100" href="#" @click.prevent="generate">
{{ i18n.t('projects.reports.index.generate') }}
</a>
</div>

View file

@ -272,7 +272,7 @@ export default {
templateOption(option) {
return `
<div class="label-template-option" data-toggle="tooltip" data-placement="right" title="${option.params.description}">
<img src="${option.params.icon}" style=""></img>
${option.params.icon}
${option.label}
</div>
`;

View file

@ -72,7 +72,7 @@
data-object-type="Result"
:data-object-id="result.id">
<i class="sn-icon sn-icon-comments"></i>
<span class="comments-counter" v-show="result.attributes.comments_count"
<span class="comments-counter" :class="{ 'hidden': !result.attributes.comments_count }"
:id="`comment-count-${result.id}`">
{{ result.attributes.comments_count }}
</span>

View file

@ -26,7 +26,7 @@
<MenuDropdown
v-if="params.object.top_level_assignable && params.object.urls.update_access"
class="ml-auto"
:listItems="rolesFromatted"
:listItems="rolesFromatted(default_role)"
:btnText="this.roles.find((role) => role[0] == default_role)[1]"
:position="'right'"
:caret="true"
@ -57,9 +57,9 @@
<div class="text-xs text-sn-grey text-nowrap">{{ userAssignment.attributes.inherit_message }}</div>
</div>
<MenuDropdown
v-if="!userAssignment.attributes.last_owner && params.object.urls.update_access"
v-if="!userAssignment.attributes.last_owner && params.object.urls.update_access && !(userAssignment.attributes.current_user && userAssignment.attributes.inherit_message)"
class="ml-auto"
:listItems="rolesFromatted(userAssignment)"
:listItems="rolesFromatted(userAssignment.attributes.user_role.id)"
:btnText="userAssignment.attributes.user_role.name"
:position="'right'"
:caret="true"
@ -140,7 +140,7 @@ export default {
this.$emit('usersReloaded');
});
},
rolesFromatted(userAssignment = null) {
rolesFromatted(activeRoleId = null) {
let roles = [];
if (!this.params.object.top_level_assignable) {
@ -157,7 +157,7 @@ export default {
emit: 'setRole',
text: role[1],
params: role[0],
active: (userAssignment?.attributes?.user_role?.id === role[0])
active: (activeRoleId === role[0])
}
)));

View file

@ -111,6 +111,23 @@ export default {
this.extension = extension;
this.imageBlob = imageBlob;
}
},
uploadImage() {
const newName = this.fileName;
const imageBlog = this.imageBlob;
// check if the name is set
if (newName && newName.length > 0) {
const extension = imageBlog.name.slice(
(Math.max(0, imageBlog.name.lastIndexOf('.')) || Infinity) + 1
);
// hack to inject custom name in File object
const name = newName + '.' + extension;
const blob = imageBlog.slice(0, imageBlog.size, imageBlog.type);
// make new blob with the correct name;
this.imageBlob = new File([blob], name, { type: imageBlog.type });
}
$(this.$refs.modal).modal('hide');
this.$emit('files', this.imageBlob, this.target);
}
}
};

View file

@ -8,8 +8,7 @@ export default {
data() {
return {
localAppName: null,
scinoteEditRunning: false,
scinoteEditVersion: null,
scinoteEditRunning: null,
showNoPredefinedAppModal: false,
showUpdateVersionModal: false,
editAppModal: false,
@ -36,34 +35,65 @@ export default {
this.stopPolling();
},
methods: {
async fetchLocalAppInfo() {
async checkScinoteEditRunning() {
// responses will be cached on window, so the requests only run once per page load
if (window.scinoteEditRunning !== undefined) {
this.scinoteEditRunning = window.scinoteEditRunning;
return;
}
try {
const statusResponse = await axios.get(
`${this.attributes.urls.open_locally_api}/status`
);
if (statusResponse.status === 200) {
this.scinoteEditRunning = true;
this.scinoteEditVersion = statusResponse.data.version;
window.scinoteEditRunning = true;
window.scinoteEditVersion = statusResponse.data.version;
} else {
return;
window.scinoteEditRunning = false;
}
} catch (error) {
window.scinoteEditRunning = false;
}
this.scinoteEditRunning = window.scinoteEditRunning;
},
async fetchLocalAppInfo() {
this.checkScinoteEditRunning();
// responses will be cached on window, so the requests only run once per page load
if (window.scinoteEditRunning === false) return;
if (window.scinoteEditLocalApps === undefined) {
window.scinoteEditLocalApps = {};
}
if (window.scinoteEditLocalApps[this.attributes.file_extension] !== undefined) {
this.localAppName = window.scinoteEditLocalApps[this.attributes.file_extension];
return;
}
try {
const response = await axios.get(
`${this.attributes.urls.open_locally_api}/default-application/${this.attributes.file_extension}`
);
if (response.data.application.toLowerCase() !== 'pick an app') {
window.scinoteEditLocalApps[this.attributes.file_extension] = response.data.application;
this.localAppName = response.data.application;
}
} catch (error) {
if (error.response?.status === 404) return; // all good, no app was found for the file
if (error.response?.status === 404) {
window.scinoteEditLocalApps[this.attributes.file_extension] = null;
return; // all good, no app was found for the file
}
console.error('Error in request: ', error);
}
},
async openLocally() {
if (this.isWrongVersion(this.scinoteEditVersion)) {
if (this.isWrongVersion(window.scinoteEditVersion)) {
this.showUpdateVersionModal = true;
return;
} else if (this.localAppName === null) {

View file

@ -1,13 +1,12 @@
<template>
<div class="sn-open-locally-menu" @mouseenter="fetchLocalAppInfo">
<button class="btn btn-light" v-if="!editWopiSupported" style="pointer-events: all;"
:title="attachment.attributes.wopi_context.title" disabled="true">
{{ attachment.attributes.wopi_context.button_text }}
</button>
<div v-else-if="!canOpenLocally && (attachment.attributes.wopi && attachment.attributes.urls.edit_asset)">
<a :href="`${attachment.attributes.urls.edit_asset}`" target="_blank"
class="block whitespace-nowrap rounded px-3 py-2.5
hover:!text-sn-blue hover:no-underline cursor-pointer hover:bg-sn-super-light-grey">
<div v-if="!canOpenLocally && (attachment.attributes.wopi && attachment.attributes.urls.edit_asset)">
<a :href="editWopiSupported ? attachment.attributes.urls.edit_asset : null" target="_blank"
class="block whitespace-nowrap rounded px-3 py-2.5 hover:!text-sn-blue hover:no-underline cursor-pointer hover:bg-sn-super-light-grey"
:class="{ 'disabled': !editWopiSupported }"
:title="editWopiSupported ? null : attachment.attributes.wopi_context.title"
style="pointer-events: all"
>
{{ attachment.attributes.wopi_context.button_text }}
</a>
</div>
@ -75,7 +74,9 @@ export default {
if (this.attachment.attributes.wopi && this.attachment.attributes.urls.edit_asset) {
menu.push({
text: this.attachment.attributes.wopi_context.button_text,
url: this.attachment.attributes.urls.edit_asset,
url: this.editWopiSupported ? this.attachment.attributes.urls.edit_asset : null,
disabled: !this.editWopiSupported && 'style-only',
title: this.editWopiSupported ? null : this.attachment.attributes.wopi_context.title,
url_target: '_blank'
});
}

View file

@ -1,11 +1,18 @@
export default {
computed: {
cardSelected() {
const item = this.dtComponent.selectedRows.find((i) => (i.code === this.params.code));
return !!item;
}
},
methods: {
itemSelected() {
const item = this.dtComponent.selectedRows.find((i) => (i.id === this.params.id));
const item = this.dtComponent.selectedRows.find((i) => (i.code === this.params.code));
if (item) {
this.dtComponent.selectedRows = this.dtComponent.selectedRows
.filter((i) => (i.id !== this.params.id));
.filter((i) => (i.code !== this.params.code));
} else {
this.dtComponent.selectedRows.push(this.params);
}

View file

@ -302,6 +302,7 @@ export default {
this.updateTable();
this.$nextTick(() => {
this.selectedRows = [];
this.gridApi?.deselectAll();
});
}
},
@ -352,6 +353,8 @@ export default {
const gap = (maxGridCols - 1) * GLOBAL_CONSTANTS.TABLE_CARD_GAP;
maxGridCols = Math.floor((availableGridWidth - gap - padding) / GLOBAL_CONSTANTS.TABLE_CARD_MIN_WIDTH);
}
// grid-cols-2 grid-cols-3 grid-cols-4 grid-cols-5 grid-cols-6 grid-cols-7 grid-cols-8 grid-cols-9 grid-cols-10
if (maxGridCols > 10) maxGridCols = 10;
if (this.navigatorCollapsed) {
this.gridColsClass = 'grid-cols-2 xl:grid-cols-3 2xl:grid-cols-4';
@ -477,7 +480,10 @@ export default {
if (this.dataLoading) return;
this.dataLoading = true;
if (clearSelection) this.selectedRows = [];
if (clearSelection) {
this.selectedRows = [];
this.gridApi?.deselectAll();
}
this.page = 1;
this.loadData(true);
},
@ -605,6 +611,7 @@ export default {
if (this.currentViewRender === view) return;
this.currentViewRender = view;
this.columnApi = null;
this.gridApi?.deselectAll();
this.gridApi = null;
this.saveTableState();
this.initializing = true;

View file

@ -18,15 +18,17 @@
<span v-for="(item, i) in listItems" :key="i" class="contents">
<div v-if="item.dividerBefore" class="border-0 border-t border-solid border-sn-light-grey"></div>
<a :href="item.url" v-if="!item.submenu"
v-html="item.text"
:target="item.url_target || '_self'"
:class="{ 'bg-sn-super-light-blue': item.active }"
:class="{ 'bg-sn-super-light-blue': item.active, 'disabled': item.disabled }"
:style="item.disabled === 'style-only' && 'pointer-events: all'"
:title="item.title"
:data-toggle="item.modalTarget && 'modal'"
:data-target="item.modalTarget"
:data-e2e="item.data_e2e"
class="block whitespace-nowrap rounded px-3 py-2.5 hover:!text-sn-blue hover:no-underline cursor-pointer hover:bg-sn-super-light-grey leading-5"
class="block whitespace-nowrap rounded px-3 py-2.5 hover:!text-sn-blue hover:no-underline cursor-pointer hover:bg-sn-super-light-grey leading-5 relative"
@click="handleClick($event, item)"
>
{{ item.text }}
</a>
<div v-else class="-mx-2.5 px-2.5 group relative">
<span

View file

@ -45,7 +45,6 @@ export default {
if (!field || !flyout) return;
const rect = field.getBoundingClientRect();
const flyoutRect = flyout.getBoundingClientRect();
const screenHeight = window.innerHeight;
const windowHasScroll = document.documentElement.scrollHeight > document.documentElement.clientHeight;
@ -64,10 +63,7 @@ export default {
flyout.style.minWidth = `${width}px`;
}
if (window.innerWidth - (rect.x + flyoutRect.width) < 0) { // when flyout is out of screen
flyout.style.left = 'unset';
flyout.style.right = `${width - Math.abs(right)}px`;
} else if (this.position === 'right') {
if (this.position === 'right') {
flyout.style.right = `${right - rightScrollOffset}px`;
flyout.style.left = 'unset';
} else {

View file

@ -322,6 +322,10 @@ class Project < ApplicationRecord
report.destroy if report.present?
end
def archived_branch?
archived?
end
private
def project_folder_team

View file

@ -5,10 +5,4 @@ class RepositoryChecklistItemsValue < ApplicationRecord
belongs_to :repository_checklist_value, inverse_of: :repository_checklist_items_values
validates :repository_checklist_item, :repository_checklist_value, presence: true
after_destroy :destroy_empty_value
def destroy_empty_value
repository_checklist_value.destroy! if repository_checklist_value.repository_checklist_items_values.blank?
end
end

View file

@ -7,7 +7,8 @@ module Lists
attributes :name, :language_type, :urls, :type,
:default, :format, :modified_by, :created_by,
:created_at, :updated_at, :id, :icon_url, :description
:created_at, :updated_at, :id, :icon_url, :description,
:width_mm, :height_mm, :unit, :density
def icon_url
ActionController::Base.helpers.image_tag(
@ -35,7 +36,7 @@ module Lists
end
def urls
return {} unless can_manage_label_templates?(object.team)
return {} unless can_view_label_templates?(object.team)
{
show: label_template_path(object)

View file

@ -75,11 +75,31 @@ module Lists
created_at: 'experiments.created_at',
name: 'experiments.name',
code: 'experiments.id',
archived_on: 'COALESCE(experiments.archived_on, projects.archived_on)',
archived_on: 'archived_on',
updated_at: 'experiments.updated_at',
completed_tasks: 'completed_task_count',
description: 'experiments.description'
}
end
def sort_records
return unless @params[:order] && sortable_columns[order_params[:column].to_sym].present?
@records = case order_params[:column]
when 'archived_on'
if order_params[:dir] == 'asc'
@records.order(Arel.sql('COALESCE(experiments.archived_on, projects.archived_on) ASC'))
.group('experiments.archived_on', 'projects.archived_on')
else
@records.order(Arel.sql('COALESCE(experiments.archived_on, projects.archived_on) DESC'))
.group('experiments.archived_on', 'projects.archived_on')
end
else
sort_by = "#{sortable_columns[order_params[:column].to_sym]} #{sort_direction(order_params)}"
@records.order(sort_by)
end
@records = @records.order(:id)
end
end
end

View file

@ -30,9 +30,9 @@ module Reports::Docx::PrivateMethods
end
def insert_logo
logo_data = File.read(Rails.root.join('app/assets/images/scinote_logo.svg'))
logo_data = File.read(Rails.root.join('app/assets/images/scinote_logo.png'))
@docx.img 'logo.svg' do
@docx.img 'logo.png' do
data logo_data
height 20
width 100

View file

@ -128,7 +128,8 @@ module Reports
image = TinyMceAsset.find_by(id: Base62.decode(elem.attributes['data-mce-token'].value))
return unless image
image_path = Reports::Utils.image_prepare(image).url
image_path = Reports::Utils.image_prepare(image).processed.url
dimension = FastImage.size(image_path)
return unless dimension

View file

@ -11,12 +11,12 @@ module Reports
def self.image_prepare(asset)
if asset.class == Asset
if asset.inline?
asset.large_preview
asset.preview_attachment.representation(resize_to_limit: Constants::MEDIUM_PIC_FORMAT, format: :png)
else
asset.medium_preview
asset.preview_attachment.representation(resize_to_limit: Constants::LARGE_PIC_FORMAT, format: :png)
end
elsif asset.class == TinyMceAsset
asset.image
asset.image.representation(format: :png)
end
end

View file

@ -239,7 +239,7 @@ class Constants
}
SCINOTE_FLUICS_URL = 'https://www.scinote.net/fluics/'.freeze
SCINOTE_ZEBRA_DOWNLOAD_URL = 'https://www.zebra.com/us/en/products/software/barcode-printers/link-os/browser-print.html'.freeze
SCINOTE_ZEBRA_DOWNLOAD_URL = 'https://www.zebra.com/us/en/software/printer-software/browser-print.html'.freeze
SCINOTE_ZEBRA_BLOG_URL = 'https://www.scinote.net/blog/connect-zebra-printers/'.freeze
SCINOTE_ZEBRA_SUPPORT_URL = 'https://www.zebra.com/us/en/about-zebra/contact-zebra/contact-tech-support.html'.freeze
TWO_FACTOR_RECOVERY_CODE_COUNT = 6

View file

@ -1561,7 +1561,7 @@ en:
column:
id_html: 'ID'
task_name_html: 'Task name'
due_date_html: 'Due'
due_date_html: 'Due date'
archived_html: 'Archived on'
age_html: 'Age'
results_html: 'Results'
@ -3240,6 +3240,7 @@ en:
import: "Import"
import_eln: "SciNote file (.eln)"
import_docx: "MS Word (.docx)"
beta: "BETA"
import_protocols_io: "From Protocols.io"
modal_import_json_upload: "Upload"
modal_import_json_title: "Import protocols.io file"