mirror of
https://github.com/LaCasemate/fab-manager.git
synced 2025-02-19 13:54:25 +01:00
customize home page css
This commit is contained in:
parent
22b6560baf
commit
656a603d6c
@ -47,6 +47,7 @@
|
||||
- [TODO DEPLOY] add the `USER_CONFIRMATION_NEEDED_TO_SIGN_IN` environment variable (see [doc/environment.md](doc/environment.md#USER_CONFIRMATION_NEEDED_TO_SIGN_IN) for configuration details)
|
||||
- [TODO DEPLOY] -> (only dev) `bundle install && yarn install`
|
||||
- [TODO DEPLOY] `rake db:migrate && rake db:seed`
|
||||
- [TODO DEPLOY] `rake fablab:fix:name_stylesheet`
|
||||
|
||||
## v4.2.4 2019 October 30
|
||||
|
||||
|
@ -20,7 +20,7 @@ angular.module('application', ['ngCookies', 'ngResource', 'ngSanitize', 'ui.rout
|
||||
'ui.select', 'ui.calendar', 'angularMoment', 'Devise', 'DeviseModal', 'angular-growl', 'xeditable',
|
||||
'checklist-model', 'unsavedChanges', 'angular-loading-bar', 'ngTouch', 'angular-google-analytics',
|
||||
'angularUtils.directives.dirDisqus', 'summernote', 'elasticsearch', 'angular-medium-editor', 'naif.base64',
|
||||
'minicolors', 'pascalprecht.translate', 'ngFitText', 'ngAside', 'ngCapsLock', 'vcRecaptcha'])
|
||||
'minicolors', 'pascalprecht.translate', 'ngFitText', 'ngAside', 'ngCapsLock', 'vcRecaptcha', 'ui.codemirror'])
|
||||
.config(['$httpProvider', 'AuthProvider', 'growlProvider', 'unsavedWarningsConfigProvider', 'AnalyticsProvider', 'uibDatepickerPopupConfig', '$provide', '$translateProvider',
|
||||
function ($httpProvider, AuthProvider, growlProvider, unsavedWarningsConfigProvider, AnalyticsProvider, uibDatepickerPopupConfig, $provide, $translateProvider) {
|
||||
// Google analytics
|
||||
|
@ -67,6 +67,11 @@
|
||||
//= require angular-aside/dist/js/angular-aside
|
||||
//= require ng-caps-lock/ng-caps-lock
|
||||
//= require angular-recaptcha
|
||||
//= require codemirror/lib/codemirror
|
||||
//= require codemirror/addon/edit/matchbrackets
|
||||
//= require codemirror/mode/css/css
|
||||
//= require codemirror/mode/sass/sass
|
||||
//= require angular-ui-codemirror/src/ui-codemirror
|
||||
//= require_tree ./controllers
|
||||
//= require_tree ./services
|
||||
//= require_tree ./directives
|
||||
|
@ -60,6 +60,7 @@ Application.Controllers.controller('SettingsController', ['$scope', '$rootScope'
|
||||
$scope.aboutContactsSetting = { name: 'about_contacts', value: settingsPromise.about_contacts };
|
||||
$scope.homeBlogpostSetting = { name: 'home_blogpost', value: settingsPromise.home_blogpost };
|
||||
$scope.homeContent = { name: 'home_content', value: settingsPromise.home_content };
|
||||
$scope.homeCss = { name: 'home_css', value: settingsPromise.home_css };
|
||||
$scope.machineExplicationsAlert = { name: 'machine_explications_alert', value: settingsPromise.machine_explications_alert };
|
||||
$scope.trainingExplicationsAlert = { name: 'training_explications_alert', value: settingsPromise.training_explications_alert };
|
||||
$scope.trainingInformationMessage = { name: 'training_information_message', value: settingsPromise.training_information_message };
|
||||
@ -151,6 +152,21 @@ Application.Controllers.controller('SettingsController', ['$scope', '$rootScope'
|
||||
]
|
||||
}
|
||||
|
||||
// codemirror editor
|
||||
$scope.codeMirrorEditor = null;
|
||||
|
||||
// Options for codemirror editor, used for custom css
|
||||
$scope.codemirrorOpts = {
|
||||
matchBrackets : true,
|
||||
lineNumbers: true,
|
||||
mode: 'sass'
|
||||
}
|
||||
|
||||
// Show or hide advanced settings
|
||||
$scope.advancedSettings = {
|
||||
open: false
|
||||
}
|
||||
|
||||
/**
|
||||
* For use with 'ng-class', returns the CSS class name for the uploads previews.
|
||||
* The preview may show a placeholder or the content of the file depending on the upload state.
|
||||
@ -332,6 +348,14 @@ Application.Controllers.controller('SettingsController', ['$scope', '$rootScope'
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback triggered when the codemirror editor is loaded into the DOM
|
||||
* @param editor codemirror instance
|
||||
*/
|
||||
$scope.codemirrorLoaded = function (editor) {
|
||||
$scope.codeMirrorEditor = editor;
|
||||
}
|
||||
|
||||
/* PRIVATE SCOPE */
|
||||
|
||||
/**
|
||||
@ -377,6 +401,11 @@ Application.Controllers.controller('SettingsController', ['$scope', '$rootScope'
|
||||
privacyDraftsPromise.setting.history.forEach(function (draft) {
|
||||
$scope.privacyDraftsHistory.push({ id: draft.id, name: _t('app.admin.settings.privacy.draft_from_USER_DATE', { USER: draft.user.name, DATE: moment(draft.created_at).format('L LT') }), content: draft.value });
|
||||
});
|
||||
|
||||
// refresh codemirror to display the fetched setting
|
||||
$scope.$watch('advancedSettings.open', function (newValue) {
|
||||
if (newValue) $scope.codeMirrorEditor.refresh();
|
||||
})
|
||||
};
|
||||
|
||||
// init the controller (call at the end !)
|
||||
|
@ -1002,7 +1002,7 @@ angular.module('application.router', ['ui.router'])
|
||||
'fablab_name', 'name_genre', 'reminder_enable', \
|
||||
'reminder_delay', 'visibility_yearly', 'visibility_others', \
|
||||
'display_name_enable', 'machines_sort_by', 'fab_analytics', \
|
||||
'link_name', 'home_content']` }).$promise;
|
||||
'link_name', 'home_content', 'home_css']` }).$promise;
|
||||
}],
|
||||
privacyDraftsPromise: ['Setting', function (Setting) { return Setting.get({ name: 'privacy_draft', history: true }).$promise; }],
|
||||
cguFile: ['CustomAsset', function (CustomAsset) { return CustomAsset.get({ name: 'cgu-file' }).$promise; }],
|
||||
|
@ -14,6 +14,7 @@
|
||||
*= require summernote/dist/summernote
|
||||
*= require jquery-minicolors/jquery.minicolors.css
|
||||
*= require angular-aside/dist/css/angular-aside
|
||||
*= require codemirror/lib/codemirror
|
||||
*/
|
||||
|
||||
@import "app.functions";
|
||||
|
@ -28,4 +28,4 @@ li.abuse {
|
||||
margin-top: 1em;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -40,5 +40,24 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
.home-page-style {
|
||||
.panel {
|
||||
border: 0;
|
||||
.panel-heading {
|
||||
background: none;
|
||||
|
||||
.panel-title {
|
||||
font-size: 12px;
|
||||
font-style: italic;
|
||||
}
|
||||
}
|
||||
|
||||
.CodeMirror {
|
||||
border: 1px solid #ddd;
|
||||
font-size: 12px;
|
||||
height: 400px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -36,5 +36,27 @@
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row m-t-lg home-page-style">
|
||||
<uib-accordion>
|
||||
<uib-accordion-group is-open="advancedSettings.open">
|
||||
<uib-accordion-heading>
|
||||
<span translate>{{ 'app.admin.settings.advanced' }}</span> <i class="pull-right glyphicon" ng-class="{'glyphicon-chevron-down': advancedSettings.open, 'glyphicon-chevron-right': !advancedSettings.open}"></i>
|
||||
</uib-accordion-heading>
|
||||
<div class="col-md-12 ">
|
||||
<h4 translate>{{ 'app.admin.settings.customize_home_page_css' }}</h4>
|
||||
<div ui-codemirror="{ onLoad: codemirrorLoaded }" ng-model="homeCss.value"
|
||||
ui-codemirror-opts="codemirrorOpts">
|
||||
</div>
|
||||
<uib-alert type="info m">
|
||||
<p class="text-sm font-bold">
|
||||
<i class="fa fa-lightbulb-o"></i>
|
||||
<span translate>{{ 'app.admin.settings.home_css_notice' }}</span>
|
||||
</p>
|
||||
</uib-alert>
|
||||
<button name="button" class="btn btn-warning" ng-click="save(homeCss)" translate>{{ 'app.shared.buttons.save' }}</button>
|
||||
</div>
|
||||
</uib-accordion-group>
|
||||
</uib-accordion>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -12,6 +12,7 @@ class API::SettingsController < API::ApiController
|
||||
authorize Setting
|
||||
@setting = Setting.find_or_initialize_by(name: params[:name])
|
||||
if @setting.save && @setting.history_values.create(value: setting_params[:value], invoicing_profile: current_user.invoicing_profile)
|
||||
SettingService.new.after_update(@setting)
|
||||
render status: :ok
|
||||
else
|
||||
render json: @setting.errors.full_messages, status: :unprocessable_entity
|
||||
|
@ -1,3 +1,6 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Admin defined assets (like PDF or images uploaded)
|
||||
class CustomAsset < ActiveRecord::Base
|
||||
has_one :custom_asset_file, as: :viewable, dependent: :destroy
|
||||
accepts_nested_attributes_for :custom_asset_file, allow_destroy: true
|
||||
@ -11,6 +14,6 @@ class CustomAsset < ActiveRecord::Base
|
||||
after_update :update_stylesheet if :viewable_changed?
|
||||
|
||||
def update_stylesheet
|
||||
Stylesheet.first.rebuild! if %w[profile-image-file].include? name
|
||||
Stylesheet.theme.rebuild! if %w[profile-image-file].include? name
|
||||
end
|
||||
end
|
||||
|
@ -3,6 +3,7 @@
|
||||
# Setting is a configuration element of the platform. Only administrators are allowed to modify Settings
|
||||
# For some settings, changing them will involve some callback actions (like rebuilding the stylesheets if the theme color Setting is changed).
|
||||
# A full history of the previous values is kept in database with the date and the author of the change
|
||||
# after_update callback is handled by SettingService
|
||||
class Setting < ActiveRecord::Base
|
||||
has_many :history_values
|
||||
validates :name, inclusion:
|
||||
@ -71,20 +72,8 @@ class Setting < ActiveRecord::Base
|
||||
hub_public_key
|
||||
fab_analytics
|
||||
link_name
|
||||
home_content] }
|
||||
|
||||
after_update :update_stylesheet, :notify_privacy_policy_changed if :value_changed?
|
||||
|
||||
def update_stylesheet
|
||||
Stylesheet.first&.rebuild! if %w[main_color secondary_color].include? name
|
||||
end
|
||||
|
||||
def notify_privacy_policy_changed
|
||||
return unless name == 'privacy_body'
|
||||
|
||||
NotifyPrivacyUpdateWorker.perform_async(id)
|
||||
end
|
||||
|
||||
home_content
|
||||
home_css] }
|
||||
def value
|
||||
last_value = history_values.order(HistoryValue.arel_table['created_at'].desc).first
|
||||
last_value&.value
|
||||
|
@ -6,19 +6,23 @@
|
||||
class Stylesheet < ActiveRecord::Base
|
||||
validates_presence_of :contents
|
||||
|
||||
def rebuild!
|
||||
return unless Stylesheet.primary && Stylesheet.secondary
|
||||
## ===== THEME =====
|
||||
|
||||
update(contents: Stylesheet.css)
|
||||
def rebuild!
|
||||
if Stylesheet.primary && Stylesheet.secondary && name == 'theme'
|
||||
update(contents: Stylesheet.css)
|
||||
elsif name == 'home_page'
|
||||
update(contents: Stylesheet.home_page_css)
|
||||
end
|
||||
end
|
||||
|
||||
def self.build_sheet!
|
||||
return unless Stylesheet.primary && Stylesheet.secondary
|
||||
|
||||
if Stylesheet.first
|
||||
Stylesheet.first.rebuild!
|
||||
if Stylesheet.theme
|
||||
Stylesheet.theme.rebuild!
|
||||
else
|
||||
Stylesheet.create!(contents: Stylesheet.css)
|
||||
Stylesheet.create!(contents: Stylesheet.css, name: 'theme')
|
||||
end
|
||||
end
|
||||
|
||||
@ -50,6 +54,10 @@ class Stylesheet < ActiveRecord::Base
|
||||
Stylesheet.primary.paint.to_rgb.insert(3, 'a').insert(-2, ", #{alpha}")
|
||||
end
|
||||
|
||||
def self.theme
|
||||
Stylesheet.find_by(name: 'theme')
|
||||
end
|
||||
|
||||
def self.css # rubocop:disable Metrics/AbcSize
|
||||
<<~CSS
|
||||
.bg-red { background-color: #{Stylesheet.primary}; }
|
||||
@ -91,4 +99,28 @@ class Stylesheet < ActiveRecord::Base
|
||||
section#cookies-modal div.cookies-consent .cookies-actions button.accept { background-color: #{Stylesheet.secondary}; }
|
||||
CSS
|
||||
end
|
||||
|
||||
## ===== HOME PAGE =====
|
||||
|
||||
def self.home_style
|
||||
style = Setting.find_by(name: 'home_css')&.value
|
||||
".home-page { #{style} }"
|
||||
end
|
||||
|
||||
def self.build_home!
|
||||
if Stylesheet.home_page
|
||||
Stylesheet.home_page.rebuild!
|
||||
else
|
||||
Stylesheet.create!(contents: Stylesheet.home_page_css, name: 'home_page')
|
||||
end
|
||||
end
|
||||
|
||||
def self.home_page
|
||||
Stylesheet.find_by(name: 'home_page')
|
||||
end
|
||||
|
||||
def self.home_page_css
|
||||
engine = Sass::Engine.new(home_style, syntax: :scss)
|
||||
engine.render.presence || '.home-page {}'
|
||||
end
|
||||
end
|
||||
|
15
app/services/setting_service.rb
Normal file
15
app/services/setting_service.rb
Normal file
@ -0,0 +1,15 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Settings are saved in two database tables: Settings and HistoryValues.
|
||||
# Due to the way the controller updates the settings, we cannot safely use ActiveRecord's callbacks (eg. after_update, after_commit...)
|
||||
# so this service provides a wrapper around these operations.
|
||||
class SettingService
|
||||
def after_update(setting)
|
||||
# update the stylesheet
|
||||
Stylesheet.theme&.rebuild! if %w[main_color secondary_color].include? setting.name
|
||||
Stylesheet.home_page&.rebuild! if setting.name == 'home_css'
|
||||
|
||||
# notify about a change in privacy policy
|
||||
NotifyPrivacyUpdateWorker.perform_async(id) if setting.name == 'privacy_body'
|
||||
end
|
||||
end
|
@ -1,9 +1,10 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Build a cached version of the CSS stylesheet
|
||||
class StylesheetSweeper < ActionController::Caching::Sweeper
|
||||
observe Stylesheet
|
||||
|
||||
def after_update(record)
|
||||
if record.contents_changed?
|
||||
expire_page(:controller => 'stylesheets', action: 'show', id: record.id)
|
||||
end
|
||||
expire_page(controller: 'stylesheets', action: 'show', id: record.id) if record.contents_changed?
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -65,9 +65,12 @@
|
||||
|
||||
<%= stylesheet_link_tag 'application', media: 'all' %>
|
||||
<%= stylesheet_link_tag 'app.printer', media: 'print' %>
|
||||
<% if !Stylesheet.first.nil? %>
|
||||
<link rel="stylesheet" media="all" href="<%= stylesheet_path(Stylesheet.first.id) %>-<%=Stylesheet.first.updated_at.to_i.to_s%>.css" />
|
||||
<% end %>
|
||||
<% unless Stylesheet.theme.nil? %>
|
||||
<link rel="stylesheet" media="all" href="<%= stylesheet_path(Stylesheet.theme.id) %>-<%= Stylesheet.theme.updated_at.to_i.to_s %>.css" />
|
||||
<% end %>
|
||||
<% unless Stylesheet.home_page.nil? %>
|
||||
<link rel="stylesheet" media="all" href="<%= stylesheet_path(Stylesheet.home_page.id) %>-<%= Stylesheet.home_page.updated_at.to_i.to_s %>.css" />
|
||||
<% end %>
|
||||
<base href="/"></base>
|
||||
|
||||
<% if CustomAsset.get_url('favicon-file') %>
|
||||
|
@ -1,3 +1,6 @@
|
||||
# For development & test environments, copy this file to database.yml
|
||||
# For staging & production environments, use docker/database.yml
|
||||
|
||||
development:
|
||||
adapter: postgresql
|
||||
host: localhost
|
||||
@ -18,22 +21,3 @@ test:
|
||||
pool: 25
|
||||
username: postgres
|
||||
password: postgres
|
||||
|
||||
staging:
|
||||
adapter: postgresql
|
||||
host: localhost
|
||||
encoding: unicode
|
||||
database: fabmanager_development
|
||||
pool: 25
|
||||
username: postgres
|
||||
password: postgres
|
||||
|
||||
|
||||
production:
|
||||
adapter: postgresql
|
||||
host: localhost
|
||||
encoding: unicode
|
||||
database: fabmanager_development
|
||||
pool: 25
|
||||
username: postgres
|
||||
password: postgres
|
||||
|
@ -940,6 +940,7 @@ fr:
|
||||
item_events: "Prochains événements"
|
||||
home_content: "la page d'accueil"
|
||||
home_content_reset: "La page d'accueil a bien été restaurée dans sa configuration initiale."
|
||||
home_css: "la feuille de style de la page d'accueil"
|
||||
home_blogpost: "la brève de la page d'accueil"
|
||||
twitter_name: "nom du flux Twitter"
|
||||
link_name: "l'intitulé du lien vers la page \"À propos\""
|
||||
@ -969,6 +970,9 @@ fr:
|
||||
elements_ordering: "Ordre d'affichage des éléments"
|
||||
machines_order: "Ordre des machines"
|
||||
display_machines_sorted_by: "Afficher les machines triées par"
|
||||
advanced: "Paramètres avancés"
|
||||
customize_home_page_css: "Personnaliser la feuille de style de la page d'accueil"
|
||||
home_css_notice: "Vous pouvez utiliser la syntaxe SASS. Pas de garantie que ça va pas péter avec les updates"
|
||||
sort_by:
|
||||
default: "Défaut"
|
||||
name: "Nom"
|
||||
|
5
db/migrate/20200127111404_add_name_to_stylesheet.rb
Normal file
5
db/migrate/20200127111404_add_name_to_stylesheet.rb
Normal file
@ -0,0 +1,5 @@
|
||||
class AddNameToStylesheet < ActiveRecord::Migration
|
||||
def change
|
||||
add_column :stylesheets, :name, :string
|
||||
end
|
||||
end
|
@ -11,7 +11,7 @@
|
||||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema.define(version: 20191202135507) do
|
||||
ActiveRecord::Schema.define(version: 20200127111404) do
|
||||
|
||||
# These are extensions that must be enabled in order to support this database
|
||||
enable_extension "plpgsql"
|
||||
@ -760,6 +760,7 @@ ActiveRecord::Schema.define(version: 20191202135507) do
|
||||
t.text "contents"
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.string "name"
|
||||
end
|
||||
|
||||
create_table "subscriptions", force: :cascade do |t|
|
||||
|
@ -427,6 +427,7 @@ unless Setting.find_by(name: 'secondary_color').try(:value)
|
||||
end
|
||||
|
||||
Stylesheet.build_sheet!
|
||||
Stylesheet.build_home!
|
||||
|
||||
unless Setting.find_by(name: 'training_information_message').try(:value)
|
||||
setting = Setting.find_or_initialize_by(name: 'training_information_message')
|
||||
|
@ -1,3 +1,6 @@
|
||||
# For staging & production environments, copy this file to database.yml
|
||||
# For development & test environments, you can use this file OR config/database.yml.default
|
||||
|
||||
default: &default
|
||||
adapter: postgresql
|
||||
encoding: unicode
|
||||
|
@ -143,5 +143,12 @@ namespace :fablab do
|
||||
attached_object: u
|
||||
end
|
||||
end
|
||||
|
||||
desc '[release 4.3.0] add name to theme stylesheet'
|
||||
task name_stylesheet: :environment do
|
||||
Stylesheet.order(:created_at).first.update_attributes(
|
||||
name: 'theme'
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -51,10 +51,12 @@
|
||||
"angular-translate-loader-partial": "2.18",
|
||||
"angular-ui-bootstrap": "0.14.3",
|
||||
"angular-ui-calendar": "https://github.com/angular-ui/ui-calendar.git#1.0.1",
|
||||
"angular-ui-codemirror": "^0.3.0",
|
||||
"angular-unsavedchanges": "0.2",
|
||||
"angular-xeditable": "0.10",
|
||||
"bootstrap-switch": "3.3.2",
|
||||
"checklist-model": "0.2",
|
||||
"codemirror": "^4.8.0",
|
||||
"d3": "3.5",
|
||||
"elasticsearch-browser": "3.1",
|
||||
"font-awesome": "4.3.0",
|
||||
|
10
yarn.lock
10
yarn.lock
@ -182,6 +182,11 @@ angular-ui-bootstrap@0.14.3:
|
||||
version "1.0.1"
|
||||
resolved "https://github.com/angular-ui/ui-calendar.git#f0ab8e186da6b946eafa12ab4154578c1fdf1e1d"
|
||||
|
||||
angular-ui-codemirror@^0.3.0:
|
||||
version "0.3.0"
|
||||
resolved "https://registry.yarnpkg.com/angular-ui-codemirror/-/angular-ui-codemirror-0.3.0.tgz#e4286fc50f393f2a6e697c1bf3170424e12cbb60"
|
||||
integrity sha1-5ChvxQ85PypuaXwb8xcEJOEsu2A=
|
||||
|
||||
angular-unsavedchanges@0.2:
|
||||
version "0.2.5"
|
||||
resolved "https://registry.yarnpkg.com/angular-unsavedchanges/-/angular-unsavedchanges-0.2.5.tgz#34be961d547051f2c6d536c60a5b42f565c8858b"
|
||||
@ -301,6 +306,11 @@ cli-width@^2.0.0:
|
||||
resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.0.tgz#ff19ede8a9a5e579324147b0c11f0fbcbabed639"
|
||||
integrity sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=
|
||||
|
||||
codemirror@^4.8.0:
|
||||
version "4.13.0"
|
||||
resolved "https://registry.yarnpkg.com/codemirror/-/codemirror-4.13.0.tgz#209772d38a7bb99647c37b500db121110dd9af6f"
|
||||
integrity sha1-IJdy04p7uZZHw3tQDbEhEQ3Zr28=
|
||||
|
||||
coffee-script@~1.7.0:
|
||||
version "1.7.1"
|
||||
resolved "https://registry.yarnpkg.com/coffee-script/-/coffee-script-1.7.1.tgz#62996a861780c75e6d5069d13822723b73404bfc"
|
||||
|
Loading…
x
Reference in New Issue
Block a user