...
 
Commits (135)
image: 0xacab.org:4567/riseuplabs/docker/crabgrass:latest
image: 0xacab.org:4567/riseuplabs/docker/crabgrass:stretch_amd64
variables:
MYSQL_ALLOW_EMPTY_PASSWORD: "yes"
......@@ -23,37 +23,14 @@ bundle_audit:
bundle-audit check
test:
stage: test
services:
- mysql:5.5
script: |
bundle exec rake create_a_secret
cp config/database.yml.example config/database.yml
bundle exec rake db:create
bundle exec rake db:schema:load
bundle exec rake cg:test:update_fixtures
bundle exec rake db:test:prepare
RAILS_ENV=test bundle exec rake db:fixtures:load
RAILS_ENV=test bundle exec rake ts:index ts:start
bundle exec rake
bundle exec rake cg:cleanup:all # test cleanup tasks
artifacts:
when: on_failure
paths:
- log/test.log
- tmp/*.log
- tmp/*.html
- tmp/*.png
test_stretch:
image: 0xacab.org:4567/riseuplabs/docker/crabgrass:stretch_amd64
stage: test
services:
- mariadb:10.1
variables:
MYSQL_HOST: 'mariadb'
RAILS_ENV: 'test'
COVERAGE: 'true'
script: |
bundle exec rake create_a_secret
cp config/database.yml.example config/database.yml
bundle exec rake db:create
bundle exec rake db:schema:load
......@@ -61,8 +38,9 @@ test_stretch:
bundle exec rake db:test:prepare
RAILS_ENV=test bundle exec rake db:fixtures:load
RAILS_ENV=test bundle exec rake ts:index ts:start
bundle exec rake
bundle exec rake cg:cleanup:all # test cleanup tasks
bundle exec rails test test/functional test/unit test/integration extensions/pages
echo 'Number of deprecations in the test logs:'
grep DEPRECATION log/test.log | wc -l
artifacts:
when: on_failure
paths:
......
AllCops:
TargetRailsVersion: 5.1
TargetRubyVersion: 2.3
Exclude:
- 'bin/*'
DisplayCopNames: true
......@@ -17,3 +19,8 @@ Metrics/LineLength:
- 'config/deploy.rb'
- 'config/permissions.rb'
- 'lib/**/*'
Style/ClassAndModuleChildren:
Exclude:
- 'app/helpers/**/*'
Style/FrozenStringLiteralComment:
AutoCorrect: false
SimpleCov.start 'rails' do
add_filter 'vendor/ruby' # Don't include bundled stuff
end
......@@ -20,7 +20,6 @@ addons:
before_install:
- "bundle --version"
before_script:
- "bundle exec rake create_a_secret"
- "cp config/database.yml.example config/database.yml"
- "bundle exec rake db:create"
- "bundle exec rake db:schema:load"
......
source 'https://rubygems.org'
# ensure github urls use https rather than insecure git protocol.
git_source(:github) do |repo_name|
repo_name = "#{repo_name}/#{repo_name}" unless repo_name.include?('/')
"https://github.com/#{repo_name}.git"
end
git_source(:github) { |repo| "https://github.com/#{repo}.git" }
##
# Core components
##
# Rails is the framework we use.
# use the 4.2 series including all security fixes
gem 'rails', '~> 4.2.11'
gem 'rails', '~> 5.2.3'
# Security updates
# https://github.com/sparklemotion/nokogiri/issues/1785
gem 'nokogiri', '~> 1.8.5'
#https://github.com/sparklemotion/nokogiri/issues/1892
gem 'nokogiri', '~> 1.10.3'
# Rake is rubys make... performing tasks
# locking in to latest major to fix API
......@@ -25,13 +21,15 @@ gem 'rake', '~> 10.0', require: false
# Application preloader for faster start time
gem 'spring', group: :development
# translating strings for the user interface
# reduces boot times through caching; required in config/boot.rb
gem 'bootsnap', '>= 1.1.0', require: false
# locking in to latest major to fix API
gem 'i18n', '~> 0.7'
# improved gem to access mysql database
# locking in to latest major to fix API
gem 'mysql2', '~> 0.3.18'
gem 'mysql2', '~> 0.5.2'
# parsing and generating JSON
# locking in to latest major to fix API
......@@ -39,8 +37,8 @@ gem 'json', '~> 1.8'
# Markup language that uses indent to indicate nesting
# locking in to latest major to fix API
gem 'haml', '~> 4.0'
gem 'haml-rails', '~> 0.9.0'
gem 'haml', '~> 5.0'
gem 'haml-rails', '~> 1.0'
# Extendet scriptable CSS language
# locking in to latest major to fix API
......@@ -48,29 +46,21 @@ gem 'sass'
##
# Prototype - yes. we still use it.
# these will be replaced by jquery equivalents at some point:
##
# main part of prototype
# needs special branch for rails 4.2
gem 'prototype-rails', github: 'rails/prototype-rails', branch: '4.2'
# we use a fork which is rails 5.x compatible
# tests do not pass for this fork
gem 'prototype-rails', github: 'voxmedia/prototype-rails', ref: 'e385756cbabb5608d1eab47b6416cdd49613c73b'
# Full text search for the database
gem 'thinking-sphinx', '~> 3.4.2'
# Enhanced Tagging lib. Used to tag pages
gem 'acts-as-taggable-on', '~> 4.0'
gem 'acts-as-taggable-on', '~> 6.0'
# Rails 5 migration
##
# security updates
##
#
# CVE-2018-16471
# Criticality: Unknown
# URL:
# https://groups.google.com/forum/#!topic/ruby-security-ann/NAalCee8n6o
# Title: Possible XSS vulnerability in Rack
gem 'rack', '~> 1.6.11'
# ActionView::Helpers::RecordTagHelper moved to external gem
gem 'record_tag_helper', '~> 1.0'
##
# Upgrade pending
......@@ -95,10 +85,8 @@ gem 'pundit', '~> 1.1'
# Bcrypt for has_secure_password
gem 'bcrypt', '~> 3.1.7'
#
gem 'secure_headers', '~> 4.0.2'
# ?
# locking in to latest major to fix API
gem 'http_accept_language', '~> 2.0'
......@@ -144,7 +132,7 @@ gem 'greencloth', require: 'greencloth',
# media upload post processing has it's own repo
# version is rather strict for now as api may still change.
gem 'crabgrass_media', '~> 0.2.1', require: 'media'
gem 'crabgrass_media', '~> 0.3.1', require: 'media'
##
## not required, but a really good idea
......@@ -181,6 +169,7 @@ group :production do
# runs independendly - so no version restriction for now
# TODO: check if we want this or nodejs
gem 'therubyracer'
# gem 'mini_racer', platforms: :ruby # new default in Rails 5.2
end
group :production, :development do
......@@ -204,6 +193,8 @@ group :test, :development do
gem 'byebug'
end
gem 'web-console', group: :development
group :test, :ci do
##
## TESTS
......@@ -212,7 +203,11 @@ group :test, :ci do
gem 'factory_bot_rails'
gem 'faker', '~> 1.0.0'
gem 'minitest', require: false
# temporary fix for minitest 5.11 issue
gem 'minitest', '~>5.10.3', require: false
# contains helper methods like assigns and assert_template
gem 'rails-controller-testing'
##
## INTEGRATION TESTS
......
This diff is collapsed.
......@@ -68,7 +68,6 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
config.vm.provision 'Install project dependencies', type: 'shell', privileged: false, inline: <<-SHELL
cd /vagrant/
bundle install
rake create_a_secret
SHELL
config.vm.provision 'Connect MySQL DB', type: 'shell', privileged: false, inline: <<-SHELL
......
......@@ -2,6 +2,7 @@ Javascripts
==============================================
Javascript are served using the Sprockets gem for asset pipelining.
We load all scripts by including application.js on all pages.
About these scripts
----------------------------------------------
......@@ -20,19 +21,6 @@ app/javascripts/prototype.js
scriptaculous effects for prototype that we also use.
Using scripts
----------------------------------------------
We currently load all scripts on all pages (e.g. there is nothing in
as_needed). If you want an 'as_needed' script that is only loaded for a
particular controller:
class MyController < ApplicationController
javascript :tasks, :wiki
javascript :extra, :action => :index
end
Stylesheets
==============================================
......
......@@ -16,18 +16,18 @@ function cgAutocompleteEntities(id, url, opts) {
rowRenderer: autoCompleteRowRenderer,
selectValue: autoCompleteSelectValue
};
if (opts) { Object.extend(options, opts); }
new Autocomplete(id, options, random_id);
}
if (opts) { Object.extend(options, opts); }
new Autocomplete(id, options, random_id);
function autoCompleteRowRenderer(value, re, data) {
return "<p class='icon xsmall' style='background-image: url(/avatars/" + data + "/xsmall.jpg)'>" + value.replace(/^<em>(.*)<\/em>(<br\/>(.*))?$/gi, function(m, m1, m2, m3){return "<em>" + Autocomplete.highlight(m1,re) + "</em>" + (m3 ? "<br/>" + Autocomplete.highlight(m3, re) : "")}) + "</p>";
}
function autoCompleteRowRenderer(value, re, data) {
return "<p class='icon xsmall' style='background-image: url(/avatars/" + data + "/xsmall.jpg)'>" + value.replace(/^<em>(.*)<\/em>(<br\/>(.*))?$/gi, function(m, m1, m2, m3){return "<em>" + Autocomplete.highlight(m1,re) + "</em>" + (m3 ? "<br/>" + Autocomplete.highlight(m3, re) : "")}) + "</p>";
}
function autoCompleteSelectValue(value){
// if there are two rows pick the second one
row = value.replace(/.*<br\/>(.*)/g,'$1');
// encode text without the tags
return row.replace(/<[^>]*>/g,'');
// return encodeURIComponent(row.replace(/<[^>]*>/g,''));
function autoCompleteSelectValue(value){
// if there are two rows pick the second one
row = value.replace(/.*<br\/>(.*)/g,'$1');
// encode text without the tags
return row.replace(/<[^>]*>/g,'');
// return encodeURIComponent(row.replace(/<[^>]*>/g,''));
}
}
......@@ -48,16 +48,6 @@ function replaceClassName(element, old_class, new_class) {
element.removeClassName(old_class); element.addClassName(new_class)
}
//
// replaces an element with a new one if it is empty.
//
function replaceIfEmpty(selector, newElement) {
var el = $$(selector).first()
if (el && el.empty()) {
el.replace(newElement);
}
}
//
// EVENTS
//
......@@ -102,40 +92,3 @@ var LocationHash = {
this.polling = true;
}
}
//
// split panel
//
function activatePanelRow(row_id) {
// reset styles
$$('.panel_right .row').invoke('hide');
$$('.panel_arrow').invoke('hide');
$$('.panel_left .row').invoke('removeClassName', 'active');
if (row_id) {
// highlight left panel row
$('panel_left_'+row_id).addClassName('active');
var halfHeight = $('panel_left_'+row_id).getHeight() / 2 + "px";
var borderWidthStr = "#{top} #{right} #{bottom} #{left}".interpolate({top: halfHeight, right:"0px", bottom: halfHeight, left:"10px"});
$('panel_arrow_'+row_id).setStyle({borderWidth: borderWidthStr, display: 'block'});
// position and show right panel row
var offset = $('panel_left_'+row_id).offsetTop + 'px';
$$('.panel_right').first().setStyle({paddingTop:offset})
$('panel_right_'+row_id).show();
}
}
//
// sliding list
//
// left and right contain these keys:
// path -- the url path of the panel. this is used both for history
// and loading content via ajax.
// domid -- dom id of the element to update
//
function activateSlidingRow(left, right) {
History.replaceState({slide_right:'sliding-list', update:left}, null, left.path);
History.pushState({slide_left:'sliding-list', update:right}, null, right.path);
}
//
// Busy cursor
//
// When a ajax request is pending, this will change the cursor to be a busy cursor.
// It depends on this css:
//
// html.busy, html.busy * {
// cursor: wait !important;
// }
//
// source: http://postpostmodern.com/instructional/global-ajax-cursor-change/
//
// NOTE: currently disabled. This is really cool, but some ajax queries
// cause activeRequestCount to keep incrementing. Until that is fixed,
// this must be disabled.
//
// in prototype:
// Ajax.Responders.register({
// onCreate: function() {
// if (Ajax.activeRequestCount > 0) {
// $$('html')[0].addClassName('busy');
// }
// },
// onComplete: function() {
// console.log("Ajax.activeRequestCount " + Ajax.activeRequestCount);
// if (Ajax.activeRequestCount == 0) {
// $$('html')[0].removeClassName('busy');
// }
// }
// });
// in jquery:
// function globalAjaxCursorChange() {
// $("html").bind("ajaxStart", function() {
// $(this).addClass('busy');
// }).bind("ajaxStop", function() {
// $(this).removeClass('busy');
// });
// }
\ No newline at end of file
//
// a modification of Effect.Move to support percentage offsets.
//
Effect.MoveByPercent = Class.create(Effect.Base, {
initialize: function(element) {
this.element = $(element);
if (!this.element) throw(Effect._elementDoesNotExistError);
var options = Object.extend({x:0, y:0}, arguments[1] || { });
this.start(options);
},
setup: function() {
this.element.makePositioned();
this.originalLeft = parseFloat(this.element.getStyle('left') || '0');
this.originalTop = parseFloat(this.element.getStyle('top') || '0');
},
update: function(position) {
this.element.setStyle({
left: (this.options.x * position + this.originalLeft).round() + '%',
top: (this.options.y * position + this.originalTop).round() + '%'
});
}
});
......@@ -91,22 +91,6 @@ function linkToggle(link, element, functn) {
}
}
// Finds all elements with the selector and removes the
// 'disabled' class.
// Options:
// * selector: selects the elements to apply on
// * disable: if set class 'disabled' will be set instead
// function enableAll(selector, disable) {
// var affectedElements = $$(selector);
// affectedElements.each(function(e) {
// if (disable) {
// e.addClassName('disabled');
// } else {
// e.removeClassName('disabled');
// }
// });
// }
//
// Checks if this element is in an disabled part of the DOM
// Can be used as a condition for onclick actions that should
......
// //
// // crabgrass pushState history support.
// //
// History.Adapter.bind(window,'statechange',function() {
// var state = History.getState();
// console.log('history state change: ' + JSON.stringify({url:state.url, data:state.data}))
// if (state.data) {
// process_history_state_change(state);
// }
// });
// function process_history_state_change(state) {
// var data = state.data;
// //
// // update a dom element with an ajax request
// //
// if (data.update && $(data.update.domid)) {
// new Ajax.Updater(data.update.domid, data.update.path, {
// asynchronous:true, evalScripts:true, method:'get'
// //parameters: 'authenticity_token=' + encodeURIComponent(data.token)
// });
// }
// //
// // hide and show a dom element
// //
// //if (data.hide && $(data.hide)) { $(data.hide).hide(); }
// //if (data.show && $(data.show)) { $(data.show).show(); }
// //
// // slide an dom element around
// //
// if (data.slide_left && $(data.slide_left)) {
// new Effect.MoveByPercent(data.slide_left, {duration: 0.5, x:-100});
// }
// if (data.slide_right && $(data.slide_right)) {
// new Effect.MoveByPercent(data.slide_right, {duration: 0.5, x:100});
// }
// }
A library to make HTML5 window.history work better, and also fallback
gracefully for HTML4 browsers.
https://github.com/balupton/History.js/
It is a little heavyweight, and includes a bunch of features we don't
need -- like storing data with history. For this reason, I created
json2-dummy.js, which tricks history.js into not storing data.
To update the source files in this directory:
cd app/assets/javascripts/libraries/history
curl https://nodeload.github.com/balupton/History.js/tarball/master | tar -vxz --transform 's/.*\/scripts\/uncompressed\///' --overwrite --wildcards '*/uncompressed/*.js'
rm history.adapter.jquery.js
rm history.adapter.mootools.js
rm history.adapter.right.js
rm history.adapter.zepto.js
There are two options we have for history.js support: html5 only or html5+html4.
For html5 only support, bundle these files:
history/history.adapter.native.js
history/history.js
For html4+html5 support, bundle these files (in this order):
history/json2.js
history/history.adapter.native.js
history/history.html4.js
history/history.js
See https://github.com/balupton/history.js/blob/master/buildr.coffee for the build rules.
NOTE: if/when we switch to jquery, replace native adapter with jquery adaptor.
/**
* History.js Native Adapter
* @author Benjamin Arthur Lupton <contact@balupton.com>
* @copyright 2010-2011 Benjamin Arthur Lupton <contact@balupton.com>
* @license New BSD License <http://creativecommons.org/licenses/BSD/>
*/
// Closure
(function(window,undefined){
"use strict";
// Localise Globals
var History = window.History = window.History||{};
// Check Existence
if ( typeof History.Adapter !== 'undefined' ) {
throw new Error('History.js Adapter has already been loaded...');
}
// Add the Adapter
History.Adapter = {
/**
* History.Adapter.handlers[uid][eventName] = Array
*/
handlers: {},
/**
* History.Adapter._uid
* The current element unique identifier
*/
_uid: 1,
/**
* History.Adapter.uid(element)
* @param {Element} element
* @return {String} uid
*/
uid: function(element){
return element._uid || (element._uid = History.Adapter._uid++);
},
/**
* History.Adapter.bind(el,event,callback)
* @param {Element} element
* @param {String} eventName - custom and standard events
* @param {Function} callback
* @return
*/
bind: function(element,eventName,callback){
// Prepare
var uid = History.Adapter.uid(element);
// Apply Listener
History.Adapter.handlers[uid] = History.Adapter.handlers[uid] || {};
History.Adapter.handlers[uid][eventName] = History.Adapter.handlers[uid][eventName] || [];
History.Adapter.handlers[uid][eventName].push(callback);
// Bind Global Listener
element['on'+eventName] = (function(element,eventName){
return function(event){
History.Adapter.trigger(element,eventName,event);
};
})(element,eventName);
},
/**
* History.Adapter.trigger(el,event)
* @param {Element} element
* @param {String} eventName - custom and standard events
* @param {Object} event - a object of event data
* @return
*/
trigger: function(element,eventName,event){
// Prepare
event = event || {};
var uid = History.Adapter.uid(element),
i,n;
// Apply Listener
History.Adapter.handlers[uid] = History.Adapter.handlers[uid] || {};
History.Adapter.handlers[uid][eventName] = History.Adapter.handlers[uid][eventName] || [];
// Fire Listeners
for ( i=0,n=History.Adapter.handlers[uid][eventName].length; i<n; ++i ) {
History.Adapter.handlers[uid][eventName][i].apply(this,[event]);
}
},
/**
* History.Adapter.extractEventData(key,event,extra)
* @param {String} key - key for the event data to extract
* @param {String} event - custom and standard events
* @return {mixed}
*/
extractEventData: function(key,event){
var result = (event && event[key]) || undefined;
return result;
},
/**
* History.Adapter.onDomLoad(callback)
* @param {Function} callback
* @return
*/
onDomLoad: function(callback) {
var timeout = window.setTimeout(function(){
callback();
},2000);
window.onload = function(){
clearTimeout(timeout);
callback();
};
}
};
// Try and Initialise History
if ( typeof History.init !== 'undefined' ) {
History.init();
}
})(window);
This diff is collapsed.
//
// This is a dummy json2.js with the same API, but it doesn't do anything.
// Created because I don't want or need the functionality it provides to history.js
//
if (!window.JSON) {
window.JSON = {};
}
(function () {
if (typeof JSON.stringify !== 'function') {
JSON.stringify = function (value, replacer, space) {return "{}" }
}
if (typeof JSON.parse !== 'function') {
JSON.parse = function (text, reviver) { return {} }
}
}());
This diff is collapsed.
//= require ./shims/classList.js
/*
* classList.js: Cross-browser full element.classList implementation.
* 1.2.20171210
*
* By Eli Grey, http://eligrey.com
* License: Dedicated to the public domain.
* See https://github.com/eligrey/classList.js/blob/master/LICENSE.md
*/
/*global self, document, DOMException */
/*! @source http://purl.eligrey.com/github/classList.js/blob/master/classList.js */
if ("document" in self) {
// Full polyfill for browsers with no classList support
// Including IE < Edge missing SVGElement.classList
if (
!("classList" in document.createElement("_"))
|| document.createElementNS
&& !("classList" in document.createElementNS("http://www.w3.org/2000/svg","g"))
) {
(function (view) {
"use strict";
if (!('Element' in view)) return;
var
classListProp = "classList"
, protoProp = "prototype"
, elemCtrProto = view.Element[protoProp]
, objCtr = Object
, strTrim = String[protoProp].trim || function () {
return this.replace(/^\s+|\s+$/g, "");
}
, arrIndexOf = Array[protoProp].indexOf || function (item) {
var
i = 0
, len = this.length
;
for (; i < len; i++) {
if (i in this && this[i] === item) {
return i;
}
}
return -1;
}
// Vendors: please allow content code to instantiate DOMExceptions
, DOMEx = function (type, message) {
this.name = type;
this.code = DOMException[type];
this.message = message;
}
, checkTokenAndGetIndex = function (classList, token) {
if (token === "") {
throw new DOMEx(
"SYNTAX_ERR"
, "The token must not be empty."
);
}
if (/\s/.test(token)) {
throw new DOMEx(
"INVALID_CHARACTER_ERR"
, "The token must not contain space characters."
);
}
return arrIndexOf.call(classList, token);
}
, ClassList = function (elem) {
var
trimmedClasses = strTrim.call(elem.getAttribute("class") || "")
, classes = trimmedClasses ? trimmedClasses.split(/\s+/) : []
, i = 0
, len = classes.length
;
for (; i < len; i++) {
this.push(classes[i]);
}
this._updateClassName = function () {
elem.setAttribute("class", this.toString());
};
}
, classListProto = ClassList[protoProp] = []
, classListGetter = function () {
return new ClassList(this);
}
;
// Most DOMException implementations don't allow calling DOMException's toString()
// on non-DOMExceptions. Error's toString() is sufficient here.
DOMEx[protoProp] = Error[protoProp];
classListProto.item = function (i) {
return this[i] || null;
};
classListProto.contains = function (token) {
return ~checkTokenAndGetIndex(this, token + "");
};
classListProto.add = function () {
var
tokens = arguments
, i = 0
, l = tokens.length
, token
, updated = false
;
do {
token = tokens[i] + "";
if (!~checkTokenAndGetIndex(this, token)) {
this.push(token);
updated = true;
}
}
while (++i < l);
if (updated) {
this._updateClassName();
}
};
classListProto.remove = function () {
var
tokens = arguments
, i = 0
, l = tokens.length
, token
, updated = false
, index
;
do {
token = tokens[i] + "";
index = checkTokenAndGetIndex(this, token);
while (~index) {
this.splice(index, 1);
updated = true;
index = checkTokenAndGetIndex(this, token);
}
}
while (++i < l);
if (updated) {
this._updateClassName();
}
};
classListProto.toggle = function (token, force) {
var
result = this.contains(token)
, method = result ?
force !== true && "remove"
:
force !== false && "add"
;
if (method) {
this[method](token);
}
if (force === true || force === false) {
return force;
} else {
return !result;
}
};
classListProto.replace = function (token, replacement_token) {
var index = checkTokenAndGetIndex(token + "");
if (~index) {
this.splice(index, 1, replacement_token);
this._updateClassName();
}
}
classListProto.toString = function () {
return this.join(" ");
};
if (objCtr.defineProperty) {
var classListPropDesc = {
get: classListGetter
, enumerable: true
, configurable: true
};
try {
objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc);
} catch (ex) { // IE 8 doesn't support enumerable:true
// adding undefined to fight this issue https://github.com/eligrey/classList.js/issues/36
// modernie IE8-MSW7 machine has IE8 8.0.6001.18702 and is affected
if (ex.number === undefined || ex.number === -0x7FF5EC54) {
classListPropDesc.enumerable = false;
objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc);
}
}
} else if (objCtr[protoProp].__defineGetter__) {
elemCtrProto.__defineGetter__(classListProp, classListGetter);
}
}(self));
}
// There is full or partial native classList support, so just check if we need
// to normalize the add/remove and toggle APIs.
(function () {
"use strict";
var testElement = document.createElement("_");
testElement.classList.add("c1", "c2");
// Polyfill for IE 10/11 and Firefox <26, where classList.add and
// classList.remove exist but support only one argument at a time.
if (!testElement.classList.contains("c2")) {
var createMethod = function(method) {
var original = DOMTokenList.prototype[method];
DOMTokenList.prototype[method] = function(token) {
var i, len = arguments.length;
for (i = 0; i < len; i++) {
token = arguments[i];
original.call(this, token);
}
};
};
createMethod('add');
createMethod('remove');
}
testElement.classList.toggle("c3", false);
// Polyfill for IE 10 and Firefox <24, where classList.toggle does not
// support the second argument.
if (testElement.classList.contains("c3")) {
var _toggle = DOMTokenList.prototype.toggle;
DOMTokenList.prototype.toggle = function(token, force) {
if (1 in arguments && !this.contains(token) === !force) {
return force;
} else {
return _toggle.call(this, token);
}
};
}
// replace() polyfill
if (!("replace" in document.createElement("_").classList)) {
DOMTokenList.prototype.replace = function (token, replacement_token) {
var
tokens = this.toString().split(" ")
, index = tokens.indexOf(token + "")
;
if (~index) {
tokens = tokens.slice(index);
this.remove.apply(this, tokens);
this.add(replacement_token);
this.add.apply(this, tokens.slice(1));
}
}
}
testElement = null;
}());
}
\ No newline at end of file
......@@ -101,7 +101,7 @@ class AccountsController < ApplicationController
user = User.find_by_email params[:email]
if user
token = User::Token.to_recover.create(user: user)
Mailer.forgot_password(token, mailer_options).deliver
Mailer.forgot_password(token, mailer_options).deliver_now
end
# this gives success even if there is no user, to not confirm that an email is in db
......@@ -120,7 +120,7 @@ class AccountsController < ApplicationController
@user.password = params[:new_password]
@user.password_confirmation = params[:password_confirmation]
if @user.save
Mailer.reset_password(@user, mailer_options).deliver
Mailer.reset_password(@user, mailer_options).deliver_now
@token.destroy
success :password_reset.t, :password_reset_ok_text.t, :nofade
redirect_to root_path
......@@ -134,7 +134,7 @@ class AccountsController < ApplicationController
# confirms that the token is valid, returns false otherwise.
#
def confirm_token
@token = User::Token.to_recover.active.find_by_param(params[:token])
@token = User::Token.to_recover.active.find_by_param(params.fetch(:token))
if @token.present?
@user = @token.user
else
......
......@@ -3,18 +3,14 @@
class ApplicationController < ActionController::Base
include Pundit
protect_from_forgery
protect_from_forgery prepend: true
layout proc { |c| c.request.xhr? ? false : 'application' } # skip layout for ajax
hide_action :_layout_from_proc
include_controllers 'common/application'
include_helpers 'app/helpers/common/*/*.rb'
helper :application, :modalbox
class_attribute :stylesheets, instance_reader: false, instance_writer: false
class_attribute :javascripts, instance_reader: false, instance_writer: false
protected
# this is used by the code that is included for both controllers and helpers.
......@@ -53,58 +49,4 @@ class ApplicationController < ActionController::Base
opts[:port] = request.port_string.sub(':', '') if request.port_string.present?
opts
end
##
## CLASS METHODS
##
# We currently include all stylesheets in screen.css as it's cached,
# hardly expires and the optional stylesheets do not add much weight
# anyway.
#
# Still keeping this here though in case we need it again.
#
# rather than include every stylesheet in every request,
# we used to only include some stylesheets "as needed".
# A controller can set a custom stylesheet using 'stylesheet'
# in the class definition:
#
# for example:
#
# stylesheet 'gallery', 'images'
# stylesheet 'page_creation', :action => :create
#
# They'll be accessible in the class_attribute stylesheets
#
# as needed stylesheets are kept in public/stylesheets/as_needed