fontcustom_canvas 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,298 @@
1
+ /*
2
+ * Copyright (C) 2014 - present Instructure, Inc.
3
+ *
4
+ * This file is part of Canvas.
5
+ *
6
+ * Canvas is free software: you can redistribute it and/or modify it under
7
+ * the terms of the GNU Affero General Public License as published by the Free
8
+ * Software Foundation, version 3 of the License.
9
+ *
10
+ * Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
11
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
12
+ * A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
13
+ * details.
14
+ *
15
+ * You should have received a copy of the GNU Affero General Public License along
16
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+ /// TOC
20
+ //============================================================================================
21
+ //
22
+ // - Canvas Brand Variants
23
+ // - Custom Branding
24
+ // - Canvas LMS Color Sheet
25
+ // - Canvas Theme Color Variables
26
+ //
27
+ //============================================================================================
28
+
29
+ // This imports the variables needed to support accessibility ($use_high_contrast)
30
+ // You can also use the sass function that helps ensure color contrast ratio.
31
+ // Look in /sass-functions/ for more information on how that is used
32
+ @import "variant_variables";
33
+ @import 'https://fonts.googleapis.com/css?family=Abel|Abril+Fatface|Acme|Alegreya|Alegreya+Sans|Anton|Archivo|Archivo+Black|Archivo+Narrow|Arimo|Arvo|Asap|Asap+Condensed|Bitter|Bowlby+One+SC|Bree+Serif|Cabin|Cairo|Catamaran|Crete+Round|Crimson+Text|Cuprum|Dancing+Script|Dosis|Droid+Sans|Droid+Serif|EB+Garamond|Exo|Exo+2|Faustina|Fira+Sans|Fjalla+One|Francois+One|Gloria+Hallelujah|Hind|Inconsolata|Indie+Flower|Josefin+Sans|Julee|Karla|Lato|Libre+Baskerville|Libre+Franklin|Lobster|Lora|Mada|Manuale|Maven+Pro|Merriweather|Merriweather+Sans|Montserrat|Montserrat+Subrayada|Mukta+Vaani|Muli|Noto+Sans|Noto+Serif|Nunito|Open+Sans|Open+Sans+Condensed:300|Oswald|Oxygen|PT+Sans|PT+Sans+Caption|PT+Sans+Narrow|PT+Serif|Pacifico|Passion+One|Pathway+Gothic+One|Play|Playfair+Display|Poppins|Questrial|Quicksand|Raleway|Roboto|Roboto+Condensed|Roboto+Mono|Roboto+Slab|Ropa+Sans|Rubik|Saira|Saira+Condensed|Saira+Extra+Condensed|Saira+Semi+Condensed|Sedgwick+Ave|Sedgwick+Ave+Display|Shadows+Into+Light|Signika|Slabo+27px|Source+Code+Pro|Source+Sans+Pro|Spectral|Titillium+Web|Ubuntu|Ubuntu+Condensed|Varela+Round|Vollkorn|Work+Sans|Yanone+Kaffeesatz|Zilla+Slab|Zilla+Slab+Highlight|Nerko+One|Xanh+Mono|Texturina|Lobster|Dancing+Script|Architects+Daughter|Modak|Amatic+SC';
34
+
35
+
36
+ //=======================
37
+ // Canvas LMS Color Sheet
38
+ //=======================
39
+ // Our color variables in Canvas are simple and easy to integrate
40
+ // We use a two-tiered variable process with Canvas color variables.
41
+ // When you need to use a color, please create a functional variable name
42
+ // and use the color variable name of your choosing. See examples below.
43
+
44
+ // These "@if $use_high_contrast ..." conditions need to be run before we load
45
+ // the autogenerated brandable variables because we are setting up the correct
46
+ // "default value" for it to use.
47
+
48
+ //====================================
49
+ // Abstract Color Variables
50
+ //====================================
51
+ $electric: #008EE2; // blue
52
+ @if $use_high_contrast { $electric: #0770A3; }
53
+ $shamrock: #00AC18; // green
54
+ @if $use_high_contrast { $shamrock: #127A1B; }
55
+ $barney: #BF32A4; // magenta
56
+ @if $use_high_contrast { $barney: #B8309E; }
57
+ $crimson: #EE0612; // red
58
+ @if $use_high_contrast { $crimson: #D01A19; }
59
+ $fire: #FC5E13; // orange
60
+ @if $use_high_contrast { $fire: #C23C0D; }
61
+
62
+ //====================================
63
+ // Default Functional Swatches
64
+ //====================================
65
+ $ic-color-success: $shamrock;
66
+ $ic-color-action: $barney;
67
+ $ic-color-danger: $crimson;
68
+ $ic-color-alert: $fire;
69
+
70
+ //====================================
71
+ // Gray-ish Scale
72
+ //====================================
73
+
74
+ $ic-color-dark: #2D3B45; // licorice
75
+ $ic-color-medium-darker: #394B58; // oxford
76
+ $ic-color-medium: #73818C; // slate
77
+ @if $use_high_contrast { $ic-color-medium: #556572; }
78
+ $ic-color-medium-lighter: #8B969E; // ash
79
+ @if $use_high_contrast { $ic-color-medium-lighter: #556572; }
80
+ $heather: #A5AFB5; // heather
81
+ @if $use_high_contrast { $ic-border-dark: #556572; }
82
+ $tiara: #C7CDD1; // tiara
83
+ @if $use_high_contrast { $ic-border-color: #667887; }
84
+ $ic-color-medium-light: #F5F5F5; // porcelain
85
+ @if $use_high_contrast { $ic-color-medium-light: #FFFFFF; }
86
+ $ic-color-light: #FFFFFF; // white
87
+
88
+
89
+ //================================
90
+ // Global light background colors
91
+ //================================
92
+ // These background color variables can be used on hover states or focus states
93
+ // and meet our contrast needs when used with our main color variables
94
+
95
+ $ic-bg-light-neutral: $ic-color-medium-light;
96
+ $ic-bg-light-neutral-text: #394B58;
97
+ @if $use_high_contrast { $ic-bg-light-neutral: #EBEDEE; }
98
+ $ic-bg-light-primary: #E5F2F8;
99
+ @if $use_high_contrast { $ic-bg-light-primary: #E6F1F7;}
100
+ $ic-bg-light-primary-text: #0078BD;
101
+ @if $use_high_contrast { $ic-bg-light-primary-text: #0770A3; }
102
+ $ic-bg-light-success: #E8F6E9;
103
+ @if $use_high_contrast { $ic-bg-light-success: #E8F2E9; }
104
+ $ic-bg-light-success-text: #008A13;
105
+ @if $use_high_contrast { $ic-bg-light-success-text: $ic-color-success; }
106
+ $ic-bg-light-alert: #FBEDE7;
107
+ @if $use_high_contrast { $ic-bg-light-alert: #F9ECE8; }
108
+ $ic-bg-light-alert-text: #D14604;
109
+ @if $use_high_contrast { $ic-bg-light-alert-text: $ic-color-alert; }
110
+ $ic-bg-light-danger: #FCE8E7;
111
+ @if $use_high_contrast { $ic-bg-light-danger: #FBE8E8; }
112
+ $ic-bg-light-danger-text: #EE0612;
113
+ @if $use_high_contrast { $ic-bg-light-danger-text: $ic-color-danger; }
114
+
115
+ //====================================
116
+ // DEPRECATED COLORS - DO NOT USE
117
+ //====================================
118
+
119
+ $ic-color-neutral: hsl(0,0, 90%);
120
+ $gray-darker: $ic-color-dark;
121
+ $gray-dark: $ic-color-medium-darker;
122
+ $gray: $ic-color-medium;
123
+ $gray-light: $ic-color-medium-light;
124
+ $gray-lighter: $ic-color-light;
125
+
126
+ //==================
127
+ // Layout Variables
128
+ //==================
129
+ $right_side_width: 286px;
130
+ $right_side_margin: 13px;
131
+ $left_side_width: 175px;
132
+ $min_main_width: 510px;
133
+ $max_main_width: 1100px;
134
+ $ic-sp: 12px;
135
+ $ic-border-radius: $ic-sp/2;
136
+ $spacing-width: 20px; // TODO - would be nice to phase this one out for $ic-sp
137
+ $borderRadius: 4px;
138
+ $contentBoxPadding: 8px;
139
+
140
+ //============================
141
+ // Typography Variables
142
+ //============================
143
+
144
+ // Placeholder for old bootstrap variable; deprecated, but still in use some places
145
+ // Once all of those have been adjusted to use the new variable we can get rid of this
146
+ $ic-line-height: 1.5;
147
+
148
+ $ic-font-family: var(--ic-brand-font-family, "Lato Extended", "Lato", "Helvetica Neue", Helvetica, Arial, sans-serif);
149
+ $ic-font-family-monospace: Monaco, Menlo, Consolas, "Courier New", monospace;
150
+
151
+ $ic-font-weight: var(--ic-brand-font-weight, normal); // we can phase this var out now we're on Lato
152
+ $ic-font-weight-bold: bold;
153
+
154
+
155
+ $ic-font-size: var(--ic-brand-font-size, 16px);
156
+
157
+ $ic-font-size--xxlarge: $ic-font-size + 22;
158
+ $ic-font-size--xlarge: $ic-font-size + 16;
159
+ $ic-font-size--large: $ic-font-size + 10;
160
+ $ic-font-size--medium: $ic-font-size + 4;
161
+ $ic-font-size--small: $ic-font-size;
162
+ $ic-font-size--xsmall: $ic-font-size - 2;
163
+
164
+ /* Legacy variables. Discontinue to use these: */
165
+ $h1-font-size: 23px;
166
+ $h2-font-size: 14px;
167
+ $h3-font-size: 19px;
168
+
169
+ //============================
170
+ // Layout Functional Variables
171
+ //============================
172
+ $ic-body-background-color: lighten($ic-color-neutral, 4);
173
+ $ic-content-background-color: $ic-color-light;
174
+ $ic-content-padding: 15px;
175
+ $ic-header-background: #34444f;
176
+ $ic-header-primary-width: 100px;
177
+ $ic-header-primary-expanded-width: $ic-sp*15;
178
+ $ic-right-side-width: $ic-sp*24;
179
+ $ic-breadcrumbs-height: 4.5rem;
180
+
181
+ //============================
182
+ // List Functional Variables
183
+ //============================
184
+ $ic-list-item-background--hover: #eef7ff;
185
+ $ic-list-item-background--selected: #d9edf9;
186
+
187
+ //=====================
188
+ // Global border colors
189
+ //=====================
190
+ $ic-border-color: $tiara;
191
+ // If it's a light border, it should be this color.
192
+ $ic-border-light: $tiara;
193
+ // If it's a dark border, it should be this color.
194
+ // the most often use case for this is when the line is
195
+ // dashed on a light gray background so it visually
196
+ // looks the same as the outer border
197
+ $ic-border-dark: $heather;
198
+
199
+ //=====================
200
+ // Text-color variables
201
+ //=====================
202
+ $ic-font-color-light: $ic-color-light;
203
+ $ic-font-color-dark: var(--ic-brand-font-color-dark);
204
+ @if $use_high_contrast { $ic-font-color-dark: $ic-color-dark; }
205
+ $ic-font-color--subdued: var(--ic-brand-font-color-dark-lightened-15);
206
+ @if $use_high_contrast { $ic-font-color--subdued: $ic-color-medium-darker; }
207
+ $ic-hint-text: var(--ic-brand-font-color-dark-lightened-30);
208
+ @if $use_high_contrast { $ic-hint-text: $ic-color-medium; }
209
+
210
+ // textColor is a deprecated Bootstrap 2 variable. Please use $ic-font-color-dark!
211
+ $textColor: $ic-font-color-dark; // deprecated - do not use
212
+
213
+ //=============================
214
+ // Link-color-related variables
215
+ //=============================
216
+ $ic-icon-link-color-hover: var(--ic-brand-font-color-dark-lightened-15);
217
+ @if $use_high_contrast {
218
+ $ic-icon-link-color-hover: $ic-font-color-dark;
219
+ }
220
+
221
+ $ic-icon-link-color: var(--ic-brand-font-color-dark-lightened-30);
222
+ @if $use_high_contrast {
223
+ $ic-icon-link-color: lighten($ic-font-color-dark, 12%);
224
+ }
225
+
226
+ // linkColor is a deprecated Bootstrap 2 variable
227
+ $linkColor: var(--ic-link-color); // deprecated - do not use
228
+ $linkColorHover: var(--ic-link-color-darkened-10); // deprecated - do not use
229
+
230
+ //=====================
231
+ // Course Nav Variables
232
+ //=====================
233
+ // These variables control the active and hover
234
+ // states we have on the global left-hand sidenav in Canvas
235
+ $ic-course-sidenav_list-item--bg-color: $ic-color-light;
236
+ @if $use_high_contrast {
237
+ $ic-course-sidenav_list-item--bg-color: $ic-color-dark;
238
+ }
239
+ $ic-course-sidenav_list-item--active-font-color: var(--ic-brand-primary);
240
+ @if $use_high_contrast {
241
+ $ic-course-sidenav_list-item--active-font-color: $ic-color-light;
242
+ }
243
+ $ic-course-sidenav_list-item--inactive-font-color: darken($ic-color-neutral, 12);
244
+
245
+ //===============
246
+ // Icon Variables
247
+ //===============
248
+ //To be used for any disabled icon states
249
+ $ic-color-icon-disabled: darken($ic-color-neutral, 40);
250
+ @if $use_high_contrast {
251
+ $ic-color-icon-disabled: darken($ic-color-neutral, 60);
252
+ }
253
+ $ic-dim-helper-text: darken($ic-color-neutral, 45);
254
+ @if $use_high_contrast {
255
+ $ic-dim-helper-text: darken($ic-color-neutral, 55);
256
+ }
257
+
258
+ //==========================================================
259
+ // Legacy Vendor Variables (please do not touch these files)
260
+ //==========================================================
261
+ // We will be phasing these out
262
+ @import 'vendor/bootstrap/variables';
263
+ @import 'vendor/jqueryui/variables';
264
+
265
+ //==============================
266
+ // Legacy Global Color Variables - DO NOT USE
267
+ //==============================
268
+ // These variables we have reworked to use our canvas color variables for the
269
+ // new UI, however they will need to be evaluated on a case-by-case basis of
270
+ // their use in Canvas in order to properly phase them out
271
+ // Nothing changes in the legacy UI.
272
+
273
+ $lightBackground: $ic-color-medium-light;
274
+ $wellBackground: $ic-color-light;
275
+ $altBG: $ic-bg-light-neutral;
276
+ $borderColor: $ic-border-light;
277
+ $activeBG: $ic-bg-light-primary;
278
+
279
+ //=====================
280
+ // Transition Variables
281
+ //=====================
282
+ // This is a pleasing transition that can be used when you need
283
+ // a transition timing function
284
+ $ic-transition: cubic-bezier(0,1,0.5,1);
285
+
286
+ //=======================
287
+ // Form-related variables
288
+ //=======================
289
+ $ic-horizontal-form-offset: $ic-sp*16;
290
+ $ic-label-line-height: 1.3;
291
+ $ic-radio-checkbox-left-offset: 22px;
292
+
293
+
294
+ // override bootstrap green buttons
295
+ $green: #34832b !default;
296
+
297
+ // override default opacity for disabled classes
298
+ $ic-opacity-disabled: 0.6
@@ -0,0 +1,374 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Copyright (C) 2015 - present Instructure, Inc.
5
+ #
6
+ # This file is part of Canvas.
7
+ #
8
+ # Canvas is free software: you can redistribute it and/or modify it under
9
+ # the terms of the GNU Affero General Public License as published by the Free
10
+ # Software Foundation, version 3 of the License.
11
+ #
12
+ # Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
13
+ # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
14
+ # A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
15
+ # details.
16
+ #
17
+ # You should have received a copy of the GNU Affero General Public License along
18
+ # with this program. If not, see <http://www.gnu.org/licenses/>.
19
+
20
+ require 'pathname'
21
+ require 'yaml'
22
+ require 'open3'
23
+
24
+ module BrandableCSS
25
+ APP_ROOT = defined?(Rails) && Rails.root || Pathname.pwd
26
+ CONFIG = YAML.load_file(APP_ROOT.join('config/brandable_css.yml')).freeze
27
+ BRANDABLE_VARIABLES = JSON.parse(File.read(APP_ROOT.join(CONFIG['paths']['brandable_variables_json']))).freeze
28
+ MIGRATION_NAME = 'RegenerateBrandFilesBasedOnNewDefaults'.freeze
29
+
30
+ use_compressed = (defined?(Rails) && Rails.env.production?) || (ENV['RAILS_ENV'] == 'production')
31
+ SASS_STYLE = ENV['SASS_STYLE'] || ((use_compressed ? 'compressed' : 'nested')).freeze
32
+
33
+ VARIABLE_HUMAN_NAMES = {
34
+ "ic-brand-primary" => lambda { I18n.t("Primary Brand Color") },
35
+ "ic-brand-font-color-dark" => lambda { I18n.t("Main Text Color") },
36
+ "ic-brand-font-family" => lambda { I18n.t("Font Family") },
37
+ "ic-brand-font-size" => lambda { I18n.t("Font Size") },
38
+ "ic-brand-font-weight" => lambda { I18n.t("Font Weight") },
39
+ "ic-link-color" => lambda { I18n.t("Link Color") },
40
+ "ic-brand-button--primary-bgd" => lambda { I18n.t("Primary Button") },
41
+ "ic-brand-button--primary-text" => lambda { I18n.t("Primary Button Text") },
42
+ "ic-brand-button--secondary-bgd" => lambda { I18n.t("Secondary Button") },
43
+ "ic-brand-button--secondary-text" => lambda { I18n.t("Secondary Button Text") },
44
+ "ic-brand-global-nav-bgd" => lambda { I18n.t("Nav Background") },
45
+ "ic-brand-global-nav-ic-icon-svg-fill" => lambda { I18n.t("Nav Icon") },
46
+ "ic-brand-global-nav-ic-icon-svg-fill--active" => lambda { I18n.t("Nav Icon Active") },
47
+ "ic-brand-global-nav-menu-item__text-color" => lambda { I18n.t("Nav Text") },
48
+ "ic-brand-global-nav-menu-item__text-color--active" => lambda { I18n.t("Nav Text Active") },
49
+ "ic-brand-global-nav-avatar-border" => lambda { I18n.t("Nav Avatar Border") },
50
+ "ic-brand-global-nav-menu-item__badge-bgd" => lambda { I18n.t("Nav Badge") },
51
+ "ic-brand-global-nav-menu-item__badge-text" => lambda { I18n.t("Nav Badge Text") },
52
+ "ic-brand-global-nav-logo-bgd" => lambda { I18n.t("Nav Logo Background") },
53
+ "ic-brand-header-image" => lambda { I18n.t("Nav Logo") },
54
+ "ic-brand-mobile-global-nav-logo" => lambda { I18n.t("Responsive Global Nav Logo") },
55
+ "ic-brand-watermark" => lambda { I18n.t("Watermark") },
56
+ "ic-brand-watermark-opacity" => lambda { I18n.t("Watermark Opacity") },
57
+ "ic-brand-favicon" => lambda { I18n.t("Favicon") },
58
+ "ic-brand-apple-touch-icon" => lambda { I18n.t("Mobile Homescreen Icon") },
59
+ "ic-brand-msapplication-tile-color" => lambda { I18n.t("Windows Tile Color") },
60
+ "ic-brand-msapplication-tile-square" => lambda { I18n.t("Windows Tile: Square") },
61
+ "ic-brand-msapplication-tile-wide" => lambda { I18n.t("Windows Tile: Wide") },
62
+ "ic-brand-right-sidebar-logo" => lambda { I18n.t("Right Sidebar Logo") },
63
+ "ic-brand-Login-body-bgd-color" => lambda { I18n.t("Background Color") },
64
+ "ic-brand-Login-body-bgd-image" => lambda { I18n.t("Background Image") },
65
+ "ic-brand-Login-body-bgd-shadow-color" => lambda { I18n.t("Body Shadow") },
66
+ "ic-brand-Login-logo" => lambda { I18n.t("Login Logo") },
67
+ "ic-brand-Login-Content-bgd-color" => lambda { I18n.t("Top Box Background") },
68
+ "ic-brand-Login-Content-border-color" => lambda { I18n.t("Top Box Border") },
69
+ "ic-brand-Login-Content-inner-bgd" => lambda { I18n.t("Inner Box Background") },
70
+ "ic-brand-Login-Content-inner-border" => lambda { I18n.t("Inner Box Border") },
71
+ "ic-brand-Login-Content-inner-body-bgd" => lambda { I18n.t("Form Background") },
72
+ "ic-brand-Login-Content-inner-body-border" => lambda { I18n.t("Form Border") },
73
+ "ic-brand-Login-Content-label-text-color" => lambda { I18n.t("Login Label") },
74
+ "ic-brand-Login-Content-password-text-color" => lambda { I18n.t("Login Link Color") },
75
+ "ic-brand-Login-footer-link-color" => lambda { I18n.t("Login Footer Link") },
76
+ "ic-brand-Login-footer-link-color-hover" => lambda { I18n.t("Login Footer Link Hover") },
77
+ "ic-brand-Login-instructure-logo" => lambda { I18n.t("Login Instructure Logo") }
78
+ }.freeze
79
+
80
+ GROUP_NAMES = {
81
+ "global_branding" => lambda { I18n.t("Global Branding") },
82
+ "global_navigation" => lambda { I18n.t("Global Navigation") },
83
+ "watermarks" => lambda { I18n.t("Watermarks & Other Images") },
84
+ "login" => lambda { I18n.t("Login Screen") }
85
+ }.freeze
86
+
87
+ HELPER_TEXTS = {
88
+ "ic-brand-header-image" => lambda { I18n.t("Accepted formats: svg, png, jpg, gif") },
89
+ "ic-brand-mobile-global-nav-logo" => lambda { I18n.t("Appears at the top of the global navigation tray that opens on mobile sized screens. display height: 48px. Accepted formats: svg, png, jpg, gif") },
90
+ "ic-brand-watermark" => lambda { I18n.t("This image appears as a background watermark to your page. Accepted formats: png, svg, gif, jpeg") },
91
+ "ic-brand-watermark-opacity" => lambda { I18n.t("Specify the transparency of the watermark background image.") },
92
+ "ic-brand-favicon" => lambda { I18n.t("You can use a single 16x16, 32x32, 48x48 ico file.") },
93
+ "ic-brand-apple-touch-icon" => lambda { I18n.t("The shortcut icon for iOS/Android devices. 180x180 png") },
94
+ "ic-brand-msapplication-tile-square" => lambda { I18n.t("558x558 png, jpg, gif (1.8x the standard tile size, so it can be scaled up or down as needed)") },
95
+ "ic-brand-msapplication-tile-wide" => lambda { I18n.t("558x270 png, jpg, gif") },
96
+ "ic-brand-right-sidebar-logo" => lambda { I18n.t("A full-size logo that appears in the right sidebar on the Canvas dashboard. Ideal size is 360 x 140 pixels. Accepted formats: svg, png, jpeg, gif") },
97
+ "ic-brand-Login-body-bgd-shadow-color" => lambda { I18n.t("accepted formats: hex, rgba, rgb, hsl") }
98
+ }.freeze
99
+
100
+ class << self
101
+ def variables_map
102
+ @variables_map ||= BRANDABLE_VARIABLES.each_with_object({}) do |variable_group, memo|
103
+ variable_group['variables'].each { |variable| memo[variable['variable_name']] = variable }
104
+ end.freeze
105
+ end
106
+
107
+ def variables_map_with_image_urls
108
+ @variables_map_with_image_urls ||= variables_map.each_with_object({}) do |(key, config), memo|
109
+ if config['type'] == 'image'
110
+ memo[key] = config.merge('default' => ActionController::Base.helpers.image_url(config['default']))
111
+ else
112
+ memo[key] = config
113
+ end
114
+ end.freeze
115
+ end
116
+
117
+ def things_that_go_into_defaults_md5
118
+ variables_map.each_with_object({}) do |(variable_name, config), memo|
119
+ default = config['default']
120
+ if config['type'] == 'image'
121
+ # to make consistent md5s whether the cdn is enabled or not, don't include hostname in defaults
122
+ default = ActionController::Base.helpers.image_path(default, host: '')
123
+ end
124
+ memo[variable_name] = default
125
+ end.freeze
126
+ end
127
+
128
+ def migration_version
129
+ # ActiveRecord usually uses integer timestamps to generate migration versions but any integer
130
+ # will work, so we just use the result of stripping out the alphabetic characters from the md5
131
+ default_variables_md5_without_migration_check.gsub(/[a-z]/, '').to_i.freeze
132
+ end
133
+
134
+ def check_if_we_need_to_create_a_db_migration
135
+ path = ActiveRecord::Migrator.migrations_paths.first
136
+ args = [path]
137
+ args << ActiveRecord::SchemaMigration unless CANVAS_RAILS5_2
138
+ migrations = ActiveRecord::MigrationContext.new(*args).migrations
139
+ ['predeploy', 'postdeploy'].each do |pre_or_post|
140
+ migration = migrations.find { |m| m.name == MIGRATION_NAME + pre_or_post.camelize }
141
+ # they can't have the same id, so we just add 1 to the postdeploy one
142
+ expected_version = (pre_or_post == 'predeploy') ? migration_version : (migration_version + 1)
143
+ raise BrandConfigWithOutCompileAssets if expected_version == 85663486644871658581990
144
+ raise DefaultMD5NotUpToDateError unless migration && migration.version == expected_version
145
+ end
146
+ end
147
+
148
+ def skip_migration_check?
149
+ # our canvas_rspec build doesn't even run `yarn install` or `gulp rev` so since
150
+ # they are not expecting all the frontend assets to work, this check isn't useful
151
+ Rails.env.test? && !Rails.root.join('public', 'dist', 'rev-manifest.json').exist?
152
+ end
153
+
154
+ def default_variables_md5
155
+ @default_variables_md5 ||= begin
156
+ check_if_we_need_to_create_a_db_migration unless skip_migration_check?
157
+ default_variables_md5_without_migration_check
158
+ end
159
+ end
160
+
161
+ def default_variables_md5_without_migration_check
162
+ Digest::MD5.hexdigest(things_that_go_into_defaults_md5.to_json).freeze
163
+ end
164
+
165
+ def handle_urls(value, config, css_urls)
166
+ return value unless config['type'] == 'image' && css_urls
167
+ "url('#{value}')" if value.present?
168
+ end
169
+
170
+ # gets the *effective* value for a brandable variable
171
+ def brand_variable_value(variable_name, active_brand_config=nil, config_map=variables_map, css_urls=false)
172
+ config = config_map[variable_name]
173
+ explicit_value = active_brand_config && active_brand_config.get_value(variable_name).presence
174
+ return handle_urls(explicit_value, config, css_urls) if explicit_value
175
+ default = config['default']
176
+ if default && default.starts_with?('$')
177
+ if css_urls
178
+ return "var(--#{default[1..-1]})"
179
+ else
180
+ return brand_variable_value(default[1..-1], active_brand_config, config_map, css_urls)
181
+ end
182
+ end
183
+
184
+ # while in our sass, we want `url(/images/foo.png)`,
185
+ # the Rails Asset Helpers expect us to not have the '/images/', eg: <%= image_tag('foo.png') %>
186
+ default = default.sub(/^\/images\//, '') if config['type'] == 'image'
187
+ handle_urls(default, config, css_urls)
188
+ end
189
+
190
+ def computed_variables(active_brand_config=nil)
191
+ [
192
+ ['ic-brand-primary', 'darken', 5],
193
+ ['ic-brand-primary', 'darken', 10],
194
+ ['ic-brand-primary', 'darken', 15],
195
+ ['ic-brand-primary', 'lighten', 5],
196
+ ['ic-brand-primary', 'lighten', 10],
197
+ ['ic-brand-primary', 'lighten', 15],
198
+ ['ic-brand-button--primary-bgd', 'darken', 5],
199
+ ['ic-brand-button--primary-bgd', 'darken', 15],
200
+ ['ic-brand-button--secondary-bgd', 'darken', 5],
201
+ ['ic-brand-button--secondary-bgd', 'darken', 15],
202
+ ['ic-brand-font-color-dark', 'lighten', 15],
203
+ ['ic-brand-font-color-dark', 'lighten', 30],
204
+ ['ic-link-color', 'darken', 10],
205
+ ['ic-link-color', 'lighten', 10],
206
+ ].each_with_object({}) do |(variable_name, darken_or_lighten, percent), memo|
207
+ color = brand_variable_value(variable_name, active_brand_config, variables_map_with_image_urls)
208
+ computed_color = CanvasColor::Color.new(color).send(darken_or_lighten, percent/100.0)
209
+ memo["#{variable_name}-#{darken_or_lighten}ed-#{percent}"] = computed_color.to_s
210
+ end
211
+ end
212
+
213
+ def all_brand_variable_values(active_brand_config=nil, css_urls=false)
214
+ variables_map.each_with_object(computed_variables(active_brand_config)) do |(key, _), memo|
215
+ memo[key] = brand_variable_value(key, active_brand_config, variables_map_with_image_urls, css_urls)
216
+ end
217
+ end
218
+
219
+ def all_brand_variable_values_as_json(active_brand_config=nil)
220
+ all_brand_variable_values(active_brand_config).to_json
221
+ end
222
+
223
+ def all_brand_variable_values_as_js(active_brand_config=nil)
224
+ "CANVAS_ACTIVE_BRAND_VARIABLES = #{all_brand_variable_values_as_json(active_brand_config)};"
225
+ end
226
+
227
+ def all_brand_variable_values_as_css(active_brand_config=nil)
228
+ ":root {
229
+ #{all_brand_variable_values(active_brand_config, true).map{ |k, v| "--#{k}: #{v};"}.join("\n")}
230
+ }"
231
+ end
232
+
233
+ def public_brandable_css_folder
234
+ Pathname.new('public/dist/brandable_css')
235
+ end
236
+
237
+ def default_brand_folder
238
+ public_brandable_css_folder.join('default')
239
+ end
240
+
241
+ def default_brand_file(type, high_contrast=false)
242
+ default_brand_folder.join("variables#{high_contrast ? '-high_contrast' : ''}-#{default_variables_md5}.#{type}")
243
+ end
244
+
245
+ def high_contrast_overrides
246
+ Class.new do
247
+ def get_value(variable_name)
248
+ {"ic-brand-primary" => "#0770A3", "ic-link-color" => "#0073A7"}[variable_name]
249
+ end
250
+ end.new
251
+ end
252
+
253
+ def default(type, high_contrast=false)
254
+ bc = high_contrast ? high_contrast_overrides : nil
255
+ send("all_brand_variable_values_as_#{type}", bc)
256
+ end
257
+
258
+ def save_default!(type, high_contrast=false)
259
+ default_brand_folder.mkpath
260
+ default_brand_file(type, high_contrast).write(default(type, high_contrast))
261
+ move_default_to_s3_if_enabled!(type, high_contrast)
262
+ end
263
+
264
+ def save_default_files!
265
+ [true, false].each do |high_contrast|
266
+ ['js', 'css', 'json'].each { |type| save_default!(type, high_contrast) }
267
+ end
268
+ end
269
+
270
+ def move_default_to_s3_if_enabled!(type, high_contrast=false)
271
+ return unless defined?(Canvas) && Canvas::Cdn.enabled?
272
+ s3_uploader.upload_file(public_default_path(type, high_contrast))
273
+ begin
274
+ File.delete(default_brand_file(type, high_contrast))
275
+ rescue Errno::ENOENT # continue if something else deleted it in another process
276
+ end
277
+ end
278
+
279
+ def s3_uploader
280
+ @s3_uploaderer ||= Canvas::Cdn::S3Uploader.new
281
+ end
282
+
283
+ def public_default_path(type, high_contrast=false)
284
+ "dist/brandable_css/default/variables#{high_contrast ? '-high_contrast' : ''}-#{default_variables_md5}.#{type}"
285
+ end
286
+
287
+ def variants
288
+ @variants ||= CONFIG['variants'].keys.freeze
289
+ end
290
+
291
+ def brandable_variants
292
+ @brandable_variants ||= CONFIG['variants'].select{|_, v| v['brandable']}.map{ |k,_| k }.freeze
293
+ end
294
+
295
+ def combined_checksums
296
+ if defined?(ActionController) && ActionController::Base.perform_caching && defined?(@combined_checksums)
297
+ return @combined_checksums
298
+ end
299
+ file = APP_ROOT.join(CONFIG['paths']['bundles_with_deps'] + SASS_STYLE)
300
+ if file.exist?
301
+ @combined_checksums = JSON.parse(file.read).each_with_object({}) do |(k, v), memo|
302
+ memo[k] = v.symbolize_keys.slice(:combinedChecksum, :includesNoVariables)
303
+ end.freeze
304
+ elsif defined?(Rails) && Rails.env.production?
305
+ raise "#{file.expand_path} does not exist. You need to run brandable_css before you can serve css."
306
+ else
307
+ # for dev/test there might be cases where you don't want it to raise an exception
308
+ # if you haven't ran `brandable_css` and the manifest file doesn't exist yet.
309
+ # eg: you want to test a controller action and you don't care that it links
310
+ # to a css file that hasn't been created yet.
311
+ default_value = {:combinedChecksum => "Error: unknown css checksum. you need to run brandable_css"}.freeze
312
+ @combined_checksums = Hash.new(default_value).freeze
313
+ end
314
+ end
315
+
316
+ # bundle path should be something like "bundles/speedgrader" or "plugins/analytics/something"
317
+ def cache_for(bundle_path, variant)
318
+ key = ["#{bundle_path}.scss", variant].join(CONFIG['manifest_key_seperator'])
319
+ fingerprint = combined_checksums[key]
320
+ raise "Fingerprint not found. #{bundle_path} #{variant}" unless fingerprint
321
+ fingerprint
322
+ end
323
+
324
+ def all_fingerprints_for(bundle_path)
325
+ variants.each_with_object({}) do |variant, object|
326
+ object[variant] = cache_for(bundle_path, variant)
327
+ end
328
+ end
329
+ end
330
+
331
+ class BrandConfigWithOutCompileAssets < RuntimeError
332
+ def initialize
333
+ super <<-END
334
+
335
+ It looks like you are running a migration before running `rake canvas:compile_assets`
336
+ compile_assets needs to complete before running db:migrate if brand_configs have not run
337
+
338
+ run `rake canvas:compile_assets` and then try migrations again.
339
+
340
+ END
341
+ end
342
+ end
343
+
344
+ class DefaultMD5NotUpToDateError < RuntimeError
345
+ def initialize
346
+ super <<-END
347
+
348
+ Something has changed about the default variables or images used in the Theme Editor.
349
+ If you are seeing this and _you_ did not make changes to either app/stylesheets/brandable_variables.json
350
+ or one of the images it references, it probably meeans your local setup is out of date.
351
+
352
+ First, make sure you run `rake db:migrate`
353
+ and then run `./script/nuke_node.sh`
354
+
355
+ If that does not resolve the issue, it probably means you _did_ update one of those json variables
356
+ in app/stylesheets/brandable_variables.json or one of the images it references so you need to rename
357
+ the db migrations that makes sure when this change is deployed or checked out by anyone else
358
+ makes a new .css file for the css variables for each brand based on these new defaults.
359
+ To do that, run this command and then restart your rails process. (for local dev, if you want the
360
+ changes to show up in the ui, make sure you also run `rake db:migrate` afterwards).
361
+
362
+ ONLY DO THIS IF YOU REALLY DID MEAN TO MAKE A CHANGE TO THE DEFAULT BRANDING STUFF:
363
+
364
+ mv db/migrate/*_#{MIGRATION_NAME.underscore}_predeploy.rb \\
365
+ db/migrate/#{BrandableCSS.migration_version}_#{MIGRATION_NAME.underscore}_predeploy.rb \\
366
+ && \\
367
+ mv db/migrate/*_#{MIGRATION_NAME.underscore}_postdeploy.rb \\
368
+ db/migrate/#{BrandableCSS.migration_version + 1}_#{MIGRATION_NAME.underscore}_postdeploy.rb
369
+
370
+ FYI, current variables are: #{BrandableCSS.things_that_go_into_defaults_md5}
371
+ END
372
+ end
373
+ end
374
+ end