Pre-Registration
This commit is contained in:
commit
9ae5da70fb
28
Capfile
Normal file
28
Capfile
Normal file
@ -0,0 +1,28 @@
|
||||
# Load DSL and set up stages
|
||||
require "capistrano/setup"
|
||||
|
||||
# Include default deployment tasks
|
||||
require "capistrano/deploy"
|
||||
|
||||
# Include tasks from other gems included in your Gemfile
|
||||
#
|
||||
# For documentation on these, see for example:
|
||||
#
|
||||
# https://github.com/capistrano/rvm
|
||||
# https://github.com/capistrano/rbenv
|
||||
# https://github.com/capistrano/chruby
|
||||
# https://github.com/capistrano/bundler
|
||||
# https://github.com/capistrano/rails
|
||||
# https://github.com/capistrano/passenger
|
||||
#
|
||||
# require 'capistrano/rvm'
|
||||
# require 'capistrano/rbenv'
|
||||
# require 'capistrano/chruby'
|
||||
# require 'capistrano/bundler'
|
||||
# require 'capistrano/rails/assets'
|
||||
# require 'capistrano/rails/migrations'
|
||||
# require 'capistrano/passenger'
|
||||
require 'capistrano/faster_assets'
|
||||
|
||||
# Load custom tasks from `lib/capistrano/tasks` if you have any defined
|
||||
Dir.glob("lib/capistrano/tasks/*.rake").each { |r| import r }
|
14
Gemfile
14
Gemfile
@ -6,9 +6,6 @@ gem 'pg'
|
||||
gem 'rack-mini-profiler'
|
||||
|
||||
gem 'haml'
|
||||
# gem 'jquery-rails'
|
||||
# gem 'jquery-ui-rails'
|
||||
# gem 'coffee-rails', '~> 4.0.0'
|
||||
gem 'nokogiri', '~> 1.6.8.rc2'
|
||||
|
||||
if Dir.exists?('../lingua_franca')
|
||||
@ -33,8 +30,6 @@ gem 'oauth2', '~> 0.8.0'
|
||||
gem 'carrierwave'
|
||||
gem 'carrierwave-imageoptimizer'
|
||||
gem 'mini_magick'
|
||||
# gem 'nested_form'
|
||||
# gem 'acts_as_list'
|
||||
gem 'geocoder'
|
||||
gem 'paper_trail', '~> 3.0.5'
|
||||
gem 'sitemap_generator'
|
||||
@ -55,8 +50,13 @@ group :development do
|
||||
gem 'better_errors'
|
||||
gem 'binding_of_caller'
|
||||
gem 'meta_request'
|
||||
# gem 'haml-rails'
|
||||
# gem 'awesome_print'
|
||||
|
||||
gem 'capistrano', '~> 3.1'
|
||||
gem 'capistrano-rails', '~> 1.1'
|
||||
gem 'capistrano-faster-assets', '~> 1.0'
|
||||
|
||||
gem 'eventmachine', :github => 'krzcho/eventmachine', :branch => 'master'
|
||||
gem 'thin', :github => 'krzcho/thin', :branch => 'master'
|
||||
end
|
||||
|
||||
group :test do
|
||||
|
1
app/assets/images/edit.svg
Normal file
1
app/assets/images/edit.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" x="0px" y="0px" viewBox="0 0 100 100" enable-background="new 0 0 100 100" xml:space="preserve"><path fill="#333" d="M78.995,0L13.204,64.781c8.2,5.467,16.085,13.267,21.552,21.468l65.706-64.802C97.014,11.333,89.235,3.427,78.995,0z M7.338,78.343L-0.463,100l21.552-7.885C17.557,87.763,11.69,81.897,7.338,78.343z"></path></svg>
|
After Width: | Height: | Size: 421 B |
1
app/assets/images/img.svg
Normal file
1
app/assets/images/img.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" x="0px" y="0px" viewBox="0 0 100 100" enable-background="new 0 0 100 100" xml:space="preserve"><path fill="#FAF5F0" d="M35.417,33.351l18.75,27.083L68.75,45.851l18.75,25h-75L35.417,33.351z M4.166,8.315C0,8.314,0,8.351,0,12.518v75 c0,4.167,0,4.167,4.167,4.167h91.667c4.167,0,4.167,0,4.167-4.167v-75c0-4.167,0-4.167-4.167-4.167L4.166,8.315z"></path></svg>
|
After Width: | Height: | Size: 450 B |
1
app/assets/images/link.svg
Normal file
1
app/assets/images/link.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" x="0px" y="0px" viewBox="0 0 100 100" enable-background="new 0 0 100 100" xml:space="preserve"><path fill="#FAF5F0" d="M92.071,7.974C81.305-2.793,64.024-2.431,53.529,8.065L41.043,20.55c7.329-2.352,13.028-0.724,18.005,1.357l3.619-3.619 c5.157-5.157,13.752-4.886,18.819,0.181c5.157,5.157,5.519,13.843,0.362,19L65.019,54.298c-1.9,1.9-4.071,2.986-6.514,3.619 c-6.514,0.905-12.757-2.352-15.109-8.052L32.357,60.54c7.6,11.49,23.343,15.381,35.828,8.324c2.081-1.086,4.071-2.714,5.881-4.524 l17.914-17.914C102.476,35.931,102.838,18.741,92.071,7.974z M32.538,30.503c-2.171,1.086-4.162,2.714-5.881,4.524L8.019,53.574 C-2.476,64.069-2.838,81.259,7.929,92.026s28.047,10.405,38.543-0.09L58.957,79.45c-7.329,2.352-13.028,0.724-18.005-1.357 l-3.619,3.619c-5.157,5.157-13.752,4.886-18.819-0.181c-5.157-5.157-5.519-13.843-0.362-19l17.462-17.462 c1.9-1.9,4.071-2.986,6.514-3.619c6.514-0.905,12.757,2.352,15.109,8.052l11.129-10.676C60.676,27.336,44.933,23.445,32.538,30.503z"></path></svg>
|
After Width: | Height: | Size: 1.0 KiB |
1
app/assets/images/ol.svg
Normal file
1
app/assets/images/ol.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" x="0px" y="0px" viewBox="0 0 100 100" enable-background="new 0 0 100 100" xml:space="preserve"><path fill="#FAF5F0" d="M95.833,25h-50c-4.167,0-4.167,0-4.167,4.167v4.167c0,4.167,0,4.167,4.167,4.167h50c4.167,0,4.167,0,4.167-4.167v-4.167 C100,25,100,25,95.833,25z M95.833,62.5h-50c-4.167,0-4.167,0-4.167,4.167v4.167c0,4.167,0,4.167,4.167,4.167h50 C100,75,100,75,100,70.833v-4.167C100,62.5,100,62.5,95.833,62.5z M95.833,87.5h-50c-4.167,0-4.167,0-4.167,4.167v4.167 c0,4.167,0,4.167,4.167,4.167h50C100,100,100,100,100,95.833v-4.167C100,87.5,100,87.5,95.833,87.5z M95.833,0h-50 c-4.167,0-4.167,0-4.167,4.167v4.167c0,4.167,0,4.167,4.167,4.167h50c4.167,0,4.167,0,4.167-4.167V4.167C100,0,100,0,95.833,0z M22.917,0H6.25L4.167,10.417H12.5V37.5h10.417V0z M14.583,61.75C6.25,61.75,0,67.333,0,75.667h9.167 c0-2.917,1.667-5.583,5.417-5.583c3.375,0,5.167,2.146,5.125,4.771c-0.021,1.708-1.188,3.292-6,7.333L0,93.75V100h29.167v-9.5H18 l5.104-5.125c4.688-4.688,6.062-7.396,6.062-11.375C29.167,67.667,22.917,61.75,14.583,61.75z"></path></svg>
|
After Width: | Height: | Size: 1.1 KiB |
1
app/assets/images/quote.svg
Normal file
1
app/assets/images/quote.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" x="0px" y="0px" viewBox="17 -18 100 100" enable-background="new 17 -18 100 100" xml:space="preserve"><path fill="#FAF5F0" d="M93.2-3.9C106-3.9,117,4.9,117,23.9c0,26.7-21.8,42.4-34.2,44l-2.6-7.4c7.2-4.7,13.1-12.8,12.5-17.5 c-8.1-0.6-21.2-4.9-21.2-22.5C71.4,7.2,78.9-3.9,93.2-3.9z M38.9-3.9c12.8,0,23.8,8.8,23.8,27.8c0,26.7-21.8,42.4-34.2,44l-2.6-7.4 C33,55.8,38.9,47.8,38.2,43C30.2,42.4,17,38.1,17,20.5C17,7.2,24.6-3.9,38.9-3.9z"></path></svg>
|
After Width: | Height: | Size: 541 B |
1
app/assets/images/ul.svg
Normal file
1
app/assets/images/ul.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" x="0px" y="0px" viewBox="0 0 100 100" enable-background="new 0 0 100 100" xml:space="preserve"><path fill="#FAF5F0" d="M12.5,87.5C19.404,87.5,25,81.904,25,75s-5.596-12.5-12.5-12.5C5.596,62.5,0,68.096,0,75S5.596,87.5,12.5,87.5z M12.5,25 C19.396,25,25,19.396,25,12.5S19.396,0,12.5,0S0,5.604,0,12.5S5.604,25,12.5,25z M95.833,25h-50c-4.167,0-4.167,0-4.167,4.167v4.167 c0,4.167,0,4.167,4.167,4.167h50c4.167,0,4.167,0,4.167-4.167v-4.167C100,25,100,25,95.833,25z M95.833,62.5h-50 c-4.167,0-4.167,0-4.167,4.167v4.167c0,4.167,0,4.167,4.167,4.167h50C100,75,100,75,100,70.833v-4.167 C100,62.5,100,62.5,95.833,62.5z M95.833,87.5h-50c-4.167,0-4.167,0-4.167,4.167v4.167c0,4.167,0,4.167,4.167,4.167h50 C100,100,100,100,100,95.833v-4.167C100,87.5,100,87.5,95.833,87.5z M95.833,0h-50c-4.167,0-4.167,0-4.167,4.167v4.167 c0,4.167,0,4.167,4.167,4.167h50c4.167,0,4.167,0,4.167-4.167V4.167C100,0,100,0,95.833,0z"></path></svg>
|
After Width: | Height: | Size: 1007 B |
79
app/assets/javascripts/editor.js
Normal file
79
app/assets/javascripts/editor.js
Normal file
@ -0,0 +1,79 @@
|
||||
(function() {
|
||||
var pens = {};
|
||||
|
||||
Array.prototype.forEach.call(document.querySelectorAll('.textarea'), function(editor) {
|
||||
startEditing(editor);
|
||||
});
|
||||
|
||||
function startEditing(editor) {
|
||||
var name = editor.dataset.name;
|
||||
pens[name] = new Pen({
|
||||
editor: editor,
|
||||
class: 'pen',
|
||||
textarea: '<textarea name="' + name + '"></textarea>',
|
||||
list: ['p', 'h3', 'h4', 'blockquote', 'insertorderedlist', 'insertunorderedlist', 'bold', 'italic', 'underline', 'strikethrough', 'createlink', 'insertimage'],
|
||||
title: {
|
||||
'p': 'Paragraph',
|
||||
'h3': 'Major Heading',
|
||||
'h4': 'Minor Heading',
|
||||
'blockquote': 'Quotation',
|
||||
'insertorderedlist': 'Ordered List',
|
||||
'insertunorderedlist': 'Unordered List',
|
||||
'bold': 'Bold',
|
||||
'italic': 'Italic',
|
||||
'underline': 'Underline',
|
||||
'strikethrough': 'Strikethrough',
|
||||
'createlink': 'Link',
|
||||
'insertimage': 'Image'
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Array.prototype.forEach.call(document.querySelectorAll('form'), function(form) {
|
||||
var shouldAllowAlert = false;
|
||||
form.addEventListener('submit', function() {
|
||||
if (shouldAllowAlert) {
|
||||
return;
|
||||
}
|
||||
Array.prototype.forEach.call(document.querySelectorAll('.textarea'), function(editor) {
|
||||
var name = editor.dataset.name;
|
||||
var textarea = document.querySelector('textarea[name="' + name + '"]');
|
||||
if (!textarea) {
|
||||
textarea = document.createElement('textarea');
|
||||
textarea.name = name;
|
||||
textarea.style.display = 'none';
|
||||
form.appendChild(textarea);
|
||||
}
|
||||
textarea.value = editor.innerHTML;
|
||||
pens[name].destroy();
|
||||
});
|
||||
}, false);
|
||||
Array.prototype.forEach.call(form.querySelectorAll('button'), function(button) {
|
||||
form.addEventListener('click', function(event) {
|
||||
shouldAllowAlert = (event.target.value === 'cancel');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Array.prototype.forEach.call(document.querySelectorAll('.check-box-field .other input'), function(input) {
|
||||
var checkbox = document.getElementById(input.parentElement.parentElement.attributes.for.value);
|
||||
input.addEventListener('keyup', function(event) {
|
||||
if (event.target.value) {
|
||||
checkbox.checked = true;
|
||||
}
|
||||
});
|
||||
input.addEventListener('click', function(event) {
|
||||
checkbox.checked = true;
|
||||
});
|
||||
var setRequired = function() {
|
||||
if (checkbox.checked) {
|
||||
input.setAttribute('required', 'required');
|
||||
} else {
|
||||
input.removeAttribute('required');
|
||||
}
|
||||
};
|
||||
Array.prototype.forEach.call(document.querySelectorAll('.check-box-field input'), function(_input) {
|
||||
_input.addEventListener('change', function(event) { setRequired(); });
|
||||
});
|
||||
});
|
||||
})();
|
@ -1,37 +0,0 @@
|
||||
#= require froala_editor.min.js
|
||||
$ ->
|
||||
$('[data-editable]').editable({inlineMode: true, blockTags: ["n", "p", "h2", "blockquote", "pre"], buttons: ["formatBlock", "bold", "italic", "underline", "insertOrderedList", "insertUnorderedList", "sep", "createLink", "insertImage", "insertVideo", "html", "undo", "redo"]})
|
||||
$('[data-editor]').editable({inlineMode: false, blockTags: ["n", "p", "h2", "blockquote", "pre"], buttons: ["formatBlock", "bold", "italic", "underline", "insertOrderedList", "insertUnorderedList", "sep", "createLink", "html", "undo", "redo"]})
|
||||
$('.field.country-select-field select').change () ->
|
||||
$country = $(this)
|
||||
country = $country.val()
|
||||
$territory = $('.field.subregion-select-field select')
|
||||
if $territory.data().country == country
|
||||
$territory.removeClass('can cant').addClass('can')
|
||||
return
|
||||
|
||||
$.post '/location/territories', {country: country},
|
||||
(json) ->
|
||||
$territory.html('')
|
||||
if json && Object.keys(json).length
|
||||
$.each json, (code, name) ->
|
||||
$territory.append($('<option>').text(name).attr('value', code))
|
||||
return
|
||||
$territory.removeClass('can cant').addClass('can')
|
||||
$territory.data().country = country
|
||||
else
|
||||
$territory.removeClass('can cant').addClass('cant')
|
||||
return
|
||||
, 'json'
|
||||
$('img + input[type="file"]').change () ->
|
||||
readURL(this);
|
||||
return
|
||||
|
||||
readURL = (input) ->
|
||||
reader = null
|
||||
if input.files && input.files[0]
|
||||
reader = new FileReader()
|
||||
reader.readAsDataURL input.files[0]
|
||||
reader.onload = (e) ->
|
||||
$(input).prev().attr('src', e.target.result)
|
||||
return
|
16
app/assets/javascripts/main.js
Normal file
16
app/assets/javascripts/main.js
Normal file
@ -0,0 +1,16 @@
|
||||
(function() {
|
||||
Array.prototype.forEach.call(document.querySelectorAll('.number-field,.email-field,.text-field'), function(field) {
|
||||
var input = field.querySelector('input');
|
||||
var positionLabel = function(input) {
|
||||
field.classList[input.value ? 'remove' : 'add']('empty');
|
||||
}
|
||||
positionLabel(input);
|
||||
input.addEventListener('keyup', function(event) { positionLabel(event.target); });
|
||||
input.addEventListener('blur', function(event) { field.classList.remove('focused'); });
|
||||
input.addEventListener('focus', function(event) { field.classList.add('focused'); });
|
||||
});
|
||||
var errorField = document.querySelector('.input-field.has-error input, .input-field.has-error textarea');
|
||||
if (errorField) {
|
||||
errorField.focus();
|
||||
}
|
||||
})();
|
File diff suppressed because it is too large
Load Diff
212
app/assets/stylesheets/_editor.scss
Normal file
212
app/assets/stylesheets/_editor.scss
Normal file
@ -0,0 +1,212 @@
|
||||
$bumbleberry-no-markup: true;
|
||||
|
||||
@import "bumbleberry";
|
||||
@import "settings";
|
||||
|
||||
$white: #FFF !default;
|
||||
$black: #333 !default;
|
||||
|
||||
.pen-icon {
|
||||
font-size: 16px;
|
||||
line-height: 40px;
|
||||
min-width: 20px;
|
||||
font-style: normal;
|
||||
display: block;
|
||||
float: left;
|
||||
padding: 0 10px;
|
||||
height: 2.2em;
|
||||
overflow: hidden;
|
||||
color: $white;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
@include _(user-select, none);
|
||||
}
|
||||
|
||||
.pen {
|
||||
position: relative;
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
|
||||
.pen-menu, .pen-input {
|
||||
font-size: 14px;
|
||||
line-height:1;
|
||||
}
|
||||
|
||||
.pen-input {
|
||||
display :none;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
left: 0;
|
||||
top: 0;
|
||||
height: 2.6em;
|
||||
line-height: 20px;
|
||||
background-color: $black;
|
||||
color: $white;
|
||||
border: none;
|
||||
text-align: center;
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
|
||||
.pen-textarea {
|
||||
display: block;
|
||||
background: $white;
|
||||
padding: 20px;
|
||||
}
|
||||
.pen textarea {
|
||||
font-size: 14px;
|
||||
border: none;
|
||||
background: none;
|
||||
width: 100%;
|
||||
_height: 200px;
|
||||
min-height: 200px;
|
||||
resize: none;
|
||||
}
|
||||
|
||||
.pen-menu-below {
|
||||
@include after {
|
||||
top: -11px;
|
||||
display: block;
|
||||
@include _(transform, rotate(180deg));
|
||||
}
|
||||
}
|
||||
|
||||
.pen-menu {
|
||||
white-space: nowrap;
|
||||
box-shadow:1px 2px 3px -2px #222;
|
||||
background: #333;
|
||||
background-image: linear-gradient(to bottom, #222, #333);
|
||||
opacity: 0.9;
|
||||
position: fixed;
|
||||
height: 5.2em;
|
||||
border: 1px solid #333;
|
||||
border-radius: 0.5em;
|
||||
display:none;
|
||||
z-index: 1000;
|
||||
|
||||
@include after {
|
||||
content: " ";
|
||||
top: 100%;
|
||||
border: solid transparent;
|
||||
height: 0;
|
||||
width: 0;
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
border-color: rgba(51, 51, 51, 0);
|
||||
border-top-color: #333;
|
||||
border-width: 6px;
|
||||
left: 50%;
|
||||
margin-left: -6px;
|
||||
}
|
||||
|
||||
[class^="icon-"], [class*=" icon-"] {
|
||||
@include before {
|
||||
speak: none;
|
||||
display: inline-block;
|
||||
width: 1em;
|
||||
margin-right: .2em;
|
||||
text-align: center;
|
||||
font-variant: normal;
|
||||
text-transform: none;
|
||||
line-height: 1em;
|
||||
margin-left: .2em;
|
||||
vertical-align: text-top;
|
||||
}
|
||||
}
|
||||
|
||||
.pen-icon {
|
||||
&:hover {
|
||||
background:#000;
|
||||
}
|
||||
&.active {
|
||||
background: #000;
|
||||
@include _(box-shadow, #{inset 2px 2px 4px #000, 0 0 0.25em #000});
|
||||
}
|
||||
}
|
||||
|
||||
.icon-h1,
|
||||
.icon-h2,
|
||||
.icon-h3,
|
||||
.icon-h4,
|
||||
.icon-h5,
|
||||
.icon-h6 {
|
||||
@include before { @include font-family(secondary); }
|
||||
}
|
||||
.icon-h1 {
|
||||
@include before { content: 'H1'; }
|
||||
}
|
||||
.icon-h2 {
|
||||
@include before { content: 'H2'; }
|
||||
}
|
||||
.icon-h3 {
|
||||
@include before { content: 'H1'; }
|
||||
}
|
||||
.icon-h4 {
|
||||
@include before { content: 'H2'; }
|
||||
}
|
||||
.icon-h5 {
|
||||
@include before { content: 'H5'; }
|
||||
}
|
||||
.icon-h6 {
|
||||
@include before { content: 'H6'; }
|
||||
}
|
||||
.icon-bold {
|
||||
border-bottom-left-radius: 0.5em;
|
||||
clear: left;
|
||||
@include before { content: 'B'; font-weight: bold; }
|
||||
}
|
||||
.icon-italic {
|
||||
@include before { content: 'I'; font-style: italic;; }
|
||||
}
|
||||
.icon-underline {
|
||||
@include before { content: 'U'; text-decoration: underline; }
|
||||
}
|
||||
.icon-p {
|
||||
@include font-family(secondary);
|
||||
@include before { content: '¶'; }
|
||||
}
|
||||
.icon-blockquote {
|
||||
@include before {
|
||||
background-image: inline-svg-image('quote.svg');
|
||||
}
|
||||
}
|
||||
.icon-insertorderedlist {
|
||||
border-top-right-radius: 0.5em;
|
||||
|
||||
@include before {
|
||||
background-image: inline-svg-image('ol.svg');
|
||||
}
|
||||
}
|
||||
.icon-insertunorderedlist { @include before {
|
||||
background-image: inline-svg-image('ul.svg');
|
||||
} }
|
||||
|
||||
.icon-strikethrough { @include before { content: 'S'; text-decoration: line-through; } }
|
||||
|
||||
.icon-insertunorderedlist,
|
||||
.icon-insertorderedlist,
|
||||
.icon-blockquote,
|
||||
.icon-insertimage,
|
||||
.icon-createlink {
|
||||
@include before {
|
||||
content: '';
|
||||
background-size: contain;
|
||||
height: 1em;
|
||||
}
|
||||
}
|
||||
.icon-createlink {
|
||||
@include before {
|
||||
background-image: inline-svg-image('link.svg');
|
||||
}
|
||||
}
|
||||
.icon-insertimage {
|
||||
@include before {
|
||||
background-image: inline-svg-image('img.svg');
|
||||
}
|
||||
}
|
||||
}
|
@ -13,6 +13,9 @@ $colour-4: #D89E59; // orange
|
||||
$colour-5: #02CA9E; // green
|
||||
|
||||
$white: #FFFEFE;
|
||||
$black: #333;
|
||||
|
||||
$link-colour: darken($colour-1, 13%);
|
||||
|
||||
@mixin default-box-shadow($direction: top, $distance: 1, $inset: false, $additional-shadow: false) {
|
||||
@if capable_of(box-shadow) {
|
||||
|
@ -1,11 +1,15 @@
|
||||
{
|
||||
"stylesheets": ["application", "translations", "email-example"],
|
||||
"stylesheets": ["application", "editor"],
|
||||
"precompile": {
|
||||
"test": {
|
||||
"chrome": ["50"]
|
||||
},
|
||||
"development": {
|
||||
"and_chr": ["50"],
|
||||
"chrome": ["50"],
|
||||
"edge": ["13"],
|
||||
"firefox": ["44"],
|
||||
"ie": ["11"],
|
||||
"ios_saf": ["8", "9"]
|
||||
}
|
||||
},
|
||||
|
183
app/assets/stylesheets/user-mailer.scss
Normal file
183
app/assets/stylesheets/user-mailer.scss
Normal file
@ -0,0 +1,183 @@
|
||||
@import "settings";
|
||||
|
||||
body {
|
||||
width: 100% !important;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
-ms-text-size-adjust: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.ExternalClass {
|
||||
width: 100%;
|
||||
line-height: 100%;
|
||||
|
||||
p, span, font, td, div {
|
||||
line-height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
#backgroundTable {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 100% !important;
|
||||
line-height: 100% !important;
|
||||
}
|
||||
|
||||
img {
|
||||
outline: none;
|
||||
text-decoration: none;
|
||||
-ms-interpolation-mode: bicubic;
|
||||
|
||||
a & {
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
|
||||
.image_fix {
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
a {
|
||||
color: $colour-1;
|
||||
|
||||
#outlook & {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
&:link { color: $colour-1; }
|
||||
&:visited { color: $colour-1; }
|
||||
&:hover { color: $colour-5; }
|
||||
|
||||
@media only screen and (max-device-width: 480px) {
|
||||
&[href^="tel"], &[href^="sms"] {
|
||||
text-decoration: none;
|
||||
color: $black;
|
||||
pointer-events: none;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.mobile_link &[href^="tel"], .mobile_link &[href^="sms"] {
|
||||
text-decoration: default;
|
||||
color: $colour-1 !important;
|
||||
pointer-events: auto;
|
||||
cursor: default;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 1em;
|
||||
color: $black !important;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
color: $black !important;
|
||||
|
||||
a {
|
||||
color: $colour-1 !important;
|
||||
|
||||
&:active {
|
||||
color: $colour-4 !important;
|
||||
}
|
||||
|
||||
&:visited {
|
||||
color: $colour-2 !important;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
max-width: 512px;
|
||||
min-width: 280px;
|
||||
mso-table-lspace: 0pt;
|
||||
mso-table-rspace: 0pt;
|
||||
|
||||
td {
|
||||
border-collapse: collapse;
|
||||
}
|
||||
}
|
||||
|
||||
table#bb_full_width,
|
||||
table#ecxbb_full_width {
|
||||
|
||||
&, table {
|
||||
max-width: none;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.diff, .ecxdiff {
|
||||
margin: 1em 0 5em 1em;
|
||||
overflow: auto;
|
||||
|
||||
ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
li {
|
||||
width: 40%;
|
||||
display: inline-block;
|
||||
padding: 0.5em;
|
||||
margin: 0;
|
||||
background-color: lighten($colour-3, 33%);
|
||||
border: 2px solid #DDD;
|
||||
white-space: nowrap;
|
||||
|
||||
&:nth-child(odd) {
|
||||
margin-right: -3px;
|
||||
float: left;
|
||||
}
|
||||
|
||||
&:nth-child(even) {
|
||||
background-color: lighten($colour-5 , 33%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Buttons
|
||||
h3 {
|
||||
b {
|
||||
padding: 10px 20px;
|
||||
line-height: 50px;
|
||||
|
||||
a, a:visited {
|
||||
color: #FFF !important;
|
||||
background-color: $colour-5;
|
||||
text-decoration: none !important;
|
||||
border-radius: 4px;
|
||||
padding: 10px 15px;
|
||||
margin-left: 20px;
|
||||
border-bottom: 3px solid darken($colour-5, 10%);
|
||||
-webkit-box-shadow: 0 0.5em 1.5em -0.75em #000;
|
||||
box-shadow: 0 0.5em 1.5em -0.75em #000;
|
||||
cursor: pointer !important;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
background-color: darken($colour-5, 10%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (min-device-width: 768px) and (max-device-width: 1024px) {
|
||||
a[href^="tel"], a[href^="sms"] {
|
||||
text-decoration: none;
|
||||
color: $colour-1;
|
||||
pointer-events: none;
|
||||
cursor: default;
|
||||
|
||||
.mobile_link & {
|
||||
text-decoration: default;
|
||||
color: $colour-1 !important;
|
||||
pointer-events: auto;
|
||||
cursor: default;
|
||||
}
|
||||
}
|
||||
}
|
@ -18,6 +18,7 @@ class ApplicationController < LinguaFrancaApplicationController
|
||||
def capture_page_info
|
||||
# set the translator to the current user if we're logged in
|
||||
I18n.config.translator = current_user
|
||||
I18n.config.callback = self
|
||||
|
||||
# get the current confernece and set it globally
|
||||
@conference = Conference.order("start_date DESC").first
|
||||
@ -78,7 +79,8 @@ class ApplicationController < LinguaFrancaApplicationController
|
||||
end
|
||||
|
||||
def do_404
|
||||
params[:action] = 'error-403'
|
||||
params[:_original_action] = params[:action]
|
||||
params[:action] = 'error-404'
|
||||
render 'application/404', status: 404
|
||||
end
|
||||
|
||||
@ -88,6 +90,7 @@ class ApplicationController < LinguaFrancaApplicationController
|
||||
|
||||
def do_403(template = nil)
|
||||
@template = template
|
||||
params[:_original_action] = params[:action]
|
||||
params[:action] = 'error-403'
|
||||
render 'application/permission_denied', status: 403
|
||||
end
|
||||
@ -143,13 +146,12 @@ class ApplicationController < LinguaFrancaApplicationController
|
||||
end
|
||||
user = User.find_by_email(params[:email])
|
||||
|
||||
if !user
|
||||
unless user
|
||||
# not really a good UX so we should fix this later
|
||||
#do_404
|
||||
#return
|
||||
user = User.new(:email => params[:email])
|
||||
user.save!
|
||||
user = User.find_by_email(params[:email])
|
||||
end
|
||||
|
||||
# genereate the confirmation, send the email and show the 403
|
||||
@ -218,4 +220,28 @@ class ApplicationController < LinguaFrancaApplicationController
|
||||
auto_login(u)
|
||||
end
|
||||
|
||||
def on_translation_change(object, data, locale, translator_id)
|
||||
translator = User.find(translator_id)
|
||||
mailer = "#{object.class.table_name.singularize}_translated"
|
||||
|
||||
object.get_translators(data, locale).each do |id, user|
|
||||
if user.id != current_user.id && user.id != translator_id
|
||||
UserMailer.send_mail mailer do
|
||||
{ :args => [object, data, locale, user, translator] }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def on_translatable_content_change(object, data)
|
||||
mailer = "#{object.class.table_name.singularize}_original_content_changed"
|
||||
|
||||
object.get_translators(data).each do |id, user|
|
||||
if user.id != current_user.id
|
||||
UserMailer.send_mail mailer do
|
||||
{ :args => [object, data, user, current_user] }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -514,150 +514,219 @@ class ConferencesController < ApplicationController
|
||||
end
|
||||
|
||||
def register
|
||||
is_post = request.post? || session[:registration_step]
|
||||
# is_post = request.post? || session[:registration_step]
|
||||
set_conference
|
||||
|
||||
#if !@this_conference.registration_open
|
||||
# do_404
|
||||
# return
|
||||
#end
|
||||
|
||||
set_conference_registration
|
||||
|
||||
@register_template = nil
|
||||
|
||||
if logged_in?
|
||||
unless @this_conference.registration_open || @registration
|
||||
do_404
|
||||
return
|
||||
end
|
||||
# if the user is logged in start them off on the policy
|
||||
# page, unless they have already begun registration then
|
||||
# start them off with questions
|
||||
@register_template = @registration ? (@registration.registration_fees_paid ? :done : :payment) : :policy
|
||||
set_or_create_conference_registration
|
||||
|
||||
@name = current_user.firstname
|
||||
# we should phase out last names
|
||||
@name += " #{current_user.lastname}" if current_user.lastname
|
||||
|
||||
@name ||= current_user.username
|
||||
|
||||
@is_host = @this_conference.host? current_user
|
||||
|
||||
steps = registration_steps
|
||||
return do_404 unless steps.present?
|
||||
else
|
||||
@register_template = :confirm_email
|
||||
end
|
||||
|
||||
@errors = {}
|
||||
@warnings = []
|
||||
form_step = params[:button] ? params[:button].to_sym : nil
|
||||
|
||||
if form_step
|
||||
if form_step.to_s =~ /^prev_(.+)$/
|
||||
@register_template = steps[steps.find_index($1.to_sym) - 1]
|
||||
else
|
||||
|
||||
case form_step
|
||||
when :confirm_email
|
||||
return do_confirm
|
||||
when :contact_info
|
||||
if params[:name].present? && params[:name].gsub(/[\s\W]/, '').present?
|
||||
current_user.firstname = params[:name].squish
|
||||
current_user.lastname = nil
|
||||
else
|
||||
@errors[:name] = :empty
|
||||
end
|
||||
|
||||
if params[:location].present? && params[:location].gsub(/[\s\W]/, '').present? && (l = Geocoder.search(params[:location])).present?
|
||||
corrected = view_context.location(l.first)
|
||||
|
||||
if corrected.present?
|
||||
@registration.city = corrected
|
||||
if params[:location].gsub(/[\s,]/, '').downcase != @registration.city.gsub(/[\s,]/, '').downcase
|
||||
@warnings << view_context._('warnings.messages.location_corrected',"Your location was corrected from \"#{params[:location]}\" to \"#{corrected}\". If this doesn't reflect your intended location, you can change this again in the contact info step.", vars: {original: params[:location], corrected: corrected})
|
||||
end
|
||||
else
|
||||
@errors[:location] = :unknown
|
||||
end
|
||||
else
|
||||
@errors[:location] = :empty
|
||||
end
|
||||
|
||||
if params[:languages].present?
|
||||
current_user.languages = params[:languages].keys
|
||||
else
|
||||
@errors[:languages] = :empty
|
||||
end
|
||||
|
||||
current_user.save! unless @errors.present?
|
||||
end
|
||||
|
||||
if @errors.present?
|
||||
@register_template = form_step
|
||||
else
|
||||
unless @registration.nil?
|
||||
@register_template = steps[steps.find_index(form_step) + 1]
|
||||
|
||||
# have we reached a new level?
|
||||
unless @registration.steps_completed.include? form_step.to_s
|
||||
@registration.steps_completed ||= []
|
||||
@registration.steps_completed << form_step
|
||||
|
||||
# workshops is the last step
|
||||
if @register_template == :workshops
|
||||
UserMailer.send_mail :registration_confirmation do
|
||||
{
|
||||
:args => @registration
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@registration.save!
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@register_template ||= (params[:step] || current_step).to_sym
|
||||
|
||||
return redirect_to register_path(@this_conference.slug) if
|
||||
logged_in? && @register_template != current_step &&
|
||||
!@registration.steps_completed.include?(@register_template.to_s)
|
||||
|
||||
# process data from the last view
|
||||
case (params[:button] || '').to_sym
|
||||
when :confirm_email
|
||||
@register_template = :email_sent if is_post
|
||||
when :policy
|
||||
@register_template = :questions if is_post
|
||||
when :save
|
||||
if is_post
|
||||
if (new_registration = (!@registration))
|
||||
@registration = ConferenceRegistration.new
|
||||
end
|
||||
# case (params[:button] || '').to_sym
|
||||
# when :confirm_email
|
||||
# @register_template = :email_sent if is_post
|
||||
# when :policy
|
||||
# @register_template = :questions if is_post
|
||||
# when :save
|
||||
# if is_post
|
||||
# new_registration = !@registration
|
||||
# @registration = ConferenceRegistration.new if new_registration
|
||||
|
||||
@registration.conference_id = @this_conference.id
|
||||
@registration.user_id = current_user.id
|
||||
@registration.is_attending = 'yes'
|
||||
@registration.is_confirmed = true
|
||||
@registration.city = params[:location]
|
||||
@registration.arrival = params[:arrival]
|
||||
@registration.languages = params[:languages].keys.to_json
|
||||
@registration.departure = params[:departure]
|
||||
@registration.housing = params[:housing]
|
||||
@registration.bike = params[:bike]
|
||||
@registration.food = params[:food]
|
||||
@registration.allergies = params[:allergies]
|
||||
@registration.other = params[:other]
|
||||
@registration.save
|
||||
# @registration.conference_id = @this_conference.id
|
||||
# @registration.user_id = current_user.id
|
||||
# @registration.is_attending = 'yes'
|
||||
# @registration.is_confirmed = true
|
||||
# @registration.city = params[:location]
|
||||
# @registration.arrival = params[:arrival]
|
||||
# @registration.languages = params[:languages].keys.to_json
|
||||
# @registration.departure = params[:departure]
|
||||
# @registration.housing = params[:housing]
|
||||
# @registration.bike = params[:bike]
|
||||
# @registration.food = params[:food]
|
||||
# @registration.allergies = params[:allergies]
|
||||
# @registration.other = params[:other]
|
||||
# @registration.save
|
||||
|
||||
current_user.firstname = params[:name].squish
|
||||
current_user.lastname = nil
|
||||
current_user.save
|
||||
# current_user.firstname = params[:name].squish
|
||||
# current_user.lastname = nil
|
||||
# current_user.save
|
||||
|
||||
if new_registration
|
||||
UserMailer.send_mail :registration_confirmation do
|
||||
{
|
||||
:args => @registration
|
||||
}
|
||||
end
|
||||
end
|
||||
# if new_registration
|
||||
# UserMailer.send_mail :registration_confirmation do
|
||||
# {
|
||||
# :args => @registration
|
||||
# }
|
||||
# end
|
||||
# end
|
||||
|
||||
@register_template = @registration.registration_fees_paid ? :done : :payment
|
||||
end
|
||||
when :payment
|
||||
if is_post && @registration
|
||||
amount = params[:amount].to_f
|
||||
# @register_template = @registration.registration_fees_paid ? :done : :payment
|
||||
# end
|
||||
# when :payment
|
||||
# if is_post && @registration
|
||||
# amount = params[:amount].to_f
|
||||
|
||||
if amount > 0
|
||||
@registration.payment_confirmation_token = ENV['RAILS_ENV'] == 'test' ? 'token' : Digest::SHA256.hexdigest(rand(Time.now.to_f * 1000000).to_i.to_s)
|
||||
@registration.save
|
||||
# if amount > 0
|
||||
# @registration.payment_confirmation_token = ENV['RAILS_ENV'] == 'test' ? 'token' : Digest::SHA256.hexdigest(rand(Time.now.to_f * 1000000).to_i.to_s)
|
||||
# @registration.save
|
||||
|
||||
host = "#{request.protocol}#{request.host_with_port}"
|
||||
response = PayPal!.setup(
|
||||
PayPalRequest(amount),
|
||||
register_paypal_confirm_url(@this_conference.slug, :paypal_confirm, @registration.payment_confirmation_token),
|
||||
register_paypal_confirm_url(@this_conference.slug, :paypal_cancel, @registration.payment_confirmation_token)
|
||||
)
|
||||
if ENV['RAILS_ENV'] != 'test'
|
||||
redirect_to response.redirect_uri
|
||||
end
|
||||
return
|
||||
end
|
||||
@register_template = :done
|
||||
end
|
||||
when :paypal_confirm
|
||||
if @registration && @registration.payment_confirmation_token == params[:confirmation_token]
|
||||
# host = "#{request.protocol}#{request.host_with_port}"
|
||||
# response = PayPal!.setup(
|
||||
# PayPalRequest(amount),
|
||||
# register_paypal_confirm_url(@this_conference.slug, :paypal_confirm, @registration.payment_confirmation_token),
|
||||
# register_paypal_confirm_url(@this_conference.slug, :paypal_cancel, @registration.payment_confirmation_token)
|
||||
# )
|
||||
# if ENV['RAILS_ENV'] != 'test'
|
||||
# redirect_to response.redirect_uri
|
||||
# end
|
||||
# return
|
||||
# end
|
||||
# @register_template = :done
|
||||
# end
|
||||
# when :paypal_confirm
|
||||
# if @registration && @registration.payment_confirmation_token == params[:confirmation_token]
|
||||
|
||||
if ENV['RAILS_ENV'] == 'test'
|
||||
@amount = YAML.load(@registration.payment_info)[:amount]
|
||||
else
|
||||
@amount = PayPal!.details(params[:token]).amount.total
|
||||
# testing this does't work in test but it works in devo and prod
|
||||
@registration.payment_info = {:payer_id => params[:PayerID], :token => params[:token], :amount => @amount}.to_yaml
|
||||
end
|
||||
# if ENV['RAILS_ENV'] == 'test'
|
||||
# @amount = YAML.load(@registration.payment_info)[:amount]
|
||||
# else
|
||||
# @amount = PayPal!.details(params[:token]).amount.total
|
||||
# # testing this does't work in test but it works in devo and prod
|
||||
# @registration.payment_info = {:payer_id => params[:PayerID], :token => params[:token], :amount => @amount}.to_yaml
|
||||
# end
|
||||
|
||||
@amount = (@amount * 100).to_i.to_s.gsub(/^(.*)(\d\d)$/, '\1.\2')
|
||||
# @amount = (@amount * 100).to_i.to_s.gsub(/^(.*)(\d\d)$/, '\1.\2')
|
||||
|
||||
@registration.save!
|
||||
@register_template = :paypal_confirm
|
||||
end
|
||||
when :paypal_confirmed
|
||||
info = YAML.load(@registration.payment_info)
|
||||
@amount = nil
|
||||
status = nil
|
||||
if ENV['RAILS_ENV'] == 'test'
|
||||
status = info[:status]
|
||||
@amount = info[:amount]
|
||||
else
|
||||
paypal = PayPal!.checkout!(info[:token], info[:payer_id], PayPalRequest(info[:amount]))
|
||||
status = paypal.payment_info.first.payment_status
|
||||
@amount = paypal.payment_info.first.amount.total
|
||||
end
|
||||
if status == 'Completed'
|
||||
@registration.registration_fees_paid = @amount
|
||||
@registration.save!
|
||||
@register_template = :done
|
||||
else
|
||||
@register_template = :payment
|
||||
end
|
||||
when :paypal_cancel
|
||||
if @registration
|
||||
@registration.payment_confirmation_token = nil
|
||||
@registration.save
|
||||
@register_template = :payment
|
||||
end
|
||||
when :register
|
||||
@register_template = :questions
|
||||
end
|
||||
# @registration.save!
|
||||
# @register_template = :paypal_confirm
|
||||
# end
|
||||
# when :paypal_confirmed
|
||||
# info = YAML.load(@registration.payment_info)
|
||||
# @amount = nil
|
||||
# status = nil
|
||||
# if ENV['RAILS_ENV'] == 'test'
|
||||
# status = info[:status]
|
||||
# @amount = info[:amount]
|
||||
# else
|
||||
# paypal = PayPal!.checkout!(info[:token], info[:payer_id], PayPalRequest(info[:amount]))
|
||||
# status = paypal.payment_info.first.payment_status
|
||||
# @amount = paypal.payment_info.first.amount.total
|
||||
# end
|
||||
# if status == 'Completed'
|
||||
# @registration.registration_fees_paid = @amount
|
||||
# @registration.save!
|
||||
# @register_template = :done
|
||||
# else
|
||||
# @register_template = :payment
|
||||
# end
|
||||
# when :paypal_cancel
|
||||
# if @registration
|
||||
# @registration.payment_confirmation_token = nil
|
||||
# @registration.save
|
||||
# @register_template = :payment
|
||||
# end
|
||||
# when :register
|
||||
# @register_template = :questions
|
||||
# end
|
||||
|
||||
if @register_template == :payment && !@this_conference.paypal_username
|
||||
@register_template = :done
|
||||
end
|
||||
# if @register_template == :payment && !@this_conference.paypal_username
|
||||
# @register_template = :done
|
||||
# end
|
||||
|
||||
# don't let the user edit registration if registration is closed
|
||||
if !@conference.registration_open && @register_template == :questions
|
||||
@register_template = :done
|
||||
end
|
||||
# # don't let the user edit registration if registration is closed
|
||||
# if !@conference.registration_open && @register_template == :questions
|
||||
# @register_template = :done
|
||||
# end
|
||||
|
||||
# prepare data for the next view
|
||||
case @register_template
|
||||
@ -680,12 +749,17 @@ class ConferencesController < ApplicationController
|
||||
@languages = JSON.parse(@registration.languages).map &:to_sym
|
||||
end
|
||||
when :workshops
|
||||
@my_workshops = [1,2,3,4].map { |i|
|
||||
{
|
||||
:title => (Forgery::LoremIpsum.sentence({:random => true}).gsub(/\.$/, '').titlecase),
|
||||
:info => (Forgery::LoremIpsum.sentences(rand(1...5), {:random => true}))
|
||||
}
|
||||
}
|
||||
@page_title = 'articles.conference_registration.headings.Workshops'
|
||||
@workshops = Workshop.where(conference_id: @this_conference.id)
|
||||
@my_workshops = Workshop.joins(:workshop_facilitators).where(
|
||||
workshop_facilitators: { user_id: current_user.id },
|
||||
conference_id: @this_conference.id
|
||||
)
|
||||
@workshops_in_need = Workshop.where(conference_id: @this_conference.id, needs_facilitators: true)
|
||||
when :contact_info
|
||||
@page_title = 'articles.conference_registration.headings.Contact_Info'
|
||||
when :policy
|
||||
@page_title = 'articles.conference_registration.headings.Policy_Agreement'
|
||||
when :done
|
||||
@amount = ((@registration.registration_fees_paid || 0) * 100).to_i.to_s.gsub(/^(.*)(\d\d)$/, '\1.\2')
|
||||
end
|
||||
@ -709,7 +783,7 @@ class ConferencesController < ApplicationController
|
||||
session[:registration_step] = 'confirm'
|
||||
redirect_to action: 'register'
|
||||
else
|
||||
do_404
|
||||
return do_404
|
||||
end
|
||||
end
|
||||
|
||||
@ -734,7 +808,7 @@ class ConferencesController < ApplicationController
|
||||
redirect_to action: 'register'
|
||||
end
|
||||
else
|
||||
do_404
|
||||
return do_404
|
||||
end
|
||||
end
|
||||
|
||||
@ -754,7 +828,7 @@ class ConferencesController < ApplicationController
|
||||
# session[:registration_step] = 'paypal-confirmed'
|
||||
# redirect_to action: 'register'
|
||||
# else
|
||||
# do_404
|
||||
# return do_404
|
||||
# end
|
||||
# end
|
||||
|
||||
@ -825,32 +899,67 @@ class ConferencesController < ApplicationController
|
||||
set_conference
|
||||
set_conference_registration
|
||||
@workshop = Workshop.find_by_id_and_conference_id(params[:workshop_id], @this_conference.id)
|
||||
do_404 unless @workshop
|
||||
return do_404 unless @workshop
|
||||
|
||||
@translations_available_for_editing = []
|
||||
I18n.backend.enabled_locales.each do |locale|
|
||||
@translations_available_for_editing << locale if @workshop.can_translate?(current_user, locale)
|
||||
end
|
||||
@page_title = 'page_titles.conferences.View_Workshop'
|
||||
|
||||
render 'workshops/show'
|
||||
end
|
||||
|
||||
def create_workshop
|
||||
set_conference
|
||||
set_conference_registration
|
||||
@languages = []
|
||||
@workshop = Workshop.new
|
||||
@languages = [I18n.locale.to_sym]
|
||||
@needs = []
|
||||
@page_title = 'page_titles.conferences.Create_Workshop'
|
||||
render 'workshops/new'
|
||||
end
|
||||
|
||||
def translate_workshop
|
||||
@is_translating = true
|
||||
@translation = params[:locale]
|
||||
@page_title = 'page_titles.conferences.Translate_Workshop'
|
||||
@page_title_vars = { language: view_context.language_name(@translation) }
|
||||
|
||||
edit_workshop
|
||||
end
|
||||
|
||||
def edit_workshop
|
||||
set_conference
|
||||
set_conference_registration
|
||||
@workshop = Workshop.find_by_id_and_conference_id(params[:workshop_id], @this_conference.id)
|
||||
do_404 unless @workshop
|
||||
|
||||
return do_404 unless @workshop.present?
|
||||
|
||||
@page_title ||= 'page_titles.conferences.Edit_Workshop'
|
||||
|
||||
@can_edit = @workshop.can_edit?(current_user)
|
||||
do_403 unless @can_edit || @workshop.can_translate?(current_user, I18n.locale)
|
||||
@title = @workshop.title
|
||||
@info = @workshop.info
|
||||
|
||||
@is_translating ||= false
|
||||
if @is_translating
|
||||
return do_404 if @translation.to_s == @workshop.locale.to_s || !I18n.backend.enabled_locales.include?(@translation.to_s)
|
||||
return do_403 unless @workshop.can_translate?(current_user, @translation)
|
||||
|
||||
@title = @workshop._title(@translation)
|
||||
@info = @workshop._info(@translation)
|
||||
else
|
||||
return do_403 unless @can_edit
|
||||
|
||||
@title = @workshop.title
|
||||
@info = @workshop.info
|
||||
end
|
||||
|
||||
@needs = JSON.parse(@workshop.needs || '[]').map &:to_sym
|
||||
@languages = JSON.parse(@workshop.languages || '[]').map &:to_sym
|
||||
@space = @workshop.space.to_sym if @workshop.space
|
||||
@theme = @workshop.theme.to_sym if @workshop.theme
|
||||
@notes = @workshop.notes
|
||||
|
||||
render 'workshops/new'
|
||||
end
|
||||
|
||||
@ -859,15 +968,8 @@ class ConferencesController < ApplicationController
|
||||
set_conference_registration
|
||||
@workshop = Workshop.find_by_id_and_conference_id(params[:workshop_id], @this_conference.id)
|
||||
|
||||
if !@workshop
|
||||
do_404
|
||||
return
|
||||
end
|
||||
|
||||
if !@workshop.can_delete?(current_user)
|
||||
do_403
|
||||
return
|
||||
end
|
||||
return do_404 unless @workshop.present?
|
||||
return do_403 unless @workshop.can_delete?(current_user)
|
||||
|
||||
if request.post?
|
||||
if params[:button] == 'confirm'
|
||||
@ -876,11 +978,9 @@ class ConferencesController < ApplicationController
|
||||
@workshop.destroy
|
||||
end
|
||||
|
||||
redirect_to workshops_url
|
||||
return
|
||||
return redirect_to workshops_url
|
||||
end
|
||||
redirect_to edit_workshop_url(@this_conference.slug, @workshop.id)
|
||||
return
|
||||
return redirect_to edit_workshop_url(@this_conference.slug, @workshop.id)
|
||||
end
|
||||
|
||||
render 'workshops/delete'
|
||||
@ -890,30 +990,57 @@ class ConferencesController < ApplicationController
|
||||
set_conference
|
||||
set_conference_registration
|
||||
|
||||
if params[:workshop_id]
|
||||
if params[:button].to_sym != :save
|
||||
if params[:workshop_id].present?
|
||||
return redirect_to view_workshop_url(@this_conference.slug, params[:workshop_id])
|
||||
end
|
||||
return redirect_to register_step_path(@this_conference.slug, 'workshops')
|
||||
end
|
||||
|
||||
if params[:workshop_id].present?
|
||||
workshop = Workshop.find(params[:workshop_id])
|
||||
do_404 unless workshop
|
||||
return do_404 unless workshop.present?
|
||||
can_edit = workshop.can_edit?(current_user)
|
||||
else
|
||||
workshop = Workshop.new(:conference_id => @this_conference.id)
|
||||
workshop.workshop_facilitators = [WorkshopFacilitator.new(:user_id => current_user.id, :role => :creator)]
|
||||
can_edit = true
|
||||
end
|
||||
|
||||
can_edit = workshop.can_edit?(current_user)
|
||||
do_403 unless can_edit || workshop.can_translate?(current_user, I18n.locale)
|
||||
title = params[:title]
|
||||
info = params[:info].gsub(/^\s*(.*?)\s*$/, '\1')
|
||||
|
||||
workshop.title = params[:title]
|
||||
workshop.info = params[:info]
|
||||
if params[:translation].present? && workshop.can_translate?(current_user, params[:translation])
|
||||
old_title = workshop._title(params[:translation])
|
||||
old_info = workshop._info(params[:translation])
|
||||
|
||||
if can_edit
|
||||
# dont allow translators to edit these fields
|
||||
workshop.languages = (params[:languages] || {}).keys.to_json
|
||||
workshop.needs = (params[:needs] || {}).keys.to_json
|
||||
workshop.theme = params[:theme] == 'other' ? params[:other_theme] : params[:theme]
|
||||
workshop.space = params[:space]
|
||||
workshop.notes = params[:notes]
|
||||
do_save = false
|
||||
|
||||
unless title == old_title
|
||||
workshop.set_column_for_locale(:title, params[:translation], title, current_user.id)
|
||||
do_save = true
|
||||
end
|
||||
unless info == old_info
|
||||
workshop.set_column_for_locale(:info, params[:translation], info, current_user.id)
|
||||
do_save = true
|
||||
end
|
||||
|
||||
# only save if the text has changed, if we want to make sure only to update the translator id if necessary
|
||||
workshop.save_translations if do_save
|
||||
elsif can_edit
|
||||
workshop.title = title
|
||||
workshop.info = info
|
||||
workshop.languages = (params[:languages] || {}).keys.to_json
|
||||
workshop.needs = (params[:needs] || {}).keys.to_json
|
||||
workshop.theme = params[:theme] == 'other' ? params[:other_theme] : params[:theme]
|
||||
workshop.space = params[:space]
|
||||
workshop.notes = params[:notes]
|
||||
workshop.needs_facilitators = params[:needs_facilitators].present?
|
||||
workshop.save
|
||||
else
|
||||
return do_403
|
||||
end
|
||||
|
||||
workshop.save
|
||||
redirect_to view_workshop_url(@this_conference.slug, workshop.id)
|
||||
end
|
||||
|
||||
@ -921,7 +1048,7 @@ class ConferencesController < ApplicationController
|
||||
set_conference
|
||||
set_conference_registration
|
||||
workshop = Workshop.find_by_id_and_conference_id(params[:workshop_id], @this_conference.id)
|
||||
do_404 unless workshop
|
||||
return do_404 unless workshop
|
||||
|
||||
# save the current state
|
||||
interested = workshop.interested? current_user
|
||||
@ -939,8 +1066,8 @@ class ConferencesController < ApplicationController
|
||||
set_conference
|
||||
set_conference_registration
|
||||
@workshop = Workshop.find_by_id_and_conference_id(params[:workshop_id], @this_conference.id)
|
||||
do_404 unless @workshop
|
||||
do_403 if @workshop.facilitator?(current_user) || !current_user
|
||||
return do_404 unless @workshop
|
||||
return do_403 if @workshop.facilitator?(current_user) || !current_user
|
||||
|
||||
render 'workshops/facilitate'
|
||||
end
|
||||
@ -949,8 +1076,8 @@ class ConferencesController < ApplicationController
|
||||
set_conference
|
||||
set_conference_registration
|
||||
workshop = Workshop.find_by_id_and_conference_id(params[:workshop_id], @this_conference.id)
|
||||
do_404 unless workshop
|
||||
do_403 if workshop.facilitator?(current_user) || !current_user
|
||||
return do_404 unless workshop
|
||||
return do_403 if workshop.facilitator?(current_user) || !current_user
|
||||
|
||||
# create the request by making the user a facilitator but making their role 'requested'
|
||||
WorkshopFacilitator.create(user_id: current_user.id, workshop_id: workshop.id, role: :requested)
|
||||
@ -968,8 +1095,8 @@ class ConferencesController < ApplicationController
|
||||
set_conference
|
||||
set_conference_registration
|
||||
@workshop = Workshop.find_by_id_and_conference_id(params[:workshop_id], @this_conference.id)
|
||||
do_404 unless @workshop
|
||||
do_403 unless @workshop.requested_collaborator?(current_user)
|
||||
return do_404 unless @workshop
|
||||
return do_403 unless @workshop.requested_collaborator?(current_user)
|
||||
|
||||
render 'workshops/facilitate_request_sent'
|
||||
end
|
||||
@ -978,7 +1105,7 @@ class ConferencesController < ApplicationController
|
||||
set_conference
|
||||
set_conference_registration
|
||||
workshop = Workshop.find_by_id_and_conference_id(params[:workshop_id], @this_conference.id)
|
||||
do_404 unless workshop && current_user
|
||||
return do_404 unless workshop && current_user
|
||||
|
||||
user_id = params[:user_id].to_i
|
||||
action = params[:approve_or_deny].to_sym
|
||||
@ -1019,7 +1146,7 @@ class ConferencesController < ApplicationController
|
||||
end
|
||||
end
|
||||
|
||||
do_403
|
||||
return do_403
|
||||
end
|
||||
|
||||
def add_workshop_facilitator
|
||||
@ -1029,7 +1156,7 @@ class ConferencesController < ApplicationController
|
||||
set_conference_registration
|
||||
workshop = Workshop.find_by_id_and_conference_id(params[:workshop_id], @this_conference.id)
|
||||
|
||||
do_404 unless workshop && current_user
|
||||
return do_404 unless workshop && current_user
|
||||
|
||||
unless workshop.facilitator?(user)
|
||||
WorkshopFacilitator.create(user_id: user.id, workshop_id: workshop.id, role: :collaborator)
|
||||
@ -1046,7 +1173,7 @@ class ConferencesController < ApplicationController
|
||||
|
||||
def schedule
|
||||
set_conference
|
||||
do_404 unless @this_conference.workshop_schedule_published || @this_conference.host?(current_user)
|
||||
return do_404 unless @this_conference.workshop_schedule_published || @this_conference.host?(current_user)
|
||||
|
||||
@events = Event.where(:conference_id => @this_conference.id)
|
||||
@locations = EventLocation.where(:conference_id => @this_conference.id)
|
||||
@ -1057,7 +1184,7 @@ class ConferencesController < ApplicationController
|
||||
def edit_schedule
|
||||
set_conference
|
||||
set_conference_registration
|
||||
do_404 unless @this_conference.host?(current_user)
|
||||
return do_404 unless @this_conference.host?(current_user)
|
||||
|
||||
@workshops = Workshop.where(:conference_id => @this_conference.id)
|
||||
@events = Event.where(:conference_id => @this_conference.id)
|
||||
@ -1126,7 +1253,7 @@ class ConferencesController < ApplicationController
|
||||
|
||||
def save_schedule
|
||||
set_conference
|
||||
do_404 unless @this_conference.host?(current_user)
|
||||
return do_404 unless @this_conference.host?(current_user)
|
||||
|
||||
@days = Array.new
|
||||
start_day = @this_conference.start_date.strftime('%u').to_i
|
||||
@ -1164,7 +1291,6 @@ class ConferencesController < ApplicationController
|
||||
session[:workshops][id] = {
|
||||
:start_time => @workshops[i].start_time,
|
||||
:end_time => @workshops[i].end_time,
|
||||
:end_time => @workshops[i].end_time,
|
||||
:event_location_id => @workshops[i].event_location_id
|
||||
}
|
||||
end
|
||||
@ -1189,7 +1315,6 @@ class ConferencesController < ApplicationController
|
||||
session[:events][id] = {
|
||||
:start_time => @events[i].start_time,
|
||||
:end_time => @events[i].end_time,
|
||||
:end_time => @events[i].end_time,
|
||||
:event_location_id => @events[i].event_location_id
|
||||
}
|
||||
end
|
||||
@ -1228,7 +1353,7 @@ class ConferencesController < ApplicationController
|
||||
def add_event
|
||||
set_conference
|
||||
set_conference_registration
|
||||
do_404 unless @this_conference.host?(current_user)
|
||||
return do_404 unless @this_conference.host?(current_user)
|
||||
|
||||
render 'events/edit'
|
||||
end
|
||||
@ -1236,22 +1361,22 @@ class ConferencesController < ApplicationController
|
||||
def edit_event
|
||||
set_conference
|
||||
set_conference_registration
|
||||
do_404 unless @this_conference.host?(current_user)
|
||||
return do_404 unless @this_conference.host?(current_user)
|
||||
|
||||
@event = Event.find(params[:id])
|
||||
do_403 unless @event.conference_id == @this_conference.id
|
||||
return do_403 unless @event.conference_id == @this_conference.id
|
||||
|
||||
render 'events/edit'
|
||||
end
|
||||
|
||||
def save_event
|
||||
set_conference
|
||||
do_404 unless @this_conference.host?(current_user)
|
||||
return do_404 unless @this_conference.host?(current_user)
|
||||
|
||||
|
||||
if params[:event_id]
|
||||
event = Event.find(params[:event_id])
|
||||
do_403 unless event.conference_id == @this_conference.id
|
||||
return do_403 unless event.conference_id == @this_conference.id
|
||||
else
|
||||
event = Event.new(:conference_id => @this_conference.id)
|
||||
end
|
||||
@ -1268,7 +1393,7 @@ class ConferencesController < ApplicationController
|
||||
def add_location
|
||||
set_conference
|
||||
set_conference_registration
|
||||
do_404 unless @this_conference.host?(current_user)
|
||||
return do_404 unless @this_conference.host?(current_user)
|
||||
|
||||
render 'event_locations/edit'
|
||||
end
|
||||
@ -1276,10 +1401,10 @@ class ConferencesController < ApplicationController
|
||||
def edit_location
|
||||
set_conference
|
||||
set_conference_registration
|
||||
do_404 unless @this_conference.host?(current_user)
|
||||
return do_404 unless @this_conference.host?(current_user)
|
||||
|
||||
@location = EventLocation.find(params[:id])
|
||||
do_403 unless @location.conference_id == @this_conference.id
|
||||
return do_403 unless @location.conference_id == @this_conference.id
|
||||
|
||||
@amenities = JSON.parse(@location.amenities || '[]').map &:to_sym
|
||||
|
||||
@ -1288,12 +1413,12 @@ class ConferencesController < ApplicationController
|
||||
|
||||
def save_location
|
||||
set_conference
|
||||
do_404 unless @this_conference.host?(current_user)
|
||||
return do_404 unless @this_conference.host?(current_user)
|
||||
|
||||
|
||||
if params[:location_id]
|
||||
location = EventLocation.find(params[:location_id])
|
||||
do_403 unless location.conference_id == @this_conference.id
|
||||
return do_403 unless location.conference_id == @this_conference.id
|
||||
else
|
||||
location = EventLocation.new(:conference_id => @this_conference.id)
|
||||
end
|
||||
@ -1313,6 +1438,61 @@ class ConferencesController < ApplicationController
|
||||
# redirect_to conferences_url, notice: 'Conference was successfully destroyed.'
|
||||
#end
|
||||
|
||||
helper_method :registration_steps
|
||||
helper_method :current_registration_steps
|
||||
helper_method :registration_complete?
|
||||
|
||||
def registration_steps(conference = nil)
|
||||
conference ||= @this_conference || @conference
|
||||
{
|
||||
pre: [:policy, :contact_info, :workshops],
|
||||
open: [:policy, :contact_info, :questions, :payment, :workshops]
|
||||
}[conference.registration_status]
|
||||
end
|
||||
|
||||
def required_steps(conference = nil)
|
||||
# return the intersection of current steps and reuired steps
|
||||
registration_steps(conference || @this_conference || @conference) & # current steps
|
||||
[:policy, :contact_info, :questions] # all required steps
|
||||
end
|
||||
|
||||
def registration_complete?(registration = @registration)
|
||||
completed_steps = registration.steps_completed || []
|
||||
required_steps(registration.conference).each do | step |
|
||||
return false unless completed_steps.include?(step.to_s)
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
def current_registration_steps(registration = @registration)
|
||||
return nil unless registration.present?
|
||||
|
||||
steps = registration_steps(registration.conference)
|
||||
current_steps = []
|
||||
disable_steps = false
|
||||
completed_steps = registration.steps_completed || []
|
||||
steps.each do | step |
|
||||
# disable the step if we've already found an incomplete step
|
||||
enabled = !disable_steps
|
||||
# record whether or not we've found an incomplete step
|
||||
disable_steps ||= !completed_steps.include?(step.to_s)
|
||||
|
||||
current_steps << {
|
||||
name: step,
|
||||
enabled: enabled
|
||||
}
|
||||
end
|
||||
return current_steps
|
||||
end
|
||||
|
||||
def current_step(registration = @registration)
|
||||
completed_steps = registration.steps_completed || []
|
||||
(registration_steps(registration.conference) || []).each do | step |
|
||||
return step unless completed_steps.include?(step.to_s)
|
||||
end
|
||||
return registration_steps(registration.conference).last
|
||||
end
|
||||
|
||||
private
|
||||
# Use callbacks to share common setup or constraints between actions.
|
||||
def set_conference
|
||||
@ -1321,24 +1501,20 @@ class ConferencesController < ApplicationController
|
||||
|
||||
def set_conference_registration
|
||||
@registration = logged_in? ? ConferenceRegistration.find_by(:user_id => current_user.id, :conference_id => @this_conference.id) : nil
|
||||
@is_host = @this_conference.host?(current_user)
|
||||
if @registration || @is_host
|
||||
@submenu = {
|
||||
register_path(@this_conference.slug) => 'registration.Registration',
|
||||
workshops_path(@this_conference.slug) => 'registration.Workshops'
|
||||
}
|
||||
@submenu[schedule_path(@this_conference.slug)] = 'registration.Schedule' if @this_conference.workshop_schedule_published || @is_host
|
||||
if @is_host
|
||||
@submenu[edit_conference_path(@this_conference.slug)] = 'registration.Edit'
|
||||
@submenu[stats_path(@this_conference.slug)] = 'registration.Stats'
|
||||
@submenu[broadcast_path(@this_conference.slug)] = 'registration.Broadcast'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def set_or_create_conference_registration
|
||||
set_conference_registration
|
||||
@registration ||= ConferenceRegistration.new(
|
||||
conference: @this_conference,
|
||||
user_id: current_user.id,
|
||||
steps_completed: []
|
||||
)
|
||||
end
|
||||
|
||||
# Only allow a trusted parameter "white list" through.
|
||||
def conference_params
|
||||
params.require(:conference).permit(:title, :slug, :start_date, :end_date, :info, :poster, :cover, :workshop_schedule_published, :registration_open, :meals_provided, :meal_info, :travel_info, :conference_type_id, conference_types: [:id])
|
||||
params.require(:conference).permit(:title, :slug, :start_date, :end_date, :info, :poster, :cover, :workshop_schedule_published, :registration_status, :meals_provided, :meal_info, :travel_info, :conference_type_id, conference_types: [:id])
|
||||
end
|
||||
|
||||
def update_field_position(field_id, position)
|
||||
|
@ -4,24 +4,37 @@ class OauthsController < ApplicationController
|
||||
# sends the user on a trip to the provider,
|
||||
# and after authorizing there back to the callback url.
|
||||
def oauth
|
||||
session[:oauth_last_url] = request.referer
|
||||
login_at(auth_params[:provider])
|
||||
end
|
||||
|
||||
def callback
|
||||
provider = auth_params[:provider]
|
||||
if @user = login_from(provider)
|
||||
redirect_to root_path, :notice => "Logged in with #{provider.titleize}!"
|
||||
else
|
||||
begin
|
||||
@user = create_from(auth_params[:provider])
|
||||
|
||||
reset_session
|
||||
auto_login(@user)
|
||||
redirect_to root_path, :notice => "Signed up with #{provider.titleize}!"
|
||||
rescue
|
||||
redirect_to root_path, :alert => "Failed to login with #{provider.titleize}!"
|
||||
end
|
||||
user_info = (sorcery_fetch_user_hash auth_params[:provider] || {})[:user_info]
|
||||
user = User.find_by_email(user_info['email'])
|
||||
|
||||
# create the user if the email is not recognized
|
||||
unless user
|
||||
user = User.new(email: user_info['email'], firstname: user_info['name'])
|
||||
user.save!
|
||||
end
|
||||
|
||||
# log in the user
|
||||
auto_login(user) if user
|
||||
|
||||
redirect_to (session[:oauth_last_url] || home_path)
|
||||
#, :notice => "Logged in with #{provider.titleize}!"
|
||||
# if @user = login_from(provider)
|
||||
# else
|
||||
# begin
|
||||
# @user = create_from(auth_params[:provider])
|
||||
|
||||
# reset_session
|
||||
# auto_login(@user)
|
||||
# redirect_to redirect_url, :notice => "Signed up with #{provider.titleize}!"
|
||||
# rescue
|
||||
# redirect_to redirect_url, :alert => "Failed to login with #{provider.titleize}!"
|
||||
# end
|
||||
# end
|
||||
end
|
||||
|
||||
private
|
||||
|
@ -98,6 +98,35 @@ module ApplicationHelper
|
||||
content_for(:banner) { ('<div class="row"><h1>' + banner_title.to_s + '</h1></div>').html_safe }
|
||||
end
|
||||
|
||||
def add_stylesheet(sheet)
|
||||
@stylesheets ||= []
|
||||
@stylesheets << sheet
|
||||
end
|
||||
|
||||
def stylesheets
|
||||
html = ''
|
||||
Rack::MiniProfiler.step('inject_css') do
|
||||
html += inject_css!
|
||||
end
|
||||
(@stylesheets || []).each do |css|
|
||||
Rack::MiniProfiler.step("inject_css #{css}") do
|
||||
html += inject_css! css.to_s
|
||||
end
|
||||
end
|
||||
html += stylesheet_link_tag 'i18n-debug' if request.params['i18nDebug']
|
||||
return html.html_safe
|
||||
end
|
||||
|
||||
def add_inline_script(script)
|
||||
@_inline_scripts ||= []
|
||||
@_inline_scripts << Rails.application.assets.find_asset("#{script.to_s}.js").to_s
|
||||
end
|
||||
|
||||
def inline_scripts
|
||||
return '' unless @_inline_scripts.present?
|
||||
"<script>#{@_inline_scripts.join("\n")}</script>".html_safe
|
||||
end
|
||||
|
||||
def banner_attribution
|
||||
if @@banner_image && @@banner_attribution_details
|
||||
src = @@banner_attribution_details[:src]
|
||||
@ -185,12 +214,17 @@ module ApplicationHelper
|
||||
false
|
||||
end
|
||||
|
||||
def off_screen(text)
|
||||
"<span class=\"screen-reader-text\">#{text}</span>".html_safe
|
||||
end
|
||||
|
||||
def url_for_locale(locale)
|
||||
url_for(params
|
||||
.merge({action: (params[:_original_action] || params[:action])}
|
||||
.merge(url_params(locale)))
|
||||
.delete(:_original_action)
|
||||
)
|
||||
new_params = params.merge({action: (params[:_original_action] || params[:action])})
|
||||
new_params.delete(:_original_action)
|
||||
|
||||
return url_for(new_params.merge({lang: locale.to_s})) if Rails.env.development? || Rails.env.test?
|
||||
return "https://preview-#{locale.to_s}.bikebike.org#{url_for(new_params)}" if Rails.env.preview?
|
||||
"https://#{locale.to_s}.bikebike.org#{url_for(new_params)}"
|
||||
end
|
||||
|
||||
def registration_steps(conference = @conference)
|
||||
@ -550,6 +584,18 @@ module ApplicationHelper
|
||||
country = location.country
|
||||
region = location.territory
|
||||
city = location.city
|
||||
elsif location.data.present? && location.data['address_components'].present?
|
||||
component_map = {
|
||||
'locality' => :city,
|
||||
'administrative_area_level_1' => :region,
|
||||
'country' => :country
|
||||
}
|
||||
location.data['address_components'].each do | component |
|
||||
types = component['types']
|
||||
country = component['short_name'] if types.include? 'country'
|
||||
region = component['short_name'] if types.include? 'administrative_area_level_1'
|
||||
city = component['long_name'] if types.include? 'locality'
|
||||
end
|
||||
else
|
||||
country = location.data['country_code']
|
||||
region = location.data['region_code']
|
||||
@ -565,6 +611,14 @@ module ApplicationHelper
|
||||
return hash.length > 1 ? _("geography.formats.#{hash.keys.join('_')}", vars: hash) : hash.values.first
|
||||
end
|
||||
|
||||
def show_errors(field)
|
||||
return '' unless @errors && @errors[field].present?
|
||||
|
||||
error_txt = _"errors.messages.fields.#{field.to_s}.#{@errors[field]}", :s
|
||||
|
||||
"<div class=\"field-error\">#{error_txt}</div>".html_safe
|
||||
end
|
||||
|
||||
def nav_link(link, title = nil, class_name = nil)
|
||||
if title.nil? && link.is_a?(Symbol)
|
||||
title = link
|
||||
@ -572,12 +626,13 @@ module ApplicationHelper
|
||||
end
|
||||
if class_name.nil? && title.is_a?(Symbol)
|
||||
class_name = title
|
||||
title = _"page_titles.#{title.to_s.titlecase}"
|
||||
end
|
||||
title = _"page_titles.#{title.to_s.titlecase.gsub(/\s/, '_')}"
|
||||
classes = []
|
||||
classes << class_name if class_name.present?
|
||||
classes << 'current' if current_page?(link.gsub(/(^.*)\/$/, '\1'))
|
||||
link_to title.html_safe, link, :class => classes
|
||||
classes << "strlen-#{strip_tags(title).length}"
|
||||
classes << 'current' if request.fullpath.start_with?(link.gsub(/^(.*?)\/$/, '\1'))
|
||||
link_to "<span class=\"title\">#{title}</span>".html_safe, link, :class => classes
|
||||
end
|
||||
|
||||
def date(date, format = :long)
|
||||
|
@ -1,3 +1,5 @@
|
||||
require 'diffy'
|
||||
|
||||
class UserMailer < ActionMailer::Base
|
||||
add_template_helper(ApplicationHelper)
|
||||
include LinguaFrancaHelper
|
||||
@ -26,10 +28,6 @@ class UserMailer < ActionMailer::Base
|
||||
mail to: "to@example.org"
|
||||
end
|
||||
|
||||
def test_email
|
||||
mail to: 'goodgodwin@hotmail.com', subject: 'This is a test', from: 'info@preview.bikebike.org'
|
||||
end
|
||||
|
||||
def conference_registration_email(conference, data, conference_registration)
|
||||
@data = data
|
||||
@conference = conference
|
||||
@ -49,8 +47,8 @@ class UserMailer < ActionMailer::Base
|
||||
def email_confirmation(confirmation)
|
||||
@confirmation = confirmation
|
||||
@host = UserMailer.default_url_options[:host]
|
||||
mail to: confirmation.user.email,
|
||||
subject: (_'email.subject.confirm_email','Please confirm your email address')
|
||||
@subject = _'email.subject.confirm_email','Please confirm your email address'
|
||||
mail to: confirmation.user.email, subject: @subject
|
||||
end
|
||||
|
||||
def registration_confirmation(registration)
|
||||
@ -58,19 +56,27 @@ class UserMailer < ActionMailer::Base
|
||||
@registration = registration
|
||||
@conference = Conference.find(@registration.conference_id)
|
||||
@user = User.find(@registration.user_id)
|
||||
mail to: @user.email,
|
||||
subject: _('email.subject.registration_confirmed',
|
||||
"Thank you for registering for #{@conference.title}",
|
||||
:vars => {:conference_title => @conference.title})
|
||||
@subject = @conference.registration_status.to_sym == :pre ?
|
||||
_(
|
||||
'email.subject.pre_registration_confirmed',
|
||||
"Thank you for pre-registering for #{@conference.title}",
|
||||
:vars => {:conference_title => @conference.title}
|
||||
) : _(
|
||||
'email.subject.registration_confirmed',
|
||||
"Thank you for registering for #{@conference.title}",
|
||||
:vars => {:conference_title => @conference.title}
|
||||
)
|
||||
mail to: @user.email, subject: @subject
|
||||
end
|
||||
|
||||
def broadcast(host, subject, content, user, conference)
|
||||
@host = host
|
||||
@content = content
|
||||
@banner = nil#(@host || 'http://localhost/') + (conference ? (conference.poster.preview.url || '') : image_url('logo.png'))
|
||||
@subject = "[#{conference ? conference.title : 'Bike!Bike!'}] #{subject}"
|
||||
if user && user.email
|
||||
email = user.email
|
||||
mail to: email, subject: "[#{conference ? conference.title : 'Bike!Bike!'}] #{subject}"
|
||||
mail to: email, subject: @subject
|
||||
end
|
||||
end
|
||||
|
||||
@ -84,11 +90,10 @@ class UserMailer < ActionMailer::Base
|
||||
end
|
||||
@message = message
|
||||
@conference = Conference.find(@workshop.conference_id)
|
||||
mail to: addresses,
|
||||
from: @requester.email,
|
||||
subject: _('email.subject.workshop_facilitator_request',
|
||||
"Request to facilitate #{@workshop.title} from #{@requester.firstname}",
|
||||
@subject = _('email.subject.workshop_facilitator_request',
|
||||
"Request to facilitate #{@workshop.title} from #{@requester.name}",
|
||||
:vars => {:workshop_title => @workshop.title, :requester_name => @requester.firstname})
|
||||
mail to: addresses, from: @requester.email, subject: @subject
|
||||
end
|
||||
|
||||
def workshop_facilitator_request_approved(workshop, user)
|
||||
@ -96,10 +101,10 @@ class UserMailer < ActionMailer::Base
|
||||
@workshop = workshop
|
||||
@conference = Conference.find(@workshop.conference_id)
|
||||
@user = user
|
||||
mail to: user.email,
|
||||
subject: (_'email.subject.workshop_request_approved',
|
||||
"You have been added as a facilitator of #{@workshop.title}",
|
||||
:vars => {:workshop_title => @workshop.title})
|
||||
@subject = (_'email.subject.workshop_request_approved',
|
||||
"You have been added as a facilitator of #{@workshop.title}",
|
||||
:vars => {:workshop_title => @workshop.title})
|
||||
mail to: user.email, subject: @subject
|
||||
end
|
||||
|
||||
def workshop_facilitator_request_denied(workshop, user)
|
||||
@ -107,10 +112,56 @@ class UserMailer < ActionMailer::Base
|
||||
@workshop = workshop
|
||||
@conference = Conference.find(@workshop.conference_id)
|
||||
@user = user
|
||||
mail to: user.email,
|
||||
subject: (_'email.subject.workshop_request_denied',
|
||||
"Your request to facilitate #{@workshop.title} has been denied",
|
||||
:vars => {:workshop_title => @workshop.title})
|
||||
@subject = (_'email.subject.workshop_request_denied',
|
||||
"Your request to facilitate #{@workshop.title} has been denied",
|
||||
:vars => {:workshop_title => @workshop.title})
|
||||
mail to: user.email, subject: @subject
|
||||
end
|
||||
|
||||
def workshop_translated(workshop, data, locale, user, translator)
|
||||
@host = UserMailer.default_url_options[:host]
|
||||
@workshop = workshop
|
||||
@data = data
|
||||
@locale = locale
|
||||
@locale_name = language_name(locale)
|
||||
@user = user
|
||||
@translator = translator
|
||||
@subject = (_'email.subject.workshop_translated',
|
||||
"The #{@locale_name} translation for #{@workshop.title} has been modified",
|
||||
vars: {language: @language_name, workshop_title: @workshop.title})
|
||||
@data.each do |field, values|
|
||||
diff = Diffy::Diff.new(values[:old], values[:new])
|
||||
@data[field][:diff] = {
|
||||
text: diff.to_s(:text),
|
||||
html: diff.to_s(:html)
|
||||
}
|
||||
end
|
||||
|
||||
@wrapper_id = :full_width
|
||||
|
||||
mail to: user.email, subject: @subject
|
||||
end
|
||||
|
||||
def workshop_original_content_changed(workshop, data, user, translator)
|
||||
@host = UserMailer.default_url_options[:host]
|
||||
@workshop = workshop
|
||||
@data = data
|
||||
@user = user
|
||||
@translator = translator
|
||||
@subject = (_'email.subject.workshop_original_content_changed',
|
||||
"Original content for #{@workshop.title} has been modified",
|
||||
vars: {workshop_title: @workshop.title})
|
||||
@data.each do |field, values|
|
||||
diff = Diffy::Diff.new(values[:old], values[:new])
|
||||
@data[field][:diff] = {
|
||||
text: diff.to_s(:text),
|
||||
html: diff.to_s(:html)
|
||||
}
|
||||
end
|
||||
|
||||
@wrapper_id = :full_width
|
||||
|
||||
mail to: user.email, subject: @subject
|
||||
end
|
||||
|
||||
end
|
||||
|
@ -42,8 +42,23 @@ class Conference < ActiveRecord::Base
|
||||
|
||||
def registered?(user)
|
||||
registration = ConferenceRegistration.find_by(:user_id => user.id, :conference_id => id)
|
||||
return false unless registration
|
||||
return registration.is_attending
|
||||
return registration ? registration.is_attending : false
|
||||
end
|
||||
|
||||
def registration_exists?(user)
|
||||
ConferenceRegistration.find_by(:user_id => user.id, :conference_id => id).present?
|
||||
end
|
||||
|
||||
def registration_open
|
||||
registration_status == :open
|
||||
end
|
||||
|
||||
def registration_status
|
||||
read_attribute(:registration_status).to_sym
|
||||
end
|
||||
|
||||
def registration_status=(new_registration_status)
|
||||
write_attribute :registration_status, new_registration_status.to_s
|
||||
end
|
||||
|
||||
end
|
||||
|
@ -1,5 +1,11 @@
|
||||
class ConferenceRegistration < ActiveRecord::Base
|
||||
has_many :conference_registration_responses
|
||||
|
||||
AttendingOptions = [:yes, :no]
|
||||
end
|
||||
class ConferenceRegistration < ActiveRecord::Base
|
||||
belongs_to :conference
|
||||
belongs_to :user
|
||||
has_many :conference_registration_responses
|
||||
|
||||
AttendingOptions = [:yes, :no]
|
||||
|
||||
def languages
|
||||
user.languages
|
||||
end
|
||||
end
|
||||
|
@ -21,8 +21,18 @@ class User < ActiveRecord::Base
|
||||
has_many :authentications, :dependent => :destroy
|
||||
accepts_nested_attributes_for :authentications
|
||||
|
||||
def can_translate?
|
||||
is_translator
|
||||
def can_translate?(to_locale = nil, from_locale = nil)
|
||||
is_translator unless to_locale.present?
|
||||
|
||||
from_locale = I18n.locale unless from_locale.present?
|
||||
return languages.present? &&
|
||||
to_locale.to_s != from_locale.to_s &&
|
||||
languages.include?(to_locale.to_s) &&
|
||||
languages.include?(from_locale.to_s)
|
||||
end
|
||||
|
||||
def name
|
||||
firstname || username || email
|
||||
end
|
||||
|
||||
end
|
||||
|
@ -18,7 +18,7 @@ class Workshop < ActiveRecord::Base
|
||||
return nil unless user
|
||||
workshop_facilitators.each do |u|
|
||||
if u.user_id == user.id
|
||||
return conference.registered?(user) ? u.role.to_sym : :unregistered
|
||||
return conference.registration_exists?(user) ? u.role.to_sym : :unregistered
|
||||
end
|
||||
end
|
||||
return nil
|
||||
@ -31,7 +31,7 @@ class Workshop < ActiveRecord::Base
|
||||
def active_facilitators
|
||||
users = []
|
||||
workshop_facilitators.each do |u|
|
||||
users << User.find(u.user_id) if u.role.to_sym != :request
|
||||
users << User.find(u.user_id) unless u.role.to_sym == :requested
|
||||
end
|
||||
return users
|
||||
end
|
||||
@ -75,16 +75,18 @@ class Workshop < ActiveRecord::Base
|
||||
end
|
||||
|
||||
def interested_count
|
||||
return 0 unless id
|
||||
collaborators = []
|
||||
workshop_facilitators.each do |f|
|
||||
collaborators << f.user_id unless f.role.to_sym == :requested
|
||||
end
|
||||
interested = WorkshopInterest.where("workshop_id=#{id} AND user_id NOT IN (#{collaborators.join ','})")
|
||||
return 10 unless collaborators.present?
|
||||
interested = WorkshopInterest.where("workshop_id=#{id} AND user_id NOT IN (#{collaborators.join ','})") || []
|
||||
interested ? interested.size : 0
|
||||
end
|
||||
|
||||
def can_translate?(user, lang)
|
||||
(user.can_translate? && lang.to_sym != locale.to_sym) || can_edit?(user)
|
||||
user.can_translate?(lang, locale) || (can_edit?(user) && lang.to_s != locale.to_s)
|
||||
end
|
||||
|
||||
def conference_day
|
||||
@ -100,6 +102,28 @@ class Workshop < ActiveRecord::Base
|
||||
((end_time - start_time) / 60).to_i
|
||||
end
|
||||
|
||||
def self.all_themes
|
||||
[:race_gender, :mechanics, :funding, :organization, :community]
|
||||
end
|
||||
|
||||
def get_translators(data, loc = nil)
|
||||
notify_list = {}
|
||||
active_facilitators.each do |facilitator|
|
||||
notify_list[facilitator.id] = facilitator
|
||||
end
|
||||
|
||||
data.each do | column, value |
|
||||
(
|
||||
loc.present? ?
|
||||
get_translators_for_column_and_locale(column, loc) :
|
||||
get_translators_for_column(column)
|
||||
).each do |id|
|
||||
notify_list[id] = User.find(id)
|
||||
end
|
||||
end
|
||||
return notify_list
|
||||
end
|
||||
|
||||
private
|
||||
def make_slug
|
||||
if !self.slug
|
||||
|
@ -5,5 +5,4 @@
|
||||
= columns(medium: 12) do
|
||||
%h1
|
||||
=_'error.404.title','This page does not exist!'
|
||||
%p
|
||||
=p(_'error.404.description', :p)
|
||||
=paragraph(_'error.404.description', :p)
|
@ -5,13 +5,14 @@
|
||||
- figure = nil
|
||||
- if capable_of(:css_mixblendmode)
|
||||
- cover = "<div class=\"cover\" style=\"background-image: url(#{image})\"></div>"
|
||||
- elsif capable_of(:svg) && Rails.env != 'test'
|
||||
- banner_image = 'application/banner_image.svg'
|
||||
- else
|
||||
- style = "background-image: url(#{image})"
|
||||
#header-title.short{style: style}
|
||||
= (render banner_image, {:image => image}) if banner_image
|
||||
= (render banner_image, {:image => image}) if defined?(banner_image) == "local-variable"
|
||||
= cover.html_safe if cover
|
||||
= row do
|
||||
= columns do
|
||||
%h1=_(@page_title || "page_titles.#{page_group.to_s}.#{page_key.to_s}")
|
||||
- if @page_title.present? || defined?(page_group)
|
||||
- content_for :title do
|
||||
=@page_title.present? ? I18n.t(@page_title, @page_title_vars) : I18n.t("page_titles.#{page_group.to_s}.#{page_key.to_s}")
|
||||
= row do
|
||||
= columns do
|
||||
%h1=(_"page_titles.#{page_group.to_s}.#{page_key.to_s}")
|
||||
|
@ -2,4 +2,4 @@
|
||||
= columns(medium: 12) do
|
||||
%h2=_'articles.permission_denied.headings.confirmation_sent','Confirmation Sent'
|
||||
= columns(medium: 6) do
|
||||
=m('articles.permission_denied.paragraphs.confirmation_sent', :p)
|
||||
%p='articles.permission_denied.paragraphs.confirmation_sent', :p
|
||||
|
@ -9,16 +9,16 @@
|
||||
- if @schedule
|
||||
%h3=_'articles.workshops.headings.Schedule'
|
||||
= render 'schedule/programme', :schedule => @schedule, :conference => @conference, :workshops => @workshops, :events => @events, :locations => @locations, :show_interest => true, :day_parts => @day_parts, :show_previews => true
|
||||
- elsif @conference.registration_open
|
||||
%h3=_'articles.workshops.headings.Proposed_Workshops'
|
||||
%p=_'articles.workshops.paragraphs.Proposed_Workshops', "Would you like to facilitate your own workshop? Simply register and visit the workshops page. If you have already registered you can access the page by restarting the registration process."
|
||||
%ul.workshop-list
|
||||
- @conference.workshops.sort_by{ |w| w.title.downcase }.each do |w|
|
||||
%li
|
||||
%h4=w.title
|
||||
.workshop-interest
|
||||
- if w.interested?(current_user)
|
||||
=_'articles.workshops.info.you_are_interested_count', "You and #{w.interested_count - 1} others are interested in this workshop", :vars => {:count => (w.interested_count - 1)}
|
||||
- elsif w.interested_count > 0
|
||||
=_'articles.workshops.info.interested_count', "#{w.interested_count} people are interested in this workshop", :vars => {:count => w.interested_count}
|
||||
.workshop-description=markdown w.info
|
||||
- if @conference.registration_status == :open
|
||||
%h3=_'articles.workshops.headings.Proposed_Workshops'
|
||||
%p=_'articles.workshops.paragraphs.Proposed_Workshops', "Would you like to facilitate your own workshop? Simply register and visit the workshops page. If you have already registered you can access the page by restarting the registration process."
|
||||
%ul.workshop-list
|
||||
- @conference.workshops.sort_by{ |w| w.title.downcase }.each do |w|
|
||||
%li
|
||||
%h4=w.title
|
||||
.workshop-interest
|
||||
- if w.interested?(current_user)
|
||||
=_'articles.workshops.info.you_are_interested_count', "You and #{w.interested_count - 1} others are interested in this workshop", :vars => {:count => (w.interested_count - 1)}
|
||||
- elsif w.interested_count > 0
|
||||
=_'articles.workshops.info.interested_count', "#{w.interested_count} people are interested in this workshop", :vars => {:count => w.interested_count}
|
||||
.workshop-description=markdown w.info
|
||||
|
@ -1,7 +1,9 @@
|
||||
= render :partial => 'application/header', :locals => {:image_file => @banner_image || '403.jpg'}
|
||||
%article
|
||||
- if @template
|
||||
- if @template.present?
|
||||
=render @template
|
||||
- else
|
||||
%h2=_'articles.permission_denied.headings.main','Sorry, you currently don\'t have access to this page'
|
||||
%p=_'articles.permission_denied.paragraphs.main', :p
|
||||
= row do
|
||||
= columns do
|
||||
%h2=_'articles.permission_denied.headings.main','Sorry, you currently don\'t have access to this page'
|
||||
%p=_'articles.permission_denied.paragraphs.main', :p
|
||||
|
@ -1,6 +1,15 @@
|
||||
%article
|
||||
= columns(medium: 12) do
|
||||
%h2=_'articles.conference_registration.headings.Confirm_You_Email_Address','Confirm Email Address'
|
||||
= columns(medium: 6, large: 5) do
|
||||
= form_tag register_path(@this_conference.slug) do
|
||||
%h2=_'articles.conference_registration.headings.Pre_Registration_Details','Pre-Registration is now open!'
|
||||
%p=_'articles.conference_registration.paragraphs.Pre_Registration_Details', 'By completing the pre-registration process you are letting us know that you are interested in coming, it allows us to contact you when we have news, and lets us better plan by knowing who might be coming. Once registration is fully open, we will need to ask a few more questions and you can confirm whether or not you will actually be coming.'
|
||||
%h3=_'articles.conference_registration.headings.Verify_Account','Verify your account'
|
||||
%p=_'articles.conference_registration.paragraphs.Verify_Account', 'In order to confirm that you are a real person and that we will be able to contact you later, please verify your email address. Before continuing with pre-registration, we will send you an email that will allow you to continue with the process.'
|
||||
= columns(medium: 12) do
|
||||
= form_tag register_path(@this_conference.slug), class: 'flex-form' do
|
||||
.email-field.input-field.big
|
||||
= email_field_tag :email, nil, required: true
|
||||
= label_tag :email
|
||||
= button_tag :continue, :value => :confirm_email
|
||||
= columns(medium: 12, class: 'flex-column') do
|
||||
%p.stretch-item=_'articles.conference_registration.paragraphs.facebook_sign_in','Alternatively you can sign in using your Facebook account and skip waiting for us to send you an email.'
|
||||
= link_to (_'forms.actions.generic.facebook_sign_in','Facebook Sign In'), auth_at_provider_path(:provider => :facebook), class: [:button, :facebook]
|
||||
|
26
app/views/conferences/_contact_info.html.haml
Normal file
26
app/views/conferences/_contact_info.html.haml
Normal file
@ -0,0 +1,26 @@
|
||||
= columns(medium: 12) do
|
||||
%h2=_(@page_title)
|
||||
%p=_'articles.conference_registration.paragraphs.Contact_Info', :s, 2
|
||||
= columns(medium: 12) do
|
||||
= form_tag register_path(@this_conference.slug) do
|
||||
%h3=_'articles.conference_registration.headings.name','What is your name?'
|
||||
.text-field.input-field{class: @errors[:name].present? ? 'has-error' : nil}
|
||||
= show_errors :name
|
||||
= label_tag :name
|
||||
= text_field_tag :name, @name, required: true
|
||||
%h3=_'articles.conference_registration.headings.location','Where are you coming from?'
|
||||
.text-field.input-field{class: @errors[:location].present? ? 'has-error' : nil}
|
||||
= show_errors :location
|
||||
= label_tag :location
|
||||
= text_field_tag :location, @registration.city || location(lookup_ip_location), required: true
|
||||
%h3=_'articles.conference_registration.headings.languages','Which languages do you speak?'
|
||||
- puts @errors.to_json.to_s
|
||||
.check-box-field.input-field{class: @errors[:languages].present? ? 'has-error' : nil}
|
||||
- [:en, :es, :fr].each do |language|
|
||||
= check_box_tag "languages[#{language}]", 1, (@registration.languages || [I18n.locale.to_s]).include?(language.to_s)
|
||||
= label_tag "languages_#{language}" do
|
||||
= _"languages.#{language}"
|
||||
= show_errors :languages
|
||||
.actions.next-prev
|
||||
= button_tag (params[:step] == :save ? :save : :next), value: :contact_info
|
||||
= button_tag :previous, value: :prev_contact_info, class: :subdued, formnovalidate: true
|
@ -3,4 +3,4 @@
|
||||
= columns(medium: 12) do
|
||||
%h2=_'articles.conference_registration.headings.email_confirm','Please confirm your email address'
|
||||
= columns(medium: 12) do
|
||||
=m('articles.conference_registration.paragraphs.email_confirm', :p)
|
||||
%p=_'articles.conference_registration.paragraphs.email_confirm', :p
|
||||
|
@ -2,7 +2,7 @@
|
||||
.title
|
||||
%h1=_!@conference.title
|
||||
.details
|
||||
%h3.primary=location(@conference.organizations.first.locations.first)
|
||||
%h2.primary=location(@conference.organizations.first.locations.first)
|
||||
.secondary
|
||||
= date_span(@conference.start_date.to_date, @conference.end_date.to_date)
|
||||
%img{src: @conference.poster.full.url || image_path('default_poster.jpg')}
|
||||
%img{src: @conference.poster.full.url || image_path('default_poster.jpg'), role: :presentation, alt: ''}
|
||||
|
@ -1,10 +1,9 @@
|
||||
%article
|
||||
= columns(medium: 12) do
|
||||
%h2=_'articles.conference_registration.headings.Policy_Agreement'
|
||||
%p=_'articles.conference_registration.paragraphs.Policy_Agreement', :s, 2
|
||||
= columns(medium: 10, push: 1) do
|
||||
= render 'application/policy'
|
||||
= columns(medium: 12) do
|
||||
%p=_'articles.conference_registration.paragraphs.Confirm_Agreement', :p
|
||||
= form_tag register_path(@this_conference.slug) do
|
||||
.actions= button_tag :agree, :value => :policy
|
||||
= columns(medium: 12) do
|
||||
%h2=_@page_title
|
||||
%p=_'articles.conference_registration.paragraphs.Policy_Agreement', :s, 2
|
||||
= columns(medium: 10, push: 1) do
|
||||
= render 'application/policy'
|
||||
= columns(medium: 12) do
|
||||
%p=_'articles.conference_registration.paragraphs.Confirm_Agreement', :p
|
||||
= form_tag register_path(@this_conference.slug) do
|
||||
.actions= button_tag :agree, :value => :policy
|
||||
|
@ -1,42 +1,20 @@
|
||||
= columns(medium: 12) do
|
||||
%h2=_'articles.conference_registration.headings.Workshops','Workshops'
|
||||
= columns(medium: 6) do
|
||||
%h3=_'articles.conference_registration.headings.Add_Workshop'
|
||||
= form_tag register_path(@this_conference.slug) do
|
||||
.text-field.input-field
|
||||
= text_field_tag :title, nil, required: true
|
||||
= label_tag :title
|
||||
= text_area_tag :info, nil, required: true
|
||||
= button_tag :add
|
||||
= columns(medium: 6) do
|
||||
%h3=_'articles.conference_registration.headings.Your_Workshops'
|
||||
%ul.workshops.my-workshops
|
||||
- @my_workshops.each do |workshop|
|
||||
%li
|
||||
%h4=workshop[:title]
|
||||
.info
|
||||
%h5{:contenteditable => true}=workshop[:title]
|
||||
%p{:contenteditable => true}=workshop[:info]
|
||||
= form_tag register_path(@this_conference.slug) do
|
||||
= button_tag :save, :value => :save
|
||||
= button_tag :delete, :value => :delete
|
||||
= button_tag :cancel, :value => :cancel
|
||||
%h2=_@page_title
|
||||
= columns(medium: 12) do
|
||||
= form_tag register_path(@this_conference.slug) do
|
||||
= button_tag :previous, :value => :registration
|
||||
= button_tag :next, :value => :workshops
|
||||
:javascript
|
||||
function makeWorkshopsClickable() {
|
||||
var workshops = document.querySelectorAll('ul.workshops h4');
|
||||
for (var i = 0; i < workshops.length; i++) {
|
||||
workshops[i].addEventListener('click', function(e) {
|
||||
var workshop = e.target.parentElement;
|
||||
workshop.className = 'view';
|
||||
workshop.querySelector('form').onsubmit = function() {
|
||||
workshop.removeAttribute('class');
|
||||
return false;
|
||||
}
|
||||
}, false);
|
||||
}
|
||||
}
|
||||
makeWorkshopsClickable();
|
||||
- if @my_workshops.present?
|
||||
%h3=_'articles.conference_registration.headings.Your_Workshops'
|
||||
%p=_'articles.conference_registration.paragraphs.Your_Workshops', :p
|
||||
= render 'workshops/workshop_previews', :workshops => @my_workshops
|
||||
- else
|
||||
%p=_'articles.conference_registration.paragraphs.Create_Workshop', :p
|
||||
= link_to (_'articles.conference_registration.headings.Add_Workshop'), create_workshop_path(@this_conference.slug), class: :button
|
||||
- if @workshops_in_need.present?
|
||||
= columns(medium: 12) do
|
||||
%h3=_'articles.conference_registration.headings.Workshops_Looking_For_Facilitators'
|
||||
%p=_'articles.conference_registration.paragraphs.Workshops_Looking_For_Facilitators', :p
|
||||
= render 'workshops/workshop_previews', :workshops => @workshops_in_need
|
||||
- if @workshops.present?
|
||||
= columns(medium: 12) do
|
||||
%h3=_'articles.conference_registration.headings.All_Workshops'
|
||||
-#%p=_'articles.conference_registration.paragraphs.All_Workshops', :p
|
||||
= render 'workshops/workshop_previews', :workshops => @workshops
|
||||
|
@ -13,18 +13,18 @@
|
||||
|
||||
= button_tag :save, :value => :save
|
||||
|
||||
:javascript
|
||||
window.jQuery || document.write('<script src="https://code.jquery.com/jquery-2.1.3.min.js"><\/script>')
|
||||
= javascript_include_tag "froala_editor.min.js"
|
||||
= stylesheet_link_tag "froala_editor.min.css"
|
||||
= stylesheet_link_tag "font-awesome.min.css"
|
||||
:javascript
|
||||
$(function() {
|
||||
$('.text-editor textarea').editable({
|
||||
language: '<% I18n.locale.to_s %>',
|
||||
inlineMode: false,
|
||||
blockTags: ["n", "p", "h2", "blockquote", "pre"],
|
||||
buttons: ["formatBlock", "bold", "italic", "underline", "insertOrderedList", "insertUnorderedList", "sep", "createLink", "html", "undo", "redo"],
|
||||
colors: ['#00ADEF', '#DD57EF', '#E6C74B', 'REMOVE']
|
||||
});
|
||||
});
|
||||
/ :javascript
|
||||
/ window.jQuery || document.write('<script src="https://code.jquery.com/jquery-2.1.3.min.js"><\/script>')
|
||||
-#= javascript_include_tag "froala_editor.min.js"
|
||||
-#= stylesheet_link_tag "froala_editor.min.css"
|
||||
-#= stylesheet_link_tag "font-awesome.min.css"
|
||||
/ :javascript
|
||||
/ $(function() {
|
||||
/ $('.text-editor textarea').editable({
|
||||
/ language: '<% I18n.locale.to_s %>',
|
||||
/ inlineMode: false,
|
||||
/ blockTags: ["n", "p", "h2", "blockquote", "pre"],
|
||||
/ buttons: ["formatBlock", "bold", "italic", "underline", "insertOrderedList", "insertUnorderedList", "sep", "createLink", "html", "undo", "redo"],
|
||||
/ colors: ['#00ADEF', '#DD57EF', '#E6C74B', 'REMOVE']
|
||||
/ });
|
||||
/ });
|
||||
|
@ -1,4 +1,20 @@
|
||||
= render :partial => 'page_header', :locals => {:page_key => 'Conference_Registration'}
|
||||
- if (steps = current_registration_steps)
|
||||
= row id: 'registration-steps', class: 'flow-steps' do
|
||||
= columns do
|
||||
%ul
|
||||
- current_registration_steps.each do | step |
|
||||
- text = _"articles.conference_registration.headings.#{step[:name].to_s}"
|
||||
%li{class: [step[:enabled] ? :enabled : nil, @register_template == step[:name] ? :current : nil]}
|
||||
- if step[:enabled]
|
||||
.step= link_to text, register_step_path(@this_conference.slug, step[:name])
|
||||
- else
|
||||
.step= text
|
||||
- if @warnings.present?
|
||||
= row class: 'warnings', tag: :ul do
|
||||
- @warnings.each do | warning |
|
||||
= columns tag: :li do
|
||||
= warning
|
||||
%article
|
||||
= row do
|
||||
= render (@register_template || :register).to_s
|
||||
= render @register_template.to_s
|
||||
|
@ -3,19 +3,17 @@
|
||||
%head
|
||||
%meta{ charset: 'utf-8' }
|
||||
%meta{ name: 'viewport', content: 'width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0' }
|
||||
%title= (yield :title) + (content_for?(:title) ? (_!' | ') : '') + (_!'Bike!Bike!')
|
||||
-#%title= (yield :title) + (content_for?(:title) ? (_!' | ') : '') + (_!'Bike!Bike!')
|
||||
%title=_!('Bike!Bike!' + (content_for?(:title) ? " - #{yield :title}" : ''))
|
||||
%meta{ name: 'description', content: (yield_or_default :description, I18n.t('page_descriptions.home')) }
|
||||
= csrf_meta_tags
|
||||
= inject_css!
|
||||
- (@stylesheets || []).each do |css|
|
||||
= inject_css! css
|
||||
= stylesheet_link_tag 'i18n-debug' if request.params['i18nDebug']
|
||||
= stylesheets
|
||||
%link{ href: asset_path('favicon.ico'), rel: 'shortcut icon', type: 'image/x-icon' }
|
||||
%link{ href: asset_path('favicon.ico'), rel: 'icon', type: 'image/x-icon' }
|
||||
%link{ href: asset_path('apple-touch-icon.png'), rel: 'apple-touch-icon' }
|
||||
%link{ href: asset_path('apple-touch-icon-72x72.png'), rel: 'apple-touch-icon', sizes: '72x72' }
|
||||
%link{ href: asset_path('apple-touch-icon-114x114.png'), rel: 'apple-touch-icon', sizes: '114x114' }
|
||||
%link{ href: asset_path('apple-touch-icon-144x144.png'), rel: 'apple-touch-icon', sizes: '144x144' }
|
||||
-#%link{ href: asset_path('apple-touch-icon.png'), rel: 'apple-touch-icon' }
|
||||
-#%link{ href: asset_path('apple-touch-icon-72x72.png'), rel: 'apple-touch-icon', sizes: '72x72' }
|
||||
-#%link{ href: asset_path('apple-touch-icon-114x114.png'), rel: 'apple-touch-icon', sizes: '114x114' }
|
||||
-#%link{ href: asset_path('apple-touch-icon-144x144.png'), rel: 'apple-touch-icon', sizes: '144x144' }
|
||||
= yield :head
|
||||
|
||||
%body{ class: page_style }
|
||||
@ -24,8 +22,8 @@
|
||||
- if content_for?(:side_bar)
|
||||
%nav#side-bar
|
||||
= yield :side_bar
|
||||
- flash.each do |key, msg|
|
||||
= content_tag :div, msg, :id => key
|
||||
-# - flash.each do |key, msg|
|
||||
-# = content_tag :div, msg, :id => key
|
||||
%header#banner=yield :banner
|
||||
- if @submenu
|
||||
=row do
|
||||
@ -39,5 +37,7 @@
|
||||
= yield
|
||||
#footer
|
||||
%footer= render 'shared/footer'
|
||||
- if content_for?(:footer_scripts)
|
||||
= yield :footer_scripts
|
||||
|
||||
= yield :footer_scripts if content_for?(:footer_scripts)
|
||||
- add_inline_script :main
|
||||
= inline_scripts
|
||||
|
@ -3,74 +3,10 @@
|
||||
%head
|
||||
%meta{:content => "text/html; charset=utf-8", "http-equiv" => "Content-Type"}/
|
||||
%meta{:content => "width=device-width, initial-scale=1.0", :name => "viewport"}/
|
||||
%title Your Message Subject or Title
|
||||
:css
|
||||
#outlook a {padding:0;}
|
||||
body{width:100% !important; -webkit-text-size-adjust:100%; -ms-text-size-adjust:100%; margin:0; padding:0;}
|
||||
.ExternalClass {width:100%;}
|
||||
.ExternalClass, .ExternalClass p, .ExternalClass span, .ExternalClass font, .ExternalClass td, .ExternalClass div {line-height: 100%;}
|
||||
#backgroundTable {margin:0; padding:0; width:100% !important; line-height: 100% !important;}
|
||||
img {outline:none; text-decoration:none; -ms-interpolation-mode: bicubic;}
|
||||
a img {border:none;}
|
||||
.image_fix {display:block;max-width:100%}
|
||||
p {margin: 1em;color:#333 !important;}
|
||||
h1, h2, h3, h4, h5, h6 {color:#333 !important;}
|
||||
h1 a, h2 a, h3 a, h4 a, h5 a, h6 a {color: #00ADEF !important;}
|
||||
h1 a:active, h2 a:active, h3 a:active, h4 a:active, h5 a:active, h6 a:active { color: #D89E59 !important; }
|
||||
h1 a:visited, h2 a:visited, h3 a:visited, h4 a:visited, h5 a:visited, h6 a:visited { color: #DD57EF !important; }
|
||||
table td {border-collapse: collapse;}
|
||||
table { border-collapse:collapse; mso-table-lspace:0pt; mso-table-rspace:0pt; max-width:512px;min-width:280px;}
|
||||
a {color: #00ADEF;}
|
||||
a:link { color: #00ADEF; }
|
||||
a:visited { color: #00ADEF; }
|
||||
a:hover { color: #02CA9E; }
|
||||
@media only screen and (max-device-width: 480px) {
|
||||
a[href^="tel"], a[href^="sms"] {
|
||||
text-decoration: none;
|
||||
color: #333;
|
||||
pointer-events: none;
|
||||
cursor: default;
|
||||
}
|
||||
.mobile_link a[href^="tel"], .mobile_link a[href^="sms"] {
|
||||
text-decoration: default;
|
||||
color: #00ADEF !important;
|
||||
pointer-events: auto;
|
||||
cursor: default;
|
||||
}
|
||||
}
|
||||
h3 b {
|
||||
padding: 10px 20px;
|
||||
line-height: 50px;
|
||||
}
|
||||
h3 b a,
|
||||
h3 b a:visited {
|
||||
color: #FFF !important;
|
||||
background-color: #02CA9E;
|
||||
text-decoration: none !important;
|
||||
border-radius: 4px;
|
||||
padding: 10px 15px;
|
||||
margin-left: 20px;
|
||||
border-bottom: 3px solid #00AF88;
|
||||
}
|
||||
h3 b a:hover {
|
||||
background-color: #00AF88;
|
||||
}
|
||||
@media only screen and (min-device-width: 768px) and (max-device-width: 1024px) {
|
||||
a[href^="tel"], a[href^="sms"] {
|
||||
text-decoration: none;
|
||||
color: #00ADEF;
|
||||
pointer-events: none;
|
||||
cursor: default;
|
||||
}
|
||||
.mobile_link a[href^="tel"], .mobile_link a[href^="sms"] {
|
||||
text-decoration: default;
|
||||
color: #00ADEF !important;
|
||||
pointer-events: auto;
|
||||
cursor: default;
|
||||
}
|
||||
}
|
||||
%title=@subject
|
||||
%style{type: 'text/css'}=Rails.application.assets.find_asset("user-mailer.css").to_s
|
||||
%body
|
||||
%table#backgroundTable{:border => "0", :cellpadding => "0", :cellspacing => "0"}
|
||||
%table{:border => "0", :cellpadding => "0", :cellspacing => "0", id: @wrapper_id.present? ? "bb_#{@wrapper_id.to_s}" : 'backgroundTable'}
|
||||
%tr
|
||||
%td
|
||||
%table{:align => "center", :border => "0", :cellpadding => "0", :cellspacing => "0"}
|
||||
|
@ -5,4 +5,5 @@
|
||||
%input{type: 'hidden', name: 'item_name', value: 'Bike!Bike!'}
|
||||
%input{type: 'hidden', name: 'no_note', value: '0'}
|
||||
%input{type: 'hidden', name: 'currency_code', value: 'USD'}
|
||||
%button{type: 'submit', value: 'PP-DonationsBF:paypal_logo.png:NonHostedGuest'}=_'donate.button_label','Donate'
|
||||
%button{type: 'submit', value: 'PP-DonationsBF:paypal_logo.png:NonHostedGuest'}
|
||||
%span.title=_'donate.button_label','Donate'
|
||||
|
@ -1,14 +1,23 @@
|
||||
%nav
|
||||
#main-nav
|
||||
= row(class: 'inner-nav') do
|
||||
- if logged_in?
|
||||
= columns(medium: 12, class: 'user-nav') do
|
||||
.user-nav-bar
|
||||
=link_to (_!current_user.name), register_step_path(@conference.slug, :contact_info), class: 'my-account'
|
||||
=link_to (_'forms.actions.generic.Log_out'), logout_path
|
||||
= columns(medium: 4, class: 'inner-nav') do
|
||||
= link_to '/', :class => 'logo' do
|
||||
= link_to home_path, :class => 'logo' do
|
||||
= svg_sprite('icons', "bb-icon-logo", "Logo")
|
||||
= svg_sprite('icons', "bb-icon-logo-text", "Logo Text")
|
||||
= off_screen _!'Bike Bike'
|
||||
= columns(medium: 8, class: 'nav') do
|
||||
= nav_link about_path, (_'page_titles.About'), 'about'
|
||||
= nav_link policy_path, (_'page_titles.Policy'), 'policy'
|
||||
- if @conference && @conference.registration_open
|
||||
= nav_link register_path(@conference.slug), (_'page_titles.Register')
|
||||
= nav_link :about
|
||||
= nav_link :policy
|
||||
- case (@conference ? @conference.registration_status.to_sym : :closed)
|
||||
- when :pre
|
||||
= nav_link register_path(@conference.slug), :pre_register, :register
|
||||
- when :open
|
||||
= nav_link register_path(@conference.slug), :register
|
||||
- else
|
||||
= render 'shared/donate_button', :email_address => (@conference.paypal_email_address || @conference.email_address)
|
||||
= render 'shared/donate_button', :email_address => (@conference.paypal_email_address || @conference.email_address || @conference.organizations.first.email_address)
|
@ -1,4 +1,8 @@
|
||||
%p=_'email.general.paragraph.thank_you', "Thank you #{@user.firstname},", :vars => {:name => @user.firstname}
|
||||
%p=_'email.registration.paragraph.confirmed', "You have successfully registered for #{@conference.title}. You can modify your registration details, pay, or add workshops at any time by restarting the registration process. If you have yet to pay or add your workshops and plan to do so, we ask that you take care of it as soon as possible to help us prepare in advance of your arrival.", :vars => {:conference_title => @conference.title}
|
||||
|
||||
%p=_'email.general.paragraph.see_you', "See you in #{@conference.location.city}!", :vars => {:conference_location => @conference.location.city}
|
||||
- if @conference.registration_status.to_sym == :pre
|
||||
%p=_'email.general.paragraph.pre_registered', "You have successfully pre-registered for #{@conference.title}. We will let you know when registration is fully open and if there is any important news about the conference that you should know about. We encourage you to create or volunteer to facilitate workshops soon. You can do that, or change your registration details at any time by clicking on the pre-register link again.", :vars => {:conference_title => @conference.title}
|
||||
- else
|
||||
%p=_'email.registration.paragraph.confirmed', :vars => {:conference_title => @conference.title}
|
||||
|
||||
%p=_'email.general.paragraph.see_you', :vars => {:conference_location => @conference.location.city}
|
||||
|
@ -1,5 +1,8 @@
|
||||
=_'email.general.paragraph.thank_you', "Thank you #{@user.firstname},", :vars => {:name => @user.firstname}
|
||||
|
||||
=_'email.registration.paragraph.confirmed', "You have successfully registered for #{@conference.title}. You can modify your registration details, pay, or add workshops at any time by restarting the registration process. If you have yet to pay or add your workshops and plan to do so, we ask that you take care of it as soon as possible to help us prepare in advance of your arrival.", :vars => {:conference_title => @conference.title}
|
||||
- if @conference.registration_status.to_sym == :pre
|
||||
=_'email.general.paragraph.pre_registered', "You have successfully pre-registered for #{@conference.title}. We will let you know when registration is fully open and if there is any important news about the conference that you should know about. We encourage you to create or volunteer to facilitate workshops soon. You can do that, or change your registration details at any time by clicking on the pre-register link again.", :vars => {:conference_title => @conference.title}
|
||||
- else
|
||||
=_'email.registration.paragraph.confirmed', :vars => {:conference_title => @conference.title}
|
||||
|
||||
=_'email.general.paragraph.see_you', "See you in #{@conference.location.city}!", :vars => {:conference_location => @conference.location.city}
|
||||
=_'email.general.paragraph.see_you', :vars => {:conference_location => @conference.location.city}
|
@ -1 +0,0 @@
|
||||
Hi, this is a test. Did you get it?
|
@ -0,0 +1,15 @@
|
||||
%p=_('email.translations.paragraph.workshop_original_content_changed', "#{@translator.name} has modified the original content for #{@workshop.title}. Translations may need to be updated.", vars: {user_name: @translator.firstname, workshop_title: @workshop.title})
|
||||
|
||||
- @data.each do |field, values|
|
||||
%h2=_"forms.labels.generic.#{field.to_s}"
|
||||
%b=_'email.translations.headings.new_value'
|
||||
%blockquote=_!values[:new].html_safe
|
||||
%b=_'email.translations.headings.old_value'
|
||||
%blockquote=_!values[:old].html_safe
|
||||
%b=_'email.translations.headings.diff'
|
||||
=_!(values[:diff][:html].html_safe)
|
||||
|
||||
%p
|
||||
=_'email.workshop.paragraph.view_workshop',"You can view the workshop here: "
|
||||
- workshop_link = view_workshop_url(@workshop.conference.slug, @workshop.id)
|
||||
%a{href: workshop_link}=workshop_link
|
@ -0,0 +1,25 @@
|
||||
=_('email.translations.paragraph.workshop_translated', "#{@translator.name} has modified the #{@locale_name} translation for #{@workshop.title}.", vars: {user_name: @translator.firstname, language: @locale_name, workshop_title: @workshop.title})
|
||||
|
||||
|
||||
- @data.each do |field, values|
|
||||
=(_!'** ') + (_"forms.labels.generic.#{field.to_s}")
|
||||
=(_!' - ') + (_'email.translations.headings.new_value')
|
||||
=(_!' ------------------------------ ')
|
||||
=_!values[:new]
|
||||
=(_!' ------------------------------ ')
|
||||
|
||||
|
||||
=(_!' - ') + (_'email.translations.headings.old_value')
|
||||
=(_!' ------------------------------ ')
|
||||
=_!values[:old]
|
||||
=(_!' ------------------------------ ')
|
||||
|
||||
|
||||
=(_!' - ') + (_'email.translations.headings.diff')
|
||||
=(_!' ------------------------------ ')
|
||||
=(_!values[:diff][:text])
|
||||
=(_!' ------------------------------ ')
|
||||
|
||||
|
||||
=_'email.workshop.paragraph.view_workshop',"You can view the workshop here: "
|
||||
= view_workshop_url(@workshop.conference.slug, @workshop.id)
|
15
app/views/user_mailer/workshop_translated.html.haml
Normal file
15
app/views/user_mailer/workshop_translated.html.haml
Normal file
@ -0,0 +1,15 @@
|
||||
%p=_('email.translations.paragraph.workshop_translated', "#{@translator.name} has modified the #{@locale_name} translation for #{@workshop.title}.", vars: {user_name: @translator.firstname, language: @locale_name, workshop_title: @workshop.title})
|
||||
|
||||
- @data.each do |field, values|
|
||||
%h2=_"forms.labels.generic.#{field.to_s}"
|
||||
%b=_'email.translations.headings.new_value'
|
||||
%blockquote=_!values[:new].html_safe
|
||||
%b=_'email.translations.headings.old_value'
|
||||
%blockquote=_!values[:old].html_safe
|
||||
%b=_'email.translations.headings.diff'
|
||||
=_!(values[:diff][:html].html_safe)
|
||||
|
||||
%p
|
||||
=_'email.workshop.paragraph.view_workshop',"You can view the workshop here: "
|
||||
- workshop_link = view_workshop_url(@workshop.conference.slug, @workshop.id)
|
||||
%a{href: workshop_link}=workshop_link
|
25
app/views/user_mailer/workshop_translated.text.haml
Normal file
25
app/views/user_mailer/workshop_translated.text.haml
Normal file
@ -0,0 +1,25 @@
|
||||
=_('email.translations.paragraph.workshop_translated', "#{@translator.firstname} has modified the #{@locale_name} translation for #{@workshop.title}.", vars: {user_name: @translator.firstname, language: @locale_name, workshop_title: @workshop.title})
|
||||
|
||||
|
||||
- @data.each do |field, values|
|
||||
=(_!'** ') + (_"forms.labels.generic.#{field.to_s}")
|
||||
=(_!' - ') + (_'email.translations.headings.new_value')
|
||||
=(_!' ------------------------------ ')
|
||||
=_!values[:new]
|
||||
=(_!' ------------------------------ ')
|
||||
|
||||
|
||||
=(_!' - ') + (_'email.translations.headings.old_value')
|
||||
=(_!' ------------------------------ ')
|
||||
=_!values[:old]
|
||||
=(_!' ------------------------------ ')
|
||||
|
||||
|
||||
=(_!' - ') + (_'email.translations.headings.diff')
|
||||
=(_!' ------------------------------ ')
|
||||
=(_!values[:diff][:text])
|
||||
=(_!' ------------------------------ ')
|
||||
|
||||
|
||||
=_'email.workshop.paragraph.view_workshop',"You can view the workshop here: "
|
||||
= view_workshop_url(@workshop.conference.slug, @workshop.id)
|
@ -6,14 +6,14 @@
|
||||
=_'articles.workshops.info.you_are_interested_count', "You and #{workshop.interested_count - 1} others are interested in this workshop", :vars => {:count => (workshop.interested_count - 1)}
|
||||
- else
|
||||
=_'articles.workshops.info.interested_count', "#{workshop.interested_count} people are interested in this workshop", :vars => {:count => workshop.interested_count}
|
||||
- if !preview && workshop.can_show_interest?(current_user)
|
||||
- if preview.blank? && workshop.can_show_interest?(current_user)
|
||||
= form_tag toggle_workshop_interest_path(workshop.conference.slug, workshop.id) do
|
||||
= button_tag (workshop.interested?(current_user) ? :remove_interest : :show_interest), :value => :toggle_interest, :class => (workshop.interested?(current_user) ? 'delete' : 'add')
|
||||
=markdown _!(workshop.info) || ''
|
||||
- if !preview && logged_in? && current_user.can_translate?
|
||||
= button_tag (workshop.interested?(current_user) ? :remove_interest : :show_interest), :value => :toggle_interest, :class => (workshop.interested?(current_user) ? :delete : :add)
|
||||
=_!(workshop.info).html_safe if workshop.info.present?
|
||||
- if preview.blank? && translations_available_for_editing
|
||||
.actions
|
||||
- I18n.backend.enabled_locales.each do |locale|
|
||||
= (link_to (_'actions.workshops.Translate', "Translate into #{language_name(locale)}", :vars => {:language => language_name(locale)}), edit_workshop_url(workshop.conference.slug, workshop.id, url_params(locale)), :class => 'button translate') if workshop.can_translate?(current_user, locale)
|
||||
- translations_available_for_editing.each do |locale|
|
||||
= link_to (_'actions.workshops.Translate', "Translate into #{language_name(locale)}", :vars => {:language => language_name(locale)}), translate_workshop_url(workshop.conference.slug, workshop.id, locale), :class => [:button, :translate]
|
||||
= columns(medium: 6) do
|
||||
%h3=_'articles.workshops.headings.facilitators'
|
||||
.facilitators
|
||||
@ -21,38 +21,40 @@
|
||||
- u = User.find(f.user_id)
|
||||
- if logged_in? && (workshop.public_facilitator?(u) || f.user_id == current_user.id || workshop.active_facilitator?(current_user))
|
||||
.facilitator
|
||||
.name=_!(u.firstname || u.username || u.email)
|
||||
.name=_!u.name
|
||||
.role
|
||||
=_"roles.workshops.facilitator.#{workshop.role(u).to_s}"
|
||||
- if !preview && f.role.to_sym == :requested && workshop.active_facilitator?(current_user)
|
||||
=(link_to (_'actions.workshops.Approve'), approve_facilitate_workshop_request_path(workshop.conference.slug, workshop.id, f.user_id, 'approve'), :class => 'button modify')
|
||||
=(link_to (_'actions.workshops.Deny'), approve_facilitate_workshop_request_path(workshop.conference.slug, workshop.id, f.user_id, 'deny'), :class => 'button delete')
|
||||
- elsif !preview && (f.user_id == current_user.id && f.role.to_sym != :creator) || (!workshop.conference.registered?(u) && workshop.active_facilitator?(current_user))
|
||||
=(link_to (_'actions.workshops.Remove'), approve_facilitate_workshop_request_path(workshop.conference.slug, workshop.id, f.user_id, 'remove'), :class => 'button delete')
|
||||
- if !preview
|
||||
- if preview.blank? && f.role.to_sym == :requested && workshop.active_facilitator?(current_user)
|
||||
=(link_to (_'actions.workshops.Approve'), approve_facilitate_workshop_request_path(workshop.conference.slug, workshop.id, f.user_id, 'approve'), :class => [:button, :modify])
|
||||
=(link_to (_'actions.workshops.Deny'), approve_facilitate_workshop_request_path(workshop.conference.slug, workshop.id, f.user_id, 'deny'), :class => [:button, :delete])
|
||||
- elsif preview.blank? && (f.user_id == current_user.id && f.role.to_sym != :creator) || (!workshop.conference.registered?(u) && workshop.active_facilitator?(current_user))
|
||||
=(link_to (_'actions.workshops.Remove'), approve_facilitate_workshop_request_path(workshop.conference.slug, workshop.id, f.user_id, 'remove'), :class => [:button, :delete])
|
||||
- unless preview.present?
|
||||
.actions
|
||||
=(link_to (_'actions.workshops.Facilitate'), facilitate_workshop_path(workshop.conference.slug, workshop.id), :class => 'button modify') if !workshop.facilitator?(current_user)
|
||||
=(link_to (_'actions.workshops.Facilitate'), facilitate_workshop_path(workshop.conference.slug, workshop.id), :class => [:button, workshop.needs_facilitators ? :accented : :subdued]) unless workshop.facilitator?(current_user)
|
||||
- if workshop.active_facilitator?(current_user)
|
||||
= form_tag workshop_add_facilitator_path(workshop.conference.slug, workshop.id), :class => 'add-facilitator' do
|
||||
%h4='Add a facilitator'
|
||||
%h4=_'articles.workshops.headings.add_facilitator','Add a facilitator'
|
||||
= form_tag workshop_add_facilitator_path(workshop.conference.slug, workshop.id), :class => 'add-facilitator flex-form' do
|
||||
.email-field.input-field
|
||||
= email_field_tag :email, nil, required: true
|
||||
= label_tag :email
|
||||
= button_tag :add
|
||||
- if workshop.languages
|
||||
- languages = JSON.parse(workshop.languages || '[]')
|
||||
- if languages.present?
|
||||
= columns(medium: 6) do
|
||||
%h3=_'articles.workshops.headings.languages','Workshop Language'
|
||||
%p= _!((JSON.parse(workshop.languages || '[]').map { |x| _"languages.#{x}" }).join(', ').to_s.html_safe)
|
||||
- if workshop.theme
|
||||
%p= _!((languages.map { |x| _"languages.#{x}" }).join(', ').to_s.html_safe)
|
||||
- if workshop.theme.present?
|
||||
= columns(medium: 6) do
|
||||
%h3=_'articles.workshops.headings.theme','Theme'
|
||||
%p= [:race_gender, :mechanics, :funding, :organization, :community].include?((workshop.theme || '').to_sym) ? (_"workshop.options.theme.#{workshop.theme}") : workshop.theme
|
||||
%p= Workshop.all_themes.include?((workshop.theme || '').to_sym) ? (_"workshop.options.theme.#{workshop.theme}") : workshop.theme
|
||||
- if workshop.active_facilitator?(current_user) || workshop.conference.host?(current_user)
|
||||
- if workshop.needs
|
||||
- needs = JSON.parse(workshop.needs || '[]')
|
||||
- if needs.present?
|
||||
= columns(medium: 6) do
|
||||
%h3=_'articles.workshops.headings.needs','What do you need?'
|
||||
%p= _!((JSON.parse(workshop.needs || '[]').map { |x| _"workshop.options.needs.#{x}" }).join(', ').to_s.html_safe)
|
||||
- if workshop.notes
|
||||
%p= _!((needs.map { |x| _"workshop.options.needs.#{x}" }).join(', ').to_s.html_safe)
|
||||
- if workshop.notes.present?
|
||||
= columns(medium: 12) do
|
||||
%h3=_'articles.workshops.headings.notes','Notes'
|
||||
=markdown _!(workshop.notes)
|
||||
=_!(workshop.notes).html_safe
|
||||
|
@ -1,8 +1,12 @@
|
||||
- # encoding: utf-8
|
||||
%ul.workshop-previews
|
||||
%ul.workshop-list
|
||||
- workshops.sort_by{ |w| w.title.downcase }.each do |w|
|
||||
- if w.title
|
||||
%li
|
||||
=link_to view_workshop_path(@this_conference.slug, w.id) do
|
||||
%h4=_!(w.title)
|
||||
%p=_!(w.info || '').gsub(/\n\#+(.*?)\r/, "\n<strong>\\1</strong>\r").gsub(/[\t ]*\r\n[\t ]*/, '<br>').html_safe
|
||||
- is_interested = w.interested?(current_user)
|
||||
%li{class: [is_interested ? :interested : nil]}
|
||||
= link_to view_workshop_path(w.conference.slug, w.id) do
|
||||
%h4.title=w.title
|
||||
.workshop-interest
|
||||
- if is_interested
|
||||
=_'articles.workshops.info.you_are_interested_count', "You and #{w.interested_count - 1} others are interested in this workshop", :vars => {:count => (w.interested_count - 1)}
|
||||
- elsif w.interested_count > 0
|
||||
=_'articles.workshops.info.interested_count', "#{w.interested_count} people are interested in this workshop", :vars => {:count => w.interested_count}
|
||||
.workshop-description=w.info.html_safe
|
||||
|
@ -1,9 +1,11 @@
|
||||
= render 'conferences/page_header', :page_key => 'Facilitate_Workshop'
|
||||
= render 'conferences/page_header', :page_key => 'Conference_Registration'
|
||||
|
||||
%article
|
||||
= row do
|
||||
%h2=_'page_titles.conferences.Facilitate_Workshop'
|
||||
= columns(medium: 12) do
|
||||
=m('articles.workshops.paragraphs.facilitate_request_sent','Your request has been sent. You will receive an email once your request is approved or denied or if the current facilitators have any questions.')
|
||||
= columns(medium: 12) do
|
||||
.actions
|
||||
= link_to (_'actions.workshops.View'), view_workshop_url(@this_conference.slug, @workshop.id), :class => 'button'
|
||||
= link_to (_'actions.workshops.View_All'), workshops_url(@this_conference.slug), :class => 'button'
|
||||
= link_to (_'actions.workshops.View_All'), register_step_path(@this_conference.slug, :wokshops), :class => 'button'
|
||||
|
@ -1,13 +1,18 @@
|
||||
= render 'conferences/page_header', :page_key => 'Workshops'
|
||||
|
||||
%article
|
||||
= row do
|
||||
= columns(medium: 12) do
|
||||
%h2=_'articles.workshops.headings.Workshops'
|
||||
= columns(medium: 6) do
|
||||
= row do
|
||||
= columns(medium: 12) do
|
||||
%p=_'articles.workshops.paragraphs.Workshops', :p
|
||||
= columns(medium: 6) do
|
||||
= row do
|
||||
= columns(medium: 12) do
|
||||
%h3=_'articles.workshops.headings.Your_Workshops'
|
||||
= render 'workshops/workshop_previews', :workshops => @my_workshops
|
||||
= link_to (_'actions.workshops.create','New Workshop'), create_workshop_path(@this_conference.slug), class: [:button, :modify] if @conference.registration_open
|
||||
= row do
|
||||
= render 'workshops/workshop_previews', :workshops => @workshops
|
||||
= columns(medium: 12) do
|
||||
%h3=_'articles.workshops.headings.All_Workshops'
|
||||
= render 'workshops/workshop_previews', :workshops => @workshops
|
@ -1,69 +1,91 @@
|
||||
= render 'conferences/page_header', :page_key => (@workshop ? 'Edit_Workshop' : 'Create_Workshop')
|
||||
- add_stylesheet :editor
|
||||
- add_inline_script :pen
|
||||
- add_inline_script :markdown
|
||||
- add_inline_script :editor
|
||||
|
||||
-#= render 'conferences/page_header', :page_key => (@workshop.id.present? ? 'Edit_Workshop' : 'Create_Workshop')
|
||||
= render 'conferences/page_header', :page_key => 'Conference_Registration'
|
||||
|
||||
%article
|
||||
= row do
|
||||
= form_tag save_workshop_path(@this_conference.slug), class: 'composition' do
|
||||
= (hidden_field_tag :workshop_id, @workshop.id) if @workshop
|
||||
= columns(medium: 12) do
|
||||
=m('articles.workshops.paragraphs.new_workshop','Please accurately describe your workshop in detail. This will help hosts decide if they wish to add it to the schedule and when it should best be scheduled. Enter normal text but if you want to get fancy you can use [Markdown](http://daringfireball.net/projects/markdown/basics).')
|
||||
- if @workshop.id.present?
|
||||
= hidden_field_tag :workshop_id, @workshop.id
|
||||
- if @is_translating
|
||||
%h2=_(@page_title, vars: @page_title_vars)
|
||||
= hidden_field_tag :translation, @translation
|
||||
- else
|
||||
%h2=_@page_title, :t
|
||||
- else
|
||||
%h2=_@page_title, :t
|
||||
.text-field.input-field.big
|
||||
= label_tag :title
|
||||
= text_field_tag :title, @title, :required => true
|
||||
- if @workshop && I18n.locale.to_s != @workshop.locale.to_s
|
||||
= text_field_tag :title, @title, required: true, lang: @translation
|
||||
- if @is_translating
|
||||
.original-text
|
||||
%h4=_'translate.content.Translation_of'
|
||||
.value=@workshop.title!
|
||||
.text-area-field.input-field
|
||||
= label_tag :info
|
||||
= text_area_tag :info, @info, :required => true
|
||||
- if @workshop && I18n.locale.to_s != @workshop.locale.to_s
|
||||
.input-field-help=_'articles.workshops.paragraphs.info', :s, 2
|
||||
.textarea#info{lang: @translation, data: {name: :info}}=(@info || '').html_safe
|
||||
- if @is_translating
|
||||
.original-text
|
||||
%h4=_'translate.content.Translation_of'
|
||||
.value=markdown @workshop.info!
|
||||
- if !@workshop || @can_edit
|
||||
.value=(@workshop.info! || '').html_safe
|
||||
- if !@is_translating && (@workshop.id.blank? || @can_edit)
|
||||
= columns(medium: 6) do
|
||||
%h3=_'articles.workshops.headings.languages','Workshop Language'
|
||||
- [:en, :es, :fr].each do |language|
|
||||
.single-check-box-field.input-field
|
||||
.input-field-help=_'articles.workshops.paragraphs.languages', :s, 2
|
||||
.check-box-field.vertical.input-field
|
||||
- [:en, :es, :fr].each do |language|
|
||||
= check_box_tag "languages[#{language}]", 1, @languages && @languages.include?(language)
|
||||
= label_tag "languages_#{language}" do
|
||||
= check_box_tag "languages[#{language}]", 1, @languages && @languages.include?(language)
|
||||
= _"languages.#{language}"
|
||||
%h3=_'articles.workshops.headings.theme','Theme'
|
||||
.input-field-help=_'articles.workshops.paragraphs.theme', :p
|
||||
.input-field-help=_'articles.workshops.paragraphs.theme', :s, 2
|
||||
- theme_found = false
|
||||
- [:race_gender, :mechanics, :funding, :organization, :community].each do |theme|
|
||||
.single-radio-button-field.input-field
|
||||
.check-box-field.vertical.input-field
|
||||
- Workshop.all_themes.each do |theme|
|
||||
- is_selected = (@workshop.theme.to_s == theme.to_s)
|
||||
- theme_found ||= is_selected
|
||||
= radio_button_tag :theme, theme, is_selected
|
||||
= label_tag "theme_#{theme}" do
|
||||
- is_selected = (@theme == theme)
|
||||
- theme_found ||= is_selected
|
||||
= radio_button_tag :theme, theme, is_selected
|
||||
= _"workshop.options.theme.#{theme}"
|
||||
.single-radio-button-field.other-field.input-field
|
||||
- is_other = (@workshop.theme.present? && !theme_found)
|
||||
= radio_button_tag :theme, :other, is_other
|
||||
= label_tag "theme_other" do
|
||||
= radio_button_tag :theme, :other, (@theme && !theme_found)
|
||||
%div
|
||||
= _"workshop.options.theme.other"
|
||||
= text_field_tag :other_theme, (@theme && !theme_found ? @theme : nil)
|
||||
.other
|
||||
= text_field_tag :other_theme, (is_other ? @workshop.theme : nil), placeholder: (_"workshop.options.theme.other"), required: is_other
|
||||
= columns(medium: 6) do
|
||||
%h3=_'articles.workshops.headings.needs','What do you need?'
|
||||
- [:sound, :projector, :tools].each do |need|
|
||||
.single-check-box-field.input-field
|
||||
.input-field-help=_'articles.workshops.paragraphs.needs', :s, 2
|
||||
.check-box-field.vertical.input-field
|
||||
- [:sound, :projector, :tools].each do |need|
|
||||
= check_box_tag "needs[#{need.to_s}]", 1, JSON.parse(@workshop.needs || '[]').include?(need.to_s)
|
||||
= label_tag "needs_#{need}" do
|
||||
= check_box_tag "needs[#{need}]", 1, @needs.include?(need)
|
||||
= _"workshop.options.needs.#{need}"
|
||||
= _"workshop.options.needs.#{need.to_s}"
|
||||
%h3=_'articles.workshops.headings.space','Type of space'
|
||||
.input-field-help=_'articles.workshops.paragraphs.space', :p
|
||||
- [:meeting_room, :workshop, :outdoor_meeting].each do |space|
|
||||
.single-radio-button-field.input-field
|
||||
.input-field-help=_'articles.workshops.paragraphs.space', :s, 2
|
||||
.check-box-field.vertical.input-field
|
||||
- [:meeting_room, :workshop, :outdoor_meeting].each do |space|
|
||||
= radio_button_tag :space, space, @workshop.space == space
|
||||
= label_tag "space_#{space}" do
|
||||
= radio_button_tag :space, space, @space == space
|
||||
= _"workshop.options.space.#{space}"
|
||||
%h3=_'articles.workshops.headings.needs_facilitators','Looking for help?'
|
||||
.input-field-help=_'articles.workshops.paragraphs.needs_facilitators', :s, 2
|
||||
.check-box-field.vertical.input-field
|
||||
= check_box_tag :needs_facilitators, 1, @workshop.needs_facilitators
|
||||
= label_tag :needs_facilitators do
|
||||
= _'workshop.options.needs_facilitators', 'Needs Additional Facilitators'
|
||||
= columns(medium: 12) do
|
||||
%h3=_'articles.workshops.headings.notes','Notes for Conference Organizers and Workshop Facilitators'
|
||||
%p=_'articles.workshops.paragraphs.notes','Notes are only viewable by conference hosts and workshop facilitators'
|
||||
.text-area-field.input-field
|
||||
= label_tag :notes
|
||||
= text_area_tag :notes, @notes
|
||||
.warning-info=_'articles.workshops.paragraphs.notes','Notes are only viewable by conference hosts and workshop facilitators'
|
||||
.textarea#notes{data: {name: :notes}}=(@workshop.notes || '').html_safe
|
||||
= columns(medium: 12) do
|
||||
.actions.right
|
||||
= button_tag :save, :value => :save
|
||||
.actions.next-prev
|
||||
= button_tag :save, value: :save
|
||||
= button_tag :cancel, value: :cancel, class: :subdued, formnovalidate: true
|
||||
|
||||
|
@ -1,8 +1,20 @@
|
||||
= render 'conferences/page_header', :page_key => 'View_Workshop'
|
||||
= render 'conferences/page_header', :page_key => 'Conference_Registration'
|
||||
|
||||
= row id: 'registration-steps', class: 'flow-steps' do
|
||||
= columns do
|
||||
%ul
|
||||
- current_registration_steps.each do | step |
|
||||
- text = _"articles.conference_registration.headings.#{step[:name].to_s}"
|
||||
%li{class: [step[:enabled] ? :enabled : nil, :workshops == step[:name] ? :current : nil]}
|
||||
- if step[:enabled]
|
||||
.step= link_to text, register_step_path(@this_conference.slug, step[:name])
|
||||
- else
|
||||
.step= text
|
||||
|
||||
%article
|
||||
= render 'workshops/show', :workshop => @workshop, :preview => false
|
||||
= render 'workshops/show', :workshop => @workshop, :translations_available_for_editing => @translations_available_for_editing, :preview => false
|
||||
= row do
|
||||
= columns(medium: 12) do
|
||||
.actions.right
|
||||
= (link_to (_'actions.workshops.Edit'), edit_workshop_path(@this_conference.slug, @workshop.id), :class => 'button modify') if @workshop.can_edit?(current_user)
|
||||
= (link_to (_'actions.workshops.Edit'), edit_workshop_path(@this_conference.slug, @workshop.id), :class => [:button, :modify]) if @workshop.can_edit?(current_user)
|
||||
= (link_to (_'actions.workshops.Delete'), delete_workshop_path(@this_conference.slug, @workshop.id), :class => 'button delete') if @workshop.can_delete?(current_user)
|
||||
|
49
config/deploy.rb
Normal file
49
config/deploy.rb
Normal file
@ -0,0 +1,49 @@
|
||||
# config valid only for current version of Capistrano
|
||||
lock '3.5.0'
|
||||
|
||||
set :application, 'my_app_name'
|
||||
set :repo_url, 'git@example.com:me/my_repo.git'
|
||||
|
||||
# Default branch is :master
|
||||
# ask :branch, `git rev-parse --abbrev-ref HEAD`.chomp
|
||||
|
||||
# Default deploy_to directory is /var/www/my_app_name
|
||||
# set :deploy_to, '/var/www/my_app_name'
|
||||
|
||||
# Default value for :scm is :git
|
||||
# set :scm, :git
|
||||
|
||||
# Default value for :format is :airbrussh.
|
||||
# set :format, :airbrussh
|
||||
|
||||
# You can configure the Airbrussh format using :format_options.
|
||||
# These are the defaults.
|
||||
# set :format_options, command_output: true, log_file: 'log/capistrano.log', color: :auto, truncate: :auto
|
||||
|
||||
# Default value for :pty is false
|
||||
# set :pty, true
|
||||
|
||||
# Default value for :linked_files is []
|
||||
# set :linked_files, fetch(:linked_files, []).push('config/database.yml', 'config/secrets.yml')
|
||||
|
||||
# Default value for linked_dirs is []
|
||||
# set :linked_dirs, fetch(:linked_dirs, []).push('log', 'tmp/pids', 'tmp/cache', 'tmp/sockets', 'public/system')
|
||||
|
||||
# Default value for default_env is {}
|
||||
# set :default_env, { path: "/opt/ruby/bin:$PATH" }
|
||||
|
||||
# Default value for keep_releases is 5
|
||||
# set :keep_releases, 5
|
||||
|
||||
namespace :deploy do
|
||||
|
||||
after :restart, :clear_cache do
|
||||
on roles(:web), in: :groups, limit: 3, wait: 10 do
|
||||
# Here we can do anything such as:
|
||||
# within release_path do
|
||||
# execute :rake, 'cache:clear'
|
||||
# end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
61
config/deploy/production.rb
Normal file
61
config/deploy/production.rb
Normal file
@ -0,0 +1,61 @@
|
||||
# server-based syntax
|
||||
# ======================
|
||||
# Defines a single server with a list of roles and multiple properties.
|
||||
# You can define all roles on a single server, or split them:
|
||||
|
||||
# server 'example.com', user: 'deploy', roles: %w{app db web}, my_property: :my_value
|
||||
# server 'example.com', user: 'deploy', roles: %w{app web}, other_property: :other_value
|
||||
# server 'db.example.com', user: 'deploy', roles: %w{db}
|
||||
|
||||
|
||||
|
||||
# role-based syntax
|
||||
# ==================
|
||||
|
||||
# Defines a role with one or multiple servers. The primary server in each
|
||||
# group is considered to be the first unless any hosts have the primary
|
||||
# property set. Specify the username and a domain or IP for the server.
|
||||
# Don't use `:all`, it's a meta role.
|
||||
|
||||
# role :app, %w{deploy@example.com}, my_property: :my_value
|
||||
# role :web, %w{user1@primary.com user2@additional.com}, other_property: :other_value
|
||||
# role :db, %w{deploy@example.com}
|
||||
|
||||
|
||||
|
||||
# Configuration
|
||||
# =============
|
||||
# You can set any configuration variable like in config/deploy.rb
|
||||
# These variables are then only loaded and set in this stage.
|
||||
# For available Capistrano configuration variables see the documentation page.
|
||||
# http://capistranorb.com/documentation/getting-started/configuration/
|
||||
# Feel free to add new variables to customise your setup.
|
||||
|
||||
|
||||
|
||||
# Custom SSH Options
|
||||
# ==================
|
||||
# You may pass any option but keep in mind that net/ssh understands a
|
||||
# limited set of options, consult the Net::SSH documentation.
|
||||
# http://net-ssh.github.io/net-ssh/classes/Net/SSH.html#method-c-start
|
||||
#
|
||||
# Global options
|
||||
# --------------
|
||||
# set :ssh_options, {
|
||||
# keys: %w(/home/rlisowski/.ssh/id_rsa),
|
||||
# forward_agent: false,
|
||||
# auth_methods: %w(password)
|
||||
# }
|
||||
#
|
||||
# The server-based syntax can be used to override options:
|
||||
# ------------------------------------
|
||||
# server 'example.com',
|
||||
# user: 'user_name',
|
||||
# roles: %w{web app},
|
||||
# ssh_options: {
|
||||
# user: 'user_name', # overrides user setting above
|
||||
# keys: %w(/home/user_name/.ssh/id_rsa),
|
||||
# forward_agent: false,
|
||||
# auth_methods: %w(publickey password)
|
||||
# # password: 'please use keys'
|
||||
# }
|
61
config/deploy/staging.rb
Normal file
61
config/deploy/staging.rb
Normal file
@ -0,0 +1,61 @@
|
||||
# server-based syntax
|
||||
# ======================
|
||||
# Defines a single server with a list of roles and multiple properties.
|
||||
# You can define all roles on a single server, or split them:
|
||||
|
||||
# server 'example.com', user: 'deploy', roles: %w{app db web}, my_property: :my_value
|
||||
# server 'example.com', user: 'deploy', roles: %w{app web}, other_property: :other_value
|
||||
# server 'db.example.com', user: 'deploy', roles: %w{db}
|
||||
|
||||
|
||||
|
||||
# role-based syntax
|
||||
# ==================
|
||||
|
||||
# Defines a role with one or multiple servers. The primary server in each
|
||||
# group is considered to be the first unless any hosts have the primary
|
||||
# property set. Specify the username and a domain or IP for the server.
|
||||
# Don't use `:all`, it's a meta role.
|
||||
|
||||
# role :app, %w{deploy@example.com}, my_property: :my_value
|
||||
# role :web, %w{user1@primary.com user2@additional.com}, other_property: :other_value
|
||||
# role :db, %w{deploy@example.com}
|
||||
|
||||
|
||||
|
||||
# Configuration
|
||||
# =============
|
||||
# You can set any configuration variable like in config/deploy.rb
|
||||
# These variables are then only loaded and set in this stage.
|
||||
# For available Capistrano configuration variables see the documentation page.
|
||||
# http://capistranorb.com/documentation/getting-started/configuration/
|
||||
# Feel free to add new variables to customise your setup.
|
||||
|
||||
|
||||
|
||||
# Custom SSH Options
|
||||
# ==================
|
||||
# You may pass any option but keep in mind that net/ssh understands a
|
||||
# limited set of options, consult the Net::SSH documentation.
|
||||
# http://net-ssh.github.io/net-ssh/classes/Net/SSH.html#method-c-start
|
||||
#
|
||||
# Global options
|
||||
# --------------
|
||||
# set :ssh_options, {
|
||||
# keys: %w(/home/rlisowski/.ssh/id_rsa),
|
||||
# forward_agent: false,
|
||||
# auth_methods: %w(password)
|
||||
# }
|
||||
#
|
||||
# The server-based syntax can be used to override options:
|
||||
# ------------------------------------
|
||||
# server 'example.com',
|
||||
# user: 'user_name',
|
||||
# roles: %w{web app},
|
||||
# ssh_options: {
|
||||
# user: 'user_name', # overrides user setting above
|
||||
# keys: %w(/home/user_name/.ssh/id_rsa),
|
||||
# forward_agent: false,
|
||||
# auth_methods: %w(publickey password)
|
||||
# # password: 'please use keys'
|
||||
# }
|
@ -30,10 +30,11 @@ BikeBike::Application.configure do
|
||||
# This option may cause significant delays in view rendering with a large
|
||||
# number of complex assets.
|
||||
config.assets.debug = false
|
||||
config.assets.digest = false
|
||||
config.assets.compile = true
|
||||
|
||||
config.action_mailer.delivery_method = :smtp
|
||||
config.action_mailer.smtp_settings = {
|
||||
:enable_starttls_auto => true,
|
||||
:address => 'mail.bikebike.org',
|
||||
:domain => 'preview.bikebike.org',
|
||||
:port => 587,
|
||||
@ -45,15 +46,10 @@ BikeBike::Application.configure do
|
||||
}
|
||||
config.action_mailer.raise_delivery_errors = true
|
||||
config.action_mailer.perform_deliveries = true
|
||||
#config.force_ssl = true
|
||||
#config.action_mailer.default_charset = 'utf-8'
|
||||
|
||||
#Carmen.i18n_backend.locale_paths = ''
|
||||
#puts "CARMEN\t" + Carmen.i18n_backend.locale_paths
|
||||
|
||||
#PerfTools::CpuProfiler.start('/tmp/dev_prof')
|
||||
config.serve_static_files = true
|
||||
#config.assets.precompile = false
|
||||
# config.action_controller.perform_caching = true
|
||||
|
||||
Paypal.sandbox!
|
||||
#Paypal.sandbox = false
|
||||
end
|
||||
|
@ -87,7 +87,6 @@ BikeBike::Application.configure do
|
||||
|
||||
config.action_mailer.delivery_method = :smtp
|
||||
config.action_mailer.smtp_settings = {
|
||||
:enable_starttls_auto => true,
|
||||
:address => 'mail.bikebike.org',
|
||||
:domain => 'preview.bikebike.org',
|
||||
:port => 587,
|
||||
|
@ -88,7 +88,6 @@ BikeBike::Application.configure do
|
||||
|
||||
config.action_mailer.delivery_method = :smtp
|
||||
config.action_mailer.smtp_settings = {
|
||||
:enable_starttls_auto => true,
|
||||
:address => 'mail.bikebike.org',
|
||||
:domain => 'preview.bikebike.org',
|
||||
:port => 587,
|
||||
|
11
config/initializers/assets.rb
Normal file
11
config/initializers/assets.rb
Normal file
@ -0,0 +1,11 @@
|
||||
# Be sure to restart your server when you modify this file.
|
||||
|
||||
# Version of your assets, change this if you want to expire all your assets.
|
||||
Rails.application.config.assets.version = '1.0'
|
||||
|
||||
# Add additional assets to the asset load path
|
||||
# Rails.application.config.assets.paths << Emoji.images_path
|
||||
|
||||
# Precompile additional assets.
|
||||
# application.js, application.css, and all non-JS/CSS in app/assets folder are already added.
|
||||
Rails.application.config.assets.precompile += %w( map.js pen.js editor.js markdown.js main.js favicon.ico )
|
@ -3,7 +3,19 @@
|
||||
# Available submodules are: :user_activation, :http_basic_auth, :remember_me,
|
||||
# :reset_password, :session_timeout, :brute_force_protection, :activity_logging, :external
|
||||
# Rails.application.config.sorcery.submodules = [:remember_me, :reset_password, :user_activation, :brute_force_protection, :external]
|
||||
Rails.application.config.sorcery.submodules = [:remember_me, :reset_password, :brute_force_protection, :external]
|
||||
require 'sorcery'
|
||||
require 'openssl'
|
||||
|
||||
OpenSSL::SSL::VERIFY_PEER = OpenSSL::SSL::VERIFY_NONE if Rails.env.development?
|
||||
|
||||
ENV['bb_host'] = {
|
||||
test: 'localhost:3000',
|
||||
development: 'development.bikebike.org:3000',
|
||||
preview: 'preview.bikebike.org',
|
||||
production: 'bikebike.org',
|
||||
}[Rails.env]
|
||||
|
||||
Rails.application.config.sorcery.submodules = [:external]
|
||||
|
||||
# Here you can configure each submodule's features.
|
||||
Rails.application.config.sorcery.configure do |config|
|
||||
@ -112,11 +124,13 @@ Rails.application.config.sorcery.configure do |config|
|
||||
|
||||
config.facebook.key = "257350517701074"
|
||||
config.facebook.secret = "2f6ab1fd7eeff9aee73140991fc68314"
|
||||
config.facebook.callback_url = "http://dev.bikebike.org/oauth/callback?provider=facebook"
|
||||
config.facebook.user_info_mapping = {:email => "email", :username => "username", :avatar => "picture/data/url"}
|
||||
config.facebook.callback_url = "#{ENV['bb_host']}/oauth/callback?provider=facebook"
|
||||
config.facebook.user_info_mapping = {:email => "email", :username => "username"}
|
||||
config.facebook.scope = "email"
|
||||
config.facebook.display = "popup"
|
||||
|
||||
config.facebook.api_version = "v2.5"
|
||||
config.facebook.user_info_path = "me?fields=email,name"
|
||||
|
||||
# config.github.key = ""
|
||||
# config.github.secret = ""
|
||||
# config.github.callback_url = "http://0.0.0.0:3000/oauth/callback?provider=github"
|
||||
@ -306,7 +320,7 @@ Rails.application.config.sorcery.configure do |config|
|
||||
# mailer class. Needed.
|
||||
# Default: `nil`
|
||||
#
|
||||
user.reset_password_mailer = UserMailer
|
||||
# user.reset_password_mailer = UserMailer
|
||||
|
||||
|
||||
# reset password email method on your mailer class.
|
||||
@ -378,7 +392,7 @@ Rails.application.config.sorcery.configure do |config|
|
||||
# Unlock token mailer class
|
||||
# Default: `nil`
|
||||
#
|
||||
user.unlock_token_mailer = UserMailer
|
||||
# user.unlock_token_mailer = UserMailer
|
||||
|
||||
# -- activity logging --
|
||||
# Last login attribute name.
|
||||
|
@ -5212,6 +5212,7 @@ en:
|
||||
un_translated: Untranslated content
|
||||
outdated_translation: Outdated content
|
||||
not_translation: Not a translation
|
||||
Translation_of: Translation of
|
||||
volunteer:
|
||||
become_a_volunteer: Become a volunteer translator
|
||||
volunteer: Can you help us translate it?
|
||||
@ -5294,6 +5295,7 @@ en:
|
||||
conference_registration:
|
||||
headings:
|
||||
Policy_Agreement: Safer Space Agreement
|
||||
policy: Policy
|
||||
arrival_and_departure: For what days will you need housing? (If you don't
|
||||
need housing, just tell us how long you plan to hang out with Bike!Bike!)
|
||||
other: Is there anything else you'd like to tell us?
|
||||
@ -5316,6 +5318,7 @@ en:
|
||||
Allergies: Allergies
|
||||
Stats: Stats
|
||||
Workshops: Workshops
|
||||
workshops: Workshops
|
||||
Your_Workshops: Your Workshops
|
||||
payment_confirm: Please confirm your payment
|
||||
Preview: Preview
|
||||
@ -5323,8 +5326,13 @@ en:
|
||||
date: Date
|
||||
email: Email
|
||||
fees_paid: Fees Paid
|
||||
Contact_Info: Contact Info
|
||||
contact_info: Contact Info
|
||||
Add_Workshop: Propose a Workshop
|
||||
Workshops_Looking_For_Facilitators: Workshops Looking for Facilitators
|
||||
All_Workshops: All Workshops
|
||||
paragraphs:
|
||||
Policy_Agreement: Safer Space Agreement
|
||||
Policy_Agreement: Ensuring that all attendees feel welcome, safe, and respected at all times is especially important to us all. Please ensure that you have fully read and understand our safer spaces policy below, if you have any questions or concerns you can reach out to the organizers at any time.
|
||||
Confirm_Agreement: By clicking the "I Agree" button, you are pledging to do
|
||||
your best to uphold Bike!Bike!'s safer space agreement. Thank you!
|
||||
Registration_Info: Please fill in this registration form to help us prepare
|
||||
@ -5361,6 +5369,10 @@ en:
|
||||
participants_emailed: Your email has been sent to all participants of %%{conference_title}.
|
||||
workshops: You can now take a look at proposed workshops and even propose
|
||||
one yourself if you like.
|
||||
Contact_Info: Please let us know a little bit about you.
|
||||
Create_Workshop: At Bike!Bike! anyone can lead a workshop or just propose and idea that someone else can volunteer to lead. If, where, and when the workshop will be scheduled will ultimately be decided by the conference organizers.
|
||||
Workshops_Looking_For_Facilitators: Would you like to lend a hand facilitating a workshop proposed by someone else? Below is a list of workshops that are actively looking for volunteers, if you are interested in helping out you can make a facilitation request.
|
||||
Your_Workshops: The following is a list of all the workshops that you have created or have requested to facilitate.
|
||||
questions:
|
||||
bike:
|
||||
large: Large
|
||||
@ -5462,13 +5474,13 @@ en:
|
||||
Workshops: Workshops
|
||||
Your_Workshops: Your Workshops
|
||||
facilitate: Request to Facilitate ‘%{workshop_title}’
|
||||
add_facilitator: Add a facilitator
|
||||
needs_facilitators: Looking for help?
|
||||
paragraphs:
|
||||
Proposed_Workshops: Would you like to facilitate your own workshop? Simply
|
||||
register and visit the workshops page. If you have already registered you
|
||||
can access the page by restarting the registration process.
|
||||
info: Please accurately describe your workshop in detail. This will help hosts
|
||||
decide if they wish to add it to the schedule and when it should best be
|
||||
scheduled. Enter normal text but if you want to get fancy you can use [Markdown](http://daringfireball.net/projects/markdown/basics)
|
||||
info: Describe your workshop in detail; highlighting text will reveal formatting controls.
|
||||
space: What kind of space do you need for your workshop?
|
||||
theme: Which of the themes below best match your workshop? This will help
|
||||
hosts to avoid scheduling conflicts. Select other if none of the options
|
||||
@ -5495,6 +5507,9 @@ en:
|
||||
facilitate_request_sent: Your request has been sent. You will receive an email
|
||||
once your request is approved or denied or if the current facilitators have
|
||||
any questions.
|
||||
languages: What language will the workshop be presented in? Will there be translations or translators available?
|
||||
needs: If you need any of the following the conference organizers will do their best to make sure you have them. If you have any other requests, you can include them in the notes section.
|
||||
needs_facilitators: Are you actively looking for help running this workshop? Anyone who is registered for the conference can request to facilitate at any time but checking this box will improve your chances of finding collaborators.
|
||||
info:
|
||||
interested_count:
|
||||
one: One person is interested in this workshop
|
||||
@ -5522,7 +5537,7 @@ en:
|
||||
name: Name
|
||||
subject: Subject
|
||||
title: Title
|
||||
info: Info
|
||||
info: Description
|
||||
content: Content
|
||||
notes: Notes
|
||||
message: 'Your Message:'
|
||||
@ -5546,6 +5561,8 @@ en:
|
||||
remove_interest: "-1"
|
||||
show_interest: "+1"
|
||||
add: "+"
|
||||
previous: Previous
|
||||
next: Next
|
||||
page_titles:
|
||||
'403':
|
||||
Please_Confirm_Email: Please confirm your email
|
||||
@ -5562,8 +5579,9 @@ en:
|
||||
Registration_Stats: Registration Stats
|
||||
View_Workshop: View Workshop
|
||||
Workshops: Workshops
|
||||
Delete_Workshop: Create Workshop
|
||||
Delete_Workshop: Delete Workshop
|
||||
Edit_Workshop: Edit Workshop
|
||||
Translate_Workshop: Edit %{language} Workshop Translation
|
||||
Edit: Edit Conference
|
||||
Facilitate_Workshop: Workshop Facilitation Request
|
||||
policy:
|
||||
@ -5572,6 +5590,8 @@ en:
|
||||
Page_Not_Found: Page Not Found
|
||||
Policy: 'Policy'
|
||||
About: 'About'
|
||||
Register: 'Register'
|
||||
Pre_Register: 'Pre-Register'
|
||||
links:
|
||||
footer:
|
||||
help_text:
|
||||
@ -5640,6 +5660,7 @@ en:
|
||||
organization: Organizational Concerns
|
||||
other: Other
|
||||
race_gender: Race, Gender, or Class Politics
|
||||
needs_facilitators: Needs Additional Facilitators
|
||||
email:
|
||||
confirmation:
|
||||
paragraph:
|
||||
@ -5650,10 +5671,13 @@ en:
|
||||
subject:
|
||||
confirm_email: Confirmation Email
|
||||
registration_confirmed: Thank you for registering for %{conference_title}
|
||||
pre_registration_confirmed: Thank you for pre-registering for %{conference_title}
|
||||
workshop_facilitator_request: Request to facilitate %{workshop_title} from %{requester_name}
|
||||
workshop_request_approved: You have been added as a facilitator of %{workshop_title}
|
||||
workshop_request_denied: Your request to facilitate %{workshop_title} has been
|
||||
denied
|
||||
workshop_translated: The %{language} translation for %{workshop_title} has been modified
|
||||
workshop_original_content_changed: Original content for %{workshop_title} has been modified
|
||||
general:
|
||||
paragraph:
|
||||
see_you: See you in %{conference_location}!
|
||||
@ -5665,7 +5689,14 @@ en:
|
||||
the registration process. If you have yet to pay or add your workshops and
|
||||
plan to do so, we ask that you take care of it as soon as possible to help
|
||||
us prepare in advance of your arrival.
|
||||
workshop:
|
||||
translations:
|
||||
headings:
|
||||
new_value: 'New Value: '
|
||||
old_value: 'Old Value: '
|
||||
diff: 'Difference: '
|
||||
paragraph:
|
||||
workshop_translated: '%{user_name} has modified the %{language} translation for %{workshop_title}.'
|
||||
workshop_original_content_changed: '%{user_name} has modified the original content for %{workshop_title}. Translations may need to be updated.'
|
||||
paragraph:
|
||||
request_approved: You have been added as a facilitator of %{workshop_title}.
|
||||
request_denied: Your request to become a facilitator of %{workshop_title}
|
||||
|
@ -702,7 +702,7 @@ es:
|
||||
actions:
|
||||
Register: Registrate
|
||||
donate:
|
||||
button_label: donar
|
||||
button_label: Donar
|
||||
articles:
|
||||
conference_registration:
|
||||
headings:
|
||||
@ -744,7 +744,6 @@ es:
|
||||
en: bikebike@gdlenbici.org
|
||||
Workshops: 'Título de la actividad: Que necesitas? Tipo de espacio? Tema?
|
||||
Quién es responsable? Descripción? Tiempo?'
|
||||
Policy_Agreement: Acuerdo de Espacios Más Seguros
|
||||
Confirm_Agreement: Al hacer click en el botón "Acepto", estás comprometiéndote
|
||||
a cumplir el Acuerdo de Espacios Más Seguros de Bike!Bike!. ¡Gracias!
|
||||
Email_Participants: Esta página se usa para contactar a todxs lxs participantes.
|
||||
@ -851,8 +850,6 @@ es:
|
||||
Proposed_Workshops: ¿Te gustaría facilitar tu propio taller? Simplemente regístrate
|
||||
y visita la página de Talleres. Si ya te registraste puedes acceder a la
|
||||
página reiniciando el proceso de registro.
|
||||
info: 'Título de la actividad: Que necesitas? Tipo de espacio? Tema? Quién
|
||||
es responsable? Descripción? Tiempo?'
|
||||
Workshops: ¿Tienes alguna habilidad emocionante que quieres compartir con
|
||||
nosotros? ¿Quieres charlar sobre crear espacios comunitarios seguros? ¿Quieres
|
||||
asegurarte de que hagamos un buen paseo el fin de semana? ¡Propón un taller!
|
||||
@ -1012,6 +1009,8 @@ es:
|
||||
Page_Not_Found: Crear un taller
|
||||
Policy: 'Política'
|
||||
About: 'Acerca de'
|
||||
Register: 'Registrate'
|
||||
Pre_Register: 'Pre-Registrate'
|
||||
actions:
|
||||
workshops:
|
||||
create: Crear
|
||||
|
@ -6,6 +6,7 @@ BikeBike::Application.routes.draw do
|
||||
post '/conferences/:slug/save' => 'conferences#save', :as => :save_conference
|
||||
|
||||
match '/conferences/:slug/register' => 'conferences#register', :as => :register, via: [:get, :post]
|
||||
get '/conferences/:slug/register/:step' => 'conferences#register', :as => :register_step
|
||||
match '/conferences/:slug/broadcast' => 'conferences#broadcast', :as => :broadcast, via: [:get, :post]
|
||||
get '/conferences/:slug/stats' => 'conferences#stats', :as => :stats
|
||||
get '/conferences/:slug/register/:button/:confirmation_token' => 'conferences#register', :as => :register_paypal_confirm
|
||||
@ -24,12 +25,13 @@ BikeBike::Application.routes.draw do
|
||||
get '/conferences/:slug/schedule/event/:id' => 'conferences#view_event', :as => :view_event
|
||||
get '/conferences/:slug/schedule/event/:id/edit' => 'conferences#edit_event', :as => :edit_event
|
||||
|
||||
get '/conferences/:slug/workshops' => 'conferences#workshops', :as => :workshops
|
||||
# get '/conferences/:slug/workshops' => 'conferences#workshops', :as => :workshops
|
||||
match '/conferences/:slug/workshops/create' => 'conferences#create_workshop', :as => :create_workshop, via: [:get, :post]
|
||||
post '/conferences/:slug/workshops/save' => 'conferences#save_workshop', :as => :save_workshop
|
||||
get '/conferences/:slug/workshops/:workshop_id' => 'conferences#view_workshop', :as => :view_workshop
|
||||
post '/conferences/:slug/workshops/:workshop_id/toggle-interest' => 'conferences#toggle_workshop_interest', :as => :toggle_workshop_interest
|
||||
match '/conferences/:slug/workshops/:workshop_id/edit' => 'conferences#edit_workshop', :as => :edit_workshop, via: [:get, :post]
|
||||
match '/conferences/:slug/workshops/:workshop_id/translate/:locale' => 'conferences#translate_workshop', :as => :translate_workshop, via: [:get, :post]
|
||||
match '/conferences/:slug/workshops/:workshop_id/delete' => 'conferences#delete_workshop', :as => :delete_workshop, via: [:get, :post]
|
||||
get '/conferences/:slug/workshops/:workshop_id/facilitate' => 'conferences#facilitate_workshop', :as => :facilitate_workshop
|
||||
post '/conferences/:slug/workshops/:workshop_id/facilitate_request' => 'conferences#facilitate_request', :as => :facilitate_workshop_request
|
||||
@ -43,7 +45,9 @@ BikeBike::Application.routes.draw do
|
||||
get '/confirm/:token' => 'application#confirm', :as => :confirm
|
||||
match '/doconfirm' => 'application#do_confirm', :as => :do_confirm, via: [:get, :post]
|
||||
#post '/doconfirm' => 'application#do_confirm', :as => :do_confirm
|
||||
post '/logout' => 'application#user_logout', :as => :logout
|
||||
match '/logout' => 'application#user_logout', :as => :logout, :via => [:get, :post]
|
||||
match '/oauth/callback' => 'oauths#callback', :via => [:get, :post]
|
||||
get '/oauth/:provider' => 'oauths#oauth', :as => :auth_at_provider
|
||||
post '/translator-request' => 'application#translator_request', :as => :translator_request
|
||||
|
||||
get '/error_404' => 'application#error_404'
|
||||
|
@ -0,0 +1,9 @@
|
||||
class AddRegistrationStatusToConferences < ActiveRecord::Migration
|
||||
def change
|
||||
add_column :conferences, :registration_status, :string
|
||||
Conference.find_each do |conference|
|
||||
conference.registration_status = conference.registration_open ? :open : :closed
|
||||
conference.save!
|
||||
end
|
||||
end
|
||||
end
|
@ -0,0 +1,5 @@
|
||||
class AddHighestStepToConferenceRegistrations < ActiveRecord::Migration
|
||||
def change
|
||||
add_column :conference_registrations, :highest_step, :string
|
||||
end
|
||||
end
|
@ -0,0 +1,5 @@
|
||||
class AddStepsCompletedToConferenceRegistrations < ActiveRecord::Migration
|
||||
def change
|
||||
add_column :conference_registrations, :steps_completed, :json
|
||||
end
|
||||
end
|
@ -0,0 +1,5 @@
|
||||
class AddNeedsFacilitatorsToWorkshops < ActiveRecord::Migration
|
||||
def change
|
||||
add_column :workshops, :needs_facilitators, :boolean
|
||||
end
|
||||
end
|
5
db/migrate/20160529225253_add_languages_to_users.rb
Normal file
5
db/migrate/20160529225253_add_languages_to_users.rb
Normal file
@ -0,0 +1,5 @@
|
||||
class AddLanguagesToUsers < ActiveRecord::Migration
|
||||
def change
|
||||
add_column :users, :languages, :json
|
||||
end
|
||||
end
|
@ -0,0 +1,5 @@
|
||||
class ChangeDateFormatInDynamicTranslationRecords < ActiveRecord::Migration
|
||||
def change
|
||||
change_column :dynamic_translation_records, :created_at, :timestamp
|
||||
end
|
||||
end
|
23
db/schema.rb
23
db/schema.rb
@ -11,7 +11,7 @@
|
||||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema.define(version: 20160304021005) do
|
||||
ActiveRecord::Schema.define(version: 20160530175805) do
|
||||
|
||||
# These are extensions that must be enabled in order to support this database
|
||||
enable_extension "plpgsql"
|
||||
@ -81,6 +81,8 @@ ActiveRecord::Schema.define(version: 20160304021005) do
|
||||
t.string "allergies"
|
||||
t.string "languages"
|
||||
t.string "food"
|
||||
t.string "highest_step"
|
||||
t.json "steps_completed"
|
||||
end
|
||||
|
||||
create_table "conference_types", force: :cascade do |t|
|
||||
@ -121,6 +123,7 @@ ActiveRecord::Schema.define(version: 20160304021005) do
|
||||
t.string "paypal_password"
|
||||
t.string "paypal_signature"
|
||||
t.string "day_parts"
|
||||
t.string "registration_status"
|
||||
end
|
||||
|
||||
create_table "delayed_jobs", force: :cascade do |t|
|
||||
@ -140,13 +143,13 @@ ActiveRecord::Schema.define(version: 20160304021005) do
|
||||
add_index "delayed_jobs", ["priority", "run_at"], name: "delayed_jobs_priority", using: :btree
|
||||
|
||||
create_table "dynamic_translation_records", force: :cascade do |t|
|
||||
t.string "locale"
|
||||
t.integer "translator_id"
|
||||
t.string "model_type"
|
||||
t.integer "model_id"
|
||||
t.string "column"
|
||||
t.text "value"
|
||||
t.date "created_at"
|
||||
t.string "locale"
|
||||
t.integer "translator_id"
|
||||
t.string "model_type"
|
||||
t.integer "model_id"
|
||||
t.string "column"
|
||||
t.text "value"
|
||||
t.datetime "created_at"
|
||||
end
|
||||
|
||||
create_table "email_confirmations", force: :cascade do |t|
|
||||
@ -188,7 +191,7 @@ ActiveRecord::Schema.define(version: 20160304021005) do
|
||||
t.datetime "created_at"
|
||||
t.datetime "updated_at"
|
||||
t.integer "event_location_id"
|
||||
t.string "type"
|
||||
t.string "event_type"
|
||||
end
|
||||
|
||||
create_table "locations", force: :cascade do |t|
|
||||
@ -317,6 +320,7 @@ ActiveRecord::Schema.define(version: 20160304021005) do
|
||||
t.string "firstname"
|
||||
t.string "lastname"
|
||||
t.boolean "is_translator"
|
||||
t.json "languages"
|
||||
end
|
||||
|
||||
add_index "users", ["activation_token"], name: "index_users_on_activation_token", using: :btree
|
||||
@ -403,6 +407,7 @@ ActiveRecord::Schema.define(version: 20160304021005) do
|
||||
t.text "notes"
|
||||
t.string "locale"
|
||||
t.integer "event_location_id"
|
||||
t.boolean "needs_facilitators"
|
||||
end
|
||||
|
||||
end
|
||||
|
78
vendor/assets/javascripts/markdown.js
vendored
Normal file
78
vendor/assets/javascripts/markdown.js
vendored
Normal file
@ -0,0 +1,78 @@
|
||||
/*! Licensed under MIT, https://github.com/sofish/pen */
|
||||
(function(root) {
|
||||
|
||||
// only works with Pen
|
||||
if(!root.Pen) return;
|
||||
|
||||
// markdown covertor obj
|
||||
var covertor = {
|
||||
keymap: { '96': '`', '62': '>', '49': '1', '46': '.', '45': '-', '42': '*', '35': '#'},
|
||||
stack : []
|
||||
};
|
||||
|
||||
// return valid markdown syntax
|
||||
covertor.valid = function(str) {
|
||||
var len = str.length;
|
||||
|
||||
if(str.match(/[#]{1,6}/)) {
|
||||
return ['h' + len, len];
|
||||
} else if(str === '```') {
|
||||
return ['pre', len];
|
||||
} else if(str === '>') {
|
||||
return ['blockquote', len];
|
||||
} else if(str === '1.') {
|
||||
return ['insertorderedlist', len];
|
||||
} else if(str === '-' || str === '*') {
|
||||
return ['insertunorderedlist', len];
|
||||
} else if(str.match(/(?:\.|\*|\-){3,}/)) {
|
||||
return ['inserthorizontalrule', len];
|
||||
}
|
||||
};
|
||||
|
||||
// parse command
|
||||
covertor.parse = function(e) {
|
||||
var code = e.keyCode || e.which;
|
||||
|
||||
// when `space` is pressed
|
||||
if (code === 32) {
|
||||
var markdownSyntax = this.stack.join('');
|
||||
// reset stack
|
||||
this.stack = [];
|
||||
|
||||
var cmd = this.valid(markdownSyntax);
|
||||
if (cmd) {
|
||||
// prevents leading space after executing command
|
||||
e.preventDefault();
|
||||
return cmd;
|
||||
}
|
||||
}
|
||||
|
||||
// make cmd
|
||||
if(this.keymap[code]) this.stack.push(this.keymap[code]);
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
// exec command
|
||||
covertor.action = function(pen, cmd) {
|
||||
|
||||
// only apply effect at line start
|
||||
if(pen.selection.focusOffset > cmd[1]) return;
|
||||
|
||||
var node = pen.selection.focusNode;
|
||||
node.textContent = node.textContent.slice(cmd[1]);
|
||||
pen.execCommand(cmd[0]);
|
||||
};
|
||||
|
||||
// init covertor
|
||||
covertor.init = function(pen) {
|
||||
pen.on('keypress', function(e) {
|
||||
var cmd = covertor.parse(e);
|
||||
if(cmd) return covertor.action(pen, cmd);
|
||||
});
|
||||
};
|
||||
|
||||
// append to Pen
|
||||
root.Pen.prototype.markdown = covertor;
|
||||
|
||||
}(window));
|
835
vendor/assets/javascripts/pen.js
vendored
Normal file
835
vendor/assets/javascripts/pen.js
vendored
Normal file
@ -0,0 +1,835 @@
|
||||
/*! Licensed under MIT, https://github.com/sofish/pen */
|
||||
(function(root, doc) {
|
||||
|
||||
var Pen, debugMode, selection, utils = {};
|
||||
var toString = Object.prototype.toString;
|
||||
var slice = Array.prototype.slice;
|
||||
|
||||
// allow command list
|
||||
var commandsReg = {
|
||||
block: /^(?:p|h[1-6]|blockquote|pre)$/,
|
||||
inline: /^(?:bold|italic|underline|insertorderedlist|insertunorderedlist|indent|outdent|strikethrough)$/,
|
||||
source: /^(?:createlink|unlink)$/,
|
||||
insert: /^(?:inserthorizontalrule|insertimage|insert)$/,
|
||||
wrap: /^(?:code)$/
|
||||
};
|
||||
|
||||
var lineBreakReg = /^(?:blockquote|pre|div)$/i;
|
||||
|
||||
var effectNodeReg = /(?:[pubia]|h[1-6]|blockquote|[uo]l|li)/i;
|
||||
|
||||
var strReg = {
|
||||
whiteSpace: /(^\s+)|(\s+$)/g,
|
||||
mailTo: /^(?!mailto:|.+\/|.+#|.+\?)(.*@.*\..+)$/,
|
||||
http: /^(?!\w+?:\/\/|mailto:|\/|\.\/|\?|#)(.*)$/
|
||||
};
|
||||
|
||||
var autoLinkReg = {
|
||||
url: /((https?|ftp):\/\/|www\.)[^\s<]{3,}/gi,
|
||||
prefix: /^(?:https?|ftp):\/\//i,
|
||||
notLink: /^(?:img|a|input|audio|video|source|code|pre|script|head|title|style)$/i,
|
||||
maxLength: 100
|
||||
};
|
||||
|
||||
// type detect
|
||||
utils.is = function(obj, type) {
|
||||
return toString.call(obj).slice(8, -1) === type;
|
||||
};
|
||||
|
||||
utils.forEach = function(obj, iterator, arrayLike) {
|
||||
if (!obj) return;
|
||||
if (arrayLike == null) arrayLike = utils.is(obj, 'Array');
|
||||
if (arrayLike) {
|
||||
for (var i = 0, l = obj.length; i < l; i++) iterator(obj[i], i, obj);
|
||||
} else {
|
||||
for (var key in obj) {
|
||||
if (obj.hasOwnProperty(key)) iterator(obj[key], key, obj);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// copy props from a obj
|
||||
utils.copy = function(defaults, source) {
|
||||
utils.forEach(source, function (value, key) {
|
||||
defaults[key] = utils.is(value, 'Object') ? utils.copy({}, value) :
|
||||
utils.is(value, 'Array') ? utils.copy([], value) : value;
|
||||
});
|
||||
return defaults;
|
||||
};
|
||||
|
||||
// log
|
||||
utils.log = function(message, force) {
|
||||
if (debugMode || force)
|
||||
console.log('%cPEN DEBUGGER: %c' + message, 'font-family:arial,sans-serif;color:#1abf89;line-height:2em;', 'font-family:cursor,monospace;color:#333;');
|
||||
};
|
||||
|
||||
utils.delayExec = function (fn) {
|
||||
var timer = null;
|
||||
return function (delay) {
|
||||
clearTimeout(timer);
|
||||
timer = setTimeout(function() {
|
||||
fn();
|
||||
}, delay || 1);
|
||||
};
|
||||
};
|
||||
|
||||
// merge: make it easy to have a fallback
|
||||
utils.merge = function(config) {
|
||||
|
||||
// default settings
|
||||
var defaults = {
|
||||
class: 'pen',
|
||||
debug: false,
|
||||
toolbar: null, // custom toolbar
|
||||
stay: config.stay || !config.debug,
|
||||
stayMsg: 'Are you going to leave here?',
|
||||
textarea: '<textarea name="content"></textarea>',
|
||||
list: [
|
||||
'blockquote', 'h2', 'h3', 'p', 'code', 'insertorderedlist', 'insertunorderedlist', 'inserthorizontalrule',
|
||||
'indent', 'outdent', 'bold', 'italic', 'underline', 'createlink', 'insertimage'
|
||||
],
|
||||
titles: {},
|
||||
cleanAttrs: ['id', 'class', 'style', 'name'],
|
||||
cleanTags: ['script']
|
||||
};
|
||||
|
||||
// user-friendly config
|
||||
if (config.nodeType === 1) {
|
||||
defaults.editor = config;
|
||||
} else if (config.match && config.match(/^#[\S]+$/)) {
|
||||
defaults.editor = doc.getElementById(config.slice(1));
|
||||
} else {
|
||||
defaults = utils.copy(defaults, config);
|
||||
}
|
||||
|
||||
return defaults;
|
||||
};
|
||||
|
||||
function commandOverall(ctx, cmd, val) {
|
||||
var message = ' to exec 「' + cmd + '」 command' + (val ? (' with value: ' + val) : '');
|
||||
|
||||
try {
|
||||
doc.execCommand(cmd, false, val);
|
||||
} catch(err) {
|
||||
// TODO: there's an error when insert a image to document, bug not a bug
|
||||
return utils.log('fail' + message, true);
|
||||
}
|
||||
|
||||
utils.log('success' + message);
|
||||
}
|
||||
|
||||
function commandInsert(ctx, name, val) {
|
||||
var node = getNode(ctx);
|
||||
if (!node) return;
|
||||
ctx._range.selectNode(node);
|
||||
ctx._range.collapse(false);
|
||||
|
||||
// hide menu when a image was inserted
|
||||
if(name === 'insertimage' && ctx._menu) toggleNode(ctx._menu, true);
|
||||
|
||||
return commandOverall(ctx, name, val);
|
||||
}
|
||||
|
||||
function commandBlock(ctx, name) {
|
||||
var list = effectNode(ctx, getNode(ctx), true);
|
||||
if (list.indexOf(name) !== -1) name = 'p';
|
||||
return commandOverall(ctx, 'formatblock', name);
|
||||
}
|
||||
|
||||
function commandWrap(ctx, tag, value) {
|
||||
value = '<' + tag + '>' + (value||selection.toString()) + '</' + tag + '>';
|
||||
return commandOverall(ctx, 'insertHTML', value);
|
||||
}
|
||||
|
||||
function initToolbar(ctx) {
|
||||
var icons = '', inputStr = '<input class="pen-input" placeholder="http://" />';
|
||||
|
||||
ctx._toolbar = ctx.config.toolbar;
|
||||
if (!ctx._toolbar) {
|
||||
var toolList = ctx.config.list;
|
||||
utils.forEach(toolList, function (name) {
|
||||
var klass = 'pen-icon icon-' + name;
|
||||
var title = ctx.config.titles[name] || '';
|
||||
icons += '<i class="' + klass + '" data-action="' + name + '" title="' + title + '"></i>';
|
||||
}, true);
|
||||
if (toolList.indexOf('createlink') >= 0 || toolList.indexOf('insertimage') >= 0)
|
||||
icons += inputStr;
|
||||
} else if (ctx._toolbar.querySelectorAll('[data-action=createlink]').length ||
|
||||
ctx._toolbar.querySelectorAll('[data-action=insertimage]').length) {
|
||||
icons += inputStr;
|
||||
}
|
||||
|
||||
if (icons) {
|
||||
ctx._menu = doc.createElement('div');
|
||||
ctx._menu.setAttribute('class', ctx.config.class + '-menu pen-menu');
|
||||
ctx._menu.innerHTML = icons;
|
||||
ctx._inputBar = ctx._menu.querySelector('input');
|
||||
toggleNode(ctx._menu, true);
|
||||
doc.body.appendChild(ctx._menu);
|
||||
}
|
||||
if (ctx._toolbar && ctx._inputBar) toggleNode(ctx._inputBar);
|
||||
}
|
||||
|
||||
function initEvents(ctx) {
|
||||
var toolbar = ctx._toolbar || ctx._menu, editor = ctx.config.editor;
|
||||
|
||||
var toggleMenu = utils.delayExec(function() {
|
||||
ctx.highlight().menu();
|
||||
});
|
||||
var outsideClick = function() {};
|
||||
|
||||
function updateStatus(delay) {
|
||||
ctx._range = ctx.getRange();
|
||||
toggleMenu(delay);
|
||||
}
|
||||
|
||||
if (ctx._menu) {
|
||||
var setpos = function() {
|
||||
if (ctx._menu.style.display === 'block') ctx.menu();
|
||||
};
|
||||
|
||||
// change menu offset when window resize / scroll
|
||||
addListener(ctx, root, 'resize', setpos);
|
||||
addListener(ctx, root, 'scroll', setpos);
|
||||
|
||||
// toggle toolbar on mouse select
|
||||
var selecting = false;
|
||||
addListener(ctx, editor, 'mousedown', function() {
|
||||
selecting = true;
|
||||
});
|
||||
addListener(ctx, editor, 'mouseleave', function() {
|
||||
if (selecting) updateStatus(800);
|
||||
selecting = false;
|
||||
});
|
||||
addListener(ctx, editor, 'mouseup', function() {
|
||||
if (selecting) updateStatus(100);
|
||||
selecting = false;
|
||||
});
|
||||
// Hide menu when focusing outside of editor
|
||||
outsideClick = function(e) {
|
||||
if (ctx._menu && !containsNode(editor, e.target) && !containsNode(ctx._menu, e.target)) {
|
||||
removeListener(ctx, doc, 'click', outsideClick);
|
||||
toggleMenu(100);
|
||||
}
|
||||
};
|
||||
} else {
|
||||
addListener(ctx, editor, 'click', function() {
|
||||
updateStatus(0);
|
||||
});
|
||||
}
|
||||
|
||||
addListener(ctx, editor, 'keyup', function(e) {
|
||||
if (e.which === 8 && ctx.isEmpty()) return lineBreak(ctx, true);
|
||||
// toggle toolbar on key select
|
||||
if (e.which !== 13 || e.shiftKey) return updateStatus(400);
|
||||
var node = getNode(ctx, true);
|
||||
if (!node || !node.nextSibling || !lineBreakReg.test(node.nodeName)) return;
|
||||
if (node.nodeName !== node.nextSibling.nodeName) return;
|
||||
// hack for webkit, make 'enter' behavior like as firefox.
|
||||
if (node.lastChild.nodeName !== 'BR') node.appendChild(doc.createElement('br'));
|
||||
utils.forEach(node.nextSibling.childNodes, function(child) {
|
||||
if (child) node.appendChild(child);
|
||||
}, true);
|
||||
node.parentNode.removeChild(node.nextSibling);
|
||||
focusNode(ctx, node.lastChild, ctx.getRange());
|
||||
});
|
||||
|
||||
// check line break
|
||||
addListener(ctx, editor, 'keydown', function(e) {
|
||||
editor.classList.remove('pen-placeholder');
|
||||
if (e.which !== 13 || e.shiftKey) return;
|
||||
var node = getNode(ctx, true);
|
||||
if (!node || !lineBreakReg.test(node.nodeName)) return;
|
||||
var lastChild = node.lastChild;
|
||||
if (!lastChild || !lastChild.previousSibling) return;
|
||||
if (lastChild.previousSibling.textContent || lastChild.textContent) return;
|
||||
// quit block mode for 2 'enter'
|
||||
e.preventDefault();
|
||||
var p = doc.createElement('p');
|
||||
p.innerHTML = '<br>';
|
||||
node.removeChild(lastChild);
|
||||
if (!node.nextSibling) node.parentNode.appendChild(p);
|
||||
else node.parentNode.insertBefore(p, node.nextSibling);
|
||||
focusNode(ctx, p, ctx.getRange());
|
||||
});
|
||||
|
||||
var menuApply = function(action, value) {
|
||||
ctx.execCommand(action, value);
|
||||
ctx._range = ctx.getRange();
|
||||
ctx.highlight().menu();
|
||||
};
|
||||
|
||||
// toggle toolbar on key select
|
||||
addListener(ctx, toolbar, 'click', function(e) {
|
||||
var node = e.target, action;
|
||||
|
||||
while (node !== toolbar && !(action = node.getAttribute('data-action'))) {
|
||||
node = node.parentNode;
|
||||
}
|
||||
|
||||
if (!action) return;
|
||||
if (!/(?:createlink)|(?:insertimage)/.test(action)) return menuApply(action);
|
||||
if (!ctx._inputBar) return;
|
||||
|
||||
// create link
|
||||
var input = ctx._inputBar;
|
||||
if (toolbar === ctx._menu) toggleNode(input);
|
||||
else {
|
||||
ctx._inputActive = true;
|
||||
ctx.menu();
|
||||
}
|
||||
if (ctx._menu.style.display === 'none') return;
|
||||
|
||||
setTimeout(function() { input.focus(); }, 400);
|
||||
var createlink = function() {
|
||||
var inputValue = input.value;
|
||||
|
||||
if (!inputValue) action = 'unlink';
|
||||
else {
|
||||
inputValue = input.value
|
||||
.replace(strReg.whiteSpace, '')
|
||||
.replace(strReg.mailTo, 'mailto:$1')
|
||||
.replace(strReg.http, 'http://$1');
|
||||
}
|
||||
menuApply(action, inputValue);
|
||||
if (toolbar === ctx._menu) toggleNode(input, false);
|
||||
else toggleNode(ctx._menu, true);
|
||||
};
|
||||
|
||||
input.onkeypress = function(e) {
|
||||
if (e.which === 13) return createlink();
|
||||
};
|
||||
|
||||
});
|
||||
|
||||
// listen for placeholder
|
||||
addListener(ctx, editor, 'focus', function() {
|
||||
if (ctx.isEmpty()) lineBreak(ctx, true);
|
||||
addListener(ctx, doc, 'click', outsideClick);
|
||||
});
|
||||
|
||||
addListener(ctx, editor, 'blur', function() {
|
||||
checkPlaceholder(ctx);
|
||||
ctx.checkContentChange();
|
||||
});
|
||||
|
||||
// listen for paste and clear style
|
||||
addListener(ctx, editor, 'paste', function() {
|
||||
setTimeout(function() {
|
||||
ctx.cleanContent();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function addListener(ctx, target, type, listener) {
|
||||
if (ctx._events.hasOwnProperty(type)) {
|
||||
ctx._events[type].push(listener);
|
||||
} else {
|
||||
ctx._eventTargets = ctx._eventTargets || [];
|
||||
ctx._eventsCache = ctx._eventsCache || [];
|
||||
var index = ctx._eventTargets.indexOf(target);
|
||||
if (index < 0) index = ctx._eventTargets.push(target) - 1;
|
||||
ctx._eventsCache[index] = ctx._eventsCache[index] || {};
|
||||
ctx._eventsCache[index][type] = ctx._eventsCache[index][type] || [];
|
||||
ctx._eventsCache[index][type].push(listener);
|
||||
|
||||
target.addEventListener(type, listener, false);
|
||||
}
|
||||
return ctx;
|
||||
}
|
||||
|
||||
// trigger local events
|
||||
function triggerListener(ctx, type) {
|
||||
if (!ctx._events.hasOwnProperty(type)) return;
|
||||
var args = slice.call(arguments, 2);
|
||||
utils.forEach(ctx._events[type], function (listener) {
|
||||
listener.apply(ctx, args);
|
||||
});
|
||||
}
|
||||
|
||||
function removeListener(ctx, target, type, listener) {
|
||||
var events = ctx._events[type];
|
||||
if (!events) {
|
||||
var _index = ctx._eventTargets.indexOf(target);
|
||||
if (_index >= 0) events = ctx._eventsCache[_index][type];
|
||||
}
|
||||
if (!events) return ctx;
|
||||
var index = events.indexOf(listener);
|
||||
if (index >= 0) events.splice(index, 1);
|
||||
target.removeEventListener(type, listener, false);
|
||||
return ctx;
|
||||
}
|
||||
|
||||
function removeAllListeners(ctx) {
|
||||
utils.forEach(this._events, function (events) {
|
||||
events.length = 0;
|
||||
}, false);
|
||||
if (!ctx._eventsCache) return ctx;
|
||||
utils.forEach(ctx._eventsCache, function (events, index) {
|
||||
var target = ctx._eventTargets[index];
|
||||
utils.forEach(events, function (listeners, type) {
|
||||
utils.forEach(listeners, function (listener) {
|
||||
target.removeEventListener(type, listener, false);
|
||||
}, true);
|
||||
}, false);
|
||||
}, true);
|
||||
ctx._eventTargets = [];
|
||||
ctx._eventsCache = [];
|
||||
return ctx;
|
||||
}
|
||||
|
||||
function checkPlaceholder(ctx) {
|
||||
ctx.config.editor.classList[ctx.isEmpty() ? 'add' : 'remove']('pen-placeholder');
|
||||
}
|
||||
|
||||
function trim(str) {
|
||||
return (str || '').replace(/^\s+|\s+$/g, '');
|
||||
}
|
||||
|
||||
// node.contains is not implemented in IE10/IE11
|
||||
function containsNode(parent, child) {
|
||||
if (parent === child) return true;
|
||||
child = child.parentNode;
|
||||
while (child) {
|
||||
if (child === parent) return true;
|
||||
child = child.parentNode;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function getNode(ctx, byRoot) {
|
||||
var node, root = ctx.config.editor;
|
||||
ctx._range = ctx._range || ctx.getRange();
|
||||
node = ctx._range.commonAncestorContainer;
|
||||
if (!node || node === root) return null;
|
||||
while (node && (node.nodeType !== 1) && (node.parentNode !== root)) node = node.parentNode;
|
||||
while (node && byRoot && (node.parentNode !== root)) node = node.parentNode;
|
||||
return containsNode(root, node) ? node : null;
|
||||
}
|
||||
|
||||
// node effects
|
||||
function effectNode(ctx, el, returnAsNodeName) {
|
||||
var nodes = [];
|
||||
el = el || ctx.config.editor;
|
||||
while (el && el !== ctx.config.editor) {
|
||||
if (el.nodeName.match(effectNodeReg)) {
|
||||
nodes.push(returnAsNodeName ? el.nodeName.toLowerCase() : el);
|
||||
}
|
||||
el = el.parentNode;
|
||||
}
|
||||
return nodes;
|
||||
}
|
||||
|
||||
// breakout from node
|
||||
function lineBreak(ctx, empty) {
|
||||
var range = ctx._range = ctx.getRange(), node = doc.createElement('p');
|
||||
if (empty) ctx.config.editor.innerHTML = '';
|
||||
node.innerHTML = '<br>';
|
||||
range.insertNode(node);
|
||||
focusNode(ctx, node.childNodes[0], range);
|
||||
}
|
||||
|
||||
function focusNode(ctx, node, range) {
|
||||
range.setStartAfter(node);
|
||||
range.setEndBefore(node);
|
||||
range.collapse(false);
|
||||
ctx.setRange(range);
|
||||
}
|
||||
|
||||
function autoLink(node) {
|
||||
if (node.nodeType === 1) {
|
||||
if (autoLinkReg.notLink.test(node.tagName)) return;
|
||||
utils.forEach(node.childNodes, function (child) {
|
||||
autoLink(child);
|
||||
}, true);
|
||||
} else if (node.nodeType === 3) {
|
||||
var result = urlToLink(node.nodeValue || '');
|
||||
if (!result.links) return;
|
||||
var frag = doc.createDocumentFragment(),
|
||||
div = doc.createElement('div');
|
||||
div.innerHTML = result.text;
|
||||
while (div.childNodes.length) frag.appendChild(div.childNodes[0]);
|
||||
node.parentNode.replaceChild(frag, node);
|
||||
}
|
||||
}
|
||||
|
||||
function urlToLink(str) {
|
||||
var count = 0;
|
||||
str = str.replace(autoLinkReg.url, function(url) {
|
||||
var realUrl = url, displayUrl = url;
|
||||
count++;
|
||||
if (url.length > autoLinkReg.maxLength) displayUrl = url.slice(0, autoLinkReg.maxLength) + '...';
|
||||
// Add http prefix if necessary
|
||||
if (!autoLinkReg.prefix.test(realUrl)) realUrl = 'http://' + realUrl;
|
||||
return '<a href="' + realUrl + '">' + displayUrl + '</a>';
|
||||
});
|
||||
return {links: count, text: str};
|
||||
}
|
||||
|
||||
function toggleNode(node, hide) {
|
||||
node.style.display = hide ? 'none' : 'block';
|
||||
}
|
||||
|
||||
Pen = function(config) {
|
||||
|
||||
if (!config) throw new Error('Can\'t find config');
|
||||
|
||||
debugMode = config.debug;
|
||||
|
||||
// merge user config
|
||||
var defaults = utils.merge(config);
|
||||
|
||||
var editor = defaults.editor;
|
||||
|
||||
if (!editor || editor.nodeType !== 1) throw new Error('Can\'t find editor');
|
||||
|
||||
// set default class
|
||||
editor.classList.add(defaults.class);
|
||||
|
||||
// set contenteditable
|
||||
editor.setAttribute('contenteditable', 'true');
|
||||
|
||||
// assign config
|
||||
this.config = defaults;
|
||||
|
||||
// set placeholder
|
||||
if (defaults.placeholder) editor.setAttribute('data-placeholder', defaults.placeholder);
|
||||
checkPlaceholder(this);
|
||||
|
||||
// save the selection obj
|
||||
this.selection = selection;
|
||||
|
||||
// define local events
|
||||
this._events = {change: []};
|
||||
|
||||
// enable toolbar
|
||||
initToolbar(this);
|
||||
|
||||
// init events
|
||||
initEvents(this);
|
||||
|
||||
// to check content change
|
||||
this._prevContent = this.getContent();
|
||||
|
||||
// enable markdown covert
|
||||
if (this.markdown) this.markdown.init(this);
|
||||
|
||||
// stay on the page
|
||||
if (this.config.stay) this.stay(this.config);
|
||||
|
||||
};
|
||||
|
||||
Pen.prototype.on = function(type, listener) {
|
||||
addListener(this, this.config.editor, type, listener);
|
||||
return this;
|
||||
};
|
||||
|
||||
Pen.prototype.isEmpty = function(node) {
|
||||
node = node || this.config.editor;
|
||||
return !(node.querySelector('img')) && !(node.querySelector('blockquote')) &&
|
||||
!(node.querySelector('li')) && !trim(node.textContent);
|
||||
};
|
||||
|
||||
Pen.prototype.getContent = function() {
|
||||
return this.isEmpty() ? '' : trim(this.config.editor.innerHTML);
|
||||
};
|
||||
|
||||
Pen.prototype.setContent = function(html) {
|
||||
this.config.editor.innerHTML = html;
|
||||
this.cleanContent();
|
||||
return this;
|
||||
};
|
||||
|
||||
Pen.prototype.checkContentChange = function () {
|
||||
var prevContent = this._prevContent, currentContent = this.getContent();
|
||||
if (prevContent === currentContent) return;
|
||||
this._prevContent = currentContent;
|
||||
triggerListener(this, 'change', currentContent, prevContent);
|
||||
};
|
||||
|
||||
Pen.prototype.getRange = function() {
|
||||
var editor = this.config.editor, range = selection.rangeCount && selection.getRangeAt(0);
|
||||
if (!range) range = doc.createRange();
|
||||
if (!containsNode(editor, range.commonAncestorContainer)) {
|
||||
range.selectNodeContents(editor);
|
||||
range.collapse(false);
|
||||
}
|
||||
return range;
|
||||
};
|
||||
|
||||
Pen.prototype.setRange = function(range) {
|
||||
range = range || this._range;
|
||||
if (!range) {
|
||||
range = this.getRange();
|
||||
range.collapse(false); // set to end
|
||||
}
|
||||
try {
|
||||
selection.removeAllRanges();
|
||||
selection.addRange(range);
|
||||
} catch (e) {/* IE throws error sometimes*/}
|
||||
return this;
|
||||
};
|
||||
|
||||
Pen.prototype.focus = function(focusStart) {
|
||||
if (!focusStart) this.setRange();
|
||||
this.config.editor.focus();
|
||||
return this;
|
||||
};
|
||||
|
||||
Pen.prototype.execCommand = function(name, value) {
|
||||
name = name.toLowerCase();
|
||||
this.setRange();
|
||||
|
||||
if (commandsReg.block.test(name)) {
|
||||
commandBlock(this, name);
|
||||
} else if (commandsReg.inline.test(name) || commandsReg.source.test(name)) {
|
||||
commandOverall(this, name, value);
|
||||
} else if (commandsReg.insert.test(name)) {
|
||||
commandInsert(this, name, value);
|
||||
} else if (commandsReg.wrap.test(name)) {
|
||||
commandWrap(this, name, value);
|
||||
} else {
|
||||
utils.log('can not find command function for name: ' + name + (value ? (', value: ' + value) : ''), true);
|
||||
}
|
||||
if (name === 'indent') this.checkContentChange();
|
||||
else this.cleanContent({cleanAttrs: ['style']});
|
||||
};
|
||||
|
||||
// remove attrs and tags
|
||||
// pen.cleanContent({cleanAttrs: ['style'], cleanTags: ['id']})
|
||||
Pen.prototype.cleanContent = function(options) {
|
||||
var editor = this.config.editor;
|
||||
|
||||
if (!options) options = this.config;
|
||||
utils.forEach(options.cleanAttrs, function (attr) {
|
||||
utils.forEach(editor.querySelectorAll('[' + attr + ']'), function(item) {
|
||||
item.removeAttribute(attr);
|
||||
}, true);
|
||||
}, true);
|
||||
utils.forEach(options.cleanTags, function (tag) {
|
||||
utils.forEach(editor.querySelectorAll(tag), function(item) {
|
||||
item.parentNode.removeChild(item);
|
||||
}, true);
|
||||
}, true);
|
||||
|
||||
checkPlaceholder(this);
|
||||
this.checkContentChange();
|
||||
return this;
|
||||
};
|
||||
|
||||
// auto link content, return content
|
||||
Pen.prototype.autoLink = function() {
|
||||
autoLink(this.config.editor);
|
||||
return this.getContent();
|
||||
};
|
||||
|
||||
// highlight menu
|
||||
Pen.prototype.highlight = function() {
|
||||
var toolbar = this._toolbar || this._menu
|
||||
, node = getNode(this);
|
||||
// remove all highlights
|
||||
utils.forEach(toolbar.querySelectorAll('.active'), function(el) {
|
||||
el.classList.remove('active');
|
||||
}, true);
|
||||
|
||||
if (!node) return this;
|
||||
|
||||
var effects = effectNode(this, node)
|
||||
, inputBar = this._inputBar
|
||||
, highlight;
|
||||
|
||||
if (inputBar && toolbar === this._menu) {
|
||||
// display link input if createlink enabled
|
||||
inputBar.style.display = 'none';
|
||||
// reset link input value
|
||||
inputBar.value = '';
|
||||
}
|
||||
|
||||
highlight = function(str) {
|
||||
if (!str) return;
|
||||
var el = toolbar.querySelector('[data-action=' + str + ']');
|
||||
return el && el.classList.add('active');
|
||||
};
|
||||
utils.forEach(effects, function(item) {
|
||||
var tag = item.nodeName.toLowerCase();
|
||||
switch(tag) {
|
||||
case 'a':
|
||||
if (inputBar) inputBar.value = item.getAttribute('href');
|
||||
tag = 'createlink';
|
||||
break;
|
||||
case 'img':
|
||||
if (inputBar) inputBar.value = item.getAttribute('src');
|
||||
tag = 'insertimage';
|
||||
break;
|
||||
case 'i':
|
||||
tag = 'italic';
|
||||
break;
|
||||
case 'u':
|
||||
tag = 'underline';
|
||||
break;
|
||||
case 'strike':
|
||||
tag = 'strikethrough';
|
||||
break;
|
||||
case 'b':
|
||||
tag = 'bold';
|
||||
break;
|
||||
case 'pre':
|
||||
case 'code':
|
||||
tag = 'code';
|
||||
break;
|
||||
case 'ul':
|
||||
tag = 'insertunorderedlist';
|
||||
break;
|
||||
case 'ol':
|
||||
tag = 'insertorderedlist';
|
||||
break;
|
||||
case 'li':
|
||||
tag = 'indent';
|
||||
break;
|
||||
}
|
||||
highlight(tag);
|
||||
}, true);
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
// show menu
|
||||
Pen.prototype.menu = function() {
|
||||
if (!this._menu) return this;
|
||||
if (selection.isCollapsed) {
|
||||
this._menu.style.display = 'none'; //hide menu
|
||||
this._inputActive = false;
|
||||
return this;
|
||||
}
|
||||
if (this._toolbar) {
|
||||
if (!this._inputBar || !this._inputActive) return this;
|
||||
}
|
||||
var offset = this._range.getBoundingClientRect()
|
||||
, menuPadding = 10
|
||||
, top = offset.top - menuPadding
|
||||
, left = offset.left + (offset.width / 2)
|
||||
, menu = this._menu
|
||||
, menuOffset = {x: 0, y: 0}
|
||||
, stylesheet = this._stylesheet;
|
||||
|
||||
// fixes some browser double click visual discontinuity
|
||||
// if the offset has no width or height it should not be used
|
||||
if (offset.width === 0 && offset.height === 0) return this;
|
||||
|
||||
// store the stylesheet used for positioning the menu horizontally
|
||||
if (this._stylesheet === undefined) {
|
||||
var style = document.createElement("style");
|
||||
document.head.appendChild(style);
|
||||
this._stylesheet = stylesheet = style.sheet;
|
||||
}
|
||||
// display block to caculate its width & height
|
||||
menu.style.display = 'block';
|
||||
|
||||
menuOffset.x = left - (menu.clientWidth / 2);
|
||||
menuOffset.y = top - menu.clientHeight;
|
||||
|
||||
// check to see if menu has over-extended its bounding box. if it has,
|
||||
// 1) apply a new class if overflowed on top;
|
||||
// 2) apply a new rule if overflowed on the left
|
||||
if (stylesheet.cssRules.length > 0) {
|
||||
stylesheet.deleteRule(0);
|
||||
}
|
||||
if (menuOffset.x < 0) {
|
||||
menuOffset.x = 0;
|
||||
stylesheet.insertRule('.pen-menu:after {left: ' + left + 'px;}', 0);
|
||||
} else {
|
||||
stylesheet.insertRule('.pen-menu:after {left: 50%; }', 0);
|
||||
}
|
||||
if (menuOffset.y < 0) {
|
||||
menu.classList.add('pen-menu-below');
|
||||
menuOffset.y = offset.top + offset.height + menuPadding;
|
||||
} else {
|
||||
menu.classList.remove('pen-menu-below');
|
||||
}
|
||||
|
||||
menu.style.top = menuOffset.y + 'px';
|
||||
menu.style.left = menuOffset.x + 'px';
|
||||
return this;
|
||||
};
|
||||
|
||||
Pen.prototype.stay = function(config) {
|
||||
var ctx = this;
|
||||
if (!window.onbeforeunload) {
|
||||
window.onbeforeunload = function() {
|
||||
if (!ctx._isDestroyed) return config.stayMsg;
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
Pen.prototype.destroy = function(isAJoke) {
|
||||
var destroy = isAJoke ? false : true
|
||||
, attr = isAJoke ? 'setAttribute' : 'removeAttribute';
|
||||
|
||||
if (!isAJoke) {
|
||||
removeAllListeners(this);
|
||||
try {
|
||||
selection.removeAllRanges();
|
||||
if (this._menu) this._menu.parentNode.removeChild(this._menu);
|
||||
} catch (e) {/* IE throws error sometimes*/}
|
||||
} else {
|
||||
initToolbar(this);
|
||||
initEvents(this);
|
||||
}
|
||||
this._isDestroyed = destroy;
|
||||
this.config.editor[attr]('contenteditable', '');
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
Pen.prototype.rebuild = function() {
|
||||
return this.destroy('it\'s a joke');
|
||||
};
|
||||
|
||||
// a fallback for old browers
|
||||
root.Pen = function(config) {
|
||||
if (!config) return utils.log('can\'t find config', true);
|
||||
|
||||
var defaults = utils.merge(config)
|
||||
, klass = defaults.editor.getAttribute('class');
|
||||
|
||||
klass = klass ? klass.replace(/\bpen\b/g, '') + ' pen-textarea ' + defaults.class : 'pen pen-textarea';
|
||||
defaults.editor.setAttribute('class', klass);
|
||||
defaults.editor.innerHTML = defaults.textarea;
|
||||
return defaults.editor;
|
||||
};
|
||||
|
||||
// export content as markdown
|
||||
var regs = {
|
||||
a: [/<a\b[^>]*href=["']([^"]+|[^']+)\b[^>]*>(.*?)<\/a>/ig, '[$2]($1)'],
|
||||
img: [/<img\b[^>]*src=["']([^\"+|[^']+)[^>]*>/ig, ''],
|
||||
b: [/<b\b[^>]*>(.*?)<\/b>/ig, '**$1**'],
|
||||
i: [/<i\b[^>]*>(.*?)<\/i>/ig, '***$1***'],
|
||||
h: [/<h([1-6])\b[^>]*>(.*?)<\/h\1>/ig, function(a, b, c) {
|
||||
return '\n' + ('######'.slice(0, b)) + ' ' + c + '\n';
|
||||
}],
|
||||
li: [/<(li)\b[^>]*>(.*?)<\/\1>/ig, '* $2\n'],
|
||||
blockquote: [/<(blockquote)\b[^>]*>(.*?)<\/\1>/ig, '\n> $2\n'],
|
||||
pre: [/<pre\b[^>]*>(.*?)<\/pre>/ig, '\n```\n$1\n```\n'],
|
||||
p: [/<p\b[^>]*>(.*?)<\/p>/ig, '\n$1\n'],
|
||||
hr: [/<hr\b[^>]*>/ig, '\n---\n']
|
||||
};
|
||||
|
||||
Pen.prototype.toMd = function() {
|
||||
var html = this.getContent()
|
||||
.replace(/\n+/g, '') // remove line break
|
||||
.replace(/<([uo])l\b[^>]*>(.*?)<\/\1l>/ig, '$2'); // remove ul/ol
|
||||
|
||||
for(var p in regs) {
|
||||
if (regs.hasOwnProperty(p))
|
||||
html = html.replace.apply(html, regs[p]);
|
||||
}
|
||||
return html.replace(/\*{5}/g, '**');
|
||||
};
|
||||
|
||||
// make it accessible
|
||||
if (doc.getSelection) {
|
||||
selection = doc.getSelection();
|
||||
root.Pen = Pen;
|
||||
}
|
||||
|
||||
}(window, document));
|
1675
vendor/assets/javascripts/world-110m.json
vendored
Normal file
1675
vendor/assets/javascripts/world-110m.json
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1675
vendor/assets/javascripts/world-50m.json
vendored
Normal file
1675
vendor/assets/javascripts/world-50m.json
vendored
Normal file
File diff suppressed because it is too large
Load Diff
159
vendor/assets/stylesheets/pen.css
vendored
Normal file
159
vendor/assets/stylesheets/pen.css
vendored
Normal file
@ -0,0 +1,159 @@
|
||||
/*! Licensed under MIT, https://github.com/sofish/pen */
|
||||
|
||||
/* basic reset */
|
||||
.pen:focus{outline:none;}
|
||||
/*.pen, .pen-menu, .pen-input, .pen textarea{font:400 1.16em/1.45 Palatino, Optima, Georgia, serif;color:#331;}
|
||||
.pen fieldset, img {border: 0;}
|
||||
.pen blockquote{padding-left:10px;margin-left:-14px;border-left:4px solid #1abf89;}
|
||||
.pen a{color:#1abf89;}
|
||||
.pen del{text-decoration:line-through;}
|
||||
.pen sub, .pen sup {font-size:75%;position:relative;vertical-align:text-top;}
|
||||
:root .pen sub, :root .pen sup{vertical-align:baseline; /* for ie9 and other mordern browsers *//*}
|
||||
.pen sup {top:-0.5em;}
|
||||
.pen sub {bottom:-0.25em;}
|
||||
.pen hr{border:none;border-bottom:1px solid #cfcfcf;margin-bottom:25px;*color:pink;*filter:chroma(color=pink);height:10px;*margin:-7px 0 15px;}
|
||||
.pen small{font-size:0.8em;color:#888;}
|
||||
.pen em, .pen b, .pen strong{font-weight:700;}
|
||||
.pen pre{white-space:pre-wrap;padding:0.85em;background:#f8f8f8;}
|
||||
*/
|
||||
/* block-level element margin */
|
||||
/*.pen p, .pen pre, .pen ul, .pen ol, .pen dl, .pen form, .pen table, .pen blockquote{margin-bottom:16px;}
|
||||
*/
|
||||
/* headers */
|
||||
/*.pen h1, .pen h2, .pen h3, .pen h4, .pen h5, .pen h6{margin-bottom:16px;font-weight:700;line-height:1.2;}
|
||||
.pen h1{font-size:2em;}
|
||||
.pen h2{font-size:1.8em;}
|
||||
.pen h3{font-size:1.6em;}
|
||||
.pen h4{font-size:1.4em;}
|
||||
.pen h5, .pen h6{font-size:1.2em;}*/
|
||||
|
||||
/* list */
|
||||
/*.pen ul, .pen ol{margin-left:1.2em;}
|
||||
.pen ul, .pen-ul{list-style:disc;}
|
||||
.pen ol, .pen-ol{list-style:decimal;}
|
||||
.pen li ul, .pen li ol, .pen-ul ul, .pen-ul ol, .pen-ol ul, .pen-ol ol{margin:0 2em 0 1.2em;}
|
||||
.pen li ul, .pen-ul ul, .pen-ol ul{list-style: circle;}*/
|
||||
|
||||
/* pen menu */
|
||||
.pen-menu [class^="icon-"], .pen-menu [class*=" icon-"] { /* reset to avoid conflicts with Bootstrap */
|
||||
background: transparent;
|
||||
background-image: none;
|
||||
}
|
||||
.pen-menu { min-width: 320px; }
|
||||
.pen-menu, .pen-input{font-size:14px;line-height:1;}
|
||||
.pen-menu{white-space:nowrap;box-shadow:1px 2px 3px -2px #222;background:#333;background-image:linear-gradient(to bottom, #222, #333);opacity:0.9;position:fixed;height:36px;border:1px solid #333;border-radius:3px;display:none;z-index:1000;}
|
||||
.pen-menu:after {top:100%;border:solid transparent;content:" ";height:0;width:0;position:absolute;pointer-events:none;}
|
||||
.pen-menu:after {border-color:rgba(51, 51, 51, 0);border-top-color:#333;border-width:6px;left:50%;margin-left:-6px;}
|
||||
.pen-menu-below:after {top: -11px; display:block; -moz-transform: rotate(180deg); -webkit-transform: rotate(180deg); -ms-transform: rotate(180deg); -o-transform: rotate(180deg); transform: rotate(180deg);}
|
||||
.pen-icon{
|
||||
font-size: 16px;
|
||||
line-height: 40px;;min-width:20px;display:inline-block;padding:0 10px;height:36px;overflow:hidden;color:#fff;text-align:center;cursor:pointer;-moz-user-select:none;-webkit-user-select:none;-ms-user-select:none;user-select:none;}
|
||||
.pen-icon:first-of-type{border-top-left-radius:3px;border-bottom-left-radius:3px;}
|
||||
.pen-icon:last-of-type{border-top-right-radius:3px;border-bottom-right-radius:3px;}
|
||||
.pen-icon:hover{background:#000;}
|
||||
.pen-icon.active{color:#1abf89;background:#000;box-shadow:inset 2px 2px 4px #000;}
|
||||
.pen-input{position:absolute;width:100%;left:0;top:0;height:36px;line-height:20px;background:#333;color:#fff;border:none;text-align:center;display:none;font-family:arial, sans-serif;}
|
||||
.pen-input:focus{outline:none;}
|
||||
|
||||
.pen-textarea{display:block;background:#f8f8f8;padding:20px;}
|
||||
.pen textarea{font-size:14px;border:none;background:none;width:100%;_height:200px;min-height:200px;resize:none;}
|
||||
|
||||
/*@font-face {
|
||||
font-family: 'pen';
|
||||
src: url('font/fontello.eot?370dad08');
|
||||
src: url('font/fontello.eot?370dad08#iefix') format('embedded-opentype'),
|
||||
url('font/fontello.woff?370dad08') format('woff'),
|
||||
url('font/fontello.ttf?370dad08') format('truetype'),
|
||||
url('font/fontello.svg?370dad08#fontello') format('svg');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}*/
|
||||
|
||||
.pen-menu [class^="icon-"]:before, .pen-menu [class*=" icon-"]:before {
|
||||
speak: none;
|
||||
display: inline-block;
|
||||
width: 1em;
|
||||
margin-right: .2em;
|
||||
text-align: center;
|
||||
font-variant: normal;
|
||||
text-transform: none;
|
||||
line-height: 1em;
|
||||
margin-left: .2em;
|
||||
}
|
||||
|
||||
.pen-menu .icon-location:before { content: '\e815'; } /* '' */
|
||||
.pen-menu .icon-fit:before { content: '\e80f'; } /* '' */
|
||||
.pen-menu .icon-justifyleft:before { content: '\e80a'; } /* '' */
|
||||
.pen-menu .icon-justifycenter:before { content: '\e80b'; } /* '' */
|
||||
.pen-menu .icon-justifyright:before { content: '\e80c'; } /* '' */
|
||||
.pen-menu .icon-justifyfull:before { content: '\e80d'; } /* '' */
|
||||
.pen-menu .icon-outdent:before { content: '\e800'; } /* '' */
|
||||
.pen-menu .icon-indent:before { content: '\e801'; } /* '' */
|
||||
.pen-menu .icon-mode:before { content: '\e813'; } /* '' */
|
||||
.pen-menu .icon-fullscreen:before { content: '\e80e'; } /* '' */
|
||||
.pen-menu .icon-undo:before { content: '\e817'; } /* '' */
|
||||
.pen-menu .icon-code:before { content: '\e816'; } /* '' */
|
||||
.pen-menu .icon-pre:before { content: '\e816'; } /* '' */
|
||||
.pen-menu .icon-unlink:before { content: '\e811'; } /* '' */
|
||||
.pen-menu .icon-superscript:before { content: '\e808'; } /* '' */
|
||||
.pen-menu .icon-subscript:before { content: '\e809'; } /* '' */
|
||||
.pen-menu .icon-inserthorizontalrule:before { content: '\e818'; } /* '' */
|
||||
.pen-menu .icon-pin:before { content: '\e812'; } /* '' */
|
||||
.pen {
|
||||
position: relative;
|
||||
}
|
||||
/*.pen.hinted h1:before,
|
||||
.pen.hinted h2:before,
|
||||
.pen.hinted h3:before,
|
||||
.pen.hinted h4:before,
|
||||
.pen.hinted h5:before,
|
||||
.pen.hinted h6:before,
|
||||
.pen.hinted blockquote:before,
|
||||
.pen.hinted hr:before {
|
||||
color: #eee;
|
||||
position: absolute;
|
||||
right: 100%;
|
||||
white-space: nowrap;
|
||||
padding-right: 10px;
|
||||
}
|
||||
.pen.hinted blockquote { border-left: 0; margin-left: 0; padding-left: 0; }
|
||||
.pen.hinted blockquote:before {
|
||||
color: #1abf89;
|
||||
content: ">";
|
||||
font-weight: bold;
|
||||
vertical-align: center;
|
||||
}
|
||||
.pen.hinted h1:before { content: "#";}
|
||||
.pen.hinted h2:before { content: "##";}
|
||||
.pen.hinted h3:before { content: "###";}
|
||||
.pen.hinted h4:before { content: "####";}
|
||||
.pen.hinted h5:before { content: "#####";}
|
||||
.pen.hinted h6:before { content: "######";}
|
||||
.pen.hinted hr:before { content: "﹘﹘﹘"; line-height: 1.2; vertical-align: bottom; }
|
||||
|
||||
.pen.hinted pre:before, .pen.hinted pre:after {
|
||||
content: "```";
|
||||
display: block;
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
.pen.hinted ul { list-style: none; }
|
||||
.pen.hinted ul li:before {
|
||||
content: "*";
|
||||
color: #999;
|
||||
line-height: 1;
|
||||
vertical-align: bottom;
|
||||
margin-left: -1.2em;
|
||||
display: inline-block;
|
||||
width: 1.2em;
|
||||
}
|
||||
|
||||
.pen.hinted b:before, .pen.hinted b:after { content: "**"; color: #eee; font-weight: normal; }
|
||||
.pen.hinted i:before, .pen.hinted i:after { content: "*"; color: #eee; }
|
||||
|
||||
.pen.hinted a { text-decoration: none; }
|
||||
.pen.hinted a:before {content: "["; color: #ddd; }
|
||||
.pen.hinted a:after { content: "](" attr(href) ")"; color: #ddd; }
|
||||
|
||||
.pen-placeholder:after { position: absolute; top: 0; left: 0; content: attr(data-placeholder); color: #999; cursor: text; }
|
||||
*/
|
Loading…
x
Reference in New Issue
Block a user