biola_wcms_components 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +4 -0
- data/.ruby-version +1 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +74 -0
- data/Rakefile +1 -0
- data/app/assets/images/.gitkeep +0 -0
- data/app/assets/javascripts/biola-wcms-components.js.coffee +15 -0
- data/app/assets/javascripts/components/forms/json_editor.js.coffee +20 -0
- data/app/assets/javascripts/components/forms/person_lookup.js.coffee +29 -0
- data/app/assets/javascripts/components/forms/presentation_data_editor.js.coffee +69 -0
- data/app/assets/javascripts/components/forms/yaml_editor.js.coffee +39 -0
- data/app/assets/javascripts/configuration/file_uploader.js.coffee +55 -0
- data/app/assets/javascripts/configuration/setup_redactor.js.coffee +65 -0
- data/app/assets/stylesheets/_mixins.scss +43 -0
- data/app/assets/stylesheets/_settings.scss +1 -0
- data/app/assets/stylesheets/biola-wcms-components.scss +11 -0
- data/app/assets/stylesheets/components/alerts/_message_list.scss +3 -0
- data/app/assets/stylesheets/components/forms/_person_lookup.scss +72 -0
- data/app/assets/stylesheets/components/forms/_presentation_data_editor.scss +38 -0
- data/app/assets/stylesheets/components/navigation/_site_nav.scss +24 -0
- data/app/helpers/wcms_components/alerts_helper.rb +41 -0
- data/app/helpers/wcms_components/component_helper.rb +9 -0
- data/app/helpers/wcms_components/navigation_helper.rb +32 -0
- data/app/views/wcms_components/alerts/_message_list.html.slim +8 -0
- data/app/views/wcms_components/forms/_json_editor.html.slim +13 -0
- data/app/views/wcms_components/forms/_person_lookup.html.slim +12 -0
- data/app/views/wcms_components/forms/_presentation_data_editor.html.slim +36 -0
- data/app/views/wcms_components/forms/_redactor_editor.html.slim +32 -0
- data/app/views/wcms_components/forms/_text_area.html.slim +13 -0
- data/app/views/wcms_components/forms/_yaml_editor.html.slim +16 -0
- data/app/views/wcms_components/navigation/_page_nav.html.slim +33 -0
- data/app/views/wcms_components/navigation/_site_nav.html.slim +38 -0
- data/app/views/wcms_components/shared/.gitkeep +0 -0
- data/app/views/wcms_components/shared/_embedded_image_uploader.html.slim +6 -0
- data/biola_wcms_components.gemspec +27 -0
- data/config/locales/en.yml +4 -0
- data/lib/biola_wcms_components.rb +22 -0
- data/lib/biola_wcms_components/configuration.rb +9 -0
- data/lib/biola_wcms_components/engine.rb +6 -0
- data/lib/biola_wcms_components/version.rb +3 -0
- data/lib/components/menu_block.rb +130 -0
- data/lib/components/presentation_data_editor.rb +199 -0
- data/vendor/assets/javascripts/handlebars.js +3750 -0
- data/vendor/assets/javascripts/redactor.js +8312 -0
- data/vendor/assets/javascripts/redactor_fullscreen.js +123 -0
- data/vendor/assets/javascripts/typeahead.js +11 -0
- data/vendor/assets/stylesheets/redactor.css +924 -0
- metadata +176 -0
@@ -0,0 +1 @@
|
|
1
|
+
$color-biola-red: #CC1122;
|
@@ -0,0 +1,11 @@
|
|
1
|
+
// External files
|
2
|
+
@import "../../../vendor/assets/stylesheets/redactor";
|
3
|
+
|
4
|
+
// Mixins and settings
|
5
|
+
@import "./settings";
|
6
|
+
@import "./mixins";
|
7
|
+
|
8
|
+
// Components
|
9
|
+
@import "./components/alerts/*";
|
10
|
+
@import "./components/forms/*";
|
11
|
+
@import "./components/navigation/*";
|
@@ -0,0 +1,72 @@
|
|
1
|
+
.person-lookup {
|
2
|
+
.typeahead, .tt-query, .tt-hint {
|
3
|
+
width: 396px;
|
4
|
+
height: 34px;
|
5
|
+
padding: 8px 12px;
|
6
|
+
font-size: 15px;
|
7
|
+
line-height: 15px;
|
8
|
+
border: 1px solid #ccc;
|
9
|
+
-webkit-border-radius: 4px;
|
10
|
+
-moz-border-radius: 4px;
|
11
|
+
border-radius: 4px;
|
12
|
+
outline: none;
|
13
|
+
}
|
14
|
+
|
15
|
+
.tt-query {
|
16
|
+
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
|
17
|
+
-moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
|
18
|
+
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
|
19
|
+
}
|
20
|
+
|
21
|
+
.tt-hint {
|
22
|
+
color: #999
|
23
|
+
}
|
24
|
+
|
25
|
+
.tt-dropdown-menu {
|
26
|
+
text-align: left;
|
27
|
+
width: 422px;
|
28
|
+
margin-top: 12px;
|
29
|
+
padding: 8px 0;
|
30
|
+
background-color: #fff;
|
31
|
+
border: 1px solid #ccc;
|
32
|
+
border: 1px solid rgba(0, 0, 0, 0.2);
|
33
|
+
-webkit-border-radius: 4px;
|
34
|
+
-moz-border-radius: 4px;
|
35
|
+
border-radius: 4px;
|
36
|
+
-webkit-box-shadow: 0 5px 10px rgba(0,0,0,.2);
|
37
|
+
-moz-box-shadow: 0 5px 10px rgba(0,0,0,.2);
|
38
|
+
box-shadow: 0 5px 10px rgba(0,0,0,.2);
|
39
|
+
}
|
40
|
+
|
41
|
+
.tt-suggestion, .tt-empty-message {
|
42
|
+
padding: 3px 20px;
|
43
|
+
font-size: 16px;
|
44
|
+
line-height: 24px;
|
45
|
+
|
46
|
+
.affiliations {
|
47
|
+
color: #999;
|
48
|
+
font-size: 12px;
|
49
|
+
}
|
50
|
+
.image {
|
51
|
+
height: 30px;
|
52
|
+
width: 26px;
|
53
|
+
margin-right: 10px;
|
54
|
+
margin-left: -5px;
|
55
|
+
display: inline-block;
|
56
|
+
vertical-align: middle;
|
57
|
+
background-size: cover;
|
58
|
+
background-position: center center;
|
59
|
+
background-color: #ddd;
|
60
|
+
}
|
61
|
+
}
|
62
|
+
|
63
|
+
.tt-suggestion.tt-cursor {
|
64
|
+
cursor: pointer;
|
65
|
+
color: #fff;
|
66
|
+
background-color: #0097cf;
|
67
|
+
|
68
|
+
.affiliations {
|
69
|
+
color: #fff;
|
70
|
+
}
|
71
|
+
}
|
72
|
+
}
|
@@ -0,0 +1,38 @@
|
|
1
|
+
#presentation_data_editor {
|
2
|
+
margin-bottom: 20px;
|
3
|
+
|
4
|
+
> .nest .nest {
|
5
|
+
border-top: 1px solid #ddd;
|
6
|
+
border-left: 1px solid #ddd;
|
7
|
+
padding: 20px 0 0 20px;
|
8
|
+
|
9
|
+
h4 {
|
10
|
+
margin: 30px 0 5px 0;
|
11
|
+
}
|
12
|
+
}
|
13
|
+
|
14
|
+
// Wraps Array name and items
|
15
|
+
.array_wrapper {
|
16
|
+
margin-bottom: 20px;
|
17
|
+
}
|
18
|
+
|
19
|
+
.array_item {
|
20
|
+
border-bottom: 1px solid #ddd;
|
21
|
+
padding-bottom: 20px;
|
22
|
+
margin-bottom: 20px;
|
23
|
+
|
24
|
+
// Make the border touch the nested border
|
25
|
+
margin-left: -20px;
|
26
|
+
padding-left: 20px;
|
27
|
+
|
28
|
+
// Framework item doesn't get saved
|
29
|
+
// it is just there for building out new items
|
30
|
+
&.framework {
|
31
|
+
display: none;
|
32
|
+
}
|
33
|
+
}
|
34
|
+
|
35
|
+
.drop-image-uploader {
|
36
|
+
&.dragging { background-color: #deedff; }
|
37
|
+
}
|
38
|
+
}
|
@@ -0,0 +1,24 @@
|
|
1
|
+
#wcms_site_navigation {
|
2
|
+
background-color: #f3f3f3;
|
3
|
+
border-bottom: 1px solid #ddd;
|
4
|
+
|
5
|
+
a.top-nav-link {
|
6
|
+
display: inline-block;
|
7
|
+
padding: 10px 0 10px;
|
8
|
+
color: inherit;
|
9
|
+
}
|
10
|
+
.fa-angle-right {
|
11
|
+
padding: 0 10px;
|
12
|
+
}
|
13
|
+
|
14
|
+
.custom-dropdown {
|
15
|
+
.dropdown-toggle {
|
16
|
+
color: inherit;
|
17
|
+
}
|
18
|
+
.dropdown-menu > .active > a {
|
19
|
+
background: none;
|
20
|
+
color: black;
|
21
|
+
font-weight: bold;
|
22
|
+
}
|
23
|
+
}
|
24
|
+
}
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module WcmsComponents
|
2
|
+
module AlertsHelper
|
3
|
+
def all_alerts
|
4
|
+
flash_alerts + model_alerts
|
5
|
+
end
|
6
|
+
|
7
|
+
def flash_alerts
|
8
|
+
flash.map { |key,val| {type: key, message: val} }
|
9
|
+
end
|
10
|
+
|
11
|
+
def model_alerts(model = nil)
|
12
|
+
model ||= instance_variable_get("@#{controller_name.singularize}")
|
13
|
+
|
14
|
+
return [] if model.nil?
|
15
|
+
|
16
|
+
model.errors.full_messages.map { |msg| {type: :error, message: msg} }
|
17
|
+
end
|
18
|
+
|
19
|
+
def alert_icon(type)
|
20
|
+
case type.to_s.to_sym
|
21
|
+
when :error, :alert
|
22
|
+
'fa fa-exclamation-circle'
|
23
|
+
when :warn, :warning
|
24
|
+
'fa fa-warning'
|
25
|
+
else
|
26
|
+
'fa fa-info-circle'
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def alert_class(type)
|
31
|
+
case type.to_s.to_sym
|
32
|
+
when :error, :alert
|
33
|
+
'alert-danger'
|
34
|
+
when :warn, :warning
|
35
|
+
'alert-warning' # default yellow
|
36
|
+
else
|
37
|
+
'alert-info'
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module WcmsComponents
|
2
|
+
module NavigationHelper
|
3
|
+
|
4
|
+
# Set class on active navigation items
|
5
|
+
def nav_link(text, path_helper)
|
6
|
+
# The router can't match against a relative root in the path
|
7
|
+
# so we need to strip it out with script_name: nil.
|
8
|
+
router_path = send(path_helper, script_name: nil)
|
9
|
+
real_path = send(path_helper)
|
10
|
+
|
11
|
+
recognized = ::Rails.application.routes.recognize_path(router_path)
|
12
|
+
|
13
|
+
if policy(recognized[:controller].classify.constantize).index? # make sure user has permission to index this controller
|
14
|
+
if recognized[:controller] == params[:controller] # && recognized[:action] == params[:action]
|
15
|
+
content_tag(:li, :class => "descendant active") do
|
16
|
+
link_to( text, real_path)
|
17
|
+
end
|
18
|
+
else
|
19
|
+
content_tag(:li, :class => "descendant") do
|
20
|
+
link_to( text, real_path)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# Gets used by the side menu / page navigation
|
27
|
+
def menu_block(html_options = {}, &block)
|
28
|
+
MenuBlock.new(self, html_options).render(&block)
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
ruby:
|
2
|
+
form ||= nil
|
3
|
+
attribute ||= nil
|
4
|
+
html_class ||= 'form-control json_editor'
|
5
|
+
value ||= nil
|
6
|
+
embedded_image_url ||= nil # should be the post url for creating new embedded images
|
7
|
+
|
8
|
+
|
9
|
+
= wcms_component("shared/embedded_image_uploader", embedded_image_url: embedded_image_url)
|
10
|
+
|
11
|
+
.ace_json_editor
|
12
|
+
= wcms_component("forms/text_area", {form: form, attribute: attribute, html_class: html_class, value: value})
|
13
|
+
|
@@ -0,0 +1,12 @@
|
|
1
|
+
ruby:
|
2
|
+
lookup_url ||= nil
|
3
|
+
|
4
|
+
- if lookup_url
|
5
|
+
.person-lookup data-lookup-url=lookup_url
|
6
|
+
= hidden_field_tag :person_id, '', class: 'hidden-person-id'
|
7
|
+
= text_field_tag :person_name, '', placeholder: 'First or last name', required: true, class: 'form-control typeahead'
|
8
|
+
|
9
|
+
- else
|
10
|
+
p
|
11
|
+
' Please specify
|
12
|
+
code = ":lookup_url"
|
@@ -0,0 +1,36 @@
|
|
1
|
+
/ Example:
|
2
|
+
/ = wcms_component "forms/presentation_data_editor",
|
3
|
+
/ schema: @generic_object.presentation_data_template.schema,
|
4
|
+
/ data: @generic_object.presentation_data,
|
5
|
+
/ form: f,
|
6
|
+
/ embedded_image_url: create_embedded_images_url
|
7
|
+
/
|
8
|
+
|
9
|
+
ruby:
|
10
|
+
schema ||= []
|
11
|
+
presentation_data ||= {}
|
12
|
+
form ||= nil
|
13
|
+
embedded_image_url ||= nil # should be the post url for creating new embedded images
|
14
|
+
|
15
|
+
= wcms_component "shared/embedded_image_uploader",
|
16
|
+
embedded_image_url: embedded_image_url
|
17
|
+
|
18
|
+
|
19
|
+
/ Link to toggle between simple and advanced editors
|
20
|
+
- if current_user.try(:admin?)
|
21
|
+
- if params[:edit_raw_data] == 'true'
|
22
|
+
= link_to('Simple Editor', params.merge(edit_raw_data: nil), class: 'pull-right')
|
23
|
+
- else
|
24
|
+
= link_to('Edit Raw Data', params.merge(edit_raw_data: true), class: 'pull-right')
|
25
|
+
.clearfix
|
26
|
+
|
27
|
+
|
28
|
+
- if params[:edit_raw_data] == 'true' && current_user.try(:admin?)
|
29
|
+
/ Render JSON editor
|
30
|
+
= wcms_component 'forms/json_editor',
|
31
|
+
form: form,
|
32
|
+
attribute: :presentation_data_json
|
33
|
+
|
34
|
+
- else
|
35
|
+
/ Render HTML for presentation data editor
|
36
|
+
= PresentationDataEditor.new(self, presentation_data, schema, form).render
|
@@ -0,0 +1,32 @@
|
|
1
|
+
/ Example:
|
2
|
+
/ = wcms_component("forms/redactor_editor", {form: f,
|
3
|
+
/ attribute: :body,
|
4
|
+
/ add_buttons: ['link'],
|
5
|
+
/ })
|
6
|
+
|
7
|
+
|
8
|
+
ruby:
|
9
|
+
# Default Parameters
|
10
|
+
form ||= nil
|
11
|
+
attribute ||= nil # attribute name that the editor should be attached to
|
12
|
+
add_class ||= "" # string of additional classes that get appended to class
|
13
|
+
input_class ||= "form-control redactor #{add_class}"
|
14
|
+
value ||= nil # only used if form is nil
|
15
|
+
|
16
|
+
# Configure buttons
|
17
|
+
buttons ||= BiolaWcmsComponents.config.default_redactor_buttons
|
18
|
+
add_buttons ||= [] # adds to defaults
|
19
|
+
remove_buttons ||= [] # removes from default
|
20
|
+
buttons = Array(buttons) + Array(add_buttons) - Array(remove_buttons)
|
21
|
+
buttons << 'html_options' if current_user.try(:admin?)
|
22
|
+
buttons << 'fullscreen'
|
23
|
+
|
24
|
+
# Turn buttons into a string and make sure there are no duplicates
|
25
|
+
buttons = buttons.uniq.join(' ')
|
26
|
+
|
27
|
+
|
28
|
+
|
29
|
+
- if form
|
30
|
+
= form.text_area attribute, class: input_class, 'data-buttons' => buttons
|
31
|
+
- else
|
32
|
+
= text_area_tag attribute, value, class: input_class, 'data-buttons' => buttons
|
@@ -0,0 +1,13 @@
|
|
1
|
+
ruby:
|
2
|
+
form ||= nil
|
3
|
+
attribute ||= nil
|
4
|
+
html_class ||= 'form-control'
|
5
|
+
value ||= nil
|
6
|
+
|
7
|
+
html_options = {class: html_class}
|
8
|
+
html_options[:value] = value if value.present? # you don't want to pass value as an option if it is nil
|
9
|
+
|
10
|
+
- if form
|
11
|
+
= form.text_area attribute, html_options
|
12
|
+
- else
|
13
|
+
= text_area_tag attribute, value, html_options
|
@@ -0,0 +1,16 @@
|
|
1
|
+
ruby:
|
2
|
+
form ||= nil
|
3
|
+
attribute ||= nil
|
4
|
+
html_class ||= 'form-control'
|
5
|
+
value ||= nil
|
6
|
+
embedded_image_url ||= nil # should be the post url for creating new embedded images
|
7
|
+
|
8
|
+
# Set default value
|
9
|
+
# We have to do it this way in case value is an empty string (as opposed to nil).
|
10
|
+
value = value.presence || "---\n# YAML data goes here...\n"
|
11
|
+
|
12
|
+
|
13
|
+
= wcms_component("shared/embedded_image_uploader", embedded_image_url: embedded_image_url)
|
14
|
+
|
15
|
+
.ace_yaml_editor
|
16
|
+
= wcms_component("forms/text_area", {form: form, attribute: attribute, html_class: html_class, value: value})
|
@@ -0,0 +1,33 @@
|
|
1
|
+
/ I'm going to wait to implement this one fully...
|
2
|
+
/ The problem is going to be supporting submenus like on profile publisher
|
3
|
+
/
|
4
|
+
/ Example:
|
5
|
+
/ = wcms_component('navigation/page_menu', { menu_items: [ \
|
6
|
+
/ {body: 'Profile', url: edit_academic_program_path(@academic_program, page: 'profile'), default: true },
|
7
|
+
/ {body: 'Relationships', url: edit_academic_program_path(@academic_program, page: 'relationships') },
|
8
|
+
/ {body: 'Concentrations', url: edit_academic_program_path(@academic_program, page: 'concentrations') },
|
9
|
+
/ {body: 'Degree Requirements', url: edit_academic_program_path(@academic_program, page: 'degree_requirements') },
|
10
|
+
/ {body: 'Requirement Sections', url: edit_academic_program_path(@academic_program, page: 'requirement_sections') },
|
11
|
+
/ {body: 'Outcomes', url: edit_academic_program_path(@academic_program, page: 'outcomes') },
|
12
|
+
/ {body: 'Attachments', url: edit_academic_program_path(@academic_program, page: 'attachments') },
|
13
|
+
/ {body: 'Banner', url: edit_academic_program_path(@academic_program, page: 'banner') },
|
14
|
+
/ {body: 'Presentation Data', url: edit_academic_program_path(@academic_program, page: 'presentation_data') },
|
15
|
+
/ {body: 'History', url: edit_academic_program_path(@academic_program, page: 'activity_logs'), visible: policy(@academic_program).can_manage?(:activity_logs) },
|
16
|
+
/ ]})
|
17
|
+
/
|
18
|
+
|
19
|
+
|
20
|
+
|
21
|
+
/---------------
|
22
|
+
/ ON HOLD...
|
23
|
+
/---------------
|
24
|
+
/ ruby:
|
25
|
+
/ menu_items ||= []
|
26
|
+
|
27
|
+
/ = menu_block(class: 'list-group-item') do |menu|
|
28
|
+
/ - menu_items.each do |item|
|
29
|
+
/ / It needs to be explicitly set to false to be hidden
|
30
|
+
/ - unless item.visible == false
|
31
|
+
/ = menu.add_item(item[:body], default: item[:default]) { item[:url] }
|
32
|
+
|
33
|
+
|
@@ -0,0 +1,38 @@
|
|
1
|
+
/ Parameters
|
2
|
+
/ menu: Array of hashes. This is what goes in the dropdown menu
|
3
|
+
/ body: This is the link text
|
4
|
+
/ url: This is where the link will go. Gets processed by url_for()
|
5
|
+
/ crumbs: (optional) Array of hashes. These are the crumbs after the main menu
|
6
|
+
/ add_crumbs: (optional) Array of hashes. These get pushed onto the default crumbs
|
7
|
+
/
|
8
|
+
/ Example:
|
9
|
+
/ = wcms_component('navigation/site_nav', { menu: [ \
|
10
|
+
/ {body: 'Academic Programs', url: :academic_programs_path},
|
11
|
+
/ {body: 'Schools', url: :schools_path},
|
12
|
+
/ {body: 'ULOs', url: :university_learning_outcomes_path},
|
13
|
+
/ ]})
|
14
|
+
/
|
15
|
+
|
16
|
+
ruby:
|
17
|
+
menu ||= []
|
18
|
+
crumbs ||= [{
|
19
|
+
body: controller_name.titleize,
|
20
|
+
url: {controller: controller_name, action: :index}
|
21
|
+
}]
|
22
|
+
crumbs += Array(add_crumbs) if defined?(add_crumbs)
|
23
|
+
|
24
|
+
|
25
|
+
#wcms_site_navigation
|
26
|
+
.container
|
27
|
+
- if menu.present?
|
28
|
+
.dropdown.custom-dropdown
|
29
|
+
a.dropdown-toggle href='#' data-toggle='dropdown' role='button' Menu
|
30
|
+
ul.dropdown-menu
|
31
|
+
- menu.each do |link|
|
32
|
+
= nav_link(link[:body], link[:url])
|
33
|
+
- else
|
34
|
+
= link_to 'Home', root_url, class: 'top-nav-link'
|
35
|
+
|
36
|
+
- crumbs.each do |crumb|
|
37
|
+
i class='fa fa-angle-right'
|
38
|
+
= link_to crumb[:body], crumb[:url], class: 'top-nav-link'
|