class Constants #============================================================================= # String lengths #============================================================================= # Min characters for short text fields NAME_MIN_LENGTH = 2 # Max characters for short text fields NAME_MAX_LENGTH = 255 # Max characters for short text fields, after which they get truncated NAME_TRUNCATION_LENGTH = 25 # Max edge length MAX_EDGE_LENGTH = 75 # Max character for listing projects in dropdown MAX_NAME_TRUNCATION = 150 # Max characters for short text fields, in dropdownList NAME_TRUNCATION_LENGTH_DROPDOWN = 20 # Max characters for long text fields TEXT_MAX_LENGTH = 10000 # Max characters for rich text fields (in html format) RICH_TEXT_MAX_LENGTH = 1000000 # Max characters for color field (given in HEX format) COLOR_MAX_LENGTH = 7 # Max characters for text in dropdown list element DROPDOWN_TEXT_MAX_LENGTH = 15 # Max characters for text in modal list element MODAL_TEXT_MAX_LENGTH = 55 # Max characters limit for (on most operating systems, it's ~255 characters, # but this is with a bit more safety margin) FILENAME_MAX_LENGTH = 100 # Max characters for filenames, after which they get truncated FILENAME_TRUNCATION_LENGTH = 50 # Max characters for names of exported files and folders, after which they get # truncated EXPORTED_FILENAME_TRUNCATION_LENGTH = 20 USER_INITIALS_MAX_LENGTH = 4 # Password 'key stretching' factor PASSWORD_STRETCH_FACTOR = 10 # Standard max length for email EMAIL_MAX_LENGTH = 254 # Some big value which is still supported by all databases, no matter what # data type is used INFINITY = ((2**32) / 2) - 1 # Prevents integer overflow for reminder delta seconds MAX_NUMBER_OF_REMINDER_WEEKS = 816 #============================================================================= # Query/display limits #============================================================================= # General limited/unlimited query/display elements for pages SEARCH_LIMIT = 20 # General global search limit for object groups GLOBAL_SEARCH_PREVIEW_LIMIT = 4 SEARCH_NO_LIMIT = -1 # General limited query/display elements for popup modals MODAL_SEARCH_LIMIT = 5 # Comments limited query/display elements for pages COMMENTS_SEARCH_LIMIT = 10 # Activity limited query/display elements for pages ACTIVITY_AND_NOTIF_SEARCH_LIMIT = 20 # Infinite Scroll load limit (elements per page) INFINITE_SCROLL_LIMIT = 20 # Maximum number of users that can be invited in a single action INVITE_USERS_LIMIT = 20 # Maximum nr. of search results for atwho (smart annotations) ATWHO_SEARCH_LIMIT = 10 # Max characters for repository name in Atwho modal ATWHO_REP_NAME_LIMIT = 16 # Results limited query/display elements for pages RESULTS_PER_PAGE_LIMIT = 10 #Experiments more button appears EXPERIMENT_LONG_DESCRIPTION = 80 # Infinite scroll default elements per page DEFAULT_ELEMENTS_PER_PAGE = 20 # Default navigator width DEFAULT_NAV_WIDTH = 208 #============================================================================= # File and data memory size #============================================================================= # Max table JSON size in MB TABLE_JSON_MAX_SIZE_MB = 20 # Max uploaded user picture avatar size in MB AVATAR_MAX_SIZE_MB = 0.2 # PDF preview file limit in MB PDF_PREVIEW_MAX_SIZE_MB = 10 #============================================================================= # Application space #============================================================================= # Minimal space needed for team (in B) MINIMAL_TEAM_SPACE_TAKEN = 1.megabyte # Additional space of each file is added to its estimated size to account for # DB indexes size etc. ASSET_ESTIMATED_SIZE_FACTOR = 1.1 #============================================================================= # Format sizes #============================================================================= # Picture size formats LARGE_PIC_FORMAT = [800, 600].freeze MEDIUM_PIC_FORMAT = [300, 300].freeze THUMB_PIC_FORMAT = [100, 100].freeze ICON_PIC_FORMAT = [40, 40].freeze ICON_SMALL_PIC_FORMAT = [30, 30].freeze # Hands-on-table number of starting columns and rows HANDSONTABLE_INIT_COLS_CNT = 5 HANDSONTABLE_INIT_ROWS_CNT = 5 # Word reports format. All units in Twips. # A twip is 1/20 of a point. Word documents are printed at 72dpi. 1in == 72pt == 1440 twips. # Here is default A4 REPORT_DOCX_WIDTH = 12240 REPORT_DOCX_HEIGHT = 15840 REPORT_DOCX_MARGIN_TOP = 720 REPORT_DOCX_MARGIN_RIGHT = 720 REPORT_DOCX_MARGIN_BOTTOM = 720 REPORT_DOCX_MARGIN_LEFT = 720 # Word borders in eighth point units. # A eighth point is 1/8 of a point. A border size of 4 is equivalent to 0.5pt. REPORT_DOCX_TABLE_BORDER_SIZE = 4 # All font size in half points REPORT_DOCX_REPORT_TITLE_SIZE = 36 REPORT_DOCX_EXPERIMENT_TITLE_SIZE = 32 REPORT_DOCX_MY_MODULE_TITLE_SIZE = 28 REPORT_DOCX_STEP_TITLE_SIZE = 22 REPORT_DOCX_STEP_ELEMENTS_TITLE_SIZE = 20 #============================================================================= # Styling #============================================================================= # Dropdown top offset from the parent DROPDOWN_TOP_OFFSET_PX = 20 #============================================================================= # Date and time #============================================================================= # URL expire time, used for presigned file URLs, because outsiders shouldn't # have access to them, but some buffer time is needed for file to be loaded URL_SHORT_EXPIRE_TIME = 30 # Same as URL_EXPIRE_TIME, except for cases where the URL migth be used in # another page, and hence the URL mustn't expire by then (e.g. when generating # report and than using same HTML code in PDF, and consequently same file # URL); it expires in exactly one day URL_LONG_EXPIRE_TIME = 86_400 DEFAULT_DATE_FORMAT = '%m/%d/%Y'.freeze SUPPORTED_DATE_FORMATS = [ # US formats '%m/%d/%Y', '%m.%d.%Y', '%m. %d. %Y', '%m-%d-%Y', '%-m/%-d/%Y', '%-m.%-d.%Y', '%-m. %-d. %Y', '%-m-%-d-%Y', # European formats '%d/%m/%Y', '%d.%m.%Y', '%d. %m. %Y', '%d-%b-%Y', '%Y-%m-%d', '%d.%b.%Y', '%Y/%b/%d', '%d, %B, %Y', '%B, %d, %Y', '%-d/%-m/%Y', '%-d.%-m.%Y', '%-d. %-m. %Y', '%d-%m-%Y', '%Y-%-m-%-d', '%-d-%b-%Y', '%Y-%b-%-d', '%-d, %B, %Y', '%B, %-d, %Y' ].freeze #============================================================================= # Application colors # # NOTE: Don't use shortened syntax, e.g. #000 for #000000, as some Gems need # full syntax! #============================================================================= TAG_COLORS = %i( #C4D3A0 #5EC66F #46C3C8 #A3CCE4 #3B99FD #104DA9 #6F2DC1 #FF69B4 #DF3562 #AD0015 #FF5C00 #E9A845 #B06500 #663300 #1D2939 #98A2B3 #DCE0E7 ).freeze # Theme colors BRAND_PRIMARY = '#104da9'.freeze # $brand-primary # Grayscale colors COLOR_WHITE = '#ffffff'.freeze # $color-white COLOR_CONCRETE = '#f0f0f6'.freeze # $color-concrete COLOR_ALTO = '#d0d0d8'.freeze # $color-alto COLOR_SILVER_CHALICE = '#a0a0a8'.freeze # $color-silver-chalice COLOR_VOLCANO = '#404048'.freeze # $color-volcano COLOR_BLACK = '#231f20'.freeze # $color-black # Fonts FONT_FAMILY_BASE = 'Inter,"Open Sans",Arial,Helvetica,sans-serif;'.freeze # $font-family-base #============================================================================= # External URLs #============================================================================= HTTP = 'http://'.freeze TUTORIALS_URL = ENV.fetch('VIDEO_TUTORIALS_URL', "#{HTTP}goo.gl/YH3fXA").freeze SUPPORT_URL = ENV.fetch('KNOWLEDGE_CENTER_URL', 'https://scinote-3850750.hs-sites.com/en/knowledge').freeze FREE_TRIAL_URL = 'https://www.scinote.net/free-trial/?utm_source=SciNoteShare&utm_medium=FreeTrialButton&utm_campaign=Q3_2023'.freeze # Default user picture avatar DEFAULT_AVATAR_URL = '/images/:style/missing.svg'.freeze ACADEMY_BL_LINK = 'https://scinote.net/academy/?utm_source=SciNote%20software%20BL&utm_medium=SciNote%20software%20BL'.freeze PWA_URL = 'https://:pwa_domain/teams/:team_id/projects/:project_id/experiments/:experiment_id/tasks/:task_id/protocol/:protocol_id/:step_id?domain=:domain'.freeze TWO_FACTOR_URL = { google: { android: 'https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2', ios: 'https://apps.apple.com/us/app/google-authenticator/id388497605' }, microsoft: { android: 'https://play.google.com/store/apps/details?id=com.azure.authenticator', ios: 'https://apps.apple.com/us/app/microsoft-authenticator/id983156458' }, two_fa: { android: 'https://play.google.com/store/apps/details?id=com.twofasapp', ios: 'https://apps.apple.com/us/app/2fa-authenticator-2fas/id1217793794' }, } SCINOTE_FLUICS_URL = 'https://www.scinote.net/fluics/'.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 TWO_FACTOR_RECOVERY_CODE_LENGTH = 12 API_KEY_EXPIRES_IN = 1.year #============================================================================= # Protocol importers #============================================================================= PROTOCOLS_ENDPOINTS = { protocolsio: { v3: 'ProtocolsIo::V3' } }.freeze PROTOCOLS_IO_URL = 'https://www.protocols.io/'.freeze PROTOCOLS_IO_V3_API = { base_uri: 'https://www.protocols.io/api/v3/', default_timeout: 10, debug_level: :debug, sort_mappings: { alpha_asc: { order_field: :name, order_dir: :asc }, alpha_desc: { order_field: :name, order_dir: :desc }, newest: { order_field: :date, order_dir: :desc }, oldest: { order_field: :date, order_dir: :asc } }, endpoints: { protocols: { default_query_params: { filter: :public, key: '', order_field: :activity, order_dir: :desc, page_size: 50, page_id: 0, fields: 'id,title,authors,created_on,uri,stats,published_on' } }, publications: { default_query_params: { latest: 20 } } }, source_id: 'protocolsio/v3' }.freeze PROTOCOLS_DESC_TAGS = %w(a img i br).freeze #============================================================================= # Other #============================================================================= FILE_TEXT_FORMATS = %w(doc docm docx dot dotm dotx odt rtf).freeze FILE_TABLE_FORMATS = %w(csv ods xls xlsb xlsm xlsx).freeze FILE_PRESENTATION_FORMATS = %w(odp pot potm potx pps ppsm ppsx ppt pptm pptx).freeze WOPI_EDITABLE_FORMATS = %w( docx docm odt xlsx xlsm xlsb ods pptx ppsx odp ).freeze TEXT_EXTRACT_FILE_TYPES = [ 'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.template', 'application/pdf', 'application/rtf', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 'application/vnd.openxmlformats-officedocument.presentationml.presentation', 'application/vnd.oasis.opendocument.presentation', 'application/vnd.oasis.opendocument.spreadsheet', 'application/vnd.oasis.opendocument.text', 'application/vnd.ms-excel', 'application/vnd.ms-powerpoint', 'application/vnd.ms-word', 'text/plain' ].freeze PREVIEWABLE_FILE_TYPES = TEXT_EXTRACT_FILE_TYPES # default preview timeout to 15 minutes PREVIEW_TIMEOUT_SECONDS = ENV['PREVIEW_TIMEOUT_SECONDS'] ? ENV['PREVIEW_TIMEOUT_SECONDS'].to_i : 900 WHITELISTED_IMAGE_TYPES = [ 'gif', 'jpeg', 'pjpeg', 'png', 'x-png', 'svg+xml', 'bmp', 'tiff', 'jpg' ].freeze WHITELISTED_IMAGE_TYPES_EDITABLE = %w( jpeg pjpeg png ).freeze config = Sanitize::Config::RELAXED.deep_dup config[:attributes][:all] << 'id' config[:attributes][:all] << 'contenteditable' config[:attributes]['img'] << 'data-mce-token' config[:attributes]['img'] << 'data-source-type' config[:attributes]['a'] << 'data-turbolinks' config[:protocols]['img']['src'] << 'data' INPUT_SANITIZE_CONFIG = Sanitize::Config.freeze_config(config) REPOSITORY_DEFAULT_PAGE_SIZE = 10 REPOSITORY_LIST_ITEMS_PER_COLUMN = 500 REPOSITORY_CHECKLIST_ITEMS_PER_COLUMN = 50 REPOSITORY_STOCK_UNIT_ITEMS_PER_COLUMN = 50 REPOSITORY_NUMBER_TYPE_DEFAULT_DECIMALS = 2 REPOSITORY_NUMBER_TYPE_MAX_DECIMALS = 10 REPOSITORY_DATETIME_REMINDER_PRESET = 10 # Repository default table state REPOSITORY_TABLE_DEFAULT_STATE = { 'time' => 0, 'start' => 0, 'length' => REPOSITORY_DEFAULT_PAGE_SIZE, 'order' => [[3, 'asc']], # Default sorting by 'name' column 'columns' => [], 'assigned' => 'assigned', 'ColReorder' => [*0..10] } 11.times do |i| REPOSITORY_TABLE_DEFAULT_STATE['columns'] << { 'visible' => (i < 7 && i != 4), # relationship column is hidden by default 'searchable' => (i >= 1 && i != 4), # Checkboxes and relationship column is not searchable 'search' => { 'search' => '', 'smart' => true, 'regex' => false, 'caseInsensitive' => true } } end REPOSITORY_TABLE_DEFAULT_STATE.freeze # Repository default table state REPOSITORY_SNAPSHOT_TABLE_DEFAULT_STATE = { 'time' => 0, 'start' => 0, 'length' => REPOSITORY_DEFAULT_PAGE_SIZE, 'order' => [[1, 'asc']], # Default sorting by 'ID' column 'columns' => [], 'assigned' => 'assigned', 'ColReorder' => [*0..4] } REPOSITORY_SNAPSHOT_TABLE_DEFAULT_STATE['columns'] = REPOSITORY_TABLE_DEFAULT_STATE['columns'][0..4].deep_dup REPOSITORY_SNAPSHOT_TABLE_DEFAULT_STATE['columns'][4]['visible'] = true REPOSITORY_SNAPSHOT_TABLE_DEFAULT_STATE.freeze # For default custom column template, any searchable default # column can be reused REPOSITORY_TABLE_STATE_CUSTOM_COLUMN_TEMPLATE = REPOSITORY_TABLE_DEFAULT_STATE['columns'][1].deep_dup .freeze EXPORTABLE_ZIP_EXPIRATION_DAYS = 7 REPOSITORY_LIST_ITEMS_DELIMITERS_MAP = { return: "\n", comma: ',', semicolon: ';', space: ' ' }.freeze REPOSITORY_LIST_ITEMS_DELIMITERS_ICON_MAP = { auto: "*", return: "↵", comma: ',', semicolon: ';', space: '⎵' }.freeze IMPORT_REPOSITORY_ITEMS_LIMIT = 2000 IMPORT_REPOSITORY_ITEMS_MIN_LIMIT = 2 DEFAULT_TEAM_REPOSITORIES_LIMIT = 6 # Very basic regex to check for validity of emails BASIC_EMAIL_REGEX = URI::MailTo::EMAIL_REGEXP TINY_MCE_ASSET_REGEX = /data-mce-token="(\w+)"/ # Team name for default admin user DEFAULT_PRIVATE_TEAM_NAME = 'My projects'.freeze TEMPLATES_PROJECT_NAME = 'SciNote Examples'.freeze # Interval time for polling status state FAST_STATUS_POLLING_INTERVAL = 5000 SLOW_STATUS_POLLING_INTERVAL = 10000 # Interval time for polling asset changes when editing with SciNote Edit ASSET_POLLING_INTERVAL = 5000 ASSET_SYNC_TOKEN_EXPIRATION = 1.year ASSET_SYNC_URL = ENV['ASSET_SYNC_URL'].freeze # Grover timeout in ms GROVER_TIMEOUT_MS = 450000 # SciNote Edit supported versions MIN_SCINOTE_EDIT_VERSION = ENV['MIN_SCINOTE_EDIT_VERSION'].freeze MAX_SCINOTE_EDIT_VERSION = ENV['MAX_SCINOTE_EDIT_VERSION'].freeze # SciNote Edit unsupported extensions SCINOTE_EDIT_RESTRICTED_EXTENSIONS = %w( ACTION APP BIN COMMAND CSH OSX WORKFLOW DMG BAT BIN CAB CMD COM CPL EX_ EXE GADGET INF1 INS INX ISU JOB JSE LNK MSC MSI MSP MST PAF PIF PS1 REG RGS SCR SCT SHB SHS U3P VB VBE VBS VBSCRIPT WS WSF WSH ).freeze # quick search QUICK_SEARCH_LIMIT = 5 QUICK_SEARCH_SEARCHABLE_OBJECTS = %w(project experiment my_module protocol repository_row report project_folder result label_template).freeze # ) \ / ( # /|\ )\_/( /|\ # * / | \ (/\|/\) / | \ * # |`.____________________/__|__o____\`|'/___o__|__\___________________.'| # | '^` \|/ '^` | # | V | # | _____ _ _ __ | # | |_ _| |__ __ _ _ __ | | __ _ _ ___ _ _ ( | | # | | | | '_ \ / _` | '_ \| |/ / | | | |/ _ \| | | | | ) | # | | | | | | | (_| | | | | < | |_| | ( | | |_| | \_/ | # | |_| |_| |_|\__,_|_| |_|_|\_\ \__, |\___/ \_,|_| _ | # | |___/ (_) | # | | # | Special Thank You for supporting SciNote on Kicstarter goes | # | to the following supporters | # | ._________________________________________________________________. | # |' l /\ / \\ \ /\ l `| # * l / V )) V \ l * # l/ // \I KICKSTARTER_SUPPORTERS = [ 'Manuela Lanzafame', 'Fluckiger Rudolf', 'Emily Gleason', 'Benjamin E Doremus', 'Chord Pet Wearable', 'Chris Taylor', 'Abraham White', 'Ryotaro Eguchi', 'Simon Waldherr', 'Isaac Sandaljian', 'Markus Rademacher' ].freeze end