From 6f65e804f0dab41d2a772abba03c922fa54e5560 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Mon, 29 Apr 2013 21:33:48 +0300 Subject: [PATCH 001/499] Adding custom fonts, normalize and base styles. --- app/assets/stylesheets/_base.sass | 2 + app/assets/stylesheets/application.css.scss | 5 + app/assets/stylesheets/utils/_fonts.sass | 27 ++ .../stylesheets/vendor/_normalize.css.scss | 402 ++++++++++++++++++ app/views/layouts/application.html.haml | 5 +- public/fonts/sonar-bold-webfont.eot | Bin 0 -> 19629 bytes public/fonts/sonar-bold-webfont.svg | 241 +++++++++++ public/fonts/sonar-bold-webfont.ttf | Bin 0 -> 40248 bytes public/fonts/sonar-bold-webfont.woff | Bin 0 -> 23364 bytes public/fonts/sonar-medium-webfont.eot | Bin 0 -> 19937 bytes public/fonts/sonar-medium-webfont.svg | 241 +++++++++++ public/fonts/sonar-medium-webfont.ttf | Bin 0 -> 41080 bytes public/fonts/sonar-medium-webfont.woff | Bin 0 -> 23660 bytes public/fonts/sonar-normal-webfont.eot | Bin 0 -> 19673 bytes public/fonts/sonar-normal-webfont.svg | 241 +++++++++++ public/fonts/sonar-normal-webfont.ttf | Bin 0 -> 40696 bytes public/fonts/sonar-normal-webfont.woff | Bin 0 -> 23252 bytes 17 files changed, 1163 insertions(+), 1 deletion(-) create mode 100644 app/assets/stylesheets/_base.sass create mode 100644 app/assets/stylesheets/utils/_fonts.sass create mode 100644 app/assets/stylesheets/vendor/_normalize.css.scss create mode 100755 public/fonts/sonar-bold-webfont.eot create mode 100755 public/fonts/sonar-bold-webfont.svg create mode 100755 public/fonts/sonar-bold-webfont.ttf create mode 100755 public/fonts/sonar-bold-webfont.woff create mode 100755 public/fonts/sonar-medium-webfont.eot create mode 100755 public/fonts/sonar-medium-webfont.svg create mode 100755 public/fonts/sonar-medium-webfont.ttf create mode 100755 public/fonts/sonar-medium-webfont.woff create mode 100755 public/fonts/sonar-normal-webfont.eot create mode 100755 public/fonts/sonar-normal-webfont.svg create mode 100755 public/fonts/sonar-normal-webfont.ttf create mode 100755 public/fonts/sonar-normal-webfont.woff diff --git a/app/assets/stylesheets/_base.sass b/app/assets/stylesheets/_base.sass new file mode 100644 index 00000000..9398c453 --- /dev/null +++ b/app/assets/stylesheets/_base.sass @@ -0,0 +1,2 @@ +body + +custom-sans diff --git a/app/assets/stylesheets/application.css.scss b/app/assets/stylesheets/application.css.scss index 87dcf3cb..96fc3fd9 100644 --- a/app/assets/stylesheets/application.css.scss +++ b/app/assets/stylesheets/application.css.scss @@ -1,4 +1,9 @@ +@import "vendor/normalize"; @import "settings"; @import "bourbon"; @import "neat"; + +@import "utils/fonts"; + +@import "base"; diff --git a/app/assets/stylesheets/utils/_fonts.sass b/app/assets/stylesheets/utils/_fonts.sass new file mode 100644 index 00000000..c13e1368 --- /dev/null +++ b/app/assets/stylesheets/utils/_fonts.sass @@ -0,0 +1,27 @@ +$weights: bold bold, medium normal, normal normal + +@each $weight in $weights + +font-face(sonar-#{nth($weight, 1)}, '/fonts/sonar-#{nth($weight, 1)}-webfont', nth($weight, 2)) + +=custom-sans($weight: normal) + font-family: "omnes-pro", Helvetica, Arial, sans-serif + @if $weight == thin + font-weight: 100 + @else if $weight == light + font-weight: 300 + @else if $weight == bold + font-weight: 600 + @else if $weight == black + font-weight: 900 + @else + font-weight: 400 + +=custom-serif($weight: normal, $style: normal) + font-family: "freight-text-pro", Georgia, serif + @if $weight == bold + font-weight: 700 + @else if $weight == light + font-weight: 300 + @else + font-weight: 400 + font-style: $style diff --git a/app/assets/stylesheets/vendor/_normalize.css.scss b/app/assets/stylesheets/vendor/_normalize.css.scss new file mode 100644 index 00000000..6d24a385 --- /dev/null +++ b/app/assets/stylesheets/vendor/_normalize.css.scss @@ -0,0 +1,402 @@ +/*! normalize.css v2.1.1 | MIT License | git.io/normalize */ + +/* ========================================================================== + HTML5 display definitions + ========================================================================== */ + +/** + * Correct `block` display not defined in IE 8/9. + */ + +article, +aside, +details, +figcaption, +figure, +footer, +header, +hgroup, +main, +nav, +section, +summary { + display: block; +} + +/** + * Correct `inline-block` display not defined in IE 8/9. + */ + +audio, +canvas, +video { + display: inline-block; +} + +/** + * Prevent modern browsers from displaying `audio` without controls. + * Remove excess height in iOS 5 devices. + */ + +audio:not([controls]) { + display: none; + height: 0; +} + +/** + * Address styling not present in IE 8/9. + */ + +[hidden] { + display: none; +} + +/* ========================================================================== + Base + ========================================================================== */ + +/** + * 1. Prevent system color scheme's background color being used in Firefox, IE, + * and Opera. + * 2. Prevent system color scheme's text color being used in Firefox, IE, and + * Opera. + * 3. Set default font family to sans-serif. + * 4. Prevent iOS text size adjust after orientation change, without disabling + * user zoom. + */ + +html { + background: #fff; /* 1 */ + color: #000; /* 2 */ + font-family: sans-serif; /* 3 */ + -ms-text-size-adjust: 100%; /* 4 */ + -webkit-text-size-adjust: 100%; /* 4 */ +} + +/** + * Remove default margin. + */ + +body { + margin: 0; +} + +/* ========================================================================== + Links + ========================================================================== */ + +/** + * Address `outline` inconsistency between Chrome and other browsers. + */ + +a:focus { + outline: thin dotted; +} + +/** + * Improve readability when focused and also mouse hovered in all browsers. + */ + +a:active, +a:hover { + outline: 0; +} + +/* ========================================================================== + Typography + ========================================================================== */ + +/** + * Address variable `h1` font-size and margin within `section` and `article` + * contexts in Firefox 4+, Safari 5, and Chrome. + */ + +h1 { + font-size: 2em; + margin: 0.67em 0; +} + +/** + * Address styling not present in IE 8/9, Safari 5, and Chrome. + */ + +abbr[title] { + border-bottom: 1px dotted; +} + +/** + * Address style set to `bolder` in Firefox 4+, Safari 5, and Chrome. + */ + +b, +strong { + font-weight: bold; +} + +/** + * Address styling not present in Safari 5 and Chrome. + */ + +dfn { + font-style: italic; +} + +/** + * Address differences between Firefox and other browsers. + */ + +hr { + -moz-box-sizing: content-box; + box-sizing: content-box; + height: 0; +} + +/** + * Address styling not present in IE 8/9. + */ + +mark { + background: #ff0; + color: #000; +} + +/** + * Correct font family set oddly in Safari 5 and Chrome. + */ + +code, +kbd, +pre, +samp { + font-family: monospace, serif; + font-size: 1em; +} + +/** + * Improve readability of pre-formatted text in all browsers. + */ + +pre { + white-space: pre-wrap; +} + +/** + * Set consistent quote types. + */ + +q { + quotes: "\201C" "\201D" "\2018" "\2019"; +} + +/** + * Address inconsistent and variable font size in all browsers. + */ + +small { + font-size: 80%; +} + +/** + * Prevent `sub` and `sup` affecting `line-height` in all browsers. + */ + +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} + +sup { + top: -0.5em; +} + +sub { + bottom: -0.25em; +} + +/* ========================================================================== + Embedded content + ========================================================================== */ + +/** + * Remove border when inside `a` element in IE 8/9. + */ + +img { + border: 0; +} + +/** + * Correct overflow displayed oddly in IE 9. + */ + +svg:not(:root) { + overflow: hidden; +} + +/* ========================================================================== + Figures + ========================================================================== */ + +/** + * Address margin not present in IE 8/9 and Safari 5. + */ + +figure { + margin: 0; +} + +/* ========================================================================== + Forms + ========================================================================== */ + +/** + * Define consistent border, margin, and padding. + */ + +fieldset { + border: 1px solid #c0c0c0; + margin: 0 2px; + padding: 0.35em 0.625em 0.75em; +} + +/** + * 1. Correct `color` not being inherited in IE 8/9. + * 2. Remove padding so people aren't caught out if they zero out fieldsets. + */ + +legend { + border: 0; /* 1 */ + padding: 0; /* 2 */ +} + +/** + * 1. Correct font family not being inherited in all browsers. + * 2. Correct font size not being inherited in all browsers. + * 3. Address margins set differently in Firefox 4+, Safari 5, and Chrome. + */ + +button, +input, +select, +textarea { + font-family: inherit; /* 1 */ + font-size: 100%; /* 2 */ + margin: 0; /* 3 */ +} + +/** + * Address Firefox 4+ setting `line-height` on `input` using `!important` in + * the UA stylesheet. + */ + +button, +input { + line-height: normal; +} + +/** + * Address inconsistent `text-transform` inheritance for `button` and `select`. + * All other form control elements do not inherit `text-transform` values. + * Correct `button` style inheritance in Chrome, Safari 5+, and IE 8+. + * Correct `select` style inheritance in Firefox 4+ and Opera. + */ + +button, +select { + text-transform: none; +} + +/** + * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` + * and `video` controls. + * 2. Correct inability to style clickable `input` types in iOS. + * 3. Improve usability and consistency of cursor style between image-type + * `input` and others. + */ + +button, +html input[type="button"], /* 1 */ +input[type="reset"], +input[type="submit"] { + -webkit-appearance: button; /* 2 */ + cursor: pointer; /* 3 */ +} + +/** + * Re-set default cursor for disabled elements. + */ + +button[disabled], +html input[disabled] { + cursor: default; +} + +/** + * 1. Address box sizing set to `content-box` in IE 8/9. + * 2. Remove excess padding in IE 8/9. + */ + +input[type="checkbox"], +input[type="radio"] { + box-sizing: border-box; /* 1 */ + padding: 0; /* 2 */ +} + +/** + * 1. Address `appearance` set to `searchfield` in Safari 5 and Chrome. + * 2. Address `box-sizing` set to `border-box` in Safari 5 and Chrome + * (include `-moz` to future-proof). + */ + +input[type="search"] { + -webkit-appearance: textfield; /* 1 */ + -moz-box-sizing: content-box; + -webkit-box-sizing: content-box; /* 2 */ + box-sizing: content-box; +} + +/** + * Remove inner padding and search cancel button in Safari 5 and Chrome + * on OS X. + */ + +input[type="search"]::-webkit-search-cancel-button, +input[type="search"]::-webkit-search-decoration { + -webkit-appearance: none; +} + +/** + * Remove inner padding and border in Firefox 4+. + */ + +button::-moz-focus-inner, +input::-moz-focus-inner { + border: 0; + padding: 0; +} + +/** + * 1. Remove default vertical scrollbar in IE 8/9. + * 2. Improve readability and alignment in all browsers. + */ + +textarea { + overflow: auto; /* 1 */ + vertical-align: top; /* 2 */ +} + +/* ========================================================================== + Tables + ========================================================================== */ + +/** + * Remove most spacing between table cells. + */ + +table { + border-collapse: collapse; + border-spacing: 0; +} diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml index 02176b64..d838dcae 100644 --- a/app/views/layouts/application.html.haml +++ b/app/views/layouts/application.html.haml @@ -4,7 +4,10 @@ %title= title("Eurucamp Activities") = stylesheet_link_tag "application" = javascript_include_tag "application" - = csrf_meta_tags + + = javascript_include_tag "//use.typekit.net/vor5lqb.js" + :javascript + try{Typekit.load();}catch(e){} %body(class=body_class) = render "partials/chrome_frame" diff --git a/public/fonts/sonar-bold-webfont.eot b/public/fonts/sonar-bold-webfont.eot new file mode 100755 index 0000000000000000000000000000000000000000..1e438f944f615bac763147920057e76cc3adcbdc GIT binary patch literal 19629 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 Qvn{;sBel9ff}WNwfJcIPL;wH) literal 0 HcmV?d00001 diff --git a/public/fonts/sonar-bold-webfont.svg b/public/fonts/sonar-bold-webfont.svg new file mode 100755 index 00000000..6963939e --- /dev/null +++ b/public/fonts/sonar-bold-webfont.svg @@ -0,0 +1,241 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/public/fonts/sonar-bold-webfont.ttf b/public/fonts/sonar-bold-webfont.ttf new file mode 100755 index 0000000000000000000000000000000000000000..68480da4e7f1d03a23a9b571e49d1bddced91ae6 GIT binary patch literal 40248 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 literal 0 HcmV?d00001 diff --git a/public/fonts/sonar-bold-webfont.woff b/public/fonts/sonar-bold-webfont.woff new file mode 100755 index 0000000000000000000000000000000000000000..6368079bedb4f49d6c7a809617d5ac05aba37724 GIT binary patch literal 23364 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 Gt(36sP?pF5 literal 0 HcmV?d00001 diff --git a/public/fonts/sonar-medium-webfont.svg b/public/fonts/sonar-medium-webfont.svg new file mode 100755 index 00000000..60756f34 --- /dev/null +++ b/public/fonts/sonar-medium-webfont.svg @@ -0,0 +1,241 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/public/fonts/sonar-medium-webfont.ttf b/public/fonts/sonar-medium-webfont.ttf new file mode 100755 index 0000000000000000000000000000000000000000..d68d03f02fbf3fd111823ee5d8e9db7cb3d33f58 GIT binary patch literal 41080 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℘D8 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 RtV{I(00001Q8!J2006iBxtjn0 literal 0 HcmV?d00001 diff --git a/public/fonts/sonar-normal-webfont.eot b/public/fonts/sonar-normal-webfont.eot new file mode 100755 index 0000000000000000000000000000000000000000..503285e8334df1026574c7c0ea64e790f1a57699 GIT binary patch literal 19673 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 Au>b%7 literal 0 HcmV?d00001 diff --git a/public/fonts/sonar-normal-webfont.svg b/public/fonts/sonar-normal-webfont.svg new file mode 100755 index 00000000..2d91df9d --- /dev/null +++ b/public/fonts/sonar-normal-webfont.svg @@ -0,0 +1,241 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/public/fonts/sonar-normal-webfont.ttf b/public/fonts/sonar-normal-webfont.ttf new file mode 100755 index 0000000000000000000000000000000000000000..710111a531dcbb7653db9ff8bed33a156cc7c383 GIT binary patch literal 40696 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 Date: Mon, 29 Apr 2013 22:09:04 +0300 Subject: [PATCH 002/499] Addding modernizr. --- app/views/layouts/application.html.haml | 4 +++- vendor/assets/javascripts/modernizr.min.js | 4 ++++ 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 vendor/assets/javascripts/modernizr.min.js diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml index d838dcae..09a62909 100644 --- a/app/views/layouts/application.html.haml +++ b/app/views/layouts/application.html.haml @@ -3,7 +3,7 @@ %head %title= title("Eurucamp Activities") = stylesheet_link_tag "application" - = javascript_include_tag "application" + = javascript_include_tag "modernizr.min" = javascript_include_tag "//use.typekit.net/vor5lqb.js" :javascript @@ -13,3 +13,5 @@ = render "partials/chrome_frame" = yield + + = javascript_include_tag "application" diff --git a/vendor/assets/javascripts/modernizr.min.js b/vendor/assets/javascripts/modernizr.min.js new file mode 100644 index 00000000..2dd634df --- /dev/null +++ b/vendor/assets/javascripts/modernizr.min.js @@ -0,0 +1,4 @@ +/* Modernizr 2.6.2 (Custom Build) | MIT & BSD + * Build: http://modernizr.com/download/#-fontface-backgroundsize-borderimage-borderradius-boxshadow-flexbox-flexboxlegacy-hsla-multiplebgs-opacity-rgba-textshadow-cssanimations-csscolumns-generatedcontent-cssgradients-cssreflections-csstransforms-csstransforms3d-csstransitions-canvas-hashchange-shiv-cssclasses-teststyles-testprop-testallprops-hasevent-prefixes-domprefixes-load + */ +;window.Modernizr=function(a,b,c){function B(a){j.cssText=a}function C(a,b){return B(n.join(a+";")+(b||""))}function D(a,b){return typeof a===b}function E(a,b){return!!~(""+a).indexOf(b)}function F(a,b){for(var d in a){var e=a[d];if(!E(e,"-")&&j[e]!==c)return b=="pfx"?e:!0}return!1}function G(a,b,d){for(var e in a){var f=b[a[e]];if(f!==c)return d===!1?a[e]:D(f,"function")?f.bind(d||b):f}return!1}function H(a,b,c){var d=a.charAt(0).toUpperCase()+a.slice(1),e=(a+" "+p.join(d+" ")+d).split(" ");return D(b,"string")||D(b,"undefined")?F(e,b):(e=(a+" "+q.join(d+" ")+d).split(" "),G(e,b,c))}var d="2.6.2",e={},f=!0,g=b.documentElement,h="modernizr",i=b.createElement(h),j=i.style,k,l=":)",m={}.toString,n=" -webkit- -moz- -o- -ms- ".split(" "),o="Webkit Moz O ms",p=o.split(" "),q=o.toLowerCase().split(" "),r={},s={},t={},u=[],v=u.slice,w,x=function(a,c,d,e){var f,i,j,k,l=b.createElement("div"),m=b.body,n=m||b.createElement("body");if(parseInt(d,10))while(d--)j=b.createElement("div"),j.id=e?e[d]:h+(d+1),l.appendChild(j);return f=["­",'"].join(""),l.id=h,(m?l:n).innerHTML+=f,n.appendChild(l),m||(n.style.background="",n.style.overflow="hidden",k=g.style.overflow,g.style.overflow="hidden",g.appendChild(n)),i=c(l,a),m?l.parentNode.removeChild(l):(n.parentNode.removeChild(n),g.style.overflow=k),!!i},y=function(){function d(d,e){e=e||b.createElement(a[d]||"div"),d="on"+d;var f=d in e;return f||(e.setAttribute||(e=b.createElement("div")),e.setAttribute&&e.removeAttribute&&(e.setAttribute(d,""),f=D(e[d],"function"),D(e[d],"undefined")||(e[d]=c),e.removeAttribute(d))),e=null,f}var a={select:"input",change:"input",submit:"form",reset:"form",error:"img",load:"img",abort:"img"};return d}(),z={}.hasOwnProperty,A;!D(z,"undefined")&&!D(z.call,"undefined")?A=function(a,b){return z.call(a,b)}:A=function(a,b){return b in a&&D(a.constructor.prototype[b],"undefined")},Function.prototype.bind||(Function.prototype.bind=function(b){var c=this;if(typeof c!="function")throw new TypeError;var d=v.call(arguments,1),e=function(){if(this instanceof e){var a=function(){};a.prototype=c.prototype;var f=new a,g=c.apply(f,d.concat(v.call(arguments)));return Object(g)===g?g:f}return c.apply(b,d.concat(v.call(arguments)))};return e}),r.flexbox=function(){return H("flexWrap")},r.flexboxlegacy=function(){return H("boxDirection")},r.canvas=function(){var a=b.createElement("canvas");return!!a.getContext&&!!a.getContext("2d")},r.hashchange=function(){return y("hashchange",a)&&(b.documentMode===c||b.documentMode>7)},r.rgba=function(){return B("background-color:rgba(150,255,150,.5)"),E(j.backgroundColor,"rgba")},r.hsla=function(){return B("background-color:hsla(120,40%,100%,.5)"),E(j.backgroundColor,"rgba")||E(j.backgroundColor,"hsla")},r.multiplebgs=function(){return B("background:url(https://),url(https://),red url(https://)"),/(url\s*\(.*?){3}/.test(j.background)},r.backgroundsize=function(){return H("backgroundSize")},r.borderimage=function(){return H("borderImage")},r.borderradius=function(){return H("borderRadius")},r.boxshadow=function(){return H("boxShadow")},r.textshadow=function(){return b.createElement("div").style.textShadow===""},r.opacity=function(){return C("opacity:.55"),/^0.55$/.test(j.opacity)},r.cssanimations=function(){return H("animationName")},r.csscolumns=function(){return H("columnCount")},r.cssgradients=function(){var a="background-image:",b="gradient(linear,left top,right bottom,from(#9f9),to(white));",c="linear-gradient(left top,#9f9, white);";return B((a+"-webkit- ".split(" ").join(b+a)+n.join(c+a)).slice(0,-a.length)),E(j.backgroundImage,"gradient")},r.cssreflections=function(){return H("boxReflect")},r.csstransforms=function(){return!!H("transform")},r.csstransforms3d=function(){var a=!!H("perspective");return a&&"webkitPerspective"in g.style&&x("@media (transform-3d),(-webkit-transform-3d){#modernizr{left:9px;position:absolute;height:3px;}}",function(b,c){a=b.offsetLeft===9&&b.offsetHeight===3}),a},r.csstransitions=function(){return H("transition")},r.fontface=function(){var a;return x('@font-face {font-family:"font";src:url("https://")}',function(c,d){var e=b.getElementById("smodernizr"),f=e.sheet||e.styleSheet,g=f?f.cssRules&&f.cssRules[0]?f.cssRules[0].cssText:f.cssText||"":"";a=/src/i.test(g)&&g.indexOf(d.split(" ")[0])===0}),a},r.generatedcontent=function(){var a;return x(["#",h,"{font:0/0 a}#",h,':after{content:"',l,'";visibility:hidden;font:3px/1 a}'].join(""),function(b){a=b.offsetHeight>=3}),a};for(var I in r)A(r,I)&&(w=I.toLowerCase(),e[w]=r[I](),u.push((e[w]?"":"no-")+w));return e.addTest=function(a,b){if(typeof a=="object")for(var d in a)A(a,d)&&e.addTest(d,a[d]);else{a=a.toLowerCase();if(e[a]!==c)return e;b=typeof b=="function"?b():b,typeof f!="undefined"&&f&&(g.className+=" "+(b?"":"no-")+a),e[a]=b}return e},B(""),i=k=null,function(a,b){function k(a,b){var c=a.createElement("p"),d=a.getElementsByTagName("head")[0]||a.documentElement;return c.innerHTML="x",d.insertBefore(c.lastChild,d.firstChild)}function l(){var a=r.elements;return typeof a=="string"?a.split(" "):a}function m(a){var b=i[a[g]];return b||(b={},h++,a[g]=h,i[h]=b),b}function n(a,c,f){c||(c=b);if(j)return c.createElement(a);f||(f=m(c));var g;return f.cache[a]?g=f.cache[a].cloneNode():e.test(a)?g=(f.cache[a]=f.createElem(a)).cloneNode():g=f.createElem(a),g.canHaveChildren&&!d.test(a)?f.frag.appendChild(g):g}function o(a,c){a||(a=b);if(j)return a.createDocumentFragment();c=c||m(a);var d=c.frag.cloneNode(),e=0,f=l(),g=f.length;for(;e",f="hidden"in a,j=a.childNodes.length==1||function(){b.createElement("a");var a=b.createDocumentFragment();return typeof a.cloneNode=="undefined"||typeof a.createDocumentFragment=="undefined"||typeof a.createElement=="undefined"}()}catch(c){f=!0,j=!0}})();var r={elements:c.elements||"abbr article aside audio bdi canvas data datalist details figcaption figure footer header hgroup mark meter nav output progress section summary time video",shivCSS:c.shivCSS!==!1,supportsUnknownElements:j,shivMethods:c.shivMethods!==!1,type:"default",shivDocument:q,createElement:n,createDocumentFragment:o};a.html5=r,q(b)}(this,b),e._version=d,e._prefixes=n,e._domPrefixes=q,e._cssomPrefixes=p,e.hasEvent=y,e.testProp=function(a){return F([a])},e.testAllProps=H,e.testStyles=x,g.className=g.className.replace(/(^|\s)no-js(\s|$)/,"$1$2")+(f?" js "+u.join(" "):""),e}(this,this.document),function(a,b,c){function d(a){return"[object Function]"==o.call(a)}function e(a){return"string"==typeof a}function f(){}function g(a){return!a||"loaded"==a||"complete"==a||"uninitialized"==a}function h(){var a=p.shift();q=1,a?a.t?m(function(){("c"==a.t?B.injectCss:B.injectJs)(a.s,0,a.a,a.x,a.e,1)},0):(a(),h()):q=0}function i(a,c,d,e,f,i,j){function k(b){if(!o&&g(l.readyState)&&(u.r=o=1,!q&&h(),l.onload=l.onreadystatechange=null,b)){"img"!=a&&m(function(){t.removeChild(l)},50);for(var d in y[c])y[c].hasOwnProperty(d)&&y[c][d].onload()}}var j=j||B.errorTimeout,l=b.createElement(a),o=0,r=0,u={t:d,s:c,e:f,a:i,x:j};1===y[c]&&(r=1,y[c]=[]),"object"==a?l.data=c:(l.src=c,l.type=a),l.width=l.height="0",l.onerror=l.onload=l.onreadystatechange=function(){k.call(this,r)},p.splice(e,0,u),"img"!=a&&(r||2===y[c]?(t.insertBefore(l,s?null:n),m(k,j)):y[c].push(l))}function j(a,b,c,d,f){return q=0,b=b||"j",e(a)?i("c"==b?v:u,a,b,this.i++,c,d,f):(p.splice(this.i++,0,a),1==p.length&&h()),this}function k(){var a=B;return a.loader={load:j,i:0},a}var l=b.documentElement,m=a.setTimeout,n=b.getElementsByTagName("script")[0],o={}.toString,p=[],q=0,r="MozAppearance"in l.style,s=r&&!!b.createRange().compareNode,t=s?l:n.parentNode,l=a.opera&&"[object Opera]"==o.call(a.opera),l=!!b.attachEvent&&!l,u=r?"object":l?"script":"img",v=l?"script":u,w=Array.isArray||function(a){return"[object Array]"==o.call(a)},x=[],y={},z={timeout:function(a,b){return b.length&&(a.timeout=b[0]),a}},A,B;B=function(a){function b(a){var a=a.split("!"),b=x.length,c=a.pop(),d=a.length,c={url:c,origUrl:c,prefixes:a},e,f,g;for(f=0;f Date: Mon, 29 Apr 2013 22:12:13 +0300 Subject: [PATCH 003/499] Moving vendored styles to vendor folder. --- app/assets/stylesheets/application.css.scss | 2 +- .../vendor => vendor/assets/stylesheets}/_normalize.css.scss | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename {app/assets/stylesheets/vendor => vendor/assets/stylesheets}/_normalize.css.scss (100%) diff --git a/app/assets/stylesheets/application.css.scss b/app/assets/stylesheets/application.css.scss index 96fc3fd9..2f55f930 100644 --- a/app/assets/stylesheets/application.css.scss +++ b/app/assets/stylesheets/application.css.scss @@ -1,4 +1,4 @@ -@import "vendor/normalize"; +@import "normalize"; @import "settings"; @import "bourbon"; diff --git a/app/assets/stylesheets/vendor/_normalize.css.scss b/vendor/assets/stylesheets/_normalize.css.scss similarity index 100% rename from app/assets/stylesheets/vendor/_normalize.css.scss rename to vendor/assets/stylesheets/_normalize.css.scss From 93b5bc9887b50d295cf11a6f5adcd9ccc7652e9c Mon Sep 17 00:00:00 2001 From: Polarblau Date: Mon, 29 Apr 2013 22:22:57 +0300 Subject: [PATCH 004/499] Adding jQuery progress plugin. --- app/assets/javascripts/application.js | 1 + .../assets/javascripts/jquery.progress.coffee | 84 +++++++++++++++++++ 2 files changed, 85 insertions(+) create mode 100644 vendor/assets/javascripts/jquery.progress.coffee diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index 9e83eb5e..698f2d82 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -13,4 +13,5 @@ //= require jquery //= require jquery_ujs //= require turbolinks +//= require jquery.progress //= require_tree . diff --git a/vendor/assets/javascripts/jquery.progress.coffee b/vendor/assets/javascripts/jquery.progress.coffee new file mode 100644 index 00000000..ee93af3d --- /dev/null +++ b/vendor/assets/javascripts/jquery.progress.coffee @@ -0,0 +1,84 @@ +### + +jQuery plugin, displays a number between 0 and 100 +as partial circle around an image. + +MIT licensed, (c) 2013 Florian Plank, florian@polarblau.com + +### +$ = jQuery + +$.fn.extend + progress: (options) -> + + settings = + strokeWidth : 6 + strokeColor : '#15d701' + strokeColorFull: '#e73921' + duration : 1500 + easing : 'easeOutBounce' + + settings = $.extend settings, options + # we don't want to pass these around with every call to .render + context = null + canvasSize = 0 + color = settings.strokeColor + + init = -> + $el = $(@) + size = $el.width() + canvasSize = size + settings.strokeWidth * 2 + + # this is the value we want to display + progress = settings.progress || parseInt($el.data('progress')) + + # when the value > 100 use different color + color = settings.strokeColorFull if progress >= 100 + + # wrap image + $wrapper = $('
', class: 'wrapper').css + position: 'relative' + padding : settings.strokeWidth + $el.wrap($wrapper) + + # generate canvas + $canvas = $('') + .attr(width: canvasSize, height: canvasSize) + .css + position: 'absolute' + zIndex : 1 + left : 0 + top : 0 + $el.after($canvas) + context = $canvas.get(0).getContext("2d") + + # animated call to render + $({ progress: 0 }).animate({ + progress: progress + },{ + duration : settings.duration + queue : false + easing : settings.easing + step : render + }) + + percentageToRadians = (percentage) -> + (1.5 - (percentage * 2 / 100)) * Math.PI + + render = (percentage) -> + context.clearRect(0, 0, canvasSize, canvasSize) + context.beginPath() + context.arc(canvasSize / 2, # origin x + canvasSize / 2, # origin y + canvasSize / 2 - settings.strokeWidth / 2, # radius + percentageToRadians(percentage), # start angle + 1.5 * Math.PI, # end angle + false) + context.strokeStyle = color + context.lineCap = 'round' + context.lineWidth = settings.strokeWidth + context.stroke() + + @each -> + # ensure that the image is really loaded + $(@).load init From 9065392d1fdbcab6041ed0f5dfb443f22fef9b71 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Mon, 29 Apr 2013 22:26:10 +0300 Subject: [PATCH 005/499] Housekeeping. --- vendor/assets/javascripts/jquery.progress.coffee | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/vendor/assets/javascripts/jquery.progress.coffee b/vendor/assets/javascripts/jquery.progress.coffee index ee93af3d..a1375792 100644 --- a/vendor/assets/javascripts/jquery.progress.coffee +++ b/vendor/assets/javascripts/jquery.progress.coffee @@ -68,12 +68,12 @@ $.fn.extend render = (percentage) -> context.clearRect(0, 0, canvasSize, canvasSize) context.beginPath() - context.arc(canvasSize / 2, # origin x - canvasSize / 2, # origin y - canvasSize / 2 - settings.strokeWidth / 2, # radius - percentageToRadians(percentage), # start angle - 1.5 * Math.PI, # end angle - false) + context.arc(canvasSize / 2, # origin x + canvasSize / 2, # origin y + canvasSize / 2 - settings.strokeWidth / 2, # radius + percentageToRadians(percentage), # start angle + 1.5 * Math.PI, # end angle + false) context.strokeStyle = color context.lineCap = 'round' context.lineWidth = settings.strokeWidth From 67656ea9899c34a64e89e9f53155a599c4eafe29 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Mon, 29 Apr 2013 22:44:41 +0300 Subject: [PATCH 006/499] Adding footer and header. --- app/views/layouts/application.html.haml | 8 +++-- app/views/partials/_footer.html.haml | 9 ++++++ app/views/partials/_header.html.haml | 11 +++++++ config/locales/en.yml | 41 ++++++++++++------------- 4 files changed, 44 insertions(+), 25 deletions(-) create mode 100644 app/views/partials/_footer.html.haml create mode 100644 app/views/partials/_header.html.haml diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml index 09a62909..e61316cb 100644 --- a/app/views/layouts/application.html.haml +++ b/app/views/layouts/application.html.haml @@ -11,7 +11,9 @@ %body(class=body_class) = render "partials/chrome_frame" - - = yield - + #container + = render "partials/header" + #main(role="main") + = yield + = render "partials/footer" = javascript_include_tag "application" diff --git a/app/views/partials/_footer.html.haml b/app/views/partials/_footer.html.haml new file mode 100644 index 00000000..648f3c60 --- /dev/null +++ b/app/views/partials/_footer.html.haml @@ -0,0 +1,9 @@ +#footer + + -# cross-site navigation, add correct links + %nav + %ul + - %w(about policy eurucamp).each do |target| + %li + %a(href="#" title="#{t("footer_nav.#{target}.title")}") + = t("footer_nav.#{target}.label") diff --git a/app/views/partials/_header.html.haml b/app/views/partials/_header.html.haml new file mode 100644 index 00000000..4bab0ed2 --- /dev/null +++ b/app/views/partials/_header.html.haml @@ -0,0 +1,11 @@ +%header#header + + -# user navigation, don't show unless logged-in + %nav.user-nav + %ul + %li + %a(href="#" title="#{t("user_nav.account.title")}") + = t("user_nav.account.label") + %li + %a(href="#" title="#{t("user_nav.logout.title")}") + = t("user_nav.logout.label") diff --git a/config/locales/en.yml b/config/locales/en.yml index 06539571..e53cd600 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -1,23 +1,20 @@ -# Files in the config/locales directory are used for internationalization -# and are automatically loaded by Rails. If you want to use locales other -# than English, add the necessary files in this directory. -# -# To use the locales, use `I18n.t`: -# -# I18n.t 'hello' -# -# In views, this is aliased to just `t`: -# -# <%= t('hello') %> -# -# To use a different locale, set it with `I18n.locale`: -# -# I18n.locale = :es -# -# This would use the information in config/locales/es.yml. -# -# To learn more, please read the Rails Internationalization guide -# available at http://guides.rubyonrails.org/i18n.html. - en: - hello: "Hello world" + user_nav: + account: + label: My Account + title: Edit your account details + logout: + label: Logout + title: Leave now + + + footer_nav: + about: + label: About + title: What is this all about? + policy: + label: Policy + title: Policy and Code of Conduct + eurucamp: + label: eurucamp.org + title: Head over to the main site From bdb1c50c482afbd7580893d405e39b2e3c6f8095 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Mon, 29 Apr 2013 22:46:38 +0300 Subject: [PATCH 007/499] General link style. --- app/assets/stylesheets/_base.sass | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/app/assets/stylesheets/_base.sass b/app/assets/stylesheets/_base.sass index 9398c453..8cd0f703 100644 --- a/app/assets/stylesheets/_base.sass +++ b/app/assets/stylesheets/_base.sass @@ -1,2 +1,11 @@ body +custom-sans + + +a, a:visited + color: $blue + text-decoration: none + border-bottom: 1px dotted $blue + +a:hover + border: none From 3b28a5f9c8825ebfceeb57546144964992207b02 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Mon, 29 Apr 2013 22:51:01 +0300 Subject: [PATCH 008/499] Basic layout. --- app/assets/stylesheets/_base.sass | 4 +++- app/assets/stylesheets/application.css.scss | 4 ++++ app/assets/stylesheets/partials/_footer.sass | 2 ++ app/assets/stylesheets/partials/_header.sass | 2 ++ app/assets/stylesheets/partials/_layout.sass | 2 ++ 5 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 app/assets/stylesheets/partials/_footer.sass create mode 100644 app/assets/stylesheets/partials/_header.sass create mode 100644 app/assets/stylesheets/partials/_layout.sass diff --git a/app/assets/stylesheets/_base.sass b/app/assets/stylesheets/_base.sass index 8cd0f703..f1d71e3c 100644 --- a/app/assets/stylesheets/_base.sass +++ b/app/assets/stylesheets/_base.sass @@ -1,7 +1,9 @@ +html + background: $extra-light-gray + body +custom-sans - a, a:visited color: $blue text-decoration: none diff --git a/app/assets/stylesheets/application.css.scss b/app/assets/stylesheets/application.css.scss index 2f55f930..d4911a1e 100644 --- a/app/assets/stylesheets/application.css.scss +++ b/app/assets/stylesheets/application.css.scss @@ -7,3 +7,7 @@ @import "utils/fonts"; @import "base"; + +@import "partials/layout"; +@import "partials/header"; +@import "partials/footer"; diff --git a/app/assets/stylesheets/partials/_footer.sass b/app/assets/stylesheets/partials/_footer.sass new file mode 100644 index 00000000..aca9849d --- /dev/null +++ b/app/assets/stylesheets/partials/_footer.sass @@ -0,0 +1,2 @@ +#footer + text-align: center diff --git a/app/assets/stylesheets/partials/_header.sass b/app/assets/stylesheets/partials/_header.sass new file mode 100644 index 00000000..ec8214d4 --- /dev/null +++ b/app/assets/stylesheets/partials/_header.sass @@ -0,0 +1,2 @@ +#header + text-align: center diff --git a/app/assets/stylesheets/partials/_layout.sass b/app/assets/stylesheets/partials/_layout.sass new file mode 100644 index 00000000..83a87811 --- /dev/null +++ b/app/assets/stylesheets/partials/_layout.sass @@ -0,0 +1,2 @@ +#container + +outer-container From cf72b204cd18f8f3b6a706c4701e701eb78f8f29 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Mon, 29 Apr 2013 22:56:56 +0300 Subject: [PATCH 009/499] Helpers and better style structure: --- app/assets/stylesheets/_utils.sass | 3 +++ app/assets/stylesheets/application.css.scss | 5 +---- app/assets/stylesheets/utils/_helpers.sass | 4 ++++ app/assets/stylesheets/utils/_placeholders.sass | 2 ++ 4 files changed, 10 insertions(+), 4 deletions(-) create mode 100644 app/assets/stylesheets/_utils.sass create mode 100644 app/assets/stylesheets/utils/_helpers.sass create mode 100644 app/assets/stylesheets/utils/_placeholders.sass diff --git a/app/assets/stylesheets/_utils.sass b/app/assets/stylesheets/_utils.sass new file mode 100644 index 00000000..539a8826 --- /dev/null +++ b/app/assets/stylesheets/_utils.sass @@ -0,0 +1,3 @@ +@import "utils/placeholders" +@import "utils/fonts" +@import "utils/helpers" diff --git a/app/assets/stylesheets/application.css.scss b/app/assets/stylesheets/application.css.scss index d4911a1e..1837efeb 100644 --- a/app/assets/stylesheets/application.css.scss +++ b/app/assets/stylesheets/application.css.scss @@ -1,11 +1,8 @@ @import "normalize"; @import "settings"; - @import "bourbon"; @import "neat"; - -@import "utils/fonts"; - +@import "utils"; @import "base"; @import "partials/layout"; diff --git a/app/assets/stylesheets/utils/_helpers.sass b/app/assets/stylesheets/utils/_helpers.sass new file mode 100644 index 00000000..3f8e255d --- /dev/null +++ b/app/assets/stylesheets/utils/_helpers.sass @@ -0,0 +1,4 @@ +=unlisted + list-style: none + margin: 0 + padding: 0 diff --git a/app/assets/stylesheets/utils/_placeholders.sass b/app/assets/stylesheets/utils/_placeholders.sass new file mode 100644 index 00000000..433c32f1 --- /dev/null +++ b/app/assets/stylesheets/utils/_placeholders.sass @@ -0,0 +1,2 @@ +%clearfix + +clearfix From 7a42c5bc484178507f5fe46da145ebcdb6dcddc2 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Mon, 29 Apr 2013 23:49:17 +0300 Subject: [PATCH 010/499] Preventing asset generation. --- config/application.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/config/application.rb b/config/application.rb index c2e0203c..3f1130f0 100644 --- a/config/application.rb +++ b/config/application.rb @@ -18,6 +18,9 @@ class Application < Rails::Application # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded. # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s] # config.i18n.default_locale = :de + + # don't generate any assets + config.generators.assets = false end end From 7aa46077c7a7bb66f7dd2e9791a8afaae89bcaa8 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Mon, 29 Apr 2013 23:52:55 +0300 Subject: [PATCH 011/499] Mobile first. --- app/assets/stylesheets/_settings.sass | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/app/assets/stylesheets/_settings.sass b/app/assets/stylesheets/_settings.sass index 1d4c9a17..aeeecd56 100644 --- a/app/assets/stylesheets/_settings.sass +++ b/app/assets/stylesheets/_settings.sass @@ -8,17 +8,17 @@ $column: 225px $gutter: 0px $padding: 20px $grid-columns: 8 -$max-width: em(900) +$min-width: em(900) // breakpoints -$low-end: max-width 160px 1 -$mid-range: max-width 240px 1 -$smartphone-portrait: max-width 320px 2 -$smartphone-landscape: max-width 480px 2 -$tablet-lite: max-width 640px 2 -$tablet-lite-plus: max-width 750px 4 -$tablet-portrait: max-width 768px 4 -$tablet-portrait-plus: max-width 860px 4 +$low-end: min-width 160px 1 +$mid-range: min-width 240px 1 +$smartphone-portrait: min-width 320px 2 +$smartphone-landscape: min-width 480px 2 +$tablet-lite: min-width 640px 2 +$tablet-lite-plus: min-width 750px 4 +$tablet-portrait: min-width 768px 4 +$tablet-portrait-plus: min-width 860px 4 // colors $red: #e55924 From 4bf137c6bba3acb78f3ea0f2de5d065d9589585e Mon Sep 17 00:00:00 2001 From: Polarblau Date: Mon, 29 Apr 2013 23:53:24 +0300 Subject: [PATCH 012/499] Animate links. --- app/assets/stylesheets/_base.sass | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/assets/stylesheets/_base.sass b/app/assets/stylesheets/_base.sass index f1d71e3c..7592b266 100644 --- a/app/assets/stylesheets/_base.sass +++ b/app/assets/stylesheets/_base.sass @@ -8,6 +8,8 @@ a, a:visited color: $blue text-decoration: none border-bottom: 1px dotted $blue + +transition(all 0.25s) a:hover border: none + +transition(all 0.25s) From a266a062bf935ba74988de9d47b6975ba93b8149 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Mon, 29 Apr 2013 23:56:24 +0300 Subject: [PATCH 013/499] Basic header and footer nav styles. --- app/assets/stylesheets/_utils.sass | 4 +- app/assets/stylesheets/partials/_footer.sass | 5 ++ app/assets/stylesheets/partials/_header.sass | 5 ++ app/assets/stylesheets/partials/_layout.sass | 2 + .../stylesheets/utils/_placeholders.sass | 50 +++++++++++++++++++ 5 files changed, 64 insertions(+), 2 deletions(-) diff --git a/app/assets/stylesheets/_utils.sass b/app/assets/stylesheets/_utils.sass index 539a8826..011c64c7 100644 --- a/app/assets/stylesheets/_utils.sass +++ b/app/assets/stylesheets/_utils.sass @@ -1,3 +1,3 @@ -@import "utils/placeholders" -@import "utils/fonts" @import "utils/helpers" +@import "utils/fonts" +@import "utils/placeholders" diff --git a/app/assets/stylesheets/partials/_footer.sass b/app/assets/stylesheets/partials/_footer.sass index aca9849d..4834a7e0 100644 --- a/app/assets/stylesheets/partials/_footer.sass +++ b/app/assets/stylesheets/partials/_footer.sass @@ -1,2 +1,7 @@ #footer text-align: center + @extend %ornamental-divider-before + + nav + ul + @extend %inline-nav-list diff --git a/app/assets/stylesheets/partials/_header.sass b/app/assets/stylesheets/partials/_header.sass index ec8214d4..903ceb5a 100644 --- a/app/assets/stylesheets/partials/_header.sass +++ b/app/assets/stylesheets/partials/_header.sass @@ -1,2 +1,7 @@ #header text-align: center + padding: 0.5em 0 + + nav.user-nav + ul + @extend %inline-nav-list diff --git a/app/assets/stylesheets/partials/_layout.sass b/app/assets/stylesheets/partials/_layout.sass index 83a87811..962bae43 100644 --- a/app/assets/stylesheets/partials/_layout.sass +++ b/app/assets/stylesheets/partials/_layout.sass @@ -1,2 +1,4 @@ #container +outer-container + padding: 0 $padding + position: relative diff --git a/app/assets/stylesheets/utils/_placeholders.sass b/app/assets/stylesheets/utils/_placeholders.sass index 433c32f1..0d197bb8 100644 --- a/app/assets/stylesheets/utils/_placeholders.sass +++ b/app/assets/stylesheets/utils/_placeholders.sass @@ -1,2 +1,52 @@ %clearfix +clearfix + +%smaller-text + font-size: 0.825em + +=ornamental-divider + content: "." + text-indent: -999em + display: block + margin: 0.5em auto 1.5em + width: 100% + height: 1px + border-bottom: 1px solid $light-gray + + +media($mid-range) + width: 200px + + +media($smartphone-portrait) + width: 280px + +%ornamental-divider-after + &:after + +ornamental-divider + +%ornamental-divider-before + &:before + +ornamental-divider + +%inline-nav-list + +unlisted + + li + display: inline-block + @extend %smaller-text + + &:after + content: "•" + color: $light-gray + padding: 0 1em + + &:last-child:after + content: none + + a + color: $light-gray + border: none + text-transform: uppercase + +custom-sans(bold) + + &:hover + color: $blue From e33db19033dba9f4135e7e08facc7e08377724bb Mon Sep 17 00:00:00 2001 From: Polarblau Date: Tue, 30 Apr 2013 00:15:56 +0300 Subject: [PATCH 014/499] Adding logo. --- app/assets/images/header/logo.png | Bin 0 -> 11379 bytes app/assets/images/header/logo@2x.png | Bin 0 -> 22950 bytes app/assets/images/header/logo_small.png | Bin 0 -> 5215 bytes app/assets/images/header/logo_small@2x.png | Bin 0 -> 9954 bytes app/assets/stylesheets/partials/_header.sass | 38 +++++++++++++++++++ app/views/partials/_header.html.haml | 5 +++ 6 files changed, 43 insertions(+) create mode 100644 app/assets/images/header/logo.png create mode 100644 app/assets/images/header/logo@2x.png create mode 100644 app/assets/images/header/logo_small.png create mode 100644 app/assets/images/header/logo_small@2x.png diff --git a/app/assets/images/header/logo.png b/app/assets/images/header/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..b1a4056584defdc3594832c26172882cebe19d0f GIT binary patch literal 11379 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 literal 0 HcmV?d00001 diff --git a/app/assets/images/header/logo_small.png b/app/assets/images/header/logo_small.png new file mode 100644 index 0000000000000000000000000000000000000000..39c11cd3530432c6f5e416eecdc860d6172e6e5d GIT binary patch literal 5215 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 dbC literal 0 HcmV?d00001 diff --git a/app/assets/stylesheets/partials/_header.sass b/app/assets/stylesheets/partials/_header.sass index 903ceb5a..28ef84ef 100644 --- a/app/assets/stylesheets/partials/_header.sass +++ b/app/assets/stylesheets/partials/_header.sass @@ -2,6 +2,44 @@ text-align: center padding: 0.5em 0 + h1 + clear: both + + a + border: none + nav.user-nav ul @extend %inline-nav-list + + + +media($smartphone-portrait) + nav.user-nav + float: right + + h1 + padding: 1em 0 + + a + width: 262px + height: 192px + display: inline-block + text-indent: -999em + + background: image-url("header/logo.png") no-repeat + + +hidpi + background-size: 262px 192px + background-image: image-url("header/logo@2x.png") + + img + display: none + + + +media($smartphone-landscape) + nav.user-nav + float: right + + h1 + padding: 2em 0 + diff --git a/app/views/partials/_header.html.haml b/app/views/partials/_header.html.haml index 4bab0ed2..929bea20 100644 --- a/app/views/partials/_header.html.haml +++ b/app/views/partials/_header.html.haml @@ -9,3 +9,8 @@ %li %a(href="#" title="#{t("user_nav.logout.title")}") = t("user_nav.logout.label") + + -# the logo, link home + %h1 + = link_to "/" do + = image_tag "header/logo_small.png", :alt => "Eurucamp Activities" From ccfc2d31788dfaec8362366ccf7751276b6933e7 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Tue, 30 Apr 2013 00:36:54 +0300 Subject: [PATCH 015/499] Medium font size. --- app/assets/stylesheets/utils/_fonts.sass | 2 ++ app/assets/stylesheets/utils/_placeholders.sass | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/app/assets/stylesheets/utils/_fonts.sass b/app/assets/stylesheets/utils/_fonts.sass index c13e1368..55126de4 100644 --- a/app/assets/stylesheets/utils/_fonts.sass +++ b/app/assets/stylesheets/utils/_fonts.sass @@ -9,6 +9,8 @@ $weights: bold bold, medium normal, normal normal font-weight: 100 @else if $weight == light font-weight: 300 + @else if $weight == medium + font-weight: 500 @else if $weight == bold font-weight: 600 @else if $weight == black diff --git a/app/assets/stylesheets/utils/_placeholders.sass b/app/assets/stylesheets/utils/_placeholders.sass index 0d197bb8..20966d76 100644 --- a/app/assets/stylesheets/utils/_placeholders.sass +++ b/app/assets/stylesheets/utils/_placeholders.sass @@ -46,7 +46,7 @@ color: $light-gray border: none text-transform: uppercase - +custom-sans(bold) + +custom-sans(medium) &:hover color: $blue From bb0f525b5951b0747a4b110242736746428f7098 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Tue, 30 Apr 2013 08:40:04 +0300 Subject: [PATCH 016/499] Adding additional informatio to header. --- app/views/layouts/application.html.haml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml index e61316cb..04c6c29e 100644 --- a/app/views/layouts/application.html.haml +++ b/app/views/layouts/application.html.haml @@ -1,7 +1,13 @@ !!! -- html_tag :class => "no-js" do +- html_tag :lang => 'en', :class => "no-js" do %head + %meta(charset="utf-8") + %meta(content="IE=edge,chrome=1" http-equiv="X-UA-Compatible") %title= title("Eurucamp Activities") + %meta(content="" name="description") + %meta(content="" name="author") + %meta(content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0" name="viewport") + = stylesheet_link_tag "application" = javascript_include_tag "modernizr.min" From 7cab8d3dcec93050fe423db9cbf7397671920dce Mon Sep 17 00:00:00 2001 From: Polarblau Date: Tue, 30 Apr 2013 08:43:52 +0300 Subject: [PATCH 017/499] Fixing important typo. --- config/routes.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/routes.rb b/config/routes.rb index f77f07f1..175bf55a 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,5 +1,5 @@ Activities::Application.routes.draw do - resource :activities + resources :activities root to: 'activities#index' end From 9bad6f768f9903abb2c56e8bbe79851c338e6184 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Tue, 30 Apr 2013 09:19:25 +0300 Subject: [PATCH 018/499] Adding filters. --- app/assets/stylesheets/_settings.sass | 3 + app/assets/stylesheets/application.css.scss | 2 + .../stylesheets/partials/_activities.sass | 1 + .../partials/activities/_filters.sass | 74 +++++++++++++++++++ app/views/activities/index.html.haml | 27 +++++++ 5 files changed, 107 insertions(+) create mode 100644 app/assets/stylesheets/partials/_activities.sass create mode 100644 app/assets/stylesheets/partials/activities/_filters.sass diff --git a/app/assets/stylesheets/_settings.sass b/app/assets/stylesheets/_settings.sass index aeeecd56..2b722729 100644 --- a/app/assets/stylesheets/_settings.sass +++ b/app/assets/stylesheets/_settings.sass @@ -26,8 +26,11 @@ $blue: #0ca9c4 $gray: #5b5e61 $green: #15d701 +$yellow: #fff25e $black: #222222 +$darker-gray: #797979 +$medium-gray: #8a8a8a $white: #ffffff $light-gray: #e2e2e2 $extra-light-gray: #f7f7f7 diff --git a/app/assets/stylesheets/application.css.scss b/app/assets/stylesheets/application.css.scss index 1837efeb..54862c4a 100644 --- a/app/assets/stylesheets/application.css.scss +++ b/app/assets/stylesheets/application.css.scss @@ -8,3 +8,5 @@ @import "partials/layout"; @import "partials/header"; @import "partials/footer"; + +@import "partials/activities"; diff --git a/app/assets/stylesheets/partials/_activities.sass b/app/assets/stylesheets/partials/_activities.sass new file mode 100644 index 00000000..0ce0b053 --- /dev/null +++ b/app/assets/stylesheets/partials/_activities.sass @@ -0,0 +1 @@ +@import "activities/filters" diff --git a/app/assets/stylesheets/partials/activities/_filters.sass b/app/assets/stylesheets/partials/activities/_filters.sass new file mode 100644 index 00000000..f196c863 --- /dev/null +++ b/app/assets/stylesheets/partials/activities/_filters.sass @@ -0,0 +1,74 @@ +form.filters + @extend %clearfix + + label + background: $light-gray + @extend %smaller-text + color: $gray + text-transform: uppercase + +custom-sans(medium) + border: none + display: block + overflow: hidden + + span + padding: 0.75em + display: block + float: left + + strong + +custom-sans(bold) + display: block + float: right + padding: 0.75em + + &:hover + color: $white + background: $blue + cursor: pointer + + &.by-me + background: $yellow + + &:hover + background: $blue + + &.selected + color: $white + background: $medium-gray + + strong + background: $darker-gray + + input + margin: 0 0.75em 0 0 + + button + background: $medium-gray + border-radius: 3px + color: $white + border: none + margin: 1em auto + padding: 0.45em 2em + display: block + + button, input + html.js & + display: none + + +media($tablet-lite) + label + float: left + margin-right: 1px + + &:last-of-type + border-radius: 0 3px 3px 0 + margin: 0 + + &:first-child + border-radius: 3px 0 0 3px + + button + margin: 0 0 0 0.5em + padding: 0.45em 0.75em + display: inline-block diff --git a/app/views/activities/index.html.haml b/app/views/activities/index.html.haml index 5a1284f3..793697b2 100644 --- a/app/views/activities/index.html.haml +++ b/app/views/activities/index.html.haml @@ -1 +1,28 @@ +%form.filters(action=activities_path method="GET") + %label> + %span + %input(type="radio" name="filter" value="today") + Today + %strong + 5 + %label.selected> + %span + %input(type="radio" name="filter" value="all") + All + %strong + 267 + %label> + %span + %input(type="radio" name="filter" value="mine") + My activities + %strong + 2 + %label.by-me> + %span + %input(type="radio" name="filter" value="by_me") + Created by me + %strong + 0 + %button(type="submit") Filter + Activities! From dd7ed04ca64907e658d2de402b9d88454a0ee844 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Tue, 30 Apr 2013 09:38:41 +0300 Subject: [PATCH 019/499] Simplifying filters. --- .../partials/activities/_filters.sass | 35 +++++++++---------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/app/assets/stylesheets/partials/activities/_filters.sass b/app/assets/stylesheets/partials/activities/_filters.sass index f196c863..19e075a3 100644 --- a/app/assets/stylesheets/partials/activities/_filters.sass +++ b/app/assets/stylesheets/partials/activities/_filters.sass @@ -9,37 +9,24 @@ form.filters +custom-sans(medium) border: none display: block - overflow: hidden - - span - padding: 0.75em - display: block - float: left + padding: 0.75em 1em + +transition(all 0.5s) strong - +custom-sans(bold) - display: block + +custom-sans(light) float: right - padding: 0.75em + margin: 0 0 0 0.75em &:hover color: $white background: $blue cursor: pointer - - &.by-me - background: $yellow - - &:hover - background: $blue + +transition(all 0.15s) &.selected color: $white background: $medium-gray - strong - background: $darker-gray - input margin: 0 0.75em 0 0 @@ -51,6 +38,13 @@ form.filters margin: 1em auto padding: 0.45em 2em display: block + +transition(all 0.15s) + + &:hover + +transition(all 0.15s) + color: $white + background: $blue + button, input html.js & @@ -58,9 +52,12 @@ form.filters +media($tablet-lite) label - float: left + display: inline-block margin-right: 1px + strong + float: none + &:last-of-type border-radius: 0 3px 3px 0 margin: 0 From fa87dac1e6aef404a70bc781d8e517d8c1b392ae Mon Sep 17 00:00:00 2001 From: Polarblau Date: Tue, 30 Apr 2013 09:42:48 +0300 Subject: [PATCH 020/499] Round corners. --- app/assets/stylesheets/partials/activities/_filters.sass | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/assets/stylesheets/partials/activities/_filters.sass b/app/assets/stylesheets/partials/activities/_filters.sass index 19e075a3..fccf04ba 100644 --- a/app/assets/stylesheets/partials/activities/_filters.sass +++ b/app/assets/stylesheets/partials/activities/_filters.sass @@ -1,5 +1,7 @@ form.filters @extend %clearfix + border-radius: 3px + overflow: hidden label background: $light-gray @@ -51,6 +53,9 @@ form.filters display: none +media($tablet-lite) + border-radius: none + overflow: auto + label display: inline-block margin-right: 1px From b8943848d90d0e570345bb6f9027b62ed965fe26 Mon Sep 17 00:00:00 2001 From: pietia Date: Tue, 30 Apr 2013 16:19:27 +0200 Subject: [PATCH 021/499] updated README --- .env | 0 .gitignore | 1 + README.md | 30 ++++++++++++++++++++++++++++-- 3 files changed, 29 insertions(+), 2 deletions(-) delete mode 100644 .env diff --git a/.env b/.env deleted file mode 100644 index e69de29b..00000000 diff --git a/.gitignore b/.gitignore index 42c035fe..d037bedb 100644 --- a/.gitignore +++ b/.gitignore @@ -16,4 +16,5 @@ rerun.txt pickle-email-*.html config/database.yml +.env diff --git a/README.md b/README.md index c220f221..eb540a8d 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,28 @@ -eurucamp-activities-2013 -======================== \ No newline at end of file +# Eurucamp-activities-2013 + +## Development + +* `cp .env.sample .env` +* `cp config/database.yml.sample config/database.yml` +* update config files: `config/application.yml`, `.env` (see ENV variables listed below) +* run migration scripts +* `bundle exec foreman start` +* `tail -f log/development.log` + +## Deployment + +How to deploy app? + +* `bundle exec rake staging deploy` +* `bundle exec rake production deploy` + +**ENV** variables: + +* `DISABLE_ROBOTS` - set to `true` if you want to block robots from tracking content (via `robots.txt`) +* `GITHUB_KEY` +* `GITHUB_SECRET` +* `TWITTER_KEY` +* `TWITTER_SECRET` + + + From 06200b8121d7912780d00d401914e0c7aad73962 Mon Sep 17 00:00:00 2001 From: pietia Date: Tue, 30 Apr 2013 16:23:13 +0200 Subject: [PATCH 022/499] some formatting --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index eb540a8d..0a1ebf63 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,8 @@ ## Development +### Basic setup + * `cp .env.sample .env` * `cp config/database.yml.sample config/database.yml` * update config files: `config/application.yml`, `.env` (see ENV variables listed below) @@ -11,12 +13,12 @@ ## Deployment -How to deploy app? +### How to deploy app? * `bundle exec rake staging deploy` * `bundle exec rake production deploy` -**ENV** variables: +### **ENV** variables: * `DISABLE_ROBOTS` - set to `true` if you want to block robots from tracking content (via `robots.txt`) * `GITHUB_KEY` From ab0c07be5d228d1e1b60bcbb2e87c05319b8b587 Mon Sep 17 00:00:00 2001 From: pietia Date: Tue, 30 Apr 2013 16:55:15 +0200 Subject: [PATCH 023/499] upgraded rails to 4.0.0.rc1 --- Gemfile | 2 +- Gemfile.lock | 58 +++++++++++++--------------- config/initializers/session_store.rb | 2 +- 3 files changed, 29 insertions(+), 33 deletions(-) diff --git a/Gemfile b/Gemfile index 8a9951ea..2b29953d 100644 --- a/Gemfile +++ b/Gemfile @@ -2,7 +2,7 @@ source 'https://rubygems.org' ruby "2.0.0" -gem 'rails', '4.0.0.beta1' +gem 'rails', '4.0.0.rc1' gem 'pg' gem 'unicorn' gem 'settingslogic' diff --git a/Gemfile.lock b/Gemfile.lock index 194fee0a..8467e0db 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -9,30 +9,30 @@ GIT GEM remote: https://rubygems.org/ specs: - actionmailer (4.0.0.beta1) - actionpack (= 4.0.0.beta1) + actionmailer (4.0.0.rc1) + actionpack (= 4.0.0.rc1) mail (~> 2.5.3) - actionpack (4.0.0.beta1) - activesupport (= 4.0.0.beta1) + actionpack (4.0.0.rc1) + activesupport (= 4.0.0.rc1) builder (~> 3.1.0) erubis (~> 2.7.0) rack (~> 1.5.2) rack-test (~> 0.6.2) - activemodel (4.0.0.beta1) - activesupport (= 4.0.0.beta1) + activemodel (4.0.0.rc1) + activesupport (= 4.0.0.rc1) builder (~> 3.1.0) - activerecord (4.0.0.beta1) - activemodel (= 4.0.0.beta1) - activerecord-deprecated_finders (~> 0.0.3) - activesupport (= 4.0.0.beta1) - arel (~> 4.0.0.beta1) - activerecord-deprecated_finders (0.0.3) - activesupport (4.0.0.beta1) - i18n (~> 0.6.2) + activerecord (4.0.0.rc1) + activemodel (= 4.0.0.rc1) + activerecord-deprecated_finders (~> 1.0.2) + activesupport (= 4.0.0.rc1) + arel (~> 4.0.0) + activerecord-deprecated_finders (1.0.2) + activesupport (4.0.0.rc1) + i18n (~> 0.6, >= 0.6.4) minitest (~> 4.2) multi_json (~> 1.3) thread_safe (~> 0.1) - tzinfo (~> 0.3.33) + tzinfo (~> 0.3.37) addressable (2.3.4) ansi (1.4.3) arel (4.0.0) @@ -97,7 +97,6 @@ GEM jquery-rails (2.2.1) railties (>= 3.0, < 5.0) thor (>= 0.14, < 2.0) - json (1.7.7) kgio (2.8.0) launchy (2.3.0) addressable (~> 2.3) @@ -120,24 +119,21 @@ GEM rack rack-test (0.6.2) rack (>= 1.0) - rails (4.0.0.beta1) - actionmailer (= 4.0.0.beta1) - actionpack (= 4.0.0.beta1) - activerecord (= 4.0.0.beta1) - activesupport (= 4.0.0.beta1) + rails (4.0.0.rc1) + actionmailer (= 4.0.0.rc1) + actionpack (= 4.0.0.rc1) + activerecord (= 4.0.0.rc1) + activesupport (= 4.0.0.rc1) bundler (>= 1.3.0, < 2.0) - railties (= 4.0.0.beta1) - sprockets-rails (~> 2.0.0.rc3) - railties (4.0.0.beta1) - actionpack (= 4.0.0.beta1) - activesupport (= 4.0.0.beta1) + railties (= 4.0.0.rc1) + sprockets-rails (~> 2.0.0.rc4) + railties (4.0.0.rc1) + actionpack (= 4.0.0.rc1) + activesupport (= 4.0.0.rc1) rake (>= 0.8.7) - rdoc (~> 3.4) - thor (>= 0.17.0, < 2.0) + thor (>= 0.18.1, < 2.0) raindrops (0.11.0) rake (10.0.4) - rdoc (3.12.2) - json (~> 1.4) rest-client (1.6.7) mime-types (>= 1.16) rubyzip (0.9.9) @@ -200,7 +196,7 @@ DEPENDENCIES newrelic_rpm pg rack-robotz (~> 0.0.3) - rails (= 4.0.0.beta1) + rails (= 4.0.0.rc1) sass-rails (~> 4.0.0.beta1) settingslogic simplecov diff --git a/config/initializers/session_store.rb b/config/initializers/session_store.rb index 034b4039..ec789fa6 100644 --- a/config/initializers/session_store.rb +++ b/config/initializers/session_store.rb @@ -1,3 +1,3 @@ # Be sure to restart your server when you modify this file. -Activities::Application.config.session_store :encrypted_cookie_store, key: '_activities_session' +Activities::Application.config.session_store :cookie_store, key: '_activities_session' From 3c86729afbcfca7208829cdd20a8d92ffc606751 Mon Sep 17 00:00:00 2001 From: pietia Date: Tue, 30 Apr 2013 17:05:07 +0200 Subject: [PATCH 024/499] added rspec-rails --- Gemfile | 4 +++ Gemfile.lock | 13 ++++++++++ config/application.rb | 2 +- {app/models => lib}/settings.rb | 0 spec/spec_helper.rb | 44 +++++++++++++++++++++++++++++++++ test/controllers/.keep | 0 test/fixtures/.keep | 0 test/helpers/.keep | 0 test/integration/.keep | 0 test/mailers/.keep | 0 test/models/.keep | 0 test/test_helper.rb | 17 ------------- 12 files changed, 62 insertions(+), 18 deletions(-) rename {app/models => lib}/settings.rb (100%) create mode 100644 spec/spec_helper.rb delete mode 100644 test/controllers/.keep delete mode 100644 test/fixtures/.keep delete mode 100644 test/helpers/.keep delete mode 100644 test/integration/.keep delete mode 100644 test/mailers/.keep delete mode 100644 test/models/.keep delete mode 100644 test/test_helper.rb diff --git a/Gemfile b/Gemfile index 2b29953d..ac004949 100644 --- a/Gemfile +++ b/Gemfile @@ -27,6 +27,10 @@ group :development do gem 'binding_of_caller' end +group :test, :development do + gem "rspec-rails", "~> 2.0" +end + group :test do gem 'turn', require: false gem 'simplecov', require: false diff --git a/Gemfile.lock b/Gemfile.lock index 8467e0db..abcac009 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -62,6 +62,7 @@ GEM debugger-ruby_core_source (~> 1.2.0) debugger-linecache (1.2.0) debugger-ruby_core_source (1.2.0) + diff-lcs (1.2.4) dotenv (0.7.0) erubis (2.7.0) excon (0.20.1) @@ -136,6 +137,17 @@ GEM rake (10.0.4) rest-client (1.6.7) mime-types (>= 1.16) + rspec-core (2.13.1) + rspec-expectations (2.13.0) + diff-lcs (>= 1.1.3, < 2.0) + rspec-mocks (2.13.1) + rspec-rails (2.13.1) + actionpack (>= 3.0) + activesupport (>= 3.0) + railties (>= 3.0) + rspec-core (~> 2.13.0) + rspec-expectations (~> 2.13.0) + rspec-mocks (~> 2.13.0) rubyzip (0.9.9) sass (3.2.8) sass-rails (4.0.0.rc1) @@ -197,6 +209,7 @@ DEPENDENCIES pg rack-robotz (~> 0.0.3) rails (= 4.0.0.rc1) + rspec-rails (~> 2.0) sass-rails (~> 4.0.0.beta1) settingslogic simplecov diff --git a/config/application.rb b/config/application.rb index 3f1130f0..fbddfd10 100644 --- a/config/application.rb +++ b/config/application.rb @@ -24,4 +24,4 @@ class Application < Rails::Application end end -require Rails.root + "app/models/settings" +require Rails.root + "lib/settings" diff --git a/app/models/settings.rb b/lib/settings.rb similarity index 100% rename from app/models/settings.rb rename to lib/settings.rb diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb new file mode 100644 index 00000000..8691a77a --- /dev/null +++ b/spec/spec_helper.rb @@ -0,0 +1,44 @@ +# This file is copied to spec/ when you run 'rails generate rspec:install' +ENV["RAILS_ENV"] ||= 'test' +require 'simplecov' +SimpleCov.start 'rails' +require File.expand_path("../../config/environment", __FILE__) +require 'rspec/rails' +require 'rspec/autorun' + +# Requires supporting ruby files with custom matchers and macros, etc, +# in spec/support/ and its subdirectories. +Dir[Rails.root.join("spec/support/**/*.rb")].each { |f| require f } + +# Checks for pending migrations before tests are run. +# If you are not using ActiveRecord, you can remove this line. +ActiveRecord::Migration.check_pending! if defined?(ActiveRecord::Migration) + +RSpec.configure do |config| + # ## Mock Framework + # + # If you prefer to use mocha, flexmock or RR, uncomment the appropriate line: + # + # config.mock_with :mocha + # config.mock_with :flexmock + # config.mock_with :rr + + # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures + config.fixture_path = "#{::Rails.root}/spec/fixtures" + + # If you're not using ActiveRecord, or you'd prefer not to run each of your + # examples within a transaction, remove the following line or assign false + # instead of true. + config.use_transactional_fixtures = true + + # If true, the base class of anonymous controllers will be inferred + # automatically. This will be the default behavior in future versions of + # rspec-rails. + config.infer_base_class_for_anonymous_controllers = false + + # Run specs in random order to surface order dependencies. If you find an + # order dependency and want to debug it, you can fix the order by providing + # the seed, which is printed after each run. + # --seed 1234 + config.order = "random" +end diff --git a/test/controllers/.keep b/test/controllers/.keep deleted file mode 100644 index e69de29b..00000000 diff --git a/test/fixtures/.keep b/test/fixtures/.keep deleted file mode 100644 index e69de29b..00000000 diff --git a/test/helpers/.keep b/test/helpers/.keep deleted file mode 100644 index e69de29b..00000000 diff --git a/test/integration/.keep b/test/integration/.keep deleted file mode 100644 index e69de29b..00000000 diff --git a/test/mailers/.keep b/test/mailers/.keep deleted file mode 100644 index e69de29b..00000000 diff --git a/test/models/.keep b/test/models/.keep deleted file mode 100644 index e69de29b..00000000 diff --git a/test/test_helper.rb b/test/test_helper.rb deleted file mode 100644 index 990fea40..00000000 --- a/test/test_helper.rb +++ /dev/null @@ -1,17 +0,0 @@ -ENV["RAILS_ENV"] = "test" -require 'simplecov' -SimpleCov.start 'rails' -require File.expand_path('../../config/environment', __FILE__) -require 'rails/test_help' - -class ActiveSupport::TestCase - ActiveRecord::Migration.check_pending! - - # Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order. - # - # Note: You'll currently still have to declare fixtures explicitly in integration tests - # -- they do not yet inherit this setting - fixtures :all - - # Add more helper methods to be used by all tests here... -end From d2ef2632da771b6e2bdbe561f699900a2bb52354 Mon Sep 17 00:00:00 2001 From: pietia Date: Tue, 30 Apr 2013 17:31:24 +0200 Subject: [PATCH 025/499] added capyabra --- Gemfile | 10 +++++----- Gemfile.lock | 19 +++++++++++++++---- spec/features/.keep | 0 spec/spec_helper.rb | 12 +++++++++++- spec/support/steps/.keep | 0 5 files changed, 31 insertions(+), 10 deletions(-) create mode 100644 spec/features/.keep create mode 100644 spec/support/steps/.keep diff --git a/Gemfile b/Gemfile index ac004949..fd01e1ed 100644 --- a/Gemfile +++ b/Gemfile @@ -28,22 +28,22 @@ group :development do end group :test, :development do - gem "rspec-rails", "~> 2.0" + gem "rspec-rails", '~> 2.0' + gem 'capybara', '~> 2.1' + gem 'capybara-webkit', '~> 0.14' end group :test do - gem 'turn', require: false gem 'simplecov', require: false end group :assets do gem 'sass-rails', '~> 4.0.0.beta1' gem 'coffee-rails', '~> 4.0.0.beta1' - gem 'uglifier', '>= 1.0.3' + gem 'uglifier', '>= 1.0.3' end group :production, :staging do - # git version for rails-4.x compatibility gem 'exception_notification', require: 'exception_notifier', git: 'git://github.com/smartinez87/exception_notification.git' - gem 'rack-robotz', '~> 0.0.3' + gem 'rack-robotz', '~> 0.0.3' end diff --git a/Gemfile.lock b/Gemfile.lock index abcac009..8f78071d 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -34,7 +34,6 @@ GEM thread_safe (~> 0.1) tzinfo (~> 0.3.37) addressable (2.3.4) - ansi (1.4.3) arel (4.0.0) atomic (1.1.8) better_errors (0.8.0) @@ -46,6 +45,15 @@ GEM sass (>= 3.2.0) thor builder (3.1.4) + capybara (2.1.0) + mime-types (>= 1.16) + nokogiri (>= 1.3.3) + rack (>= 1.0.0) + rack-test (>= 0.5.4) + xpath (~> 2.0) + capybara-webkit (0.14.2) + capybara (~> 2.0, >= 2.0.2) + json coderay (1.0.9) coffee-rails (4.0.0) coffee-script (>= 2.2.0) @@ -98,6 +106,7 @@ GEM jquery-rails (2.2.1) railties (>= 3.0, < 5.0) thor (>= 0.14, < 2.0) + json (1.7.7) kgio (2.8.0) launchy (2.3.0) addressable (~> 2.3) @@ -113,6 +122,7 @@ GEM sass (>= 3.2) netrc (0.7.7) newrelic_rpm (3.6.1.87) + nokogiri (1.5.9) pg (0.15.1) polyglot (0.3.3) rack (1.5.2) @@ -178,8 +188,6 @@ GEM polyglot (>= 0.3.1) turbolinks (1.1.1) coffee-rails - turn (0.9.6) - ansi tzinfo (0.3.37) uglifier (2.0.1) execjs (>= 0.3.0) @@ -188,6 +196,8 @@ GEM kgio (~> 2.6) rack raindrops (~> 0.7) + xpath (2.0.0) + nokogiri (~> 1.3) PLATFORMS ruby @@ -196,6 +206,8 @@ DEPENDENCIES better_errors binding_of_caller bourbon + capybara (~> 2.1) + capybara-webkit (~> 0.14) coffee-rails (~> 4.0.0.beta1) debugger (~> 1.5) exception_notification! @@ -214,6 +226,5 @@ DEPENDENCIES settingslogic simplecov turbolinks - turn uglifier (>= 1.0.3) unicorn diff --git a/spec/features/.keep b/spec/features/.keep new file mode 100644 index 00000000..e69de29b diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 8691a77a..96469152 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -5,6 +5,16 @@ require File.expand_path("../../config/environment", __FILE__) require 'rspec/rails' require 'rspec/autorun' +require 'capybara/rails' + +Capybara.configure do |config| + config.default_driver = :webkit + config.default_wait_time = 5 + # config.match = :one + # config.exact_options = true + # config.ignore_hidden_elements = true + # config.visible_text_only = true +end # Requires supporting ruby files with custom matchers and macros, etc, # in spec/support/ and its subdirectories. @@ -21,7 +31,7 @@ # # config.mock_with :mocha # config.mock_with :flexmock - # config.mock_with :rr + config.mock_with :rspec # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures config.fixture_path = "#{::Rails.root}/spec/fixtures" diff --git a/spec/support/steps/.keep b/spec/support/steps/.keep new file mode 100644 index 00000000..e69de29b From 68b8e873f17b497669cac1f0833832239db9542a Mon Sep 17 00:00:00 2001 From: pietia Date: Tue, 30 Apr 2013 19:29:05 +0200 Subject: [PATCH 026/499] added devise, omniauth gems, configured rspec --- Gemfile | 34 ++-- Gemfile.lock | 56 +++++++ config/application.rb | 2 + config/application.yml | 3 + config/environments/development.rb | 2 + config/environments/production.rb | 2 + config/environments/staging.rb | 2 + config/environments/test.rb | 2 + config/initializers/devise.rb | 242 +++++++++++++++++++++++++++++ config/initializers/omni_auth.rb | 4 + config/locales/devise.en.yml | 59 +++++++ spec/lib/settings_spec.rb | 8 + spec/other/factories_spec.rb | 10 ++ spec/spec_helper.rb | 25 +++ 14 files changed, 435 insertions(+), 16 deletions(-) create mode 100644 config/initializers/devise.rb create mode 100644 config/initializers/omni_auth.rb create mode 100644 config/locales/devise.en.yml create mode 100644 spec/lib/settings_spec.rb create mode 100644 spec/other/factories_spec.rb diff --git a/Gemfile b/Gemfile index fd01e1ed..fbb35d84 100644 --- a/Gemfile +++ b/Gemfile @@ -2,48 +2,50 @@ source 'https://rubygems.org' ruby "2.0.0" -gem 'rails', '4.0.0.rc1' +gem 'rails', '4.0.0.rc1' gem 'pg' gem 'unicorn' gem 'settingslogic' gem 'newrelic_rpm' +gem 'devise', git: 'git://github.com/plataformatec/devise.git', branch: 'rails4' +gem 'omniauth' +gem 'omniauth-github' +gem 'omniauth-twitter' +gem 'simple_form' gem 'jquery-rails' -# Turbolinks makes following links in your web application faster. Read more: https://github.com/rails/turbolinks gem 'turbolinks' -# Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder -gem 'jbuilder', '~> 1.0.1' -# To use ActiveModel has_secure_password -# gem 'bcrypt-ruby', '~> 3.0.0' +gem 'jbuilder', '~> 1.0.1' gem 'bourbon' gem 'neat' gem 'haml-rails' group :development do - gem 'debugger', '~> 1.5' - gem 'heroku_san', '~> 3.0.2' + gem 'debugger', '~> 1.5' + gem 'heroku_san', '~> 3.0.2' gem 'foreman' gem 'better_errors' gem 'binding_of_caller' end group :test, :development do - gem "rspec-rails", '~> 2.0' - gem 'capybara', '~> 2.1' - gem 'capybara-webkit', '~> 0.14' + gem "rspec-rails", '~> 2.0' + gem 'factory_girl_rails','~> 4.2' end group :test do - gem 'simplecov', require: false + gem 'simplecov', require: false + gem 'capybara', '~> 2.1' + gem 'capybara-webkit', '~> 0.14' end group :assets do - gem 'sass-rails', '~> 4.0.0.beta1' - gem 'coffee-rails', '~> 4.0.0.beta1' - gem 'uglifier', '>= 1.0.3' + gem 'sass-rails', '~> 4.0.0.beta1' + gem 'coffee-rails', '~> 4.0.0.beta1' + gem 'uglifier', '>= 1.0.3' end group :production, :staging do gem 'exception_notification', require: 'exception_notifier', git: 'git://github.com/smartinez87/exception_notification.git' - gem 'rack-robotz', '~> 0.0.3' + gem 'rack-robotz', '~> 0.0.3' end diff --git a/Gemfile.lock b/Gemfile.lock index 8f78071d..224c788e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,3 +1,14 @@ +GIT + remote: git://github.com/plataformatec/devise.git + revision: c6189696772925ae0c608fdd8d535f735e8e114e + branch: rails4 + specs: + devise (2.2.3) + bcrypt-ruby (~> 3.0) + orm_adapter (~> 0.1) + railties (~> 4.0.0.beta) + warden (~> 1.2.1) + GIT remote: git://github.com/smartinez87/exception_notification.git revision: 6026c274d284def545cda9bfed8dcbc3964ae08f @@ -36,6 +47,7 @@ GEM addressable (2.3.4) arel (4.0.0) atomic (1.1.8) + bcrypt-ruby (3.0.1) better_errors (0.8.0) coderay (>= 1.0.0) erubis (>= 2.6.6) @@ -76,6 +88,13 @@ GEM excon (0.20.1) execjs (1.4.0) multi_json (~> 1.0) + factory_girl (4.2.0) + activesupport (>= 3.0.0) + factory_girl_rails (4.2.1) + factory_girl (~> 4.2.0) + railties (>= 3.0.0) + faraday (0.8.7) + multipart-post (~> 1.1) foreman (0.63.0) dotenv (>= 0.7) thor (>= 0.13.6) @@ -86,6 +105,7 @@ GEM activesupport (>= 3.1, < 4.1) haml (>= 3.1, < 4.1) railties (>= 3.1, < 4.1) + hashie (2.0.4) heroku (2.39.2) heroku-api (~> 0.3.7) launchy (>= 0.3.2) @@ -100,6 +120,7 @@ GEM heroku-api (>= 0.1.2) rake hike (1.2.2) + httpauth (0.2.0) i18n (0.6.4) jbuilder (1.0.2) activesupport (>= 3.0.0) @@ -107,6 +128,8 @@ GEM railties (>= 3.0, < 5.0) thor (>= 0.14, < 2.0) json (1.7.7) + jwt (0.1.8) + multi_json (>= 1.5) kgio (2.8.0) launchy (2.3.0) addressable (~> 2.3) @@ -117,12 +140,36 @@ GEM mime-types (1.23) minitest (4.7.3) multi_json (1.7.2) + multipart-post (1.2.0) neat (1.2.1) bourbon (>= 2.1) sass (>= 3.2) netrc (0.7.7) newrelic_rpm (3.6.1.87) nokogiri (1.5.9) + oauth (0.4.7) + oauth2 (0.8.1) + faraday (~> 0.8) + httpauth (~> 0.1) + jwt (~> 0.1.4) + multi_json (~> 1.0) + rack (~> 1.2) + omniauth (1.1.4) + hashie (>= 1.2, < 3) + rack + omniauth-github (1.1.0) + omniauth (~> 1.0) + omniauth-oauth2 (~> 1.1) + omniauth-oauth (1.0.1) + oauth + omniauth (~> 1.0) + omniauth-oauth2 (1.1.1) + oauth2 (~> 0.8.0) + omniauth (~> 1.0) + omniauth-twitter (0.0.16) + multi_json (~> 1.3) + omniauth-oauth (~> 1.0) + orm_adapter (0.4.0) pg (0.15.1) polyglot (0.3.3) rack (1.5.2) @@ -166,6 +213,7 @@ GEM sprockets-rails (~> 2.0.0.rc0) tilt (~> 1.3) settingslogic (2.0.9) + simple_form (1.4.1) simplecov (0.7.1) multi_json (~> 1.0) simplecov-html (~> 0.7.1) @@ -196,6 +244,8 @@ GEM kgio (~> 2.6) rack raindrops (~> 0.7) + warden (1.2.1) + rack (>= 1.0) xpath (2.0.0) nokogiri (~> 1.3) @@ -210,7 +260,9 @@ DEPENDENCIES capybara-webkit (~> 0.14) coffee-rails (~> 4.0.0.beta1) debugger (~> 1.5) + devise! exception_notification! + factory_girl_rails (~> 4.2) foreman haml-rails heroku_san (~> 3.0.2) @@ -218,12 +270,16 @@ DEPENDENCIES jquery-rails neat newrelic_rpm + omniauth + omniauth-github + omniauth-twitter pg rack-robotz (~> 0.0.3) rails (= 4.0.0.rc1) rspec-rails (~> 2.0) sass-rails (~> 4.0.0.beta1) settingslogic + simple_form simplecov turbolinks uglifier (>= 1.0.3) diff --git a/config/application.rb b/config/application.rb index fbddfd10..a3ef31a4 100644 --- a/config/application.rb +++ b/config/application.rb @@ -21,6 +21,8 @@ class Application < Rails::Application # don't generate any assets config.generators.assets = false + + config.assets.initialize_on_precompile = false end end diff --git a/config/application.yml b/config/application.yml index eda6754e..b479e087 100644 --- a/config/application.yml +++ b/config/application.yml @@ -1,4 +1,5 @@ defaults: &defaults + host: "activities.eurucamp.org" mailers: from: "activities@eurucamp.org" errors: @@ -9,12 +10,14 @@ defaults: &defaults development: <<: *defaults + host: "localhost:3000" test: <<: *defaults staging: <<: *defaults + host: "activities-staging.eurucamp.org" production: <<: *defaults diff --git a/config/environments/development.rb b/config/environments/development.rb index 720958e9..0cbbd093 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -24,4 +24,6 @@ # Debug mode disables concatenation and preprocessing of assets. config.assets.debug = true + + config.action_mailer.default_url_options = { :host => Settings.host } end diff --git a/config/environments/production.rb b/config/environments/production.rb index de3d247e..8ed78b41 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -75,6 +75,8 @@ # Disable automatic flushing of the log to improve performance. # config.autoflush_log = false + config.action_mailer.default_url_options = { :host => Settings.host } + # Use default logging formatter so that PID and timestamp are not suppressed. config.log_formatter = ::Logger::Formatter.new diff --git a/config/environments/staging.rb b/config/environments/staging.rb index 5b50a485..67e7c406 100644 --- a/config/environments/staging.rb +++ b/config/environments/staging.rb @@ -77,6 +77,8 @@ # Use default logging formatter so that PID and timestamp are not suppressed. config.log_formatter = ::Logger::Formatter.new + config.action_mailer.default_url_options = { :host => Settings.host } + config.middleware.insert_before(::ActionDispatch::Static, ::Rack::Robotz, "User-Agent" => "*", "Disallow" => "/") config.middleware.use ExceptionNotifier, diff --git a/config/environments/test.rb b/config/environments/test.rb index 1f27b3c6..d8826545 100644 --- a/config/environments/test.rb +++ b/config/environments/test.rb @@ -33,4 +33,6 @@ # Print deprecation notices to the stderr. config.active_support.deprecation = :stderr + + config.action_mailer.default_url_options = { :host => Settings.host } end diff --git a/config/initializers/devise.rb b/config/initializers/devise.rb new file mode 100644 index 00000000..7631cece --- /dev/null +++ b/config/initializers/devise.rb @@ -0,0 +1,242 @@ +# Use this hook to configure devise mailer, warden hooks and so forth. +# Many of these configuration options can be set straight in your model. +Devise.setup do |config| + # ==> Mailer Configuration + # Configure the e-mail address which will be shown in Devise::Mailer, + # note that it will be overwritten if you use your own mailer class with default "from" parameter. + config.mailer_sender = "please-change-me-at-config-initializers-devise@example.com" + + # Configure the class responsible to send e-mails. + # config.mailer = "Devise::Mailer" + + # ==> ORM configuration + # Load and configure the ORM. Supports :active_record (default) and + # :mongoid (bson_ext recommended) by default. Other ORMs may be + # available as additional gems. + require 'devise/orm/active_record' + + # ==> Configuration for any authentication mechanism + # Configure which keys are used when authenticating a user. The default is + # just :email. You can configure it to use [:username, :subdomain], so for + # authenticating a user, both parameters are required. Remember that those + # parameters are used only when authenticating and not when retrieving from + # session. If you need permissions, you should implement that in a before filter. + # You can also supply a hash where the value is a boolean determining whether + # or not authentication should be aborted when the value is not present. + # config.authentication_keys = [ :email ] + + # Configure parameters from the request object used for authentication. Each entry + # given should be a request method and it will automatically be passed to the + # find_for_authentication method and considered in your model lookup. For instance, + # if you set :request_keys to [:subdomain], :subdomain will be used on authentication. + # The same considerations mentioned for authentication_keys also apply to request_keys. + # config.request_keys = [] + + # Configure which authentication keys should be case-insensitive. + # These keys will be downcased upon creating or modifying a user and when used + # to authenticate or find a user. Default is :email. + config.case_insensitive_keys = [ :email ] + + # Configure which authentication keys should have whitespace stripped. + # These keys will have whitespace before and after removed upon creating or + # modifying a user and when used to authenticate or find a user. Default is :email. + config.strip_whitespace_keys = [ :email ] + + # Tell if authentication through request.params is enabled. True by default. + # It can be set to an array that will enable params authentication only for the + # given strategies, for example, `config.params_authenticatable = [:database]` will + # enable it only for database (email + password) authentication. + # config.params_authenticatable = true + + # Tell if authentication through HTTP Basic Auth is enabled. False by default. + # It can be set to an array that will enable http authentication only for the + # given strategies, for example, `config.http_authenticatable = [:token]` will + # enable it only for token authentication. + # config.http_authenticatable = false + + # If http headers should be returned for AJAX requests. True by default. + # config.http_authenticatable_on_xhr = true + + # The realm used in Http Basic Authentication. "Application" by default. + # config.http_authentication_realm = "Application" + + # It will change confirmation, password recovery and other workflows + # to behave the same regardless if the e-mail provided was right or wrong. + # Does not affect registerable. + # config.paranoid = true + + # By default Devise will store the user in session. You can skip storage for + # :http_auth and :token_auth by adding those symbols to the array below. + # Notice that if you are skipping storage for all authentication paths, you + # may want to disable generating routes to Devise's sessions controller by + # passing :skip => :sessions to `devise_for` in your config/routes.rb + config.skip_session_storage = [:http_auth] + + # ==> Configuration for :database_authenticatable + # For bcrypt, this is the cost for hashing the password and defaults to 10. If + # using other encryptors, it sets how many times you want the password re-encrypted. + # + # Limiting the stretches to just one in testing will increase the performance of + # your test suite dramatically. However, it is STRONGLY RECOMMENDED to not use + # a value less than 10 in other environments. + config.stretches = Rails.env.test? ? 1 : 10 + + # Setup a pepper to generate the encrypted password. + # config.pepper = "490e70f9a671c7ab836267420ded76e5be5d4b24fedffec1f528ea91a536e308891dfad15f22a5ff8d746fa5df4025786b0d1d9ed6bd6762edd45df4e7682ac1" + + # ==> Configuration for :confirmable + # A period that the user is allowed to access the website even without + # confirming his account. For instance, if set to 2.days, the user will be + # able to access the website for two days without confirming his account, + # access will be blocked just in the third day. Default is 0.days, meaning + # the user cannot access the website without confirming his account. + # config.allow_unconfirmed_access_for = 2.days + + # A period that the user is allowed to confirm their account before their + # token becomes invalid. For example, if set to 3.days, the user can confirm + # their account within 3 days after the mail was sent, but on the fourth day + # their account can't be confirmed with the token any more. + # Default is nil, meaning there is no restriction on how long a user can take + # before confirming their account. + # config.confirm_within = 3.days + + # If true, requires any email changes to be confirmed (exactly the same way as + # initial account confirmation) to be applied. Requires additional unconfirmed_email + # db field (see migrations). Until confirmed new email is stored in + # unconfirmed email column, and copied to email column on successful confirmation. + config.reconfirmable = true + + # Defines which key will be used when confirming an account + # config.confirmation_keys = [ :email ] + + # ==> Configuration for :rememberable + # The time the user will be remembered without asking for credentials again. + # config.remember_for = 2.weeks + + # If true, extends the user's remember period when remembered via cookie. + # config.extend_remember_period = false + + # Options to be passed to the created cookie. For instance, you can set + # :secure => true in order to force SSL only cookies. + # config.rememberable_options = {} + + # ==> Configuration for :validatable + # Range for password length. Default is 8..128. + config.password_length = 8..128 + + # Email regex used to validate email formats. It simply asserts that + # one (and only one) @ exists in the given string. This is mainly + # to give user feedback and not to assert the e-mail validity. + # config.email_regexp = /\A[^@]+@[^@]+\z/ + + # ==> Configuration for :timeoutable + # The time you want to timeout the user session without activity. After this + # time the user will be asked for credentials again. Default is 30 minutes. + # config.timeout_in = 30.minutes + + # If true, expires auth token on session timeout. + # config.expire_auth_token_on_timeout = false + + # ==> Configuration for :lockable + # Defines which strategy will be used to lock an account. + # :failed_attempts = Locks an account after a number of failed attempts to sign in. + # :none = No lock strategy. You should handle locking by yourself. + # config.lock_strategy = :failed_attempts + + # Defines which key will be used when locking and unlocking an account + # config.unlock_keys = [ :email ] + + # Defines which strategy will be used to unlock an account. + # :email = Sends an unlock link to the user email + # :time = Re-enables login after a certain amount of time (see :unlock_in below) + # :both = Enables both strategies + # :none = No unlock strategy. You should handle unlocking by yourself. + # config.unlock_strategy = :both + + # Number of authentication tries before locking an account if lock_strategy + # is failed attempts. + # config.maximum_attempts = 20 + + # Time interval to unlock the account if :time is enabled as unlock_strategy. + # config.unlock_in = 1.hour + + # ==> Configuration for :recoverable + # + # Defines which key will be used when recovering the password for an account + # config.reset_password_keys = [ :email ] + + # Time interval you can reset your password with a reset password key. + # Don't put a too small interval or your users won't have the time to + # change their passwords. + config.reset_password_within = 6.hours + + # ==> Configuration for :encryptable + # Allow you to use another encryption algorithm besides bcrypt (default). You can use + # :sha1, :sha512 or encryptors from others authentication tools as :clearance_sha1, + # :authlogic_sha512 (then you should set stretches above to 20 for default behavior) + # and :restful_authentication_sha1 (then you should set stretches to 10, and copy + # REST_AUTH_SITE_KEY to pepper). + # + # Require the `devise-encryptable` gem when using anything other than bcrypt + # config.encryptor = :sha512 + + # ==> Configuration for :token_authenticatable + # Defines name of the authentication token params key + # config.token_authentication_key = :auth_token + + # ==> Scopes configuration + # Turn scoped views on. Before rendering "sessions/new", it will first check for + # "users/sessions/new". It's turned off by default because it's slower if you + # are using only default views. + # config.scoped_views = false + + # Configure the default scope given to Warden. By default it's the first + # devise role declared in your routes (usually :user). + # config.default_scope = :user + + # Set this configuration to false if you want /users/sign_out to sign out + # only the current scope. By default, Devise signs out all scopes. + # config.sign_out_all_scopes = true + + # ==> Navigation configuration + # Lists the formats that should be treated as navigational. Formats like + # :html, should redirect to the sign in page when the user does not have + # access, but formats like :xml or :json, should return 401. + # + # If you have any extra navigational formats, like :iphone or :mobile, you + # should add them to the navigational formats lists. + # + # The "*/*" below is required to match Internet Explorer requests. + # config.navigational_formats = ["*/*", :html] + + # The default HTTP method used to sign out a resource. Default is :delete. + config.sign_out_via = :delete + + # ==> OmniAuth + # Add a new OmniAuth provider. Check the wiki for more information on setting + # up on your models and hooks. + # config.omniauth :github, 'APP_ID', 'APP_SECRET', :scope => 'user,public_repo' + + # ==> Warden configuration + # If you want to use other strategies, that are not supported by Devise, or + # change the failure app, you can configure them inside the config.warden block. + # + # config.warden do |manager| + # manager.intercept_401 = false + # manager.default_strategies(:scope => :user).unshift :some_external_strategy + # end + + # ==> Mountable engine configurations + # When using Devise inside an engine, let's call it `MyEngine`, and this engine + # is mountable, there are some extra configurations to be taken into account. + # The following options are available, assuming the engine is mounted as: + # + # mount MyEngine, at: "/my_engine" + # + # The router that invoked `devise_for`, in the example above, would be: + # config.router_name = :my_engine + # + # When using omniauth, Devise cannot automatically set Omniauth path, + # so you need to do it manually. For the users scope, it would be: + # config.omniauth_path_prefix = "/my_engine/users/auth" +end diff --git a/config/initializers/omni_auth.rb b/config/initializers/omni_auth.rb new file mode 100644 index 00000000..e0dd9da3 --- /dev/null +++ b/config/initializers/omni_auth.rb @@ -0,0 +1,4 @@ +Rails.application.config.middleware.use OmniAuth::Builder do + provider :twitter, ENV['TWITTER_KEY'], ENV['TWITTER_SECRET'] + provider :github, ENV['GITHUB_KEY'], ENV['GITHUB_SECRET'], scope: 'user:email' +end diff --git a/config/locales/devise.en.yml b/config/locales/devise.en.yml new file mode 100644 index 00000000..d01f375c --- /dev/null +++ b/config/locales/devise.en.yml @@ -0,0 +1,59 @@ +# Additional translations at https://github.com/plataformatec/devise/wiki/I18n + +en: + devise: + confirmations: + confirmed: "Your account was successfully confirmed. You are now signed in." + send_instructions: "You will receive an email with instructions about how to confirm your account in a few minutes." + send_paranoid_instructions: "If your email address exists in our database, you will receive an email with instructions about how to confirm your account in a few minutes." + failure: + already_authenticated: "You are already signed in." + inactive: "Your account was not activated yet." + invalid: "Invalid email or password." + invalid_token: "Invalid authentication token." + locked: "Your account is locked." + not_found_in_database: "Invalid email or password." + timeout: "Your session expired, please sign in again to continue." + unauthenticated: "You need to sign in or sign up before continuing." + unconfirmed: "You have to confirm your account before continuing." + mailer: + confirmation_instructions: + subject: "Confirmation instructions" + reset_password_instructions: + subject: "Reset password instructions" + unlock_instructions: + subject: "Unlock Instructions" + omniauth_callbacks: + failure: "Could not authenticate you from %{kind} because \"%{reason}\"." + success: "Successfully authenticated from %{kind} account." + passwords: + no_token: "You can't access this page without coming from a password reset email. If you do come from a password reset email, please make sure you used the full URL provided." + send_instructions: "You will receive an email with instructions about how to reset your password in a few minutes." + send_paranoid_instructions: "If your email address exists in our database, you will receive a password recovery link at your email address in a few minutes." + updated: "Your password was changed successfully. You are now signed in." + updated_not_active: "Your password was changed successfully." + registrations: + destroyed: "Bye! Your account was successfully cancelled. We hope to see you again soon." + signed_up: "Welcome! You have signed up successfully." + signed_up_but_inactive: "You have signed up successfully. However, we could not sign you in because your account is not yet activated." + signed_up_but_locked: "You have signed up successfully. However, we could not sign you in because your account is locked." + signed_up_but_unconfirmed: "A message with a confirmation link has been sent to your email address. Please open the link to activate your account." + update_needs_confirmation: "You updated your account successfully, but we need to verify your new email address. Please check your email and click on the confirm link to finalize confirming your new email address." + updated: "You updated your account successfully." + sessions: + signed_in: "Signed in successfully." + signed_out: "Signed out successfully." + unlocks: + send_instructions: "You will receive an email with instructions about how to unlock your account in a few minutes." + send_paranoid_instructions: "If your account exists, you will receive an email with instructions about how to unlock it in a few minutes." + unlocked: "Your account has been unlocked successfully. Please sign in to continue." + errors: + messages: + already_confirmed: "was already confirmed, please try signing in" + confirmation_period_expired: "needs to be confirmed within %{period}, please request a new one" + expired: "has expired, please request a new one" + not_found: "not found" + not_locked: "was not locked" + not_saved: + one: "1 error prohibited this %{resource} from being saved:" + other: "%{count} errors prohibited this %{resource} from being saved:" diff --git a/spec/lib/settings_spec.rb b/spec/lib/settings_spec.rb new file mode 100644 index 00000000..ebe866eb --- /dev/null +++ b/spec/lib/settings_spec.rb @@ -0,0 +1,8 @@ +require 'spec_helper' + +describe "Settings" do + + subject { Settings } + + its(:host) { should_not be_blank } +end \ No newline at end of file diff --git a/spec/other/factories_spec.rb b/spec/other/factories_spec.rb new file mode 100644 index 00000000..4d43af3c --- /dev/null +++ b/spec/other/factories_spec.rb @@ -0,0 +1,10 @@ +require 'spec_helper' + +describe 'validate FactoryGirl factories' do + FactoryGirl.factories.each do |factory| + context "with factory for :#{factory.name}" do + subject { FactoryGirl.build(factory.name) } + it { should be_valid, subject.errors.full_messages } + end + end +end \ No newline at end of file diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 96469152..c844d873 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -20,6 +20,31 @@ # in spec/support/ and its subdirectories. Dir[Rails.root.join("spec/support/**/*.rb")].each { |f| require f } +OmniAuth.config.test_mode = true + +OmniAuth.config.add_mock(:github, { + uid: 'xc8b12448990eaef0b420f7153ec8d58', + nickname: 'rockstar', + email: 'user@99cookies.com', + image: 'rockstar.jpg' +}) +OmniAuth.config.add_mock(:twitter, { + uid: 'xc8b12448990eaef0b420f7153ec8d58', + nickname: 'rockstar' +}) + +Devise.stretches = 1 +Rails.logger.level = 4 + +class ActiveRecord::Base + mattr_accessor :shared_connection + @@shared_connection = nil + + def self.connection + @@shared_connection || retrieve_connection + end +end + # Checks for pending migrations before tests are run. # If you are not using ActiveRecord, you can remove this line. ActiveRecord::Migration.check_pending! if defined?(ActiveRecord::Migration) From 8c7b9d94f08c04ed6fb3dfec5d2e91878fb1259d Mon Sep 17 00:00:00 2001 From: Polarblau Date: Wed, 1 May 2013 01:21:00 +0300 Subject: [PATCH 027/499] Moving html helpers into gem. --- Gemfile | 1 + Gemfile.lock | 8 +++++++ app/helpers/application_helper.rb | 20 ---------------- app/helpers/html5_helper.rb | 38 ------------------------------- 4 files changed, 9 insertions(+), 58 deletions(-) delete mode 100644 app/helpers/html5_helper.rb diff --git a/Gemfile b/Gemfile index 8a9951ea..f2d83a4c 100644 --- a/Gemfile +++ b/Gemfile @@ -18,6 +18,7 @@ gem 'jbuilder', '~> 1.0.1' gem 'bourbon' gem 'neat' gem 'haml-rails' +gem 'rails_html_helpers', :git => 'https://github.com/polarblau/rails_html_helpers.git' group :development do gem 'debugger', '~> 1.5' diff --git a/Gemfile.lock b/Gemfile.lock index 194fee0a..a93baba4 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -6,6 +6,13 @@ GIT actionmailer (>= 3.0.4) activesupport (>= 3.0.4) +GIT + remote: https://github.com/polarblau/rails_html_helpers.git + revision: c0a2ad5413cf74d2e128bb8650b789581470e1ec + specs: + rails_html_helpers (0.0.1) + railties (>= 3.2) + GEM remote: https://rubygems.org/ specs: @@ -201,6 +208,7 @@ DEPENDENCIES pg rack-robotz (~> 0.0.3) rails (= 4.0.0.beta1) + rails_html_helpers! sass-rails (~> 4.0.0.beta1) settingslogic simplecov diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 8c5d5f6a..de6be794 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -1,22 +1,2 @@ module ApplicationHelper - # Sets and formats document title - # - # @param [Mixed] - # @param [String] Separator - # @return [String] Title - def title(t, separator = " | ") - @title ||= [] - @title << t - @title.flatten.compact.join(separator) - end - - # Sets body classes - # - # @param [Mixed] - # @return [String] CSS classes - def body_class(c = nil) - @body_class ||= [controller.controller_name] - @body_class << c - @body_class.flatten.compact.uniq.join(" ") - end end diff --git a/app/helpers/html5_helper.rb b/app/helpers/html5_helper.rb deleted file mode 100644 index 26edfd5c..00000000 --- a/app/helpers/html5_helper.rb +++ /dev/null @@ -1,38 +0,0 @@ -module Html5Helper - - # Helper to display conditional html tags for IE - # http://paulirish.com/2008/conditional-stylesheets-vs-css-hacks-answer-neither - # has been lifted from the HTML5-rails gem: https://raw.github.com/sporkd/html5-rails/master/lib/html5/rails/helpers.rb - def html_tag(attrs={}) - attrs.symbolize_keys! - html = "" - html << "\n" - html << "\n" - html << "\n" - html << " " - - if block_given? && defined? Haml - haml_concat(html.html_safe) - haml_tag :html, attrs do - haml_concat("".html_safe) - yield - end - else - html = html.html_safe - html << tag(:html, attrs, true) - html << " \n".html_safe - html - end - end - -private - - def add_class(name, attrs) - classes = attrs[:class] || "" - classes.strip! - classes = " " + classes if !classes.blank? - classes = name + classes - attrs.merge(:class => classes) - end - -end From 87e51b04b4fa9324c84bfbfd68c8d7367e3f2ae7 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Wed, 1 May 2013 18:17:14 +0300 Subject: [PATCH 028/499] Using rails_html_helpers from rubygems. --- Gemfile | 2 +- Gemfile.lock | 11 +++-------- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/Gemfile b/Gemfile index f2d83a4c..ec17bb04 100644 --- a/Gemfile +++ b/Gemfile @@ -18,7 +18,7 @@ gem 'jbuilder', '~> 1.0.1' gem 'bourbon' gem 'neat' gem 'haml-rails' -gem 'rails_html_helpers', :git => 'https://github.com/polarblau/rails_html_helpers.git' +gem 'rails_html_helpers' group :development do gem 'debugger', '~> 1.5' diff --git a/Gemfile.lock b/Gemfile.lock index a93baba4..5af41bc2 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -6,13 +6,6 @@ GIT actionmailer (>= 3.0.4) activesupport (>= 3.0.4) -GIT - remote: https://github.com/polarblau/rails_html_helpers.git - revision: c0a2ad5413cf74d2e128bb8650b789581470e1ec - specs: - rails_html_helpers (0.0.1) - railties (>= 3.2) - GEM remote: https://rubygems.org/ specs: @@ -135,6 +128,8 @@ GEM bundler (>= 1.3.0, < 2.0) railties (= 4.0.0.beta1) sprockets-rails (~> 2.0.0.rc3) + rails_html_helpers (0.0.1) + railties (>= 3.2) railties (4.0.0.beta1) actionpack (= 4.0.0.beta1) activesupport (= 4.0.0.beta1) @@ -208,7 +203,7 @@ DEPENDENCIES pg rack-robotz (~> 0.0.3) rails (= 4.0.0.beta1) - rails_html_helpers! + rails_html_helpers sass-rails (~> 4.0.0.beta1) settingslogic simplecov From e9f5328d9154931e1395e9e302d7286f172df628 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Sat, 4 May 2013 19:47:38 +0300 Subject: [PATCH 029/499] Rebuilding Gemfile. --- Gemfile.lock | 154 ++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 115 insertions(+), 39 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 5af41bc2..7b41bd49 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,6 +1,17 @@ +GIT + remote: git://github.com/plataformatec/devise.git + revision: c6189696772925ae0c608fdd8d535f735e8e114e + branch: rails4 + specs: + devise (2.2.3) + bcrypt-ruby (~> 3.0) + orm_adapter (~> 0.1) + railties (~> 4.0.0.beta) + warden (~> 1.2.1) + GIT remote: git://github.com/smartinez87/exception_notification.git - revision: 6026c274d284def545cda9bfed8dcbc3964ae08f + revision: 11d61df3cb435381929757b8d7e47209b5ab2b14 specs: exception_notification (3.0.1) actionmailer (>= 3.0.4) @@ -9,43 +20,52 @@ GIT GEM remote: https://rubygems.org/ specs: - actionmailer (4.0.0.beta1) - actionpack (= 4.0.0.beta1) + actionmailer (4.0.0.rc1) + actionpack (= 4.0.0.rc1) mail (~> 2.5.3) - actionpack (4.0.0.beta1) - activesupport (= 4.0.0.beta1) + actionpack (4.0.0.rc1) + activesupport (= 4.0.0.rc1) builder (~> 3.1.0) erubis (~> 2.7.0) rack (~> 1.5.2) rack-test (~> 0.6.2) - activemodel (4.0.0.beta1) - activesupport (= 4.0.0.beta1) + activemodel (4.0.0.rc1) + activesupport (= 4.0.0.rc1) builder (~> 3.1.0) - activerecord (4.0.0.beta1) - activemodel (= 4.0.0.beta1) - activerecord-deprecated_finders (~> 0.0.3) - activesupport (= 4.0.0.beta1) - arel (~> 4.0.0.beta1) - activerecord-deprecated_finders (0.0.3) - activesupport (4.0.0.beta1) - i18n (~> 0.6.2) + activerecord (4.0.0.rc1) + activemodel (= 4.0.0.rc1) + activerecord-deprecated_finders (~> 1.0.2) + activesupport (= 4.0.0.rc1) + arel (~> 4.0.0) + activerecord-deprecated_finders (1.0.2) + activesupport (4.0.0.rc1) + i18n (~> 0.6, >= 0.6.4) minitest (~> 4.2) multi_json (~> 1.3) thread_safe (~> 0.1) - tzinfo (~> 0.3.33) + tzinfo (~> 0.3.37) addressable (2.3.4) - ansi (1.4.3) arel (4.0.0) atomic (1.1.8) + bcrypt-ruby (3.0.1) better_errors (0.8.0) coderay (>= 1.0.0) erubis (>= 2.6.6) binding_of_caller (0.7.1) debug_inspector (>= 0.0.1) - bourbon (3.1.5) + bourbon (3.1.6) sass (>= 3.2.0) thor builder (3.1.4) + capybara (2.1.0) + mime-types (>= 1.16) + nokogiri (>= 1.3.3) + rack (>= 1.0.0) + rack-test (>= 0.5.4) + xpath (~> 2.0) + capybara-webkit (0.14.2) + capybara (~> 2.0, >= 2.0.2) + json coderay (1.0.9) coffee-rails (4.0.0) coffee-script (>= 2.2.0) @@ -62,11 +82,19 @@ GEM debugger-ruby_core_source (~> 1.2.0) debugger-linecache (1.2.0) debugger-ruby_core_source (1.2.0) + diff-lcs (1.2.4) dotenv (0.7.0) erubis (2.7.0) excon (0.20.1) execjs (1.4.0) multi_json (~> 1.0) + factory_girl (4.2.0) + activesupport (>= 3.0.0) + factory_girl_rails (4.2.1) + factory_girl (~> 4.2.0) + railties (>= 3.0.0) + faraday (0.8.7) + multipart-post (~> 1.1) foreman (0.63.0) dotenv (>= 0.7) thor (>= 0.13.6) @@ -77,6 +105,7 @@ GEM activesupport (>= 3.1, < 4.1) haml (>= 3.1, < 4.1) railties (>= 3.1, < 4.1) + hashie (2.0.4) heroku (2.39.2) heroku-api (~> 0.3.7) launchy (>= 0.3.2) @@ -91,6 +120,7 @@ GEM heroku-api (>= 0.1.2) rake hike (1.2.2) + httpauth (0.2.0) i18n (0.6.4) jbuilder (1.0.2) activesupport (>= 3.0.0) @@ -98,6 +128,8 @@ GEM railties (>= 3.0, < 5.0) thor (>= 0.14, < 2.0) json (1.7.7) + jwt (0.1.8) + multi_json (>= 1.5) kgio (2.8.0) launchy (2.3.0) addressable (~> 2.3) @@ -108,11 +140,36 @@ GEM mime-types (1.23) minitest (4.7.3) multi_json (1.7.2) + multipart-post (1.2.0) neat (1.2.1) bourbon (>= 2.1) sass (>= 3.2) netrc (0.7.7) - newrelic_rpm (3.6.1.87) + newrelic_rpm (3.6.1.88) + nokogiri (1.5.9) + oauth (0.4.7) + oauth2 (0.8.1) + faraday (~> 0.8) + httpauth (~> 0.1) + jwt (~> 0.1.4) + multi_json (~> 1.0) + rack (~> 1.2) + omniauth (1.1.4) + hashie (>= 1.2, < 3) + rack + omniauth-github (1.1.0) + omniauth (~> 1.0) + omniauth-oauth2 (~> 1.1) + omniauth-oauth (1.0.1) + oauth + omniauth (~> 1.0) + omniauth-oauth2 (1.1.1) + oauth2 (~> 0.8.0) + omniauth (~> 1.0) + omniauth-twitter (0.0.16) + multi_json (~> 1.3) + omniauth-oauth (~> 1.0) + orm_adapter (0.4.0) pg (0.15.1) polyglot (0.3.3) rack (1.5.2) @@ -120,28 +177,36 @@ GEM rack rack-test (0.6.2) rack (>= 1.0) - rails (4.0.0.beta1) - actionmailer (= 4.0.0.beta1) - actionpack (= 4.0.0.beta1) - activerecord (= 4.0.0.beta1) - activesupport (= 4.0.0.beta1) + rails (4.0.0.rc1) + actionmailer (= 4.0.0.rc1) + actionpack (= 4.0.0.rc1) + activerecord (= 4.0.0.rc1) + activesupport (= 4.0.0.rc1) bundler (>= 1.3.0, < 2.0) - railties (= 4.0.0.beta1) - sprockets-rails (~> 2.0.0.rc3) - rails_html_helpers (0.0.1) + railties (= 4.0.0.rc1) + sprockets-rails (~> 2.0.0.rc4) + rails_html_helpers (0.1.1) railties (>= 3.2) - railties (4.0.0.beta1) - actionpack (= 4.0.0.beta1) - activesupport (= 4.0.0.beta1) + railties (4.0.0.rc1) + actionpack (= 4.0.0.rc1) + activesupport (= 4.0.0.rc1) rake (>= 0.8.7) - rdoc (~> 3.4) - thor (>= 0.17.0, < 2.0) + thor (>= 0.18.1, < 2.0) raindrops (0.11.0) rake (10.0.4) - rdoc (3.12.2) - json (~> 1.4) rest-client (1.6.7) mime-types (>= 1.16) + rspec-core (2.13.1) + rspec-expectations (2.13.0) + diff-lcs (>= 1.1.3, < 2.0) + rspec-mocks (2.13.1) + rspec-rails (2.13.1) + actionpack (>= 3.0) + activesupport (>= 3.0) + railties (>= 3.0) + rspec-core (~> 2.13.0) + rspec-expectations (~> 2.13.0) + rspec-mocks (~> 2.13.0) rubyzip (0.9.9) sass (3.2.8) sass-rails (4.0.0.rc1) @@ -150,6 +215,7 @@ GEM sprockets-rails (~> 2.0.0.rc0) tilt (~> 1.3) settingslogic (2.0.9) + simple_form (1.4.1) simplecov (0.7.1) multi_json (~> 1.0) simplecov-html (~> 0.7.1) @@ -166,14 +232,12 @@ GEM thor (0.18.1) thread_safe (0.1.0) atomic - tilt (1.3.7) + tilt (1.4.0) treetop (1.4.12) polyglot polyglot (>= 0.3.1) turbolinks (1.1.1) coffee-rails - turn (0.9.6) - ansi tzinfo (0.3.37) uglifier (2.0.1) execjs (>= 0.3.0) @@ -182,6 +246,10 @@ GEM kgio (~> 2.6) rack raindrops (~> 0.7) + warden (1.2.1) + rack (>= 1.0) + xpath (2.0.0) + nokogiri (~> 1.3) PLATFORMS ruby @@ -190,9 +258,13 @@ DEPENDENCIES better_errors binding_of_caller bourbon + capybara (~> 2.1) + capybara-webkit (~> 0.14) coffee-rails (~> 4.0.0.beta1) debugger (~> 1.5) + devise! exception_notification! + factory_girl_rails (~> 4.2) foreman haml-rails heroku_san (~> 3.0.2) @@ -200,14 +272,18 @@ DEPENDENCIES jquery-rails neat newrelic_rpm + omniauth + omniauth-github + omniauth-twitter pg rack-robotz (~> 0.0.3) - rails (= 4.0.0.beta1) + rails (= 4.0.0.rc1) rails_html_helpers + rspec-rails (~> 2.0) sass-rails (~> 4.0.0.beta1) settingslogic + simple_form simplecov turbolinks - turn uglifier (>= 1.0.3) unicorn From 9d77960c3238ac7165daf288007432fb63c5694a Mon Sep 17 00:00:00 2001 From: Polarblau Date: Sat, 4 May 2013 19:47:57 +0300 Subject: [PATCH 030/499] Implementing first version of activities list. --- app/assets/images/dummies/activity.jpg | Bin 0 -> 16756 bytes app/assets/images/shared/buttons_big.png | Bin 0 -> 10837 bytes app/assets/images/shared/buttons_big@2x.png | Bin 0 -> 23664 bytes app/assets/images/shared/buttons_small.png | Bin 0 -> 4655 bytes app/assets/images/shared/buttons_small@2x.png | Bin 0 -> 11524 bytes app/assets/javascripts/application.js | 9 + app/assets/stylesheets/_settings.sass | 14 +- .../stylesheets/partials/_activities.sass | 219 ++++++++++++++++++ .../partials/activities/_filters.sass | 20 +- .../stylesheets/utils/_placeholders.sass | 22 ++ app/views/activities/index.html.haml | 66 ++++-- .../assets/javascripts/jquery.easing.min.js | 44 ++++ .../assets/javascripts/jquery.progress.coffee | 11 +- 13 files changed, 377 insertions(+), 28 deletions(-) create mode 100644 app/assets/images/dummies/activity.jpg create mode 100644 app/assets/images/shared/buttons_big.png create mode 100644 app/assets/images/shared/buttons_big@2x.png create mode 100644 app/assets/images/shared/buttons_small.png create mode 100644 app/assets/images/shared/buttons_small@2x.png create mode 100644 vendor/assets/javascripts/jquery.easing.min.js diff --git a/app/assets/images/dummies/activity.jpg b/app/assets/images/dummies/activity.jpg new file mode 100644 index 0000000000000000000000000000000000000000..4f3cdbf96a99e3a5a85e5914742e9f08ab2506b6 GIT binary patch literal 16756 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 z zD2*;))gmQXI-boX0K|f8%aX}u=xeKssgq@S_{D>=f*e19+X_Xvd#6EBuro6U2x&qZ zFsY9JTRz*zPTA@0$JKRpy7u`ixiKpHI|m|x2pMG~hsZ2Sl2B)rja!O@RgGGI*V&Qn zXfwco&Ya@vEAMtPe-rbJmfzXxNR?r&Yt~wTwFw1dA1q1Tccn?MLy%$NH+Ce zffR@D>R{V7ir@DW$K~B{HzTR>ef2>U7Tuj`3Nb~woS2w6vQ|sM3+9D_N)+hY-m9-| zfnMweKAy4z^CTI<753fP2!^Dyp_O2h?^(O5dwMBqf^=>eepJqz;Dpy5J?7#I zWBNTmPM-j3=WCgX)arGABa>D8RxRbpXWZE@q?A-X>V6^@T|mZ6Pm5p#Ep*xVof-wY zn8XKl?Vi#JAx8YQZ+~7Cdjxb|iuyTajsh75>7pTA67N{a2vEWo$4yym+7yoPEk?QPPm?Ntzd0M1S{GOTCrxDZu=Z#fUYX2lSN;A z62ABX+x618PFg`skey^;4tQQ4j>=SY{|z`9cwiS+J0cDdgCjIj0Tj2Dn4FqHQg+qb zVsny84v*nGDov=-_Ij+`SJ>WB9AbJUW%r)noP}WheI|pBUccY0=QA!bI}r>4zjf)GvH)$L)3y!%|__!7f#$n#%}CZgW+J z55rhwaa*gX_4ZG~6~|*D=gvqi_+Bv72D@gg+pzA3GOaNs?2E(^(eTlA37*$e2~>y- z{o-p`Nc@=clW*nOEt_zMyXnQoWVo$c5{$V%}30XmP!e^v)T%24Bj;<-=zPS0-mr zGIX{fl=JzXqAL5PwRLwHVFafwLxZZh z8mf$FhL~NS{SjRB)(7~e;`v^nzuk^7sDzik!?r=HHrj*D^%u1mCbj=Wx-GE)5dM3T`1vyTQZ^;GnOH?@oo2z;<*fo18C1Ls+ zt#&_@^5rveaZ&fWOg6nn>3Yo~EG^A~RK7(D^q+i13f~pJxn_HDnsNwI>*ppF;N`Z+ z$u=f$yJYPL!13OD#n`c}wXKPpW=?FfJ z^qJynDAw>hYnDfB%f7y%+6!m2_xrklH}5IyB=ch3R;Z~GFjd_hk|i&kiTMQ>LFD=J z^OA@{=!8HGIoZnxQ2^$j;ik2-kK!VHxNr$|e`gRy)6T`ah;K0~=G1mUUkViGEBtzv z+IkawfMkpd0S^T;19lGORHf4&MRt|_Rwf5@23L{DH>ZUw8TDka>Iv3{Idyz0ftlYl zrpj^U;@bI1%03acO9D_Z@%Q4#oE?*E_RCx~3g9I=66LsM~Jv!*GUb;@8+d z=zw)x?wmbbk!z`iSNDLk-9S#_g?14`{%d@nufdVBJ)gh88cf%n(H_1q@*AdraO-9s<=5U6HGKOgV?U#^%?N%#6 zxnWbTyT8HKoin5z9eoGIB?Cf`fqW+8$A#gSBHJ!suo!SIjI0ZpK+e^5#C#*3f@qm! zXC@YeiwB2iL$he64fRWB8TK}^7q4aX3RZ>EV-bu8=NM2cPV*o8;2i#5*E)=~`SB{p z7*o}NA+G?dCjgF!Ge-rZK~%nq*mSK?cB00sE+Zap+=S6x!j{66ZZ!jELu>wRes(S~ z4YN`mx7;!r(gLTE;zrNts$Y%2ZO*Np?_m(6{5+D!8-m#121S@2B++_Guf4{YOens3 z3ycslSr;}uOtf&;vWvb6+LAP+9vOUjkb5^>+R=8ug`5(wQ04vgTIXR1wC>+KQiZGQ XtfwLcF)0AnL&pj_rPAmRpO*g@CqjD} literal 0 HcmV?d00001 diff --git a/app/assets/images/shared/buttons_big.png b/app/assets/images/shared/buttons_big.png new file mode 100644 index 0000000000000000000000000000000000000000..010dd028a2140e0bd3d98771b232cb870d545c82 GIT binary patch literal 10837 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
S{>Cg{!a+- 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 gF^flN0-*~621G!l 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 literal 0 HcmV?d00001 diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index 698f2d82..a1051652 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -13,5 +13,14 @@ //= require jquery //= require jquery_ujs //= require turbolinks +//= require jquery.easing.min //= require jquery.progress //= require_tree . + +$(function() { + $('#activities img.progress').each(function() { + setTimeout(function(img) { + $(img).progress(); + }, Math.random() * 1000 + 500, this); + }); +}); diff --git a/app/assets/stylesheets/_settings.sass b/app/assets/stylesheets/_settings.sass index 2b722729..a9ba3d54 100644 --- a/app/assets/stylesheets/_settings.sass +++ b/app/assets/stylesheets/_settings.sass @@ -7,7 +7,7 @@ $visual-grid: false $column: 225px $gutter: 0px $padding: 20px -$grid-columns: 8 +$grid-columns: 6 $min-width: em(900) // breakpoints @@ -15,10 +15,10 @@ $low-end: min-width 160px 1 $mid-range: min-width 240px 1 $smartphone-portrait: min-width 320px 2 $smartphone-landscape: min-width 480px 2 -$tablet-lite: min-width 640px 2 +$tablet-lite: min-width 640px 4 $tablet-lite-plus: min-width 750px 4 -$tablet-portrait: min-width 768px 4 -$tablet-portrait-plus: min-width 860px 4 +$tablet-portrait: min-width 768px 6 +$tablet-portrait-plus: min-width 860px 6 // colors $red: #e55924 @@ -26,11 +26,13 @@ $blue: #0ca9c4 $gray: #5b5e61 $green: #15d701 -$yellow: #fff25e +$yellow: #ffce00 $black: #222222 +$extra-dark-gray: #2c2c2c $darker-gray: #797979 $medium-gray: #8a8a8a -$white: #ffffff $light-gray: #e2e2e2 $extra-light-gray: #f7f7f7 +$white: #ffffff + diff --git a/app/assets/stylesheets/partials/_activities.sass b/app/assets/stylesheets/partials/_activities.sass index 0ce0b053..c763f775 100644 --- a/app/assets/stylesheets/partials/_activities.sass +++ b/app/assets/stylesheets/partials/_activities.sass @@ -1 +1,220 @@ @import "activities/filters" + +a#new-activity + // TODO: extract into button label placeholder + color: $green + text-transform: uppercase + @extend %smaller-text + line-height: 40px + float: right + border: none + display: inline-block + height: 40px + vertical-align: top + +custom-sans(bold) + + span + display: inline-block + @extend %button + @extend %button-add + margin: 0 0 0 15px + + &:hover + span + background-position: -40px -40px + +#activities + clear: both + @extend %clearfix + +unlisted + padding: 1em 0 0 0 + + li + display: block + +span-columns(2) + margin: 0 0 1em 0 + text-align: center + + span.icon + //@extend %button + position: absolute + top: 15px + right: 15px + opacity: 1 + +transition(opacity 0.25s) + + a + border: none + + &.details + display: block + position: absolute + z-index: 100 + width: 100% + height: 100% + top: 0 + left: 0 + + span + display: inline-block + background: $medium-gray + color: $white + text-shadow: 0 1px 0 $gray + @extend %small-text + padding: 0.25em 1.5em + text-transform: uppercase + border-radius: 3px + margin: 21em 0 0 + opacity: 0 + +transition(opacity 0.35s ease-in-out) + +custom-sans(medium) + + .container + background: $white + height: 25em + padding: 2.5em 1em 1em 1em + margin: 0 1px + position: relative + overflow: hidden + + .labels + margin: 1.5em 0 0 0 + color: $extra-dark-gray + + h4 + +custom-sans(light) + font-size: 1.375em + margin: 0 + padding: 0 0.727272em + + &:after + content: "." + text-indent: -999em + display: block + margin: 0.2em auto + width: 20px + height: 1px + border-bottom: 1px solid $light-gray + + p + &.creator + text-transform: uppercase + +custom-sans(medium) + @extend %smaller-text + margin: 0.5em 0 + + &.time + position: absolute + right: 1em + bottom: 0 + left: 1em + +custom-serif(bold) + border-top: 1px solid $light-gray + padding: 0.75em 0 0 + + .wrapper, + .labels h4 + +transition(all 0.15s ease-out) + + .labels p + opacity: 1 + +transition(opacity 0.1s) + + // the big button appearing on hover + .action + opacity: 0 + position: absolute + left: 0 + right: 0 + top: 7em + z-index: 1001 + +transform(scale(0.9)) + +transition(all 0.35s ease-in-out 0) + + a + +custom-sans(bold) + @extend %smaller-text + text-transform: uppercase + color: $green + display: inline-block + + span + display: block + width: 96px + height: 96px + margin: 0 auto 0.75em + background: transparent image-url("shared/buttons_big.png") no-repeat 0 0 + + &:hover + span + background-position: -96px 0 + + &:hover + cursor: pointer + + span.icon + opacity: 0.25 + +transition(opacity 0.25s) + + a.details + span + opacity: 1 + +transition(opacity 0.35s ease-in-out 0.35s) + + .wrapper + +transition(all 0.15s ease-out) + +transform(translate(0, -9em)) + opacity: 0.25 + + .labels + h4 + +transition(all 0.15s ease-out) + +transform(translate(0, 4em)) + opacity: 0.35 + + p + +transition(opacity 0.1s) + opacity: 0 + + .action + opacity: 1 + +transform(scale(1)) + +transition(all 0.15s ease-out 0.15s) + + // variations + $types: participant $red ok, owner $yellow edit + $xpos : 0 + + @each $type in $types + $xpos: $xpos + 1 + + &.#{nth($type, 1)} + span.icon + @extend %button-#{nth($type, 3)} + + .action + a + color: nth($type, 2) + + span + background-position: 0 -96px * $xpos + + &:hover + span + background-position: -96px -96px * $xpos + + + img.progress + width: 150px + height: 150px + border-radius: 75px + margin: 6px + + .wrapper + width: 162px + margin: 0 auto + + canvas + z-index: 99 !important + + diff --git a/app/assets/stylesheets/partials/activities/_filters.sass b/app/assets/stylesheets/partials/activities/_filters.sass index fccf04ba..2f97c265 100644 --- a/app/assets/stylesheets/partials/activities/_filters.sass +++ b/app/assets/stylesheets/partials/activities/_filters.sass @@ -2,6 +2,7 @@ form.filters @extend %clearfix border-radius: 3px overflow: hidden + float: left label background: $light-gray @@ -14,6 +15,21 @@ form.filters padding: 0.75em 1em +transition(all 0.5s) + &.participant, + &.owner + &:before + content: "•" + font-size: 2.5em + display: inline-block + margin: 0 0.25em 0.25em 0 + line-height: 1px + + &.participant:before + color: $green + + &.owner:before + color: $yellow + strong +custom-sans(light) float: right @@ -47,14 +63,14 @@ form.filters color: $white background: $blue - button, input html.js & display: none + +media($tablet-lite) border-radius: none - overflow: auto + overflow: visible label display: inline-block diff --git a/app/assets/stylesheets/utils/_placeholders.sass b/app/assets/stylesheets/utils/_placeholders.sass index 20966d76..1c1e9679 100644 --- a/app/assets/stylesheets/utils/_placeholders.sass +++ b/app/assets/stylesheets/utils/_placeholders.sass @@ -4,6 +4,28 @@ %smaller-text font-size: 0.825em +%small-text + font-size: 0.75em + +%button + background: image-url("shared/buttons_small.png") no-repeat 0 0 + width: 40px + height: 40px + + &:hover + background-position: -40px 0 + +$button-types: ok, add, remove, edit +$ypos : 0 +@each $type in $button-types + %button-#{$type} + @extend %button + background-position: 0 -40px * $ypos + + &:hover + background-position: -40px -40px * $ypos + $ypos: $ypos + 1 + =ornamental-divider content: "." text-indent: -999em diff --git a/app/views/activities/index.html.haml b/app/views/activities/index.html.haml index 793697b2..85d795ae 100644 --- a/app/views/activities/index.html.haml +++ b/app/views/activities/index.html.haml @@ -1,28 +1,66 @@ %form.filters(action=activities_path method="GET") %label> - %span - %input(type="radio" name="filter" value="today") - Today + %input(type="radio" name="filter" value="today") + Today %strong 5 %label.selected> - %span - %input(type="radio" name="filter" value="all") + %input(type="radio" name="filter" value="all") All %strong 267 - %label> - %span - %input(type="radio" name="filter" value="mine") - My activities + %label.participant> + %input(type="radio" name="filter" value="mine") + My activities %strong 2 - %label.by-me> - %span - %input(type="radio" name="filter" value="by_me") - Created by me + %label.owner> + %input(type="radio" name="filter" value="by_me") + Created by me %strong 0 %button(type="submit") Filter -Activities! +%a#new-activity(href="#" title="Create a new activity") + Create an activity + %span + +%ul#activities + -# just render a collection and move + -# card into partial + - date_type = %w(upcoming today archive).sample + - 8.times do |i| + - type = rand > 0.6 ? %w(owner participant).sample : 'default' + + %li(id="activity-#{i}" class="#{ type unless type == 'default' } #{date_type}") + .container + + = image_tag "dummies/activity.jpg", :alt => "Activity title", :class => "progress", :data => { :progress => rand(100) } + + .labels + %h4 + = ["Title", "A medium title", "A rather long title, just to see how it looks"].sample + %p.creator + = ["Alex Coles", "Piotr Gega", "Florian Plank"].sample + %p.time + = ["Monday, 12.8 / 16:30 – 18:00", "Anytime"].sample + + .action + - case type + - when "owner" + %a(href="#" title="Edit the activity details") + %span + Edit + - when "participant" + %a(href="#" title="Cancel your participation") + %span + Don’t participate + - else + %a(href="#" title="Sign-up as participant") + %span + Join + + %a.details(href="#" title="Learn more about this activity") + %span More Info + - unless type == 'default' + %span.icon diff --git a/vendor/assets/javascripts/jquery.easing.min.js b/vendor/assets/javascripts/jquery.easing.min.js new file mode 100644 index 00000000..83a43249 --- /dev/null +++ b/vendor/assets/javascripts/jquery.easing.min.js @@ -0,0 +1,44 @@ +/* + * jQuery Easing v1.3 - http://gsgd.co.uk/sandbox/jquery/easing/ + * + * Uses the built in easing capabilities added In jQuery 1.1 + * to offer multiple easing options + * + * TERMS OF USE - EASING EQUATIONS + * + * Open source under the BSD License. + * + * Copyright © 2001 Robert Penner + * All rights reserved. + * + * TERMS OF USE - jQuery Easing + * + * Open source under the BSD License. + * + * Copyright © 2008 George McGinley Smith + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * Neither the name of the author nor the names of contributors may be used to endorse + * or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * +*/ +jQuery.easing.jswing=jQuery.easing.swing;jQuery.extend(jQuery.easing,{def:"easeOutQuad",swing:function(e,f,a,h,g){return jQuery.easing[jQuery.easing.def](e,f,a,h,g)},easeInQuad:function(e,f,a,h,g){return h*(f/=g)*f+a},easeOutQuad:function(e,f,a,h,g){return -h*(f/=g)*(f-2)+a},easeInOutQuad:function(e,f,a,h,g){if((f/=g/2)<1){return h/2*f*f+a}return -h/2*((--f)*(f-2)-1)+a},easeInCubic:function(e,f,a,h,g){return h*(f/=g)*f*f+a},easeOutCubic:function(e,f,a,h,g){return h*((f=f/g-1)*f*f+1)+a},easeInOutCubic:function(e,f,a,h,g){if((f/=g/2)<1){return h/2*f*f*f+a}return h/2*((f-=2)*f*f+2)+a},easeInQuart:function(e,f,a,h,g){return h*(f/=g)*f*f*f+a},easeOutQuart:function(e,f,a,h,g){return -h*((f=f/g-1)*f*f*f-1)+a},easeInOutQuart:function(e,f,a,h,g){if((f/=g/2)<1){return h/2*f*f*f*f+a}return -h/2*((f-=2)*f*f*f-2)+a},easeInQuint:function(e,f,a,h,g){return h*(f/=g)*f*f*f*f+a},easeOutQuint:function(e,f,a,h,g){return h*((f=f/g-1)*f*f*f*f+1)+a},easeInOutQuint:function(e,f,a,h,g){if((f/=g/2)<1){return h/2*f*f*f*f*f+a}return h/2*((f-=2)*f*f*f*f+2)+a},easeInSine:function(e,f,a,h,g){return -h*Math.cos(f/g*(Math.PI/2))+h+a},easeOutSine:function(e,f,a,h,g){return h*Math.sin(f/g*(Math.PI/2))+a},easeInOutSine:function(e,f,a,h,g){return -h/2*(Math.cos(Math.PI*f/g)-1)+a},easeInExpo:function(e,f,a,h,g){return(f==0)?a:h*Math.pow(2,10*(f/g-1))+a},easeOutExpo:function(e,f,a,h,g){return(f==g)?a+h:h*(-Math.pow(2,-10*f/g)+1)+a},easeInOutExpo:function(e,f,a,h,g){if(f==0){return a}if(f==g){return a+h}if((f/=g/2)<1){return h/2*Math.pow(2,10*(f-1))+a}return h/2*(-Math.pow(2,-10*--f)+2)+a},easeInCirc:function(e,f,a,h,g){return -h*(Math.sqrt(1-(f/=g)*f)-1)+a},easeOutCirc:function(e,f,a,h,g){return h*Math.sqrt(1-(f=f/g-1)*f)+a},easeInOutCirc:function(e,f,a,h,g){if((f/=g/2)<1){return -h/2*(Math.sqrt(1-f*f)-1)+a}return h/2*(Math.sqrt(1-(f-=2)*f)+1)+a},easeInElastic:function(f,h,e,l,k){var i=1.70158;var j=0;var g=l;if(h==0){return e}if((h/=k)==1){return e+l}if(!j){j=k*0.3}if(g @@ -38,7 +36,7 @@ $.fn.extend # wrap image $wrapper = $('
', class: 'wrapper').css position: 'relative' - padding : settings.strokeWidth + # padding : settings.strokeWidth $el.wrap($wrapper) # generate canvas @@ -59,13 +57,14 @@ $.fn.extend duration : settings.duration queue : false easing : settings.easing - step : render + step : (percentage) -> render(percentage, context) }) percentageToRadians = (percentage) -> (1.5 - (percentage * 2 / 100)) * Math.PI - render = (percentage) -> + render = (percentage, context) -> + canvasSize = context.canvas.width context.clearRect(0, 0, canvasSize, canvasSize) context.beginPath() context.arc(canvasSize / 2, # origin x @@ -81,4 +80,4 @@ $.fn.extend @each -> # ensure that the image is really loaded - $(@).load init + if @complete then init.call(@) else $(@).load(=> init.call(@)) From 06c2528eb6f4c3ec20d6f895d2b6c4ede279636d Mon Sep 17 00:00:00 2001 From: Polarblau Date: Sat, 4 May 2013 22:21:12 +0300 Subject: [PATCH 031/499] Tweaks. --- .../stylesheets/partials/_activities.sass | 21 +++++++------------ 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/app/assets/stylesheets/partials/_activities.sass b/app/assets/stylesheets/partials/_activities.sass index c763f775..683b6f33 100644 --- a/app/assets/stylesheets/partials/_activities.sass +++ b/app/assets/stylesheets/partials/_activities.sass @@ -34,6 +34,7 @@ a#new-activity +span-columns(2) margin: 0 0 1em 0 text-align: center + +transition(box-shadow 0.25s ease-in-out) span.icon //@extend %button @@ -66,7 +67,7 @@ a#new-activity border-radius: 3px margin: 21em 0 0 opacity: 0 - +transition(opacity 0.35s ease-in-out) + +transition(all 0.35s ease-in-out) +custom-sans(medium) .container @@ -145,12 +146,11 @@ a#new-activity margin: 0 auto 0.75em background: transparent image-url("shared/buttons_big.png") no-repeat 0 0 - &:hover - span - background-position: -96px 0 - &:hover cursor: pointer + z-index: 2000 + +transition(box-shadow 0.25s ease-in-out) + box-shadow: 0 0 30px transparentize($black, 0.9) span.icon opacity: 0.25 @@ -159,18 +159,18 @@ a#new-activity a.details span opacity: 1 - +transition(opacity 0.35s ease-in-out 0.35s) + +transition(all 0.25s ease-in-out) .wrapper +transition(all 0.15s ease-out) +transform(translate(0, -9em)) - opacity: 0.25 + opacity: 0.15 .labels h4 +transition(all 0.15s ease-out) +transform(translate(0, 4em)) - opacity: 0.35 + opacity: 0.25 p +transition(opacity 0.1s) @@ -199,11 +199,6 @@ a#new-activity span background-position: 0 -96px * $xpos - &:hover - span - background-position: -96px -96px * $xpos - - img.progress width: 150px height: 150px From 83b484467762a9cb79a2ef6e0a8b89a5c3335c33 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Sat, 4 May 2013 22:28:54 +0300 Subject: [PATCH 032/499] Use CSS3 transitions for good. Might have gotten carried-away a bit. --- .../stylesheets/partials/_activities.sass | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/app/assets/stylesheets/partials/_activities.sass b/app/assets/stylesheets/partials/_activities.sass index 683b6f33..f41e74f0 100644 --- a/app/assets/stylesheets/partials/_activities.sass +++ b/app/assets/stylesheets/partials/_activities.sass @@ -65,7 +65,7 @@ a#new-activity padding: 0.25em 1.5em text-transform: uppercase border-radius: 3px - margin: 21em 0 0 + margin: 19em 0 0 opacity: 0 +transition(all 0.35s ease-in-out) +custom-sans(medium) @@ -88,6 +88,7 @@ a#new-activity margin: 0 padding: 0 0.727272em + &:before, &:after content: "." text-indent: -999em @@ -96,6 +97,9 @@ a#new-activity width: 20px height: 1px border-bottom: 1px solid $light-gray + &:before + width: 0 + margin: 0 auto 0.5em p &.creator @@ -127,7 +131,7 @@ a#new-activity position: absolute left: 0 right: 0 - top: 7em + top: 5em z-index: 1001 +transform(scale(0.9)) +transition(all 0.35s ease-in-out 0) @@ -153,7 +157,7 @@ a#new-activity box-shadow: 0 0 30px transparentize($black, 0.9) span.icon - opacity: 0.25 + opacity: 0 +transition(opacity 0.25s) a.details @@ -164,13 +168,17 @@ a#new-activity .wrapper +transition(all 0.15s ease-out) +transform(translate(0, -9em)) - opacity: 0.15 + opacity: 0 .labels h4 +transition(all 0.15s ease-out) - +transform(translate(0, 4em)) - opacity: 0.25 + +transform(translate(0, 2.5em)) + + &:before + width: 50% + &:after + width: 0 p +transition(opacity 0.1s) From 191bec4700b901850c714029fadf32d158cc39b2 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Sat, 4 May 2013 22:40:50 +0300 Subject: [PATCH 033/499] Implementing simple client side filtering for activities. Closes #20. --- app/assets/javascripts/application.js | 10 +--------- app/assets/javascripts/initializers.coffee | 17 +++++++++++++++++ app/views/activities/index.html.haml | 4 ++-- 3 files changed, 20 insertions(+), 11 deletions(-) create mode 100644 app/assets/javascripts/initializers.coffee diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index a1051652..da5d9d63 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -15,12 +15,4 @@ //= require turbolinks //= require jquery.easing.min //= require jquery.progress -//= require_tree . - -$(function() { - $('#activities img.progress').each(function() { - setTimeout(function(img) { - $(img).progress(); - }, Math.random() * 1000 + 500, this); - }); -}); +//= require initializers diff --git a/app/assets/javascripts/initializers.coffee b/app/assets/javascripts/initializers.coffee new file mode 100644 index 00000000..4085a8a9 --- /dev/null +++ b/app/assets/javascripts/initializers.coffee @@ -0,0 +1,17 @@ +$ -> + # show how "full" an activity is + showProgress = (img)-> $(img).progress() + $('#activities img.progress').each -> + setTimeout showProgress, Math.random() * 1000 + 500, @ + + # filters + $filters = $('form.filters label') + $activities = $('#activities li') + + $filters.on 'click', (e)-> + e.preventDefault() + $filters.removeClass 'selected' + $filter = $(@).addClass 'selected' + value = $filter.find('input').prop('checked', true).val() + $activities.show() + $activities.not(".#{value}").hide() unless value is 'all' diff --git a/app/views/activities/index.html.haml b/app/views/activities/index.html.haml index 85d795ae..2c5933f9 100644 --- a/app/views/activities/index.html.haml +++ b/app/views/activities/index.html.haml @@ -10,12 +10,12 @@ %strong 267 %label.participant> - %input(type="radio" name="filter" value="mine") + %input(type="radio" name="filter" value="participant") My activities %strong 2 %label.owner> - %input(type="radio" name="filter" value="by_me") + %input(type="radio" name="filter" value="owner") Created by me %strong 0 From 30ca1e3b753f63d54a1a89313e3b21f7627dabe5 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Sat, 4 May 2013 22:45:26 +0300 Subject: [PATCH 034/499] Adding hover back after cleaning active state. --- app/assets/stylesheets/partials/_activities.sass | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/app/assets/stylesheets/partials/_activities.sass b/app/assets/stylesheets/partials/_activities.sass index f41e74f0..7d036a96 100644 --- a/app/assets/stylesheets/partials/_activities.sass +++ b/app/assets/stylesheets/partials/_activities.sass @@ -150,6 +150,10 @@ a#new-activity margin: 0 auto 0.75em background: transparent image-url("shared/buttons_big.png") no-repeat 0 0 + &:hover + span + background-position: -96px 0 + &:hover cursor: pointer z-index: 2000 @@ -207,6 +211,10 @@ a#new-activity span background-position: 0 -96px * $xpos + &:hover + span + background-position: -96px -96px * $xpos + img.progress width: 150px height: 150px From fad0c876f01786c384ba61b8525493b2529241e3 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Sat, 4 May 2013 23:19:44 +0300 Subject: [PATCH 035/499] Shift-select to filter by multiple. #20. --- app/assets/javascripts/initializers.coffee | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/app/assets/javascripts/initializers.coffee b/app/assets/javascripts/initializers.coffee index 4085a8a9..9ca852e1 100644 --- a/app/assets/javascripts/initializers.coffee +++ b/app/assets/javascripts/initializers.coffee @@ -8,10 +8,20 @@ $ -> $filters = $('form.filters label') $activities = $('#activities li') + $filters.attr('unselectable', 'on') + .css('user-select', 'none') + .on('selectstart', false) + $filters.on 'click', (e)-> e.preventDefault() - $filters.removeClass 'selected' - $filter = $(@).addClass 'selected' - value = $filter.find('input').prop('checked', true).val() + if !e.shiftKey || (e.shiftKey && $(@).find('input').val() == 'all') + $filters.removeClass 'selected' + + $(@).addClass('selected') + + values = $filters.filter('.selected') + .find('input[type=radio]') + .map(-> return $(@).val()).get() $activities.show() - $activities.not(".#{value}").hide() unless value is 'all' + unless 'all' in values + $activities.not(".#{values.join(',.')}").hide() From 3be0d12abd4272e165c1ff579a9ae3d1df358f86 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Sun, 5 May 2013 00:32:27 +0300 Subject: [PATCH 036/499] First version of client side search + filter. #18. --- app/assets/javascripts/initializers.coffee | 33 ++++++++++++----- app/assets/stylesheets/_base.sass | 10 ++++++ .../partials/activities/_filters.sass | 36 +++++++++++++++++-- app/views/activities/index.html.haml | 7 ++-- 4 files changed, 74 insertions(+), 12 deletions(-) diff --git a/app/assets/javascripts/initializers.coffee b/app/assets/javascripts/initializers.coffee index 9ca852e1..18bf64c4 100644 --- a/app/assets/javascripts/initializers.coffee +++ b/app/assets/javascripts/initializers.coffee @@ -5,23 +5,40 @@ $ -> setTimeout showProgress, Math.random() * 1000 + 500, @ # filters - $filters = $('form.filters label') + $filters = $('form.filters label:not(.search)') + $search = $('form.filters label.search input') $activities = $('#activities li') $filters.attr('unselectable', 'on') .css('user-select', 'none') .on('selectstart', false) + filterActivities = -> + $activities.show() + # the filter tabs + values = $filters.filter('.selected') + .find('input[type=radio]') + .map(-> return $(@).val()).get() + unless 'all' in values + $activities.not(".#{values.join(',.')}").hide() + # use search input to filter further + if query = $search.val() + $activities + .filter(':visible') + .filter(-> !(new RegExp(query, 'i')).test $(@).find("h4").text()) + .hide() + $filters.on 'click', (e)-> e.preventDefault() if !e.shiftKey || (e.shiftKey && $(@).find('input').val() == 'all') $filters.removeClass 'selected' - $(@).addClass('selected') + filterActivities() - values = $filters.filter('.selected') - .find('input[type=radio]') - .map(-> return $(@).val()).get() - $activities.show() - unless 'all' in values - $activities.not(".#{values.join(',.')}").hide() + # search + # TODO: debounce + $search.on 'keyup', filterActivities + + $search.next('a.clear').on 'click', (e)-> + e.preventDefault() + $search.val('').trigger 'keyup' diff --git a/app/assets/stylesheets/_base.sass b/app/assets/stylesheets/_base.sass index 7592b266..f8b59f60 100644 --- a/app/assets/stylesheets/_base.sass +++ b/app/assets/stylesheets/_base.sass @@ -13,3 +13,13 @@ a, a:visited a:hover border: none +transition(all 0.25s) + + +::-webkit-input-placeholder + color: $light-gray +\:-moz-placeholder + color: $light-gray +::-moz-placeholder + color: $light-gray +\:-ms-input-placeholder + color: $light-gray diff --git a/app/assets/stylesheets/partials/activities/_filters.sass b/app/assets/stylesheets/partials/activities/_filters.sass index 2f97c265..60fbcea8 100644 --- a/app/assets/stylesheets/partials/activities/_filters.sass +++ b/app/assets/stylesheets/partials/activities/_filters.sass @@ -1,8 +1,12 @@ +#main + position: relative + form.filters @extend %clearfix border-radius: 3px overflow: hidden float: left + padding: 0 0 4.5em 0 label background: $light-gray @@ -30,6 +34,33 @@ form.filters &.owner:before color: $yellow + &.search + background: none + position: absolute + top: 4em + left: 0 + padding: 0 + width: 100% + + &:hover + background: none + + a + position: absolute + right: 0 + top: 1em + input + width: 100% + padding: 0.75em 15px 1em + margin: 0 0 2px 0 + font-size: 1.25em + border: none + outline: none + +custom-sans(light) + + &:focus + box-shadow: 0 0 7px transparentize($black, 0.9) + strong +custom-sans(light) float: right @@ -46,7 +77,8 @@ form.filters background: $medium-gray input - margin: 0 0.75em 0 0 + &[type=radio] + margin: 0 0.75em 0 0 button background: $medium-gray @@ -63,7 +95,7 @@ form.filters color: $white background: $blue - button, input + button, input[type=radio] html.js & display: none diff --git a/app/views/activities/index.html.haml b/app/views/activities/index.html.haml index 2c5933f9..d08db231 100644 --- a/app/views/activities/index.html.haml +++ b/app/views/activities/index.html.haml @@ -4,7 +4,7 @@ Today %strong 5 - %label.selected> + %label.all.selected> %input(type="radio" name="filter" value="all") All %strong @@ -19,6 +19,9 @@ Created by me %strong 0 + %label.search + %input(type="text" name="search" placeholder="Search for an activity") + %a.clear(href="#" title="Reset") Clear %button(type="submit") Filter %a#new-activity(href="#" title="Create a new activity") @@ -54,7 +57,7 @@ - when "participant" %a(href="#" title="Cancel your participation") %span - Don’t participate + Don’t participate? - else %a(href="#" title="Sign-up as participant") %span From 5676c940edc0bc4010f7f78aa8e319f8bf307a31 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Sun, 5 May 2013 00:41:48 +0300 Subject: [PATCH 037/499] Fixing margins and bumpy animation. --- .../stylesheets/partials/_activities.sass | 203 +++++++++--------- 1 file changed, 102 insertions(+), 101 deletions(-) diff --git a/app/assets/stylesheets/partials/_activities.sass b/app/assets/stylesheets/partials/_activities.sass index 7d036a96..a3585106 100644 --- a/app/assets/stylesheets/partials/_activities.sass +++ b/app/assets/stylesheets/partials/_activities.sass @@ -78,120 +78,121 @@ a#new-activity position: relative overflow: hidden - .labels - margin: 1.5em 0 0 0 - color: $extra-dark-gray - - h4 - +custom-sans(light) - font-size: 1.375em - margin: 0 - padding: 0 0.727272em - - &:before, - &:after - content: "." - text-indent: -999em - display: block - margin: 0.2em auto - width: 20px - height: 1px - border-bottom: 1px solid $light-gray - &:before - width: 0 - margin: 0 auto 0.5em - - p - &.creator - text-transform: uppercase - +custom-sans(medium) - @extend %smaller-text - margin: 0.5em 0 - - &.time - position: absolute - right: 1em - bottom: 0 - left: 1em - +custom-serif(bold) - border-top: 1px solid $light-gray - padding: 0.75em 0 0 - - .wrapper, - .labels h4 - +transition(all 0.15s ease-out) + .labels + margin: 1.5em 0 0 0 + color: $extra-dark-gray + + h4 + +custom-sans(light) + font-size: 1.375em + margin: 0 + padding: 0 0.727272em + + &:before, + &:after + content: "." + text-indent: -999em + display: block + margin: 0.2em auto + width: 20px + height: 1px + border-bottom: 1px solid $light-gray + &:before + width: 0 + margin: 0 auto + + p + &.creator + text-transform: uppercase + +custom-sans(medium) + @extend %smaller-text + margin: 0.5em 0 + + &.time + position: absolute + right: 1em + bottom: 0 + left: 1em + +custom-serif(bold) + border-top: 1px solid $light-gray + padding: 0.75em 0 0 + + .wrapper, + .labels h4 + +transition(all 0.15s ease-out) + + .labels p + opacity: 1 + +transition(opacity 0.1s) - .labels p - opacity: 1 - +transition(opacity 0.1s) + // the big button appearing on hover + .action + opacity: 0 + position: absolute + left: 0 + right: 0 + top: 5em + z-index: 1001 + +transform(scale(0.9)) + +transition(all 0.35s ease-in-out 0) + + a + +custom-sans(bold) + @extend %smaller-text + text-transform: uppercase + color: $green + display: inline-block - // the big button appearing on hover - .action - opacity: 0 - position: absolute - left: 0 - right: 0 - top: 5em - z-index: 1001 - +transform(scale(0.9)) - +transition(all 0.35s ease-in-out 0) - - a - +custom-sans(bold) - @extend %smaller-text - text-transform: uppercase - color: $green - display: inline-block + span + display: block + width: 96px + height: 96px + margin: 0 auto 0.75em + background: transparent image-url("shared/buttons_big.png") no-repeat 0 0 + &:hover span - display: block - width: 96px - height: 96px - margin: 0 auto 0.75em - background: transparent image-url("shared/buttons_big.png") no-repeat 0 0 + background-position: -96px 0 - &:hover - span - background-position: -96px 0 + &:hover + cursor: pointer + z-index: 2000 + +transition(box-shadow 0.25s ease-in-out) + box-shadow: 0 0 30px transparentize($black, 0.9) - &:hover - cursor: pointer - z-index: 2000 - +transition(box-shadow 0.25s ease-in-out) - box-shadow: 0 0 30px transparentize($black, 0.9) + span.icon + opacity: 0 + +transition(opacity 0.25s) - span.icon - opacity: 0 - +transition(opacity 0.25s) + a.details + span + opacity: 1 + +transition(all 0.25s ease-in-out) - a.details - span - opacity: 1 - +transition(all 0.25s ease-in-out) + .wrapper + +transition(all 0.15s ease-out) + +transform(translate(0, -9em)) + opacity: 0 - .wrapper + .labels + h4 +transition(all 0.15s ease-out) - +transform(translate(0, -9em)) - opacity: 0 - - .labels - h4 - +transition(all 0.15s ease-out) - +transform(translate(0, 2.5em)) + +transform(translate(0, 2.5em)) - &:before - width: 50% - &:after - width: 0 + &:before + width: 50% + margin: 0 auto 0.5em + &:after + width: 0 - p - +transition(opacity 0.1s) - opacity: 0 + p + +transition(opacity 0.1s) + opacity: 0 - .action - opacity: 1 - +transform(scale(1)) - +transition(all 0.15s ease-out 0.15s) + .action + opacity: 1 + +transform(scale(1)) + +transition(all 0.15s ease-out 0.15s) // variations $types: participant $red ok, owner $yellow edit From e885817396658028e7418367a55d59d7593ef8c7 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Sun, 5 May 2013 11:13:35 +0300 Subject: [PATCH 038/499] Swapping icon/button and simplifying placeholder a bit. --- .../stylesheets/partials/_activities.sass | 14 ++------ .../stylesheets/utils/_placeholders.sass | 35 +++++++++++-------- 2 files changed, 22 insertions(+), 27 deletions(-) diff --git a/app/assets/stylesheets/partials/_activities.sass b/app/assets/stylesheets/partials/_activities.sass index a3585106..9f164055 100644 --- a/app/assets/stylesheets/partials/_activities.sass +++ b/app/assets/stylesheets/partials/_activities.sass @@ -12,16 +12,7 @@ a#new-activity height: 40px vertical-align: top +custom-sans(bold) - - span - display: inline-block - @extend %button - @extend %button-add - margin: 0 0 0 15px - - &:hover - span - background-position: -40px -40px + @extend %icon-button-add #activities clear: both @@ -37,7 +28,6 @@ a#new-activity +transition(box-shadow 0.25s ease-in-out) span.icon - //@extend %button position: absolute top: 15px right: 15px @@ -203,7 +193,7 @@ a#new-activity &.#{nth($type, 1)} span.icon - @extend %button-#{nth($type, 3)} + @extend %icon-#{nth($type, 3)} .action a diff --git a/app/assets/stylesheets/utils/_placeholders.sass b/app/assets/stylesheets/utils/_placeholders.sass index 1c1e9679..ea78fd6a 100644 --- a/app/assets/stylesheets/utils/_placeholders.sass +++ b/app/assets/stylesheets/utils/_placeholders.sass @@ -7,23 +7,28 @@ %small-text font-size: 0.75em -%button - background: image-url("shared/buttons_small.png") no-repeat 0 0 - width: 40px - height: 40px - - &:hover - background-position: -40px 0 - -$button-types: ok, add, remove, edit -$ypos : 0 -@each $type in $button-types - %button-#{$type} - @extend %button - background-position: 0 -40px * $ypos +$icon-types: ok, add, remove, edit +$ypos : 0 +@each $type in $icon-types + %icon-button-#{$type} + span + width: 40px + height: 40px + margin: 0 0 0 15px + display: inline-block + background: image-url("shared/buttons_small.png") no-repeat + background-position: 0 -40px * $ypos &:hover - background-position: -40px -40px * $ypos + span + background-position: -40px -40px * $ypos + + %icon-#{$type} + width: 40px + height: 40px + display: inline-block + background: image-url("shared/buttons_small.png") no-repeat + background-position: -40px -40px * $ypos $ypos: $ypos + 1 =ornamental-divider From d175767085020c402e4587a651741787c08de687 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Sun, 5 May 2013 11:51:10 +0300 Subject: [PATCH 039/499] Tweaks. --- .../stylesheets/partials/_activities.sass | 20 ++++++------------- .../stylesheets/utils/_placeholders.sass | 16 ++++++++++++--- 2 files changed, 19 insertions(+), 17 deletions(-) diff --git a/app/assets/stylesheets/partials/_activities.sass b/app/assets/stylesheets/partials/_activities.sass index 9f164055..fcca88ac 100644 --- a/app/assets/stylesheets/partials/_activities.sass +++ b/app/assets/stylesheets/partials/_activities.sass @@ -1,17 +1,7 @@ @import "activities/filters" a#new-activity - // TODO: extract into button label placeholder - color: $green - text-transform: uppercase - @extend %smaller-text - line-height: 40px float: right - border: none - display: inline-block - height: 40px - vertical-align: top - +custom-sans(bold) @extend %icon-button-add #activities @@ -25,7 +15,6 @@ a#new-activity +span-columns(2) margin: 0 0 1em 0 text-align: center - +transition(box-shadow 0.25s ease-in-out) span.icon position: absolute @@ -67,6 +56,7 @@ a#new-activity margin: 0 1px position: relative overflow: hidden + +transition(box-shadow 0.25s ease-in-out) .labels margin: 1.5em 0 0 0 @@ -146,9 +136,11 @@ a#new-activity &:hover cursor: pointer - z-index: 2000 - +transition(box-shadow 0.25s ease-in-out) - box-shadow: 0 0 30px transparentize($black, 0.9) + + .container + z-index: 9001 + +transition(box-shadow 0.25s ease-in-out) + box-shadow: 0 0 20px transparentize($black, 0.9) span.icon opacity: 0 diff --git a/app/assets/stylesheets/utils/_placeholders.sass b/app/assets/stylesheets/utils/_placeholders.sass index ea78fd6a..ac43dbee 100644 --- a/app/assets/stylesheets/utils/_placeholders.sass +++ b/app/assets/stylesheets/utils/_placeholders.sass @@ -7,10 +7,20 @@ %small-text font-size: 0.75em -$icon-types: ok, add, remove, edit +$icon-types: ok $green, add $green, remove $red, edit $yellow $ypos : 0 @each $type in $icon-types - %icon-button-#{$type} + %icon-button-#{nth($type, 1)} + color: nth($type, 2) + @extend %smaller-text + line-height: 40px + text-transform: uppercase + border: none + display: inline-block + height: 40px + vertical-align: top + +custom-sans(bold) + span width: 40px height: 40px @@ -23,7 +33,7 @@ $ypos : 0 span background-position: -40px -40px * $ypos - %icon-#{$type} + %icon-#{nth($type, 1)} width: 40px height: 40px display: inline-block From d086b80c51722e68364d0c2b74c2071df3deadb8 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Sun, 5 May 2013 11:58:04 +0300 Subject: [PATCH 040/499] Reset filters on second click. --- app/assets/javascripts/initializers.coffee | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/app/assets/javascripts/initializers.coffee b/app/assets/javascripts/initializers.coffee index 18bf64c4..c2067b8e 100644 --- a/app/assets/javascripts/initializers.coffee +++ b/app/assets/javascripts/initializers.coffee @@ -25,15 +25,19 @@ $ -> if query = $search.val() $activities .filter(':visible') - .filter(-> !(new RegExp(query, 'i')).test $(@).find("h4").text()) + .filter(-> !(new RegExp(query, 'i')).test $(@).find('h4').text()) .hide() $filters.on 'click', (e)-> e.preventDefault() - if !e.shiftKey || (e.shiftKey && $(@).find('input').val() == 'all') - $filters.removeClass 'selected' - $(@).addClass('selected') - filterActivities() + $filter = $(@) + if $filter.hasClass('selected') + $filters.filter('.all').trigger('click') + else + if !e.shiftKey || (e.shiftKey && $filter.find('input').val() == 'all') + $filters.removeClass 'selected' + $filter.addClass('selected') + filterActivities() # search # TODO: debounce From f5d92637dd724ec59dfd554d9322996ac2dfc49a Mon Sep 17 00:00:00 2001 From: pietia Date: Tue, 7 May 2013 23:13:45 +0200 Subject: [PATCH 041/499] upgraded devise. close #12 --- Gemfile | 2 +- Gemfile.lock | 18 ++++++------------ 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/Gemfile b/Gemfile index e47e2d8c..22d3ab75 100644 --- a/Gemfile +++ b/Gemfile @@ -7,7 +7,7 @@ gem 'pg' gem 'unicorn' gem 'settingslogic' gem 'newrelic_rpm' -gem 'devise', git: 'git://github.com/plataformatec/devise.git', branch: 'rails4' +gem 'devise', '3.0.0.rc' gem 'omniauth' gem 'omniauth-github' gem 'omniauth-twitter' diff --git a/Gemfile.lock b/Gemfile.lock index 7b41bd49..3b640639 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,14 +1,3 @@ -GIT - remote: git://github.com/plataformatec/devise.git - revision: c6189696772925ae0c608fdd8d535f735e8e114e - branch: rails4 - specs: - devise (2.2.3) - bcrypt-ruby (~> 3.0) - orm_adapter (~> 0.1) - railties (~> 4.0.0.beta) - warden (~> 1.2.1) - GIT remote: git://github.com/smartinez87/exception_notification.git revision: 11d61df3cb435381929757b8d7e47209b5ab2b14 @@ -82,6 +71,11 @@ GEM debugger-ruby_core_source (~> 1.2.0) debugger-linecache (1.2.0) debugger-ruby_core_source (1.2.0) + devise (3.0.0.rc) + bcrypt-ruby (~> 3.0) + orm_adapter (~> 0.1) + railties (>= 3.2.6, < 5) + warden (~> 1.2.1) diff-lcs (1.2.4) dotenv (0.7.0) erubis (2.7.0) @@ -262,7 +256,7 @@ DEPENDENCIES capybara-webkit (~> 0.14) coffee-rails (~> 4.0.0.beta1) debugger (~> 1.5) - devise! + devise (= 3.0.0.rc) exception_notification! factory_girl_rails (~> 4.2) foreman From dd04e008674207892733a401aa788bcd4a5e2515 Mon Sep 17 00:00:00 2001 From: pietia Date: Tue, 7 May 2013 23:19:38 +0200 Subject: [PATCH 042/499] Devise - FROM address configured --- config/initializers/devise.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/initializers/devise.rb b/config/initializers/devise.rb index 7631cece..eabecd10 100644 --- a/config/initializers/devise.rb +++ b/config/initializers/devise.rb @@ -4,7 +4,7 @@ # ==> Mailer Configuration # Configure the e-mail address which will be shown in Devise::Mailer, # note that it will be overwritten if you use your own mailer class with default "from" parameter. - config.mailer_sender = "please-change-me-at-config-initializers-devise@example.com" + config.mailer_sender = Settings.mailers.from # Configure the class responsible to send e-mails. # config.mailer = "Devise::Mailer" From a3a522664f1972ff07fbcc8fc678a15966592e1c Mon Sep 17 00:00:00 2001 From: pietia Date: Tue, 7 May 2013 23:24:30 +0200 Subject: [PATCH 043/499] added accept_values_for --- Gemfile | 1 + Gemfile.lock | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/Gemfile b/Gemfile index 22d3ab75..6ede32d1 100644 --- a/Gemfile +++ b/Gemfile @@ -38,6 +38,7 @@ group :test do gem 'simplecov', require: false gem 'capybara', '~> 2.1' gem 'capybara-webkit', '~> 0.14' + gem 'accept_values_for' end group :assets do diff --git a/Gemfile.lock b/Gemfile.lock index 3b640639..b54ed722 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -9,6 +9,9 @@ GIT GEM remote: https://rubygems.org/ specs: + accept_values_for (0.4.3) + activemodel (>= 3.0.0) + rspec actionmailer (4.0.0.rc1) actionpack (= 4.0.0.rc1) mail (~> 2.5.3) @@ -190,6 +193,10 @@ GEM rake (10.0.4) rest-client (1.6.7) mime-types (>= 1.16) + rspec (2.13.0) + rspec-core (~> 2.13.0) + rspec-expectations (~> 2.13.0) + rspec-mocks (~> 2.13.0) rspec-core (2.13.1) rspec-expectations (2.13.0) diff-lcs (>= 1.1.3, < 2.0) @@ -249,6 +256,7 @@ PLATFORMS ruby DEPENDENCIES + accept_values_for better_errors binding_of_caller bourbon From 01b35d8c87ea8b32aa61fcafb0fd60aedd2a6341 Mon Sep 17 00:00:00 2001 From: pietia Date: Wed, 8 May 2013 11:38:16 +0200 Subject: [PATCH 044/499] added more gems, added User, Authentication models --- Gemfile | 1 - Gemfile.lock | 1 - app/models/authentication.rb | 3 ++ app/models/user.rb | 12 +++++ config/initializers/devise.rb | 3 +- config/initializers/omniauth.rb | 4 ++ config/initializers/strong_parameters.rb | 1 + config/routes.rb | 1 + .../20130508083406_devise_create_users.rb | 46 +++++++++++++++++++ .../20130508093002_create_authentications.rb | 11 +++++ .../20130508093144_add_name_to_users.rb | 5 ++ ...30508093327_add_oauth_handlers_to_users.rb | 6 +++ db/schema.rb | 31 ++++++++++++- spec/factories/users.rb | 7 +++ spec/models/user_spec.rb | 5 ++ 15 files changed, 133 insertions(+), 4 deletions(-) create mode 100644 app/models/authentication.rb create mode 100644 app/models/user.rb create mode 100644 config/initializers/omniauth.rb create mode 100644 config/initializers/strong_parameters.rb create mode 100644 db/migrate/20130508083406_devise_create_users.rb create mode 100644 db/migrate/20130508093002_create_authentications.rb create mode 100644 db/migrate/20130508093144_add_name_to_users.rb create mode 100644 db/migrate/20130508093327_add_oauth_handlers_to_users.rb create mode 100644 spec/factories/users.rb create mode 100644 spec/models/user_spec.rb diff --git a/Gemfile b/Gemfile index 6ede32d1..663a186a 100644 --- a/Gemfile +++ b/Gemfile @@ -8,7 +8,6 @@ gem 'unicorn' gem 'settingslogic' gem 'newrelic_rpm' gem 'devise', '3.0.0.rc' -gem 'omniauth' gem 'omniauth-github' gem 'omniauth-twitter' gem 'simple_form' diff --git a/Gemfile.lock b/Gemfile.lock index b54ed722..69a2427d 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -274,7 +274,6 @@ DEPENDENCIES jquery-rails neat newrelic_rpm - omniauth omniauth-github omniauth-twitter pg diff --git a/app/models/authentication.rb b/app/models/authentication.rb new file mode 100644 index 00000000..69a2df10 --- /dev/null +++ b/app/models/authentication.rb @@ -0,0 +1,3 @@ +class Authentication < ActiveRecord::Base + belongs_to :user +end diff --git a/app/models/user.rb b/app/models/user.rb new file mode 100644 index 00000000..0a38b049 --- /dev/null +++ b/app/models/user.rb @@ -0,0 +1,12 @@ +class User < ActiveRecord::Base + # Include default devise modules. Others available are: + # :token_authenticatable, :confirmable, + # :lockable, :timeoutable and :omniauthable + devise :database_authenticatable, :registerable, + :recoverable, :rememberable, :trackable, :validatable + + has_many :authentications + + # validates :name, presence: true + validates :email, presence: true +end diff --git a/config/initializers/devise.rb b/config/initializers/devise.rb index eabecd10..207b7557 100644 --- a/config/initializers/devise.rb +++ b/config/initializers/devise.rb @@ -215,7 +215,8 @@ # ==> OmniAuth # Add a new OmniAuth provider. Check the wiki for more information on setting # up on your models and hooks. - # config.omniauth :github, 'APP_ID', 'APP_SECRET', :scope => 'user,public_repo' + # config.omniauth :twitter, ENV['TWITTER_KEY'], ENV['TWITTER_SECRET'] + # config.omniauth :github, ENV['GITHUB_KEY'], ENV['GITHUB_SECRET'], scope: 'user:email' # ==> Warden configuration # If you want to use other strategies, that are not supported by Devise, or diff --git a/config/initializers/omniauth.rb b/config/initializers/omniauth.rb new file mode 100644 index 00000000..e0dd9da3 --- /dev/null +++ b/config/initializers/omniauth.rb @@ -0,0 +1,4 @@ +Rails.application.config.middleware.use OmniAuth::Builder do + provider :twitter, ENV['TWITTER_KEY'], ENV['TWITTER_SECRET'] + provider :github, ENV['GITHUB_KEY'], ENV['GITHUB_SECRET'], scope: 'user:email' +end diff --git a/config/initializers/strong_parameters.rb b/config/initializers/strong_parameters.rb new file mode 100644 index 00000000..394c1f5f --- /dev/null +++ b/config/initializers/strong_parameters.rb @@ -0,0 +1 @@ +ActiveRecord::Base.send(:include, ActiveModel::ForbiddenAttributesProtection) diff --git a/config/routes.rb b/config/routes.rb index 175bf55a..735ea8e4 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,4 +1,5 @@ Activities::Application.routes.draw do + devise_for :users resources :activities root to: 'activities#index' diff --git a/db/migrate/20130508083406_devise_create_users.rb b/db/migrate/20130508083406_devise_create_users.rb new file mode 100644 index 00000000..2099d998 --- /dev/null +++ b/db/migrate/20130508083406_devise_create_users.rb @@ -0,0 +1,46 @@ +class DeviseCreateUsers < ActiveRecord::Migration + def change + create_table(:users) do |t| + ## Database authenticatable + t.string :email, :null => false, :default => "" + t.string :encrypted_password, :null => false, :default => "" + + ## Recoverable + t.string :reset_password_token + t.datetime :reset_password_sent_at + + ## Rememberable + t.datetime :remember_created_at + + ## Trackable + t.integer :sign_in_count, :default => 0 + t.datetime :current_sign_in_at + t.datetime :last_sign_in_at + t.string :current_sign_in_ip + t.string :last_sign_in_ip + + ## Confirmable + # t.string :confirmation_token + # t.datetime :confirmed_at + # t.datetime :confirmation_sent_at + # t.string :unconfirmed_email # Only if using reconfirmable + + ## Lockable + # t.integer :failed_attempts, :default => 0 # Only if lock strategy is :failed_attempts + # t.string :unlock_token # Only if unlock strategy is :email or :both + # t.datetime :locked_at + + ## Token authenticatable + # t.string :authentication_token + + + t.timestamps + end + + add_index :users, :email, :unique => true + add_index :users, :reset_password_token, :unique => true + # add_index :users, :confirmation_token, :unique => true + # add_index :users, :unlock_token, :unique => true + # add_index :users, :authentication_token, :unique => true + end +end diff --git a/db/migrate/20130508093002_create_authentications.rb b/db/migrate/20130508093002_create_authentications.rb new file mode 100644 index 00000000..2b1706e2 --- /dev/null +++ b/db/migrate/20130508093002_create_authentications.rb @@ -0,0 +1,11 @@ +class CreateAuthentications < ActiveRecord::Migration + def change + create_table :authentications do |t| + t.integer :user_id + t.string :provider + t.string :uid + + t.timestamps null: false + end + end +end diff --git a/db/migrate/20130508093144_add_name_to_users.rb b/db/migrate/20130508093144_add_name_to_users.rb new file mode 100644 index 00000000..bac750eb --- /dev/null +++ b/db/migrate/20130508093144_add_name_to_users.rb @@ -0,0 +1,5 @@ +class AddNameToUsers < ActiveRecord::Migration + def change + add_column :users, :name, :string + end +end diff --git a/db/migrate/20130508093327_add_oauth_handlers_to_users.rb b/db/migrate/20130508093327_add_oauth_handlers_to_users.rb new file mode 100644 index 00000000..b74cdcc1 --- /dev/null +++ b/db/migrate/20130508093327_add_oauth_handlers_to_users.rb @@ -0,0 +1,6 @@ +class AddOauthHandlersToUsers < ActiveRecord::Migration + def change + add_column :users, :twitter_handle, :string + add_column :users, :github_handle, :string + end +end diff --git a/db/schema.rb b/db/schema.rb index ea89ed53..37f309fd 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,9 +11,38 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 0) do +ActiveRecord::Schema.define(version: 20130508093327) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" + create_table "authentications", force: true do |t| + t.integer "user_id" + t.string "provider" + t.string "uid" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + end + + create_table "users", force: true do |t| + t.string "email", default: "", null: false + t.string "encrypted_password", default: "", null: false + t.string "reset_password_token" + t.datetime "reset_password_sent_at" + t.datetime "remember_created_at" + t.integer "sign_in_count", default: 0 + t.datetime "current_sign_in_at" + t.datetime "last_sign_in_at" + t.string "current_sign_in_ip" + t.string "last_sign_in_ip" + t.datetime "created_at" + t.datetime "updated_at" + t.string "name" + t.string "twitter_handle" + t.string "github_handle" + end + + add_index "users", ["email"], name: "index_users_on_email", unique: true, using: :btree + add_index "users", ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true, using: :btree + end diff --git a/spec/factories/users.rb b/spec/factories/users.rb new file mode 100644 index 00000000..8dbc0372 --- /dev/null +++ b/spec/factories/users.rb @@ -0,0 +1,7 @@ +# Read about factories at https://github.com/thoughtbot/factory_girl + +FactoryGirl.define do + factory :user do + + end +end diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb new file mode 100644 index 00000000..44032b48 --- /dev/null +++ b/spec/models/user_spec.rb @@ -0,0 +1,5 @@ +require 'spec_helper' + +describe User do + pending "add some examples to (or delete) #{__FILE__}" +end From ea6e1b5b060e15c06fd86f8fdf0b0c42b77d033c Mon Sep 17 00:00:00 2001 From: pietia Date: Wed, 8 May 2013 14:53:49 +0200 Subject: [PATCH 045/499] upgraded simple_form, callbacks moved from c4p app .. --- Gemfile | 4 +- Gemfile.lock | 8 ++-- app/controllers/authentications_controller.rb | 42 +++++++++++++++++++ app/controllers/passwords_controller.rb | 3 ++ app/controllers/registrations_controller.rb | 22 ++++++++++ app/controllers/sessions_controller.rb | 3 ++ app/models/user.rb | 36 +++++++++++++++- config/routes.rb | 10 ++++- 8 files changed, 120 insertions(+), 8 deletions(-) create mode 100644 app/controllers/authentications_controller.rb create mode 100644 app/controllers/passwords_controller.rb create mode 100644 app/controllers/registrations_controller.rb create mode 100644 app/controllers/sessions_controller.rb diff --git a/Gemfile b/Gemfile index 663a186a..748aca8f 100644 --- a/Gemfile +++ b/Gemfile @@ -7,10 +7,10 @@ gem 'pg' gem 'unicorn' gem 'settingslogic' gem 'newrelic_rpm' -gem 'devise', '3.0.0.rc' +gem 'devise', '~> 3.0.0.rc' gem 'omniauth-github' gem 'omniauth-twitter' -gem 'simple_form' +gem 'simple_form', '~> 3.0.0.beta1' gem 'jquery-rails' gem 'turbolinks' diff --git a/Gemfile.lock b/Gemfile.lock index 69a2427d..1bedcb55 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -216,7 +216,9 @@ GEM sprockets-rails (~> 2.0.0.rc0) tilt (~> 1.3) settingslogic (2.0.9) - simple_form (1.4.1) + simple_form (3.0.0.beta1) + actionpack (>= 4.0.0.beta, < 4.1) + activemodel (>= 4.0.0.beta, < 4.1) simplecov (0.7.1) multi_json (~> 1.0) simplecov-html (~> 0.7.1) @@ -264,7 +266,7 @@ DEPENDENCIES capybara-webkit (~> 0.14) coffee-rails (~> 4.0.0.beta1) debugger (~> 1.5) - devise (= 3.0.0.rc) + devise (~> 3.0.0.rc) exception_notification! factory_girl_rails (~> 4.2) foreman @@ -283,7 +285,7 @@ DEPENDENCIES rspec-rails (~> 2.0) sass-rails (~> 4.0.0.beta1) settingslogic - simple_form + simple_form (~> 3.0.0.beta1) simplecov turbolinks uglifier (>= 1.0.3) diff --git a/app/controllers/authentications_controller.rb b/app/controllers/authentications_controller.rb new file mode 100644 index 00000000..3e7169a5 --- /dev/null +++ b/app/controllers/authentications_controller.rb @@ -0,0 +1,42 @@ +class AuthenticationsController < ApplicationController + skip_before_filter :authenticate + + rescue_from ActionController::RedirectBackError do |exception| + redirect_to profile_url + end + + def create + omniauth = request.env['omniauth.auth'] + provider, uid = omniauth.values_at('provider', 'uid') + authentication = Authentication.find_by_provider_and_uid(provider, uid) + + if authentication + flash[:notice] = 'Signed in successfully' + sign_in_and_redirect(:user, authentication.user) + elsif current_user + current_user.authentications.find_or_create_by_provider_and_uid(provider, uid) + current_user.apply_provider_handle(omniauth) + current_user.save + flash[:notice] = 'Connected successfully' + redirect_to :back + else + user = User.new + user.apply_omniauth(omniauth) + if user.save + flash[:notice] = 'Signed in successfully' + sign_in_and_redirect(:user, user) + else + session[:omniauth] = omniauth.except(:extra) + redirect_to new_user_registration_url + end + end + end + + def destroy + @authentication = current_user.authentications.find(params[:id]) + @authentication.destroy + + flash[:notice] = 'Successfully disconnected provider' + redirect_to authentications_url + end +end diff --git a/app/controllers/passwords_controller.rb b/app/controllers/passwords_controller.rb new file mode 100644 index 00000000..e4ac15d0 --- /dev/null +++ b/app/controllers/passwords_controller.rb @@ -0,0 +1,3 @@ +class PasswordsController < Devise::PasswordsController + skip_before_filter :authenticate +end diff --git a/app/controllers/registrations_controller.rb b/app/controllers/registrations_controller.rb new file mode 100644 index 00000000..8fa7ea6f --- /dev/null +++ b/app/controllers/registrations_controller.rb @@ -0,0 +1,22 @@ +class RegistrationsController < Devise::RegistrationsController + skip_before_filter :authenticate + + def create + super + session[:omniauth] = nil unless @user.new_record? + end + + private + def build_resource(*args) + super + if session[:omniauth] + @user.apply_omniauth(session[:omniauth]) + @user.valid? + end + end + + def resource_params + params.require(:user).permit(:name, :email, :password, :password_confirmation) + end + +end diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb new file mode 100644 index 00000000..7d22330c --- /dev/null +++ b/app/controllers/sessions_controller.rb @@ -0,0 +1,3 @@ +class SessionsController < Devise::SessionsController + skip_before_filter :authenticate +end diff --git a/app/models/user.rb b/app/models/user.rb index 0a38b049..aee86814 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -7,6 +7,40 @@ class User < ActiveRecord::Base has_many :authentications - # validates :name, presence: true + validates :name, presence: true validates :email, presence: true + + # TODO: extract to module and then to a gem / engine + def apply_omniauth(omniauth) + provider, uid, info = omniauth.values_at('provider', 'uid', 'info') + self.email = info['email'] if email.blank? + self.name = info['name'] if name.blank? + + apply_provider_handle(omniauth) + authentications.build(provider: provider, uid: uid) + end + + def apply_provider_handle(omniauth) + provider, info = omniauth.values_at('provider', 'info') + + case provider + when /github/ + self.github_handle = info['nickname'] if github_handle.blank? + when /twitter/ + self.twitter_handle = info['nickname'] if twitter_handle.blank? + end + end + + def password_required? + (authentications.empty? || password.present?) && super + end + + def connected_with_twitter? + authentications.where(provider: 'twitter').first + end + + def connected_with_github? + authentications.where(provider: 'github').first + end + end diff --git a/config/routes.rb b/config/routes.rb index 735ea8e4..77800f46 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,6 +1,12 @@ Activities::Application.routes.draw do - devise_for :users - resources :activities + get '/auth/:provider/callback' => 'authentications#create' + + devise_for :users, controllers: { + registrations: 'registrations', + sessions: 'sessions', + passwords: 'passwords' + } + resources :activities root to: 'activities#index' end From 4bef1b578b2811cec4d36c5a1cfaa0bd122ece20 Mon Sep 17 00:00:00 2001 From: pietia Date: Wed, 8 May 2013 14:59:13 +0200 Subject: [PATCH 046/499] faster impl. --- app/models/user.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/models/user.rb b/app/models/user.rb index aee86814..9aae659f 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -36,11 +36,11 @@ def password_required? end def connected_with_twitter? - authentications.where(provider: 'twitter').first + authentications.where(provider: 'twitter').any? end def connected_with_github? - authentications.where(provider: 'github').first + authentications.where(provider: 'github').any? end end From e6ac60cc07259ba8b23fb185005012b856b09b3e Mon Sep 17 00:00:00 2001 From: pietia Date: Wed, 8 May 2013 15:01:07 +0200 Subject: [PATCH 047/499] fixed rescue_from --- app/controllers/authentications_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/authentications_controller.rb b/app/controllers/authentications_controller.rb index 3e7169a5..5936b4a7 100644 --- a/app/controllers/authentications_controller.rb +++ b/app/controllers/authentications_controller.rb @@ -2,7 +2,7 @@ class AuthenticationsController < ApplicationController skip_before_filter :authenticate rescue_from ActionController::RedirectBackError do |exception| - redirect_to profile_url + redirect_to edit_user_registration_path end def create From 76292447dd1da430c68d1d45df162ab3fe4a9bb5 Mon Sep 17 00:00:00 2001 From: pietia Date: Wed, 8 May 2013 16:29:43 +0200 Subject: [PATCH 048/499] more info about alias --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 0a1ebf63..c588632f 100644 --- a/README.md +++ b/README.md @@ -4,12 +4,14 @@ ### Basic setup +* `echo 'activities.dev localhost' > /etc/hosts` * `cp .env.sample .env` * `cp config/database.yml.sample config/database.yml` * update config files: `config/application.yml`, `.env` (see ENV variables listed below) * run migration scripts * `bundle exec foreman start` * `tail -f log/development.log` +* `open http://activities.dev:3000` ## Deployment From 4c0072bf53701e84f893dac304c7b5bee86edaa9 Mon Sep 17 00:00:00 2001 From: pietia Date: Wed, 8 May 2013 16:30:35 +0200 Subject: [PATCH 049/499] added simple_form's translations, added registration link texts --- config/locales/en.yml | 3 +++ config/locales/simple_form.en.yml | 26 ++++++++++++++++++++++++++ 2 files changed, 29 insertions(+) create mode 100644 config/locales/simple_form.en.yml diff --git a/config/locales/en.yml b/config/locales/en.yml index e53cd600..54f4ead0 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -6,6 +6,9 @@ en: logout: label: Logout title: Leave now + register: + label: Register + title: Register new account footer_nav: diff --git a/config/locales/simple_form.en.yml b/config/locales/simple_form.en.yml new file mode 100644 index 00000000..0df11fe0 --- /dev/null +++ b/config/locales/simple_form.en.yml @@ -0,0 +1,26 @@ +en: + simple_form: + "yes": 'Yes' + "no": 'No' + required: + text: 'required' + mark: '*' + # You can uncomment the line below if you need to overwrite the whole required html. + # When using html, text and mark won't be used. + # html: '*' + error_notification: + default_message: "Please review the problems below:" + # Labels and hints examples + # labels: + # defaults: + # password: 'Password' + # user: + # new: + # email: 'E-mail to sign in.' + # edit: + # email: 'E-mail.' + # hints: + # defaults: + # username: 'User name to sign in.' + # password: 'No special characters, please.' + From 20bbd5707a7624cc8fe426351a20c998f86f3d6b Mon Sep 17 00:00:00 2001 From: pietia Date: Wed, 8 May 2013 16:31:27 +0200 Subject: [PATCH 050/499] basic initializers for simple_form (stolen from c4p app) --- config/initializers/simple_form.rb | 144 +++++++++++++++++++ config/initializers/simple_form_bootstrap.rb | 45 ++++++ 2 files changed, 189 insertions(+) create mode 100644 config/initializers/simple_form.rb create mode 100644 config/initializers/simple_form_bootstrap.rb diff --git a/config/initializers/simple_form.rb b/config/initializers/simple_form.rb new file mode 100644 index 00000000..0b29ab76 --- /dev/null +++ b/config/initializers/simple_form.rb @@ -0,0 +1,144 @@ +# Use this setup block to configure all options available in SimpleForm. +SimpleForm.setup do |config| + # Wrappers are used by the form builder to generate a + # complete input. You can remove any component from the + # wrapper, change the order or even add your own to the + # stack. The options given below are used to wrap the + # whole input. + config.wrappers :default, :class => :input, + :hint_class => :field_with_hint, :error_class => :field_with_errors do |b| + ## Extensions enabled by default + # Any of these extensions can be disabled for a + # given input by passing: `f.input EXTENSION_NAME => false`. + # You can make any of these extensions optional by + # renaming `b.use` to `b.optional`. + + # Determines whether to use HTML5 (:email, :url, ...) + # and required attributes + b.use :html5 + + # Calculates placeholders automatically from I18n + # You can also pass a string as f.input :placeholder => "Placeholder" + b.use :placeholder + + ## Optional extensions + # They are disabled unless you pass `f.input EXTENSION_NAME => :lookup` + # to the input. If so, they will retrieve the values from the model + # if any exists. If you want to enable the lookup for any of those + # extensions by default, you can change `b.optional` to `b.use`. + + # Calculates maxlength from length validations for string inputs + b.optional :maxlength + + # Calculates pattern from format validations for string inputs + b.optional :pattern + + # Calculates min and max from length validations for numeric inputs + b.optional :min_max + + # Calculates readonly automatically from readonly attributes + b.optional :readonly + + ## Inputs + b.use :label_input + b.use :hint, :wrap_with => { :tag => :span, :class => :hint } + b.use :error, :wrap_with => { :tag => :span, :class => :error } + end + + # The default wrapper to be used by the FormBuilder. + config.default_wrapper = :default + + # Define the way to render check boxes / radio buttons with labels. + # Defaults to :nested for bootstrap config. + # :inline => input + label + # :nested => label > input + config.boolean_style = :nested + + # Default class for buttons + config.button_class = 'btn' + + # Method used to tidy up errors. Specify any Rails Array method. + # :first lists the first message for each field. + # Use :to_sentence to list all errors for each field. + # config.error_method = :first + + # Default tag used for error notification helper. + config.error_notification_tag = :div + + # CSS class to add for error notification helper. + config.error_notification_class = 'alert alert-error' + + # ID to add for error notification helper. + # config.error_notification_id = nil + + # Series of attempts to detect a default label method for collection. + # config.collection_label_methods = [ :to_label, :name, :title, :to_s ] + + # Series of attempts to detect a default value method for collection. + # config.collection_value_methods = [ :id, :to_s ] + + # You can wrap a collection of radio/check boxes in a pre-defined tag, defaulting to none. + # config.collection_wrapper_tag = nil + + # You can define the class to use on all collection wrappers. Defaulting to none. + # config.collection_wrapper_class = nil + + # You can wrap each item in a collection of radio/check boxes with a tag, + # defaulting to :span. Please note that when using :boolean_style = :nested, + # SimpleForm will force this option to be a label. + # config.item_wrapper_tag = :span + + # You can define a class to use in all item wrappers. Defaulting to none. + # config.item_wrapper_class = nil + + # How the label text should be generated altogether with the required text. + config.label_text = lambda { |label, required| + required.empty? ? "#{label}" : "#{label}" + } + + # You can define the class to use on all labels. Default is nil. + config.label_class = 'control-label' + + # You can define the class to use on all forms. Default is simple_form. + # config.form_class = :simple_form + + # You can define which elements should obtain additional classes + # config.generate_additional_classes_for = [:wrapper, :label, :input] + + # Whether attributes are required by default (or not). Default is true. + # config.required_by_default = true + + # Tell browsers whether to use default HTML5 validations (novalidate option). + # Default is enabled. + config.browser_validations = false + + # Collection of methods to detect if a file type was given. + # config.file_methods = [ :mounted_as, :file?, :public_filename ] + + # Custom mappings for input types. This should be a hash containing a regexp + # to match as key, and the input type that will be used when the field name + # matches the regexp as value. + # config.input_mappings = { /count/ => :integer } + + # Custom wrappers for input types. This should be a hash containing an input + # type as key and the wrapper that will be used for all inputs with specified type. + # config.wrapper_mappings = { :string => :prepend } + + # Default priority for time_zone inputs. + # config.time_zone_priority = nil + + # Default priority for country inputs. + # config.country_priority = nil + + # Default size for text inputs. + # config.default_input_size = 50 + + # When false, do not use translations for labels. + # config.translate_labels = true + + # Automatically discover new inputs in Rails' autoload path. + # config.inputs_discovery = true + + # Cache SimpleForm inputs discovery + # config.cache_discovery = !Rails.env.development? +end diff --git a/config/initializers/simple_form_bootstrap.rb b/config/initializers/simple_form_bootstrap.rb new file mode 100644 index 00000000..14a8c2a7 --- /dev/null +++ b/config/initializers/simple_form_bootstrap.rb @@ -0,0 +1,45 @@ +# Use this setup block to configure all options available in SimpleForm. +SimpleForm.setup do |config| + config.wrappers :bootstrap, :tag => 'div', :class => 'control-group', :error_class => 'error' do |b| + b.use :html5 + b.use :placeholder + b.use :label + b.wrapper :tag => 'div', :class => 'controls' do |ba| + ba.use :hint, :wrap_with => { :tag => 'p', :class => 'help-block' } + ba.use :input + ba.use :error, :wrap_with => { :tag => 'span', :class => 'help-inline' } + end + end + + config.wrappers :prepend, :tag => 'div', :class => "control-group", :error_class => 'error' do |b| + b.use :html5 + b.use :placeholder + b.use :label + b.wrapper :tag => 'div', :class => 'controls' do |input| + input.wrapper :tag => 'div', :class => 'input-prepend' do |prepend| + prepend.use :input + end + input.use :hint, :wrap_with => { :tag => 'span', :class => 'help-block' } + input.use :error, :wrap_with => { :tag => 'span', :class => 'help-inline' } + end + end + + config.wrappers :append, :tag => 'div', :class => "control-group", :error_class => 'error' do |b| + b.use :html5 + b.use :placeholder + b.use :label + b.wrapper :tag => 'div', :class => 'controls' do |input| + input.wrapper :tag => 'div', :class => 'input-append' do |append| + append.use :input + end + input.use :error, :wrap_with => { :tag => 'span', :class => 'help-inline' } + input.use :hint, :wrap_with => { :tag => 'span', :class => 'help-block' } + end + end + + # Wrappers for forms and inputs using the Twitter Bootstrap toolkit. + # Check the Bootstrap docs (http://twitter.github.com/bootstrap) + # to learn about the different styles for forms and inputs, + # buttons and other elements. + config.default_wrapper = :bootstrap +end From c688d8b81c743e4c007eeec14fb5a825f4bdb9fc Mon Sep 17 00:00:00 2001 From: pietia Date: Wed, 8 May 2013 16:31:54 +0200 Subject: [PATCH 051/499] use activities.dev:3000 as a host --- config/application.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/application.yml b/config/application.yml index b479e087..d03eecf3 100644 --- a/config/application.yml +++ b/config/application.yml @@ -10,7 +10,7 @@ defaults: &defaults development: <<: *defaults - host: "localhost:3000" + host: "activities.dev:3000" test: <<: *defaults From 2bdc9bafe6e4ae7487687f2570349ac45ef6b35a Mon Sep 17 00:00:00 2001 From: pietia Date: Wed, 8 May 2013 16:32:05 +0200 Subject: [PATCH 052/499] basic work on auth --- app/controllers/registrations_controller.rb | 10 ++++---- app/models/user.rb | 2 +- app/views/authentications/index.html.erb | 11 +++++++++ app/views/layouts/application.html.haml | 2 ++ app/views/partials/_header.html.haml | 21 ++++++++++------- app/views/passwords/new.html.erb | 22 +++++++++++++++++ app/views/registrations/new.html.erb | 26 +++++++++++++++++++++ app/views/sessions/new.html.erb | 24 +++++++++++++++++++ config/routes.rb | 1 + 9 files changed, 105 insertions(+), 14 deletions(-) create mode 100644 app/views/authentications/index.html.erb create mode 100644 app/views/passwords/new.html.erb create mode 100644 app/views/registrations/new.html.erb create mode 100644 app/views/sessions/new.html.erb diff --git a/app/controllers/registrations_controller.rb b/app/controllers/registrations_controller.rb index 8fa7ea6f..6081425d 100644 --- a/app/controllers/registrations_controller.rb +++ b/app/controllers/registrations_controller.rb @@ -8,10 +8,11 @@ def create private def build_resource(*args) - super - if session[:omniauth] - @user.apply_omniauth(session[:omniauth]) - @user.valid? + super.tap do |user| + if user && session[:omniauth] + user.apply_omniauth(session[:omniauth]) + user.valid? + end end end @@ -19,4 +20,5 @@ def resource_params params.require(:user).permit(:name, :email, :password, :password_confirmation) end + end diff --git a/app/models/user.rb b/app/models/user.rb index 9aae659f..5ade0d89 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -7,7 +7,7 @@ class User < ActiveRecord::Base has_many :authentications - validates :name, presence: true + #validates :name, presence: true validates :email, presence: true # TODO: extract to module and then to a gem / engine diff --git a/app/views/authentications/index.html.erb b/app/views/authentications/index.html.erb new file mode 100644 index 00000000..244ee4ed --- /dev/null +++ b/app/views/authentications/index.html.erb @@ -0,0 +1,11 @@ + + +

+ You can also + <%= link_to 'sign up', new_registration_path(:user) %> + or + <%= link_to 'sign in with password', new_session_path(:user) %>. +

diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml index 04c6c29e..43cb9e68 100644 --- a/app/views/layouts/application.html.haml +++ b/app/views/layouts/application.html.haml @@ -8,6 +8,8 @@ %meta(content="" name="author") %meta(content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0" name="viewport") + = csrf_meta_tags + = stylesheet_link_tag "application" = javascript_include_tag "modernizr.min" diff --git a/app/views/partials/_header.html.haml b/app/views/partials/_header.html.haml index 929bea20..c4b1225d 100644 --- a/app/views/partials/_header.html.haml +++ b/app/views/partials/_header.html.haml @@ -1,14 +1,17 @@ %header#header - -# user navigation, don't show unless logged-in - %nav.user-nav - %ul - %li - %a(href="#" title="#{t("user_nav.account.title")}") - = t("user_nav.account.label") - %li - %a(href="#" title="#{t("user_nav.logout.title")}") - = t("user_nav.logout.label") + - if current_user + %nav.user-nav + %ul + %li + = link_to t("user_nav.account.label"), edit_user_registration_path, title: t("user_nav.account.title") + %li + = link_to t("user_nav.logout.label"), destroy_user_session_path, title: t("user_nav.logout.title"), method: "delete" + - else + %nav.user-nav + %ul + %li + = link_to t("user_nav.register.label"), new_user_registration_path, title: t("user_nav.register.title") -# the logo, link home %h1 diff --git a/app/views/passwords/new.html.erb b/app/views/passwords/new.html.erb new file mode 100644 index 00000000..b0b05540 --- /dev/null +++ b/app/views/passwords/new.html.erb @@ -0,0 +1,22 @@ +
+ <%= simple_form_for(resource, :as => resource_name, :url => password_path(resource_name), :html => { :method => :post }) do |f| %> + <%= f.error_notification %> + +
+ <%= f.input :email, :required => true %> +
+ +
+ <%= f.button :submit, "Send me reset password instructions", class: 'btn btn-success' %> +
+ <% end %> +
+ +

+ You can also + <%= link_to 'sign up', new_registration_path(:user) %> + if you don't have account yet, + <%= link_to 'sign in with password', new_session_path(:user) %> + or better + <%= link_to 'sign in with oauth', authentications_path %>. +

diff --git a/app/views/registrations/new.html.erb b/app/views/registrations/new.html.erb new file mode 100644 index 00000000..0ede4fe3 --- /dev/null +++ b/app/views/registrations/new.html.erb @@ -0,0 +1,26 @@ +
+ <%= simple_form_for(resource, :as => resource_name, :url => registration_path(resource_name)) do |f| %> + <%= f.error_notification %> + +
+ <%= f.input :name, :required => true, :autofocus => true %> + <%= f.input :email, :required => true %> + + <% if @user.password_required? %> + <%= f.input :password, :required => true %> + <%= f.input :password_confirmation, :required => true %> + <% end %> +
+ +
+ <%= f.button :submit, "Sign up", :class => 'btn btn-success' %> +
+ <% end %> +
+ +

+ You can also + <%= link_to 'sign in with password', new_session_path(:user) %> + or + <%= link_to 'sign in with oauth', authentications_path %>. +

diff --git a/app/views/sessions/new.html.erb b/app/views/sessions/new.html.erb new file mode 100644 index 00000000..0b91cdcd --- /dev/null +++ b/app/views/sessions/new.html.erb @@ -0,0 +1,24 @@ +
+ <%= simple_form_for(resource, as: resource_name, url: session_path(resource_name)) do |f| %> + <%= f.error_notification %> + +
+ <%= f.input :email, required: true, autofocus: true %> + <%= f.input :password, required: true %> + <%= f.input :remember_me, as: :boolean %> +
+ +
+ <%= f.button :submit, "Sign in", class: 'btn btn-success' %> +
+ <% end %> +
+ +

+ You can also + <%= link_to 'sign up', new_registration_path(:user) %> + if you don't have account yet, + <%= link_to 'reset lost password', new_password_path(:user) %> + or + <%= link_to 'sign in with oauth', authentications_path %>. +

diff --git a/config/routes.rb b/config/routes.rb index 77800f46..dc70f786 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,4 +1,5 @@ Activities::Application.routes.draw do + resources :authentications get '/auth/:provider/callback' => 'authentications#create' devise_for :users, controllers: { From b362889f5907f1033c23e669a66f47142ab4c93b Mon Sep 17 00:00:00 2001 From: pietia Date: Wed, 8 May 2013 16:48:45 +0200 Subject: [PATCH 053/499] oops - we don't need that file --- config/initializers/omniauth.rb | 4 ---- 1 file changed, 4 deletions(-) delete mode 100644 config/initializers/omniauth.rb diff --git a/config/initializers/omniauth.rb b/config/initializers/omniauth.rb deleted file mode 100644 index e0dd9da3..00000000 --- a/config/initializers/omniauth.rb +++ /dev/null @@ -1,4 +0,0 @@ -Rails.application.config.middleware.use OmniAuth::Builder do - provider :twitter, ENV['TWITTER_KEY'], ENV['TWITTER_SECRET'] - provider :github, ENV['GITHUB_KEY'], ENV['GITHUB_SECRET'], scope: 'user:email' -end From 6fbf2622d7853ca9aaaa9fe8d84a9fa84a5c332d Mon Sep 17 00:00:00 2001 From: pietia Date: Thu, 9 May 2013 10:24:48 +0200 Subject: [PATCH 054/499] specified gems versions --- Gemfile | 5 +++-- Gemfile.lock | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/Gemfile b/Gemfile index 748aca8f..9ccc486c 100644 --- a/Gemfile +++ b/Gemfile @@ -8,8 +8,9 @@ gem 'unicorn' gem 'settingslogic' gem 'newrelic_rpm' gem 'devise', '~> 3.0.0.rc' -gem 'omniauth-github' -gem 'omniauth-twitter' +gem 'omniauth', '~> 1.1.4' +gem 'omniauth-github', '~> 1.1.0' +gem 'omniauth-twitter', '~> 0.0.16' gem 'simple_form', '~> 3.0.0.beta1' gem 'jquery-rails' diff --git a/Gemfile.lock b/Gemfile.lock index 1bedcb55..6db42a8d 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -276,8 +276,9 @@ DEPENDENCIES jquery-rails neat newrelic_rpm - omniauth-github - omniauth-twitter + omniauth (~> 1.1.4) + omniauth-github (~> 1.1.0) + omniauth-twitter (~> 0.0.16) pg rack-robotz (~> 0.0.3) rails (= 4.0.0.rc1) From 85df12eb8bc7c72c166feabd9a2e89cdd00f58e3 Mon Sep 17 00:00:00 2001 From: pietia Date: Thu, 9 May 2013 10:25:03 +0200 Subject: [PATCH 055/499] added login link --- app/controllers/registrations_controller.rb | 20 ++++++++++---------- app/models/user.rb | 10 ++++++++++ app/views/partials/_header.html.haml | 2 ++ config/locales/en.yml | 3 +++ 4 files changed, 25 insertions(+), 10 deletions(-) diff --git a/app/controllers/registrations_controller.rb b/app/controllers/registrations_controller.rb index 6081425d..88ab7c21 100644 --- a/app/controllers/registrations_controller.rb +++ b/app/controllers/registrations_controller.rb @@ -7,18 +7,18 @@ def create end private - def build_resource(*args) - super.tap do |user| - if user && session[:omniauth] - user.apply_omniauth(session[:omniauth]) - user.valid? + + def build_resource(*args) + super.tap do |user| + if user && session[:omniauth] + user.apply_omniauth(session[:omniauth]) + user.valid? + end end end - end - - def resource_params - params.require(:user).permit(:name, :email, :password, :password_confirmation) - end + def resource_params + params.require(:user).permit(:name, :email, :password, :password_confirmation) + end end diff --git a/app/models/user.rb b/app/models/user.rb index 5ade0d89..a445c079 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -35,6 +35,10 @@ def password_required? (authentications.empty? || password.present?) && super end + def oauth_only? + encrypted_password.blank? && any_oauth? + end + def connected_with_twitter? authentications.where(provider: 'twitter').any? end @@ -43,4 +47,10 @@ def connected_with_github? authentications.where(provider: 'github').any? end + private + + def any_oauth? + connected_with_github? || connected_with_twitter? + end + end diff --git a/app/views/partials/_header.html.haml b/app/views/partials/_header.html.haml index c4b1225d..ef7296e1 100644 --- a/app/views/partials/_header.html.haml +++ b/app/views/partials/_header.html.haml @@ -10,6 +10,8 @@ - else %nav.user-nav %ul + %li + = link_to t("user_nav.login.label"), new_user_session_path, title: t("user_nav.login.title") %li = link_to t("user_nav.register.label"), new_user_registration_path, title: t("user_nav.register.title") diff --git a/config/locales/en.yml b/config/locales/en.yml index 54f4ead0..a7241c07 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -3,6 +3,9 @@ en: account: label: My Account title: Edit your account details + login: + label: Login + title: Login into app! logout: label: Logout title: Leave now From 2f7cf753eb4b1063a9340b8eb3e0e50840de0057 Mon Sep 17 00:00:00 2001 From: pietia Date: Thu, 9 May 2013 14:16:55 +0200 Subject: [PATCH 056/499] views with forms migrated to haml --- app/controllers/registrations_controller.rb | 30 ++++++++++++++++++--- app/models/user.rb | 16 +++++------ app/views/authentications/index.html.erb | 11 -------- app/views/passwords/new.html.erb | 22 --------------- app/views/passwords/new.html.haml | 13 +++++++++ app/views/registrations/edit.html.haml | 18 +++++++++++++ app/views/registrations/new.html.erb | 26 ------------------ app/views/registrations/new.html.haml | 22 +++++++++++++++ app/views/sessions/new.html.erb | 24 ----------------- app/views/sessions/new.html.haml | 19 +++++++++++++ config/routes.rb | 2 +- 11 files changed, 107 insertions(+), 96 deletions(-) delete mode 100644 app/views/authentications/index.html.erb delete mode 100644 app/views/passwords/new.html.erb create mode 100644 app/views/passwords/new.html.haml create mode 100644 app/views/registrations/edit.html.haml delete mode 100644 app/views/registrations/new.html.erb create mode 100644 app/views/registrations/new.html.haml delete mode 100644 app/views/sessions/new.html.erb create mode 100644 app/views/sessions/new.html.haml diff --git a/app/controllers/registrations_controller.rb b/app/controllers/registrations_controller.rb index 88ab7c21..a4690082 100644 --- a/app/controllers/registrations_controller.rb +++ b/app/controllers/registrations_controller.rb @@ -1,11 +1,29 @@ class RegistrationsController < Devise::RegistrationsController - skip_before_filter :authenticate + skip_before_filter :authenticate, only: :create def create super session[:omniauth] = nil unless @user.new_record? end + def update + self.resource = resource_class.to_adapter.get!(send(:"current_#{resource_name}").to_key) + prev_unconfirmed_email = resource.unconfirmed_email if resource.respond_to?(:unconfirmed_email) + + if update_resource(params) + if is_navigational_format? + flash_key = update_needs_confirmation?(resource, prev_unconfirmed_email) ? + :update_needs_confirmation : :updated + set_flash_message :notice, flash_key + end + sign_in resource_name, resource, :bypass => true + respond_with resource, :location => after_update_path_for(resource) + else + clean_up_passwords resource + respond_with resource + end + end + private def build_resource(*args) @@ -17,8 +35,14 @@ def build_resource(*args) end end - def resource_params - params.require(:user).permit(:name, :email, :password, :password_confirmation) + def update_resource(params) + if resource.no_oauth_connected? + paramz = params.require(:user).permit(:name, :email, :password, :password_confirmation, :current_password) + resource.update_with_password(paramz) + else + paramz = params.require(:user).permit(:name, :email) + resource.update_without_password(paramz) + end end end diff --git a/app/models/user.rb b/app/models/user.rb index a445c079..d1fefe6e 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -32,11 +32,15 @@ def apply_provider_handle(omniauth) end def password_required? - (authentications.empty? || password.present?) && super + !any_oauth_connected? && super end - def oauth_only? - encrypted_password.blank? && any_oauth? + def no_oauth_connected? + !any_oauth_connected? && encrypted_password.present? + end + + def any_oauth_connected? + authentications.any? end def connected_with_twitter? @@ -47,10 +51,4 @@ def connected_with_github? authentications.where(provider: 'github').any? end - private - - def any_oauth? - connected_with_github? || connected_with_twitter? - end - end diff --git a/app/views/authentications/index.html.erb b/app/views/authentications/index.html.erb deleted file mode 100644 index 244ee4ed..00000000 --- a/app/views/authentications/index.html.erb +++ /dev/null @@ -1,11 +0,0 @@ - - -

- You can also - <%= link_to 'sign up', new_registration_path(:user) %> - or - <%= link_to 'sign in with password', new_session_path(:user) %>. -

diff --git a/app/views/passwords/new.html.erb b/app/views/passwords/new.html.erb deleted file mode 100644 index b0b05540..00000000 --- a/app/views/passwords/new.html.erb +++ /dev/null @@ -1,22 +0,0 @@ -
- <%= simple_form_for(resource, :as => resource_name, :url => password_path(resource_name), :html => { :method => :post }) do |f| %> - <%= f.error_notification %> - -
- <%= f.input :email, :required => true %> -
- -
- <%= f.button :submit, "Send me reset password instructions", class: 'btn btn-success' %> -
- <% end %> -
- -

- You can also - <%= link_to 'sign up', new_registration_path(:user) %> - if you don't have account yet, - <%= link_to 'sign in with password', new_session_path(:user) %> - or better - <%= link_to 'sign in with oauth', authentications_path %>. -

diff --git a/app/views/passwords/new.html.haml b/app/views/passwords/new.html.haml new file mode 100644 index 00000000..8982cf7f --- /dev/null +++ b/app/views/passwords/new.html.haml @@ -0,0 +1,13 @@ +.auth + = simple_form_for(resource, :as => resource_name, :url => password_path(resource_name), :html => { :method => :post }) do |f| + = f.error_notification + .inputs + = f.input :email, :required => true + .actions + = f.button :submit, "Send me reset password instructions", class: 'btn btn-success' + +%p.hint + You can also + = link_to 'sign up', new_registration_path(:user) + if you don't have account yet + diff --git a/app/views/registrations/edit.html.haml b/app/views/registrations/edit.html.haml new file mode 100644 index 00000000..8f347e36 --- /dev/null +++ b/app/views/registrations/edit.html.haml @@ -0,0 +1,18 @@ +%h1 Edit profile + +.profile + = simple_form_for @user, :url => registration_path(resource_name) do |f| + = f.error_notification + = devise_error_messages! + + .inputs + = f.input :name, :hint => "Your name or nick" + = f.input :email + + - if @user.no_oauth_connected? + = f.input :password, :required => true + = f.input :password_confirmation, :required => true + = f.input :current_password, :required => true + + .actions + = f.button :submit, class: 'btn btn-info' diff --git a/app/views/registrations/new.html.erb b/app/views/registrations/new.html.erb deleted file mode 100644 index 0ede4fe3..00000000 --- a/app/views/registrations/new.html.erb +++ /dev/null @@ -1,26 +0,0 @@ -
- <%= simple_form_for(resource, :as => resource_name, :url => registration_path(resource_name)) do |f| %> - <%= f.error_notification %> - -
- <%= f.input :name, :required => true, :autofocus => true %> - <%= f.input :email, :required => true %> - - <% if @user.password_required? %> - <%= f.input :password, :required => true %> - <%= f.input :password_confirmation, :required => true %> - <% end %> -
- -
- <%= f.button :submit, "Sign up", :class => 'btn btn-success' %> -
- <% end %> -
- -

- You can also - <%= link_to 'sign in with password', new_session_path(:user) %> - or - <%= link_to 'sign in with oauth', authentications_path %>. -

diff --git a/app/views/registrations/new.html.haml b/app/views/registrations/new.html.haml new file mode 100644 index 00000000..ee59b74c --- /dev/null +++ b/app/views/registrations/new.html.haml @@ -0,0 +1,22 @@ +.auth + = simple_form_for(resource, :as => resource_name, :url => registration_path(resource_name)) do |f| + = f.error_notification + + .inputs + = f.input :name, :required => true, :autofocus => true + = f.input :email, :required => true + + - if @user.password_required? + = f.input :password, :required => true + = f.input :password_confirmation, :required => true + + .actions + = f.button :submit, "Sign up", :class => 'btn btn-success' + + = link_to "Sign in with Github", "/auth/github", class: "btn-auth btn-github large" + = link_to "Sign in with Twitter", "/auth/twitter", class: "btn-auth btn-twitter large" + +%p.hint + Have an account already? + = link_to 'sign in with password', new_session_path(:user) + diff --git a/app/views/sessions/new.html.erb b/app/views/sessions/new.html.erb deleted file mode 100644 index 0b91cdcd..00000000 --- a/app/views/sessions/new.html.erb +++ /dev/null @@ -1,24 +0,0 @@ -
- <%= simple_form_for(resource, as: resource_name, url: session_path(resource_name)) do |f| %> - <%= f.error_notification %> - -
- <%= f.input :email, required: true, autofocus: true %> - <%= f.input :password, required: true %> - <%= f.input :remember_me, as: :boolean %> -
- -
- <%= f.button :submit, "Sign in", class: 'btn btn-success' %> -
- <% end %> -
- -

- You can also - <%= link_to 'sign up', new_registration_path(:user) %> - if you don't have account yet, - <%= link_to 'reset lost password', new_password_path(:user) %> - or - <%= link_to 'sign in with oauth', authentications_path %>. -

diff --git a/app/views/sessions/new.html.haml b/app/views/sessions/new.html.haml new file mode 100644 index 00000000..3659cc4d --- /dev/null +++ b/app/views/sessions/new.html.haml @@ -0,0 +1,19 @@ +.auth + = simple_form_for(resource, as: resource_name, url: session_path(resource_name)) do |f| + = f.error_notification + .inputs + = f.input :email, required: true, autofocus: true + = f.input :password, required: true + .actions + = f.button :submit, "Sign in", class: 'btn btn-success' + + = link_to "Sign in with Github", "/auth/github", class: "btn-auth btn-github large" + = link_to "Sign in with Twitter", "/auth/twitter", class: "btn-auth btn-twitter large" + +%p.hint + You can also + = link_to 'sign up', new_registration_path(:user) + if you don't have account yet, + = link_to 'reset lost password', new_password_path(:user) + + diff --git a/config/routes.rb b/config/routes.rb index dc70f786..1d1908e7 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,5 +1,5 @@ Activities::Application.routes.draw do - resources :authentications + resources :authentications, only: [:create, :destroy] get '/auth/:provider/callback' => 'authentications#create' devise_for :users, controllers: { From e1034db80b0af355e7eecd5f627cbff3357b17ae Mon Sep 17 00:00:00 2001 From: pietia Date: Thu, 9 May 2013 15:04:39 +0200 Subject: [PATCH 057/499] more stolen helpers --- app/helpers/application_helper.rb | 26 ++++++++++++++++++++++++++ app/views/registrations/edit.html.haml | 13 +++++++++++++ 2 files changed, 39 insertions(+) diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index de6be794..80639136 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -1,2 +1,28 @@ module ApplicationHelper + def twitter_link(user) + profile_link("http://twitter.com", user.twitter_handle) + end + + def github_link(user) + profile_link("http://github.com", user.github_handle) + end + + def profile_link(base_url, handle) + handle = h(handle.to_s.sub(/\A@/, '')) + link_to(truncate(handle, length: 20), "#{base_url}/#{handle}") unless handle.blank? + end + + def gravatar_avatar_url(user, size) + gravatar_id = Digest::MD5::hexdigest(user.email).downcase + "http://gravatar.com/avatar/#{gravatar_id}.png?s=#{size}" + end + + def twitter_avatar_url(handle) + "https://twitter.com/api/users/profile_image/#{handle}?size=bigger" + end + + def avatar_url(user, size = 64) + return twitter_avatar_url(user.twitter_handle) if user.twitter_handle + gravatar_avatar_url(user, size) + end end diff --git a/app/views/registrations/edit.html.haml b/app/views/registrations/edit.html.haml index 8f347e36..d635da91 100644 --- a/app/views/registrations/edit.html.haml +++ b/app/views/registrations/edit.html.haml @@ -5,6 +5,9 @@ = f.error_notification = devise_error_messages! + .avatar + = image_tag(avatar_url(@user, 64)) + .inputs = f.input :name, :hint => "Your name or nick" = f.input :email @@ -14,5 +17,15 @@ = f.input :password_confirmation, :required => true = f.input :current_password, :required => true + - if @user.connected_with_github? + = github_link(@user) + - else + = link_to "connect with Github", "/auth/github", class: "btn-auth btn-github" + + - if @user.connected_with_twitter? + = twitter_link(@user) + - else + = link_to "connect with Twitter", "/auth/twitter", class: "btn-auth btn-github" + .actions = f.button :submit, class: 'btn btn-info' From 316094331b7cc0d47da4ebb774b58913bf48282a Mon Sep 17 00:00:00 2001 From: pietia Date: Sun, 12 May 2013 21:34:44 +0200 Subject: [PATCH 058/499] basic User factory --- spec/factories/sequences.rb | 3 +++ spec/factories/users.rb | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 spec/factories/sequences.rb diff --git a/spec/factories/sequences.rb b/spec/factories/sequences.rb new file mode 100644 index 00000000..22d304ec --- /dev/null +++ b/spec/factories/sequences.rb @@ -0,0 +1,3 @@ +FactoryGirl.define do + sequence(:email) { |n| "person#{n}@example.com" } +end \ No newline at end of file diff --git a/spec/factories/users.rb b/spec/factories/users.rb index 8dbc0372..8f488ca6 100644 --- a/spec/factories/users.rb +++ b/spec/factories/users.rb @@ -2,6 +2,7 @@ FactoryGirl.define do factory :user do - + email { generate(:email) } + password "qweqweqwe" end end From 47425468faaa161fd1224932953dda114bad3f82 Mon Sep 17 00:00:00 2001 From: pietia Date: Sun, 12 May 2013 22:22:43 +0200 Subject: [PATCH 059/499] some basic specs --- spec/models/activity_spec.rb | 30 ++++++++++++++++++++++++++++++ spec/models/user_spec.rb | 24 +++++++++++++++++++++++- 2 files changed, 53 insertions(+), 1 deletion(-) create mode 100644 spec/models/activity_spec.rb diff --git a/spec/models/activity_spec.rb b/spec/models/activity_spec.rb new file mode 100644 index 00000000..3bf851f3 --- /dev/null +++ b/spec/models/activity_spec.rb @@ -0,0 +1,30 @@ +require 'spec_helper' + +describe Activity do + + describe "validations" do + + subject { Activity.new } + + it { should accept_values_for(:name, "Football game" ) } + it { should_not accept_values_for(:name, "", nil) } + specify { FactoryGirl.create(:activity).clone.save! }.to raise(ActiveRecord::RecordInvalid) + + it { should accept_values_for(:description, nil, "", "Wear some solid boots!")} + + it { should accept_values_for(:place, "football itch" ) } + it { should_not accept_values_for(:place, "", nil) } + + it { should accept_values_for(:start_at, Time.now) } + it { should_not accept_values_for(:start_at, "", nil) } + + # whole day activity? + it { should accept_values_for(:time_frame, 15, 30, 60, 120, 360 ) } # minutes + it { should_not accept_values_for(:time_frame, "", nil, 0, -10) } + + it { should accept_values_for(:participants_limit, nil, 12, 100) } + it { should_not accept_values_for(:participants_limit, -1, 0) } + + end + +end \ No newline at end of file diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 44032b48..522a995d 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -1,5 +1,27 @@ require 'spec_helper' describe User do - pending "add some examples to (or delete) #{__FILE__}" + + describe "validations" do + + subject { User.new } + + it { should accept_values_for(:email, "xx@xx.com" ) } + it { should_not accept_values_for(:email, "", nil, " x @ x.com", "123", "login@server." ) } + + it { should accept_values_for(:password, "qweqweqwe" ) } + it { should_not accept_values_for(:password, "qweqwe", nil, "") } + + it { should accept_values_for(:name, nil, "", "Florian Gęga") } + + end + + describe "methods" do + + subject { FactoryGirl.create(:user) } + + it { should respond_to :activities } + + end + end From 852ba3a9b5e109e41bef578a01b6c3fe164558b3 Mon Sep 17 00:00:00 2001 From: pietia Date: Mon, 13 May 2013 13:48:25 +0200 Subject: [PATCH 060/499] refactoring --- app/models/activity.rb | 12 ++++++++ app/models/event.rb | 30 +++++++++++++++++++ spec/models/activity_spec.rb | 58 ++++++++++++++++++++++++------------ spec/models/event_spec.rb | 32 ++++++++++++++++++++ 4 files changed, 113 insertions(+), 19 deletions(-) create mode 100644 app/models/activity.rb create mode 100644 app/models/event.rb create mode 100644 spec/models/event_spec.rb diff --git a/app/models/activity.rb b/app/models/activity.rb new file mode 100644 index 00000000..2db016be --- /dev/null +++ b/app/models/activity.rb @@ -0,0 +1,12 @@ +class Activity + attr_accessor :event, :name, :start_at + + def initialize(attrs = {}) + attrs.each do |k,v| send("#{k}=",v) end + end + + def recent + [] + end + +end \ No newline at end of file diff --git a/app/models/event.rb b/app/models/event.rb new file mode 100644 index 00000000..0e2569c8 --- /dev/null +++ b/app/models/event.rb @@ -0,0 +1,30 @@ +class Event + attr_writer :activity_source # DI + attr_reader :name + + def initialize(name, fetcher = ->{ Activity.recent }) + @name = name + @fetcher = fetcher + end + + def new_activity(*args) + activity_source.call(*args).tap do |activity| + activity.event = self + end + end + + def activities + fetch + end + + private + + def fetch + @fetcher.() + end + + def activity_source + @activity_source ||= Activity.public_method(:new) + end + +end \ No newline at end of file diff --git a/spec/models/activity_spec.rb b/spec/models/activity_spec.rb index 3bf851f3..41c57457 100644 --- a/spec/models/activity_spec.rb +++ b/spec/models/activity_spec.rb @@ -2,29 +2,49 @@ describe Activity do - describe "validations" do + let(:event) { mock(:event) } + subject(:activity) { Activity.new } - subject { Activity.new } + describe "#event" do + before do + activity.event = event + end - it { should accept_values_for(:name, "Football game" ) } - it { should_not accept_values_for(:name, "", nil) } - specify { FactoryGirl.create(:activity).clone.save! }.to raise(ActiveRecord::RecordInvalid) - - it { should accept_values_for(:description, nil, "", "Wear some solid boots!")} - - it { should accept_values_for(:place, "football itch" ) } - it { should_not accept_values_for(:place, "", nil) } - - it { should accept_values_for(:start_at, Time.now) } - it { should_not accept_values_for(:start_at, "", nil) } - - # whole day activity? - it { should accept_values_for(:time_frame, 15, 30, 60, 120, 360 ) } # minutes - it { should_not accept_values_for(:time_frame, "", nil, 0, -10) } + its(:event) { should == event } + end - it { should accept_values_for(:participants_limit, nil, 12, 100) } - it { should_not accept_values_for(:participants_limit, -1, 0) } + describe ".recent" do + let!(:old_activities) { [mock(:activity)] } + let!(:recent_activities) { [mock(:activity1), mock(:activity2)] } + subject { Activity.recent } + it { should == recent_activities } end + # + # describe "validations" do + # + # subject { Activity.new } + # + # it { should accept_values_for(:name, "Football game" ) } + # it { should_not accept_values_for(:name, "", nil) } + # specify { FactoryGirl.create(:activity).clone.save! }.to raise(ActiveRecord::RecordInvalid) + # + # it { should accept_values_for(:description, nil, "", "Wear some solid boots!")} + # + # it { should accept_values_for(:place, "football itch" ) } + # it { should_not accept_values_for(:place, "", nil) } + # + # it { should accept_values_for(:start_at, Time.now) } + # it { should_not accept_values_for(:start_at, "", nil) } + # + # # whole day activity? + # it { should accept_values_for(:time_frame, 15, 30, 60, 120, 360 ) } # minutes + # it { should_not accept_values_for(:time_frame, "", nil, 0, -10) } + # + # it { should accept_values_for(:participants_limit, nil, 12, 100) } + # it { should_not accept_values_for(:participants_limit, -1, 0) } + # + # end + # end \ No newline at end of file diff --git a/spec/models/event_spec.rb b/spec/models/event_spec.rb new file mode 100644 index 00000000..a48ff796 --- /dev/null +++ b/spec/models/event_spec.rb @@ -0,0 +1,32 @@ +require 'spec_helper' + +describe Event do + + subject(:event) { Event.new("Eurucamp") } + + describe "#new" do + its(:name) { should == "Eurucamp" } + end + + describe "#new_activity" do + let(:new_activity) { OpenStruct.new } + let(:args) { {} } + subject { event.new_activity(args) } + + before do + event.activity_source = ->(x){ new_activity } + end + + its(:event) { should == event } + it { should == new_activity } + end + + describe "#activities" do + let(:activities) { [mock(:activity1), mock(:activity2)] } + let(:event) { Event.new("Conf", ->{ activities }) } + subject { event.activities } + + it { should == activities } + end + +end \ No newline at end of file From 7a42b2fd661da0aeaeeeef6465d8351915c8979d Mon Sep 17 00:00:00 2001 From: pietia Date: Mon, 13 May 2013 14:12:19 +0200 Subject: [PATCH 061/499] basic tests for Activity --- app/models/activity.rb | 18 ++++--- .../20130513115254_create_activities.rb | 16 ++++++ db/schema.rb | 15 +++++- spec/factories/activities.rb | 8 +++ spec/models/activity_spec.rb | 54 +++++++++---------- spec/models/user_spec.rb | 8 --- 6 files changed, 76 insertions(+), 43 deletions(-) create mode 100644 db/migrate/20130513115254_create_activities.rb create mode 100644 spec/factories/activities.rb diff --git a/app/models/activity.rb b/app/models/activity.rb index 2db016be..22083854 100644 --- a/app/models/activity.rb +++ b/app/models/activity.rb @@ -1,12 +1,16 @@ -class Activity - attr_accessor :event, :name, :start_at +class Activity < ActiveRecord::Base + DEFAULT_LIMIT = 100 - def initialize(attrs = {}) - attrs.each do |k,v| send("#{k}=",v) end - end + attr_accessor :event + + validates :start_at, presence: true, allow_blank: false + validates :name, presence: true, allow_blank: false, uniqueness: true + validates :place, presence: true, allow_blank: false + validates :limit_of_participants, numericality: {greater_than: 0}, allow_nil: true + validates :time_frame, numericality: {greater_than: 0} - def recent - [] + def self.recent(limit = DEFAULT_LIMIT) + where("start_at >= :t", t: 1.month.ago).limit(limit) end end \ No newline at end of file diff --git a/db/migrate/20130513115254_create_activities.rb b/db/migrate/20130513115254_create_activities.rb new file mode 100644 index 00000000..5236f5e6 --- /dev/null +++ b/db/migrate/20130513115254_create_activities.rb @@ -0,0 +1,16 @@ +class CreateActivities < ActiveRecord::Migration + def change + create_table :activities do |t| + t.string :name, :null => false, :default => "" + t.text :description + t.string :place + t.timestamp :start_at, :null => false + t.integer :time_frame + t.integer :limit_of_participants + + t.timestamps + end + + add_index :activities, :name, :unique => true + end +end diff --git a/db/schema.rb b/db/schema.rb index 37f309fd..d3bc1164 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,11 +11,24 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20130508093327) do +ActiveRecord::Schema.define(version: 20130513115254) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" + create_table "activities", force: true do |t| + t.string "name", default: "", null: false + t.text "description" + t.string "place" + t.datetime "start_at", null: false + t.integer "time_frame" + t.integer "limit_of_participants" + t.datetime "created_at" + t.datetime "updated_at" + end + + add_index "activities", ["name"], name: "index_activities_on_name", unique: true, using: :btree + create_table "authentications", force: true do |t| t.integer "user_id" t.string "provider" diff --git a/spec/factories/activities.rb b/spec/factories/activities.rb new file mode 100644 index 00000000..b95bcf59 --- /dev/null +++ b/spec/factories/activities.rb @@ -0,0 +1,8 @@ +# Read about factories at https://github.com/thoughtbot/factory_girl + +FactoryGirl.define do + factory :activity do + name "Party!" + start_at "2013/12/12" + end +end diff --git a/spec/models/activity_spec.rb b/spec/models/activity_spec.rb index 41c57457..09213dc3 100644 --- a/spec/models/activity_spec.rb +++ b/spec/models/activity_spec.rb @@ -14,37 +14,37 @@ end describe ".recent" do - let!(:old_activities) { [mock(:activity)] } let!(:recent_activities) { [mock(:activity1), mock(:activity2)] } subject { Activity.recent } + before do + Activity.stub_chain(:where, :limit).and_return(recent_activities) + end + it { should == recent_activities } end - # - # describe "validations" do - # - # subject { Activity.new } - # - # it { should accept_values_for(:name, "Football game" ) } - # it { should_not accept_values_for(:name, "", nil) } - # specify { FactoryGirl.create(:activity).clone.save! }.to raise(ActiveRecord::RecordInvalid) - # - # it { should accept_values_for(:description, nil, "", "Wear some solid boots!")} - # - # it { should accept_values_for(:place, "football itch" ) } - # it { should_not accept_values_for(:place, "", nil) } - # - # it { should accept_values_for(:start_at, Time.now) } - # it { should_not accept_values_for(:start_at, "", nil) } - # - # # whole day activity? - # it { should accept_values_for(:time_frame, 15, 30, 60, 120, 360 ) } # minutes - # it { should_not accept_values_for(:time_frame, "", nil, 0, -10) } - # - # it { should accept_values_for(:participants_limit, nil, 12, 100) } - # it { should_not accept_values_for(:participants_limit, -1, 0) } - # - # end - # + describe "validations" do + + it { should accept_values_for(:name, "Football game" ) } + it { should_not accept_values_for(:name, "", nil) } + #specify { FactoryGirl.create(:activity).clone.save! }.to raise(ActiveRecord::RecordInvalid) + + it { should accept_values_for(:description, nil, "", "Wear some solid boots!")} + + it { should accept_values_for(:place, "football itch" ) } + it { should_not accept_values_for(:place, "", nil) } + + it { should accept_values_for(:start_at, Time.now) } + it { should_not accept_values_for(:start_at, "", nil) } + + # whole day activity? + it { should accept_values_for(:time_frame, 15, 30, 60, 120, 360 ) } # minutes + it { should_not accept_values_for(:time_frame, "", nil, 0, -10) } + + it { should accept_values_for(:limit_of_participants, nil, 12, 100) } + it { should_not accept_values_for(:limit_of_participants, -1, 0) } + + end + end \ No newline at end of file diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 522a995d..d65c4482 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -16,12 +16,4 @@ end - describe "methods" do - - subject { FactoryGirl.create(:user) } - - it { should respond_to :activities } - - end - end From f39775a885456a9c018481f992e1aec65dc2296f Mon Sep 17 00:00:00 2001 From: pietia Date: Mon, 13 May 2013 14:16:15 +0200 Subject: [PATCH 062/499] handler for the event --- config/application.yml | 2 ++ config/initializers/event.rb | 1 + 2 files changed, 3 insertions(+) create mode 100644 config/initializers/event.rb diff --git a/config/application.yml b/config/application.yml index d03eecf3..611f4b01 100644 --- a/config/application.yml +++ b/config/application.yml @@ -1,4 +1,6 @@ defaults: &defaults + event: + name: "Eurucamp 2013" host: "activities.eurucamp.org" mailers: from: "activities@eurucamp.org" diff --git a/config/initializers/event.rb b/config/initializers/event.rb new file mode 100644 index 00000000..da3355f2 --- /dev/null +++ b/config/initializers/event.rb @@ -0,0 +1 @@ +EVENT = Event.new(Settings.event.name) \ No newline at end of file From 73013024c286c6c15c741f82ed1805ec8241bc44 Mon Sep 17 00:00:00 2001 From: pietia Date: Mon, 13 May 2013 14:16:23 +0200 Subject: [PATCH 063/499] typo --- spec/models/activity_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/models/activity_spec.rb b/spec/models/activity_spec.rb index 09213dc3..313c6049 100644 --- a/spec/models/activity_spec.rb +++ b/spec/models/activity_spec.rb @@ -32,7 +32,7 @@ it { should accept_values_for(:description, nil, "", "Wear some solid boots!")} - it { should accept_values_for(:place, "football itch" ) } + it { should accept_values_for(:place, "football pitch" ) } it { should_not accept_values_for(:place, "", nil) } it { should accept_values_for(:start_at, Time.now) } From 7e03dd2bf254910bb540b0ad9f6ed37a85525425 Mon Sep 17 00:00:00 2001 From: pietia Date: Mon, 13 May 2013 14:19:07 +0200 Subject: [PATCH 064/499] fixed Activity factory --- spec/factories/activities.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/spec/factories/activities.rb b/spec/factories/activities.rb index b95bcf59..e8450a95 100644 --- a/spec/factories/activities.rb +++ b/spec/factories/activities.rb @@ -4,5 +4,7 @@ factory :activity do name "Party!" start_at "2013/12/12" + place "Ballroom" + time_frame 4*60 end end From 0ec0fd1d84e29aced030f8e2b2a416fe43a210cb Mon Sep 17 00:00:00 2001 From: pietia Date: Mon, 13 May 2013 14:23:39 +0200 Subject: [PATCH 065/499] new hash syntax --- .../20130508083406_devise_create_users.rb | 28 ++++--------------- .../20130513115254_create_activities.rb | 6 ++-- 2 files changed, 8 insertions(+), 26 deletions(-) diff --git a/db/migrate/20130508083406_devise_create_users.rb b/db/migrate/20130508083406_devise_create_users.rb index 2099d998..f24516c4 100644 --- a/db/migrate/20130508083406_devise_create_users.rb +++ b/db/migrate/20130508083406_devise_create_users.rb @@ -2,8 +2,8 @@ class DeviseCreateUsers < ActiveRecord::Migration def change create_table(:users) do |t| ## Database authenticatable - t.string :email, :null => false, :default => "" - t.string :encrypted_password, :null => false, :default => "" + t.string :email, null: false, default: "" + t.string :encrypted_password, null: false, default: "" ## Recoverable t.string :reset_password_token @@ -13,34 +13,16 @@ def change t.datetime :remember_created_at ## Trackable - t.integer :sign_in_count, :default => 0 + t.integer :sign_in_count, default: 0 t.datetime :current_sign_in_at t.datetime :last_sign_in_at t.string :current_sign_in_ip t.string :last_sign_in_ip - ## Confirmable - # t.string :confirmation_token - # t.datetime :confirmed_at - # t.datetime :confirmation_sent_at - # t.string :unconfirmed_email # Only if using reconfirmable - - ## Lockable - # t.integer :failed_attempts, :default => 0 # Only if lock strategy is :failed_attempts - # t.string :unlock_token # Only if unlock strategy is :email or :both - # t.datetime :locked_at - - ## Token authenticatable - # t.string :authentication_token - - t.timestamps end - add_index :users, :email, :unique => true - add_index :users, :reset_password_token, :unique => true - # add_index :users, :confirmation_token, :unique => true - # add_index :users, :unlock_token, :unique => true - # add_index :users, :authentication_token, :unique => true + add_index :users, :email, unique: true + add_index :users, :reset_password_token, unique: true end end diff --git a/db/migrate/20130513115254_create_activities.rb b/db/migrate/20130513115254_create_activities.rb index 5236f5e6..cec1bf1b 100644 --- a/db/migrate/20130513115254_create_activities.rb +++ b/db/migrate/20130513115254_create_activities.rb @@ -1,16 +1,16 @@ class CreateActivities < ActiveRecord::Migration def change create_table :activities do |t| - t.string :name, :null => false, :default => "" + t.string :name, null: false, default: "" t.text :description t.string :place - t.timestamp :start_at, :null => false + t.timestamp :start_at, null: false t.integer :time_frame t.integer :limit_of_participants t.timestamps end - add_index :activities, :name, :unique => true + add_index :activities, :name, unique: true end end From 5495d293f728f6c8a3e926de9c640b7209f09822 Mon Sep 17 00:00:00 2001 From: pietia Date: Sun, 26 May 2013 15:09:09 +0200 Subject: [PATCH 066/499] limited number of actions --- config/routes.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/routes.rb b/config/routes.rb index 1d1908e7..cb01140e 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -8,6 +8,6 @@ passwords: 'passwords' } - resources :activities + resources :activities, only: [:create, :destroy, :update, :index] root to: 'activities#index' end From 30f3bfe17a16a5d597c6bdd8b15b4cbdd2bc79fe Mon Sep 17 00:00:00 2001 From: pietia Date: Sun, 26 May 2013 15:09:23 +0200 Subject: [PATCH 067/499] basic tests for activities#index --- spec/controllers/activities_controller_spec.rb | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 spec/controllers/activities_controller_spec.rb diff --git a/spec/controllers/activities_controller_spec.rb b/spec/controllers/activities_controller_spec.rb new file mode 100644 index 00000000..73d6c789 --- /dev/null +++ b/spec/controllers/activities_controller_spec.rb @@ -0,0 +1,17 @@ +require 'spec_helper' + +describe ActivitiesController do + describe "#index" do + subject { get :index } + + it { should render_template(:index) } + its(:status){ should == 200 } + end + + describe "#create" + + describe "#destroy" + + describe "#update" + +end \ No newline at end of file From c14960dce3d6859606dfe0ad1c742cd4e6e9bd6c Mon Sep 17 00:00:00 2001 From: pietia Date: Sun, 26 May 2013 15:21:27 +0200 Subject: [PATCH 068/499] display list of activities --- app/controllers/activities_controller.rb | 6 ++++++ app/controllers/application_controller.rb | 5 +++++ app/views/activities/index.html.haml | 2 +- 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/app/controllers/activities_controller.rb b/app/controllers/activities_controller.rb index 50d1891a..7a576032 100644 --- a/app/controllers/activities_controller.rb +++ b/app/controllers/activities_controller.rb @@ -1,2 +1,8 @@ class ActivitiesController < ApplicationController + respond_to :json, :html + + def index + @activities = current_event.activities + respond_with(@activities) + end end diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index d83690e1..986bc632 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -2,4 +2,9 @@ class ApplicationController < ActionController::Base # Prevent CSRF attacks by raising an exception. # For APIs, you may want to use :null_session instead. protect_from_forgery with: :exception + helper_method :current_event + + def current_event + EVENT + end end diff --git a/app/views/activities/index.html.haml b/app/views/activities/index.html.haml index d08db231..c90e8b87 100644 --- a/app/views/activities/index.html.haml +++ b/app/views/activities/index.html.haml @@ -32,7 +32,7 @@ -# just render a collection and move -# card into partial - date_type = %w(upcoming today archive).sample - - 8.times do |i| + - @activities.each_with_index do |activity, i| - type = rand > 0.6 ? %w(owner participant).sample : 'default' %li(id="activity-#{i}" class="#{ type unless type == 'default' } #{date_type}") From 0027311f153c0c4ab96a7caf4cd06494af73686a Mon Sep 17 00:00:00 2001 From: pietia Date: Sun, 26 May 2013 15:32:11 +0200 Subject: [PATCH 069/499] event should have a creator --- spec/models/activity_spec.rb | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/spec/models/activity_spec.rb b/spec/models/activity_spec.rb index 313c6049..5a998048 100644 --- a/spec/models/activity_spec.rb +++ b/spec/models/activity_spec.rb @@ -5,6 +5,14 @@ let(:event) { mock(:event) } subject(:activity) { Activity.new } + describe "#creator" do + before do + activity.creator = creator + end + + its(:creator) { should == creator } + end + describe "#event" do before do activity.event = event From 95f427e2e6eb24e2c3bc7d3d86921203a2da96e9 Mon Sep 17 00:00:00 2001 From: pietia Date: Sun, 26 May 2013 15:59:16 +0200 Subject: [PATCH 070/499] added creator_id --- ...0526133400_add_creator_id_to_activities.rb | 5 ++++ db/schema.rb | 3 ++- db/seeds.rb | 23 +++++++++++++------ 3 files changed, 23 insertions(+), 8 deletions(-) create mode 100644 db/migrate/20130526133400_add_creator_id_to_activities.rb diff --git a/db/migrate/20130526133400_add_creator_id_to_activities.rb b/db/migrate/20130526133400_add_creator_id_to_activities.rb new file mode 100644 index 00000000..e361c45e --- /dev/null +++ b/db/migrate/20130526133400_add_creator_id_to_activities.rb @@ -0,0 +1,5 @@ +class AddCreatorIdToActivities < ActiveRecord::Migration + def change + add_column :activities, :creator_id, :integer + end +end diff --git a/db/schema.rb b/db/schema.rb index d3bc1164..5ae4b7dc 100644 --- a/db/schema.rb +++ b/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: 20130513115254) do +ActiveRecord::Schema.define(version: 20130526133400) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -25,6 +25,7 @@ t.integer "limit_of_participants" t.datetime "created_at" t.datetime "updated_at" + t.integer "creator_id" end add_index "activities", ["name"], name: "index_activities_on_name", unique: true, using: :btree diff --git a/db/seeds.rb b/db/seeds.rb index 4edb1e85..b7c37f11 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -1,7 +1,16 @@ -# This file should contain all the record creation needed to seed the database with its default values. -# The data can then be loaded with the rake db:seed (or created alongside the db with db:setup). -# -# Examples: -# -# cities = City.create([{ name: 'Chicago' }, { name: 'Copenhagen' }]) -# Mayor.create(name: 'Emanuel', city: cities.first) +User.transaction do |tx| + user = User.new( + email: "johnny@cash.com", + name: "Johnny Cash" + ) + user.password=user.password_confirmation="qweqweqwe" + user.save! + + Activity.create!( + name: "Party!", + start_at: 1.day.from_now.to_time, + place: "Pool", + time_frame: 200, + creator: user + ) +end From 591627f713ecb70a756f80494837242b8fafe86e Mon Sep 17 00:00:00 2001 From: pietia Date: Sun, 26 May 2013 15:59:58 +0200 Subject: [PATCH 071/499] added creator association --- app/models/activity.rb | 2 ++ spec/factories/activities.rb | 1 + spec/models/activity_spec.rb | 1 + 3 files changed, 4 insertions(+) diff --git a/app/models/activity.rb b/app/models/activity.rb index 22083854..3bfcc4ec 100644 --- a/app/models/activity.rb +++ b/app/models/activity.rb @@ -3,6 +3,8 @@ class Activity < ActiveRecord::Base attr_accessor :event + belongs_to :creator, class_name: "User" + validates :start_at, presence: true, allow_blank: false validates :name, presence: true, allow_blank: false, uniqueness: true validates :place, presence: true, allow_blank: false diff --git a/spec/factories/activities.rb b/spec/factories/activities.rb index e8450a95..5ce694dc 100644 --- a/spec/factories/activities.rb +++ b/spec/factories/activities.rb @@ -6,5 +6,6 @@ start_at "2013/12/12" place "Ballroom" time_frame 4*60 + creator { FactoryGirl.create(:user) } end end diff --git a/spec/models/activity_spec.rb b/spec/models/activity_spec.rb index 5a998048..d1d59967 100644 --- a/spec/models/activity_spec.rb +++ b/spec/models/activity_spec.rb @@ -3,6 +3,7 @@ describe Activity do let(:event) { mock(:event) } + let(:creator) { User.new } subject(:activity) { Activity.new } describe "#creator" do From fd669330c697423a8ebcbc43e03e91324e4612e4 Mon Sep 17 00:00:00 2001 From: pietia Date: Sun, 26 May 2013 20:32:46 +0200 Subject: [PATCH 072/499] added json_spec --- Gemfile | 1 + Gemfile.lock | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/Gemfile b/Gemfile index 9ccc486c..a540ee08 100644 --- a/Gemfile +++ b/Gemfile @@ -39,6 +39,7 @@ group :test do gem 'capybara', '~> 2.1' gem 'capybara-webkit', '~> 0.14' gem 'accept_values_for' + gem 'json_spec' end group :assets do diff --git a/Gemfile.lock b/Gemfile.lock index 6db42a8d..959e4eb4 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -125,6 +125,9 @@ GEM railties (>= 3.0, < 5.0) thor (>= 0.14, < 2.0) json (1.7.7) + json_spec (1.1.1) + multi_json (~> 1.0) + rspec (~> 2.0) jwt (0.1.8) multi_json (>= 1.5) kgio (2.8.0) @@ -274,6 +277,7 @@ DEPENDENCIES heroku_san (~> 3.0.2) jbuilder (~> 1.0.1) jquery-rails + json_spec neat newrelic_rpm omniauth (~> 1.1.4) From a1fb60e56c2d16f485b0dc81b64403781471d523 Mon Sep 17 00:00:00 2001 From: pietia Date: Sun, 26 May 2013 20:33:25 +0200 Subject: [PATCH 073/499] basic tests and impl. ActivitiesController#create --- app/controllers/activities_controller.rb | 12 ++++++++++- .../controllers/activities_controller_spec.rb | 20 ++++++++++++++++++- spec/spec_helper.rb | 3 +++ 3 files changed, 33 insertions(+), 2 deletions(-) diff --git a/app/controllers/activities_controller.rb b/app/controllers/activities_controller.rb index 7a576032..899f1c91 100644 --- a/app/controllers/activities_controller.rb +++ b/app/controllers/activities_controller.rb @@ -1,8 +1,18 @@ class ActivitiesController < ApplicationController - respond_to :json, :html + respond_to :json, :html, :only => :index + respond_to :json, :only => [:create] def index @activities = current_event.activities respond_with(@activities) end + + def create + sanitized_params = params.require(:activity).permit(:start_at, :name, :place, :limit_of_participants, :time_frame) + @activity = current_event.new_activity(sanitized_params) + @activity.creator = current_user + @activity.save + respond_with(@activity) + end + end diff --git a/spec/controllers/activities_controller_spec.rb b/spec/controllers/activities_controller_spec.rb index 73d6c789..701627b3 100644 --- a/spec/controllers/activities_controller_spec.rb +++ b/spec/controllers/activities_controller_spec.rb @@ -8,7 +8,25 @@ its(:status){ should == 200 } end - describe "#create" + describe "#create" do + context "format :json" do + subject { post :create, params.merge({format: :json}) } + + before do + sign_in(FactoryGirl.create(:user)) + end + + context "valid parameters" do + let(:params) { {activity: {name: "Pool party", start_at: 2.days.from_now.to_s, place: "Pool", time_frame: 120 }} } + its(:status) { should == 201 } + end + + context "invalid parameters" do + let(:params) { {activity: {x: 10}} } + its(:status) { should == 422 } + end + end + end describe "#destroy" diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index c844d873..a8efc32f 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -76,4 +76,7 @@ def self.connection # the seed, which is printed after each run. # --seed 1234 config.order = "random" + + config.include Devise::TestHelpers, type: :controller + config.include JsonSpec::Helpers, type: :controller end From 2136a50ca8a7634de732a1ee85f4d3329e87d136 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Sun, 26 May 2013 22:48:21 +0300 Subject: [PATCH 074/499] Adding draper and moving activity fields into decorator. --- Gemfile | 1 + Gemfile.lock | 6 ++++ app/controllers/activities_controller.rb | 2 +- app/decorators/activity_decorator.rb | 34 ++++++++++++++++++++++ app/models/activity.rb | 2 +- app/views/activities/index.html.haml | 15 +++++----- spec/decorators/activity_decorator_spec.rb | 4 +++ 7 files changed, 54 insertions(+), 10 deletions(-) create mode 100644 app/decorators/activity_decorator.rb create mode 100644 spec/decorators/activity_decorator_spec.rb diff --git a/Gemfile b/Gemfile index a540ee08..4f7a0e9c 100644 --- a/Gemfile +++ b/Gemfile @@ -20,6 +20,7 @@ gem 'bourbon' gem 'neat' gem 'haml-rails' gem 'rails_html_helpers' +gem 'draper' group :development do gem 'debugger', '~> 1.5' diff --git a/Gemfile.lock b/Gemfile.lock index 959e4eb4..54df2c89 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -81,6 +81,10 @@ GEM warden (~> 1.2.1) diff-lcs (1.2.4) dotenv (0.7.0) + draper (1.2.1) + actionpack (>= 3.0) + activesupport (>= 3.0) + request_store (~> 1.0.3) erubis (2.7.0) excon (0.20.1) execjs (1.4.0) @@ -194,6 +198,7 @@ GEM thor (>= 0.18.1, < 2.0) raindrops (0.11.0) rake (10.0.4) + request_store (1.0.5) rest-client (1.6.7) mime-types (>= 1.16) rspec (2.13.0) @@ -270,6 +275,7 @@ DEPENDENCIES coffee-rails (~> 4.0.0.beta1) debugger (~> 1.5) devise (~> 3.0.0.rc) + draper exception_notification! factory_girl_rails (~> 4.2) foreman diff --git a/app/controllers/activities_controller.rb b/app/controllers/activities_controller.rb index 899f1c91..5650455a 100644 --- a/app/controllers/activities_controller.rb +++ b/app/controllers/activities_controller.rb @@ -3,7 +3,7 @@ class ActivitiesController < ApplicationController respond_to :json, :only => [:create] def index - @activities = current_event.activities + @activities = current_event.activities.decorate respond_with(@activities) end diff --git a/app/decorators/activity_decorator.rb b/app/decorators/activity_decorator.rb new file mode 100644 index 00000000..d0fee836 --- /dev/null +++ b/app/decorators/activity_decorator.rb @@ -0,0 +1,34 @@ +class ActivityDecorator < Draper::Decorator + delegate_all + + def creator_name + creator.name + end + + def relation_ship_with(user) + if user.nil? then "default" + elsif object.creator == user then "owner" + #elsif object.participants.include? user then "participant" + else "default" + end + end + + def status + today = Date.current + if object.start_at > today then "upcoming" + elsif object.start_at.to_date == today then "today" + else "archive" + end + end + + def full_by + rand(100) + end + + def time + # Case for "Anytime" needed + object.start_at.strftime("%A, %-d.%-m / %k:%M – ") + + (object.start_at + object.time_frame.minutes).strftime("%k:%M") + end + +end diff --git a/app/models/activity.rb b/app/models/activity.rb index 3bfcc4ec..6cc0e9ae 100644 --- a/app/models/activity.rb +++ b/app/models/activity.rb @@ -15,4 +15,4 @@ def self.recent(limit = DEFAULT_LIMIT) where("start_at >= :t", t: 1.month.ago).limit(limit) end -end \ No newline at end of file +end diff --git a/app/views/activities/index.html.haml b/app/views/activities/index.html.haml index c90e8b87..dba1eb83 100644 --- a/app/views/activities/index.html.haml +++ b/app/views/activities/index.html.haml @@ -31,22 +31,21 @@ %ul#activities -# just render a collection and move -# card into partial - - date_type = %w(upcoming today archive).sample - - @activities.each_with_index do |activity, i| - - type = rand > 0.6 ? %w(owner participant).sample : 'default' + - @activities.each do |activity| + - type = activity.relation_ship_with(current_user) - %li(id="activity-#{i}" class="#{ type unless type == 'default' } #{date_type}") + %li(id="activity-#{activity.id}" class="#{type} #{activity.status}") .container - = image_tag "dummies/activity.jpg", :alt => "Activity title", :class => "progress", :data => { :progress => rand(100) } + = image_tag "dummies/activity.jpg", alt: activity.name, class: "progress", data: { progress: activity.full_by } .labels %h4 - = ["Title", "A medium title", "A rather long title, just to see how it looks"].sample + = activity.name %p.creator - = ["Alex Coles", "Piotr Gega", "Florian Plank"].sample + = activity.creator_name %p.time - = ["Monday, 12.8 / 16:30 – 18:00", "Anytime"].sample + = activity.time.html_safe .action - case type diff --git a/spec/decorators/activity_decorator_spec.rb b/spec/decorators/activity_decorator_spec.rb new file mode 100644 index 00000000..6c620981 --- /dev/null +++ b/spec/decorators/activity_decorator_spec.rb @@ -0,0 +1,4 @@ +require 'spec_helper' + +describe ActivityDecorator do +end From 43539e79b67146c5959f7d9e4b11d011d4518393 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Mon, 27 May 2013 00:03:44 +0300 Subject: [PATCH 075/499] Move html_safe: --- app/decorators/activity_decorator.rb | 2 +- app/views/activities/index.html.haml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/decorators/activity_decorator.rb b/app/decorators/activity_decorator.rb index d0fee836..078e0aea 100644 --- a/app/decorators/activity_decorator.rb +++ b/app/decorators/activity_decorator.rb @@ -28,7 +28,7 @@ def full_by def time # Case for "Anytime" needed object.start_at.strftime("%A, %-d.%-m / %k:%M – ") + - (object.start_at + object.time_frame.minutes).strftime("%k:%M") + (object.start_at + object.time_frame.minutes).strftime("%k:%M").html_safe end end diff --git a/app/views/activities/index.html.haml b/app/views/activities/index.html.haml index dba1eb83..497f4d00 100644 --- a/app/views/activities/index.html.haml +++ b/app/views/activities/index.html.haml @@ -45,7 +45,7 @@ %p.creator = activity.creator_name %p.time - = activity.time.html_safe + = activity.time .action - case type From df3c2c5c388fd2ad9a27426c44e417516a765463 Mon Sep 17 00:00:00 2001 From: pietia Date: Sun, 26 May 2013 20:42:21 +0200 Subject: [PATCH 076/499] refactoring: pass user to new_activity --- app/controllers/activities_controller.rb | 3 +-- app/models/event.rb | 3 ++- spec/controllers/activities_controller_spec.rb | 3 ++- spec/models/event_spec.rb | 3 ++- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/app/controllers/activities_controller.rb b/app/controllers/activities_controller.rb index 5650455a..bb668c53 100644 --- a/app/controllers/activities_controller.rb +++ b/app/controllers/activities_controller.rb @@ -9,8 +9,7 @@ def index def create sanitized_params = params.require(:activity).permit(:start_at, :name, :place, :limit_of_participants, :time_frame) - @activity = current_event.new_activity(sanitized_params) - @activity.creator = current_user + @activity = current_event.new_activity(current_user, sanitized_params) @activity.save respond_with(@activity) end diff --git a/app/models/event.rb b/app/models/event.rb index 0e2569c8..cbe88f69 100644 --- a/app/models/event.rb +++ b/app/models/event.rb @@ -7,9 +7,10 @@ def initialize(name, fetcher = ->{ Activity.recent }) @fetcher = fetcher end - def new_activity(*args) + def new_activity(author, *args) activity_source.call(*args).tap do |activity| activity.event = self + activity.creator = author end end diff --git a/spec/controllers/activities_controller_spec.rb b/spec/controllers/activities_controller_spec.rb index 701627b3..980d77eb 100644 --- a/spec/controllers/activities_controller_spec.rb +++ b/spec/controllers/activities_controller_spec.rb @@ -11,9 +11,10 @@ describe "#create" do context "format :json" do subject { post :create, params.merge({format: :json}) } + let(:user) { FactoryGirl.create(:user) } before do - sign_in(FactoryGirl.create(:user)) + sign_in(user) end context "valid parameters" do diff --git a/spec/models/event_spec.rb b/spec/models/event_spec.rb index a48ff796..585a6eff 100644 --- a/spec/models/event_spec.rb +++ b/spec/models/event_spec.rb @@ -9,9 +9,10 @@ end describe "#new_activity" do + let(:user) { mock_model(User) } let(:new_activity) { OpenStruct.new } let(:args) { {} } - subject { event.new_activity(args) } + subject { event.new_activity(user, args) } before do event.activity_source = ->(x){ new_activity } From fdcd7f7b5e8fc0f50f691cb5aebc7b020a07f2f7 Mon Sep 17 00:00:00 2001 From: pietia Date: Mon, 27 May 2013 00:11:10 +0200 Subject: [PATCH 077/499] simple implementation of update action --- app/controllers/activities_controller.rb | 15 +++++++++-- app/models/event.rb | 4 +++ .../controllers/activities_controller_spec.rb | 26 ++++++++++++++++--- 3 files changed, 40 insertions(+), 5 deletions(-) diff --git a/app/controllers/activities_controller.rb b/app/controllers/activities_controller.rb index bb668c53..9fba5d5a 100644 --- a/app/controllers/activities_controller.rb +++ b/app/controllers/activities_controller.rb @@ -1,6 +1,6 @@ class ActivitiesController < ApplicationController respond_to :json, :html, :only => :index - respond_to :json, :only => [:create] + respond_to :json def index @activities = current_event.activities.decorate @@ -8,10 +8,21 @@ def index end def create - sanitized_params = params.require(:activity).permit(:start_at, :name, :place, :limit_of_participants, :time_frame) @activity = current_event.new_activity(current_user, sanitized_params) @activity.save respond_with(@activity) end + def update + @activity = current_event.activity(params[:id]) + @activity.update_attributes(sanitized_params) + respond_with(@activity) + end + + private + + def sanitized_params + params.require(:activity).permit(:start_at, :name, :place, :limit_of_participants, :time_frame) + end + end diff --git a/app/models/event.rb b/app/models/event.rb index cbe88f69..6a438c60 100644 --- a/app/models/event.rb +++ b/app/models/event.rb @@ -14,6 +14,10 @@ def new_activity(author, *args) end end + def activity(activity_id) + activities.where(:id => activity_id).first + end + def activities fetch end diff --git a/spec/controllers/activities_controller_spec.rb b/spec/controllers/activities_controller_spec.rb index 980d77eb..dbffb98e 100644 --- a/spec/controllers/activities_controller_spec.rb +++ b/spec/controllers/activities_controller_spec.rb @@ -1,6 +1,8 @@ require 'spec_helper' describe ActivitiesController do + let(:user) { FactoryGirl.create(:user) } + describe "#index" do subject { get :index } @@ -11,7 +13,6 @@ describe "#create" do context "format :json" do subject { post :create, params.merge({format: :json}) } - let(:user) { FactoryGirl.create(:user) } before do sign_in(user) @@ -29,8 +30,27 @@ end end - describe "#destroy" + describe "#update" do + context "format :json" do + subject { put :update, params.merge({format: :json}) } + let!(:activity) { FactoryGirl.create(:activity, creator: user) } - describe "#update" + before do + sign_in(user) + end + + context "valid parameters" do + let(:params) { {id: activity.id, activity: {name: "Pool party", start_at: 2.days.from_now.to_s, place: "Pool", time_frame: 120 }} } + its(:status) { should == 204 } + end + + context "invalid parameters" do + let(:params) { {id: activity.id, activity: {start_at: ""}} } + its(:status) { should == 422 } + end + end + end + + describe "#destroy" end \ No newline at end of file From 1f5d1b9c2bc9b243241155a9c3395118f73c20ee Mon Sep 17 00:00:00 2001 From: pietia Date: Mon, 27 May 2013 00:49:58 +0200 Subject: [PATCH 078/499] added missing filters, added #destroy --- app/controllers/activities_controller.rb | 8 ++++++++ app/controllers/application_controller.rb | 2 ++ app/controllers/authentications_controller.rb | 2 +- app/controllers/passwords_controller.rb | 2 +- app/controllers/registrations_controller.rb | 2 +- app/controllers/sessions_controller.rb | 2 +- spec/controllers/activities_controller_spec.rb | 13 ++++++++++++- 7 files changed, 26 insertions(+), 5 deletions(-) diff --git a/app/controllers/activities_controller.rb b/app/controllers/activities_controller.rb index 9fba5d5a..8a17577c 100644 --- a/app/controllers/activities_controller.rb +++ b/app/controllers/activities_controller.rb @@ -2,6 +2,8 @@ class ActivitiesController < ApplicationController respond_to :json, :html, :only => :index respond_to :json + skip_before_filter :authenticate_user!, :only => :index + def index @activities = current_event.activities.decorate respond_with(@activities) @@ -19,6 +21,12 @@ def update respond_with(@activity) end + def destroy + @activity = current_event.activity(params[:id]) + @activity.destroy if @activity && @activity.creator == current_user #TODO + respond_with(@activity) + end + private def sanitized_params diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 986bc632..f67c7e7d 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -4,6 +4,8 @@ class ApplicationController < ActionController::Base protect_from_forgery with: :exception helper_method :current_event + before_filter :authenticate_user! + def current_event EVENT end diff --git a/app/controllers/authentications_controller.rb b/app/controllers/authentications_controller.rb index 5936b4a7..19c26269 100644 --- a/app/controllers/authentications_controller.rb +++ b/app/controllers/authentications_controller.rb @@ -1,5 +1,5 @@ class AuthenticationsController < ApplicationController - skip_before_filter :authenticate + skip_before_filter :authenticate_user! rescue_from ActionController::RedirectBackError do |exception| redirect_to edit_user_registration_path diff --git a/app/controllers/passwords_controller.rb b/app/controllers/passwords_controller.rb index e4ac15d0..7272ed88 100644 --- a/app/controllers/passwords_controller.rb +++ b/app/controllers/passwords_controller.rb @@ -1,3 +1,3 @@ class PasswordsController < Devise::PasswordsController - skip_before_filter :authenticate + skip_before_filter :authenticate_user! end diff --git a/app/controllers/registrations_controller.rb b/app/controllers/registrations_controller.rb index a4690082..acdce4e0 100644 --- a/app/controllers/registrations_controller.rb +++ b/app/controllers/registrations_controller.rb @@ -1,5 +1,5 @@ class RegistrationsController < Devise::RegistrationsController - skip_before_filter :authenticate, only: :create + skip_before_filter :authenticate_user!, only: create def create super diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb index 7d22330c..01abd8b7 100644 --- a/app/controllers/sessions_controller.rb +++ b/app/controllers/sessions_controller.rb @@ -1,3 +1,3 @@ class SessionsController < Devise::SessionsController - skip_before_filter :authenticate + skip_before_filter :authenticate_user! end diff --git a/spec/controllers/activities_controller_spec.rb b/spec/controllers/activities_controller_spec.rb index dbffb98e..fe7367d8 100644 --- a/spec/controllers/activities_controller_spec.rb +++ b/spec/controllers/activities_controller_spec.rb @@ -51,6 +51,17 @@ end end - describe "#destroy" + describe "#destroy" do + context "format :json" do + subject { delete :destroy, {id: activity.id, format: :json} } + let!(:activity) { FactoryGirl.create(:activity, creator: user) } + + before do + sign_in(user) + end + + its(:status) { should == 204 } + end + end end \ No newline at end of file From 8e8078ab1b97ddfe22745c4de509513f0e990b98 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Mon, 27 May 2013 09:47:58 +0300 Subject: [PATCH 079/499] WIP. Adding participation. #36. --- app/decorators/activity_decorator.rb | 4 ---- app/models/activity.rb | 6 ++++++ app/models/participation.rb | 4 ++++ app/models/user.rb | 2 ++ .../20130527054241_create_participations.rb | 12 +++++++++++ db/schema.rb | 10 +++++++++- db/seeds.rb | 20 ++++++++++++++----- spec/factories/participations.rb | 8 ++++++++ spec/models/participation_spec.rb | 5 +++++ 9 files changed, 61 insertions(+), 10 deletions(-) create mode 100644 app/models/participation.rb create mode 100644 db/migrate/20130527054241_create_participations.rb create mode 100644 spec/factories/participations.rb create mode 100644 spec/models/participation_spec.rb diff --git a/app/decorators/activity_decorator.rb b/app/decorators/activity_decorator.rb index 078e0aea..e2deade8 100644 --- a/app/decorators/activity_decorator.rb +++ b/app/decorators/activity_decorator.rb @@ -21,10 +21,6 @@ def status end end - def full_by - rand(100) - end - def time # Case for "Anytime" needed object.start_at.strftime("%A, %-d.%-m / %k:%M – ") + diff --git a/app/models/activity.rb b/app/models/activity.rb index 6cc0e9ae..97bb94e1 100644 --- a/app/models/activity.rb +++ b/app/models/activity.rb @@ -4,6 +4,8 @@ class Activity < ActiveRecord::Base attr_accessor :event belongs_to :creator, class_name: "User" + has_many :participations, :dependent => :destroy + has_many :participants, through: :participations, class_name: "User" validates :start_at, presence: true, allow_blank: false validates :name, presence: true, allow_blank: false, uniqueness: true @@ -15,4 +17,8 @@ def self.recent(limit = DEFAULT_LIMIT) where("start_at >= :t", t: 1.month.ago).limit(limit) end + def full_by + participations_count.to_f / limit_of_participants.to_f * 100 + end + end diff --git a/app/models/participation.rb b/app/models/participation.rb new file mode 100644 index 00000000..cc16dc7b --- /dev/null +++ b/app/models/participation.rb @@ -0,0 +1,4 @@ +class Participation < ActiveRecord::Base + belongs_to :participant, class_name: "User", foreign_key: "user_id" + belongs_to :activity, counter_cache: true +end diff --git a/app/models/user.rb b/app/models/user.rb index d1fefe6e..411a1a5b 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -6,6 +6,8 @@ class User < ActiveRecord::Base :recoverable, :rememberable, :trackable, :validatable has_many :authentications + has_many :participations, :dependent => :destroy + has_many :activities, through: :participations #validates :name, presence: true validates :email, presence: true diff --git a/db/migrate/20130527054241_create_participations.rb b/db/migrate/20130527054241_create_participations.rb new file mode 100644 index 00000000..c21a27d6 --- /dev/null +++ b/db/migrate/20130527054241_create_participations.rb @@ -0,0 +1,12 @@ +class CreateParticipations < ActiveRecord::Migration + def change + create_table :participations do |t| + t.integer :user_id + t.integer :activity_id + + t.timestamps + end + + add_column :activities, :participations_count, :integer, default: 0 + end +end diff --git a/db/schema.rb b/db/schema.rb index 5ae4b7dc..f47c508a 100644 --- a/db/schema.rb +++ b/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: 20130526133400) do +ActiveRecord::Schema.define(version: 20130527054241) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -26,6 +26,7 @@ t.datetime "created_at" t.datetime "updated_at" t.integer "creator_id" + t.integer "participations_count", default: 0 end add_index "activities", ["name"], name: "index_activities_on_name", unique: true, using: :btree @@ -38,6 +39,13 @@ t.datetime "updated_at", null: false end + create_table "participations", force: true do |t| + t.integer "user_id" + t.integer "activity_id" + t.datetime "created_at" + t.datetime "updated_at" + end + create_table "users", force: true do |t| t.string "email", default: "", null: false t.string "encrypted_password", default: "", null: false diff --git a/db/seeds.rb b/db/seeds.rb index b7c37f11..0ae77e7f 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -1,16 +1,26 @@ User.transaction do |tx| - user = User.new( + creator = User.new( email: "johnny@cash.com", name: "Johnny Cash" ) - user.password=user.password_confirmation="qweqweqwe" - user.save! + creator.password = creator.password_confirmation = "qweqweqwe" + creator.save! - Activity.create!( + participant = User.new( + email: "johnny@begood.com", + name: "Johnny Begood" + ) + participant.password = participant.password_confirmation = "qweqweqwe" + participant.save! + + activity = Activity.new( name: "Party!", start_at: 1.day.from_now.to_time, place: "Pool", time_frame: 200, - creator: user + limit_of_participants: 2, + creator: creator ) + activity.participants << participant + activity.save! end diff --git a/spec/factories/participations.rb b/spec/factories/participations.rb new file mode 100644 index 00000000..b5470bf1 --- /dev/null +++ b/spec/factories/participations.rb @@ -0,0 +1,8 @@ +# Read about factories at https://github.com/thoughtbot/factory_girl + +FactoryGirl.define do + factory :participation do + user_id 1 + activity_id 1 + end +end diff --git a/spec/models/participation_spec.rb b/spec/models/participation_spec.rb new file mode 100644 index 00000000..29bd3f91 --- /dev/null +++ b/spec/models/participation_spec.rb @@ -0,0 +1,5 @@ +require 'spec_helper' + +describe Participation do + pending "add some examples to (or delete) #{__FILE__}" +end From 9620564323f8e339d4733d2f85043d67ab415d8e Mon Sep 17 00:00:00 2001 From: Polarblau Date: Mon, 27 May 2013 00:03:44 +0300 Subject: [PATCH 080/499] Move html_safe: --- app/decorators/activity_decorator.rb | 2 +- app/views/activities/index.html.haml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/decorators/activity_decorator.rb b/app/decorators/activity_decorator.rb index d0fee836..078e0aea 100644 --- a/app/decorators/activity_decorator.rb +++ b/app/decorators/activity_decorator.rb @@ -28,7 +28,7 @@ def full_by def time # Case for "Anytime" needed object.start_at.strftime("%A, %-d.%-m / %k:%M – ") + - (object.start_at + object.time_frame.minutes).strftime("%k:%M") + (object.start_at + object.time_frame.minutes).strftime("%k:%M").html_safe end end diff --git a/app/views/activities/index.html.haml b/app/views/activities/index.html.haml index dba1eb83..497f4d00 100644 --- a/app/views/activities/index.html.haml +++ b/app/views/activities/index.html.haml @@ -45,7 +45,7 @@ %p.creator = activity.creator_name %p.time - = activity.time.html_safe + = activity.time .action - case type From c3799b377eba54116923d75f0dff7c61b344ee9b Mon Sep 17 00:00:00 2001 From: Polarblau Date: Mon, 27 May 2013 09:47:58 +0300 Subject: [PATCH 081/499] WIP. Adding participation. #36. --- app/decorators/activity_decorator.rb | 4 ---- app/models/activity.rb | 6 ++++++ app/models/participation.rb | 4 ++++ app/models/user.rb | 2 ++ .../20130527054241_create_participations.rb | 12 +++++++++++ db/schema.rb | 10 +++++++++- db/seeds.rb | 20 ++++++++++++++----- spec/factories/participations.rb | 8 ++++++++ spec/models/participation_spec.rb | 5 +++++ 9 files changed, 61 insertions(+), 10 deletions(-) create mode 100644 app/models/participation.rb create mode 100644 db/migrate/20130527054241_create_participations.rb create mode 100644 spec/factories/participations.rb create mode 100644 spec/models/participation_spec.rb diff --git a/app/decorators/activity_decorator.rb b/app/decorators/activity_decorator.rb index 078e0aea..e2deade8 100644 --- a/app/decorators/activity_decorator.rb +++ b/app/decorators/activity_decorator.rb @@ -21,10 +21,6 @@ def status end end - def full_by - rand(100) - end - def time # Case for "Anytime" needed object.start_at.strftime("%A, %-d.%-m / %k:%M – ") + diff --git a/app/models/activity.rb b/app/models/activity.rb index 6cc0e9ae..97bb94e1 100644 --- a/app/models/activity.rb +++ b/app/models/activity.rb @@ -4,6 +4,8 @@ class Activity < ActiveRecord::Base attr_accessor :event belongs_to :creator, class_name: "User" + has_many :participations, :dependent => :destroy + has_many :participants, through: :participations, class_name: "User" validates :start_at, presence: true, allow_blank: false validates :name, presence: true, allow_blank: false, uniqueness: true @@ -15,4 +17,8 @@ def self.recent(limit = DEFAULT_LIMIT) where("start_at >= :t", t: 1.month.ago).limit(limit) end + def full_by + participations_count.to_f / limit_of_participants.to_f * 100 + end + end diff --git a/app/models/participation.rb b/app/models/participation.rb new file mode 100644 index 00000000..cc16dc7b --- /dev/null +++ b/app/models/participation.rb @@ -0,0 +1,4 @@ +class Participation < ActiveRecord::Base + belongs_to :participant, class_name: "User", foreign_key: "user_id" + belongs_to :activity, counter_cache: true +end diff --git a/app/models/user.rb b/app/models/user.rb index d1fefe6e..411a1a5b 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -6,6 +6,8 @@ class User < ActiveRecord::Base :recoverable, :rememberable, :trackable, :validatable has_many :authentications + has_many :participations, :dependent => :destroy + has_many :activities, through: :participations #validates :name, presence: true validates :email, presence: true diff --git a/db/migrate/20130527054241_create_participations.rb b/db/migrate/20130527054241_create_participations.rb new file mode 100644 index 00000000..c21a27d6 --- /dev/null +++ b/db/migrate/20130527054241_create_participations.rb @@ -0,0 +1,12 @@ +class CreateParticipations < ActiveRecord::Migration + def change + create_table :participations do |t| + t.integer :user_id + t.integer :activity_id + + t.timestamps + end + + add_column :activities, :participations_count, :integer, default: 0 + end +end diff --git a/db/schema.rb b/db/schema.rb index 5ae4b7dc..f47c508a 100644 --- a/db/schema.rb +++ b/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: 20130526133400) do +ActiveRecord::Schema.define(version: 20130527054241) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -26,6 +26,7 @@ t.datetime "created_at" t.datetime "updated_at" t.integer "creator_id" + t.integer "participations_count", default: 0 end add_index "activities", ["name"], name: "index_activities_on_name", unique: true, using: :btree @@ -38,6 +39,13 @@ t.datetime "updated_at", null: false end + create_table "participations", force: true do |t| + t.integer "user_id" + t.integer "activity_id" + t.datetime "created_at" + t.datetime "updated_at" + end + create_table "users", force: true do |t| t.string "email", default: "", null: false t.string "encrypted_password", default: "", null: false diff --git a/db/seeds.rb b/db/seeds.rb index b7c37f11..0ae77e7f 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -1,16 +1,26 @@ User.transaction do |tx| - user = User.new( + creator = User.new( email: "johnny@cash.com", name: "Johnny Cash" ) - user.password=user.password_confirmation="qweqweqwe" - user.save! + creator.password = creator.password_confirmation = "qweqweqwe" + creator.save! - Activity.create!( + participant = User.new( + email: "johnny@begood.com", + name: "Johnny Begood" + ) + participant.password = participant.password_confirmation = "qweqweqwe" + participant.save! + + activity = Activity.new( name: "Party!", start_at: 1.day.from_now.to_time, place: "Pool", time_frame: 200, - creator: user + limit_of_participants: 2, + creator: creator ) + activity.participants << participant + activity.save! end diff --git a/spec/factories/participations.rb b/spec/factories/participations.rb new file mode 100644 index 00000000..b5470bf1 --- /dev/null +++ b/spec/factories/participations.rb @@ -0,0 +1,8 @@ +# Read about factories at https://github.com/thoughtbot/factory_girl + +FactoryGirl.define do + factory :participation do + user_id 1 + activity_id 1 + end +end diff --git a/spec/models/participation_spec.rb b/spec/models/participation_spec.rb new file mode 100644 index 00000000..29bd3f91 --- /dev/null +++ b/spec/models/participation_spec.rb @@ -0,0 +1,5 @@ +require 'spec_helper' + +describe Participation do + pending "add some examples to (or delete) #{__FILE__}" +end From 2acafc0805086453b3ee5d9f313410f0f8be5288 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Mon, 27 May 2013 09:50:16 +0300 Subject: [PATCH 082/499] Revert "Move html_safe:" This reverts commit 43539e79b67146c5959f7d9e4b11d011d4518393. --- app/decorators/activity_decorator.rb | 2 +- app/views/activities/index.html.haml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/decorators/activity_decorator.rb b/app/decorators/activity_decorator.rb index e2deade8..095572f6 100644 --- a/app/decorators/activity_decorator.rb +++ b/app/decorators/activity_decorator.rb @@ -24,7 +24,7 @@ def status def time # Case for "Anytime" needed object.start_at.strftime("%A, %-d.%-m / %k:%M – ") + - (object.start_at + object.time_frame.minutes).strftime("%k:%M").html_safe + (object.start_at + object.time_frame.minutes).strftime("%k:%M") end end diff --git a/app/views/activities/index.html.haml b/app/views/activities/index.html.haml index 497f4d00..dba1eb83 100644 --- a/app/views/activities/index.html.haml +++ b/app/views/activities/index.html.haml @@ -45,7 +45,7 @@ %p.creator = activity.creator_name %p.time - = activity.time + = activity.time.html_safe .action - case type From 1e19ad47b7309f9d56d011fa194756672aa442f4 Mon Sep 17 00:00:00 2001 From: pietia Date: Mon, 27 May 2013 22:34:56 +0200 Subject: [PATCH 083/499] added specs for AuthenticationController --- .../authentications_controller_spec.rb | 63 +++++++++++++++++++ spec/factories/authentications.rb | 7 +++ spec/factories/sequences.rb | 1 + 3 files changed, 71 insertions(+) create mode 100644 spec/controllers/authentications_controller_spec.rb create mode 100644 spec/factories/authentications.rb diff --git a/spec/controllers/authentications_controller_spec.rb b/spec/controllers/authentications_controller_spec.rb new file mode 100644 index 00000000..971a00f5 --- /dev/null +++ b/spec/controllers/authentications_controller_spec.rb @@ -0,0 +1,63 @@ +require "spec_helper" + +describe AuthenticationsController do + + before do + @request.env["devise.mapping"] = Devise.mappings[:user] + end + + describe '#create' do + subject { post :create } + + before do + request.env['omniauth.auth'] = omniauth_data + end + + context "no authentication" do + let(:omniauth_data) { { provider: 'none', uuid: 'none', info: { email: 'test@john.com', name: 'test'} } } + it { should redirect_to "/" } + end + + context "no authentication or incorrect details" do + let(:omniauth_data) { { provider: 'none', uuid: 'none', info: {} } } + it { should redirect_to new_user_registration_url } + end + + context "user already logged in" do + let(:omniauth_data) { { provider: 'none', uuid: 'none' } } + + before do + sign_in FactoryGirl.create(:user) + end + + context "when HTTP_REFERER set" do + before do + request.env['HTTP_REFERER'] = 'http://backtoreality/' + end + + it { should redirect_to 'http://backtoreality/' } + end + + context "when HTTP_REFERER not set" do + it { should redirect_to edit_user_registration_path } + end + end + + end + + describe "#delete" do + subject(:logout_action) { delete :destroy, id: authentication.id } + let(:authentication) { FactoryGirl.create(:authentication) } + + before do + sign_in authentication.user + end + + it { should redirect_to authentications_path } + + specify do + expect { logout_action }.to change(Authentication.count).by(-1) + end + end + +end diff --git a/spec/factories/authentications.rb b/spec/factories/authentications.rb new file mode 100644 index 00000000..023ada09 --- /dev/null +++ b/spec/factories/authentications.rb @@ -0,0 +1,7 @@ +FactoryGirl.define do + factory :authentication do + uid { generate(:uid) } + provider "Provider" + user + end +end diff --git a/spec/factories/sequences.rb b/spec/factories/sequences.rb index 22d304ec..14777efd 100644 --- a/spec/factories/sequences.rb +++ b/spec/factories/sequences.rb @@ -1,3 +1,4 @@ FactoryGirl.define do sequence(:email) { |n| "person#{n}@example.com" } + sequence(:uid) { |n| "uid#{n}" } end \ No newline at end of file From de2f4a9e20e66a8dcacd5b77efc500fe060986fa Mon Sep 17 00:00:00 2001 From: pietia Date: Mon, 27 May 2013 23:00:41 +0200 Subject: [PATCH 084/499] fixed tests --- app/models/user.rb | 6 ++++-- spec/controllers/authentications_controller_spec.rb | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/app/models/user.rb b/app/models/user.rb index d1fefe6e..23a205da 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -13,8 +13,10 @@ class User < ActiveRecord::Base # TODO: extract to module and then to a gem / engine def apply_omniauth(omniauth) provider, uid, info = omniauth.values_at('provider', 'uid', 'info') - self.email = info['email'] if email.blank? - self.name = info['name'] if name.blank? + unless info.blank? + self.email = info['email'] if email.blank? + self.name = info['name'] if name.blank? + end apply_provider_handle(omniauth) authentications.build(provider: provider, uid: uid) diff --git a/spec/controllers/authentications_controller_spec.rb b/spec/controllers/authentications_controller_spec.rb index 971a00f5..f65dd7f7 100644 --- a/spec/controllers/authentications_controller_spec.rb +++ b/spec/controllers/authentications_controller_spec.rb @@ -15,7 +15,7 @@ context "no authentication" do let(:omniauth_data) { { provider: 'none', uuid: 'none', info: { email: 'test@john.com', name: 'test'} } } - it { should redirect_to "/" } + it { should redirect_to new_user_registration_url } end context "no authentication or incorrect details" do @@ -56,7 +56,7 @@ it { should redirect_to authentications_path } specify do - expect { logout_action }.to change(Authentication.count).by(-1) + expect { logout_action }.to change(Authentication, :count).by(-1) end end From 27dbaa86d31fb18322d84e218866192ed3b589db Mon Sep 17 00:00:00 2001 From: pietia Date: Tue, 28 May 2013 20:24:12 +0200 Subject: [PATCH 085/499] added tests for #full_by --- app/models/activity.rb | 2 +- spec/models/activity_spec.rb | 22 ++++++++++++++++++++++ spec/models/participation_spec.rb | 1 - 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/app/models/activity.rb b/app/models/activity.rb index 97bb94e1..168c3e42 100644 --- a/app/models/activity.rb +++ b/app/models/activity.rb @@ -18,7 +18,7 @@ def self.recent(limit = DEFAULT_LIMIT) end def full_by - participations_count.to_f / limit_of_participants.to_f * 100 + limit_of_participants.nil? ? 0 : [100.0 * participations_count / limit_of_participants.to_f, 100.0].min end end diff --git a/spec/models/activity_spec.rb b/spec/models/activity_spec.rb index d1d59967..464e28f9 100644 --- a/spec/models/activity_spec.rb +++ b/spec/models/activity_spec.rb @@ -33,6 +33,28 @@ it { should == recent_activities } end + describe "#full_by" do + subject { activity.full_by } + + context "limit of participants is not set" do + before do + activity.stub!(:limit_of_participants).and_return(nil) + end + + it { should == 0 } + end + + context "limit of participants is set" do + before do + activity.stub!(:limit_of_participants).and_return(10) + activity.stub!(:participations_count).and_return(8) + end + + it { should == 80 } + end + + end + describe "validations" do it { should accept_values_for(:name, "Football game" ) } diff --git a/spec/models/participation_spec.rb b/spec/models/participation_spec.rb index 29bd3f91..bb67b4fc 100644 --- a/spec/models/participation_spec.rb +++ b/spec/models/participation_spec.rb @@ -1,5 +1,4 @@ require 'spec_helper' describe Participation do - pending "add some examples to (or delete) #{__FILE__}" end From e20bf974741241563efeb5ef4a0641d5ab9bb170 Mon Sep 17 00:00:00 2001 From: pietia Date: Tue, 28 May 2013 21:52:50 +0200 Subject: [PATCH 086/499] removed deprecated code --- app/controllers/authentications_controller.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/controllers/authentications_controller.rb b/app/controllers/authentications_controller.rb index 19c26269..a088019f 100644 --- a/app/controllers/authentications_controller.rb +++ b/app/controllers/authentications_controller.rb @@ -8,13 +8,13 @@ class AuthenticationsController < ApplicationController def create omniauth = request.env['omniauth.auth'] provider, uid = omniauth.values_at('provider', 'uid') - authentication = Authentication.find_by_provider_and_uid(provider, uid) + authentication = Authentication.where(provider: provider, uid: uid).first if authentication flash[:notice] = 'Signed in successfully' sign_in_and_redirect(:user, authentication.user) elsif current_user - current_user.authentications.find_or_create_by_provider_and_uid(provider, uid) + current_user.authentications.find_or_create_by(provider: provider, uid: uid) current_user.apply_provider_handle(omniauth) current_user.save flash[:notice] = 'Connected successfully' From 2febcdfb7124998e9ecaa12793c6e2c39978a370 Mon Sep 17 00:00:00 2001 From: pietia Date: Tue, 28 May 2013 22:02:59 +0200 Subject: [PATCH 087/499] added Activity#new_participation --- app/models/activity.rb | 14 ++++++++++++++ spec/models/activity_spec.rb | 15 +++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/app/models/activity.rb b/app/models/activity.rb index 168c3e42..f2030f46 100644 --- a/app/models/activity.rb +++ b/app/models/activity.rb @@ -2,6 +2,7 @@ class Activity < ActiveRecord::Base DEFAULT_LIMIT = 100 attr_accessor :event + attr_writer :participation_source # DI belongs_to :creator, class_name: "User" has_many :participations, :dependent => :destroy @@ -21,4 +22,17 @@ def full_by limit_of_participants.nil? ? 0 : [100.0 * participations_count / limit_of_participants.to_f, 100.0].min end + def new_participation(user) + participation_source.call.tap do |participation| + participation.activity = self + participation.participant = user + end + end + + private + + def participation_source + @participation_source ||= Participation.public_method(:new) + end + end diff --git a/spec/models/activity_spec.rb b/spec/models/activity_spec.rb index 464e28f9..5397971c 100644 --- a/spec/models/activity_spec.rb +++ b/spec/models/activity_spec.rb @@ -6,6 +6,21 @@ let(:creator) { User.new } subject(:activity) { Activity.new } + describe "#new_participation" do + let(:user) { mock_model(User) } + let(:new_participation) { OpenStruct.new } + let(:args) { {} } + subject { activity.new_participation(user) } + + before do + activity.participation_source = ->{ new_participation } + end + + its(:participant) { should == user } + its(:activity) { should == activity } + it { should == new_participation } + end + describe "#creator" do before do activity.creator = creator From cb8f75f0ee51df8ca94b1592181f3992bde6fb7d Mon Sep 17 00:00:00 2001 From: pietia Date: Tue, 28 May 2013 22:03:10 +0200 Subject: [PATCH 088/499] use public api in seed script --- db/seeds.rb | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/db/seeds.rb b/db/seeds.rb index 0ae77e7f..10fd7b31 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -1,26 +1,27 @@ User.transaction do |tx| + ultra_secure_password = "qweqweqwe" + creator = User.new( email: "johnny@cash.com", name: "Johnny Cash" ) - creator.password = creator.password_confirmation = "qweqweqwe" + creator.password = creator.password_confirmation = ultra_secure_password creator.save! participant = User.new( - email: "johnny@begood.com", + email: "johnny@begood.com", # go go! name: "Johnny Begood" ) - participant.password = participant.password_confirmation = "qweqweqwe" + participant.password = participant.password_confirmation = ultra_secure_password participant.save! - activity = Activity.new( + activity = EVENT.new_activity(creator, name: "Party!", start_at: 1.day.from_now.to_time, place: "Pool", time_frame: 200, - limit_of_participants: 2, - creator: creator + limit_of_participants: 2 ) - activity.participants << participant activity.save! + activity.new_participation(participant).save! end From cd1b288b7302ae28c75e7178f28d30341c7a23b1 Mon Sep 17 00:00:00 2001 From: pietia Date: Tue, 28 May 2013 22:20:23 +0200 Subject: [PATCH 089/499] fixed Participation factory --- spec/factories/participations.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/factories/participations.rb b/spec/factories/participations.rb index b5470bf1..a4c17b74 100644 --- a/spec/factories/participations.rb +++ b/spec/factories/participations.rb @@ -2,7 +2,7 @@ FactoryGirl.define do factory :participation do - user_id 1 - activity_id 1 + participant{ FactoryGirl.create(:user) } + activity end end From 1e9e2f7931731150b1b7566fa8fe926163b20e5d Mon Sep 17 00:00:00 2001 From: pietia Date: Tue, 28 May 2013 22:33:37 +0200 Subject: [PATCH 090/499] added specs for validations in Participation model --- spec/models/participation_spec.rb | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/spec/models/participation_spec.rb b/spec/models/participation_spec.rb index bb67b4fc..d8d80643 100644 --- a/spec/models/participation_spec.rb +++ b/spec/models/participation_spec.rb @@ -1,4 +1,17 @@ require 'spec_helper' describe Participation do + + describe "validations" do + + it { should accept_values_for(:user_id, 1 ) } + it { should_not accept_values_for(:user_id, "", nil) } + + it { should accept_values_for(:activity_id, 1 ) } + it { should_not accept_values_for(:activity_id, "", nil) } + + specify { FactoryGirl.create(:participation).clone.save! }.to raise(ActiveRecord::RecordInvalid) + + end + end From d1076c0c5d8bdc0b3fce342cd5622cd669aa818b Mon Sep 17 00:00:00 2001 From: pietia Date: Tue, 28 May 2013 22:38:37 +0200 Subject: [PATCH 091/499] basic validations in Participation and routes added --- app/models/participation.rb | 3 +++ config/routes.rb | 4 +++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/app/models/participation.rb b/app/models/participation.rb index cc16dc7b..b32ae460 100644 --- a/app/models/participation.rb +++ b/app/models/participation.rb @@ -1,4 +1,7 @@ class Participation < ActiveRecord::Base belongs_to :participant, class_name: "User", foreign_key: "user_id" belongs_to :activity, counter_cache: true + + validates :activity_id, presence: true, uniqueness: {scope: [:user_id]} + validates :user_id, presence: true end diff --git a/config/routes.rb b/config/routes.rb index cb01140e..3683d1ca 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -8,6 +8,8 @@ passwords: 'passwords' } - resources :activities, only: [:create, :destroy, :update, :index] + resources :activities, only: [:create, :destroy, :update, :index] do + resource :participation, only: [:create, :destroy] + end root to: 'activities#index' end From ed560902227b916bcd770009d51f9dced28a69ad Mon Sep 17 00:00:00 2001 From: pietia Date: Wed, 29 May 2013 14:50:43 +0200 Subject: [PATCH 092/499] more work on ParticipationsController --- app/controllers/participations_controller.rb | 22 ++++++++++ app/models/activity.rb | 4 ++ .../participation_controller_spec.rb | 41 +++++++++++++++++++ 3 files changed, 67 insertions(+) create mode 100644 app/controllers/participations_controller.rb create mode 100644 spec/controllers/participation_controller_spec.rb diff --git a/app/controllers/participations_controller.rb b/app/controllers/participations_controller.rb new file mode 100644 index 00000000..b7508ef2 --- /dev/null +++ b/app/controllers/participations_controller.rb @@ -0,0 +1,22 @@ +class ParticipationsController < ApplicationController + respond_to :json + + def create + participation = current_activity.new_participation(current_user) + participation.save + respond_with(current_activity, participation) + end + + def destroy + participation = current_activity.participation(current_user) + participation.destroy if participation && participation.participant == current_user #TODO + respond_with(current_activity, participation) + end + + private + + def current_activity + @current_activity ||= current_event.activity(params[:activity_id]) + end + +end diff --git a/app/models/activity.rb b/app/models/activity.rb index f2030f46..232940ce 100644 --- a/app/models/activity.rb +++ b/app/models/activity.rb @@ -29,6 +29,10 @@ def new_participation(user) end end + def participation(user) + participations.find_by(user_id: user) + end + private def participation_source diff --git a/spec/controllers/participation_controller_spec.rb b/spec/controllers/participation_controller_spec.rb new file mode 100644 index 00000000..0df5f55b --- /dev/null +++ b/spec/controllers/participation_controller_spec.rb @@ -0,0 +1,41 @@ +require 'spec_helper' + +describe ParticipationsController do + let(:user) { FactoryGirl.create(:user) } + let(:activity) { FactoryGirl.create(:activity) } + + describe "#create" do + context "format :json" do + subject { post :create, {activity_id: activity.id, format: :json} } + + before do + sign_in(user) + end + + context "valid parameters" do + its(:status) { should == 201 } + end + + context "user is already a participant" do + before do + Participation.create(participant: user, activity: activity) + end + + its(:status) { should == 422 } + end + end + end + + describe "#destroy" do + context "format :json" do + subject { delete :destroy, {activity_id: activity.id, format: :json} } + + before do + sign_in(user) + end + + its(:status) { should == 204 } + end + end + +end \ No newline at end of file From 5f087a37d21ca83dbb64c10410b61c8afbaa9afc Mon Sep 17 00:00:00 2001 From: pietia Date: Wed, 29 May 2013 15:29:30 +0200 Subject: [PATCH 093/499] basic GET activities/:id added --- app/controllers/activities_controller.rb | 7 ++++++- config/routes.rb | 2 +- spec/controllers/activities_controller_spec.rb | 7 +++++++ spec/models/participation_spec.rb | 4 +++- 4 files changed, 17 insertions(+), 3 deletions(-) diff --git a/app/controllers/activities_controller.rb b/app/controllers/activities_controller.rb index 8a17577c..876b0d8e 100644 --- a/app/controllers/activities_controller.rb +++ b/app/controllers/activities_controller.rb @@ -2,13 +2,18 @@ class ActivitiesController < ApplicationController respond_to :json, :html, :only => :index respond_to :json - skip_before_filter :authenticate_user!, :only => :index + skip_before_filter :authenticate_user!, :only => [:index, :show] def index @activities = current_event.activities.decorate respond_with(@activities) end + def show + @activity = current_event.activity(params.require(:id)) + respond_with(@activity) + end + def create @activity = current_event.new_activity(current_user, sanitized_params) @activity.save diff --git a/config/routes.rb b/config/routes.rb index 3683d1ca..081b8cfa 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -8,7 +8,7 @@ passwords: 'passwords' } - resources :activities, only: [:create, :destroy, :update, :index] do + resources :activities, only: [:create, :destroy, :update, :index, :show] do resource :participation, only: [:create, :destroy] end root to: 'activities#index' diff --git a/spec/controllers/activities_controller_spec.rb b/spec/controllers/activities_controller_spec.rb index fe7367d8..ec714b3f 100644 --- a/spec/controllers/activities_controller_spec.rb +++ b/spec/controllers/activities_controller_spec.rb @@ -2,6 +2,7 @@ describe ActivitiesController do let(:user) { FactoryGirl.create(:user) } + let(:activity) { FactoryGirl.create(:activity) } describe "#index" do subject { get :index } @@ -10,6 +11,12 @@ its(:status){ should == 200 } end + describe "#show" do + subject { get :show, {id: activity.id, format: :json} } + + its(:status){ should == 200 } + end + describe "#create" do context "format :json" do subject { post :create, params.merge({format: :json}) } diff --git a/spec/models/participation_spec.rb b/spec/models/participation_spec.rb index d8d80643..3e84cdb0 100644 --- a/spec/models/participation_spec.rb +++ b/spec/models/participation_spec.rb @@ -10,7 +10,9 @@ it { should accept_values_for(:activity_id, 1 ) } it { should_not accept_values_for(:activity_id, "", nil) } - specify { FactoryGirl.create(:participation).clone.save! }.to raise(ActiveRecord::RecordInvalid) + specify { + FactoryGirl.create(:participation).clone.save! + }.to raise(ActiveRecord::RecordInvalid) end From a79e06e91b55208520d311bd9cbae7f8989c6014 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Wed, 29 May 2013 16:40:42 +0300 Subject: [PATCH 094/499] Javascript redirect for 401s. --- app/assets/javascripts/initializers.coffee | 4 ++++ app/views/layouts/application.html.haml | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/app/assets/javascripts/initializers.coffee b/app/assets/javascripts/initializers.coffee index c2067b8e..788c1997 100644 --- a/app/assets/javascripts/initializers.coffee +++ b/app/assets/javascripts/initializers.coffee @@ -1,4 +1,8 @@ $ -> + $(document).ajaxError (e, xhr) -> + console.error "AJAX ERRPR!" + window.location.replace(App.paths.login) if xhr.status == 401 + # show how "full" an activity is showProgress = (img)-> $(img).progress() $('#activities img.progress').each -> diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml index 43cb9e68..99d38de7 100644 --- a/app/views/layouts/application.html.haml +++ b/app/views/layouts/application.html.haml @@ -17,6 +17,11 @@ :javascript try{Typekit.load();}catch(e){} + var App = { + paths: { + login: '#{new_user_session_path}' + } + }; %body(class=body_class) = render "partials/chrome_frame" #container From 4f43cfb3de08aa85f8e8cadc35b18e803982c5a0 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Wed, 29 May 2013 16:40:50 +0300 Subject: [PATCH 095/499] WIP join button for testing. --- app/views/activities/index.html.haml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/views/activities/index.html.haml b/app/views/activities/index.html.haml index 497f4d00..483bca5d 100644 --- a/app/views/activities/index.html.haml +++ b/app/views/activities/index.html.haml @@ -58,7 +58,8 @@ %span Don’t participate? - else - %a(href="#" title="Sign-up as participant") + -# %a(href="#" title="Sign-up as participant") + = button_to activity_participation_path(activity), :method => :post, :disable_with => 'loading...', :remote => true do %span Join From c11ce9f9ad972bf59259df2d0fffc2390441e59d Mon Sep 17 00:00:00 2001 From: pietia Date: Wed, 29 May 2013 16:09:21 +0200 Subject: [PATCH 096/499] rails must die --- app/controllers/application_controller.rb | 2 +- app/models/activity.rb | 3 +++ config/initializers/event.rb | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index f67c7e7d..51a73791 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -7,6 +7,6 @@ class ApplicationController < ActionController::Base before_filter :authenticate_user! def current_event - EVENT + @current_event ||= Event.new(Settings.event.name) end end diff --git a/app/models/activity.rb b/app/models/activity.rb index 232940ce..b05f7a23 100644 --- a/app/models/activity.rb +++ b/app/models/activity.rb @@ -18,6 +18,9 @@ def self.recent(limit = DEFAULT_LIMIT) where("start_at >= :t", t: 1.month.ago).limit(limit) end + + + def full_by limit_of_participants.nil? ? 0 : [100.0 * participations_count / limit_of_participants.to_f, 100.0].min end diff --git a/config/initializers/event.rb b/config/initializers/event.rb index da3355f2..884e46d6 100644 --- a/config/initializers/event.rb +++ b/config/initializers/event.rb @@ -1 +1 @@ -EVENT = Event.new(Settings.event.name) \ No newline at end of file +#EVENT = Event.new(Settings.event.name) \ No newline at end of file From 52016fc0e841f5fcf65f92c4000fe82f1c0db222 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Wed, 29 May 2013 17:12:08 +0300 Subject: [PATCH 097/499] Clean-up. --- app/assets/javascripts/initializers.coffee | 2 +- app/views/layouts/application.html.haml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/app/assets/javascripts/initializers.coffee b/app/assets/javascripts/initializers.coffee index 788c1997..fae57546 100644 --- a/app/assets/javascripts/initializers.coffee +++ b/app/assets/javascripts/initializers.coffee @@ -22,7 +22,7 @@ $ -> # the filter tabs values = $filters.filter('.selected') .find('input[type=radio]') - .map(-> return $(@).val()).get() + .map(-> $(@).val()).get() unless 'all' in values $activities.not(".#{values.join(',.')}").hide() # use search input to filter further diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml index 99d38de7..1484f3c8 100644 --- a/app/views/layouts/application.html.haml +++ b/app/views/layouts/application.html.haml @@ -22,6 +22,7 @@ login: '#{new_user_session_path}' } }; + %body(class=body_class) = render "partials/chrome_frame" #container From 2f48e6535d2b0308ce9c4f0d49f72da2aab9d30e Mon Sep 17 00:00:00 2001 From: pietia Date: Wed, 29 May 2013 16:45:11 +0200 Subject: [PATCH 098/499] simplified code a bit --- app/controllers/activities_controller.rb | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/app/controllers/activities_controller.rb b/app/controllers/activities_controller.rb index 876b0d8e..16e02077 100644 --- a/app/controllers/activities_controller.rb +++ b/app/controllers/activities_controller.rb @@ -10,26 +10,25 @@ def index end def show - @activity = current_event.activity(params.require(:id)) - respond_with(@activity) + respond_with(current_event.activity(params[:id])) end def create - @activity = current_event.new_activity(current_user, sanitized_params) - @activity.save - respond_with(@activity) + activity = current_event.new_activity(current_user, sanitized_params) + activity.save + respond_with(activity) end def update - @activity = current_event.activity(params[:id]) - @activity.update_attributes(sanitized_params) - respond_with(@activity) + activity = current_event.activity(params[:id]) + activity.update_attributes(sanitized_params) + respond_with(activity) end def destroy - @activity = current_event.activity(params[:id]) - @activity.destroy if @activity && @activity.creator == current_user #TODO - respond_with(@activity) + activity = current_event.activity(params[:id]) + activity.destroy if activity && activity.creator == current_user #TODO + respond_with(activity) end private From 802ace1ce02cead188c07bc58571683b947dd2ca Mon Sep 17 00:00:00 2001 From: Polarblau Date: Wed, 29 May 2013 18:14:29 +0300 Subject: [PATCH 099/499] Basic joining and leaving. --- app/assets/stylesheets/partials/_activities.sass | 6 ++++-- app/decorators/activity_decorator.rb | 2 +- app/views/activities/index.html.haml | 7 +++---- app/views/layouts/application.html.haml | 2 +- config/routes.rb | 2 +- 5 files changed, 10 insertions(+), 9 deletions(-) diff --git a/app/assets/stylesheets/partials/_activities.sass b/app/assets/stylesheets/partials/_activities.sass index fcca88ac..e6c1afc1 100644 --- a/app/assets/stylesheets/partials/_activities.sass +++ b/app/assets/stylesheets/partials/_activities.sass @@ -116,7 +116,9 @@ a#new-activity +transform(scale(0.9)) +transition(all 0.35s ease-in-out 0) - a + a, button + border: none + background: none +custom-sans(bold) @extend %smaller-text text-transform: uppercase @@ -188,7 +190,7 @@ a#new-activity @extend %icon-#{nth($type, 3)} .action - a + a, button color: nth($type, 2) span diff --git a/app/decorators/activity_decorator.rb b/app/decorators/activity_decorator.rb index e2deade8..490d4e0f 100644 --- a/app/decorators/activity_decorator.rb +++ b/app/decorators/activity_decorator.rb @@ -8,7 +8,7 @@ def creator_name def relation_ship_with(user) if user.nil? then "default" elsif object.creator == user then "owner" - #elsif object.participants.include? user then "participant" + elsif object.participants.include? user then "participant" else "default" end end diff --git a/app/views/activities/index.html.haml b/app/views/activities/index.html.haml index 483bca5d..cb023e9f 100644 --- a/app/views/activities/index.html.haml +++ b/app/views/activities/index.html.haml @@ -50,16 +50,15 @@ .action - case type - when "owner" - %a(href="#" title="Edit the activity details") + = link_to edit_activity_path(activity), title: "Edit the activity details" do %span Edit - when "participant" - %a(href="#" title="Cancel your participation") + = button_to activity_participation_path(activity), method: :delete, remote: true, title: "Cancel your participation" do %span Don’t participate? - else - -# %a(href="#" title="Sign-up as participant") - = button_to activity_participation_path(activity), :method => :post, :disable_with => 'loading...', :remote => true do + = button_to activity_participation_path(activity), method: :post, remote: true, title: "Sign-up as participant" do %span Join diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml index 1484f3c8..1a9e72e1 100644 --- a/app/views/layouts/application.html.haml +++ b/app/views/layouts/application.html.haml @@ -13,7 +13,7 @@ = stylesheet_link_tag "application" = javascript_include_tag "modernizr.min" - = javascript_include_tag "//use.typekit.net/vor5lqb.js" + -#= javascript_include_tag "//use.typekit.net/vor5lqb.js" :javascript try{Typekit.load();}catch(e){} diff --git a/config/routes.rb b/config/routes.rb index 081b8cfa..2b0c6924 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -8,7 +8,7 @@ passwords: 'passwords' } - resources :activities, only: [:create, :destroy, :update, :index, :show] do + resources :activities, only: [:create, :destroy, :update, :index, :show, :edit] do resource :participation, only: [:create, :destroy] end root to: 'activities#index' From d17511fa43e0e68682d2d970605bc81cc166f988 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Wed, 29 May 2013 18:18:53 +0300 Subject: [PATCH 100/499] Partial for activity. --- app/views/activities/_activity.html.haml | 34 +++++++++++++++++++++ app/views/activities/index.html.haml | 38 +----------------------- 2 files changed, 35 insertions(+), 37 deletions(-) create mode 100644 app/views/activities/_activity.html.haml diff --git a/app/views/activities/_activity.html.haml b/app/views/activities/_activity.html.haml new file mode 100644 index 00000000..c861b318 --- /dev/null +++ b/app/views/activities/_activity.html.haml @@ -0,0 +1,34 @@ +- type = activity.relation_ship_with(current_user) + +%li(id="activity-#{activity.id}" class="#{type} #{activity.status}") + .container + + = image_tag "dummies/activity.jpg", alt: activity.name, class: "progress", data: { progress: activity.full_by } + + .labels + %h4 + = activity.name + %p.creator + = activity.creator_name + %p.time + = activity.time + + .action + - case type + - when "owner" + = link_to edit_activity_path(activity), title: "Edit the activity details" do + %span + Edit + - when "participant" + = button_to activity_participation_path(activity), method: :delete, remote: true, title: "Cancel your participation" do + %span + Don’t participate? + - else + = button_to activity_participation_path(activity), method: :post, remote: true, title: "Sign-up as participant" do + %span + Join + + %a.details(href="#" title="Learn more about this activity") + %span More Info + - unless type == 'default' + %span.icon diff --git a/app/views/activities/index.html.haml b/app/views/activities/index.html.haml index cb023e9f..2ab0b115 100644 --- a/app/views/activities/index.html.haml +++ b/app/views/activities/index.html.haml @@ -29,40 +29,4 @@ %span %ul#activities - -# just render a collection and move - -# card into partial - - @activities.each do |activity| - - type = activity.relation_ship_with(current_user) - - %li(id="activity-#{activity.id}" class="#{type} #{activity.status}") - .container - - = image_tag "dummies/activity.jpg", alt: activity.name, class: "progress", data: { progress: activity.full_by } - - .labels - %h4 - = activity.name - %p.creator - = activity.creator_name - %p.time - = activity.time - - .action - - case type - - when "owner" - = link_to edit_activity_path(activity), title: "Edit the activity details" do - %span - Edit - - when "participant" - = button_to activity_participation_path(activity), method: :delete, remote: true, title: "Cancel your participation" do - %span - Don’t participate? - - else - = button_to activity_participation_path(activity), method: :post, remote: true, title: "Sign-up as participant" do - %span - Join - - %a.details(href="#" title="Learn more about this activity") - %span More Info - - unless type == 'default' - %span.icon + = render @activities From f5ec1293243ac32b58b6e7a919c4023388a85502 Mon Sep 17 00:00:00 2001 From: pietia Date: Wed, 29 May 2013 17:19:45 +0200 Subject: [PATCH 101/499] added missing actions (:new, :edit) --- app/controllers/activities_controller.rb | 35 ++++++++++++++++-------- config/routes.rb | 2 +- 2 files changed, 24 insertions(+), 13 deletions(-) diff --git a/app/controllers/activities_controller.rb b/app/controllers/activities_controller.rb index 16e02077..ae49ae86 100644 --- a/app/controllers/activities_controller.rb +++ b/app/controllers/activities_controller.rb @@ -1,6 +1,6 @@ class ActivitiesController < ApplicationController - respond_to :json, :html, :only => :index - respond_to :json + respond_to :html, only: [:new, :edit] + respond_to :json, :html skip_before_filter :authenticate_user!, :only => [:index, :show] @@ -10,25 +10,36 @@ def index end def show - respond_with(current_event.activity(params[:id])) + @activity = current_event.activity(params[:id]) + respond_with(@activity) + end + + def new + @activity = current_event.new_activity(current_user, {}) + respond_with(@activity) + end + + def edit + @activity = current_event.activity(params[:id]) + respond_with(@activity) end def create - activity = current_event.new_activity(current_user, sanitized_params) - activity.save - respond_with(activity) + @activity = current_event.new_activity(current_user, sanitized_params) + @activity.save + respond_with(@activity) end def update - activity = current_event.activity(params[:id]) - activity.update_attributes(sanitized_params) - respond_with(activity) + @activity = current_event.activity(params[:id]) + @activity.update_attributes(sanitized_params) + respond_with(@activity) end def destroy - activity = current_event.activity(params[:id]) - activity.destroy if activity && activity.creator == current_user #TODO - respond_with(activity) + @activity = current_event.activity(params[:id]) + @activity.destroy if @activity && @activity.creator == current_user #TODO + respond_with(@activity) end private diff --git a/config/routes.rb b/config/routes.rb index 081b8cfa..784928a6 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -8,7 +8,7 @@ passwords: 'passwords' } - resources :activities, only: [:create, :destroy, :update, :index, :show] do + resources :activities do resource :participation, only: [:create, :destroy] end root to: 'activities#index' From f595b45910759bd8aa6fcef289e9eabe360e51d6 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Wed, 29 May 2013 18:33:19 +0300 Subject: [PATCH 102/499] Translating activity partial. --- app/views/activities/_activity.html.haml | 17 +++++++++-------- config/locales/en.yml | 13 +++++++++++++ 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/app/views/activities/_activity.html.haml b/app/views/activities/_activity.html.haml index c861b318..0539e5ba 100644 --- a/app/views/activities/_activity.html.haml +++ b/app/views/activities/_activity.html.haml @@ -16,19 +16,20 @@ .action - case type - when "owner" - = link_to edit_activity_path(activity), title: "Edit the activity details" do + = link_to edit_activity_path(activity), title: t("activities.edit.title") do %span - Edit + = t("activities.edit.label") - when "participant" - = button_to activity_participation_path(activity), method: :delete, remote: true, title: "Cancel your participation" do + = button_to activity_participation_path(activity), method: :delete, remote: true, title: t("activities.leave.title") do %span - Don’t participate? + = t("activities.leave.label") - else - = button_to activity_participation_path(activity), method: :post, remote: true, title: "Sign-up as participant" do + = button_to activity_participation_path(activity), method: :post, remote: true, title: t("activities.join.title") do %span - Join + = t("activities.join.label") - %a.details(href="#" title="Learn more about this activity") - %span More Info + %a.details(href="#" title="#{ t("activities.more_info.title") }") + %span + = t("activities.more_info.label") - unless type == 'default' %span.icon diff --git a/config/locales/en.yml b/config/locales/en.yml index a7241c07..8c4383d6 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -13,6 +13,19 @@ en: label: Register title: Register new account + activities: + join: + label: Join + title: Sign-up as participant + edit: + label: Edit + title: Edit the activity details + leave: + label: Cancel + title: Cancel your participation + more_info: + label: More info + title: Learn more about this activity footer_nav: about: From 8e0aca841eb2be18f085a1558a11aec949339c87 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Wed, 29 May 2013 18:40:25 +0300 Subject: [PATCH 103/499] Translating activity index. --- app/views/activities/index.html.haml | 20 +++++++++++--------- config/locales/en.yml | 12 ++++++++++++ 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/app/views/activities/index.html.haml b/app/views/activities/index.html.haml index 2ab0b115..3844eb17 100644 --- a/app/views/activities/index.html.haml +++ b/app/views/activities/index.html.haml @@ -1,31 +1,33 @@ %form.filters(action=activities_path method="GET") %label> %input(type="radio" name="filter" value="today") - Today + = t("activities.filters.today") %strong 5 %label.all.selected> %input(type="radio" name="filter" value="all") - All + = t("activities.filters.all") %strong 267 %label.participant> %input(type="radio" name="filter" value="participant") - My activities + = t("activities.filters.participant") %strong 2 %label.owner> %input(type="radio" name="filter" value="owner") - Created by me + = t("activities.filters.owner") %strong 0 %label.search - %input(type="text" name="search" placeholder="Search for an activity") - %a.clear(href="#" title="Reset") Clear - %button(type="submit") Filter + %input(type="text" name="search" placeholder="#{ t("activities.search.placeholder") }") + %a.clear(href="#" title="Reset") + = t("activities.search.clear") + %button(type="submit") + = t("activities.filters.submit") -%a#new-activity(href="#" title="Create a new activity") - Create an activity +%a#new-activity(href="#" title="#{ t("activities.new.title") }") + = t("activities.new.label") %span %ul#activities diff --git a/config/locales/en.yml b/config/locales/en.yml index 8c4383d6..4d1083cf 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -14,6 +14,18 @@ en: title: Register new account activities: + new: + label: Create an activity + title: Create a new activity + filters: + today: Today + all: All + owner: Created by me + participant: My activities + submit: Filter + search: + placeholder: Search for an activity + clear: Clear join: label: Join title: Sign-up as participant From b858fe577f87af8c4ee832ae95e5c7d0ad584bc0 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Wed, 29 May 2013 20:05:39 +0300 Subject: [PATCH 104/499] Joining and leaving, ajaxified. --- app/assets/javascripts/initializers.coffee | 2 +- app/controllers/participations_controller.rb | 4 ++-- app/views/participations/create.js.erb | 5 +++++ app/views/participations/destroy.js.erb | 5 +++++ 4 files changed, 13 insertions(+), 3 deletions(-) create mode 100644 app/views/participations/create.js.erb create mode 100644 app/views/participations/destroy.js.erb diff --git a/app/assets/javascripts/initializers.coffee b/app/assets/javascripts/initializers.coffee index fae57546..50f1cd07 100644 --- a/app/assets/javascripts/initializers.coffee +++ b/app/assets/javascripts/initializers.coffee @@ -1,6 +1,6 @@ $ -> $(document).ajaxError (e, xhr) -> - console.error "AJAX ERRPR!" + console.error "AJAX ERRPR!", arguments window.location.replace(App.paths.login) if xhr.status == 401 # show how "full" an activity is diff --git a/app/controllers/participations_controller.rb b/app/controllers/participations_controller.rb index b7508ef2..8f343e9c 100644 --- a/app/controllers/participations_controller.rb +++ b/app/controllers/participations_controller.rb @@ -1,5 +1,5 @@ class ParticipationsController < ApplicationController - respond_to :json + respond_to :js, :html def create participation = current_activity.new_participation(current_user) @@ -16,7 +16,7 @@ def destroy private def current_activity - @current_activity ||= current_event.activity(params[:activity_id]) + @current_activity ||= current_event.activity(params[:activity_id]).decorate end end diff --git a/app/views/participations/create.js.erb b/app/views/participations/create.js.erb new file mode 100644 index 00000000..0fe2a2c5 --- /dev/null +++ b/app/views/participations/create.js.erb @@ -0,0 +1,5 @@ +$('#activity-<%= @current_activity.id %>') + .replaceWith('<%= escape_javascript(render partial: 'activities/activity', locals: { activity: @current_activity }) %>'); +$('#activity-<%= @current_activity.id %>') + .find('img.progress') + .progress(); diff --git a/app/views/participations/destroy.js.erb b/app/views/participations/destroy.js.erb new file mode 100644 index 00000000..0fe2a2c5 --- /dev/null +++ b/app/views/participations/destroy.js.erb @@ -0,0 +1,5 @@ +$('#activity-<%= @current_activity.id %>') + .replaceWith('<%= escape_javascript(render partial: 'activities/activity', locals: { activity: @current_activity }) %>'); +$('#activity-<%= @current_activity.id %>') + .find('img.progress') + .progress(); From 3c2668ce598a653f867bec4159abab975e8f565d Mon Sep 17 00:00:00 2001 From: Polarblau Date: Wed, 29 May 2013 20:09:36 +0300 Subject: [PATCH 105/499] Testing for no participants. --- spec/models/activity_spec.rb | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/spec/models/activity_spec.rb b/spec/models/activity_spec.rb index 5397971c..b243925c 100644 --- a/spec/models/activity_spec.rb +++ b/spec/models/activity_spec.rb @@ -68,6 +68,14 @@ it { should == 80 } end + context "no participants" do + before do + activity.stub!(:limit_of_participants).and_return(10) + activity.stub!(:participations_count).and_return(0) + end + + it { should == 0 } + end end describe "validations" do @@ -93,4 +101,4 @@ end -end \ No newline at end of file +end From e2e182687baf024162cf9fdd05715e2ca113f3af Mon Sep 17 00:00:00 2001 From: Polarblau Date: Wed, 29 May 2013 20:13:50 +0300 Subject: [PATCH 106/499] Reloading activity. --- app/controllers/participations_controller.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/controllers/participations_controller.rb b/app/controllers/participations_controller.rb index 8f343e9c..17497da1 100644 --- a/app/controllers/participations_controller.rb +++ b/app/controllers/participations_controller.rb @@ -4,13 +4,13 @@ class ParticipationsController < ApplicationController def create participation = current_activity.new_participation(current_user) participation.save - respond_with(current_activity, participation) + respond_with(current_activity.reload, participation) end def destroy participation = current_activity.participation(current_user) participation.destroy if participation && participation.participant == current_user #TODO - respond_with(current_activity, participation) + respond_with(current_activity.reload, participation) end private From acbeedb42c71029ad0f9f7bff997c1ed6836c6c0 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Wed, 29 May 2013 20:23:32 +0300 Subject: [PATCH 107/499] Redirecting without JS. --- app/controllers/participations_controller.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/controllers/participations_controller.rb b/app/controllers/participations_controller.rb index 17497da1..22374e91 100644 --- a/app/controllers/participations_controller.rb +++ b/app/controllers/participations_controller.rb @@ -4,13 +4,13 @@ class ParticipationsController < ApplicationController def create participation = current_activity.new_participation(current_user) participation.save - respond_with(current_activity.reload, participation) + respond_with(current_activity.reload, participation, location: activities_path) end def destroy participation = current_activity.participation(current_user) participation.destroy if participation && participation.participant == current_user #TODO - respond_with(current_activity.reload, participation) + respond_with(current_activity.reload, participation, location: activities_path) end private From d5a9c1ddcc528a2e829daeb2dc7d2d803ff81d3e Mon Sep 17 00:00:00 2001 From: Polarblau Date: Wed, 29 May 2013 20:26:04 +0300 Subject: [PATCH 108/499] Fixing HTML entity. --- app/decorators/activity_decorator.rb | 2 +- app/views/activities/_activity.html.haml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/decorators/activity_decorator.rb b/app/decorators/activity_decorator.rb index 490d4e0f..91820315 100644 --- a/app/decorators/activity_decorator.rb +++ b/app/decorators/activity_decorator.rb @@ -24,7 +24,7 @@ def status def time # Case for "Anytime" needed object.start_at.strftime("%A, %-d.%-m / %k:%M – ") + - (object.start_at + object.time_frame.minutes).strftime("%k:%M").html_safe + (object.start_at + object.time_frame.minutes).strftime("%k:%M") end end diff --git a/app/views/activities/_activity.html.haml b/app/views/activities/_activity.html.haml index 0539e5ba..ada7b95c 100644 --- a/app/views/activities/_activity.html.haml +++ b/app/views/activities/_activity.html.haml @@ -11,7 +11,7 @@ %p.creator = activity.creator_name %p.time - = activity.time + = activity.time.html_safe .action - case type From 3df62d6f43bfa588c3384f769731c590328ffd2b Mon Sep 17 00:00:00 2001 From: Polarblau Date: Wed, 29 May 2013 20:47:17 +0300 Subject: [PATCH 109/499] Additional fields for activity. --- app/views/activities/index.html.haml | 2 +- app/views/activities/new.html.haml | 10 ++++++++++ ...20130529174513_add_additional_fields_to_activity.rb | 6 ++++++ db/schema.rb | 8 +++++--- 4 files changed, 22 insertions(+), 4 deletions(-) create mode 100644 app/views/activities/new.html.haml create mode 100644 db/migrate/20130529174513_add_additional_fields_to_activity.rb diff --git a/app/views/activities/index.html.haml b/app/views/activities/index.html.haml index 3844eb17..a1d04fee 100644 --- a/app/views/activities/index.html.haml +++ b/app/views/activities/index.html.haml @@ -26,7 +26,7 @@ %button(type="submit") = t("activities.filters.submit") -%a#new-activity(href="#" title="#{ t("activities.new.title") }") +%a#new-activity(href="#{ new_activity_path }" title="#{ t("activities.new.title") }") = t("activities.new.label") %span diff --git a/app/views/activities/new.html.haml b/app/views/activities/new.html.haml new file mode 100644 index 00000000..b70f997d --- /dev/null +++ b/app/views/activities/new.html.haml @@ -0,0 +1,10 @@ +%h2 + Organize an activity + += form_for @activity, id: "new-activity" do |f| + = f.text_field :name, placeholder: "Give your activity a name", class: "title" + %hr + = f.text_field :start_at, placeholder: Date.current.strftime("%a, %-d.%-m"), class: "date" + + + %button(type="submit") Save diff --git a/db/migrate/20130529174513_add_additional_fields_to_activity.rb b/db/migrate/20130529174513_add_additional_fields_to_activity.rb new file mode 100644 index 00000000..4f0ca4cf --- /dev/null +++ b/db/migrate/20130529174513_add_additional_fields_to_activity.rb @@ -0,0 +1,6 @@ +class AddAdditionalFieldsToActivity < ActiveRecord::Migration + def change + add_column :activities, :anytime, :boolean, default: false + add_column :activities, :requirements, :text + end +end diff --git a/db/schema.rb b/db/schema.rb index f47c508a..e9a1bcb7 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,22 +11,24 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20130527054241) do +ActiveRecord::Schema.define(version: 20130529174513) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" create_table "activities", force: true do |t| - t.string "name", default: "", null: false + t.string "name", default: "", null: false t.text "description" t.string "place" - t.datetime "start_at", null: false + t.datetime "start_at", null: false t.integer "time_frame" t.integer "limit_of_participants" t.datetime "created_at" t.datetime "updated_at" t.integer "creator_id" t.integer "participations_count", default: 0 + t.boolean "anytime", default: false + t.text "requirements" end add_index "activities", ["name"], name: "index_activities_on_name", unique: true, using: :btree From 74b8eb51ba2558835eb014c6af7f2901b5ffc38c Mon Sep 17 00:00:00 2001 From: Polarblau Date: Wed, 29 May 2013 20:57:59 +0300 Subject: [PATCH 110/499] WIP new activity. --- app/models/activity.rb | 10 ++++++++++ app/views/activities/new.html.haml | 10 +++++++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/app/models/activity.rb b/app/models/activity.rb index b05f7a23..724dd144 100644 --- a/app/models/activity.rb +++ b/app/models/activity.rb @@ -18,8 +18,18 @@ def self.recent(limit = DEFAULT_LIMIT) where("start_at >= :t", t: 1.month.ago).limit(limit) end + def raw_start_date + start_at ? start_at.strftime('%a, %-d.%-m') : '' + end + def raw_start_time + start_at ? start_at.strftime('%k:%M') : '' + end + def raw_end_time + # TODO: move into method, not safe for + start_at && time_frame ? (start_at + time_frame.minutes).strftime('%k:%M') : '' + end def full_by limit_of_participants.nil? ? 0 : [100.0 * participations_count / limit_of_participants.to_f, 100.0].min diff --git a/app/views/activities/new.html.haml b/app/views/activities/new.html.haml index b70f997d..9bfc62ca 100644 --- a/app/views/activities/new.html.haml +++ b/app/views/activities/new.html.haml @@ -4,7 +4,15 @@ = form_for @activity, id: "new-activity" do |f| = f.text_field :name, placeholder: "Give your activity a name", class: "title" %hr - = f.text_field :start_at, placeholder: Date.current.strftime("%a, %-d.%-m"), class: "date" + = f.text_field :raw_start_date, placeholder: Date.current.strftime("%a, %-d.%-m"), class: "date" + = f.text_field :raw_start_time, placeholder: Date.current.strftime("%k:%M"), class: "time" + – + = f.text_field :raw_end_time, placeholder: Date.current.strftime("%k:%M"), class: "time" + %span or + %label + = f.check_box :anytime + Without a specific date/time + %button(type="submit") Save From b748a8db82b6b8ff472fa7a95bb21da28c66fc31 Mon Sep 17 00:00:00 2001 From: pietia Date: Wed, 29 May 2013 20:27:41 +0200 Subject: [PATCH 111/499] better timestamps --- .../20130529181136_remove_time_frame_from_activities.rb | 5 +++++ db/migrate/20130529181823_add_end_time_to_activities.rb | 5 +++++ db/migrate/20130529181849_rename_start_at_in_activities.rb | 5 +++++ db/migrate/20130529182104_start_time_can_be_null.rb | 5 +++++ db/schema.rb | 6 +++--- 5 files changed, 23 insertions(+), 3 deletions(-) create mode 100644 db/migrate/20130529181136_remove_time_frame_from_activities.rb create mode 100644 db/migrate/20130529181823_add_end_time_to_activities.rb create mode 100644 db/migrate/20130529181849_rename_start_at_in_activities.rb create mode 100644 db/migrate/20130529182104_start_time_can_be_null.rb diff --git a/db/migrate/20130529181136_remove_time_frame_from_activities.rb b/db/migrate/20130529181136_remove_time_frame_from_activities.rb new file mode 100644 index 00000000..2ba8bfa5 --- /dev/null +++ b/db/migrate/20130529181136_remove_time_frame_from_activities.rb @@ -0,0 +1,5 @@ +class RemoveTimeFrameFromActivities < ActiveRecord::Migration + def change + remove_column :activities, :time_frame + end +end diff --git a/db/migrate/20130529181823_add_end_time_to_activities.rb b/db/migrate/20130529181823_add_end_time_to_activities.rb new file mode 100644 index 00000000..8b5b659b --- /dev/null +++ b/db/migrate/20130529181823_add_end_time_to_activities.rb @@ -0,0 +1,5 @@ +class AddEndTimeToActivities < ActiveRecord::Migration + def change + add_column :activities, :end_time, :datetime + end +end diff --git a/db/migrate/20130529181849_rename_start_at_in_activities.rb b/db/migrate/20130529181849_rename_start_at_in_activities.rb new file mode 100644 index 00000000..32bbe9f9 --- /dev/null +++ b/db/migrate/20130529181849_rename_start_at_in_activities.rb @@ -0,0 +1,5 @@ +class RenameStartAtInActivities < ActiveRecord::Migration + def change + rename_column :activities, :start_at, :start_time + end +end diff --git a/db/migrate/20130529182104_start_time_can_be_null.rb b/db/migrate/20130529182104_start_time_can_be_null.rb new file mode 100644 index 00000000..35109a9e --- /dev/null +++ b/db/migrate/20130529182104_start_time_can_be_null.rb @@ -0,0 +1,5 @@ +class StartTimeCanBeNull < ActiveRecord::Migration + def change + change_column :activities, :start_time, :datetime, null: true + end +end diff --git a/db/schema.rb b/db/schema.rb index e9a1bcb7..4383eacc 100644 --- a/db/schema.rb +++ b/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: 20130529174513) do +ActiveRecord::Schema.define(version: 20130529182104) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -20,8 +20,7 @@ t.string "name", default: "", null: false t.text "description" t.string "place" - t.datetime "start_at", null: false - t.integer "time_frame" + t.datetime "start_time" t.integer "limit_of_participants" t.datetime "created_at" t.datetime "updated_at" @@ -29,6 +28,7 @@ t.integer "participations_count", default: 0 t.boolean "anytime", default: false t.text "requirements" + t.datetime "end_time" end add_index "activities", ["name"], name: "index_activities_on_name", unique: true, using: :btree From f84cf0abdcde2d10fde496e4583a5c038689c405 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Wed, 29 May 2013 21:49:38 +0300 Subject: [PATCH 112/499] Updating seeds, validations etc. --- app/decorators/activity_decorator.rb | 8 ++++---- app/models/activity.rb | 19 +++---------------- app/views/activities/new.html.haml | 10 +++++----- db/seeds.rb | 3 ++- 4 files changed, 14 insertions(+), 26 deletions(-) diff --git a/app/decorators/activity_decorator.rb b/app/decorators/activity_decorator.rb index 91820315..476a8a64 100644 --- a/app/decorators/activity_decorator.rb +++ b/app/decorators/activity_decorator.rb @@ -15,16 +15,16 @@ def relation_ship_with(user) def status today = Date.current - if object.start_at > today then "upcoming" - elsif object.start_at.to_date == today then "today" + if object.start_time > today then "upcoming" + elsif object.start_time.to_date == today then "today" else "archive" end end def time # Case for "Anytime" needed - object.start_at.strftime("%A, %-d.%-m / %k:%M – ") + - (object.start_at + object.time_frame.minutes).strftime("%k:%M") + object.start_time.strftime("%A, %-d.%-m / %k:%M – ") + + object.end_time.strftime("%k:%M") end end diff --git a/app/models/activity.rb b/app/models/activity.rb index 724dd144..33f98fd2 100644 --- a/app/models/activity.rb +++ b/app/models/activity.rb @@ -8,27 +8,14 @@ class Activity < ActiveRecord::Base has_many :participations, :dependent => :destroy has_many :participants, through: :participations, class_name: "User" - validates :start_at, presence: true, allow_blank: false + validates :start_time, presence: true, allow_blank: false + validates :end_time, presence: true, allow_blank: false validates :name, presence: true, allow_blank: false, uniqueness: true validates :place, presence: true, allow_blank: false validates :limit_of_participants, numericality: {greater_than: 0}, allow_nil: true - validates :time_frame, numericality: {greater_than: 0} def self.recent(limit = DEFAULT_LIMIT) - where("start_at >= :t", t: 1.month.ago).limit(limit) - end - - def raw_start_date - start_at ? start_at.strftime('%a, %-d.%-m') : '' - end - - def raw_start_time - start_at ? start_at.strftime('%k:%M') : '' - end - - def raw_end_time - # TODO: move into method, not safe for - start_at && time_frame ? (start_at + time_frame.minutes).strftime('%k:%M') : '' + where("start_time >= :t", t: 1.month.ago).limit(limit) end def full_by diff --git a/app/views/activities/new.html.haml b/app/views/activities/new.html.haml index 9bfc62ca..7102c173 100644 --- a/app/views/activities/new.html.haml +++ b/app/views/activities/new.html.haml @@ -4,15 +4,15 @@ = form_for @activity, id: "new-activity" do |f| = f.text_field :name, placeholder: "Give your activity a name", class: "title" %hr - = f.text_field :raw_start_date, placeholder: Date.current.strftime("%a, %-d.%-m"), class: "date" - = f.text_field :raw_start_time, placeholder: Date.current.strftime("%k:%M"), class: "time" + = f.select_datetime :start_time – - = f.text_field :raw_end_time, placeholder: Date.current.strftime("%k:%M"), class: "time" + = f.select_datetime :end_time %span or %label = f.check_box :anytime Without a specific date/time - - + = f.text_field :raw_start_date, placeholder: Date.current.strftime("%a, %-d.%-m"), class: "date" + = f.text_area :requirements, placeholder: "Requirements, what to bring …" + = f.text_area :description, placeholder: "Description, %button(type="submit") Save diff --git a/db/seeds.rb b/db/seeds.rb index 10fd7b31..ccf0c31f 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -17,7 +17,8 @@ activity = EVENT.new_activity(creator, name: "Party!", - start_at: 1.day.from_now.to_time, + start_time: 1.day.from_now.to_time, + end_time: 1.day.from_now.to_time + 4.hours, place: "Pool", time_frame: 200, limit_of_participants: 2 From 984f196c0d4c399936fe2a1639539e0e6ea263ca Mon Sep 17 00:00:00 2001 From: pietia Date: Wed, 29 May 2013 20:52:30 +0200 Subject: [PATCH 113/499] updated seed - now it should work --- db/seeds.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/db/seeds.rb b/db/seeds.rb index ccf0c31f..87878801 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -1,4 +1,5 @@ User.transaction do |tx| + EVENT = Event.new(Settings.event.name) ultra_secure_password = "qweqweqwe" creator = User.new( @@ -20,7 +21,6 @@ start_time: 1.day.from_now.to_time, end_time: 1.day.from_now.to_time + 4.hours, place: "Pool", - time_frame: 200, limit_of_participants: 2 ) activity.save! From 2006f1ca66be4b1a0f49d1b0848b282235f8ed1e Mon Sep 17 00:00:00 2001 From: Polarblau Date: Wed, 29 May 2013 21:59:13 +0300 Subject: [PATCH 114/499] Fixing form. --- app/views/activities/new.html.haml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/app/views/activities/new.html.haml b/app/views/activities/new.html.haml index 7102c173..c9d9f601 100644 --- a/app/views/activities/new.html.haml +++ b/app/views/activities/new.html.haml @@ -4,15 +4,14 @@ = form_for @activity, id: "new-activity" do |f| = f.text_field :name, placeholder: "Give your activity a name", class: "title" %hr - = f.select_datetime :start_time + = f.datetime_select :start_time – - = f.select_datetime :end_time + = f.datetime_select :end_time %span or %label = f.check_box :anytime Without a specific date/time - = f.text_field :raw_start_date, placeholder: Date.current.strftime("%a, %-d.%-m"), class: "date" = f.text_area :requirements, placeholder: "Requirements, what to bring …" - = f.text_area :description, placeholder: "Description, + = f.text_area :description, placeholder: "Description," %button(type="submit") Save From 89c999f04b0b15d56441692c5dd6a54a701d4694 Mon Sep 17 00:00:00 2001 From: pietia Date: Wed, 29 May 2013 21:45:52 +0200 Subject: [PATCH 115/499] updated tests, now we can pass start/end time to Event's constructor --- app/controllers/application_controller.rb | 2 +- app/controllers/participations_controller.rb | 2 +- app/models/event.rb | 6 +- config/application.yml | 2 + db/seeds.rb | 2 +- .../controllers/activities_controller_spec.rb | 78 +++++++++---------- spec/factories/activities.rb | 4 +- spec/models/activity_spec.rb | 9 +-- spec/models/event_spec.rb | 4 +- 9 files changed, 53 insertions(+), 56 deletions(-) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 51a73791..810c009a 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -7,6 +7,6 @@ class ApplicationController < ActionController::Base before_filter :authenticate_user! def current_event - @current_event ||= Event.new(Settings.event.name) + @current_event ||= Event.new(Settings.event.name, Settings.event.start_time, Settings.event.end_time) end end diff --git a/app/controllers/participations_controller.rb b/app/controllers/participations_controller.rb index 22374e91..af356883 100644 --- a/app/controllers/participations_controller.rb +++ b/app/controllers/participations_controller.rb @@ -1,5 +1,5 @@ class ParticipationsController < ApplicationController - respond_to :js, :html + respond_to :json, :html def create participation = current_activity.new_participation(current_user) diff --git a/app/models/event.rb b/app/models/event.rb index 6a438c60..92490312 100644 --- a/app/models/event.rb +++ b/app/models/event.rb @@ -1,9 +1,9 @@ class Event attr_writer :activity_source # DI - attr_reader :name + attr_reader :name, :start_time, :end_time - def initialize(name, fetcher = ->{ Activity.recent }) - @name = name + def initialize(name, start_time, end_time, fetcher = ->{ Activity.recent }) + @name, @start_time, @end_time = name, start_time, end_time @fetcher = fetcher end diff --git a/config/application.yml b/config/application.yml index 611f4b01..c89e2368 100644 --- a/config/application.yml +++ b/config/application.yml @@ -1,6 +1,8 @@ defaults: &defaults event: name: "Eurucamp 2013" + start_time: 2013-08-10 00:00:00 + end_time: 2013-08-19 00:00:00 host: "activities.eurucamp.org" mailers: from: "activities@eurucamp.org" diff --git a/db/seeds.rb b/db/seeds.rb index 87878801..12313261 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -1,5 +1,5 @@ User.transaction do |tx| - EVENT = Event.new(Settings.event.name) + EVENT = Event.new(Settings.event.name, Settings.event.start_time, Settings.event.end_time) ultra_secure_password = "qweqweqwe" creator = User.new( diff --git a/spec/controllers/activities_controller_spec.rb b/spec/controllers/activities_controller_spec.rb index ec714b3f..08cbcf56 100644 --- a/spec/controllers/activities_controller_spec.rb +++ b/spec/controllers/activities_controller_spec.rb @@ -12,63 +12,57 @@ end describe "#show" do - subject { get :show, {id: activity.id, format: :json} } + subject { get :show, {id: activity.id} } its(:status){ should == 200 } end describe "#create" do - context "format :json" do - subject { post :create, params.merge({format: :json}) } - - before do - sign_in(user) - end - - context "valid parameters" do - let(:params) { {activity: {name: "Pool party", start_at: 2.days.from_now.to_s, place: "Pool", time_frame: 120 }} } - its(:status) { should == 201 } - end - - context "invalid parameters" do - let(:params) { {activity: {x: 10}} } - its(:status) { should == 422 } - end + subject { post :create, params } + + before do + sign_in(user) + end + + context "valid parameters" do + let(:params) { {activity: {name: "Pool party", start_time: 2.days.from_now.to_s, end_time: 2.days.from_now.to_s, place: "Pool" }} } + its(:status) { should == 201 } + end + + context "invalid parameters" do + let(:params) { {activity: {x: 10}} } + its(:status) { should == 422 } end end describe "#update" do - context "format :json" do - subject { put :update, params.merge({format: :json}) } - let!(:activity) { FactoryGirl.create(:activity, creator: user) } - - before do - sign_in(user) - end - - context "valid parameters" do - let(:params) { {id: activity.id, activity: {name: "Pool party", start_at: 2.days.from_now.to_s, place: "Pool", time_frame: 120 }} } - its(:status) { should == 204 } - end - - context "invalid parameters" do - let(:params) { {id: activity.id, activity: {start_at: ""}} } - its(:status) { should == 422 } - end + subject { put :update, params } + let!(:activity) { FactoryGirl.create(:activity, creator: user) } + + before do + sign_in(user) + end + + context "valid parameters" do + let(:params) { {id: activity.id, activity: {name: "Pool party", start_time: 2.days.from_now.to_s, end_time: 3.days.from_now.to_s, place: "Pool" }} } + its(:status) { should == 204 } + end + + context "invalid parameters" do + let(:params) { {id: activity.id, activity: {start_time: ""}} } + its(:status) { should == 422 } end end describe "#destroy" do - context "format :json" do - subject { delete :destroy, {id: activity.id, format: :json} } - let!(:activity) { FactoryGirl.create(:activity, creator: user) } + subject { delete :destroy, {id: activity.id} } + let!(:activity) { FactoryGirl.create(:activity, creator: user) } - before do - sign_in(user) - end - - its(:status) { should == 204 } + before do + sign_in(user) end + + its(:status) { should == 204 } end end \ No newline at end of file diff --git a/spec/factories/activities.rb b/spec/factories/activities.rb index 5ce694dc..98f270b6 100644 --- a/spec/factories/activities.rb +++ b/spec/factories/activities.rb @@ -3,9 +3,9 @@ FactoryGirl.define do factory :activity do name "Party!" - start_at "2013/12/12" + start_time "2013/12/12 18:00" + end_time "2013/12/13 03:00" place "Ballroom" - time_frame 4*60 creator { FactoryGirl.create(:user) } end end diff --git a/spec/models/activity_spec.rb b/spec/models/activity_spec.rb index b243925c..db2e17d5 100644 --- a/spec/models/activity_spec.rb +++ b/spec/models/activity_spec.rb @@ -89,12 +89,11 @@ it { should accept_values_for(:place, "football pitch" ) } it { should_not accept_values_for(:place, "", nil) } - it { should accept_values_for(:start_at, Time.now) } - it { should_not accept_values_for(:start_at, "", nil) } + it { should accept_values_for(:start_time, Time.now, nil) } + it { should_not accept_values_for(:start_time, "") } - # whole day activity? - it { should accept_values_for(:time_frame, 15, 30, 60, 120, 360 ) } # minutes - it { should_not accept_values_for(:time_frame, "", nil, 0, -10) } + it { should accept_values_for(:end_time, Time.now, nil) } + it { should_not accept_values_for(:end_time, "") } it { should accept_values_for(:limit_of_participants, nil, 12, 100) } it { should_not accept_values_for(:limit_of_participants, -1, 0) } diff --git a/spec/models/event_spec.rb b/spec/models/event_spec.rb index 585a6eff..4c27a148 100644 --- a/spec/models/event_spec.rb +++ b/spec/models/event_spec.rb @@ -2,10 +2,12 @@ describe Event do - subject(:event) { Event.new("Eurucamp") } + subject(:event) { Event.new("Eurucamp", Date.parse("2012-10-10"), Date.parse("2012-12-12")) } describe "#new" do its(:name) { should == "Eurucamp" } + its(:start_time) { should == Date.parse("2012-10-10") } + its(:end_time) { should == Date.parse("2012-12-12") } end describe "#new_activity" do From bf91840826346cb41b9f0384a78930fafdf26d87 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Wed, 29 May 2013 23:29:35 +0300 Subject: [PATCH 116/499] WIP activity creation. --- app/controllers/activities_controller.rb | 2 +- app/views/activities/new.html.haml | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/app/controllers/activities_controller.rb b/app/controllers/activities_controller.rb index ae49ae86..38757ddc 100644 --- a/app/controllers/activities_controller.rb +++ b/app/controllers/activities_controller.rb @@ -45,7 +45,7 @@ def destroy private def sanitized_params - params.require(:activity).permit(:start_at, :name, :place, :limit_of_participants, :time_frame) + params.require(:activity).permit(:start_time, :end_time, :name, :place, :limit_of_participants) end end diff --git a/app/views/activities/new.html.haml b/app/views/activities/new.html.haml index c9d9f601..6595e650 100644 --- a/app/views/activities/new.html.haml +++ b/app/views/activities/new.html.haml @@ -2,6 +2,11 @@ Organize an activity = form_for @activity, id: "new-activity" do |f| + - if @activity.errors.any? + %ul.errors + - @activity.errors.full_messages.each do |error| + %li= error + = f.text_field :name, placeholder: "Give your activity a name", class: "title" %hr = f.datetime_select :start_time @@ -11,6 +16,7 @@ %label = f.check_box :anytime Without a specific date/time + = f.text_field :place, placeholder: "Location, where to meet …" = f.text_area :requirements, placeholder: "Requirements, what to bring …" = f.text_area :description, placeholder: "Description," From ef28779b5acd9d2c173bca979f27ea0cce39eaca Mon Sep 17 00:00:00 2001 From: pietia Date: Wed, 29 May 2013 22:51:05 +0200 Subject: [PATCH 117/499] it seems that we don't need json api for now --- app/controllers/activities_controller.rb | 3 +-- app/controllers/participations_controller.rb | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/app/controllers/activities_controller.rb b/app/controllers/activities_controller.rb index 38757ddc..06b2e017 100644 --- a/app/controllers/activities_controller.rb +++ b/app/controllers/activities_controller.rb @@ -1,6 +1,5 @@ class ActivitiesController < ApplicationController - respond_to :html, only: [:new, :edit] - respond_to :json, :html + respond_to :html skip_before_filter :authenticate_user!, :only => [:index, :show] diff --git a/app/controllers/participations_controller.rb b/app/controllers/participations_controller.rb index af356883..22374e91 100644 --- a/app/controllers/participations_controller.rb +++ b/app/controllers/participations_controller.rb @@ -1,5 +1,5 @@ class ParticipationsController < ApplicationController - respond_to :json, :html + respond_to :js, :html def create participation = current_activity.new_participation(current_user) From 84e6df3dacfb0e1bd6383587e628506c8fb1fd08 Mon Sep 17 00:00:00 2001 From: pietia Date: Wed, 29 May 2013 22:54:46 +0200 Subject: [PATCH 118/499] :html format not needed here --- app/controllers/participations_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/participations_controller.rb b/app/controllers/participations_controller.rb index 22374e91..cefa8683 100644 --- a/app/controllers/participations_controller.rb +++ b/app/controllers/participations_controller.rb @@ -1,5 +1,5 @@ class ParticipationsController < ApplicationController - respond_to :js, :html + respond_to :js def create participation = current_activity.new_participation(current_user) From c5e7b0551965bf777875b663eae8bf1da49a36db Mon Sep 17 00:00:00 2001 From: Polarblau Date: Thu, 30 May 2013 00:48:34 +0300 Subject: [PATCH 119/499] Time and date picker. --- app/assets/javascripts/application.js | 4 + app/assets/javascripts/initializers.coffee | 19 + app/assets/stylesheets/application.css.scss | 4 + app/views/activities/new.html.haml | 5 + vendor/assets/javascripts/pickadate/legacy.js | 140 +++ .../javascripts/pickadate/picker.date.js | 941 ++++++++++++++++++ vendor/assets/javascripts/pickadate/picker.js | 785 +++++++++++++++ .../javascripts/pickadate/picker.time.js | 651 ++++++++++++ .../assets/stylesheets/pickadate/default.css | 234 +++++ .../stylesheets/pickadate/default.date.css | 332 ++++++ .../stylesheets/pickadate/default.time.css | 193 ++++ 11 files changed, 3308 insertions(+) create mode 100755 vendor/assets/javascripts/pickadate/legacy.js create mode 100755 vendor/assets/javascripts/pickadate/picker.date.js create mode 100755 vendor/assets/javascripts/pickadate/picker.js create mode 100755 vendor/assets/javascripts/pickadate/picker.time.js create mode 100755 vendor/assets/stylesheets/pickadate/default.css create mode 100755 vendor/assets/stylesheets/pickadate/default.date.css create mode 100755 vendor/assets/stylesheets/pickadate/default.time.css diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index da5d9d63..6b639bc8 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -15,4 +15,8 @@ //= require turbolinks //= require jquery.easing.min //= require jquery.progress +//= require pickadate/picker +//= require pickadate/picker.date +//= require pickadate/picker.time +//= require pickadate/legacy //= require initializers diff --git a/app/assets/javascripts/initializers.coffee b/app/assets/javascripts/initializers.coffee index 50f1cd07..22648dd7 100644 --- a/app/assets/javascripts/initializers.coffee +++ b/app/assets/javascripts/initializers.coffee @@ -8,6 +8,25 @@ $ -> $('#activities img.progress').each -> setTimeout showProgress, Math.random() * 1000 + 500, @ + + $('.date-capture').pickadate + onSet: (e)-> + date = new Date(e.select) + target = @$node.data 'target' + [day, month, year] = [date.getDate(), date.getMonth() + 1, date.getFullYear()] + console.log day, month, year + $("#activity_#{target}_1i").val(year) + $("#activity_#{target}_2i").val(month) + $("#activity_#{target}_3i").val(day) + + $('.time-capture').pickatime + onSet: (e)-> + target = @$node.data 'target' + [hours, minutes] = [parseInt((e.select / 60), 10), e.select % 60] + $("#activity_#{target}_4i").val(if hours < 10 then "0#{hours}" else hours) + $("#activity_#{target}_5i").val(minutes) + + # filters $filters = $('form.filters label:not(.search)') $search = $('form.filters label.search input') diff --git a/app/assets/stylesheets/application.css.scss b/app/assets/stylesheets/application.css.scss index 54862c4a..a63437d7 100644 --- a/app/assets/stylesheets/application.css.scss +++ b/app/assets/stylesheets/application.css.scss @@ -10,3 +10,7 @@ @import "partials/footer"; @import "partials/activities"; + +@import "pickadate/default"; +@import "pickadate/default.date"; +@import "pickadate/default.time"; diff --git a/app/views/activities/new.html.haml b/app/views/activities/new.html.haml index 6595e650..bbc4ecc0 100644 --- a/app/views/activities/new.html.haml +++ b/app/views/activities/new.html.haml @@ -10,8 +10,13 @@ = f.text_field :name, placeholder: "Give your activity a name", class: "title" %hr = f.datetime_select :start_time + %input.date-capture(data-target="start_time" type="text") + %input.time-capture(data-target="start_time" type="text") – = f.datetime_select :end_time + %input.date-capture(data-target="end_time" type="text") + %input.time-capture(data-target="end_time" type="text") + %span or %label = f.check_box :anytime diff --git a/vendor/assets/javascripts/pickadate/legacy.js b/vendor/assets/javascripts/pickadate/legacy.js new file mode 100755 index 00000000..42277ca1 --- /dev/null +++ b/vendor/assets/javascripts/pickadate/legacy.js @@ -0,0 +1,140 @@ + +/*jshint + asi: true, + unused: true, + boss: true, + loopfunc: true, + eqnull: true + */ + + +/*! + * Legacy browser support + */ + +// isArray support +if ( !Array.isArray ) { + Array.isArray = function( value ) { + return {}.toString.call( value ) == '[object Array]' + } +} + + +// Map array support +if ( ![].map ) { + Array.prototype.map = function ( callback, self ) { + var array = this, len = array.length, newArray = new Array( len ) + for ( var i = 0; i < len; i++ ) { + if ( i in array ) { + newArray[ i ] = callback.call( self, array[ i ], i, array ) + } + } + return newArray + } +} + + +// Filter array support +if ( ![].filter ) { + Array.prototype.filter = function( callback ) { + if ( this == null ) throw new TypeError() + var t = Object( this ), len = t.length >>> 0 + if ( typeof callback != 'function' ) throw new TypeError() + var newArray = [], thisp = arguments[ 1 ] + for ( var i = 0; i < len; i++ ) { + if ( i in t ) { + var val = t[ i ] + if ( callback.call( thisp, val, i, t ) ) newArray.push( val ) + } + } + return newArray + } +} + + +// Index of array support +if ( ![].indexOf ) { + Array.prototype.indexOf = function( searchElement ) { + if ( this == null ) throw new TypeError() + var t = Object( this ), len = t.length >>> 0 + if ( len === 0 ) return -1 + var n = 0 + if ( arguments.length > 1 ) { + n = Number( arguments[ 1 ] ) + if ( n != n ) { + n = 0 + } + else if ( n !== 0 && n != Infinity && n != -Infinity ) { + n = ( n > 0 || -1 ) * Math.floor( Math.abs( n ) ) + } + } + if ( n >= len ) return -1 + var k = n >= 0 ? n : Math.max( len - Math.abs( n ), 0 ) + for ( ; k < len; k++ ) { + if ( k in t && t[ k ] === searchElement ) return k + } + return -1 + } +} + + +/*! + * Cross-Browser Split 1.1.1 + * Copyright 2007-2012 Steven Levithan + * Available under the MIT License + * http://blog.stevenlevithan.com/archives/cross-browser-split + */ +var nativeSplit = String.prototype.split, compliantExecNpcg = /()??/.exec('')[1] === undefined +String.prototype.split = function(separator, limit) { + var str = this + if (Object.prototype.toString.call(separator) !== '[object RegExp]') { + return nativeSplit.call(str, separator, limit) + } + var output = [], + flags = (separator.ignoreCase ? 'i' : '') + + (separator.multiline ? 'm' : '') + + (separator.extended ? 'x' : '') + + (separator.sticky ? 'y' : ''), + lastLastIndex = 0, + separator2, match, lastIndex, lastLength + separator = new RegExp(separator.source, flags + 'g') + str += '' + if (!compliantExecNpcg) { + separator2 = new RegExp('^' + separator.source + '$(?!\\s)', flags) + } + limit = limit === undefined ? -1 >>> 0 : limit >>> 0 + while (match = separator.exec(str)) { + lastIndex = match.index + match[0].length + if (lastIndex > lastLastIndex) { + output.push(str.slice(lastLastIndex, match.index)) + if (!compliantExecNpcg && match.length > 1) { + match[0].replace(separator2, function () { + for (var i = 1; i < arguments.length - 2; i++) { + if (arguments[i] === undefined) { + match[i] = undefined + } + } + }) + } + if (match.length > 1 && match.index < str.length) { + Array.prototype.push.apply(output, match.slice(1)) + } + lastLength = match[0].length + lastLastIndex = lastIndex + if (output.length >= limit) { + break + } + } + if (separator.lastIndex === match.index) { + separator.lastIndex++ + } + } + if (lastLastIndex === str.length) { + if (lastLength || !separator.test('')) { + output.push('') + } + } else { + output.push(str.slice(lastLastIndex)) + } + return output.length > limit ? output.slice(0, limit) : output +} diff --git a/vendor/assets/javascripts/pickadate/picker.date.js b/vendor/assets/javascripts/pickadate/picker.date.js new file mode 100755 index 00000000..435ce33a --- /dev/null +++ b/vendor/assets/javascripts/pickadate/picker.date.js @@ -0,0 +1,941 @@ + +/*! + * Date picker for pickadate.js v3.0.4 + * http://amsul.github.io/pickadate.js/date.htm + */ + +/*jshint + debug: true, + devel: true, + browser: true, + asi: true, + unused: true, + boss: true + */ + + +// Create a new scope. +(function() { + + +/** + * Globals and constants + */ +var DAYS_IN_WEEK = 7, + WEEKS_IN_CALENDAR = 6 + + + +/** + * The date picker constructor + */ +function DatePicker( picker, settings ) { + + var calendar = this, + elementDataValue = picker.$node.data( 'value' ) + + calendar.settings = settings + + // The queue of methods that will be used to build item objects. + calendar.queue = { + min: 'measure create', + max: 'measure create', + now: 'now create', + select: 'parse create validate', + highlight: 'navigate create validate', + view: 'create validate viewset', + disable: 'flipItem', + enable: 'flipItem' + } + + // The component's item object. + calendar.item = {} + + calendar.item.disable = ( settings.disable || [] ).slice( 0 ) + calendar.item.enable = -(function( collectionDisabled ) { + return collectionDisabled[ 0 ] === true ? collectionDisabled.shift() : -1 + })( calendar.item.disable ) + + calendar. + set( 'min', settings.min ). + set( 'max', settings.max ). + set( 'now' ). + + // Setting the `select` also sets the `highlight` and `view`. + set( 'select', + + // If there's a `value` or `data-value`, use that with formatting. + // Otherwise default to selecting “today”. + elementDataValue || picker.$node[ 0 ].value || calendar.item.now, + + // Use the relevant format and data property. + { format: elementDataValue ? settings.formatSubmit : settings.format, data: !!elementDataValue } + ) + + + // The keycode to movement mapping. + calendar.key = { + 40: 7, // Down + 38: -7, // Up + 39: 1, // Right + 37: -1, // Left + go: function( timeChange ) { + calendar.set( 'highlight', [ calendar.item.highlight.year, calendar.item.highlight.month, calendar.item.highlight.date + timeChange ], { interval: timeChange } ) + this.render() + } + } + + + // Bind some picker events. + picker. + on( 'render', function() { + picker.$root.find( '.' + settings.klass.selectMonth ).on( 'change', function() { + picker.set( 'highlight', [ picker.get( 'view' ).year, this.value, picker.get( 'highlight' ).date ] ) + picker.$root.find( '.' + settings.klass.selectMonth ).focus() + }) + picker.$root.find( '.' + settings.klass.selectYear ).on( 'change', function() { + picker.set( 'highlight', [ this.value, picker.get( 'view' ).month, picker.get( 'highlight' ).date ] ) + picker.$root.find( '.' + settings.klass.selectYear ).focus() + }) + }). + on( 'open', function() { + picker.$root.find( 'button, select' ).attr( 'disabled', false ) + }). + on( 'close', function() { + picker.$root.find( 'button, select' ).attr( 'disabled', true ) + }) + +} //DatePicker + + +/** + * Set a datepicker item object. + */ +DatePicker.prototype.set = function( type, value, options ) { + + var calendar = this + + // Go through the queue of methods, and invoke the function. Update this + // as the time unit, and set the final resultant as this item type. + // * In the case of `enable`, keep the queue but set `disable` instead. + // And in the case of `flip`, keep the queue but set `enable` instead. + calendar.item[ ( type == 'enable' ? 'disable' : type == 'flip' ? 'enable' : type ) ] = calendar.queue[ type ].split( ' ' ).map( function( method ) { + return value = calendar[ method ]( type, value, options ) + }).pop() + + // Check if we need to cascade through more updates. + if ( type == 'select' ) { + calendar.set( 'highlight', calendar.item.select, options ) + } + else if ( type == 'highlight' ) { + calendar.set( 'view', calendar.item.highlight, options ) + } + else if ( ( type == 'flip' || type == 'min' || type == 'max' || type == 'disable' || type == 'enable' ) && calendar.item.select && calendar.item.highlight ) { + calendar. + set( 'select', calendar.item.select, options ). + set( 'highlight', calendar.item.highlight, options ) + } + + return calendar +} //DatePicker.prototype.set + + +/** + * Get a datepicker item object. + */ +DatePicker.prototype.get = function( type ) { + return this.item[ type ] +} //DatePicker.prototype.get + + +/** + * Create a picker date object. + */ +DatePicker.prototype.create = function( type, value, options ) { + + var isInfiniteValue, + calendar = this + + // If there’s no value, use the type as the value. + value = value === undefined ? type : value + + + // If it’s infinity, update the value. + if ( value == -Infinity || value == Infinity ) { + isInfiniteValue = value + } + + // If it’s an object, use the native date object. + else if ( Picker._.isObject( value ) && Picker._.isInteger( value.pick ) ) { + value = value.obj + } + + // If it’s an array, convert it into a date. + else if ( Array.isArray( value ) ) { + value = new Date( value[ 0 ], value[ 1 ], value[ 2 ] ) + } + + // If it’s a number or date object, make a normalized date. + else if ( Picker._.isInteger( value ) || Picker._.isDate( value ) ) { + value = calendar.normalize( new Date( value ), options ) + } + + // If it’s a literal true or any other case, set it to now. + else /*if ( value === true )*/ { + value = calendar.now( type, value, options ) + } + + // Return the compiled object. + return { + year: isInfiniteValue || value.getFullYear(), + month: isInfiniteValue || value.getMonth(), + date: isInfiniteValue || value.getDate(), + day: isInfiniteValue || value.getDay(), + obj: isInfiniteValue || value, + pick: isInfiniteValue || value.getTime() + } +} //DatePicker.prototype.create + + +/** + * Get the date today. + */ +DatePicker.prototype.now = function( type, value, options ) { + value = new Date() + if ( options && options.rel ) { + value.setDate( value.getDate() + options.rel ) + } + return this.normalize( value, options ) +} //DatePicker.prototype.now + + +/** + * Navigate to next/prev month. + */ +DatePicker.prototype.navigate = function( type, value, options ) { + + if ( Picker._.isObject( value ) ) { + + var targetDateObject = new Date( value.year, value.month + ( options && options.nav ? options.nav : 0 ), 1 ), + year = targetDateObject.getFullYear(), + month = targetDateObject.getMonth(), + date = value.date + + // If the month we’re going to doesn’t have enough days, + // keep decreasing the date until we reach the month’s last date. + while ( new Date( year, month, date ).getMonth() !== month ) { + date -= 1 + } + + value = [ year, month, date ] + } + + return value +} //DatePicker.prototype.navigate + + +/** + * Normalize a date by setting the hours to midnight. + */ +DatePicker.prototype.normalize = function( value/*, options*/ ) { + value.setHours( 0, 0, 0, 0 ) + return value +} + + +/** + * Measure the range of dates. + */ +DatePicker.prototype.measure = function( type, value/*, options*/ ) { + + var calendar = this + + // If it's anything false-y, remove the limits. + if ( !value ) { + value = type == 'min' ? -Infinity : Infinity + } + + // If it's an integer, get a date relative to today. + else if ( Picker._.isInteger( value ) ) { + value = calendar.now( type, value, { rel: value } ) + } + + return value +} ///DatePicker.prototype.measure + + +/** + * Create a viewset object based on navigation. + */ +DatePicker.prototype.viewset = function( type, dateObject/*, options*/ ) { + return this.create([ dateObject.year, dateObject.month, 1 ]) +} + + +/** + * Validate a date as enabled and shift if needed. + */ +DatePicker.prototype.validate = function( type, dateObject, options ) { + + var calendar = this, + + // Keep a reference to the original date. + originalDateObject = dateObject, + + // Make sure we have an interval. + interval = options && options.interval ? options.interval : 1, + + // Check if the calendar enabled dates are inverted. + isInverted = calendar.item.enable === -1, + + // Check if we have any enabled dates after/before now. + hasEnabledBeforeTarget, hasEnabledAfterTarget, + + // The min & max limits. + minLimitObject = calendar.item.min, + maxLimitObject = calendar.item.max, + + // Check if we’ve reached the limit during shifting. + reachedMin, reachedMax, + + // Check if the calendar is inverted and at least one weekday is enabled. + hasEnabledWeekdays = isInverted && calendar.item.disable.filter( function( value ) { + + // If there’s a date, check where it is relative to the target. + if ( Array.isArray( value ) ) { + var dateTime = calendar.create( value ).pick + if ( dateTime < dateObject.pick ) hasEnabledBeforeTarget = true + else if ( dateTime > dateObject.pick ) hasEnabledAfterTarget = true + } + + // Return only integers for enabled weekdays. + return Picker._.isInteger( value ) + }).length + + + + // Cases to validate for: + // [1] Not inverted and date disabled. + // [2] Inverted and some dates enabled. + // [3] Out of range. + // + // Cases to **not** validate for: + // • Not inverted and date enabled. + // • Inverted and all dates disabled. + // • Navigating months. + // • ..and anything else. + if ( + /* 1 */ ( !isInverted && calendar.disabled( dateObject ) ) || + /* 2 */ ( isInverted && calendar.disabled( dateObject ) && ( hasEnabledWeekdays || hasEnabledBeforeTarget || hasEnabledAfterTarget ) ) || + /* 3 */ ( dateObject.pick <= minLimitObject.pick || dateObject.pick >= maxLimitObject.pick ) + ) { + + + // When inverted, flip the direction if there aren’t any enabled weekdays + // and there are no enabled dates in the direction of the interval. + if ( isInverted && !hasEnabledWeekdays && ( ( !hasEnabledAfterTarget && interval > 0 ) || ( !hasEnabledBeforeTarget && interval < 0 ) ) ) { + interval *= -1 + } + + + // Keep looping until we reach an enabled date. + while ( calendar.disabled( dateObject ) ) { + + + // If we’ve looped into the next/prev month, return to the original date and flatten the interval. + if ( Math.abs( interval ) > 1 && ( dateObject.month < originalDateObject.month || dateObject.month > originalDateObject.month ) ) { + dateObject = originalDateObject + interval = Math.abs( interval ) / interval + } + + + // If we’ve reached the min/max limit, reverse the direction and flatten the interval. + if ( dateObject.pick <= minLimitObject.pick ) { + reachedMin = true + interval = 1 + } + else if ( dateObject.pick >= maxLimitObject.pick ) { + reachedMax = true + interval = -1 + } + + + // If we’ve reached both limits, just break out of the loop. + if ( reachedMin && reachedMax ) { + break + } + + + // Finally, create the shifted date using the interval and keep looping. + dateObject = calendar.create([ dateObject.year, dateObject.month, dateObject.date + interval ]) + } + + } //endif + + + // Return the date object settled on. + return dateObject +} //DatePicker.prototype.validate + + +/** + * Check if an object is disabled. + */ +DatePicker.prototype.disabled = function( dateObject ) { + + var calendar = this, + + // Filter through the disabled dates to check if this is one. + isDisabledDate = calendar.item.disable.filter( function( dateToDisable ) { + + // If the date is a number, match the weekday with 0index and `firstDay` check. + if ( Picker._.isInteger( dateToDisable ) ) { + return dateObject.day === ( calendar.settings.firstDay ? dateToDisable : dateToDisable - 1 ) % 7 + } + + // If it's an array, create the object and match the exact date. + if ( Array.isArray( dateToDisable ) ) { + return dateObject.pick === calendar.create( dateToDisable ).pick + } + }).length + + + // It’s disabled beyond the min/max limits. If within the limits, check the + // calendar “enabled” flag is flipped and respectively flip the condition. + return dateObject.pick < calendar.item.min.pick || + dateObject.pick > calendar.item.max.pick || + calendar.item.enable === -1 ? !isDisabledDate : isDisabledDate +} //DatePicker.prototype.disabled + + +/** + * Parse a string into a usable type. + */ +DatePicker.prototype.parse = function( type, value, options ) { + + var calendar = this, + parsingObject = {} + + if ( !value || Picker._.isInteger( value ) || Array.isArray( value ) || Picker._.isDate( value ) || Picker._.isObject( value ) && Picker._.isInteger( value.pick ) ) { + return value + } + + // We need a `.format` to parse the value. + if ( !( options && options.format ) ) { + // should probably default to the default format. + throw "Need a formatting option to parse this.." + } + + // Convert the format into an array and then map through it. + calendar.formats.toArray( options.format ).map( function( label ) { + + var + // Grab the formatting label. + formattingLabel = calendar.formats[ label ], + + // The format length is from the formatting label function or the + // label length without the escaping exclamation (!) mark. + formatLength = formattingLabel ? Picker._.trigger( formattingLabel, calendar, [ value, parsingObject ] ) : label.replace( /^!/, '' ).length + + // If there's a format label, split the value up to the format length. + // Then add it to the parsing object with appropriate label. + if ( formattingLabel ) { + parsingObject[ label ] = value.substr( 0, formatLength ) + } + + // Update the value as the substring from format length to end. + value = value.substr( formatLength ) + }) + + // If it’s parsing a `data-value`, compensate for month 0index. + return [ parsingObject.yyyy || parsingObject.yy, +( parsingObject.mm || parsingObject.m ) - ( options.data ? 1 : 0 ), parsingObject.dd || parsingObject.d ] +} //DatePicker.prototype.parse + + +/** + * Various formats to display the object in. + */ +DatePicker.prototype.formats = (function() { + + // Return the length of the first word in a collection. + var getWordLengthFromCollection = function( string, collection, dateObject ) { + + // Grab the first word from the string. + var word = string.match( /\w+/ )[ 0 ] + + // If there's no month index, add it to the date object + if ( !dateObject.mm && !dateObject.m ) { + dateObject.m = collection.indexOf( word ) + } + + // Return the length of the word. + return word.length + } + + return { + + d: function( string, dateObject ) { + + // If there's string, then get the digits length. + // Otherwise return the selected date. + return string ? Picker._.digits( string ) : dateObject.date + }, + dd: function( string, dateObject ) { + + // If there's a string, then the length is always 2. + // Otherwise return the selected date with a leading zero. + return string ? 2 : Picker._.lead( dateObject.date ) + }, + ddd: function( string, dateObject ) { + + // If there's a string, then get the length of the first word. + // Otherwise return the short selected weekday. + return string ? getFirstWordLength( string ) : this.settings.weekdaysShort[ dateObject.day ] + }, + dddd: function( string, dateObject ) { + + // If there's a string, then get the length of the first word. + // Otherwise return the full selected weekday. + return string ? getFirstWordLength( string ) : this.settings.weekdaysFull[ dateObject.day ] + }, + m: function( string, dateObject ) { + + // If there's a string, then get the length of the digits + // Otherwise return the selected month with 0index compensation. + return string ? Picker._.digits( string ) : dateObject.month + 1 + }, + mm: function( string, dateObject ) { + + // If there's a string, then the length is always 2. + // Otherwise return the selected month with 0index and leading zero. + return string ? 2 : Picker._.lead( dateObject.month + 1 ) + }, + mmm: function( string, dateObject ) { + + var collection = this.settings.monthsShort + + // If there's a string, get length of the relevant month from the short + // months collection. Otherwise return the selected month from that collection. + return string ? getWordLengthFromCollection( string, collection, dateObject ) : collection[ dateObject.month ] + }, + mmmm: function( string, dateObject ) { + + var collection = this.settings.monthsFull + + // If there's a string, get length of the relevant month from the full + // months collection. Otherwise return the selected month from that collection. + return string ? getWordLengthFromCollection( string, collection, dateObject ) : collection[ dateObject.month ] + }, + yy: function( string, dateObject ) { + + // If there's a string, then the length is always 2. + // Otherwise return the selected year by slicing out the first 2 digits. + return string ? 2 : ( '' + dateObject.year ).slice( 2 ) + }, + yyyy: function( string, dateObject ) { + + // If there's a string, then the length is always 4. + // Otherwise return the selected year. + return string ? 4 : dateObject.year + }, + + // Create an array by splitting the formatting string passed. + toArray: function( formatString ) { return formatString.split( /(d{1,4}|m{1,4}|y{4}|yy|!.)/g ) }, + + // Format an object into a string using the formatting options. + toString: function ( formatString, itemObject ) { + var calendar = this + return calendar.formats.toArray( formatString ).map( function( label ) { + return Picker._.trigger( calendar.formats[ label ], calendar, [ 0, itemObject ] ) || label.replace( /^!/, '' ) + }).join( '' ) + } + } +})() //DatePicker.prototype.formats + + +/** + * Flip an item as enabled or disabled. + */ +DatePicker.prototype.flipItem = function( type, value/*, options*/ ) { + + var calendar = this, + collection = calendar.item.disable, + isInverted = calendar.item.enable === -1 + + // Flip the enabled and disabled dates. + if ( value == 'flip' ) { + calendar.item.enable = isInverted ? 1 : -1 + } + + // Check if we have to add/remove from collection. + else if ( !isInverted && type == 'enable' || isInverted && type == 'disable' ) { + collection = calendar.removeDisabled( collection, value ) + } + else if ( !isInverted && type == 'disable' || isInverted && type == 'enable' ) { + collection = calendar.addDisabled( collection, value ) + } + + return collection +} //DatePicker.prototype.flipItem + + +/** + * Add an item to the disabled collection. + */ +DatePicker.prototype.addDisabled = function( collection, item ) { + var calendar = this + item.map( function( timeUnit ) { + if ( !calendar.filterDisabled( collection, timeUnit ).length ) { + collection.push( timeUnit ) + } + }) + return collection +} //DatePicker.prototype.addDisabled + + +/** + * Remove an item from the disabled collection. + */ +DatePicker.prototype.removeDisabled = function( collection, item ) { + var calendar = this + item.map( function( timeUnit ) { + collection = calendar.filterDisabled( collection, timeUnit, 1 ) + }) + return collection +} //DatePicker.prototype.removeDisabled + + +/** + * Filter through the disabled collection to find a time unit. + */ +DatePicker.prototype.filterDisabled = function( collection, timeUnit, isRemoving ) { + var timeIsArray = Array.isArray( timeUnit ) + return collection.filter( function( disabledTimeUnit ) { + var isMatch = !timeIsArray && timeUnit === disabledTimeUnit || + timeIsArray && Array.isArray( disabledTimeUnit ) && timeUnit.toString() === disabledTimeUnit.toString() + return isRemoving ? !isMatch : isMatch + }) +} //DatePicker.prototype.filterDisabled + + +/** + * Create a string for the nodes in the picker. + */ +DatePicker.prototype.nodes = function( isOpen ) { + + var + calendar = this, + settings = calendar.settings, + nowObject = calendar.item.now, + selectedObject = calendar.item.select, + highlightedObject = calendar.item.highlight, + viewsetObject = calendar.item.view, + disabledCollection = calendar.item.disable, + minLimitObject = calendar.item.min, + maxLimitObject = calendar.item.max, + + + // Create the calendar table head using a copy of weekday labels collection. + // * We do a copy so we don't mutate the original array. + tableHead = (function( collection ) { + + // If the first day should be Monday, move Sunday to the end. + if ( settings.firstDay ) { + collection.push( collection.shift() ) + } + + // Create and return the table head group. + return Picker._.node( + 'thead', + Picker._.group({ + min: 0, + max: DAYS_IN_WEEK - 1, + i: 1, + node: 'th', + item: function( counter ) { + return [ + collection[ counter ], + settings.klass.weekdays + ] + } + }) + ) //endreturn + })( ( settings.showWeekdaysFull ? settings.weekdaysFull : settings.weekdaysShort ).slice( 0 ) ), //tableHead + + + // Create the nav for next/prev month. + createMonthNav = function( next ) { + + // Otherwise, return the created month tag. + return Picker._.node( + 'div', + ' ', + settings.klass[ 'nav' + ( next ? 'Next' : 'Prev' ) ] + ( + + // If the focused month is outside the range, disabled the button. + ( next && viewsetObject.year >= maxLimitObject.year && viewsetObject.month >= maxLimitObject.month ) || + ( !next && viewsetObject.year <= minLimitObject.year && viewsetObject.month <= minLimitObject.month ) ? + ' ' + settings.klass.navDisabled : '' + ), + 'data-nav=' + ( next || -1 ) + ) //endreturn + }, //createMonthNav + + + // Create the month label. + createMonthLabel = function( monthsCollection ) { + + // If there are months to select, add a dropdown menu. + if ( settings.selectMonths ) { + + return Picker._.node( 'select', Picker._.group({ + min: 0, + max: 11, + i: 1, + node: 'option', + item: function( loopedMonth ) { + + return [ + + // The looped month and no classes. + monthsCollection[ loopedMonth ], 0, + + // Set the value and selected index. + 'value=' + loopedMonth + + ( viewsetObject.month == loopedMonth ? ' selected' : '' ) + + ( + ( + ( viewsetObject.year == minLimitObject.year && loopedMonth < minLimitObject.month ) || + ( viewsetObject.year == maxLimitObject.year && loopedMonth > maxLimitObject.month ) + ) ? + ' disabled' : '' + ) + ] + } + }), settings.klass.selectMonth, isOpen ? '' : 'disabled' ) + } + + // If there's a need for a month selector + return Picker._.node( 'div', monthsCollection[ viewsetObject.month ], settings.klass.month ) + }, //createMonthLabel + + + // Create the year label. + createYearLabel = function() { + + var focusedYear = viewsetObject.year, + + // If years selector is set to a literal "true", set it to 5. Otherwise + // divide in half to get half before and half after focused year. + numberYears = settings.selectYears === true ? 5 : ~~( settings.selectYears / 2 ) + + // If there are years to select, add a dropdown menu. + if ( numberYears ) { + + var + minYear = minLimitObject.year, + maxYear = maxLimitObject.year, + lowestYear = focusedYear - numberYears, + highestYear = focusedYear + numberYears + + // If the min year is greater than the lowest year, increase the highest year + // by the difference and set the lowest year to the min year. + if ( minYear > lowestYear ) { + highestYear += minYear - lowestYear + lowestYear = minYear + } + + // If the max year is less than the highest year, decrease the lowest year + // by the lower of the two: available and needed years. Then set the + // highest year to the max year. + if ( maxYear < highestYear ) { + + var availableYears = lowestYear - minYear, + neededYears = highestYear - maxYear + + lowestYear -= availableYears > neededYears ? neededYears : availableYears + highestYear = maxYear + } + + return Picker._.node( 'select', Picker._.group({ + min: lowestYear, + max: highestYear, + i: 1, + node: 'option', + item: function( loopedYear ) { + return [ + + // The looped year and no classes. + loopedYear, 0, + + // Set the value and selected index. + 'value=' + loopedYear + ( focusedYear == loopedYear ? ' selected' : '' ) + ] + } + }), settings.klass.selectYear, isOpen ? '' : 'disabled' ) + } + + // Otherwise just return the year focused + return Picker._.node( 'div', focusedYear, settings.klass.year ) + } //createYearLabel + + + // Create and return the entire calendar. + return Picker._.node( + 'div', + createMonthNav() + createMonthNav( 1 ) + + createMonthLabel( settings.showMonthsShort ? settings.monthsShort : settings.monthsFull ) + + createYearLabel(), + settings.klass.header + ) + Picker._.node( + 'table', + tableHead + + Picker._.node( + 'tbody', + Picker._.group({ + min: 0, + max: WEEKS_IN_CALENDAR - 1, + i: 1, + node: 'tr', + item: function( rowCounter ) { + + // If Monday is the first day and the month starts on Sunday, shift the date back a week. + var shiftDateBy = settings.firstDay && calendar.create([ viewsetObject.year, viewsetObject.month, 1 ]).day === 0 ? -7 : 0 + + return [ + Picker._.group({ + min: DAYS_IN_WEEK * rowCounter - viewsetObject.day + shiftDateBy + 1, // Add 1 for weekday 0index + max: function() { + return this.min + DAYS_IN_WEEK - 1 + }, + i: 1, + node: 'td', + item: function( targetDate ) { + + // Convert the time date from a relative date to a target date. + targetDate = calendar.create([ viewsetObject.year, viewsetObject.month, targetDate + ( settings.firstDay ? 1 : 0 ) ]) + + return [ + Picker._.node( + 'div', + targetDate.date, + (function( klasses ) { + + // Add the `infocus` or `outfocus` classes based on month in view. + klasses.push( viewsetObject.month == targetDate.month ? settings.klass.infocus : settings.klass.outfocus ) + + // Add the `today` class if needed. + if ( nowObject.pick == targetDate.pick ) { + klasses.push( settings.klass.now ) + } + + // Add the `selected` class if something's selected and the time matches. + if ( selectedObject && selectedObject.pick == targetDate.pick ) { + klasses.push( settings.klass.selected ) + } + + // Add the `highlighted` class if something's highlighted and the time matches. + if ( highlightedObject && highlightedObject.pick == targetDate.pick ) { + klasses.push( settings.klass.highlighted ) + } + + // Add the `disabled` class if something's disabled and the object matches. + if ( disabledCollection && calendar.disabled( targetDate ) || targetDate.pick < minLimitObject.pick || targetDate.pick > maxLimitObject.pick ) { + klasses.push( settings.klass.disabled ) + } + + return klasses.join( ' ' ) + })([ settings.klass.day ]), + 'data-pick=' + targetDate.pick + ) + ] //endreturn + } + }) + ] //endreturn + } + }) + ), + settings.klass.table + ) + + + Picker._.node( + 'div', + Picker._.node( 'button', settings.today, settings.klass.buttonToday, 'data-pick=' + nowObject.pick + ( isOpen ? '' : ' disabled' ) ) + + Picker._.node( 'button', settings.clear, settings.klass.buttonClear, 'data-clear=1' + ( isOpen ? '' : ' disabled' ) ), + settings.klass.footer + ) //endreturn +} //DatePicker.prototype.nodes + + + + +/** + * The date picker defaults. + */ +DatePicker.defaults = (function( prefix ) { + + return { + + // Months and weekdays + monthsFull: [ 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December' ], + monthsShort: [ 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' ], + weekdaysFull: [ 'Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday' ], + weekdaysShort: [ 'Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat' ], + + // Today and clear + today: 'Today', + clear: 'Clear', + + // The format to show on the `input` element + format: 'd mmmm, yyyy', + + // Classes + klass: { + + table: prefix + 'table', + + header: prefix + 'header', + + navPrev: prefix + 'nav--prev', + navNext: prefix + 'nav--next', + navDisabled: prefix + 'nav--disabled', + + month: prefix + 'month', + year: prefix + 'year', + + selectMonth: prefix + 'select--month', + selectYear: prefix + 'select--year', + + weekdays: prefix + 'weekday', + + day: prefix + 'day', + disabled: prefix + 'day--disabled', + selected: prefix + 'day--selected', + highlighted: prefix + 'day--highlighted', + now: prefix + 'day--today', + infocus: prefix + 'day--infocus', + outfocus: prefix + 'day--outfocus', + + footer: prefix + 'footer', + + buttonClear: prefix + 'button--clear', + buttonToday: prefix + 'button--today' + } + } +})( Picker.klasses().picker + '__' ) + + + + + +/** + * Extend the picker to add the date picker. + */ +Picker.extend( 'pickadate', DatePicker ) + + +// Close the scope. +})(); + + + diff --git a/vendor/assets/javascripts/pickadate/picker.js b/vendor/assets/javascripts/pickadate/picker.js new file mode 100755 index 00000000..ea8e67ef --- /dev/null +++ b/vendor/assets/javascripts/pickadate/picker.js @@ -0,0 +1,785 @@ + +/*! + * pickadate.js v3.0.4, 2013/05/25 + * By Amsul, http://amsul.ca + * Hosted on http://amsul.github.io/pickadate.js + * Licensed under MIT + */ + +/*jshint + debug: true, + devel: true, + browser: true, + asi: true, + unused: true, + boss: true, + eqnull: true + */ + + +// Create a global scope. +window.Picker = (function( $, $document, undefined ) { + + +/** + * The picker constructor that creates a blank picker. + */ +function PickerConstructor( ELEMENT, NAME, COMPONENT, OPTIONS ) { + + // If there’s no element, return the picker constructor. + if ( !ELEMENT ) return PickerConstructor + + + var + // The state of the picker. + STATE = { + id: Math.abs( ~~( Math.random() * 1e9 ) ) + }, + + + // Merge the defaults and options passed. + SETTINGS = COMPONENT ? $.extend( true, {}, COMPONENT.defaults, OPTIONS ) : OPTIONS || {}, + + + // Merge the default classes with the settings classes. + CLASSES = $.extend( {}, PickerConstructor.klasses(), SETTINGS.klass ), + + + // The element node wrapper into a jQuery object. + $ELEMENT = $( ELEMENT ), + + + // Pseudo picker constructor. + PickerInstance = function() { + return this.start() + }, + + + // The picker prototype. + P = PickerInstance.prototype = { + + constructor: PickerInstance, + + $node: $ELEMENT, + + + /** + * Initialize everything + */ + start: function() { + + // If it’s already started, do nothing. + if ( STATE && STATE.start ) return P + + + // Update the picker states. + STATE.methods = {} + STATE.start = true + STATE.open = false + STATE.type = ELEMENT.type + + + // Confirm focus state, save original type, convert into text input + // to remove UA stylings, and set as readonly to prevent keyboard popup. + ELEMENT.autofocus = ELEMENT == document.activeElement + ELEMENT.type = 'text' + ELEMENT.readOnly = true + + + // Create a new picker component with the settings. + P.component = new COMPONENT( P, SETTINGS ) + + + // Create the picker root with a new wrapped holder and bind the events. + P.$root = $( PickerConstructor._.node( 'div', createWrappedComponent(), CLASSES.picker ) ).on({ + + // When something within the root is focused, stop from bubbling + // to the doc and remove the “focused” state from the root. + focusin: function( event ) { + P.$root.removeClass( CLASSES.focused ) + event.stopPropagation() + }, + + // If the event is not on the root holder, stop it from bubbling to the doc. + mousedown: function( event ) { + if ( event.target != P.$root.children()[ 0 ] ) { + event.stopPropagation() + } + }, + + // When something within the root holder is clicked, handle the various event. + click: function( event ) { + + var target = event.target, + $target = target.attributes.length ? $( target ) : $( target ).closest( '[data-pick]' ), + targetData = $target.data() + + // If the event is not on the root holder itself, handle the clicks within. + if ( target != P.$root.children()[ 0 ] ) { + + // Stop it from propagating to the doc. + event.stopPropagation() + + // If nothing inside is actively focused, re-focus the element. + if ( !P.$root.find( document.activeElement ).length ) { + ELEMENT.focus() + } + + // If something is superficially changed, update the `highlight` based on the `nav`. + if ( targetData.nav && !$target.hasClass( CLASSES.navDisabled ) ) { + P.set( 'highlight', P.component.item.highlight, { nav: targetData.nav } ) + } + + // If something is picked, set `select` then close with focus. + else if ( PickerConstructor._.isInteger( targetData.pick ) && !$target.hasClass( CLASSES.disabled ) ) { + P.set( 'select', targetData.pick ).close( true ) + } + + // If a “clear” button is pressed, empty the values and close with focus. + else if ( targetData.clear ) { + P.clear().close( true ) + } + } + } + }) //P.$root + + + // If there’s a format for the hidden input element, create the element + // using the name of the original input plus suffix. Otherwise set it to null. + // If the element has a value, use either the `data-value` or `value`. + P._hidden = SETTINGS.formatSubmit ? $( '' )[ 0 ] : undefined + + + // Add the class and bind the events on the element. + $ELEMENT.addClass( CLASSES.input ). + + // On focus/click, open the picker and adjust the root “focused” state. + on( 'focus.P' + STATE.id + ' click.P' + STATE.id, focusToOpen ). + + // If the value changes, update the hidden input with the correct format. + on( 'change.P' + STATE.id, function() { + if ( P._hidden ) { + P._hidden.value = ELEMENT.value ? PickerConstructor._.trigger( P.component.formats.toString, P.component, [ SETTINGS.formatSubmit, P.component.item.select ] ) : '' + } + }). + + // Handle keyboard event based on the picker being opened or not. + on( 'keydown.P' + STATE.id, function( event ) { + + var keycode = event.keyCode, + + // Check if one of the delete keys was pressed. + isKeycodeDelete = /^(8|46)$/.test( keycode ) + + // For some reason IE clears the input value on “escape”. + if ( keycode == 27 ) { + P.close() + return false + } + + // Check if `space` or `delete` was pressed or the picker is closed with a key movement. + if ( keycode == 32 || isKeycodeDelete || !STATE.open && P.component.key[ keycode ] ) { + + // Prevent it from moving the page and bubbling to doc. + event.preventDefault() + event.stopPropagation() + + // If `delete` was pressed, clear the values and close the picker. + // Otherwise open the picker. + if ( isKeycodeDelete ) { P.clear().close() } + else { P.open() } + } + }). + + // If there’s a `data-value`, update the value of the element. + val( $ELEMENT.data( 'value' ) ? PickerConstructor._.trigger( P.component.formats.toString, P.component, [ SETTINGS.format, P.component.item.select ] ) : ELEMENT.value ). + + // Insert the root and hidden input after the element. + after( P.$root, P._hidden ). + + // Store the picker data by component name. + data( NAME, P ) + + + // Bind the default component and settings events. + P.on({ + start: P.component.onStart, + render: P.component.onRender, + stop: P.component.onStop, + open: P.component.onOpen, + close: P.component.onClose, + set: P.component.onSet + }).on({ + start: SETTINGS.onStart, + render: SETTINGS.onRender, + stop: SETTINGS.onStop, + open: SETTINGS.onOpen, + close: SETTINGS.onClose, + set: SETTINGS.onSet + }) + + + // If the element has autofocus, open the picker. + if ( ELEMENT.autofocus ) { + P.open() + } + + + // Trigger queued the “start” and “render” events. + return P.trigger( 'start' ).trigger( 'render' ) + }, //start + + + /** + * Render a new picker within the root + */ + render: function() { + + // Insert a new component holder in the root. + P.$root.html( createWrappedComponent() ) + + // Trigger the queued “render” events. + return P.trigger( 'render' ) + }, //render + + + /** + * Destroy everything + */ + stop: function() { + + // If it’s already stopped, do nothing. + if ( !STATE.start ) return P + + // Then close the picker. + P.close() + + // Remove the hidden field. + if ( P._hidden ) { + P._hidden.parentNode.removeChild( P._hidden ) + } + + // Remove the root. + P.$root.remove() + + // Remove the input class, unbind the events, and remove the stored data. + $ELEMENT.removeClass( CLASSES.input ).off( '.P' + STATE.id ).removeData( NAME ) + + // Restore the element state + ELEMENT.type = STATE.type + ELEMENT.readOnly = false + + // Trigger the queued “stop” events. + P.trigger( 'stop' ) + + // Reset the picker states. + STATE.methods = {} + STATE.start = false + + return P + }, //stop + + + /* + * Open up the picker + */ + open: function( dontGiveFocus ) { + + // If it’s already open, do nothing. + if ( STATE.open ) return P + + // Add the “active” class. + $ELEMENT.addClass( CLASSES.active ) + + // Add the “opened” class to the picker root. + P.$root.addClass( CLASSES.opened ) + + // If we have to give focus, bind the element and doc events. + if ( dontGiveFocus !== false ) { + + // Set it as open. + STATE.open = true + + // Pass focus to the element’s jQuery object. + $ELEMENT.focus() + + // Bind the document events. + $document.on( 'click.P' + STATE.id + ' focusin.P' + STATE.id, function( event ) { + + // If the target of the event is not the element, close the picker picker. + // * Don’t worry about clicks or focusins on the root because those don’t bubble up. + // Also, for Firefox, a click on an `option` element bubbles up directly + // to the doc. So make sure the target wasn't the doc. + if ( event.target != ELEMENT && event.target != document ) P.close() + + }).on( 'keydown.P' + STATE.id, function( event ) { + + var + // Get the keycode. + keycode = event.keyCode, + + // Translate that to a selection change. + keycodeToMove = P.component.key[ keycode ], + + // Grab the target. + target = event.target + + + // On escape, close the picker and give focus. + if ( keycode == 27 ) { + P.close( true ) + } + + + // Check if there is a key movement or “enter” keypress on the element. + else if ( target == ELEMENT && ( keycodeToMove || keycode == 13 ) ) { + + // Prevent the default action to stop page movement. + event.preventDefault() + + // Trigger the key movement action. + if ( keycodeToMove ) { + PickerConstructor._.trigger( P.component.key.go, P, [ keycodeToMove ] ) + } + + // On “enter”, if the highlighted item isn’t disabled, set the value and close. + else if ( !P.$root.find( '.' + CLASSES.highlighted ).hasClass( CLASSES.disabled ) ) { + P.set( 'select', P.component.item.highlight ).close() + } + } + + + // If the target is within the root and “enter” is pressed, + // prevent the default action and trigger a click on the target instead. + else if ( P.$root.find( target ).length && keycode == 13 ) { + event.preventDefault() + target.click() + } + }) + } + + // Trigger the queued “open” events. + return P.trigger( 'open' ) + }, //open + + + /** + * Close the picker + */ + close: function( giveFocus ) { + + // If we need to give focus, do it before changing states. + if ( giveFocus ) { + // ....ah yes! It would’ve been incomplete without a crazy workaround for IE :| + // The focus is triggered *after* the close has completed - causing it + // to open again. So unbind and rebind the event at the next tick. + $ELEMENT.off( 'focus.P' + STATE.id ).focus() + setTimeout( function() { + $ELEMENT.on( 'focus.P' + STATE.id, focusToOpen ) + }, 0 ) + } + + // Remove the “active” class. + $ELEMENT.removeClass( CLASSES.active ) + + // Remove the “opened” and “focused” class from the picker root. + P.$root.removeClass( CLASSES.opened + ' ' + CLASSES.focused ) + + // If it’s open, update the state. + if ( STATE.open ) { + + // Set it as closed. + STATE.open = false + + // Unbind the document events. + $document.off( '.P' + STATE.id ) + } + + // Trigger the queued “close” events. + return P.trigger( 'close' ) + }, //close + + + /** + * Clear the values + */ + clear: function() { + return P.set( 'clear' ) + }, //clear + + + /** + * Set something + */ + set: function( thing, value, options ) { + + var thingItem, thingValue, + thingIsObject = PickerConstructor._.isObject( thing ), + thingObject = thingIsObject ? thing : {} + + if ( thing ) { + + // If the thing isn’t an object, make it one. + if ( !thingIsObject ) { + thingObject[ thing ] = value + } + + // Go through the things of items to set. + for ( thingItem in thingObject ) { + + // Grab the value of the thing. + thingValue = thingObject[ thingItem ] + + // First, if the item exists and there’s a value, set it. + if ( P.component.item[ thingItem ] ) { + P.component.set( thingItem, thingValue, options || {} ) + } + + // Then, check to update the element value and broadcast a change. + if ( thingItem == 'select' || thingItem == 'clear' ) { + $ELEMENT.val( thingItem == 'clear' ? '' : + PickerConstructor._.trigger( P.component.formats.toString, P.component, [ SETTINGS.format, P.component.get( thingItem ) ] ) + ).trigger( 'change' ) + } + } + + // Render a new picker. + P.render() + } + + // Trigger queued “set” events and pass the `thingObject`. + return P.trigger( 'set', thingObject ) + }, //set + + + /** + * Get something + */ + get: function( thing, format ) { + + // Make sure there’s something to get. + thing = thing || 'value' + + // If a picker state exists, return that. + if ( STATE[ thing ] != null ) { + return STATE[ thing ] + } + + // Return the value, if that. + if ( thing == 'value' ) { + return ELEMENT.value + } + + // Check if a component item exists, return that. + if ( P.component.item[ thing ] ) { + if ( typeof format == 'string' ) { + return PickerConstructor._.trigger( P.component.formats.toString, P.component, [ format, P.component.get( thing ) ] ) + } + return P.component.get( thing ) + } + }, //get + + + + /** + * Bind events on the things. + */ + on: function( thing, method ) { + + var thingName, thingMethod, + thingIsObject = PickerConstructor._.isObject( thing ), + thingObject = thingIsObject ? thing : {} + + if ( thing ) { + + // If the thing isn’t an object, make it one. + if ( !thingIsObject ) { + thingObject[ thing ] = method + } + + // Go through the things to bind to. + for ( thingName in thingObject ) { + + // Grab the method of the thing. + thingMethod = thingObject[ thingName ] + + // Make sure the thing methods collection exists. + STATE.methods[ thingName ] = STATE.methods[ thingName ] || [] + + // Add the method to the relative method collection. + STATE.methods[ thingName ].push( thingMethod ) + } + } + + return P + }, //on + + + /** + * Fire off method events. + */ + trigger: function( name, data ) { + var methodList = STATE.methods[ name ] + if ( methodList ) { + methodList.map( function( method ) { + PickerConstructor._.trigger( method, P, [ data ] ) + }) + } + return P + } //trigger + } //PickerInstance.prototype + + + /** + * Wrap the picker holder components together. + */ + function createWrappedComponent() { + + // Create a picker wrapper holder + return PickerConstructor._.node( 'div', + + // Create a picker wrapper node + PickerConstructor._.node( 'div', + + // Create a picker frame + PickerConstructor._.node( 'div', + + // Create a picker box node + PickerConstructor._.node( 'div', + + // Create the components nodes. + P.component.nodes( STATE.open ), + + // The picker box class + CLASSES.box + ), + + // Picker wrap class + CLASSES.wrap + ), + + // Picker frame class + CLASSES.frame + ), + + // Picker holder class + CLASSES.holder + ) //endreturn + } //createWrappedComponent + + + // Separated for IE + function focusToOpen( event ) { + + // Stop the event from propagating to the doc. + event.stopPropagation() + + // If it’s a focus event, add the “focused” class to the root. + if ( event.type == 'focus' ) P.$root.addClass( CLASSES.focused ) + + // And then finally open the picker. + P.open() + } + + + // Return a new picker instance. + return new PickerInstance() +} //PickerConstructor + + + +/** + * The default classes and prefix to use for the HTML classes. + */ +PickerConstructor.klasses = function( prefix ) { + prefix = prefix || 'picker' + return { + + picker: prefix, + opened: prefix + '--opened', + focused: prefix + '--focused', + + input: prefix + '__input', + active: prefix + '__input--active', + + holder: prefix + '__holder', + + frame: prefix + '__frame', + wrap: prefix + '__wrap', + + box: prefix + '__box' + } +} //PickerConstructor.klasses + + + +/** + * PickerConstructor helper methods. + */ +PickerConstructor._ = { + + /** + * Create a group of nodes. Expects: + * ` + { + min: {Integer}, + max: {Integer}, + i: {Integer}, + node: {String}, + item: {Function} + } + * ` + */ + group: function( groupObject ) { + + var + // Scope for the looped object + loopObjectScope, + + // Create the nodes list + nodesList = '', + + // The counter starts from the `min` + counter = PickerConstructor._.trigger( groupObject.min, groupObject ) + + + // Loop from the `min` to `max`, incrementing by `i` + for ( ; counter <= PickerConstructor._.trigger( groupObject.max, groupObject, [ counter ] ); counter += groupObject.i ) { + + // Trigger the `item` function within scope of the object + loopObjectScope = PickerConstructor._.trigger( groupObject.item, groupObject, [ counter ] ) + + // Splice the subgroup and create nodes out of the sub nodes + nodesList += PickerConstructor._.node( + groupObject.node, + loopObjectScope[ 0 ], // the node + loopObjectScope[ 1 ], // the classes + loopObjectScope[ 2 ] // the attributes + ) + } + + // Return the list of nodes + return nodesList + }, //group + + + /** + * Create a dom node string + */ + node: function( wrapper, item, klass, attribute ) { + + // If the item is false-y, just return an empty string + if ( !item ) return '' + + // If the item is an array, do a join + item = Array.isArray( item ) ? item.join( '' ) : item + + // Check for the class + klass = klass ? ' class="' + klass + '"' : '' + + // Check for any attributes + attribute = attribute ? ' ' + attribute : '' + + // Return the wrapped item + return '<' + wrapper + klass + attribute + '>' + item + '' + }, //node + + + /** + * Lead numbers below 10 with a zero. + */ + lead: function( number ) { + return ( number < 10 ? '0': '' ) + number + }, + + + /** + * Trigger a function otherwise return the value. + */ + trigger: function( callback, scope, args ) { + return typeof callback == 'function' ? callback.apply( scope, args || [] ) : callback + }, + + + /** + * If the second character is a digit, length is 2 otherwise 1. + */ + digits: function( string ) { + return ( /\d/ ).test( string[ 1 ] ) ? 2 : 1 + }, + + + /** + * Tell if something is an object. + */ + isObject: function( value ) { + return {}.toString.call( value ).indexOf( 'Object' ) > -1 + }, + + + /** + * Tell if something is a date object. + */ + isDate: function( value ) { + return {}.toString.call( value ).indexOf( 'Date' ) > -1 + }, + + + /** + * Tell if something is an integer. + */ + isInteger: function( value ) { + return {}.toString.call( value ).indexOf( 'Number' ) > -1 && value % 1 === 0 + } +} //PickerConstructor._ + + + +/** + * Extend the picker with a component and defaults. + */ +PickerConstructor.extend = function( name, Component ) { + + // Extend jQuery. + $.fn[ name ] = function( options, action ) { + + // Grab the component data. + var componentData = this.data( name ) + + // If the picker is requested, return the data object. + if ( options == 'picker' ) { + return componentData + } + + // If the component data exists and `options` is a string, carry out the action. + if ( componentData && typeof options == 'string' ) { + PickerConstructor._.trigger( componentData[ options ], componentData, [ action ] ) + return this + } + + // Otherwise go through each matched element and if the component + // doesn’t exist, create a new picker using `this` element + // and merging the defaults and options with a deep copy. + return this.each( function() { + var $this = $( this ) + if ( !$this.data( name ) ) { + new PickerConstructor( this, name, Component, options ) + } + }) + } + + // Set the defaults. + $.fn[ name ].defaults = Component.defaults +} //PickerConstructor.extend + + + +// Return the picker constructor. +return PickerConstructor + + +// Close the global scope. +})( jQuery, jQuery( document ) ); + + + diff --git a/vendor/assets/javascripts/pickadate/picker.time.js b/vendor/assets/javascripts/pickadate/picker.time.js new file mode 100755 index 00000000..5f875a57 --- /dev/null +++ b/vendor/assets/javascripts/pickadate/picker.time.js @@ -0,0 +1,651 @@ + +/*! + * Time picker for pickadate.js v3.0.4 + * http://amsul.github.io/pickadate.js/time.htm + */ + +/*jshint + debug: true, + devel: true, + browser: true, + asi: true, + unused: true, + boss: true + */ + + +// Create a new scope. +(function() { + + +/** + * Globals and constants + */ +var HOURS_IN_DAY = 24, + MINUTES_IN_HOUR = 60, + HOURS_TO_NOON = 12, + MINUTES_IN_DAY = HOURS_IN_DAY * MINUTES_IN_HOUR + + + +/** + * The time picker constructor + */ +function TimePicker( picker, settings ) { + + var clock = this, + elementDataValue = picker.$node.data( 'value' ) + + clock.settings = settings + + // The queue of methods that will be used to build item objects. + clock.queue = { + interval: 'i', + min: 'measure create', + max: 'measure create', + now: 'now create', + select: 'parse create validate', + highlight: 'create validate', + view: 'create validate', + disable: 'flipItem', + enable: 'flipItem' + } + + // The component's item object. + clock.item = {} + + clock.item.interval = settings.interval || 30 + clock.item.disable = ( settings.disable || [] ).slice( 0 ) + clock.item.enable = -(function( collectionDisabled ) { + return collectionDisabled[ 0 ] === true ? collectionDisabled.shift() : -1 + })( clock.item.disable ) + + clock. + set( 'min', settings.min ). + set( 'max', settings.max ). + set( 'now' ). + + // Setting the `select` also sets the `highlight` and `view`. + set( 'select', + + // If there's a `value` or `data-value`, use that with formatting. + // Otherwise default to the minimum selectable time. + elementDataValue || picker.$node[ 0 ].value || clock.item.min, + + // Use the relevant format. + { format: elementDataValue ? settings.formatSubmit : settings.format } + ) + + // The keycode to movement mapping. + clock.key = { + 40: 1, // Down + 38: -1, // Up + 39: 1, // Right + 37: -1, // Left + go: function( timeChange ) { + clock.set( 'highlight', clock.item.highlight.pick + timeChange * clock.item.interval, { interval: timeChange * clock.item.interval } ) + this.render() + } + } + + + // Bind some picker events. + picker. + on( 'render', function() { + var $pickerHolder = picker.$root.children(), + $viewset = $pickerHolder.find( '.' + settings.klass.viewset ) + if ( $viewset.length ) { + $pickerHolder[ 0 ].scrollTop = ~~( $viewset.position().top - ( $viewset[ 0 ].clientHeight * 2 ) ) + } + else { + console.warn( 'Nothing to viewset with', clock.item.view ) + } + }). + on( 'open', function() { + picker.$root.find( 'button' ).attr( 'disable', false ) + }). + on( 'close', function() { + picker.$root.find( 'button' ).attr( 'disable', true ) + }) + +} //TimePicker + + +/** + * Set a timepicker item object. + */ +TimePicker.prototype.set = function( type, value, options ) { + + var clock = this + + // Go through the queue of methods, and invoke the function. Update this + // as the time unit, and set the final resultant as this item type. + // * In the case of `enable`, keep the queue but set `disable` instead. + // And in the case of `flip`, keep the queue but set `enable` instead. + clock.item[ ( type == 'enable' ? 'disable' : type == 'flip' ? 'enable' : type ) ] = clock.queue[ type ].split( ' ' ).map( function( method ) { + return value = clock[ method ]( type, value, options ) + }).pop() + + // Check if we need to cascade through more updates. + if ( type == 'select' ) { + clock.set( 'highlight', clock.item.select, options ) + } + else if ( type == 'highlight' ) { + clock.set( 'view', clock.item.highlight, options ) + } + else if ( type == 'interval' ) { + clock. + set( 'min', clock.item.min, options ). + set( 'max', clock.item.max, options ) + } + else if ( ( type == 'flip' || type == 'min' || type == 'max' || type == 'disable' || type == 'enable' ) && clock.item.select && clock.item.highlight ) { + if ( type == 'min' ) { + clock.set( 'max', clock.item.max, options ) + } + clock. + set( 'select', clock.item.select, options ). + set( 'highlight', clock.item.highlight, options ) + } + + return clock +} //TimePicker.prototype.set + + +/** + * Get a timepicker item object. + */ +TimePicker.prototype.get = function( type ) { + return this.item[ type ] +} //TimePicker.prototype.get + + +/** + * Create a picker time object. + */ +TimePicker.prototype.create = function( type, value, options ) { + + var clock = this + + // If there's no value, use the type as the value. + value = value === undefined ? type : value + + // If it's an object, use the "pick" value. + if ( Picker._.isObject( value ) && Picker._.isInteger( value.pick ) ) { + value = value.pick + } + + // If it's an array, convert it into minutes. + else if ( Array.isArray( value ) ) { + value = +value[ 0 ] * MINUTES_IN_HOUR + (+value[ 1 ]) + } + + // If no valid value is passed, set it to "now". + else if ( !Picker._.isInteger( value ) ) { + value = clock.now( type, value, options ) + } + + // If we're setting the max, make sure it's greater than the min. + if ( type == 'max' && value < clock.item.min.pick ) { + value += MINUTES_IN_DAY + } + + // Normalize it into a "reachable" interval. + value = clock.normalize( value, options ) + + // Return the compiled object. + return { + + // Divide to get hours from minutes. + hour: ~~( HOURS_IN_DAY + value / MINUTES_IN_HOUR ) % HOURS_IN_DAY, + + // The remainder is the minutes. + mins: ( MINUTES_IN_HOUR + value % MINUTES_IN_HOUR ) % MINUTES_IN_HOUR, + + // The time in total minutes. + time: ( MINUTES_IN_DAY + value ) % MINUTES_IN_DAY, + + // Reference to the "relative" value to pick. + pick: value + } +} //TimePicker.prototype.create + + +/** + * Get the time relative to now. + */ +TimePicker.prototype.now = function( type, value/*, options*/ ) { + + var date = new Date(), + dateMinutes = date.getHours() * MINUTES_IN_HOUR + date.getMinutes() + + // If the value is a number, adjust by that many intervals because + // the time has passed. In the case of “midnight” and a negative `min`, + // increase the value by 2. Otherwise increase it by 1. + if ( Picker._.isInteger( value ) ) { + value += type == 'min' && value < 0 && dateMinutes === 0 ? 2 : 1 + } + + // If the value isn’t a number, default to 1 passed interval. + else { + value = 1 + } + + // Calculate the final relative time. + return value * this.item.interval + dateMinutes +} //TimePicker.prototype.now + + +/** + * Normalize minutes or an object to be "reachable" based on the interval. + */ +TimePicker.prototype.normalize = function( value/*, options*/ ) { + // If it's a negative value, add one interval to keep it as "passed". + return value - ( ( value < 0 ? this.item.interval : 0 ) + value % this.item.interval ) +} //TimePicker.prototype.normalize + + +/** + * Measure the range of minutes. + */ +TimePicker.prototype.measure = function( type, value, options ) { + + var clock = this + + // If it's anything false-y, set it to the default. + if ( !value ) { + value = type == 'min' ? [ 0, 0 ] : [ HOURS_IN_DAY - 1, MINUTES_IN_HOUR - 1 ] + } + + // If it's a literal true, or an integer, make it relative to now. + else if ( value === true || Picker._.isInteger( value ) ) { + value = clock.now( type, value, options ) + } + + // If it's an object already, just normalize it. + else if ( Picker._.isObject( value ) && Picker._.isInteger( value.pick ) ) { + value = clock.normalize( value.pick, options ) + } + + return value +} ///TimePicker.prototype.measure + + +/** + * Validate an object as enabled. + */ +TimePicker.prototype.validate = function( type, timeObject, options ) { + + var clock = this, + interval = options && options.interval ? options.interval : clock.item.interval + + // Check if the object is disabled. + if ( clock.disabled( timeObject ) ) { + + // Shift with the interval until we reach an enabled time. + timeObject = clock.shift( timeObject, interval ) + } + + // Scope the object into range. + timeObject = clock.scope( timeObject ) + + // Do a second check to see if we landed on a disabled min/max. + // In that case, shift using the opposite interval as before. + if ( clock.disabled( timeObject ) ) { + timeObject = clock.shift( timeObject, interval * -1 ) + } + + // Return the final object. + return timeObject +} //TimePicker.prototype.validate + + +/** + * Check if an object is disabled. + */ +TimePicker.prototype.disabled = function( timeObject ) { + + var + clock = this, + + // Filter through the disabled times to check if this is one. + isDisabledTime = clock.item.disable.filter( function( timeToDisable ) { + + // If the time is a number, match the hours. + if ( Picker._.isInteger( timeToDisable ) ) { + return timeObject.hour == timeToDisable + } + + // If it's an array, create the object and match the times. + if ( Array.isArray( timeToDisable ) ) { + return timeObject.pick == clock.create( timeToDisable ).pick + } + }).length + + // If the clock is "enabled" flag is flipped, flip the condition. + return clock.item.enable === -1 ? !isDisabledTime : isDisabledTime +} //TimePicker.prototype.disabled + + +/** + * Shift an object by an interval until we reach an enabled object. + */ +TimePicker.prototype.shift = function( timeObject, interval ) { + + var + clock = this + + // Keep looping as long as the time is disabled. + while ( clock.disabled( timeObject ) ) { + + // Increase/decrease the time by the interval and keep looping. + timeObject = clock.create( timeObject.pick += interval || clock.item.interval ) + + // If we've looped beyond the limits, break out of the loop. + if ( timeObject.pick <= clock.item.min.pick || timeObject.pick >= clock.item.max.pick ) { + break + } + } + + // Return the final object. + return timeObject +} //TimePicker.prototype.shift + + +/** + * Scope an object to be within range of min and max. + */ +TimePicker.prototype.scope = function( timeObject ) { + var minLimit = this.item.min.pick, + maxLimit = this.item.max.pick + return this.create( timeObject.pick > maxLimit ? maxLimit : timeObject.pick < minLimit ? minLimit : timeObject ) +} //TimePicker.prototype.scope + + +/** + * Parse a string into a usable type. + */ +TimePicker.prototype.parse = function( type, value, options ) { + + var + clock = this, + parsingObject = {} + + if ( !value || Picker._.isInteger( value ) || Array.isArray( value ) || Picker._.isDate( value ) || Picker._.isObject( value ) && Picker._.isInteger( value.pick ) ) { + return value + } + + // We need a `.format` to parse the value. + if ( !( options && options.format ) ) { + throw "Need a formatting option to parse this.." + } + + // Convert the format into an array and then map through it. + clock.formats.toArray( options.format ).map( function( label ) { + + var + // Grab the formatting label. + formattingLabel = clock.formats[ label ], + + // The format length is from the formatting label function or the + // label length without the escaping exclamation (!) mark. + formatLength = formattingLabel ? Picker._.trigger( formattingLabel, clock, [ value, parsingObject ] ) : label.replace( /^!/, '' ).length + + // If there's a format label, split the value up to the format length. + // Then add it to the parsing object with appropriate label. + if ( formattingLabel ) { + parsingObject[ label ] = value.substr( 0, formatLength ) + } + + // Update the time value as the substring from format length to end. + value = value.substr( formatLength ) + }) + + return +parsingObject.i + MINUTES_IN_HOUR * ( + + +( parsingObject.H || parsingObject.HH ) || + + ( +( parsingObject.h || parsingObject.hh ) % 12 + ( /^p/i.test( parsingObject.A || parsingObject.a ) ? 12 : 0 ) ) + ) +} //TimePicker.prototype.parse + + +/** + * Various formats to display the object in. + */ +TimePicker.prototype.formats = { + + h: function( string, timeObject ) { + + // If there's string, then get the digits length. + // Otherwise return the selected hour in "standard" format. + return string ? Picker._.digits( string ) : timeObject.hour % HOURS_TO_NOON || HOURS_TO_NOON + }, + hh: function( string, timeObject ) { + + // If there's a string, then the length is always 2. + // Otherwise return the selected hour in "standard" format with a leading zero. + return string ? 2 : Picker._.lead( timeObject.hour % HOURS_TO_NOON || HOURS_TO_NOON ) + }, + H: function( string, timeObject ) { + + // If there's string, then get the digits length. + // Otherwise return the selected hour in "military" format as a string. + return string ? Picker._.digits( string ) : '' + timeObject.hour + }, + HH: function( string, timeObject ) { + + // If there's string, then get the digits length. + // Otherwise return the selected hour in "military" format with a leading zero. + return string ? Picker._.digits( string ) : Picker._.lead( timeObject.hour ) + }, + i: function( string, timeObject ) { + + // If there's a string, then the length is always 2. + // Otherwise return the selected minutes. + return string ? 2 : Picker._.lead( timeObject.mins ) + }, + a: function( string, timeObject ) { + + // If there's a string, then the length is always 4. + // Otherwise check if it's more than "noon" and return either am/pm. + return string ? 4 : MINUTES_IN_DAY / 2 > timeObject.time % MINUTES_IN_DAY ? 'a.m.' : 'p.m.' + }, + A: function( string, timeObject ) { + + // If there's a string, then the length is always 2. + // Otherwise check if it's more than "noon" and return either am/pm. + return string ? 2 : MINUTES_IN_DAY / 2 > timeObject.time % MINUTES_IN_DAY ? 'AM' : 'PM' + }, + + // Create an array by splitting the formatting string passed. + toArray: function( formatString ) { return formatString.split( /(h{1,2}|H{1,2}|i|a|A|!.)/g ) }, + + // Format an object into a string using the formatting options. + toString: function ( formatString, itemObject ) { + var clock = this + return clock.formats.toArray( formatString ).map( function( label ) { + return Picker._.trigger( clock.formats[ label ], clock, [ 0, itemObject ] ) || label.replace( /^!/, '' ) + }).join( '' ) + } +} //TimePicker.prototype.formats + + +/** + * Flip an item as enabled or disabled. + */ +TimePicker.prototype.flipItem = function( type, value/*, options*/ ) { + + var clock = this, + collection = clock.item.disable, + isFlipped = clock.item.enable === -1 + + // Flip the enabled and disabled times. + if ( value == 'flip' ) { + clock.item.enable = isFlipped ? 1 : -1 + } + + // Check if we have to add/remove from collection. + else if ( !isFlipped && type == 'enable' || isFlipped && type == 'disable' ) { + collection = clock.removeDisabled( collection, value ) + } + else if ( !isFlipped && type == 'disable' || isFlipped && type == 'enable' ) { + collection = clock.addDisabled( collection, value ) + } + + return collection +} //TimePicker.prototype.flipItem + + +/** + * Add an item to the disabled collection. + */ +TimePicker.prototype.addDisabled = function( collection, item ) { + var clock = this + item.map( function( timeUnit ) { + if ( !clock.filterDisabled( collection, timeUnit ).length ) { + collection.push( timeUnit ) + } + }) + return collection +} //TimePicker.prototype.addDisabled + + +/** + * Remove an item from the disabled collection. + */ +TimePicker.prototype.removeDisabled = function( collection, item ) { + var clock = this + item.map( function( timeUnit ) { + collection = clock.filterDisabled( collection, timeUnit, 1 ) + }) + return collection +} //TimePicker.prototype.removeDisabled + + +/** + * Filter through the disabled collection to find a time unit. + */ +TimePicker.prototype.filterDisabled = function( collection, timeUnit, isRemoving ) { + var timeIsArray = Array.isArray( timeUnit ) + return collection.filter( function( disabledTimeUnit ) { + var isMatch = !timeIsArray && timeUnit === disabledTimeUnit || + timeIsArray && Array.isArray( disabledTimeUnit ) && timeUnit.toString() === disabledTimeUnit.toString() + return isRemoving ? !isMatch : isMatch + }) +} //TimePicker.prototype.filterDisabled + + +/** + * The division to use for the range intervals. + */ +TimePicker.prototype.i = function( type, value/*, options*/ ) { + return Picker._.isInteger( value ) && value > 0 ? value : this.item.interval +} + + +/** + * Create a string for the nodes in the picker. + */ +TimePicker.prototype.nodes = function( isOpen ) { + + var + clock = this, + settings = clock.settings, + selectedObject = clock.item.select, + highlightedObject = clock.item.highlight, + viewsetObject = clock.item.view, + disabledCollection = clock.item.disable + + return Picker._.node( 'ul', Picker._.group({ + min: clock.item.min.pick, + max: clock.item.max.pick, + i: clock.item.interval, + node: 'li', + item: function( loopedTime ) { + loopedTime = clock.create( loopedTime ) + return [ + Picker._.trigger( clock.formats.toString, clock, [ Picker._.trigger( settings.formatLabel, clock, [ loopedTime ] ) || settings.format, loopedTime ] ), + (function( klasses, timeMinutes ) { + + if ( selectedObject && selectedObject.pick == timeMinutes ) { + klasses.push( settings.klass.selected ) + } + + if ( highlightedObject && highlightedObject.pick == timeMinutes ) { + klasses.push( settings.klass.highlighted ) + } + + if ( viewsetObject && viewsetObject.pick == timeMinutes ) { + klasses.push( settings.klass.viewset ) + } + + if ( disabledCollection && clock.disabled( loopedTime ) ) { + klasses.push( settings.klass.disabled ) + } + + return klasses.join( ' ' ) + })( [ settings.klass.listItem ], loopedTime.pick ), + 'data-pick=' + loopedTime.pick + ] + } + }) + Picker._.node( 'li', Picker._.node( 'button', settings.clear, settings.klass.buttonClear, 'data-clear=1' + ( isOpen ? '' : ' disable' ) ) ), settings.klass.list ) +} //TimePicker.prototype.nodes + + + + + + + +/* ========================================================================== + Extend the picker to add the component with the defaults. + ========================================================================== */ + +TimePicker.defaults = (function( prefix ) { + + return { + + // Clear + clear: 'Clear', + + // The format to show on the `input` element + format: 'h:i A', + + // The interval between each time + interval: 30, + + // Classes + klass: { + + picker: prefix + ' ' + prefix + '--time', + holder: prefix + '__holder', + + list: prefix + '__list', + listItem: prefix + '__list-item', + + disabled: prefix + '__list-item--disabled', + selected: prefix + '__list-item--selected', + highlighted: prefix + '__list-item--highlighted', + viewset: prefix + '__list-item--viewset', + now: prefix + '__list-item--now', + + buttonClear: prefix + '__button--clear' + } + } +})( Picker.klasses().picker ) + + + + + +/** + * Extend the picker to add the date picker. + */ +Picker.extend( 'pickatime', TimePicker ) + + +// Close the scope. +})(); + + + diff --git a/vendor/assets/stylesheets/pickadate/default.css b/vendor/assets/stylesheets/pickadate/default.css new file mode 100755 index 00000000..cea60bd9 --- /dev/null +++ b/vendor/assets/stylesheets/pickadate/default.css @@ -0,0 +1,234 @@ +/* ========================================================================== + $BASE-PICKER + ========================================================================== */ +/* ========================================================================== + $VARIABLES + ========================================================================== */ +/** + * Base colors + */ +/** + * Backgrounds + */ +/** + * Borders + */ +/** + * Buttons + */ +/** + * Picker base + */ +/** + * Date picker options + */ +/** + * Theme configurations + */ +/* ========================================================================== + $MIXINS + ========================================================================== */ +/** + * Common picker item states + */ +/** + * Opacity + */ +/** + * Vendor prefixes + */ +/** + * Note: the root picker element should __NOT__ be styled + * more than what’s here. Style the `.picker__holder` instead. + */ +.picker { + font-size: 16px; + text-align: left; + line-height: 1.2; + color: #000000; + position: absolute; + z-index: 10000; +} +/** + * When the picker is opened, the input element is “activated”. + */ +.picker__input.picker__input--active { + border-color: #0089ec; +} +/** + * The holder is the only “scrollable” top-level container element. + */ +.picker__holder { + width: 100%; + overflow-y: auto; + -webkit-overflow-scrolling: touch; +} + +/*! + * Default mobile-first, responsive styling for pickadate.js + * Demo: http://amsul.github.io/pickadate.js/themes.htm#default + */ +/* ========================================================================== + $VARIABLES + ========================================================================== */ +/** + * Base colors + */ +/** + * Backgrounds + */ +/** + * Borders + */ +/** + * Buttons + */ +/** + * Picker base + */ +/** + * Date picker options + */ +/** + * Theme configurations + */ +/* ========================================================================== + $MIXINS + ========================================================================== */ +/** + * Common picker item states + */ +/** + * Opacity + */ +/** + * Vendor prefixes + */ +/** + * Make the holder and frame fullscreen. + */ +.picker__holder, +.picker__frame { + bottom: 0; + left: 0; + right: 0; + top: 100%; +} +/** + * The holder should overlay the entire screen. + */ +.picker__holder { + position: fixed; + -webkit-transition: background 0.15s ease-out, top 0s 0.15s; + -moz-transition: background 0.15s ease-out, top 0s 0.15s; + transition: background 0.15s ease-out, top 0s 0.15s; +} +/** + * The frame that bounds the box contents of the picker. + */ +.picker__frame { + position: absolute; + margin: 0 auto; + min-width: 256px; + max-width: 666px; + width: 100%; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; + filter: alpha(opacity=0); + -moz-opacity: 0; + opacity: 0; + -webkit-transition: all 0.15s ease-out; + -moz-transition: all 0.15s ease-out; + transition: all 0.15s ease-out; +} +@media (min-height: 33.875em) { + .picker__frame { + overflow: visible; + top: auto; + bottom: -100%; + max-height: 80%; + } +} +@media (min-height: 40.125em) { + .picker__frame { + margin-bottom: 7.5%; + } +} +/** + * The wrapper sets the stage to vertically align the box contents. + */ +.picker__wrap { + display: table; + width: 100%; + height: 100%; +} +@media (min-height: 33.875em) { + .picker__wrap { + display: block; + } +} +/** + * The box contains all the picker contents. + */ +.picker__box { + background: #ffffff; + display: table-cell; + vertical-align: middle; +} +@media (min-height: 26.5em) { + .picker__box { + font-size: 1.25em; + } +} +@media (min-height: 33.875em) { + .picker__box { + display: block; + font-size: 1.33em; + border: 1px solid #777777; + border-top-color: #898989; + border-bottom-width: 0; + -webkit-border-radius: 5px 5px 0 0; + -moz-border-radius: 5px 5px 0 0; + border-radius: 5px 5px 0 0; + -webkit-box-shadow: 0 12px 36px 16px rgba(0, 0, 0, 0.24); + -moz-box-shadow: 0 12px 36px 16px rgba(0, 0, 0, 0.24); + box-shadow: 0 12px 36px 16px rgba(0, 0, 0, 0.24); + } +} +@media (min-height: 40.125em) { + .picker__box { + font-size: 1.5em; + border-bottom-width: 1px; + -webkit-border-radius: 5px; + -moz-border-radius: 5px; + border-radius: 5px; + } +} +/** + * When the picker opens... + */ +.picker--opened .picker__holder { + top: 0; + background: transparent; + -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr=#1E000000,endColorstr=#1E000000)"; + zoom: 1; + background: rgba(0, 0, 0, 0.32); + -webkit-transition: background 0.15s ease-out; + -moz-transition: background 0.15s ease-out; + transition: background 0.15s ease-out; +} +.picker--opened .picker__frame { + top: 0; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=100)"; + filter: alpha(opacity=100); + -moz-opacity: 1; + opacity: 1; +} +@media (min-height: 33.875em) { + .picker--opened .picker__frame { + top: auto; + bottom: 0; + } +} +/** + * For `large` screens, transform into an inline picker. + */ diff --git a/vendor/assets/stylesheets/pickadate/default.date.css b/vendor/assets/stylesheets/pickadate/default.date.css new file mode 100755 index 00000000..93006db6 --- /dev/null +++ b/vendor/assets/stylesheets/pickadate/default.date.css @@ -0,0 +1,332 @@ +/* ========================================================================== + $BASE-DATE-PICKER + ========================================================================== */ +/* ========================================================================== + $VARIABLES + ========================================================================== */ +/** + * Base colors + */ +/** + * Backgrounds + */ +/** + * Borders + */ +/** + * Buttons + */ +/** + * Picker base + */ +/** + * Date picker options + */ +/** + * Theme configurations + */ +/* ========================================================================== + $MIXINS + ========================================================================== */ +/** + * Common picker item states + */ +/** + * Opacity + */ +/** + * Vendor prefixes + */ +/** + * The picker box. + */ +.picker__box { + padding: 0 1em; +} +/** + * The header containing the month and year stuff. + */ +.picker__header { + text-align: center; + position: relative; + margin-top: .75em; +} +/** + * The month and year labels. + */ +.picker__month, +.picker__year { + font-weight: 500; + display: inline-block; + margin-left: .25em; + margin-right: .25em; +} +.picker__year { + color: #999999; + font-size: .8em; + font-style: italic; +} +/** + * The month and year selectors. + */ +.picker__select--month, +.picker__select--year { + font-size: .8em; + border: 1px solid #b7b7b7; + height: 2.5em; + padding: .66em .25em; + margin-left: .25em; + margin-right: .25em; + margin-top: -0.5em; +} +.picker__select--month { + width: 35%; +} +.picker__select--year { + width: 22.5%; +} +.picker__select--month:focus, +.picker__select--year:focus { + border-color: #0089ec; +} +/** + * The month navigation buttons. + */ +.picker__nav--prev, +.picker__nav--next { + position: absolute; + top: -0.33em; + padding: .5em 1.33em; + width: 1em; + height: 1em; +} +.picker__nav--prev { + left: -1em; + padding-right: 1.5em; +} +.picker__nav--next { + right: -1em; + padding-left: 1.5em; +} +.picker__nav--prev:before, +.picker__nav--next:before { + content: " "; + border-top: .5em solid transparent; + border-bottom: .5em solid transparent; + border-right: 0.75em solid #000000; + width: 0; + height: 0; + display: block; + margin: 0 auto; +} +.picker__nav--next:before { + border-right: 0; + border-left: 0.75em solid #000000; +} +.picker__nav--prev:hover, +.picker__nav--next:hover { + cursor: pointer; + color: #000000; + background: #b1dcfb; +} +.picker__nav--disabled, +.picker__nav--disabled:hover, +.picker__nav--disabled:before, +.picker__nav--disabled:before:hover { + cursor: default; + background: none; + border-right-color: #f5f5f5; + border-left-color: #f5f5f5; +} +/** + * The calendar table of dates + */ +.picker__table { + text-align: center; + border-collapse: collapse; + border-spacing: 0; + table-layout: fixed; + font-size: inherit; + width: 100%; + margin-top: .75em; + margin-bottom: .5em; +} +@media (min-height: 33.875em) { + .picker__table { + margin-bottom: .75em; + } +} +.picker__table td { + margin: 0; + padding: 0; +} +/** + * The weekday labels + */ +.picker__weekday { + width: 14.285714286%; + font-size: .75em; + padding-bottom: .25em; + color: #999999; + font-weight: 500; + /* Increase the spacing a tad */ + +} +@media (min-height: 33.875em) { + .picker__weekday { + padding-bottom: .5em; + } +} +/** + * The days on the calendar + */ +.picker__day { + padding: .3125em 0; + font-weight: 200; + border: 1px solid transparent; +} +.picker__day--today { + color: #0089ec; + position: relative; +} +.picker__day--today:before { + content: " "; + position: absolute; + top: 2px; + right: 2px; + width: 0; + height: 0; + border-top: 0.5em solid #0059bc; + border-left: .5em solid transparent; +} +.picker__day--selected, +.picker__day--selected:hover { + border-color: #0089ec; +} +.picker__day--highlighted { + background: #b1dcfb; +} +.picker__day--disabled:before { + border-top-color: #aaaaaa; +} +.picker__day--outfocus { + color: #dddddd; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=66)"; + filter: alpha(opacity=66); + -moz-opacity: 0.66; + opacity: 0.66; +} +.picker__day--infocus:hover, +.picker__day--outfocus:hover { + cursor: pointer; + color: #000000; + background: #b1dcfb; +} +.picker__day--highlighted:hover, +.picker--focused .picker__day--highlighted { + background: #0089ec; + color: #ffffff; +} +.picker__day--disabled:hover, +.picker--focused .picker__day--disabled { + background: #f5f5f5; + border-color: #f5f5f5; + color: #dddddd; + cursor: default; +} +.picker__day--highlighted.picker__day--disabled:hover, +.picker--focused .picker__day--highlighted.picker__day--disabled { + background: #bbbbbb; +} +/** + * The footer containing the "today" and "clear" buttons. + */ +.picker__footer { + text-align: center; +} +.picker__button--today, +.picker__button--clear { + border: 1px solid #ffffff; + background: #ffffff; + font-size: .8em; + padding: .66em 0; + font-weight: bold; + width: 50%; + display: inline-block; + vertical-align: bottom; +} +.picker__button--today:hover, +.picker__button--clear:hover { + cursor: pointer; + color: #000000; + background: #b1dcfb; + border-bottom-color: #b1dcfb; +} +.picker__button--today:focus, +.picker__button--clear:focus { + background: #b1dcfb; + border-color: #0089ec; + outline: none; +} +.picker__button--today:before, +.picker__button--clear:before { + position: relative; + display: inline-block; + height: 0; +} +.picker__button--today:before { + content: " "; + margin-right: .45em; + top: -0.05em; + width: 0; + border-top: 0.66em solid #0059bc; + border-left: .66em solid transparent; +} +.picker__button--clear:before { + content: "\D7"; + margin-right: .35em; + top: -0.1em; + color: #ee2200; + vertical-align: top; + font-size: 1.1em; +} + +/* ========================================================================== + $DEFAULT-DATE-PICKER + ========================================================================== */ +/* ========================================================================== + $VARIABLES + ========================================================================== */ +/** + * Base colors + */ +/** + * Backgrounds + */ +/** + * Borders + */ +/** + * Buttons + */ +/** + * Picker base + */ +/** + * Date picker options + */ +/** + * Theme configurations + */ +/* ========================================================================== + $MIXINS + ========================================================================== */ +/** + * Common picker item states + */ +/** + * Opacity + */ +/** + * Vendor prefixes + */ diff --git a/vendor/assets/stylesheets/pickadate/default.time.css b/vendor/assets/stylesheets/pickadate/default.time.css new file mode 100755 index 00000000..526ccd0b --- /dev/null +++ b/vendor/assets/stylesheets/pickadate/default.time.css @@ -0,0 +1,193 @@ +/* ========================================================================== + $BASE-TIME-PICKER + ========================================================================== */ +/* ========================================================================== + $VARIABLES + ========================================================================== */ +/** + * Base colors + */ +/** + * Backgrounds + */ +/** + * Borders + */ +/** + * Buttons + */ +/** + * Picker base + */ +/** + * Date picker options + */ +/** + * Theme configurations + */ +/* ========================================================================== + $MIXINS + ========================================================================== */ +/** + * Common picker item states + */ +/** + * Opacity + */ +/** + * Vendor prefixes + */ +/** + * The list of times. + */ +.picker__list { + list-style: none; + padding: 0.75em 0 4.2em; + margin: 0; +} +/** + * The times on the clock. + */ +.picker__list-item { + border-bottom: 1px solid #dddddd; + border-top: 1px solid #dddddd; + margin-bottom: -1px; + position: relative; + background: #ffffff; + padding: .75em 1.25em; +} +@media (min-height: 46.75em) { + .picker__list-item { + padding: .5em 1em; + } +} +/* Hovered time */ +.picker__list-item:hover { + cursor: pointer; + color: #000000; + background: #b1dcfb; + border-color: #0089ec; + z-index: 10; +} +/* Selected time */ +.picker__list-item--selected, +.picker__list-item--selected:hover { + border-color: #0089ec; + z-index: 10; +} +/* Highlighted time */ +.picker__list-item--highlighted { + background: #b1dcfb; +} +/* Highlighted and hovered/focused time */ +.picker__list-item--highlighted:hover, +.picker--focused .picker__list-item--highlighted { + background: #0089ec; + color: #ffffff; +} +/* Disabled time */ +.picker__list-item--disabled, +.picker__list-item--disabled:hover { + background: #f5f5f5; + border-color: #f5f5f5; + color: #dddddd; + cursor: default; + border-color: #777777; + z-index: auto; +} +/** + * The clear button + */ +.picker--time .picker__button--clear { + display: block; + width: 80%; + margin: 1em auto 0; + padding: 1em 1.25em; + background: none; + border: 0; + font-weight: 500; + font-size: .67em; + text-align: center; + text-transform: uppercase; + color: #666; +} +.picker--time .picker__button--clear:hover, +.picker--time .picker__button--clear:focus { + color: #000000; + background: #b1dcfb; + background: #ee2200; + border-color: #ee2200; + cursor: pointer; + color: #ffffff; + outline: none; +} +.picker--time .picker__button--clear:before { + top: -0.25em; + color: #666; + font-size: 1.25em; + font-weight: bold; +} +.picker--time .picker__button--clear:hover:before, +.picker--time .picker__button--clear:focus:before { + color: #ffffff; +} + +/* ========================================================================== + $DEFAULT-TIME-PICKER + ========================================================================== */ +/* ========================================================================== + $VARIABLES + ========================================================================== */ +/** + * Base colors + */ +/** + * Backgrounds + */ +/** + * Borders + */ +/** + * Buttons + */ +/** + * Picker base + */ +/** + * Date picker options + */ +/** + * Theme configurations + */ +/* ========================================================================== + $MIXINS + ========================================================================== */ +/** + * Common picker item states + */ +/** + * Opacity + */ +/** + * Vendor prefixes + */ +/** + * The frame the bounds the time picker. + */ +.picker--time .picker__frame { + min-width: 256px; + max-width: 320px; +} +/** + * The picker box. + */ +.picker--time .picker__box { + font-size: 1em; + background: #f2f2f2; + padding: 0; +} +@media (min-height: 40.125em) { + .picker--time .picker__box { + margin-bottom: 5em; + } +} From 6718b555ac21f75be16c548d6892dd037cfbddb5 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Thu, 30 May 2013 10:19:28 +0300 Subject: [PATCH 120/499] Some basic styling. --- app/assets/javascripts/initializers.coffee | 3 +- app/assets/stylesheets/_utils.sass | 1 + app/assets/stylesheets/application.css.scss | 9 ++--- .../partials/activities/_filters.sass | 11 ------ .../stylesheets/partials/activities/_new.sass | 14 ++++++++ app/assets/stylesheets/utils/_forms.sass | 34 +++++++++++++++++++ app/views/activities/new.html.haml | 26 ++++++++------ app/views/layouts/application.html.haml | 2 +- 8 files changed, 72 insertions(+), 28 deletions(-) create mode 100644 app/assets/stylesheets/partials/activities/_new.sass create mode 100644 app/assets/stylesheets/utils/_forms.sass diff --git a/app/assets/javascripts/initializers.coffee b/app/assets/javascripts/initializers.coffee index 22648dd7..3ef2e980 100644 --- a/app/assets/javascripts/initializers.coffee +++ b/app/assets/javascripts/initializers.coffee @@ -10,16 +10,17 @@ $ -> $('.date-capture').pickadate + format: 'ddd, d.m.' onSet: (e)-> date = new Date(e.select) target = @$node.data 'target' [day, month, year] = [date.getDate(), date.getMonth() + 1, date.getFullYear()] - console.log day, month, year $("#activity_#{target}_1i").val(year) $("#activity_#{target}_2i").val(month) $("#activity_#{target}_3i").val(day) $('.time-capture').pickatime + format: 'h:i' onSet: (e)-> target = @$node.data 'target' [hours, minutes] = [parseInt((e.select / 60), 10), e.select % 60] diff --git a/app/assets/stylesheets/_utils.sass b/app/assets/stylesheets/_utils.sass index 011c64c7..cc7fd829 100644 --- a/app/assets/stylesheets/_utils.sass +++ b/app/assets/stylesheets/_utils.sass @@ -1,3 +1,4 @@ @import "utils/helpers" @import "utils/fonts" @import "utils/placeholders" +@import "utils/forms" diff --git a/app/assets/stylesheets/application.css.scss b/app/assets/stylesheets/application.css.scss index a63437d7..7aab8a23 100644 --- a/app/assets/stylesheets/application.css.scss +++ b/app/assets/stylesheets/application.css.scss @@ -5,12 +5,13 @@ @import "utils"; @import "base"; +@import "pickadate/default"; +@import "pickadate/default.date"; +@import "pickadate/default.time"; + @import "partials/layout"; @import "partials/header"; @import "partials/footer"; @import "partials/activities"; - -@import "pickadate/default"; -@import "pickadate/default.date"; -@import "pickadate/default.time"; +@import "partials/activities/new"; diff --git a/app/assets/stylesheets/partials/activities/_filters.sass b/app/assets/stylesheets/partials/activities/_filters.sass index 60fbcea8..1b9f74bb 100644 --- a/app/assets/stylesheets/partials/activities/_filters.sass +++ b/app/assets/stylesheets/partials/activities/_filters.sass @@ -49,17 +49,6 @@ form.filters position: absolute right: 0 top: 1em - input - width: 100% - padding: 0.75em 15px 1em - margin: 0 0 2px 0 - font-size: 1.25em - border: none - outline: none - +custom-sans(light) - - &:focus - box-shadow: 0 0 7px transparentize($black, 0.9) strong +custom-sans(light) diff --git a/app/assets/stylesheets/partials/activities/_new.sass b/app/assets/stylesheets/partials/activities/_new.sass new file mode 100644 index 00000000..7e3fc6d8 --- /dev/null +++ b/app/assets/stylesheets/partials/activities/_new.sass @@ -0,0 +1,14 @@ +form#new-activity + input.date-capture, + input.time-capture + display: none + + html.js & + .select + display: none + + input.date-capture, + input.time-capture + display: inline + + diff --git a/app/assets/stylesheets/utils/_forms.sass b/app/assets/stylesheets/utils/_forms.sass new file mode 100644 index 00000000..29ae7744 --- /dev/null +++ b/app/assets/stylesheets/utils/_forms.sass @@ -0,0 +1,34 @@ +form + input, textarea + width: 100% + padding: 0.75em 15px 1em + margin: 0 0 2px 0 + font-size: 1.25em + border: none + outline: none + +custom-sans(light) + + &.date-capture + width: 10em + + &.time-capture + width: 4em + text-align: center + + &:focus + box-shadow: 0 0 7px transparentize($black, 0.9) + + span.between + display: inline-block + margin: 0 2em + +custom-sans(bold) + text-transform: uppercase + + label.inline + display: inline + + input + display: inline + width: auto + margin: 0 0.25em 0 0 + vertical-align: baseline diff --git a/app/views/activities/new.html.haml b/app/views/activities/new.html.haml index bbc4ecc0..42e653b8 100644 --- a/app/views/activities/new.html.haml +++ b/app/views/activities/new.html.haml @@ -1,7 +1,7 @@ %h2 Organize an activity -= form_for @activity, id: "new-activity" do |f| += form_for @activity, html: { id: "new-activity" } do |f| - if @activity.errors.any? %ul.errors - @activity.errors.full_messages.each do |error| @@ -9,20 +9,24 @@ = f.text_field :name, placeholder: "Give your activity a name", class: "title" %hr - = f.datetime_select :start_time - %input.date-capture(data-target="start_time" type="text") - %input.time-capture(data-target="start_time" type="text") - – - = f.datetime_select :end_time - %input.date-capture(data-target="end_time" type="text") - %input.time-capture(data-target="end_time" type="text") + .select + = f.datetime_select :start_time + %input.date-capture(data-target="start_time" type="text" placeholder="Sat, 17.8") + %input.time-capture(data-target="start_time" type="text" placeholder="15:00") + %span – + .select + = f.datetime_select :end_time + %input.date-capture(data-target="end_time" type="text" placeholder="Sat, 17.8") + %input.time-capture(data-target="end_time" type="text" placeholder="16:30") - %span or - %label + %span.between or + %label.inline = f.check_box :anytime Without a specific date/time + = content_tag :div, "" = f.text_field :place, placeholder: "Location, where to meet …" = f.text_area :requirements, placeholder: "Requirements, what to bring …" - = f.text_area :description, placeholder: "Description," + = f.text_area :description, placeholder: "Description, background information, links, etc." + = "###{@activity.description.to_s.html_safe}##" %button(type="submit") Save diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml index 1a9e72e1..1484f3c8 100644 --- a/app/views/layouts/application.html.haml +++ b/app/views/layouts/application.html.haml @@ -13,7 +13,7 @@ = stylesheet_link_tag "application" = javascript_include_tag "modernizr.min" - -#= javascript_include_tag "//use.typekit.net/vor5lqb.js" + = javascript_include_tag "//use.typekit.net/vor5lqb.js" :javascript try{Typekit.load();}catch(e){} From 25c25023de65902d7a80b4702b0030a5971814dd Mon Sep 17 00:00:00 2001 From: pietia Date: Wed, 29 May 2013 23:55:25 +0200 Subject: [PATCH 121/499] added CanCan --- Gemfile | 1 + Gemfile.lock | 2 ++ app/models/ability.rb | 32 ++++++++++++++++++++++++++++++++ 3 files changed, 35 insertions(+) create mode 100644 app/models/ability.rb diff --git a/Gemfile b/Gemfile index 4f7a0e9c..b35fb8e3 100644 --- a/Gemfile +++ b/Gemfile @@ -21,6 +21,7 @@ gem 'neat' gem 'haml-rails' gem 'rails_html_helpers' gem 'draper' +gem 'cancan' group :development do gem 'debugger', '~> 1.5' diff --git a/Gemfile.lock b/Gemfile.lock index 54df2c89..e310a787 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -49,6 +49,7 @@ GEM sass (>= 3.2.0) thor builder (3.1.4) + cancan (1.6.10) capybara (2.1.0) mime-types (>= 1.16) nokogiri (>= 1.3.3) @@ -270,6 +271,7 @@ DEPENDENCIES better_errors binding_of_caller bourbon + cancan capybara (~> 2.1) capybara-webkit (~> 0.14) coffee-rails (~> 4.0.0.beta1) diff --git a/app/models/ability.rb b/app/models/ability.rb new file mode 100644 index 00000000..e03bf9be --- /dev/null +++ b/app/models/ability.rb @@ -0,0 +1,32 @@ +class Ability + include CanCan::Ability + + def initialize(user) + # Define abilities for the passed in user here. For example: + # + # user ||= User.new # guest user (not logged in) + # if user.admin? + # can :manage, :all + # else + # can :read, :all + # end + # + # The first argument to `can` is the action you are giving the user + # permission to do. + # If you pass :manage it will apply to every action. Other common actions + # here are :read, :create, :update and :destroy. + # + # The second argument is the resource the user can perform the action on. + # If you pass :all it will apply to every resource. Otherwise pass a Ruby + # class of the resource. + # + # The third argument is an optional hash of conditions to further filter the + # objects. + # For example, here the user can only update published articles. + # + # can :update, Article, :published => true + # + # See the wiki for details: + # https://github.com/ryanb/cancan/wiki/Defining-Abilities + end +end From ac2c2b0665c2a4b874fa858ee1d2bfe28a887362 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Thu, 30 May 2013 16:40:14 +0300 Subject: [PATCH 122/499] Counters. --- app/assets/javascripts/application.js | 1 - app/controllers/activities_controller.rb | 2 +- app/models/activity.rb | 18 ++++++++++++++++-- app/views/activities/index.html.haml | 10 +++++----- 4 files changed, 22 insertions(+), 9 deletions(-) diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index 6b639bc8..e07feab8 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -18,5 +18,4 @@ //= require pickadate/picker //= require pickadate/picker.date //= require pickadate/picker.time -//= require pickadate/legacy //= require initializers diff --git a/app/controllers/activities_controller.rb b/app/controllers/activities_controller.rb index 06b2e017..485ab25d 100644 --- a/app/controllers/activities_controller.rb +++ b/app/controllers/activities_controller.rb @@ -4,7 +4,7 @@ class ActivitiesController < ApplicationController skip_before_filter :authenticate_user!, :only => [:index, :show] def index - @activities = current_event.activities.decorate + @activities = current_event.activities respond_with(@activities) end diff --git a/app/models/activity.rb b/app/models/activity.rb index 33f98fd2..0964bc7b 100644 --- a/app/models/activity.rb +++ b/app/models/activity.rb @@ -14,8 +14,22 @@ class Activity < ActiveRecord::Base validates :place, presence: true, allow_blank: false validates :limit_of_participants, numericality: {greater_than: 0}, allow_nil: true - def self.recent(limit = DEFAULT_LIMIT) - where("start_time >= :t", t: 1.month.ago).limit(limit) + class << self + def recent(limit = DEFAULT_LIMIT) + where("start_time >= :t", t: 1.month.ago).limit(limit) + end + + def today + where("created_at >= :t", t: Time.zone.now.beginning_of_day) + end + + def created_by(user) + where(creator: user) + end + + def participated_by(user) + includes(:participations).where(participations: { participant: user }) + end end def full_by diff --git a/app/views/activities/index.html.haml b/app/views/activities/index.html.haml index a1d04fee..bfd056e1 100644 --- a/app/views/activities/index.html.haml +++ b/app/views/activities/index.html.haml @@ -3,22 +3,22 @@ %input(type="radio" name="filter" value="today") = t("activities.filters.today") %strong - 5 + = @activities.today.count %label.all.selected> %input(type="radio" name="filter" value="all") = t("activities.filters.all") %strong - 267 + = @activities.count %label.participant> %input(type="radio" name="filter" value="participant") = t("activities.filters.participant") %strong - 2 + = @activities.participated_by(current_user).count %label.owner> %input(type="radio" name="filter" value="owner") = t("activities.filters.owner") %strong - 0 + = @activities.created_by(current_user).count %label.search %input(type="text" name="search" placeholder="#{ t("activities.search.placeholder") }") %a.clear(href="#" title="Reset") @@ -31,4 +31,4 @@ %span %ul#activities - = render @activities + = render @activities.decorate From abc94fbf42abb8fb68e749077537a0717daa3d7b Mon Sep 17 00:00:00 2001 From: pietia Date: Thu, 30 May 2013 15:45:56 +0200 Subject: [PATCH 123/499] redirect to the list --- app/controllers/activities_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/activities_controller.rb b/app/controllers/activities_controller.rb index 485ab25d..280bc637 100644 --- a/app/controllers/activities_controller.rb +++ b/app/controllers/activities_controller.rb @@ -26,7 +26,7 @@ def edit def create @activity = current_event.new_activity(current_user, sanitized_params) @activity.save - respond_with(@activity) + respond_with(@activity, location: activities_path) end def update From 44a95a410b9eb62f0d55013bfb3c23f4d9f08c65 Mon Sep 17 00:00:00 2001 From: pietia Date: Thu, 30 May 2013 15:51:06 +0200 Subject: [PATCH 124/499] it should be true by default --- db/migrate/20130530134900_set_anytime_to_true_by_default.rb | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 db/migrate/20130530134900_set_anytime_to_true_by_default.rb diff --git a/db/migrate/20130530134900_set_anytime_to_true_by_default.rb b/db/migrate/20130530134900_set_anytime_to_true_by_default.rb new file mode 100644 index 00000000..b489fa42 --- /dev/null +++ b/db/migrate/20130530134900_set_anytime_to_true_by_default.rb @@ -0,0 +1,5 @@ +class SetAnytimeToTrueByDefault < ActiveRecord::Migration + def change + change_column :activities, :antyime, boolean, default: true, null: false + end +end From ebfbd170d1e690420c3fc0d0e6c283534b68d9f7 Mon Sep 17 00:00:00 2001 From: pietia Date: Thu, 30 May 2013 15:55:18 +0200 Subject: [PATCH 125/499] whoops, typos --- db/migrate/20130530134900_set_anytime_to_true_by_default.rb | 2 +- db/schema.rb | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/db/migrate/20130530134900_set_anytime_to_true_by_default.rb b/db/migrate/20130530134900_set_anytime_to_true_by_default.rb index b489fa42..e20c75fc 100644 --- a/db/migrate/20130530134900_set_anytime_to_true_by_default.rb +++ b/db/migrate/20130530134900_set_anytime_to_true_by_default.rb @@ -1,5 +1,5 @@ class SetAnytimeToTrueByDefault < ActiveRecord::Migration def change - change_column :activities, :antyime, boolean, default: true, null: false + change_column :activities, :anytime, :boolean, default: true, null: false end end diff --git a/db/schema.rb b/db/schema.rb index 4383eacc..28bd372e 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,13 +11,13 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20130529182104) do +ActiveRecord::Schema.define(version: 20130530134900) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" create_table "activities", force: true do |t| - t.string "name", default: "", null: false + t.string "name", default: "", null: false t.text "description" t.string "place" t.datetime "start_time" @@ -26,7 +26,7 @@ t.datetime "updated_at" t.integer "creator_id" t.integer "participations_count", default: 0 - t.boolean "anytime", default: false + t.boolean "anytime", default: true, null: false t.text "requirements" t.datetime "end_time" end From 169ed5488a848360be330fcd6c1ebc438407e065 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Thu, 30 May 2013 16:55:41 +0300 Subject: [PATCH 126/499] Updating counter for participation. --- app/views/activities/index.html.haml | 2 +- app/views/participations/create.js.erb | 1 + app/views/participations/destroy.js.erb | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/app/views/activities/index.html.haml b/app/views/activities/index.html.haml index bfd056e1..bff056dd 100644 --- a/app/views/activities/index.html.haml +++ b/app/views/activities/index.html.haml @@ -1,4 +1,4 @@ -%form.filters(action=activities_path method="GET") +%form.filters(action=activities_path method="GET" id="activities-filter") %label> %input(type="radio" name="filter" value="today") = t("activities.filters.today") diff --git a/app/views/participations/create.js.erb b/app/views/participations/create.js.erb index 0fe2a2c5..fa2b1019 100644 --- a/app/views/participations/create.js.erb +++ b/app/views/participations/create.js.erb @@ -3,3 +3,4 @@ $('#activity-<%= @current_activity.id %>') $('#activity-<%= @current_activity.id %>') .find('img.progress') .progress(); +$('#activities-filter .participant strong').text('<%= Activity.participated_by(current_user).count %>') diff --git a/app/views/participations/destroy.js.erb b/app/views/participations/destroy.js.erb index 0fe2a2c5..fa2b1019 100644 --- a/app/views/participations/destroy.js.erb +++ b/app/views/participations/destroy.js.erb @@ -3,3 +3,4 @@ $('#activity-<%= @current_activity.id %>') $('#activity-<%= @current_activity.id %>') .find('img.progress') .progress(); +$('#activities-filter .participant strong').text('<%= Activity.participated_by(current_user).count %>') From d82b2f9ed7c721d5cfbfebc8037f1fd3edef05a1 Mon Sep 17 00:00:00 2001 From: pietia Date: Thu, 30 May 2013 15:56:49 +0200 Subject: [PATCH 127/499] ok,it works, tested in lynx ;) --- app/controllers/participations_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/participations_controller.rb b/app/controllers/participations_controller.rb index cefa8683..22374e91 100644 --- a/app/controllers/participations_controller.rb +++ b/app/controllers/participations_controller.rb @@ -1,5 +1,5 @@ class ParticipationsController < ApplicationController - respond_to :js + respond_to :js, :html def create participation = current_activity.new_participation(current_user) From 57ec84dac94fa9b17f1cd2eb14e2ceaff2dd539b Mon Sep 17 00:00:00 2001 From: pietia Date: Thu, 30 May 2013 15:57:11 +0200 Subject: [PATCH 128/499] nil is acceptable --- app/models/activity.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/models/activity.rb b/app/models/activity.rb index 0964bc7b..516f40b7 100644 --- a/app/models/activity.rb +++ b/app/models/activity.rb @@ -8,8 +8,8 @@ class Activity < ActiveRecord::Base has_many :participations, :dependent => :destroy has_many :participants, through: :participations, class_name: "User" - validates :start_time, presence: true, allow_blank: false - validates :end_time, presence: true, allow_blank: false + validates :start_time, presence: true, allow_blank: false, allow_nil: true + validates :end_time, presence: true, allow_blank: false, allow_nil: true validates :name, presence: true, allow_blank: false, uniqueness: true validates :place, presence: true, allow_blank: false validates :limit_of_participants, numericality: {greater_than: 0}, allow_nil: true From 967a671caf6d52bb9dfe9513881c75fbf2d002e4 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Thu, 30 May 2013 18:19:36 +0300 Subject: [PATCH 129/499] Limits and defaults. --- app/assets/javascripts/initializers.coffee | 2 ++ app/views/activities/new.html.haml | 4 ++-- app/views/layouts/application.html.haml | 6 +++++- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/app/assets/javascripts/initializers.coffee b/app/assets/javascripts/initializers.coffee index 3ef2e980..a71fa9fd 100644 --- a/app/assets/javascripts/initializers.coffee +++ b/app/assets/javascripts/initializers.coffee @@ -10,6 +10,8 @@ $ -> $('.date-capture').pickadate + min: new Date(App.event.startTime) + max: new Date(App.event.endTime) format: 'ddd, d.m.' onSet: (e)-> date = new Date(e.select) diff --git a/app/views/activities/new.html.haml b/app/views/activities/new.html.haml index 42e653b8..84c12b85 100644 --- a/app/views/activities/new.html.haml +++ b/app/views/activities/new.html.haml @@ -11,12 +11,12 @@ %hr .select = f.datetime_select :start_time - %input.date-capture(data-target="start_time" type="text" placeholder="Sat, 17.8") + %input.date-capture(data-target="start_time" type="text" placeholder="#{ Settings.event.start_time.strftime("%a, %e.%-m.") }") %input.time-capture(data-target="start_time" type="text" placeholder="15:00") %span – .select = f.datetime_select :end_time - %input.date-capture(data-target="end_time" type="text" placeholder="Sat, 17.8") + %input.date-capture(data-target="end_time" type="text" placeholder="#{ Settings.event.start_time.strftime("%a, %e.%-m.") }") %input.time-capture(data-target="end_time" type="text" placeholder="16:30") %span.between or diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml index 1484f3c8..36ea8f47 100644 --- a/app/views/layouts/application.html.haml +++ b/app/views/layouts/application.html.haml @@ -16,10 +16,14 @@ = javascript_include_tag "//use.typekit.net/vor5lqb.js" :javascript try{Typekit.load();}catch(e){} - + // TODO: partial var App = { paths: { login: '#{new_user_session_path}' + }, + event: { + startTime: #{Settings.event.start_time.to_i * 1000}, + endTime: #{Settings.event.end_time.to_i * 1000} } }; From 7eadfb2a2ea53536e790e38f04ffd971c4f8dae0 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Thu, 30 May 2013 18:29:58 +0300 Subject: [PATCH 130/499] Additional fields. --- app/controllers/activities_controller.rb | 10 +++++++++- app/views/activities/new.html.haml | 4 ++-- db/migrate/20130530152254_add_image_url_to_activity.rb | 5 +++++ db/schema.rb | 3 ++- 4 files changed, 18 insertions(+), 4 deletions(-) create mode 100644 db/migrate/20130530152254_add_image_url_to_activity.rb diff --git a/app/controllers/activities_controller.rb b/app/controllers/activities_controller.rb index 280bc637..ed8f07c3 100644 --- a/app/controllers/activities_controller.rb +++ b/app/controllers/activities_controller.rb @@ -44,7 +44,15 @@ def destroy private def sanitized_params - params.require(:activity).permit(:start_time, :end_time, :name, :place, :limit_of_participants) + params.require(:activity) + .permit(:start_time, + :end_time, + :name, + :place, + :description, + :limit_of_participants, + :anytime, + :image_url) end end diff --git a/app/views/activities/new.html.haml b/app/views/activities/new.html.haml index 84c12b85..ce8b10c3 100644 --- a/app/views/activities/new.html.haml +++ b/app/views/activities/new.html.haml @@ -23,10 +23,10 @@ %label.inline = f.check_box :anytime Without a specific date/time - = content_tag :div, "" = f.text_field :place, placeholder: "Location, where to meet …" + = f.number_field :limit_of_participants, placeholder: "5" = f.text_area :requirements, placeholder: "Requirements, what to bring …" = f.text_area :description, placeholder: "Description, background information, links, etc." - = "###{@activity.description.to_s.html_safe}##" + = f.url_field :image_url, placeholder: "URL for an image" %button(type="submit") Save diff --git a/db/migrate/20130530152254_add_image_url_to_activity.rb b/db/migrate/20130530152254_add_image_url_to_activity.rb new file mode 100644 index 00000000..7fb780b9 --- /dev/null +++ b/db/migrate/20130530152254_add_image_url_to_activity.rb @@ -0,0 +1,5 @@ +class AddImageUrlToActivity < ActiveRecord::Migration + def change + add_column :activities, :image_url, :string + end +end diff --git a/db/schema.rb b/db/schema.rb index 28bd372e..cc40a4f6 100644 --- a/db/schema.rb +++ b/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: 20130530134900) do +ActiveRecord::Schema.define(version: 20130530152254) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -29,6 +29,7 @@ t.boolean "anytime", default: true, null: false t.text "requirements" t.datetime "end_time" + t.string "image_url" end add_index "activities", ["name"], name: "index_activities_on_name", unique: true, using: :btree From 3d32e3276449b375d72658cff256b77aba62971e Mon Sep 17 00:00:00 2001 From: pietia Date: Thu, 30 May 2013 19:15:02 +0200 Subject: [PATCH 131/499] more work validations, Event - small refactoring --- app/controllers/application_controller.rb | 4 +++- app/decorators/activity_decorator.rb | 14 +++++++++----- app/models/activity.rb | 21 ++++++++++++++++++++- config/initializers/event.rb | 4 +++- spec/models/activity_spec.rb | 2 ++ 5 files changed, 37 insertions(+), 8 deletions(-) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 810c009a..20c12f24 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -7,6 +7,8 @@ class ApplicationController < ActionController::Base before_filter :authenticate_user! def current_event - @current_event ||= Event.new(Settings.event.name, Settings.event.start_time, Settings.event.end_time) + @current_event ||= begin + Rails.env.production? ? EVENT : Event.new(Settings.event.name, Settings.event.start_time, Settings.event.end_time) + end end end diff --git a/app/decorators/activity_decorator.rb b/app/decorators/activity_decorator.rb index 476a8a64..ecc3bbf3 100644 --- a/app/decorators/activity_decorator.rb +++ b/app/decorators/activity_decorator.rb @@ -15,16 +15,20 @@ def relation_ship_with(user) def status today = Date.current - if object.start_time > today then "upcoming" + if object.anytime? then "" + elsif object.start_time > today then "upcoming" elsif object.start_time.to_date == today then "today" - else "archive" + else "archive" end end def time - # Case for "Anytime" needed - object.start_time.strftime("%A, %-d.%-m / %k:%M – ") + - object.end_time.strftime("%k:%M") + if object.anytime? + "" + else + object.start_time.strftime("%A, %-d.%-m / %k:%M – ") + + object.end_time.strftime("%A, %-d.%-m / %k:%M – ") + end end end diff --git a/app/models/activity.rb b/app/models/activity.rb index 516f40b7..59522c3f 100644 --- a/app/models/activity.rb +++ b/app/models/activity.rb @@ -10,13 +10,19 @@ class Activity < ActiveRecord::Base validates :start_time, presence: true, allow_blank: false, allow_nil: true validates :end_time, presence: true, allow_blank: false, allow_nil: true + validates :anytime, inclusion: {in: [true, false]} validates :name, presence: true, allow_blank: false, uniqueness: true validates :place, presence: true, allow_blank: false validates :limit_of_participants, numericality: {greater_than: 0}, allow_nil: true + validate :time_frame_order, if: ->{ !anytime && event && start_time.present? && end_time.present? } + validate :during_the_event, if: ->{ !anytime && event && start_time.present? && end_time.present? } + validates :event, presence: true + + before_validation :clear_time_frame, if: ->{ anytime } class << self def recent(limit = DEFAULT_LIMIT) - where("start_time >= :t", t: 1.month.ago).limit(limit) + where("start_time >= :t OR anytime = true", t: 1.month.ago).limit(limit) end def today @@ -53,4 +59,17 @@ def participation_source @participation_source ||= Participation.public_method(:new) end + def clear_time_frame + self.start_time, self.end_time = nil, nil + end + + def time_frame_order + errors.add(:end_time, "in wrong order") if end_time < start_time + end + + def during_the_event + errors.add(:start_time, "too early") if start_time < event.start_time + errors.add(:end_time, "too late") if end_time > event.end_time + end + end diff --git a/config/initializers/event.rb b/config/initializers/event.rb index 884e46d6..54569d16 100644 --- a/config/initializers/event.rb +++ b/config/initializers/event.rb @@ -1 +1,3 @@ -#EVENT = Event.new(Settings.event.name) \ No newline at end of file +if Rails.env.production? + ::EVENT = Event.new(Settings.event.name, Settings.event.start_time, Settings.event.end_time) +end diff --git a/spec/models/activity_spec.rb b/spec/models/activity_spec.rb index db2e17d5..eb6651a4 100644 --- a/spec/models/activity_spec.rb +++ b/spec/models/activity_spec.rb @@ -86,6 +86,8 @@ it { should accept_values_for(:description, nil, "", "Wear some solid boots!")} + it { should_not accept_values_for(:event, nil) } + it { should accept_values_for(:place, "football pitch" ) } it { should_not accept_values_for(:place, "", nil) } From f586cd6191d7515dcfdcfb1fb72739fd1390126d Mon Sep 17 00:00:00 2001 From: pietia Date: Thu, 30 May 2013 19:17:40 +0200 Subject: [PATCH 132/499] use 'public' Api --- app/views/layouts/application.html.haml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml index 36ea8f47..980e5a51 100644 --- a/app/views/layouts/application.html.haml +++ b/app/views/layouts/application.html.haml @@ -22,8 +22,9 @@ login: '#{new_user_session_path}' }, event: { - startTime: #{Settings.event.start_time.to_i * 1000}, - endTime: #{Settings.event.end_time.to_i * 1000} + name: '#{current_event.name}', + startTime: #{current_event.start_time.to_i * 1000}, + endTime: #{current_event.end_time.to_i * 1000} } }; From 50dd4737806dc606589926914ae1cee78a9d2dbe Mon Sep 17 00:00:00 2001 From: pietia Date: Thu, 30 May 2013 20:48:46 +0200 Subject: [PATCH 133/499] place renamed to location --- app/controllers/activities_controller.rb | 3 ++- app/models/activity.rb | 2 +- app/views/activities/new.html.haml | 2 +- db/migrate/20130530184628_rename_place_to_location.rb | 5 +++++ 4 files changed, 9 insertions(+), 3 deletions(-) create mode 100644 db/migrate/20130530184628_rename_place_to_location.rb diff --git a/app/controllers/activities_controller.rb b/app/controllers/activities_controller.rb index ed8f07c3..22a3c72a 100644 --- a/app/controllers/activities_controller.rb +++ b/app/controllers/activities_controller.rb @@ -48,7 +48,8 @@ def sanitized_params .permit(:start_time, :end_time, :name, - :place, + :location, + :requirements, :description, :limit_of_participants, :anytime, diff --git a/app/models/activity.rb b/app/models/activity.rb index 59522c3f..60864b0a 100644 --- a/app/models/activity.rb +++ b/app/models/activity.rb @@ -12,7 +12,7 @@ class Activity < ActiveRecord::Base validates :end_time, presence: true, allow_blank: false, allow_nil: true validates :anytime, inclusion: {in: [true, false]} validates :name, presence: true, allow_blank: false, uniqueness: true - validates :place, presence: true, allow_blank: false + validates :location, presence: true, allow_blank: false validates :limit_of_participants, numericality: {greater_than: 0}, allow_nil: true validate :time_frame_order, if: ->{ !anytime && event && start_time.present? && end_time.present? } validate :during_the_event, if: ->{ !anytime && event && start_time.present? && end_time.present? } diff --git a/app/views/activities/new.html.haml b/app/views/activities/new.html.haml index ce8b10c3..eccc909a 100644 --- a/app/views/activities/new.html.haml +++ b/app/views/activities/new.html.haml @@ -23,7 +23,7 @@ %label.inline = f.check_box :anytime Without a specific date/time - = f.text_field :place, placeholder: "Location, where to meet …" + = f.text_field :location, placeholder: "Location, where to meet …" = f.number_field :limit_of_participants, placeholder: "5" = f.text_area :requirements, placeholder: "Requirements, what to bring …" = f.text_area :description, placeholder: "Description, background information, links, etc." diff --git a/db/migrate/20130530184628_rename_place_to_location.rb b/db/migrate/20130530184628_rename_place_to_location.rb new file mode 100644 index 00000000..d76f771a --- /dev/null +++ b/db/migrate/20130530184628_rename_place_to_location.rb @@ -0,0 +1,5 @@ +class RenamePlaceToLocation < ActiveRecord::Migration + def change + rename_column :activities, :place, :location + end +end From dc7a9e1bcc3914f2d52f057f5b42ace067b02f00 Mon Sep 17 00:00:00 2001 From: pietia Date: Thu, 30 May 2013 21:30:55 +0200 Subject: [PATCH 134/499] added basic authorization --- app/controllers/activities_controller.rb | 5 ++- app/controllers/application_controller.rb | 22 ++++++++++++++ app/controllers/participations_controller.rb | 3 +- app/models/ability.rb | 32 -------------------- config/application.rb | 4 +-- db/schema.rb | 4 +-- lib/ability.rb | 8 +++++ 7 files changed, 40 insertions(+), 38 deletions(-) delete mode 100644 app/models/ability.rb create mode 100644 lib/ability.rb diff --git a/app/controllers/activities_controller.rb b/app/controllers/activities_controller.rb index 22a3c72a..a560bc24 100644 --- a/app/controllers/activities_controller.rb +++ b/app/controllers/activities_controller.rb @@ -20,6 +20,7 @@ def new def edit @activity = current_event.activity(params[:id]) + authorize!(:edit, @activity) respond_with(@activity) end @@ -31,13 +32,15 @@ def create def update @activity = current_event.activity(params[:id]) + authorize!(:update, @activity) @activity.update_attributes(sanitized_params) respond_with(@activity) end def destroy @activity = current_event.activity(params[:id]) - @activity.destroy if @activity && @activity.creator == current_user #TODO + authorize!(:destroy, @activity) + @activity.destroy respond_with(@activity) end diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 20c12f24..26c2b5b2 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -6,9 +6,31 @@ class ApplicationController < ActionController::Base before_filter :authenticate_user! + rescue_from CanCan::AccessDenied, :with => :rescue_access_denied + rescue_from ActiveRecord::RecordNotFound, :with => :rescue_record_not_found + def current_event @current_event ||= begin Rails.env.production? ? EVENT : Event.new(Settings.event.name, Settings.event.start_time, Settings.event.end_time) end end + + def not_found + if request.xhr? + render :nothing => true, :status => 404 + else + raise ActionController::RoutingError.new('Not Found') + end + end + + private + + def rescue_access_denied + render :nothing => true, :status => 401 + end + + def rescue_record_not_found + not_found + end + end diff --git a/app/controllers/participations_controller.rb b/app/controllers/participations_controller.rb index 22374e91..e5608399 100644 --- a/app/controllers/participations_controller.rb +++ b/app/controllers/participations_controller.rb @@ -9,7 +9,8 @@ def create def destroy participation = current_activity.participation(current_user) - participation.destroy if participation && participation.participant == current_user #TODO + authorize!(:destroy, participation) + participation.destroy respond_with(current_activity.reload, participation, location: activities_path) end diff --git a/app/models/ability.rb b/app/models/ability.rb deleted file mode 100644 index e03bf9be..00000000 --- a/app/models/ability.rb +++ /dev/null @@ -1,32 +0,0 @@ -class Ability - include CanCan::Ability - - def initialize(user) - # Define abilities for the passed in user here. For example: - # - # user ||= User.new # guest user (not logged in) - # if user.admin? - # can :manage, :all - # else - # can :read, :all - # end - # - # The first argument to `can` is the action you are giving the user - # permission to do. - # If you pass :manage it will apply to every action. Other common actions - # here are :read, :create, :update and :destroy. - # - # The second argument is the resource the user can perform the action on. - # If you pass :all it will apply to every resource. Otherwise pass a Ruby - # class of the resource. - # - # The third argument is an optional hash of conditions to further filter the - # objects. - # For example, here the user can only update published articles. - # - # can :update, Article, :published => true - # - # See the wiki for details: - # https://github.com/ryanb/cancan/wiki/Defining-Abilities - end -end diff --git a/config/application.rb b/config/application.rb index a3ef31a4..de854822 100644 --- a/config/application.rb +++ b/config/application.rb @@ -19,11 +19,11 @@ class Application < Rails::Application # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s] # config.i18n.default_locale = :de + config.autoload_paths += Dir["#{config.root}/lib/**/"] + # don't generate any assets config.generators.assets = false config.assets.initialize_on_precompile = false end end - -require Rails.root + "lib/settings" diff --git a/db/schema.rb b/db/schema.rb index cc40a4f6..d53c1872 100644 --- a/db/schema.rb +++ b/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: 20130530152254) do +ActiveRecord::Schema.define(version: 20130530184628) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -19,7 +19,7 @@ create_table "activities", force: true do |t| t.string "name", default: "", null: false t.text "description" - t.string "place" + t.string "location" t.datetime "start_time" t.integer "limit_of_participants" t.datetime "created_at" diff --git a/lib/ability.rb b/lib/ability.rb new file mode 100644 index 00000000..ab28f9dd --- /dev/null +++ b/lib/ability.rb @@ -0,0 +1,8 @@ +class Ability + include CanCan::Ability + + def initialize(user) + can [:edit, :update, :destroy], Activity, creator: user + can :destroy, Participation, participant: user + end +end From e3d2db0b08181c51b4d79fee21ac03aa421ceb3a Mon Sep 17 00:00:00 2001 From: pietia Date: Thu, 30 May 2013 23:16:33 +0200 Subject: [PATCH 135/499] more refactoring in controllers: filters --- app/controllers/activities_controller.rb | 15 +++++++-------- app/controllers/participations_controller.rb | 18 +++++++++++------- app/controllers/registrations_controller.rb | 2 +- 3 files changed, 19 insertions(+), 16 deletions(-) diff --git a/app/controllers/activities_controller.rb b/app/controllers/activities_controller.rb index a560bc24..2a8c86ab 100644 --- a/app/controllers/activities_controller.rb +++ b/app/controllers/activities_controller.rb @@ -1,7 +1,9 @@ class ActivitiesController < ApplicationController respond_to :html - skip_before_filter :authenticate_user!, :only => [:index, :show] + skip_before_filter :authenticate_user!, only: [:index, :show] + before_filter :load_resource, only: [:show, :edit, :update, :destroy] + authorize_resource only: [:show, :edit, :update, :destroy] def index @activities = current_event.activities @@ -9,7 +11,6 @@ def index end def show - @activity = current_event.activity(params[:id]) respond_with(@activity) end @@ -19,8 +20,6 @@ def new end def edit - @activity = current_event.activity(params[:id]) - authorize!(:edit, @activity) respond_with(@activity) end @@ -31,21 +30,21 @@ def create end def update - @activity = current_event.activity(params[:id]) - authorize!(:update, @activity) @activity.update_attributes(sanitized_params) respond_with(@activity) end def destroy - @activity = current_event.activity(params[:id]) - authorize!(:destroy, @activity) @activity.destroy respond_with(@activity) end private + def load_resource + @activity = current_event.activity(params[:id]) + end + def sanitized_params params.require(:activity) .permit(:start_time, diff --git a/app/controllers/participations_controller.rb b/app/controllers/participations_controller.rb index e5608399..918c6986 100644 --- a/app/controllers/participations_controller.rb +++ b/app/controllers/participations_controller.rb @@ -1,21 +1,25 @@ class ParticipationsController < ApplicationController respond_to :js, :html + before_filter :load_resource + authorize_resource only: :destroy + def create - participation = current_activity.new_participation(current_user) - participation.save - respond_with(current_activity.reload, participation, location: activities_path) + @participation.save + respond_with(current_activity.reload, @participation, location: activities_path) end def destroy - participation = current_activity.participation(current_user) - authorize!(:destroy, participation) - participation.destroy - respond_with(current_activity.reload, participation, location: activities_path) + @participation.destroy + respond_with(current_activity.reload, @participation, location: activities_path) end private + def load_resource + @participation = current_activity.new_participation(current_user) + end + def current_activity @current_activity ||= current_event.activity(params[:activity_id]).decorate end diff --git a/app/controllers/registrations_controller.rb b/app/controllers/registrations_controller.rb index acdce4e0..cd79e493 100644 --- a/app/controllers/registrations_controller.rb +++ b/app/controllers/registrations_controller.rb @@ -1,5 +1,5 @@ class RegistrationsController < Devise::RegistrationsController - skip_before_filter :authenticate_user!, only: create + skip_before_filter :authenticate_user!, only: :create def create super From ca3f85372424d54c9c8e4cdcaa9bd1c81ad43bd3 Mon Sep 17 00:00:00 2001 From: pietia Date: Thu, 30 May 2013 23:20:14 +0200 Subject: [PATCH 136/499] location not place (seed) --- db/seeds.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/db/seeds.rb b/db/seeds.rb index 12313261..552896a0 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -20,7 +20,7 @@ name: "Party!", start_time: 1.day.from_now.to_time, end_time: 1.day.from_now.to_time + 4.hours, - place: "Pool", + location: "Pool", limit_of_participants: 2 ) activity.save! From cfdaab7c684797ea4e8d24405c02cf2edb9cd194 Mon Sep 17 00:00:00 2001 From: pietia Date: Fri, 31 May 2013 01:16:27 +0200 Subject: [PATCH 137/499] don't fail with nils --- app/models/activity.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/models/activity.rb b/app/models/activity.rb index 60864b0a..243b6b00 100644 --- a/app/models/activity.rb +++ b/app/models/activity.rb @@ -30,11 +30,11 @@ def today end def created_by(user) - where(creator: user) + where(creator_id: user) end def participated_by(user) - includes(:participations).where(participations: { participant: user }) + includes(:participations).where(participations: { user_id: user }) end end From 0026e056df8feb77060fd56e019c54521f94c224 Mon Sep 17 00:00:00 2001 From: pietia Date: Fri, 31 May 2013 01:16:37 +0200 Subject: [PATCH 138/499] fixed filters.. --- app/controllers/participations_controller.rb | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/app/controllers/participations_controller.rb b/app/controllers/participations_controller.rb index 918c6986..fa741ad6 100644 --- a/app/controllers/participations_controller.rb +++ b/app/controllers/participations_controller.rb @@ -1,25 +1,21 @@ class ParticipationsController < ApplicationController respond_to :js, :html - before_filter :load_resource - authorize_resource only: :destroy - def create + @participation = current_activity.new_participation(current_user) @participation.save respond_with(current_activity.reload, @participation, location: activities_path) end def destroy + @participation = current_activity.participation(current_user) + authorize!(:destroy, @participation) @participation.destroy respond_with(current_activity.reload, @participation, location: activities_path) end private - def load_resource - @participation = current_activity.new_participation(current_user) - end - def current_activity @current_activity ||= current_event.activity(params[:activity_id]).decorate end From bfaa5c9b8698c470afe9b0bb014ab873c0cfddf4 Mon Sep 17 00:00:00 2001 From: pietia Date: Fri, 31 May 2013 01:22:01 +0200 Subject: [PATCH 139/499] use public api --- app/views/activities/new.html.haml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/activities/new.html.haml b/app/views/activities/new.html.haml index eccc909a..872136f6 100644 --- a/app/views/activities/new.html.haml +++ b/app/views/activities/new.html.haml @@ -11,12 +11,12 @@ %hr .select = f.datetime_select :start_time - %input.date-capture(data-target="start_time" type="text" placeholder="#{ Settings.event.start_time.strftime("%a, %e.%-m.") }") + %input.date-capture(data-target="start_time" type="text" placeholder="#{ current_event.start_time.strftime("%a, %e.%-m.") }") %input.time-capture(data-target="start_time" type="text" placeholder="15:00") %span – .select = f.datetime_select :end_time - %input.date-capture(data-target="end_time" type="text" placeholder="#{ Settings.event.start_time.strftime("%a, %e.%-m.") }") + %input.date-capture(data-target="end_time" type="text" placeholder="#{ current_event.end_time.strftime("%a, %e.%-m.") }") %input.time-capture(data-target="end_time" type="text" placeholder="16:30") %span.between or From 9698f82be235a781eff4f8a9b57a2e33c052a385 Mon Sep 17 00:00:00 2001 From: pietia Date: Fri, 31 May 2013 01:26:16 +0200 Subject: [PATCH 140/499] typo --- app/assets/javascripts/initializers.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/javascripts/initializers.coffee b/app/assets/javascripts/initializers.coffee index a71fa9fd..1697a816 100644 --- a/app/assets/javascripts/initializers.coffee +++ b/app/assets/javascripts/initializers.coffee @@ -1,6 +1,6 @@ $ -> $(document).ajaxError (e, xhr) -> - console.error "AJAX ERRPR!", arguments + console.error "AJAX ERROR!", arguments window.location.replace(App.paths.login) if xhr.status == 401 # show how "full" an activity is From 9fe8c94c0630445b474fb536c44d02e705dd4297 Mon Sep 17 00:00:00 2001 From: pietia Date: Fri, 31 May 2013 02:21:44 +0200 Subject: [PATCH 141/499] fixed first tab --- app/decorators/activity_decorator.rb | 9 ++++----- app/models/activity.rb | 11 ++++++++++- app/models/event.rb | 4 +++- 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/app/decorators/activity_decorator.rb b/app/decorators/activity_decorator.rb index ecc3bbf3..d4bfe70b 100644 --- a/app/decorators/activity_decorator.rb +++ b/app/decorators/activity_decorator.rb @@ -14,11 +14,10 @@ def relation_ship_with(user) end def status - today = Date.current - if object.anytime? then "" - elsif object.start_time > today then "upcoming" - elsif object.start_time.to_date == today then "today" - else "archive" + if object.anytime? then "" + elsif object.today? then "today" + elsif object.upcoming? then "upcoming" + else "archive" end end diff --git a/app/models/activity.rb b/app/models/activity.rb index 243b6b00..883138b3 100644 --- a/app/models/activity.rb +++ b/app/models/activity.rb @@ -26,7 +26,7 @@ def recent(limit = DEFAULT_LIMIT) end def today - where("created_at >= :t", t: Time.zone.now.beginning_of_day) + where(":t between start_time and end_time", t: Date.current) end def created_by(user) @@ -42,6 +42,15 @@ def full_by limit_of_participants.nil? ? 0 : [100.0 * participations_count / limit_of_participants.to_f, 100.0].min end + def today? + t = Time.now.end_of_day + t > start_time && t < end_time + end + + def upcoming? + start_time > Time.now.end_of_day + end + def new_participation(user) participation_source.call.tap do |participation| participation.activity = self diff --git a/app/models/event.rb b/app/models/event.rb index 92490312..62ce104f 100644 --- a/app/models/event.rb +++ b/app/models/event.rb @@ -15,7 +15,9 @@ def new_activity(author, *args) end def activity(activity_id) - activities.where(:id => activity_id).first + activities.where(:id => activity_id).first.tap do |activity| + activity.event = self + end end def activities From e94a72bd82c9647d519c6174d30248ee02a13735 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Fri, 31 May 2013 09:34:02 +0300 Subject: [PATCH 142/499] Clean-up. Adding styles for form. --- app/assets/stylesheets/_base.sass | 2 + app/assets/stylesheets/_settings.sass | 2 +- .../stylesheets/partials/_activities.sass | 223 +----------------- .../partials/activities/_filters.sass | 3 - .../partials/activities/_index.sass | 212 +++++++++++++++++ .../stylesheets/partials/activities/_new.sass | 6 +- app/assets/stylesheets/utils/_forms.sass | 31 +++ app/views/activities/new.html.haml | 59 +++-- app/views/layouts/application.html.haml | 2 +- 9 files changed, 298 insertions(+), 242 deletions(-) create mode 100644 app/assets/stylesheets/partials/activities/_index.sass diff --git a/app/assets/stylesheets/_base.sass b/app/assets/stylesheets/_base.sass index f8b59f60..737e6f31 100644 --- a/app/assets/stylesheets/_base.sass +++ b/app/assets/stylesheets/_base.sass @@ -14,6 +14,8 @@ a:hover border: none +transition(all 0.25s) +#main + position: relative ::-webkit-input-placeholder color: $light-gray diff --git a/app/assets/stylesheets/_settings.sass b/app/assets/stylesheets/_settings.sass index a9ba3d54..f4c00e52 100644 --- a/app/assets/stylesheets/_settings.sass +++ b/app/assets/stylesheets/_settings.sass @@ -33,6 +33,6 @@ $extra-dark-gray: #2c2c2c $darker-gray: #797979 $medium-gray: #8a8a8a $light-gray: #e2e2e2 -$extra-light-gray: #f7f7f7 +$extra-light-gray: #f1f1f1 $white: #ffffff diff --git a/app/assets/stylesheets/partials/_activities.sass b/app/assets/stylesheets/partials/_activities.sass index e6c1afc1..43e1f24b 100644 --- a/app/assets/stylesheets/partials/_activities.sass +++ b/app/assets/stylesheets/partials/_activities.sass @@ -1,216 +1,7 @@ -@import "activities/filters" - -a#new-activity - float: right - @extend %icon-button-add - -#activities - clear: both - @extend %clearfix - +unlisted - padding: 1em 0 0 0 - - li - display: block - +span-columns(2) - margin: 0 0 1em 0 - text-align: center - - span.icon - position: absolute - top: 15px - right: 15px - opacity: 1 - +transition(opacity 0.25s) - - a - border: none - - &.details - display: block - position: absolute - z-index: 100 - width: 100% - height: 100% - top: 0 - left: 0 - - span - display: inline-block - background: $medium-gray - color: $white - text-shadow: 0 1px 0 $gray - @extend %small-text - padding: 0.25em 1.5em - text-transform: uppercase - border-radius: 3px - margin: 19em 0 0 - opacity: 0 - +transition(all 0.35s ease-in-out) - +custom-sans(medium) - - .container - background: $white - height: 25em - padding: 2.5em 1em 1em 1em - margin: 0 1px - position: relative - overflow: hidden - +transition(box-shadow 0.25s ease-in-out) - - .labels - margin: 1.5em 0 0 0 - color: $extra-dark-gray - - h4 - +custom-sans(light) - font-size: 1.375em - margin: 0 - padding: 0 0.727272em - - &:before, - &:after - content: "." - text-indent: -999em - display: block - margin: 0.2em auto - width: 20px - height: 1px - border-bottom: 1px solid $light-gray - &:before - width: 0 - margin: 0 auto - - p - &.creator - text-transform: uppercase - +custom-sans(medium) - @extend %smaller-text - margin: 0.5em 0 - - &.time - position: absolute - right: 1em - bottom: 0 - left: 1em - +custom-serif(bold) - border-top: 1px solid $light-gray - padding: 0.75em 0 0 - - .wrapper, - .labels h4 - +transition(all 0.15s ease-out) - - .labels p - opacity: 1 - +transition(opacity 0.1s) - - // the big button appearing on hover - .action - opacity: 0 - position: absolute - left: 0 - right: 0 - top: 5em - z-index: 1001 - +transform(scale(0.9)) - +transition(all 0.35s ease-in-out 0) - - a, button - border: none - background: none - +custom-sans(bold) - @extend %smaller-text - text-transform: uppercase - color: $green - display: inline-block - - span - display: block - width: 96px - height: 96px - margin: 0 auto 0.75em - background: transparent image-url("shared/buttons_big.png") no-repeat 0 0 - - &:hover - span - background-position: -96px 0 - - &:hover - cursor: pointer - - .container - z-index: 9001 - +transition(box-shadow 0.25s ease-in-out) - box-shadow: 0 0 20px transparentize($black, 0.9) - - span.icon - opacity: 0 - +transition(opacity 0.25s) - - a.details - span - opacity: 1 - +transition(all 0.25s ease-in-out) - - .wrapper - +transition(all 0.15s ease-out) - +transform(translate(0, -9em)) - opacity: 0 - - .labels - h4 - +transition(all 0.15s ease-out) - +transform(translate(0, 2.5em)) - - &:before - width: 50% - margin: 0 auto 0.5em - &:after - width: 0 - - p - +transition(opacity 0.1s) - opacity: 0 - - .action - opacity: 1 - +transform(scale(1)) - +transition(all 0.15s ease-out 0.15s) - - // variations - $types: participant $red ok, owner $yellow edit - $xpos : 0 - - @each $type in $types - $xpos: $xpos + 1 - - &.#{nth($type, 1)} - span.icon - @extend %icon-#{nth($type, 3)} - - .action - a, button - color: nth($type, 2) - - span - background-position: 0 -96px * $xpos - - &:hover - span - background-position: -96px -96px * $xpos - - img.progress - width: 150px - height: 150px - border-radius: 75px - margin: 6px - - .wrapper - width: 162px - margin: 0 auto - - canvas - z-index: 99 !important - - +body.activities + &.index + @import "activities/filters" + @import "activities/index" + + &.new + @import "activities/new" \ No newline at end of file diff --git a/app/assets/stylesheets/partials/activities/_filters.sass b/app/assets/stylesheets/partials/activities/_filters.sass index 1b9f74bb..f2891e93 100644 --- a/app/assets/stylesheets/partials/activities/_filters.sass +++ b/app/assets/stylesheets/partials/activities/_filters.sass @@ -1,6 +1,3 @@ -#main - position: relative - form.filters @extend %clearfix border-radius: 3px diff --git a/app/assets/stylesheets/partials/activities/_index.sass b/app/assets/stylesheets/partials/activities/_index.sass new file mode 100644 index 00000000..51c5075d --- /dev/null +++ b/app/assets/stylesheets/partials/activities/_index.sass @@ -0,0 +1,212 @@ +a#new-activity + float: right + @extend %icon-button-add + +#activities + clear: both + @extend %clearfix + +unlisted + padding: 1em 0 0 0 + + li + display: block + +span-columns(2) + margin: 0 0 1em 0 + text-align: center + + span.icon + position: absolute + top: 15px + right: 15px + opacity: 1 + +transition(opacity 0.25s) + + a + border: none + + &.details + display: block + position: absolute + z-index: 100 + width: 100% + height: 100% + top: 0 + left: 0 + + span + display: inline-block + background: $medium-gray + color: $white + text-shadow: 0 1px 0 $gray + @extend %small-text + padding: 0.25em 1.5em + text-transform: uppercase + border-radius: 3px + margin: 19em 0 0 + opacity: 0 + +transition(all 0.35s ease-in-out) + +custom-sans(medium) + + .container + background: $white + height: 25em + padding: 2.5em 1em 1em 1em + margin: 0 1px + position: relative + overflow: hidden + +transition(box-shadow 0.25s ease-in-out) + + .labels + margin: 1.5em 0 0 0 + color: $extra-dark-gray + + h4 + +custom-sans(light) + font-size: 1.375em + margin: 0 + padding: 0 0.727272em + + &:before, + &:after + content: "." + text-indent: -999em + display: block + margin: 0.2em auto + width: 20px + height: 1px + border-bottom: 1px solid $light-gray + &:before + width: 0 + margin: 0 auto + + p + &.creator + text-transform: uppercase + +custom-sans(medium) + @extend %smaller-text + margin: 0.5em 0 + + &.time + position: absolute + right: 1em + bottom: 0 + left: 1em + +custom-serif(bold) + border-top: 1px solid $light-gray + padding: 0.75em 0 0 + + .wrapper, + .labels h4 + +transition(all 0.15s ease-out) + + .labels p + opacity: 1 + +transition(opacity 0.1s) + + // the big button appearing on hover + .action + opacity: 0 + position: absolute + left: 0 + right: 0 + top: 5em + z-index: 1001 + +transform(scale(0.9)) + +transition(all 0.35s ease-in-out 0) + + a, button + border: none + background: none + +custom-sans(bold) + @extend %smaller-text + text-transform: uppercase + color: $green + display: inline-block + + span + display: block + width: 96px + height: 96px + margin: 0 auto 0.75em + background: transparent image-url("shared/buttons_big.png") no-repeat 0 0 + + &:hover + span + background-position: -96px 0 + + &:hover + cursor: pointer + + .container + z-index: 9001 + +transition(box-shadow 0.25s ease-in-out) + box-shadow: 0 0 20px transparentize($black, 0.9) + + span.icon + opacity: 0 + +transition(opacity 0.25s) + + a.details + span + opacity: 1 + +transition(all 0.25s ease-in-out) + + .wrapper + +transition(all 0.15s ease-out) + +transform(translate(0, -9em)) + opacity: 0 + + .labels + h4 + +transition(all 0.15s ease-out) + +transform(translate(0, 2.5em)) + + &:before + width: 50% + margin: 0 auto 0.5em + &:after + width: 0 + + p + +transition(opacity 0.1s) + opacity: 0 + + .action + opacity: 1 + +transform(scale(1)) + +transition(all 0.15s ease-out 0.15s) + + // variations + $types: participant $red ok, owner $yellow edit + $xpos : 0 + + @each $type in $types + $xpos: $xpos + 1 + + &.#{nth($type, 1)} + span.icon + @extend %icon-#{nth($type, 3)} + + .action + a, button + color: nth($type, 2) + + span + background-position: 0 -96px * $xpos + + &:hover + span + background-position: -96px -96px * $xpos + + img.progress + width: 150px + height: 150px + border-radius: 75px + margin: 6px + + .wrapper + width: 162px + margin: 0 auto + + canvas + z-index: 99 !important \ No newline at end of file diff --git a/app/assets/stylesheets/partials/activities/_new.sass b/app/assets/stylesheets/partials/activities/_new.sass index 7e3fc6d8..6d374799 100644 --- a/app/assets/stylesheets/partials/activities/_new.sass +++ b/app/assets/stylesheets/partials/activities/_new.sass @@ -11,4 +11,8 @@ form#new-activity input.time-capture display: inline - + fieldset + &.start-time, + &.end-time, + &.anytime + display: inline-block diff --git a/app/assets/stylesheets/utils/_forms.sass b/app/assets/stylesheets/utils/_forms.sass index 29ae7744..067db451 100644 --- a/app/assets/stylesheets/utils/_forms.sass +++ b/app/assets/stylesheets/utils/_forms.sass @@ -1,4 +1,5 @@ form + input, textarea width: 100% padding: 0.75em 15px 1em @@ -8,6 +9,13 @@ form outline: none +custom-sans(light) + +placeholder + color: darken($light-gray, 10) + + &.title + font-size: 1.35em + +custom-sans(medium) + &.date-capture width: 10em @@ -18,12 +26,35 @@ form &:focus box-shadow: 0 0 7px transparentize($black, 0.9) + fieldset + border: none + padding: 0 + + fieldset, + label + display: block + margin: 0 0 1.25em + + hr + background-color: $light-gray + border-width: 0 + color: $light-gray + height: 1px + line-height: 1em + margin: 1.25em auto + text-align: center + width: 50% + span.between display: inline-block margin: 0 2em +custom-sans(bold) text-transform: uppercase + span.divider + display: inline-block + margin: 0 1em + label.inline display: inline diff --git a/app/views/activities/new.html.haml b/app/views/activities/new.html.haml index 872136f6..3eed27bd 100644 --- a/app/views/activities/new.html.haml +++ b/app/views/activities/new.html.haml @@ -7,26 +7,45 @@ - @activity.errors.full_messages.each do |error| %li= error - = f.text_field :name, placeholder: "Give your activity a name", class: "title" + %label.name + = f.text_field :name, placeholder: "Give your activity a name", class: "title" + %hr - .select - = f.datetime_select :start_time - %input.date-capture(data-target="start_time" type="text" placeholder="#{ current_event.start_time.strftime("%a, %e.%-m.") }") - %input.time-capture(data-target="start_time" type="text" placeholder="15:00") - %span – - .select - = f.datetime_select :end_time - %input.date-capture(data-target="end_time" type="text" placeholder="#{ current_event.end_time.strftime("%a, %e.%-m.") }") - %input.time-capture(data-target="end_time" type="text" placeholder="16:30") - - %span.between or - %label.inline - = f.check_box :anytime - Without a specific date/time - = f.text_field :location, placeholder: "Location, where to meet …" - = f.number_field :limit_of_participants, placeholder: "5" - = f.text_area :requirements, placeholder: "Requirements, what to bring …" - = f.text_area :description, placeholder: "Description, background information, links, etc." - = f.url_field :image_url, placeholder: "URL for an image" + + %fieldset.start-time + .select + = f.datetime_select :start_time + %input.date-capture(data-target="start_time" type="text" placeholder="#{ current_event.start_time.strftime("%a, %e.%-m.") }") + %input.time-capture(data-target="start_time" type="text" placeholder="15:00") + + %span.divider + – + + %fieldset.end-time + .select + = f.datetime_select :end_time + %input.date-capture(data-target="end_time" type="text" placeholder="#{ current_event.end_time.strftime("%a, %e.%-m.") }") + %input.time-capture(data-target="end_time" type="text" placeholder="16:30") + + %fieldset.anytime + %span.between or + %label.inline + = f.check_box :anytime + Without a specific date/time + + %label.location + = f.text_field :location, placeholder: "Location, where to meet …" + + %label.limit-of-participants + = f.number_field :limit_of_participants, placeholder: "5" + + %label.requirements + = f.text_area :requirements, placeholder: "Requirements, what to bring …" + + %label.description + = f.text_area :description, placeholder: "Description, background information, links, etc." + + %label.image-url + = f.url_field :image_url, placeholder: "URL for an image" %button(type="submit") Save diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml index 980e5a51..7a12f84e 100644 --- a/app/views/layouts/application.html.haml +++ b/app/views/layouts/application.html.haml @@ -28,7 +28,7 @@ } }; - %body(class=body_class) + %body(class="#{ body_class(controller.action_name) }") = render "partials/chrome_frame" #container = render "partials/header" From 10c4c0c80f845a477486ad96cef90b85a66cbaf2 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Fri, 31 May 2013 09:53:27 +0300 Subject: [PATCH 143/499] Fixing date formates. --- app/assets/stylesheets/partials/_activities.sass | 4 ++-- app/decorators/activity_decorator.rb | 15 ++++++++++++--- app/views/activities/new.html.haml | 4 ++-- config/locales/en.yml | 3 +++ 4 files changed, 19 insertions(+), 7 deletions(-) diff --git a/app/assets/stylesheets/partials/_activities.sass b/app/assets/stylesheets/partials/_activities.sass index 43e1f24b..76a26d19 100644 --- a/app/assets/stylesheets/partials/_activities.sass +++ b/app/assets/stylesheets/partials/_activities.sass @@ -2,6 +2,6 @@ body.activities &.index @import "activities/filters" @import "activities/index" - + &.new - @import "activities/new" \ No newline at end of file + @import "activities/new" diff --git a/app/decorators/activity_decorator.rb b/app/decorators/activity_decorator.rb index d4bfe70b..d237ca37 100644 --- a/app/decorators/activity_decorator.rb +++ b/app/decorators/activity_decorator.rb @@ -23,10 +23,19 @@ def status def time if object.anytime? - "" + "Anytime" else - object.start_time.strftime("%A, %-d.%-m / %k:%M – ") + - object.end_time.strftime("%A, %-d.%-m / %k:%M – ") + alpha, omega, out = object.start_time, object.end_time, "" + + out << alpha.strftime(I18n.t("activities.date_format")) + out << " / " + out << alpha.strftime(I18n.t("activities.time_format")) + out << " – " + unless alpha.to_date == omega.to_date + out << omega.strftime(I18n.t("activities.date_format")) + out << " / " + end + out << omega.strftime(I18n.t("activities.time_format")) end end diff --git a/app/views/activities/new.html.haml b/app/views/activities/new.html.haml index 3eed27bd..f9d8fcc8 100644 --- a/app/views/activities/new.html.haml +++ b/app/views/activities/new.html.haml @@ -15,7 +15,7 @@ %fieldset.start-time .select = f.datetime_select :start_time - %input.date-capture(data-target="start_time" type="text" placeholder="#{ current_event.start_time.strftime("%a, %e.%-m.") }") + %input.date-capture(data-target="start_time" type="text" placeholder="#{ current_event.start_time.strftime(t("activities.date_format")) }") %input.time-capture(data-target="start_time" type="text" placeholder="15:00") %span.divider @@ -24,7 +24,7 @@ %fieldset.end-time .select = f.datetime_select :end_time - %input.date-capture(data-target="end_time" type="text" placeholder="#{ current_event.end_time.strftime("%a, %e.%-m.") }") + %input.date-capture(data-target="end_time" type="text" placeholder="#{ current_event.end_time.strftime(t("activities.date_format")) }") %input.time-capture(data-target="end_time" type="text" placeholder="16:30") %fieldset.anytime diff --git a/config/locales/en.yml b/config/locales/en.yml index 4d1083cf..ba06a3c1 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -38,6 +38,9 @@ en: more_info: label: More info title: Learn more about this activity + anytime: Anytime + date_format: "%a, %-d.%-m" + time_format: "%k:%M" footer_nav: about: From b9d98fa3d8ccbf0bd0ca1b60e0b6c7c7fc5e77d2 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Fri, 31 May 2013 09:56:41 +0300 Subject: [PATCH 144/499] Enabling edit form. --- app/views/activities/_form.html.haml | 48 +++++++++++++++++++++++++++ app/views/activities/edit.html.haml | 4 +++ app/views/activities/new.html.haml | 49 +--------------------------- 3 files changed, 53 insertions(+), 48 deletions(-) create mode 100644 app/views/activities/_form.html.haml create mode 100644 app/views/activities/edit.html.haml diff --git a/app/views/activities/_form.html.haml b/app/views/activities/_form.html.haml new file mode 100644 index 00000000..700ecc1d --- /dev/null +++ b/app/views/activities/_form.html.haml @@ -0,0 +1,48 @@ += form_for @activity, html: { id: "new-activity" } do |f| + - if @activity.errors.any? + %ul.errors + - @activity.errors.full_messages.each do |error| + %li= error + + %label.name + = f.text_field :name, placeholder: "Give your activity a name", class: "title" + + %hr + + %fieldset.start-time + .select + = f.datetime_select :start_time + %input.date-capture(data-target="start_time" type="text" placeholder="#{ current_event.start_time.strftime(t("activities.date_format")) }") + %input.time-capture(data-target="start_time" type="text" placeholder="15:00") + + %span.divider + – + + %fieldset.end-time + .select + = f.datetime_select :end_time + %input.date-capture(data-target="end_time" type="text" placeholder="#{ current_event.end_time.strftime(t("activities.date_format")) }") + %input.time-capture(data-target="end_time" type="text" placeholder="16:30") + + %fieldset.anytime + %span.between or + %label.inline + = f.check_box :anytime + Without a specific date/time + + %label.location + = f.text_field :location, placeholder: "Location, where to meet …" + + %label.limit-of-participants + = f.number_field :limit_of_participants, placeholder: "5" + + %label.requirements + = f.text_area :requirements, placeholder: "Requirements, what to bring …" + + %label.description + = f.text_area :description, placeholder: "Description, background information, links, etc." + + %label.image-url + = f.url_field :image_url, placeholder: "URL for an image" + + %button(type="submit") Save diff --git a/app/views/activities/edit.html.haml b/app/views/activities/edit.html.haml new file mode 100644 index 00000000..824ab409 --- /dev/null +++ b/app/views/activities/edit.html.haml @@ -0,0 +1,4 @@ +%h2 + Edit your activity + += render "form" \ No newline at end of file diff --git a/app/views/activities/new.html.haml b/app/views/activities/new.html.haml index f9d8fcc8..6a2a797e 100644 --- a/app/views/activities/new.html.haml +++ b/app/views/activities/new.html.haml @@ -1,51 +1,4 @@ %h2 Organize an activity -= form_for @activity, html: { id: "new-activity" } do |f| - - if @activity.errors.any? - %ul.errors - - @activity.errors.full_messages.each do |error| - %li= error - - %label.name - = f.text_field :name, placeholder: "Give your activity a name", class: "title" - - %hr - - %fieldset.start-time - .select - = f.datetime_select :start_time - %input.date-capture(data-target="start_time" type="text" placeholder="#{ current_event.start_time.strftime(t("activities.date_format")) }") - %input.time-capture(data-target="start_time" type="text" placeholder="15:00") - - %span.divider - – - - %fieldset.end-time - .select - = f.datetime_select :end_time - %input.date-capture(data-target="end_time" type="text" placeholder="#{ current_event.end_time.strftime(t("activities.date_format")) }") - %input.time-capture(data-target="end_time" type="text" placeholder="16:30") - - %fieldset.anytime - %span.between or - %label.inline - = f.check_box :anytime - Without a specific date/time - - %label.location - = f.text_field :location, placeholder: "Location, where to meet …" - - %label.limit-of-participants - = f.number_field :limit_of_participants, placeholder: "5" - - %label.requirements - = f.text_area :requirements, placeholder: "Requirements, what to bring …" - - %label.description - = f.text_area :description, placeholder: "Description, background information, links, etc." - - %label.image-url - = f.url_field :image_url, placeholder: "URL for an image" - - %button(type="submit") Save += render "form" \ No newline at end of file From 1905632e52d0c3d04cab24d76eb1354f72d4b87f Mon Sep 17 00:00:00 2001 From: Polarblau Date: Fri, 31 May 2013 11:12:56 +0300 Subject: [PATCH 145/499] More form styles. --- app/assets/stylesheets/_base.sass | 13 ++-- .../stylesheets/partials/_activities.sass | 5 +- .../partials/activities/_index.sass | 4 +- .../stylesheets/partials/activities/_new.sass | 63 ++++++++++++++++++- app/assets/stylesheets/utils/_forms.sass | 24 +++---- app/views/activities/_form.html.haml | 29 +++++---- 6 files changed, 101 insertions(+), 37 deletions(-) diff --git a/app/assets/stylesheets/_base.sass b/app/assets/stylesheets/_base.sass index 737e6f31..ed47be36 100644 --- a/app/assets/stylesheets/_base.sass +++ b/app/assets/stylesheets/_base.sass @@ -17,11 +17,8 @@ a:hover #main position: relative -::-webkit-input-placeholder - color: $light-gray -\:-moz-placeholder - color: $light-gray -::-moz-placeholder - color: $light-gray -\:-ms-input-placeholder - color: $light-gray + h2 + text-align: center + +custom-sans(light) + font-size: 2em + margin: 0 0 2em 0 diff --git a/app/assets/stylesheets/partials/_activities.sass b/app/assets/stylesheets/partials/_activities.sass index 76a26d19..46fa867b 100644 --- a/app/assets/stylesheets/partials/_activities.sass +++ b/app/assets/stylesheets/partials/_activities.sass @@ -3,5 +3,8 @@ body.activities @import "activities/filters" @import "activities/index" - &.new + &.new, &.edit @import "activities/new" + + #main + margin: 0 150px diff --git a/app/assets/stylesheets/partials/activities/_index.sass b/app/assets/stylesheets/partials/activities/_index.sass index 51c5075d..7ee89fc1 100644 --- a/app/assets/stylesheets/partials/activities/_index.sass +++ b/app/assets/stylesheets/partials/activities/_index.sass @@ -1,6 +1,6 @@ a#new-activity float: right - @extend %icon-button-add + @extend %icon-button-ok #activities clear: both @@ -209,4 +209,4 @@ a#new-activity margin: 0 auto canvas - z-index: 99 !important \ No newline at end of file + z-index: 99 !important diff --git a/app/assets/stylesheets/partials/activities/_new.sass b/app/assets/stylesheets/partials/activities/_new.sass index 6d374799..1313546e 100644 --- a/app/assets/stylesheets/partials/activities/_new.sass +++ b/app/assets/stylesheets/partials/activities/_new.sass @@ -11,8 +11,69 @@ form#new-activity input.time-capture display: inline + span + &.divider + width: 10% + text-align: center + fieldset &.start-time, - &.end-time, + &.end-time + display: inline-block + width: 45% + position: relative + + &.start-time + //margin-right: 1% + + &.end-time + float: right + &.anytime + span + +custom-sans(bold) + text-transform: uppercase + margin: 0 1em 0 15px + + input + &.date-capture + width: 69% + + &.time-capture + width: 4em + text-align: center + width: 30% + + label + &.location + display: inline-block + width: 80% + + &.limit-of-participants display: inline-block + text-align: right + width: 20% + + input + width: 85% + + button + border: none + background: none + +custom-sans(bold) + @extend %smaller-text + text-transform: uppercase + color: $green + display: block + margin: 1em auto 4em + + span + display: block + width: 96px + height: 96px + margin: 0 auto 0.75em + background: transparent image-url("shared/buttons_big.png") no-repeat 0 0 + + &:hover + span + background-position: -96px 0 diff --git a/app/assets/stylesheets/utils/_forms.sass b/app/assets/stylesheets/utils/_forms.sass index 067db451..dcc903e6 100644 --- a/app/assets/stylesheets/utils/_forms.sass +++ b/app/assets/stylesheets/utils/_forms.sass @@ -8,6 +8,8 @@ form border: none outline: none +custom-sans(light) + z-index: 1 + position: relative +placeholder color: darken($light-gray, 10) @@ -16,14 +18,8 @@ form font-size: 1.35em +custom-sans(medium) - &.date-capture - width: 10em - - &.time-capture - width: 4em - text-align: center - &:focus + z-index: 1000 box-shadow: 0 0 7px transparentize($black, 0.9) fieldset @@ -45,15 +41,8 @@ form text-align: center width: 50% - span.between - display: inline-block - margin: 0 2em - +custom-sans(bold) - text-transform: uppercase - span.divider display: inline-block - margin: 0 1em label.inline display: inline @@ -63,3 +52,10 @@ form width: auto margin: 0 0.25em 0 0 vertical-align: baseline + + small + font-size: 0.85em + text-align: right + display: block + color: $medium-gray + padding: 0.5em 15px diff --git a/app/views/activities/_form.html.haml b/app/views/activities/_form.html.haml index 700ecc1d..534d3add 100644 --- a/app/views/activities/_form.html.haml +++ b/app/views/activities/_form.html.haml @@ -12,17 +12,17 @@ %fieldset.start-time .select = f.datetime_select :start_time - %input.date-capture(data-target="start_time" type="text" placeholder="#{ current_event.start_time.strftime(t("activities.date_format")) }") - %input.time-capture(data-target="start_time" type="text" placeholder="15:00") + %input.date-capture(data-target="start_time" type="text" placeholder="#{ current_event.start_time.strftime(t("activities.date_format")) }")> + %input.time-capture(data-target="start_time" type="text" placeholder="15:00")> - %span.divider + %span.divider> – %fieldset.end-time .select = f.datetime_select :end_time - %input.date-capture(data-target="end_time" type="text" placeholder="#{ current_event.end_time.strftime(t("activities.date_format")) }") - %input.time-capture(data-target="end_time" type="text" placeholder="16:30") + %input.date-capture(data-target="end_time" type="text" placeholder="#{ current_event.end_time.strftime(t("activities.date_format")) }")> + %input.time-capture(data-target="end_time" type="text" placeholder="16:30")> %fieldset.anytime %span.between or @@ -30,19 +30,26 @@ = f.check_box :anytime Without a specific date/time - %label.location - = f.text_field :location, placeholder: "Location, where to meet …" + %label.location> + = f.text_field :location, placeholder: "Location" + %small Where does your activity take place, where to meet, … - %label.limit-of-participants + %label.limit-of-participants> = f.number_field :limit_of_participants, placeholder: "5" + %small Max. participants %label.requirements - = f.text_area :requirements, placeholder: "Requirements, what to bring …" + = f.text_area :requirements, placeholder: "Requirements" + %small What to bring, to wear etc. %label.description - = f.text_area :description, placeholder: "Description, background information, links, etc." + = f.text_area :description, placeholder: "Description" + %small Background information, links, etc. %label.image-url = f.url_field :image_url, placeholder: "URL for an image" + %small Upload an image to e.g. imgur.com and link it here - %button(type="submit") Save + %button(type="submit")> + %span> + Save From 647f972ad5f972242d465eab3d0456610aadb00c Mon Sep 17 00:00:00 2001 From: Polarblau Date: Fri, 31 May 2013 11:18:32 +0300 Subject: [PATCH 146/499] Redirect. --- app/controllers/activities_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/activities_controller.rb b/app/controllers/activities_controller.rb index 2a8c86ab..994032c4 100644 --- a/app/controllers/activities_controller.rb +++ b/app/controllers/activities_controller.rb @@ -31,7 +31,7 @@ def create def update @activity.update_attributes(sanitized_params) - respond_with(@activity) + respond_with(@activity, location: edit_activity_path(@activity)) end def destroy From d6c09bba52c412bcbba42c1146169eeefa7dda36 Mon Sep 17 00:00:00 2001 From: pietia Date: Fri, 31 May 2013 15:23:02 +0200 Subject: [PATCH 147/499] place -> location in specs --- spec/controllers/activities_controller_spec.rb | 4 ++-- spec/factories/activities.rb | 2 +- spec/models/activity_spec.rb | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/spec/controllers/activities_controller_spec.rb b/spec/controllers/activities_controller_spec.rb index 08cbcf56..39e82dbb 100644 --- a/spec/controllers/activities_controller_spec.rb +++ b/spec/controllers/activities_controller_spec.rb @@ -25,7 +25,7 @@ end context "valid parameters" do - let(:params) { {activity: {name: "Pool party", start_time: 2.days.from_now.to_s, end_time: 2.days.from_now.to_s, place: "Pool" }} } + let(:params) { {activity: {name: "Pool party", start_time: 2.days.from_now.to_s, end_time: 2.days.from_now.to_s, location: "Pool" }} } its(:status) { should == 201 } end @@ -44,7 +44,7 @@ end context "valid parameters" do - let(:params) { {id: activity.id, activity: {name: "Pool party", start_time: 2.days.from_now.to_s, end_time: 3.days.from_now.to_s, place: "Pool" }} } + let(:params) { {id: activity.id, activity: {name: "Pool party", start_time: 2.days.from_now.to_s, end_time: 3.days.from_now.to_s, location: "Pool" }} } its(:status) { should == 204 } end diff --git a/spec/factories/activities.rb b/spec/factories/activities.rb index 98f270b6..4e7ae537 100644 --- a/spec/factories/activities.rb +++ b/spec/factories/activities.rb @@ -5,7 +5,7 @@ name "Party!" start_time "2013/12/12 18:00" end_time "2013/12/13 03:00" - place "Ballroom" + location "Ballroom" creator { FactoryGirl.create(:user) } end end diff --git a/spec/models/activity_spec.rb b/spec/models/activity_spec.rb index eb6651a4..046703aa 100644 --- a/spec/models/activity_spec.rb +++ b/spec/models/activity_spec.rb @@ -88,8 +88,8 @@ it { should_not accept_values_for(:event, nil) } - it { should accept_values_for(:place, "football pitch" ) } - it { should_not accept_values_for(:place, "", nil) } + it { should accept_values_for(:location, "football pitch" ) } + it { should_not accept_values_for(:location, "", nil) } it { should accept_values_for(:start_time, Time.now, nil) } it { should_not accept_values_for(:start_time, "") } From d5cca9ac63474171d45213a8a2dc4f367c3ba461 Mon Sep 17 00:00:00 2001 From: pietia Date: Fri, 31 May 2013 22:58:44 +0200 Subject: [PATCH 148/499] use I18n.localize --- app/decorators/activity_decorator.rb | 8 ++++---- app/views/activities/_form.html.haml | 4 ++-- config/locales/en.yml | 7 +++++-- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/app/decorators/activity_decorator.rb b/app/decorators/activity_decorator.rb index d237ca37..46346f9d 100644 --- a/app/decorators/activity_decorator.rb +++ b/app/decorators/activity_decorator.rb @@ -27,15 +27,15 @@ def time else alpha, omega, out = object.start_time, object.end_time, "" - out << alpha.strftime(I18n.t("activities.date_format")) + out << I18n.localize(alpha, format: :long) out << " / " - out << alpha.strftime(I18n.t("activities.time_format")) + out << I18n.localize(alpha, format: :time_only) out << " – " unless alpha.to_date == omega.to_date - out << omega.strftime(I18n.t("activities.date_format")) + out << I18n.localize(omega, format: :long) out << " / " end - out << omega.strftime(I18n.t("activities.time_format")) + out << I18n.localize(omega, format: :time_only) end end diff --git a/app/views/activities/_form.html.haml b/app/views/activities/_form.html.haml index 534d3add..fdb32110 100644 --- a/app/views/activities/_form.html.haml +++ b/app/views/activities/_form.html.haml @@ -12,7 +12,7 @@ %fieldset.start-time .select = f.datetime_select :start_time - %input.date-capture(data-target="start_time" type="text" placeholder="#{ current_event.start_time.strftime(t("activities.date_format")) }")> + %input.date-capture(data-target="start_time" type="text" placeholder="#{ localize(current_event.start_time, format: :long) }")> %input.time-capture(data-target="start_time" type="text" placeholder="15:00")> %span.divider> @@ -21,7 +21,7 @@ %fieldset.end-time .select = f.datetime_select :end_time - %input.date-capture(data-target="end_time" type="text" placeholder="#{ current_event.end_time.strftime(t("activities.date_format")) }")> + %input.date-capture(data-target="end_time" type="text" placeholder="#{ localize(current_event.end_time, format: :long) }")> %input.time-capture(data-target="end_time" type="text" placeholder="16:30")> %fieldset.anytime diff --git a/config/locales/en.yml b/config/locales/en.yml index ba06a3c1..948e9a8c 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -1,4 +1,9 @@ en: + time: + formats: + time_only: "%k:%M" + long: "%a, %-d.%-m" + user_nav: account: label: My Account @@ -39,8 +44,6 @@ en: label: More info title: Learn more about this activity anytime: Anytime - date_format: "%a, %-d.%-m" - time_format: "%k:%M" footer_nav: about: From a99f36c7807d6209c8fad689470a38931c775b63 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Fri, 31 May 2013 23:06:01 +0300 Subject: [PATCH 149/499] Fixing accidentally changed button type. --- app/assets/stylesheets/partials/activities/_index.sass | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/stylesheets/partials/activities/_index.sass b/app/assets/stylesheets/partials/activities/_index.sass index 7ee89fc1..a2c2064c 100644 --- a/app/assets/stylesheets/partials/activities/_index.sass +++ b/app/assets/stylesheets/partials/activities/_index.sass @@ -1,6 +1,6 @@ a#new-activity float: right - @extend %icon-button-ok + @extend %icon-button-add #activities clear: both From 6e6546f685707ade89752c0c2ab5084b39970316 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Sat, 1 Jun 2013 00:01:59 +0300 Subject: [PATCH 150/499] Clean-up. --- app/assets/stylesheets/partials/_activities.sass | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/stylesheets/partials/_activities.sass b/app/assets/stylesheets/partials/_activities.sass index 46fa867b..4f01fa42 100644 --- a/app/assets/stylesheets/partials/_activities.sass +++ b/app/assets/stylesheets/partials/_activities.sass @@ -3,7 +3,7 @@ body.activities @import "activities/filters" @import "activities/index" - &.new, &.edit + &.new @import "activities/new" #main From d39b4ad676cbb81074697ce8aab00a8fb079a137 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Sat, 1 Jun 2013 00:02:07 +0300 Subject: [PATCH 151/499] i18n: --- app/decorators/activity_decorator.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/decorators/activity_decorator.rb b/app/decorators/activity_decorator.rb index 46346f9d..c76df3c4 100644 --- a/app/decorators/activity_decorator.rb +++ b/app/decorators/activity_decorator.rb @@ -23,7 +23,7 @@ def status def time if object.anytime? - "Anytime" + I18n.t("activities.anytime") else alpha, omega, out = object.start_time, object.end_time, "" From bcf1abbf39bef6111c3f569fb2aae3f679175b8d Mon Sep 17 00:00:00 2001 From: Polarblau Date: Sat, 1 Jun 2013 00:09:54 +0300 Subject: [PATCH 152/499] Short form for localize. --- app/views/activities/_form.html.haml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/activities/_form.html.haml b/app/views/activities/_form.html.haml index fdb32110..7d26dd99 100644 --- a/app/views/activities/_form.html.haml +++ b/app/views/activities/_form.html.haml @@ -12,7 +12,7 @@ %fieldset.start-time .select = f.datetime_select :start_time - %input.date-capture(data-target="start_time" type="text" placeholder="#{ localize(current_event.start_time, format: :long) }")> + %input.date-capture(data-target="start_time" type="text" placeholder="#{ l(current_event.start_time, format: :long) }")> %input.time-capture(data-target="start_time" type="text" placeholder="15:00")> %span.divider> @@ -21,7 +21,7 @@ %fieldset.end-time .select = f.datetime_select :end_time - %input.date-capture(data-target="end_time" type="text" placeholder="#{ localize(current_event.end_time, format: :long) }")> + %input.date-capture(data-target="end_time" type="text" placeholder="#{ l(current_event.end_time, format: :long) }")> %input.time-capture(data-target="end_time" type="text" placeholder="16:30")> %fieldset.anytime From d01fd9f07555acaee3e490597aad30b711fca1ae Mon Sep 17 00:00:00 2001 From: pietia Date: Fri, 31 May 2013 23:23:49 +0200 Subject: [PATCH 153/499] include blank options --- app/views/activities/_form.html.haml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/activities/_form.html.haml b/app/views/activities/_form.html.haml index 7d26dd99..76be0186 100644 --- a/app/views/activities/_form.html.haml +++ b/app/views/activities/_form.html.haml @@ -11,7 +11,7 @@ %fieldset.start-time .select - = f.datetime_select :start_time + = f.datetime_select :start_time, include_blank: true %input.date-capture(data-target="start_time" type="text" placeholder="#{ l(current_event.start_time, format: :long) }")> %input.time-capture(data-target="start_time" type="text" placeholder="15:00")> @@ -20,7 +20,7 @@ %fieldset.end-time .select - = f.datetime_select :end_time + = f.datetime_select :end_time, include_blank: true %input.date-capture(data-target="end_time" type="text" placeholder="#{ l(current_event.end_time, format: :long) }")> %input.time-capture(data-target="end_time" type="text" placeholder="16:30")> From 06b7763d642ed35b7042f6c7528be1e2c39a0971 Mon Sep 17 00:00:00 2001 From: pietia Date: Sat, 1 Jun 2013 08:13:32 +0200 Subject: [PATCH 154/499] WIP parse date --- app/assets/javascripts/initializers.coffee | 2 ++ app/views/activities/_form.html.haml | 8 ++++---- config/locales/en.yml | 1 + 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/app/assets/javascripts/initializers.coffee b/app/assets/javascripts/initializers.coffee index 1697a816..cdb43e6c 100644 --- a/app/assets/javascripts/initializers.coffee +++ b/app/assets/javascripts/initializers.coffee @@ -13,6 +13,7 @@ $ -> min: new Date(App.event.startTime) max: new Date(App.event.endTime) format: 'ddd, d.m.' + formatSubmit: 'dd-mm-yyyy' onSet: (e)-> date = new Date(e.select) target = @$node.data 'target' @@ -23,6 +24,7 @@ $ -> $('.time-capture').pickatime format: 'h:i' + formatSubmit: 'h:i' onSet: (e)-> target = @$node.data 'target' [hours, minutes] = [parseInt((e.select / 60), 10), e.select % 60] diff --git a/app/views/activities/_form.html.haml b/app/views/activities/_form.html.haml index 76be0186..c923660b 100644 --- a/app/views/activities/_form.html.haml +++ b/app/views/activities/_form.html.haml @@ -12,8 +12,8 @@ %fieldset.start-time .select = f.datetime_select :start_time, include_blank: true - %input.date-capture(data-target="start_time" type="text" placeholder="#{ l(current_event.start_time, format: :long) }")> - %input.time-capture(data-target="start_time" type="text" placeholder="15:00")> + %input.date-capture(data-target="start_time" data-value="#{ l(@activity.start_time, format: :date_only) rescue nil }" type="text" placeholder="#{ l(current_event.start_time, format: :long) }")> + %input.time-capture(data-target="start_time" data-value="#{ l(@activity.start_time, format: :time_only) rescue nil }" type="text" placeholder="15:00")> %span.divider> – @@ -21,8 +21,8 @@ %fieldset.end-time .select = f.datetime_select :end_time, include_blank: true - %input.date-capture(data-target="end_time" type="text" placeholder="#{ l(current_event.end_time, format: :long) }")> - %input.time-capture(data-target="end_time" type="text" placeholder="16:30")> + %input.date-capture(data-target="end_time" data-value="#{ l(@activity.end_time, format: :date_only) rescue nil }" type="text" placeholder="#{ l(current_event.end_time, format: :long) }")> + %input.time-capture(data-target="end_time" data-value="#{ l(@activity.end_time, format: :time_only) rescue nil }" type="text" placeholder="16:30")> %fieldset.anytime %span.between or diff --git a/config/locales/en.yml b/config/locales/en.yml index 948e9a8c..de738e01 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -2,6 +2,7 @@ en: time: formats: time_only: "%k:%M" + date_only: "%d-%m-%Y" long: "%a, %-d.%-m" user_nav: From 5a1c73bf804a8477216955dc7e5dc4944a4cb6e5 Mon Sep 17 00:00:00 2001 From: pietia Date: Sat, 1 Jun 2013 10:05:16 +0200 Subject: [PATCH 155/499] errors extracted to its own partial --- app/views/activities/_form.html.haml | 5 +---- app/views/partials/_errors.html.haml | 4 ++++ 2 files changed, 5 insertions(+), 4 deletions(-) create mode 100644 app/views/partials/_errors.html.haml diff --git a/app/views/activities/_form.html.haml b/app/views/activities/_form.html.haml index c923660b..b3c6641e 100644 --- a/app/views/activities/_form.html.haml +++ b/app/views/activities/_form.html.haml @@ -1,8 +1,5 @@ = form_for @activity, html: { id: "new-activity" } do |f| - - if @activity.errors.any? - %ul.errors - - @activity.errors.full_messages.each do |error| - %li= error + = render partial: "partials/errors", locals: { resource: @activity } %label.name = f.text_field :name, placeholder: "Give your activity a name", class: "title" diff --git a/app/views/partials/_errors.html.haml b/app/views/partials/_errors.html.haml new file mode 100644 index 00000000..087f2229 --- /dev/null +++ b/app/views/partials/_errors.html.haml @@ -0,0 +1,4 @@ +- if resource.errors.any? + %ul.errors + - resource.errors.full_messages.each do |error| + %li= error \ No newline at end of file From 5a3fb6ac1cf541e063b5bbb4bb64b16e48c9ed6f Mon Sep 17 00:00:00 2001 From: Polarblau Date: Sun, 2 Jun 2013 00:15:15 +0300 Subject: [PATCH 156/499] Inline validation errors. --- .../stylesheets/partials/_activities.sass | 2 +- app/assets/stylesheets/utils/_forms.sass | 17 ++++++++++++ app/views/activities/_form.html.haml | 9 +++---- app/views/partials/_errors.html.haml | 4 --- config/initializers/validation_error.rb | 26 +++++++++++++++++++ 5 files changed, 48 insertions(+), 10 deletions(-) delete mode 100644 app/views/partials/_errors.html.haml create mode 100644 config/initializers/validation_error.rb diff --git a/app/assets/stylesheets/partials/_activities.sass b/app/assets/stylesheets/partials/_activities.sass index 4f01fa42..17f30ccc 100644 --- a/app/assets/stylesheets/partials/_activities.sass +++ b/app/assets/stylesheets/partials/_activities.sass @@ -3,7 +3,7 @@ body.activities @import "activities/filters" @import "activities/index" - &.new + &.new, &.edit, &.create, &.update @import "activities/new" #main diff --git a/app/assets/stylesheets/utils/_forms.sass b/app/assets/stylesheets/utils/_forms.sass index dcc903e6..e3b7adc5 100644 --- a/app/assets/stylesheets/utils/_forms.sass +++ b/app/assets/stylesheets/utils/_forms.sass @@ -3,6 +3,8 @@ form input, textarea width: 100% padding: 0.75em 15px 1em + background: $white + border-bottom: 1px solid $white margin: 0 0 2px 0 font-size: 1.25em border: none @@ -18,6 +20,12 @@ form font-size: 1.35em +custom-sans(medium) + &.validation-error + border-bottom: 1px solid $red + + +placeholder + color: lighten($red, 30) + &:focus z-index: 1000 box-shadow: 0 0 7px transparentize($black, 0.9) @@ -30,6 +38,15 @@ form label display: block margin: 0 0 1.25em + position: relative + + span.validation-error-message + position: absolute + right: 15px + top: 0 + color: $red + z-index: 1000 + line-height: 4.25em hr background-color: $light-gray diff --git a/app/views/activities/_form.html.haml b/app/views/activities/_form.html.haml index b3c6641e..e62fa741 100644 --- a/app/views/activities/_form.html.haml +++ b/app/views/activities/_form.html.haml @@ -1,5 +1,4 @@ = form_for @activity, html: { id: "new-activity" } do |f| - = render partial: "partials/errors", locals: { resource: @activity } %label.name = f.text_field :name, placeholder: "Give your activity a name", class: "title" @@ -9,8 +8,8 @@ %fieldset.start-time .select = f.datetime_select :start_time, include_blank: true - %input.date-capture(data-target="start_time" data-value="#{ l(@activity.start_time, format: :date_only) rescue nil }" type="text" placeholder="#{ l(current_event.start_time, format: :long) }")> - %input.time-capture(data-target="start_time" data-value="#{ l(@activity.start_time, format: :time_only) rescue nil }" type="text" placeholder="15:00")> + %input.date-capture(data-target="start_time" data-value="#{ l(@activity.start_time, format: :date_only) rescue nil }" type="text" placeholder="#{ l(current_event.start_time, format: :long) }" class="#{ "validation-error" if @activity.errors[:start_time].any? }")> + %input.time-capture(data-target="start_time" data-value="#{ l(@activity.start_time, format: :time_only) rescue nil }" type="text" placeholder="15:00" class="#{ "validation-error" if @activity.errors[:start_time].any? }")> %span.divider> – @@ -18,8 +17,8 @@ %fieldset.end-time .select = f.datetime_select :end_time, include_blank: true - %input.date-capture(data-target="end_time" data-value="#{ l(@activity.end_time, format: :date_only) rescue nil }" type="text" placeholder="#{ l(current_event.end_time, format: :long) }")> - %input.time-capture(data-target="end_time" data-value="#{ l(@activity.end_time, format: :time_only) rescue nil }" type="text" placeholder="16:30")> + %input.date-capture(data-target="end_time" data-value="#{ l(@activity.end_time, format: :date_only) rescue nil }" type="text" placeholder="#{ l(current_event.end_time, format: :long) }" class="#{ "validation-error" if @activity.errors[:end_time].any? }")> + %input.time-capture(data-target="end_time" data-value="#{ l(@activity.end_time, format: :time_only) rescue nil }" type="text" placeholder="16:30" class="#{ "validation-error" if @activity.errors[:end_time].any? }")> %fieldset.anytime %span.between or diff --git a/app/views/partials/_errors.html.haml b/app/views/partials/_errors.html.haml deleted file mode 100644 index 087f2229..00000000 --- a/app/views/partials/_errors.html.haml +++ /dev/null @@ -1,4 +0,0 @@ -- if resource.errors.any? - %ul.errors - - resource.errors.full_messages.each do |error| - %li= error \ No newline at end of file diff --git a/config/initializers/validation_error.rb b/config/initializers/validation_error.rb new file mode 100644 index 00000000..ed233e6c --- /dev/null +++ b/config/initializers/validation_error.rb @@ -0,0 +1,26 @@ +# Overrides Rails' default handling of validation and other +# error messages for objects. +# Instead of displaying all errors as list above the form +# the errors are now displayed after each individual input +# element wrapped in a '' +ActionView::Base.field_error_proc = Proc.new do |html_tag, instance| + error_class = "validation-error" + error_message_class = "validation-error-message" + + if html_tag =~ /<(input|textarea|select)[^>]+class=/ + style_attribute = html_tag =~ /class=['"]/ + html_tag.insert(style_attribute + 7, "#{error_class} ") + elsif html_tag =~ /<(input|textarea|select)/ + first_whitespace = html_tag =~ /\s/ + html_tag[first_whitespace] = " class='#{error_class}' " + end + + if html_tag =~ /<(label)/ + html_tag + elsif instance.error_message.kind_of?(Array) + %(#{html_tag} #{instance.error_message.join(', ')}).html_safe + else + %(#{html_tag} #{instance.error_message}).html_safe + end + +end From ea1b2aa97a1e71f70a9721121c4ea94fe519b3fe Mon Sep 17 00:00:00 2001 From: Polarblau Date: Sun, 2 Jun 2013 00:18:30 +0300 Subject: [PATCH 157/499] Prevent resizing of textareas. Closes #49. --- app/assets/stylesheets/utils/_forms.sass | 1 + 1 file changed, 1 insertion(+) diff --git a/app/assets/stylesheets/utils/_forms.sass b/app/assets/stylesheets/utils/_forms.sass index e3b7adc5..27907f09 100644 --- a/app/assets/stylesheets/utils/_forms.sass +++ b/app/assets/stylesheets/utils/_forms.sass @@ -12,6 +12,7 @@ form +custom-sans(light) z-index: 1 position: relative + resize: none +placeholder color: darken($light-gray, 10) From 5b46ee2cb59b3dddaf9733b63bba7e21bad69e2f Mon Sep 17 00:00:00 2001 From: Alex Coles Date: Mon, 3 Jun 2013 21:11:13 +0200 Subject: [PATCH 158/499] Loosen .ruby-version requirement Signed-off-by: Alex Coles --- .ruby-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ruby-version b/.ruby-version index c031dda5..359a5b95 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -2.0.0-p0 +2.0.0 \ No newline at end of file From 924e999f4dd1d6e6d92ed358a0456bab00d3537b Mon Sep 17 00:00:00 2001 From: Alex Coles Date: Mon, 3 Jun 2013 21:11:25 +0200 Subject: [PATCH 159/499] Bump debugger to 1.6.0 Signed-off-by: Alex Coles --- Gemfile | 2 +- Gemfile.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Gemfile b/Gemfile index b35fb8e3..da6cb8e5 100644 --- a/Gemfile +++ b/Gemfile @@ -24,7 +24,7 @@ gem 'draper' gem 'cancan' group :development do - gem 'debugger', '~> 1.5' + gem 'debugger', '~> 1.6' gem 'heroku_san', '~> 3.0.2' gem 'foreman' gem 'better_errors' diff --git a/Gemfile.lock b/Gemfile.lock index e310a787..9ab7a161 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -69,12 +69,12 @@ GEM coffee-script-source (1.6.2) columnize (0.3.6) debug_inspector (0.0.2) - debugger (1.5.0) + debugger (1.6.0) columnize (>= 0.3.1) debugger-linecache (~> 1.2.0) - debugger-ruby_core_source (~> 1.2.0) + debugger-ruby_core_source (~> 1.2.1) debugger-linecache (1.2.0) - debugger-ruby_core_source (1.2.0) + debugger-ruby_core_source (1.2.2) devise (3.0.0.rc) bcrypt-ruby (~> 3.0) orm_adapter (~> 0.1) @@ -275,7 +275,7 @@ DEPENDENCIES capybara (~> 2.1) capybara-webkit (~> 0.14) coffee-rails (~> 4.0.0.beta1) - debugger (~> 1.5) + debugger (~> 1.6) devise (~> 3.0.0.rc) draper exception_notification! From 91971a9abee4bdd0f27cab5359359ebff5df4afb Mon Sep 17 00:00:00 2001 From: Polarblau Date: Wed, 5 Jun 2013 00:11:05 +0300 Subject: [PATCH 160/499] Don't authorize show. --- app/controllers/activities_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/activities_controller.rb b/app/controllers/activities_controller.rb index 994032c4..f6568719 100644 --- a/app/controllers/activities_controller.rb +++ b/app/controllers/activities_controller.rb @@ -3,7 +3,7 @@ class ActivitiesController < ApplicationController skip_before_filter :authenticate_user!, only: [:index, :show] before_filter :load_resource, only: [:show, :edit, :update, :destroy] - authorize_resource only: [:show, :edit, :update, :destroy] + authorize_resource only: [:edit, :update, :destroy] def index @activities = current_event.activities From 9bb99440224072ebed5dbd93baded70717eee4d8 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Wed, 5 Jun 2013 10:05:55 +0300 Subject: [PATCH 161/499] Temp. fix for #42. --- config/initializers/haml.rb | 1 + 1 file changed, 1 insertion(+) create mode 100644 config/initializers/haml.rb diff --git a/config/initializers/haml.rb b/config/initializers/haml.rb new file mode 100644 index 00000000..e4da4c54 --- /dev/null +++ b/config/initializers/haml.rb @@ -0,0 +1 @@ +Haml::Template.options[:remove_whitespace] = true From b1d4e66156c05921275b3a8090be1e7c0ac384e7 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Wed, 5 Jun 2013 11:01:36 +0300 Subject: [PATCH 162/499] Updating haml to lastest and installing redcarpet. --- Gemfile | 1 + Gemfile.lock | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Gemfile b/Gemfile index da6cb8e5..03364c4d 100644 --- a/Gemfile +++ b/Gemfile @@ -22,6 +22,7 @@ gem 'haml-rails' gem 'rails_html_helpers' gem 'draper' gem 'cancan' +gem 'redcarpet' group :development do gem 'debugger', '~> 1.6' diff --git a/Gemfile.lock b/Gemfile.lock index 9ab7a161..900380db 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -100,7 +100,7 @@ GEM foreman (0.63.0) dotenv (>= 0.7) thor (>= 0.13.6) - haml (4.0.2) + haml (4.0.3) tilt haml-rails (0.4) actionpack (>= 3.1, < 4.1) @@ -199,6 +199,7 @@ GEM thor (>= 0.18.1, < 2.0) raindrops (0.11.0) rake (10.0.4) + redcarpet (2.3.0) request_store (1.0.5) rest-client (1.6.7) mime-types (>= 1.16) @@ -244,7 +245,7 @@ GEM thor (0.18.1) thread_safe (0.1.0) atomic - tilt (1.4.0) + tilt (1.4.1) treetop (1.4.12) polyglot polyglot (>= 0.3.1) @@ -295,6 +296,7 @@ DEPENDENCIES rack-robotz (~> 0.0.3) rails (= 4.0.0.rc1) rails_html_helpers + redcarpet rspec-rails (~> 2.0) sass-rails (~> 4.0.0.beta1) settingslogic From 4d5183f2807607e748379ccb3ea17ccb89989659 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Wed, 5 Jun 2013 11:03:02 +0300 Subject: [PATCH 163/499] Markdown support. --- app/assets/images/shared/markdown.png | Bin 0 -> 1386 bytes app/assets/stylesheets/utils/_forms.sass | 5 +++++ app/decorators/activity_decorator.rb | 17 +++++++++++++++++ 3 files changed, 22 insertions(+) create mode 100644 app/assets/images/shared/markdown.png diff --git a/app/assets/images/shared/markdown.png b/app/assets/images/shared/markdown.png new file mode 100644 index 0000000000000000000000000000000000000000..047bde537d0a09a64932bb06b8facb228175d5ec GIT binary patch literal 1386 zcmeAS@N?(olHy`uVBq!ia0vp^u0U+Y!3HEJT8Fp+DajJoh?3y^w370~qErUQl>DSr z1<%~X^wgl##FWaylc_cg49qH-ArU1JzCKpT`MG+DAT@dwxdlMo3=B5*6$OdO*{LN8 zNvY|XdA3ULckfqH$V{%1*XSQL?vFu&J;D8jzb> zlBiITo0C^;Rbi_HHrEQs1_|pcDS(xfWZNo192Makpx~Tel&WB=XRMoSU}&gdW~OIo zVrph)sH0$HU}&Uo07PcGh9*{~W>!Y#3Q(W~w5=#5%__*n4QdyVXRDM^Qc_^0uU}qX zu2*iXmtT~wZ)j<02{OaTNEfI=x41H|B(Xv_uUHvof=g;~a#3bMNoIbY0?5R~r2Ntn zTP2`NAzsKWfE$}v3=Jk=fazBx7U&!58GyV5Q|Rl9UukYGTy=3tP%6T`SPd=?sVqp< z4@xc0FD*(2MqHXQ$f^P>=c3falKi5O{QMkPC z!8&|>tvvIJOA_;vQ$1a5m4IgGWoD*W892I`I$OB8IGdWg7#g}7S(vz(IJ&x68Uk5P z&L$=>y)OC5rManjB{01y2)(X2^@0*ZZUN9{m(-%nveXo}qWoM1u&1pual6F{r+HAl zDY)I@j8m^Z&@uX;s6~oum=G}ifSB-v3*^9)erg^ty%zx!_O&iC4+aLtJDx6%Ar-gY z%&_-9>>zPWT2#Gr^@H>e$~&$sWwmdx(DdtSTD(Z(m#9vmkhNvmi%V;}6?SmewKYAs zqQud3k9leDlWS*H;;OE*NU6=xvF-SNo@7yYj2Nwkz`6?#JYxO#hJO5t9@*^Yaw% z9a(q6O~dxsJmsHMvy4^$RjqgO(l(`8t=4Wkoiy&B6y0CD!0kx>^VHT&%<-*9RJVy< zJlB1*>HgzMYYxWUThMXs;kv^6S0iN3d=seMzV+P+lRgvs8w-jQVkp04`hwnuO0%S( zIKK2alw4F#9grX<@kO<-QUlV^_(|F53~8M=#~&T`UdaT4TOD1eN-E`C)z Zz`*NK_K3woSrSzGdAj Date: Wed, 5 Jun 2013 11:04:38 +0300 Subject: [PATCH 164/499] Implementing first version of fancy date helper, untested. --- app/helpers/activity_form_helper.rb | 37 ++++++++++++++++++++++++++++ app/views/activities/_form.html.haml | 30 +++++++++++++--------- config/locales/en.yml | 3 ++- 3 files changed, 57 insertions(+), 13 deletions(-) create mode 100644 app/helpers/activity_form_helper.rb diff --git a/app/helpers/activity_form_helper.rb b/app/helpers/activity_form_helper.rb new file mode 100644 index 00000000..7d3c28c1 --- /dev/null +++ b/app/helpers/activity_form_helper.rb @@ -0,0 +1,37 @@ +module ActivityFormHelper + + def fancy_datime_select(form, field, model) + field = field.to_s + haml_tag :fieldset, class: field.dasherize do + haml_tag :div, class: 'select' do + haml_concat form.datetime_select(field.to_sym, include_blank: true) + end + haml_tag :input, capture_attributes(field, model, 'date') + haml_tag :input, capture_attributes(field, model, 'time') + end + end + + private + + def capture_attributes(field, model, type = 'date') + field = field.to_sym + + attributes = { + type: 'text', + class: "#{type}-capture", + placeholder: l(current_event.send(field), format: "#{type}_only".to_sym), + data: { target: field } + } + + if (value = model.send(field)) + attributes[:value] = l(value, format: "#{type}_only".to_sym) + end + + if model.errors[field].any? + attributes[:class] << ' validation-error' + end + + attributes + end + +end diff --git a/app/views/activities/_form.html.haml b/app/views/activities/_form.html.haml index e62fa741..98d52ee2 100644 --- a/app/views/activities/_form.html.haml +++ b/app/views/activities/_form.html.haml @@ -5,20 +5,26 @@ %hr - %fieldset.start-time - .select - = f.datetime_select :start_time, include_blank: true - %input.date-capture(data-target="start_time" data-value="#{ l(@activity.start_time, format: :date_only) rescue nil }" type="text" placeholder="#{ l(current_event.start_time, format: :long) }" class="#{ "validation-error" if @activity.errors[:start_time].any? }")> - %input.time-capture(data-target="start_time" data-value="#{ l(@activity.start_time, format: :time_only) rescue nil }" type="text" placeholder="15:00" class="#{ "validation-error" if @activity.errors[:start_time].any? }")> + -# + %fieldset.start-time + .select + = f.datetime_select :start_time, include_blank: true + %input.date-capture(data-target="start_time" data-update="end-time" data-value="#{ l(@activity.start_time, format: :date_only) rescue nil }" type="text" placeholder="#{ l(current_event.start_time, format: :long) }" class="#{ "validation-error" if @activity.errors[:start_time].any? }")> + %input.time-capture(data-target="start_time" data-update="end-time" data-value="#{ l(@activity.start_time, format: :time_only) rescue nil }" type="text" placeholder="15:00" class="#{ "validation-error" if @activity.errors[:start_time].any? }")> + + - fancy_datime_select f, :start_time, @activity %span.divider> – - %fieldset.end-time - .select - = f.datetime_select :end_time, include_blank: true - %input.date-capture(data-target="end_time" data-value="#{ l(@activity.end_time, format: :date_only) rescue nil }" type="text" placeholder="#{ l(current_event.end_time, format: :long) }" class="#{ "validation-error" if @activity.errors[:end_time].any? }")> - %input.time-capture(data-target="end_time" data-value="#{ l(@activity.end_time, format: :time_only) rescue nil }" type="text" placeholder="16:30" class="#{ "validation-error" if @activity.errors[:end_time].any? }")> + -# + %fieldset.end-time + .select + = f.datetime_select :end_time, include_blank: true + %input.date-capture(data-target="end_time" data-update="start-time" data-value="#{ l(@activity.end_time, format: :date_only) rescue nil }" type="text" placeholder="#{ l(current_event.end_time, format: :long) }" class="#{ "validation-error" if @activity.errors[:end_time].any? }")> + %input.time-capture(data-target="end_time" data-update="start-time" data-value="#{ l(@activity.end_time, format: :time_only) rescue nil }" type="text" placeholder="16:30" class="#{ "validation-error" if @activity.errors[:end_time].any? }")> + + - fancy_datime_select f, :end_time, @activity %fieldset.anytime %span.between or @@ -35,11 +41,11 @@ %small Max. participants %label.requirements - = f.text_area :requirements, placeholder: "Requirements" + = f.text_area :requirements, placeholder: "Requirements", rows: 6, class: "markdown" %small What to bring, to wear etc. %label.description - = f.text_area :description, placeholder: "Description" + = f.text_area :description, placeholder: "Description", rows: 8, class: "markdown" %small Background information, links, etc. %label.image-url diff --git a/config/locales/en.yml b/config/locales/en.yml index de738e01..c32de199 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -2,7 +2,8 @@ en: time: formats: time_only: "%k:%M" - date_only: "%d-%m-%Y" + #date_only: "%d-%m-%Y" + date_only: "%a, %-d.%-m" long: "%a, %-d.%-m" user_nav: From 19292822a4dbf3e728f20c552246d393ccc76a41 Mon Sep 17 00:00:00 2001 From: pietia Date: Wed, 5 Jun 2013 14:06:42 +0200 Subject: [PATCH 165/499] don't fail when updating date --- app/models/activity.rb | 4 ++++ app/models/event.rb | 33 +++++++++++++++++++++++---------- 2 files changed, 27 insertions(+), 10 deletions(-) diff --git a/app/models/activity.rb b/app/models/activity.rb index 883138b3..75aaa5c3 100644 --- a/app/models/activity.rb +++ b/app/models/activity.rb @@ -25,6 +25,10 @@ def recent(limit = DEFAULT_LIMIT) where("start_time >= :t OR anytime = true", t: 1.month.ago).limit(limit) end + def all_activities(limit = DEFAULT_LIMIT) + limit(limit) + end + def today where(":t between start_time and end_time", t: Date.current) end diff --git a/app/models/event.rb b/app/models/event.rb index 62ce104f..99fa5834 100644 --- a/app/models/event.rb +++ b/app/models/event.rb @@ -2,32 +2,45 @@ class Event attr_writer :activity_source # DI attr_reader :name, :start_time, :end_time - def initialize(name, start_time, end_time, fetcher = ->{ Activity.recent }) + def initialize(name, start_time, end_time, + recent_activities_fetcher = ->{ Activity.recent }, + all_activities_fetcher = ->{ Activity.all_activities }) @name, @start_time, @end_time = name, start_time, end_time - @fetcher = fetcher + @recent_activities_fetcher = recent_activities_fetcher + @all_activities_fetcher = all_activities_fetcher end def new_activity(author, *args) activity_source.call(*args).tap do |activity| - activity.event = self - activity.creator = author + if activity + activity.event = self + activity.creator = author + end end end def activity(activity_id) - activities.where(:id => activity_id).first.tap do |activity| - activity.event = self + all_activities.where(:id => activity_id).first.tap do |activity| + activity.event = self if activity end end - def activities - fetch + def recent_activities + fetch_recent + end + + def all_activities + fetch_all_activities end private - def fetch - @fetcher.() + def fetch_recent + @recent_activities_fetcher.() + end + + def fetch_all_activities + @all_activities_fetcher.() end def activity_source From b08a40a8669344545d513f0e70d8d2bd0f1ca429 Mon Sep 17 00:00:00 2001 From: pietia Date: Wed, 5 Jun 2013 15:05:21 +0200 Subject: [PATCH 166/499] more work on 404 --- app/controllers/activities_controller.rb | 4 +-- app/controllers/api_controller.rb | 5 ++++ app/controllers/participations_controller.rb | 2 +- lib/api_responder.rb | 27 ++++++++++++++++++++ 4 files changed, 35 insertions(+), 3 deletions(-) create mode 100644 app/controllers/api_controller.rb create mode 100644 lib/api_responder.rb diff --git a/app/controllers/activities_controller.rb b/app/controllers/activities_controller.rb index f6568719..96090604 100644 --- a/app/controllers/activities_controller.rb +++ b/app/controllers/activities_controller.rb @@ -1,4 +1,4 @@ -class ActivitiesController < ApplicationController +class ActivitiesController < ApiController respond_to :html skip_before_filter :authenticate_user!, only: [:index, :show] @@ -20,7 +20,7 @@ def new end def edit - respond_with(@activity) + respond_with(@activity, status: :not_found) end def create diff --git a/app/controllers/api_controller.rb b/app/controllers/api_controller.rb new file mode 100644 index 00000000..ff83be7e --- /dev/null +++ b/app/controllers/api_controller.rb @@ -0,0 +1,5 @@ +class ApiController < ApplicationController + + self.responder = ApiResponder + +end diff --git a/app/controllers/participations_controller.rb b/app/controllers/participations_controller.rb index fa741ad6..5a4c5647 100644 --- a/app/controllers/participations_controller.rb +++ b/app/controllers/participations_controller.rb @@ -1,4 +1,4 @@ -class ParticipationsController < ApplicationController +class ParticipationsController < ApiController respond_to :js, :html def create diff --git a/lib/api_responder.rb b/lib/api_responder.rb new file mode 100644 index 00000000..b8357f0d --- /dev/null +++ b/lib/api_responder.rb @@ -0,0 +1,27 @@ +# -*- encoding : utf-8 -*- +class ApiResponder < ActionController::Responder + + def to_html + if get? && resource.nil? + raise ActionController::RoutingError.new('Not Found') + else + super + end + end + + protected + + # This is the common behavior for formats associated with APIs, such as :xml and :json. + def api_behavior(error) + raise error unless resourceful? + + if get? + resource.nil? ? display(resource, status: :not_found) : display(resource) + elsif post? + display resource, :status => :created, :location => api_location + else + head :no_content + end + end + +end From 5e239ac0de0f352775f749ccf0b19d94a52ea274 Mon Sep 17 00:00:00 2001 From: pietia Date: Wed, 5 Jun 2013 16:05:50 +0200 Subject: [PATCH 167/499] activities form usable again --- app/helpers/activity_form_helper.rb | 15 ++++++++++----- config/locales/en.yml | 4 ++-- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/app/helpers/activity_form_helper.rb b/app/helpers/activity_form_helper.rb index 7d3c28c1..223b86b4 100644 --- a/app/helpers/activity_form_helper.rb +++ b/app/helpers/activity_form_helper.rb @@ -15,17 +15,18 @@ def fancy_datime_select(form, field, model) def capture_attributes(field, model, type = 'date') field = field.to_sym + format = "#{type}_only".to_sym attributes = { type: 'text', class: "#{type}-capture", - placeholder: l(current_event.send(field), format: "#{type}_only".to_sym), - data: { target: field } + placeholder: l(current_event.send(field), format: format), + data: { target: field, value: parse_date(model.send(field), format) } } - if (value = model.send(field)) - attributes[:value] = l(value, format: "#{type}_only".to_sym) - end + #if (value = model.send(field)) + # attributes[:value] = l(value, format: "#{type}_only".to_sym) + #end if model.errors[field].any? attributes[:class] << ' validation-error' @@ -34,4 +35,8 @@ def capture_attributes(field, model, type = 'date') attributes end + def parse_date(d, format) + l(d, format: format) rescue nil + end + end diff --git a/config/locales/en.yml b/config/locales/en.yml index c32de199..78ce443c 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -2,8 +2,8 @@ en: time: formats: time_only: "%k:%M" - #date_only: "%d-%m-%Y" - date_only: "%a, %-d.%-m" + date_only: "%d-%m-%Y" + #date_only: "%a, %-d.%-m" long: "%a, %-d.%-m" user_nav: From 820e872d6b372dde93bc83cd1d5c8a6054b19589 Mon Sep 17 00:00:00 2001 From: pietia Date: Wed, 5 Jun 2013 16:10:41 +0200 Subject: [PATCH 168/499] cleanups --- app/helpers/activity_form_helper.rb | 4 ---- app/views/activities/_form.html.haml | 14 -------------- 2 files changed, 18 deletions(-) diff --git a/app/helpers/activity_form_helper.rb b/app/helpers/activity_form_helper.rb index 223b86b4..73f19d9d 100644 --- a/app/helpers/activity_form_helper.rb +++ b/app/helpers/activity_form_helper.rb @@ -24,10 +24,6 @@ def capture_attributes(field, model, type = 'date') data: { target: field, value: parse_date(model.send(field), format) } } - #if (value = model.send(field)) - # attributes[:value] = l(value, format: "#{type}_only".to_sym) - #end - if model.errors[field].any? attributes[:class] << ' validation-error' end diff --git a/app/views/activities/_form.html.haml b/app/views/activities/_form.html.haml index 98d52ee2..5788f5de 100644 --- a/app/views/activities/_form.html.haml +++ b/app/views/activities/_form.html.haml @@ -5,25 +5,11 @@ %hr - -# - %fieldset.start-time - .select - = f.datetime_select :start_time, include_blank: true - %input.date-capture(data-target="start_time" data-update="end-time" data-value="#{ l(@activity.start_time, format: :date_only) rescue nil }" type="text" placeholder="#{ l(current_event.start_time, format: :long) }" class="#{ "validation-error" if @activity.errors[:start_time].any? }")> - %input.time-capture(data-target="start_time" data-update="end-time" data-value="#{ l(@activity.start_time, format: :time_only) rescue nil }" type="text" placeholder="15:00" class="#{ "validation-error" if @activity.errors[:start_time].any? }")> - - fancy_datime_select f, :start_time, @activity %span.divider> – - -# - %fieldset.end-time - .select - = f.datetime_select :end_time, include_blank: true - %input.date-capture(data-target="end_time" data-update="start-time" data-value="#{ l(@activity.end_time, format: :date_only) rescue nil }" type="text" placeholder="#{ l(current_event.end_time, format: :long) }" class="#{ "validation-error" if @activity.errors[:end_time].any? }")> - %input.time-capture(data-target="end_time" data-update="start-time" data-value="#{ l(@activity.end_time, format: :time_only) rescue nil }" type="text" placeholder="16:30" class="#{ "validation-error" if @activity.errors[:end_time].any? }")> - - fancy_datime_select f, :end_time, @activity %fieldset.anytime From b6a5701d15a74aa199ab2cd9c3d44a9475adc86c Mon Sep 17 00:00:00 2001 From: Polarblau Date: Wed, 5 Jun 2013 11:12:37 +0300 Subject: [PATCH 169/499] Adding link to show. --- app/views/activities/edit.html.haml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/views/activities/edit.html.haml b/app/views/activities/edit.html.haml index 824ab409..a1fe38d0 100644 --- a/app/views/activities/edit.html.haml +++ b/app/views/activities/edit.html.haml @@ -1,4 +1,7 @@ %h2 Edit your activity -= render "form" \ No newline at end of file + %small + = link_to "View activity page", @activity + += render "form" From 6beff5aa2b91c3adf564caf08953c76b32b44488 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Wed, 5 Jun 2013 11:19:07 +0300 Subject: [PATCH 170/499] Sorting of recent activities. --- app/models/activity.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/models/activity.rb b/app/models/activity.rb index 75aaa5c3..db85466b 100644 --- a/app/models/activity.rb +++ b/app/models/activity.rb @@ -22,7 +22,9 @@ class Activity < ActiveRecord::Base class << self def recent(limit = DEFAULT_LIMIT) - where("start_time >= :t OR anytime = true", t: 1.month.ago).limit(limit) + where("start_time >= :t OR anytime = true", t: 1.month.ago) + .limit(limit) + .order("anytime DESC, start_time ASC") end def all_activities(limit = DEFAULT_LIMIT) From 5126acec12b5fb4fcf817bcabd8cb6ecc2cdd625 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Wed, 5 Jun 2013 18:20:52 +0300 Subject: [PATCH 171/499] Basic mark-up for show. --- app/decorators/activity_decorator.rb | 11 +++++++ app/views/activities/show.html.haml | 45 ++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+) create mode 100644 app/views/activities/show.html.haml diff --git a/app/decorators/activity_decorator.rb b/app/decorators/activity_decorator.rb index 90830937..3c115a50 100644 --- a/app/decorators/activity_decorator.rb +++ b/app/decorators/activity_decorator.rb @@ -29,6 +29,17 @@ def requirements_markdown object.requirements ? markdown(object.requirements) : '' end + def room_left + left = object.limit_of_participants - object.participations_count + if left == 1 + "Quick, only one place left." + elsif left > 0 + "Still room for #{left} people" + else + "Sorry. All places are gone." + end + end + def time if object.anytime? I18n.t("activities.anytime") diff --git a/app/views/activities/show.html.haml b/app/views/activities/show.html.haml new file mode 100644 index 00000000..afd3dc36 --- /dev/null +++ b/app/views/activities/show.html.haml @@ -0,0 +1,45 @@ +%article + %header + %h2 + = @activity.name + %p + Organized by + = @activity.creator_name + + %section.description + = @activity.description_markdown + + %section.overview + %figure + = image_tag "dummies/activity.jpg", alt: @activity.name, class: "progress", data: { progress: @activity.full_by } + %p + = @activity.room_left + %a.participants(href="#" title="") + %span + Participants + + %section.details + %dl + - if @activity.anytime? + %dt + Time & Date + %dd + Anytime + - else + %dt + Start + %dd + = l(@activity.start_time, format: :long) + %dt + End + %dd + = l(@activity.end_time, format: :long) + %dt + Location + %dd + = @activity.location + %dt + Required + %dd + = @activity.requirements + From f38138fbeab15bf4c7ad132ca9bc0fa82c7a3165 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Wed, 5 Jun 2013 18:21:05 +0300 Subject: [PATCH 172/499] Missing styles for cross links. --- app/assets/stylesheets/_base.sass | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/app/assets/stylesheets/_base.sass b/app/assets/stylesheets/_base.sass index ed47be36..761db236 100644 --- a/app/assets/stylesheets/_base.sass +++ b/app/assets/stylesheets/_base.sass @@ -22,3 +22,11 @@ a:hover +custom-sans(light) font-size: 2em margin: 0 0 2em 0 + + small + font-size: 0.5em + display: block + + strong + +custom-sans(medium) + margin: 0 0.5em From 763c7e3bc90db3f9be9fd2854046fde27a1a3d83 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Wed, 5 Jun 2013 18:21:23 +0300 Subject: [PATCH 173/499] Using ugly as recommended. --- config/initializers/haml.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/initializers/haml.rb b/config/initializers/haml.rb index e4da4c54..7e8ddb37 100644 --- a/config/initializers/haml.rb +++ b/config/initializers/haml.rb @@ -1 +1 @@ -Haml::Template.options[:remove_whitespace] = true +Haml::Template.options[:ugly] = true From 94623f3a0adbfad5a9019d4e9b0c9b3c89077785 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Wed, 5 Jun 2013 18:21:42 +0300 Subject: [PATCH 174/499] Decorate. --- app/controllers/activities_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/activities_controller.rb b/app/controllers/activities_controller.rb index 96090604..83011856 100644 --- a/app/controllers/activities_controller.rb +++ b/app/controllers/activities_controller.rb @@ -42,7 +42,7 @@ def destroy private def load_resource - @activity = current_event.activity(params[:id]) + @activity = current_event.activity(params[:id]).decorate end def sanitized_params From 048615f29196ba5b1d878f23d215458b58f9f9f9 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Wed, 5 Jun 2013 18:22:03 +0300 Subject: [PATCH 175/499] Markdown icon in textarea. --- app/assets/stylesheets/utils/_forms.sass | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/assets/stylesheets/utils/_forms.sass b/app/assets/stylesheets/utils/_forms.sass index 7eea2bed..a834e18d 100644 --- a/app/assets/stylesheets/utils/_forms.sass +++ b/app/assets/stylesheets/utils/_forms.sass @@ -36,6 +36,9 @@ form z-index: 1000 box-shadow: 0 0 7px transparentize($black, 0.9) + &.markdown + background-image: none + fieldset border: none padding: 0 From dd37541102a1d51b9c981d47da749753a30eef36 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Wed, 5 Jun 2013 18:22:34 +0300 Subject: [PATCH 176/499] Markdown info and rule. --- app/views/activities/_form.html.haml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app/views/activities/_form.html.haml b/app/views/activities/_form.html.haml index 5788f5de..9b94f8e5 100644 --- a/app/views/activities/_form.html.haml +++ b/app/views/activities/_form.html.haml @@ -18,6 +18,8 @@ = f.check_box :anytime Without a specific date/time + %hr + %label.location> = f.text_field :location, placeholder: "Location" %small Where does your activity take place, where to meet, … @@ -27,11 +29,11 @@ %small Max. participants %label.requirements - = f.text_area :requirements, placeholder: "Requirements", rows: 6, class: "markdown" + = f.text_area :requirements, placeholder: "Requirements", rows: 6, class: "markdown", title: "You can use markdown here!" %small What to bring, to wear etc. %label.description - = f.text_area :description, placeholder: "Description", rows: 8, class: "markdown" + = f.text_area :description, placeholder: "Description", rows: 8, class: "markdown", title: "You can use markdown here!" %small Background information, links, etc. %label.image-url From d5018db7342215556267771b06643ed136dd1c6a Mon Sep 17 00:00:00 2001 From: Polarblau Date: Wed, 5 Jun 2013 18:22:59 +0300 Subject: [PATCH 177/499] Linking activity. --- app/views/activities/_activity.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/activities/_activity.html.haml b/app/views/activities/_activity.html.haml index ada7b95c..6cd0422c 100644 --- a/app/views/activities/_activity.html.haml +++ b/app/views/activities/_activity.html.haml @@ -28,7 +28,7 @@ %span = t("activities.join.label") - %a.details(href="#" title="#{ t("activities.more_info.title") }") + %a.details(href="#{activity_path(activity)}" title="#{ t("activities.more_info.title") }") %span = t("activities.more_info.label") - unless type == 'default' From 4a865f9ab63eddadaabb2691bf6b6685acdc9e17 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Wed, 5 Jun 2013 18:23:43 +0300 Subject: [PATCH 178/499] Create link on edit page. --- app/views/activities/edit.html.haml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/views/activities/edit.html.haml b/app/views/activities/edit.html.haml index a1fe38d0..61779212 100644 --- a/app/views/activities/edit.html.haml +++ b/app/views/activities/edit.html.haml @@ -2,6 +2,9 @@ Edit your activity %small - = link_to "View activity page", @activity + = link_to "View the activity page", @activity + %strong or + = link_to "create a new activity", new_activity_path + = render "form" From d83de68234dc178ca96dc8ca921023078364e1c7 Mon Sep 17 00:00:00 2001 From: pietia Date: Wed, 5 Jun 2013 18:51:45 +0200 Subject: [PATCH 179/499] better validations - dates --- app/controllers/application_controller.rb | 4 +--- app/models/activity.rb | 7 ++++--- app/models/event.rb | 1 + config/initializers/event.rb | 6 +++--- db/seeds.rb | 4 ++-- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 26c2b5b2..29c798b2 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -10,9 +10,7 @@ class ApplicationController < ActionController::Base rescue_from ActiveRecord::RecordNotFound, :with => :rescue_record_not_found def current_event - @current_event ||= begin - Rails.env.production? ? EVENT : Event.new(Settings.event.name, Settings.event.start_time, Settings.event.end_time) - end + @current_event ||= Event.new(Settings.event.name, Settings.event.start_time, Settings.event.end_time) end def not_found diff --git a/app/models/activity.rb b/app/models/activity.rb index db85466b..67dc10df 100644 --- a/app/models/activity.rb +++ b/app/models/activity.rb @@ -8,9 +8,9 @@ class Activity < ActiveRecord::Base has_many :participations, :dependent => :destroy has_many :participants, through: :participations, class_name: "User" - validates :start_time, presence: true, allow_blank: false, allow_nil: true - validates :end_time, presence: true, allow_blank: false, allow_nil: true - validates :anytime, inclusion: {in: [true, false]} + validates :start_time, presence: true, allow_blank: false, if: ->{ !anytime? } + validates :end_time, presence: true, allow_blank: false, if: ->{ !anytime? } + validates :anytime, presence: true, allow_blank: false, allow_nil: false, if: ->{ start_time.blank? && end_time.blank? } validates :name, presence: true, allow_blank: false, uniqueness: true validates :location, presence: true, allow_blank: false validates :limit_of_participants, numericality: {greater_than: 0}, allow_nil: true @@ -49,6 +49,7 @@ def full_by end def today? + return true if anytime? t = Time.now.end_of_day t > start_time && t < end_time end diff --git a/app/models/event.rb b/app/models/event.rb index 99fa5834..60d80a3d 100644 --- a/app/models/event.rb +++ b/app/models/event.rb @@ -32,6 +32,7 @@ def recent_activities def all_activities fetch_all_activities end + alias_method :activities, :all_activities private diff --git a/config/initializers/event.rb b/config/initializers/event.rb index 54569d16..192947e6 100644 --- a/config/initializers/event.rb +++ b/config/initializers/event.rb @@ -1,3 +1,3 @@ -if Rails.env.production? - ::EVENT = Event.new(Settings.event.name, Settings.event.start_time, Settings.event.end_time) -end +#if Rails.env.production? +# ::EVENT = Event.new(Settings.event.name, Settings.event.start_time, Settings.event.end_time) +#end diff --git a/db/seeds.rb b/db/seeds.rb index 552896a0..f8aa3fb0 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -1,5 +1,5 @@ User.transaction do |tx| - EVENT = Event.new(Settings.event.name, Settings.event.start_time, Settings.event.end_time) + event = Event.new(Settings.event.name, Settings.event.start_time, Settings.event.end_time) ultra_secure_password = "qweqweqwe" creator = User.new( @@ -16,7 +16,7 @@ participant.password = participant.password_confirmation = ultra_secure_password participant.save! - activity = EVENT.new_activity(creator, + activity = event.new_activity(creator, name: "Party!", start_time: 1.day.from_now.to_time, end_time: 1.day.from_now.to_time + 4.hours, From 11bdaf213315ff47b634d0ac0f2d2fb3ad26fb73 Mon Sep 17 00:00:00 2001 From: pietia Date: Wed, 5 Jun 2013 22:54:59 +0200 Subject: [PATCH 180/499] quickfix - render activities#show properly --- app/decorators/activity_decorator.rb | 12 ++++++------ app/models/activity.rb | 4 ++++ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/app/decorators/activity_decorator.rb b/app/decorators/activity_decorator.rb index 3c115a50..74e73b1a 100644 --- a/app/decorators/activity_decorator.rb +++ b/app/decorators/activity_decorator.rb @@ -30,13 +30,13 @@ def requirements_markdown end def room_left - left = object.limit_of_participants - object.participations_count - if left == 1 - "Quick, only one place left." - elsif left > 0 - "Still room for #{left} people" + if object.anybody_can_join? then "" else - "Sorry. All places are gone." + left = object.limit_of_participants - object.participations_count + if left == 1 then "Quick, only one place left." + elsif left > 0 then "Still room for #{left} people" + else "Sorry. All places are gone." + end end end diff --git a/app/models/activity.rb b/app/models/activity.rb index 67dc10df..4b2598e3 100644 --- a/app/models/activity.rb +++ b/app/models/activity.rb @@ -48,6 +48,10 @@ def full_by limit_of_participants.nil? ? 0 : [100.0 * participations_count / limit_of_participants.to_f, 100.0].min end + def anybody_can_join? + limit_of_participants.nil? + end + def today? return true if anytime? t = Time.now.end_of_day From 48cb1ec0d7ad150d35db3bf2f89858c3a2ed4f20 Mon Sep 17 00:00:00 2001 From: pietia Date: Wed, 5 Jun 2013 23:17:55 +0200 Subject: [PATCH 181/499] participations_count can't be null --- .../20130605211503_participations_count_cant_be_null.rb | 5 +++++ db/schema.rb | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) create mode 100644 db/migrate/20130605211503_participations_count_cant_be_null.rb diff --git a/db/migrate/20130605211503_participations_count_cant_be_null.rb b/db/migrate/20130605211503_participations_count_cant_be_null.rb new file mode 100644 index 00000000..b06ed0e5 --- /dev/null +++ b/db/migrate/20130605211503_participations_count_cant_be_null.rb @@ -0,0 +1,5 @@ +class ParticipationsCountCantBeNull < ActiveRecord::Migration + def change + change_column(:activities, :participations_count, :integer, default: 0, null: false) + end +end diff --git a/db/schema.rb b/db/schema.rb index d53c1872..fe53ce56 100644 --- a/db/schema.rb +++ b/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: 20130530184628) do +ActiveRecord::Schema.define(version: 20130605211503) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -25,7 +25,7 @@ t.datetime "created_at" t.datetime "updated_at" t.integer "creator_id" - t.integer "participations_count", default: 0 + t.integer "participations_count", default: 0, null: false t.boolean "anytime", default: true, null: false t.text "requirements" t.datetime "end_time" From f46c2afa4dd2400e05f876801123650a1bdf98c7 Mon Sep 17 00:00:00 2001 From: pietia Date: Thu, 6 Jun 2013 00:34:05 +0200 Subject: [PATCH 182/499] use 24h time --- app/assets/javascripts/initializers.coffee | 4 ++-- config/locales/en.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/assets/javascripts/initializers.coffee b/app/assets/javascripts/initializers.coffee index cdb43e6c..63857f0d 100644 --- a/app/assets/javascripts/initializers.coffee +++ b/app/assets/javascripts/initializers.coffee @@ -23,8 +23,8 @@ $ -> $("#activity_#{target}_3i").val(day) $('.time-capture').pickatime - format: 'h:i' - formatSubmit: 'h:i' + format: 'HH:i' + formatSubmit: 'HH:i' onSet: (e)-> target = @$node.data 'target' [hours, minutes] = [parseInt((e.select / 60), 10), e.select % 60] diff --git a/config/locales/en.yml b/config/locales/en.yml index 78ce443c..4128286d 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -1,7 +1,7 @@ en: time: formats: - time_only: "%k:%M" + time_only: "%H:%M" date_only: "%d-%m-%Y" #date_only: "%a, %-d.%-m" long: "%a, %-d.%-m" From 51babfce711f5b94f2d4ecf5f37500f8216fce8e Mon Sep 17 00:00:00 2001 From: pietia Date: Thu, 6 Jun 2013 09:41:36 +0200 Subject: [PATCH 183/499] quickfix - counter cache --- app/decorators/activity_decorator.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/decorators/activity_decorator.rb b/app/decorators/activity_decorator.rb index 74e73b1a..3605dfee 100644 --- a/app/decorators/activity_decorator.rb +++ b/app/decorators/activity_decorator.rb @@ -32,7 +32,7 @@ def requirements_markdown def room_left if object.anybody_can_join? then "" else - left = object.limit_of_participants - object.participations_count + left = [[object.limit_of_participants - object.participations_count, object.participations_count].min, 0].max if left == 1 then "Quick, only one place left." elsif left > 0 then "Still room for #{left} people" else "Sorry. All places are gone." From a7f389e5c1fd8ba78e7193f364a1811fe7057b25 Mon Sep 17 00:00:00 2001 From: pietia Date: Thu, 6 Jun 2013 09:54:46 +0200 Subject: [PATCH 184/499] allow to pass name in registration form --- app/controllers/registrations_controller.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/controllers/registrations_controller.rb b/app/controllers/registrations_controller.rb index cd79e493..4d4a9697 100644 --- a/app/controllers/registrations_controller.rb +++ b/app/controllers/registrations_controller.rb @@ -35,6 +35,10 @@ def build_resource(*args) end end + def sign_up_params + params.require(:user).permit(:name, :email, :password, :password_confirmation) + end + def update_resource(params) if resource.no_oauth_connected? paramz = params.require(:user).permit(:name, :email, :password, :password_confirmation, :current_password) From eb96dfa710d312f781f14244e842764f7a0a7314 Mon Sep 17 00:00:00 2001 From: pietia Date: Thu, 6 Jun 2013 10:28:49 +0200 Subject: [PATCH 185/499] don't force users to provide current password --- app/controllers/registrations_controller.rb | 14 ++++---------- app/models/user.rb | 11 +++++++++++ app/views/registrations/edit.html.haml | 1 - 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/app/controllers/registrations_controller.rb b/app/controllers/registrations_controller.rb index 4d4a9697..61b3428b 100644 --- a/app/controllers/registrations_controller.rb +++ b/app/controllers/registrations_controller.rb @@ -10,7 +10,7 @@ def update self.resource = resource_class.to_adapter.get!(send(:"current_#{resource_name}").to_key) prev_unconfirmed_email = resource.unconfirmed_email if resource.respond_to?(:unconfirmed_email) - if update_resource(params) + if resource.update_without_password(editable_params) if is_navigational_format? flash_key = update_needs_confirmation?(resource, prev_unconfirmed_email) ? :update_needs_confirmation : :updated @@ -36,17 +36,11 @@ def build_resource(*args) end def sign_up_params - params.require(:user).permit(:name, :email, :password, :password_confirmation) + editable_params end - def update_resource(params) - if resource.no_oauth_connected? - paramz = params.require(:user).permit(:name, :email, :password, :password_confirmation, :current_password) - resource.update_with_password(paramz) - else - paramz = params.require(:user).permit(:name, :email) - resource.update_without_password(paramz) - end + def editable_params + params.require(:user).permit(:name, :email, :password, :password_confirmation) end end diff --git a/app/models/user.rb b/app/models/user.rb index d692ad40..e30c8858 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -35,6 +35,17 @@ def apply_provider_handle(omniauth) end end + def update_without_password(params, *options) + if params[:password].blank? + params.delete(:password) + params.delete(:password_confirmation) + end + + result = update_attributes(params, *options) + clean_up_passwords + result + end + def password_required? !any_oauth_connected? && super end diff --git a/app/views/registrations/edit.html.haml b/app/views/registrations/edit.html.haml index d635da91..3c99af1b 100644 --- a/app/views/registrations/edit.html.haml +++ b/app/views/registrations/edit.html.haml @@ -15,7 +15,6 @@ - if @user.no_oauth_connected? = f.input :password, :required => true = f.input :password_confirmation, :required => true - = f.input :current_password, :required => true - if @user.connected_with_github? = github_link(@user) From 09882051606dc89f05c7cec8f5eca4d696dc79c1 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Thu, 6 Jun 2013 11:33:58 +0300 Subject: [PATCH 186/499] Console check. --- app/assets/javascripts/initializers.coffee | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/assets/javascripts/initializers.coffee b/app/assets/javascripts/initializers.coffee index 63857f0d..997a6cef 100644 --- a/app/assets/javascripts/initializers.coffee +++ b/app/assets/javascripts/initializers.coffee @@ -1,6 +1,6 @@ $ -> $(document).ajaxError (e, xhr) -> - console.error "AJAX ERROR!", arguments + console.error "AJAX ERROR!", arguments if console?.error? window.location.replace(App.paths.login) if xhr.status == 401 # show how "full" an activity is @@ -8,6 +8,7 @@ $ -> $('#activities img.progress').each -> setTimeout showProgress, Math.random() * 1000 + 500, @ + $('#activity img.progress').progress(strokeWidth: 12) $('.date-capture').pickadate min: new Date(App.event.startTime) From ce812e7f492f5e5e28da746b6d72d5b6c37ed5e2 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Thu, 6 Jun 2013 11:34:12 +0300 Subject: [PATCH 187/499] WIP activity/show. --- .../stylesheets/partials/_activities.sass | 3 + .../partials/activities/_show.sass | 73 +++++++++++++++++++ app/views/activities/show.html.haml | 26 +++++-- 3 files changed, 97 insertions(+), 5 deletions(-) create mode 100644 app/assets/stylesheets/partials/activities/_show.sass diff --git a/app/assets/stylesheets/partials/_activities.sass b/app/assets/stylesheets/partials/_activities.sass index 17f30ccc..88bd0c07 100644 --- a/app/assets/stylesheets/partials/_activities.sass +++ b/app/assets/stylesheets/partials/_activities.sass @@ -3,6 +3,9 @@ body.activities @import "activities/filters" @import "activities/index" + &.show + @import "activities/show" + &.new, &.edit, &.create, &.update @import "activities/new" diff --git a/app/assets/stylesheets/partials/activities/_show.sass b/app/assets/stylesheets/partials/activities/_show.sass new file mode 100644 index 00000000..ea534491 --- /dev/null +++ b/app/assets/stylesheets/partials/activities/_show.sass @@ -0,0 +1,73 @@ +#activity + +clearfix + + header + text-align: center + margin: 0 0 3em 0 + + h2 + margin: 0 + +custom-sans(medium) + + p + strong + +custom-sans(medium) + + section + width: percentage(1/3) + float: left + position: relative + + &.description + +custom-serif + //text-align: justify + font-size: 1.1em + line-height: 1.35em + + .wrapper + margin-right: 50px + + &.overview + text-align: center + + img.progress + width: 250px + height: 250px + border-radius: 125px + margin: 12px + + .wrapper + width: 274px + margin: 0 auto + + p + font-size: 1.5em + + a + display: inline-block + + &.details + dl.wrapper + margin-left: 50px + +clearfix + border-bottom: 1px solid $light-gray + + dt, dd + float: left + line-height: 3em + font-size: 1.1em + line-height: 1.35em + padding: 1em 0 + width: 50% + border-top: 1px solid $light-gray + + dt + +custom-sans(medium) + clear: left + + dd + +custom-serif + margin: 0 + + + diff --git a/app/views/activities/show.html.haml b/app/views/activities/show.html.haml index afd3dc36..b0c82118 100644 --- a/app/views/activities/show.html.haml +++ b/app/views/activities/show.html.haml @@ -1,17 +1,33 @@ -%article +%article#activity %header %h2 = @activity.name %p Organized by - = @activity.creator_name + %strong= @activity.creator_name + + .action + - case @activity.relation_ship_with(current_user) + - when "owner" + = link_to edit_activity_path(@activity), title: t("activities.edit.title") do + %span + = t("activities.edit.label") + - when "participant" + = button_to activity_participation_path(@activity), method: :delete, remote: true, title: t("activities.leave.title") do + %span + = t("activities.leave.label") + - else + = button_to activity_participation_path(@activity), method: :post, remote: true, title: t("activities.join.title") do + %span + = t("activities.join.label") %section.description - = @activity.description_markdown + .wrapper + = @activity.description_markdown %section.overview %figure - = image_tag "dummies/activity.jpg", alt: @activity.name, class: "progress", data: { progress: @activity.full_by } + = image_tag @activity.image_url, alt: @activity.name, class: "progress", data: { progress: @activity.full_by } %p = @activity.room_left %a.participants(href="#" title="") @@ -19,7 +35,7 @@ Participants %section.details - %dl + %dl.wrapper - if @activity.anytime? %dt Time & Date From cfd4fc16d91c3ac099cd691c52cc5b22b9c4cc49 Mon Sep 17 00:00:00 2001 From: pietia Date: Thu, 6 Jun 2013 12:05:38 +0200 Subject: [PATCH 188/499] one event per app - let's use Singleton then --- app/controllers/application_controller.rb | 2 +- app/models/event.rb | 9 +++++++-- spec/factories/activities.rb | 1 + 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 29c798b2..7b3f83b6 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -10,7 +10,7 @@ class ApplicationController < ActionController::Base rescue_from ActiveRecord::RecordNotFound, :with => :rescue_record_not_found def current_event - @current_event ||= Event.new(Settings.event.name, Settings.event.start_time, Settings.event.end_time) + @current_event ||= Event.instance end def not_found diff --git a/app/models/event.rb b/app/models/event.rb index 60d80a3d..d503acd9 100644 --- a/app/models/event.rb +++ b/app/models/event.rb @@ -1,10 +1,15 @@ class Event + include Singleton + attr_writer :activity_source # DI attr_reader :name, :start_time, :end_time - def initialize(name, start_time, end_time, + def initialize( + name = Settings.event.name, + start_time = Settings.event.start_time, + end_time = Settings.event.end_time, recent_activities_fetcher = ->{ Activity.recent }, - all_activities_fetcher = ->{ Activity.all_activities }) + all_activities_fetcher = ->{ Activity.all_activities }) @name, @start_time, @end_time = name, start_time, end_time @recent_activities_fetcher = recent_activities_fetcher @all_activities_fetcher = all_activities_fetcher diff --git a/spec/factories/activities.rb b/spec/factories/activities.rb index 4e7ae537..782a9c54 100644 --- a/spec/factories/activities.rb +++ b/spec/factories/activities.rb @@ -7,5 +7,6 @@ end_time "2013/12/13 03:00" location "Ballroom" creator { FactoryGirl.create(:user) } + event { Event.instance } end end From 655890e8bd3a75c81826721a58edd218d743f7fb Mon Sep 17 00:00:00 2001 From: pietia Date: Thu, 6 Jun 2013 12:06:31 +0200 Subject: [PATCH 189/499] use singleton in seed --- db/seeds.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/db/seeds.rb b/db/seeds.rb index f8aa3fb0..05b58d5f 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -1,5 +1,5 @@ User.transaction do |tx| - event = Event.new(Settings.event.name, Settings.event.start_time, Settings.event.end_time) + event = Event.instance ultra_secure_password = "qweqweqwe" creator = User.new( From 1db3de4ff648826bc6cf66e9d2838c624cd01a8c Mon Sep 17 00:00:00 2001 From: pietia Date: Thu, 6 Jun 2013 12:07:03 +0200 Subject: [PATCH 190/499] unused initializer removed --- config/initializers/event.rb | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 config/initializers/event.rb diff --git a/config/initializers/event.rb b/config/initializers/event.rb deleted file mode 100644 index 192947e6..00000000 --- a/config/initializers/event.rb +++ /dev/null @@ -1,3 +0,0 @@ -#if Rails.env.production? -# ::EVENT = Event.new(Settings.event.name, Settings.event.start_time, Settings.event.end_time) -#end From 8abdc461929fbfbcfb326062da14671f093e9204 Mon Sep 17 00:00:00 2001 From: pietia Date: Thu, 6 Jun 2013 12:10:00 +0200 Subject: [PATCH 191/499] get rid of warning --- spec/other/factories_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/other/factories_spec.rb b/spec/other/factories_spec.rb index 4d43af3c..6fb3a9aa 100644 --- a/spec/other/factories_spec.rb +++ b/spec/other/factories_spec.rb @@ -4,7 +4,7 @@ FactoryGirl.factories.each do |factory| context "with factory for :#{factory.name}" do subject { FactoryGirl.build(factory.name) } - it { should be_valid, subject.errors.full_messages } + it { should be_valid, subject.errors.full_messages.join(", ") } end end end \ No newline at end of file From 10d4f975ebc0cc044eba44d00d4726a1b04fe635 Mon Sep 17 00:00:00 2001 From: pietia Date: Thu, 6 Jun 2013 12:45:18 +0200 Subject: [PATCH 192/499] expect not specify --- spec/models/activity_spec.rb | 4 +--- spec/models/participation_spec.rb | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/spec/models/activity_spec.rb b/spec/models/activity_spec.rb index 046703aa..52556d90 100644 --- a/spec/models/activity_spec.rb +++ b/spec/models/activity_spec.rb @@ -82,7 +82,7 @@ it { should accept_values_for(:name, "Football game" ) } it { should_not accept_values_for(:name, "", nil) } - #specify { FactoryGirl.create(:activity).clone.save! }.to raise(ActiveRecord::RecordInvalid) + specify { expect { FactoryGirl.create(:activity).clone.save! }.to raise_exception(ActiveRecord::RecordInvalid) } it { should accept_values_for(:description, nil, "", "Wear some solid boots!")} @@ -92,10 +92,8 @@ it { should_not accept_values_for(:location, "", nil) } it { should accept_values_for(:start_time, Time.now, nil) } - it { should_not accept_values_for(:start_time, "") } it { should accept_values_for(:end_time, Time.now, nil) } - it { should_not accept_values_for(:end_time, "") } it { should accept_values_for(:limit_of_participants, nil, 12, 100) } it { should_not accept_values_for(:limit_of_participants, -1, 0) } diff --git a/spec/models/participation_spec.rb b/spec/models/participation_spec.rb index 3e84cdb0..9b8622b2 100644 --- a/spec/models/participation_spec.rb +++ b/spec/models/participation_spec.rb @@ -10,9 +10,7 @@ it { should accept_values_for(:activity_id, 1 ) } it { should_not accept_values_for(:activity_id, "", nil) } - specify { - FactoryGirl.create(:participation).clone.save! - }.to raise(ActiveRecord::RecordInvalid) + specify { expect { FactoryGirl.create(:participation).clone.save! }.to raise_exception(ActiveRecord::RecordInvalid) } end From 508b518a3ed26989a225a68b8a46a2796e86b21d Mon Sep 17 00:00:00 2001 From: pietia Date: Thu, 6 Jun 2013 12:52:12 +0200 Subject: [PATCH 193/499] didn't really like it --- app/controllers/application_controller.rb | 2 +- app/models/event.rb | 2 -- db/seeds.rb | 2 +- spec/factories/activities.rb | 2 +- spec/models/activity_spec.rb | 2 +- 5 files changed, 4 insertions(+), 6 deletions(-) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 7b3f83b6..75aa0387 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -10,7 +10,7 @@ class ApplicationController < ActionController::Base rescue_from ActiveRecord::RecordNotFound, :with => :rescue_record_not_found def current_event - @current_event ||= Event.instance + @current_event ||= Event.new end def not_found diff --git a/app/models/event.rb b/app/models/event.rb index d503acd9..4a13b4ab 100644 --- a/app/models/event.rb +++ b/app/models/event.rb @@ -1,6 +1,4 @@ class Event - include Singleton - attr_writer :activity_source # DI attr_reader :name, :start_time, :end_time diff --git a/db/seeds.rb b/db/seeds.rb index 05b58d5f..5b717940 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -1,5 +1,5 @@ User.transaction do |tx| - event = Event.instance + event = Event.new ultra_secure_password = "qweqweqwe" creator = User.new( diff --git a/spec/factories/activities.rb b/spec/factories/activities.rb index 782a9c54..03ae0e0a 100644 --- a/spec/factories/activities.rb +++ b/spec/factories/activities.rb @@ -7,6 +7,6 @@ end_time "2013/12/13 03:00" location "Ballroom" creator { FactoryGirl.create(:user) } - event { Event.instance } + event { Event.new } end end diff --git a/spec/models/activity_spec.rb b/spec/models/activity_spec.rb index 52556d90..6cca10d7 100644 --- a/spec/models/activity_spec.rb +++ b/spec/models/activity_spec.rb @@ -42,7 +42,7 @@ subject { Activity.recent } before do - Activity.stub_chain(:where, :limit).and_return(recent_activities) + Activity.stub_chain(:where, :limit, :order).and_return(recent_activities) end it { should == recent_activities } From a1250cd89a4319149b641641db866c145a13aeda Mon Sep 17 00:00:00 2001 From: pietia Date: Thu, 6 Jun 2013 13:06:30 +0200 Subject: [PATCH 194/499] updated specs: all activities fetcher --- app/models/event.rb | 2 +- spec/models/event_spec.rb | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/app/models/event.rb b/app/models/event.rb index 4a13b4ab..b19f46f9 100644 --- a/app/models/event.rb +++ b/app/models/event.rb @@ -1,5 +1,5 @@ class Event - attr_writer :activity_source # DI + attr_writer :activity_source, :all_activities_fetcher, :recent_activities_fetcher # DI attr_reader :name, :start_time, :end_time def initialize( diff --git a/spec/models/event_spec.rb b/spec/models/event_spec.rb index 4c27a148..2be89c64 100644 --- a/spec/models/event_spec.rb +++ b/spec/models/event_spec.rb @@ -26,9 +26,13 @@ describe "#activities" do let(:activities) { [mock(:activity1), mock(:activity2)] } - let(:event) { Event.new("Conf", ->{ activities }) } + let(:event) { Event.new } subject { event.activities } + before do + event.all_activities_fetcher = ->{ activities } + end + it { should == activities } end From 5d6fd4b9fa309dfeca8d9cb72cfe59fbb68faa80 Mon Sep 17 00:00:00 2001 From: pietia Date: Thu, 6 Jun 2013 13:09:30 +0200 Subject: [PATCH 195/499] test basic settings --- spec/lib/settings_spec.rb | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/spec/lib/settings_spec.rb b/spec/lib/settings_spec.rb index ebe866eb..5377fca0 100644 --- a/spec/lib/settings_spec.rb +++ b/spec/lib/settings_spec.rb @@ -5,4 +5,16 @@ subject { Settings } its(:host) { should_not be_blank } + its(:event) { should_not be_blank } + + describe "#event" do + + subject { Settings.event } + + its(:name) { should_not be_blank } + its(:start_time) { should be_a_kind_of(Time) } + its(:end_time) { should be_a_kind_of(Time) } + + end + end \ No newline at end of file From b9d43adf415ab881962ed888f5311a2b08f26678 Mon Sep 17 00:00:00 2001 From: pietia Date: Thu, 6 Jun 2013 13:19:16 +0200 Subject: [PATCH 196/499] redirect to account when updating --- app/controllers/registrations_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/registrations_controller.rb b/app/controllers/registrations_controller.rb index 61b3428b..939a12de 100644 --- a/app/controllers/registrations_controller.rb +++ b/app/controllers/registrations_controller.rb @@ -20,7 +20,7 @@ def update respond_with resource, :location => after_update_path_for(resource) else clean_up_passwords resource - respond_with resource + respond_with(resource, location: edit_user_registration_path) end end From 12e47bcb4ae57c88574a2e1fbfb21eaea87d1888 Mon Sep 17 00:00:00 2001 From: pietia Date: Thu, 6 Jun 2013 13:30:51 +0200 Subject: [PATCH 197/499] updated specs for activities_controller --- app/controllers/activities_controller.rb | 2 +- spec/controllers/activities_controller_spec.rb | 13 +++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/app/controllers/activities_controller.rb b/app/controllers/activities_controller.rb index 83011856..9c22bda2 100644 --- a/app/controllers/activities_controller.rb +++ b/app/controllers/activities_controller.rb @@ -36,7 +36,7 @@ def update def destroy @activity.destroy - respond_with(@activity) + respond_with(@activity, location: activities_path) end private diff --git a/spec/controllers/activities_controller_spec.rb b/spec/controllers/activities_controller_spec.rb index 39e82dbb..ab82bf67 100644 --- a/spec/controllers/activities_controller_spec.rb +++ b/spec/controllers/activities_controller_spec.rb @@ -14,6 +14,7 @@ describe "#show" do subject { get :show, {id: activity.id} } + it { should render_template(:show) } its(:status){ should == 200 } end @@ -26,12 +27,12 @@ context "valid parameters" do let(:params) { {activity: {name: "Pool party", start_time: 2.days.from_now.to_s, end_time: 2.days.from_now.to_s, location: "Pool" }} } - its(:status) { should == 201 } + it { should redirect_to activities_path } end context "invalid parameters" do let(:params) { {activity: {x: 10}} } - its(:status) { should == 422 } + it { should render_template(:new) } end end @@ -45,12 +46,12 @@ context "valid parameters" do let(:params) { {id: activity.id, activity: {name: "Pool party", start_time: 2.days.from_now.to_s, end_time: 3.days.from_now.to_s, location: "Pool" }} } - its(:status) { should == 204 } + it { should redirect_to edit_activity_path(activity) } end context "invalid parameters" do - let(:params) { {id: activity.id, activity: {start_time: ""}} } - its(:status) { should == 422 } + let(:params) { {id: activity.id, activity: {location: ""}} } + it { should render_template(:edit) } end end @@ -62,7 +63,7 @@ sign_in(user) end - its(:status) { should == 204 } + it { should redirect_to activities_path } end end \ No newline at end of file From de5a963d1a231f3e77a63b2b8135f79eb02d889f Mon Sep 17 00:00:00 2001 From: Polarblau Date: Thu, 6 Jun 2013 13:47:08 +0300 Subject: [PATCH 198/499] Moving action into partial. Closes #64 --- app/views/activities/_action.html.haml | 14 ++++++++++++++ app/views/activities/_activity.html.haml | 15 +-------------- app/views/activities/show.html.haml | 15 +-------------- 3 files changed, 16 insertions(+), 28 deletions(-) create mode 100644 app/views/activities/_action.html.haml diff --git a/app/views/activities/_action.html.haml b/app/views/activities/_action.html.haml new file mode 100644 index 00000000..b5d8e2b7 --- /dev/null +++ b/app/views/activities/_action.html.haml @@ -0,0 +1,14 @@ +.action + - case type + - when "owner" + = link_to edit_activity_path(activity), title: t("activities.edit.title") do + %span + = t("activities.edit.label") + - when "participant" + = button_to activity_participation_path(activity), method: :delete, remote: true, title: t("activities.leave.title") do + %span + = t("activities.leave.label") + - else + = button_to activity_participation_path(activity), method: :post, remote: true, title: t("activities.join.title") do + %span + = t("activities.join.label") \ No newline at end of file diff --git a/app/views/activities/_activity.html.haml b/app/views/activities/_activity.html.haml index 6cd0422c..7fb2e9d1 100644 --- a/app/views/activities/_activity.html.haml +++ b/app/views/activities/_activity.html.haml @@ -13,20 +13,7 @@ %p.time = activity.time.html_safe - .action - - case type - - when "owner" - = link_to edit_activity_path(activity), title: t("activities.edit.title") do - %span - = t("activities.edit.label") - - when "participant" - = button_to activity_participation_path(activity), method: :delete, remote: true, title: t("activities.leave.title") do - %span - = t("activities.leave.label") - - else - = button_to activity_participation_path(activity), method: :post, remote: true, title: t("activities.join.title") do - %span - = t("activities.join.label") + = render "action", :activity => @activity, :type => type %a.details(href="#{activity_path(activity)}" title="#{ t("activities.more_info.title") }") %span diff --git a/app/views/activities/show.html.haml b/app/views/activities/show.html.haml index b0c82118..9b86f939 100644 --- a/app/views/activities/show.html.haml +++ b/app/views/activities/show.html.haml @@ -6,20 +6,7 @@ Organized by %strong= @activity.creator_name - .action - - case @activity.relation_ship_with(current_user) - - when "owner" - = link_to edit_activity_path(@activity), title: t("activities.edit.title") do - %span - = t("activities.edit.label") - - when "participant" - = button_to activity_participation_path(@activity), method: :delete, remote: true, title: t("activities.leave.title") do - %span - = t("activities.leave.label") - - else - = button_to activity_participation_path(@activity), method: :post, remote: true, title: t("activities.join.title") do - %span - = t("activities.join.label") + = render "action", :activity => @activity, :type => @activity.relation_ship_with(current_user) %section.description .wrapper From 3d5c3909d0c69a8cb5d5508b18c3e99e8ba55888 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Thu, 6 Jun 2013 14:35:37 +0300 Subject: [PATCH 199/499] Translations and small fixes. --- .../partials/activities/_show.sass | 1 - app/controllers/activities_controller.rb | 3 +- app/decorators/activity_decorator.rb | 14 ++--- app/views/activities/_activity.html.haml | 2 +- app/views/activities/_form.html.haml | 37 +++++++------ app/views/activities/edit.html.haml | 10 ++-- app/views/activities/new.html.haml | 2 +- app/views/activities/show.html.haml | 17 +++--- config/locales/en.yml | 54 +++++++++++++++++-- 9 files changed, 94 insertions(+), 46 deletions(-) diff --git a/app/assets/stylesheets/partials/activities/_show.sass b/app/assets/stylesheets/partials/activities/_show.sass index ea534491..c44688b2 100644 --- a/app/assets/stylesheets/partials/activities/_show.sass +++ b/app/assets/stylesheets/partials/activities/_show.sass @@ -20,7 +20,6 @@ &.description +custom-serif - //text-align: justify font-size: 1.1em line-height: 1.35em diff --git a/app/controllers/activities_controller.rb b/app/controllers/activities_controller.rb index 9c22bda2..61a01494 100644 --- a/app/controllers/activities_controller.rb +++ b/app/controllers/activities_controller.rb @@ -42,7 +42,8 @@ def destroy private def load_resource - @activity = current_event.activity(params[:id]).decorate + @activity = current_event.activity(params[:id]) + @activity.decorate if @activity end def sanitized_params diff --git a/app/decorators/activity_decorator.rb b/app/decorators/activity_decorator.rb index 3605dfee..e7108486 100644 --- a/app/decorators/activity_decorator.rb +++ b/app/decorators/activity_decorator.rb @@ -33,9 +33,9 @@ def room_left if object.anybody_can_join? then "" else left = [[object.limit_of_participants - object.participations_count, object.participations_count].min, 0].max - if left == 1 then "Quick, only one place left." - elsif left > 0 then "Still room for #{left} people" - else "Sorry. All places are gone." + if left == 1 then I18n.t("activities.room_left.one") + elsif left > 0 then I18n.t("activities.room_left.none") + else I18n.t("activities.room_left.one") end end end @@ -46,15 +46,15 @@ def time else alpha, omega, out = object.start_time, object.end_time, "" - out << I18n.localize(alpha, format: :long) + out << I18n.l(alpha, format: :long) out << " / " - out << I18n.localize(alpha, format: :time_only) + out << I18n.l(alpha, format: :time_only) out << " – " unless alpha.to_date == omega.to_date - out << I18n.localize(omega, format: :long) + out << I18n.l(omega, format: :long) out << " / " end - out << I18n.localize(omega, format: :time_only) + out << I18n.l(omega, format: :time_only) end end diff --git a/app/views/activities/_activity.html.haml b/app/views/activities/_activity.html.haml index 7fb2e9d1..f27b35e8 100644 --- a/app/views/activities/_activity.html.haml +++ b/app/views/activities/_activity.html.haml @@ -13,7 +13,7 @@ %p.time = activity.time.html_safe - = render "action", :activity => @activity, :type => type + = render "action", :activity => activity, :type => type %a.details(href="#{activity_path(activity)}" title="#{ t("activities.more_info.title") }") %span diff --git a/app/views/activities/_form.html.haml b/app/views/activities/_form.html.haml index 9b94f8e5..aa12c590 100644 --- a/app/views/activities/_form.html.haml +++ b/app/views/activities/_form.html.haml @@ -1,45 +1,48 @@ = form_for @activity, html: { id: "new-activity" } do |f| %label.name - = f.text_field :name, placeholder: "Give your activity a name", class: "title" + = f.text_field :name, placeholder: t("activity_form.name.label"), class: "title" %hr - fancy_datime_select f, :start_time, @activity - - %span.divider> - – - + %span.divider> – - fancy_datime_select f, :end_time, @activity %fieldset.anytime - %span.between or + %span.between + = t("common.or").downcase %label.inline = f.check_box :anytime - Without a specific date/time + = t("activity_form.anytime.hint") %hr %label.location> - = f.text_field :location, placeholder: "Location" - %small Where does your activity take place, where to meet, … + = f.text_field :location, placeholder: t("activity_form.location.label") + %small + = t("activity_form.location.hint") %label.limit-of-participants> = f.number_field :limit_of_participants, placeholder: "5" - %small Max. participants + %small + = t("activity_form.limit_of_participants.hint") %label.requirements - = f.text_area :requirements, placeholder: "Requirements", rows: 6, class: "markdown", title: "You can use markdown here!" - %small What to bring, to wear etc. + = f.text_area :requirements, placeholder: t("activity_form.requirements.label"), rows: 6, class: "markdown", title: t("activity_form.markdown_hint") + %small + = t("activity_form.requirements.hint") %label.description - = f.text_area :description, placeholder: "Description", rows: 8, class: "markdown", title: "You can use markdown here!" - %small Background information, links, etc. + = f.text_area :description, placeholder: t("activity_form.description.label"), rows: 8, class: "markdown", title: t("activity_form.markdown_hint") + %small + = t("activity_form.description.hint") %label.image-url = f.url_field :image_url, placeholder: "URL for an image" - %small Upload an image to e.g. imgur.com and link it here - + %small + = t("activity_form.image_url.hint") + %button(type="submit")> %span> - Save + = t("common.submit.#{controller.action_name}") diff --git a/app/views/activities/edit.html.haml b/app/views/activities/edit.html.haml index 61779212..bcff6408 100644 --- a/app/views/activities/edit.html.haml +++ b/app/views/activities/edit.html.haml @@ -1,10 +1,10 @@ %h2 - Edit your activity + = t("edit_activity.title") %small - = link_to "View the activity page", @activity - %strong or - = link_to "create a new activity", new_activity_path - + = link_to t("activities.more_info.label_long"), @activity + %strong + = t("common.or").downcase + = link_to t("activities.new.label").downcase, new_activity_path = render "form" diff --git a/app/views/activities/new.html.haml b/app/views/activities/new.html.haml index 6a2a797e..baf78510 100644 --- a/app/views/activities/new.html.haml +++ b/app/views/activities/new.html.haml @@ -1,4 +1,4 @@ %h2 - Organize an activity + = t("new_activity.title") = render "form" \ No newline at end of file diff --git a/app/views/activities/show.html.haml b/app/views/activities/show.html.haml index 9b86f939..ec7a6d1c 100644 --- a/app/views/activities/show.html.haml +++ b/app/views/activities/show.html.haml @@ -3,8 +3,7 @@ %h2 = @activity.name %p - Organized by - %strong= @activity.creator_name + = t("activities.organized_by", name: @activity.creator_name).html_safe = render "action", :activity => @activity, :type => @activity.relation_ship_with(current_user) @@ -19,30 +18,30 @@ = @activity.room_left %a.participants(href="#" title="") %span - Participants + = t("activities.participants") %section.details %dl.wrapper - if @activity.anytime? %dt - Time & Date + = t("activities.time_and_date") %dd - Anytime + = t("activities.anytime") - else %dt - Start + = t("activities.start.label_short") %dd = l(@activity.start_time, format: :long) %dt - End + = t("activities.end.label_short") %dd = l(@activity.end_time, format: :long) %dt - Location + = t("activities.location") %dd = @activity.location %dt - Required + = t("activities.required") %dd = @activity.requirements diff --git a/config/locales/en.yml b/config/locales/en.yml index 4128286d..cc39221f 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -5,7 +5,12 @@ en: date_only: "%d-%m-%Y" #date_only: "%a, %-d.%-m" long: "%a, %-d.%-m" - + common: + or: Or + submit: + new: Save + edit: Update + user_nav: account: label: My Account @@ -20,16 +25,17 @@ en: label: Register title: Register new account + # index and shared translations activities: - new: - label: Create an activity - title: Create a new activity filters: today: Today all: All owner: Created by me participant: My activities submit: Filter + new: + label: Create an activity + title: Create a new activity search: placeholder: Search for an activity clear: Clear @@ -44,8 +50,48 @@ en: title: Cancel your participation more_info: label: More info + label_long: View activity details title: Learn more about this activity anytime: Anytime + time_and_date: "Time & Date" + start: + label_short: Start + end: + label_short: End + location: Location + required: Required + participants: Participants + organized_by: "Organized by %{name}" + room_left: + none: Sorry. All places are gone. + one: "Quick, only one place left." + many: "Still room for %{left} people" + + new_activity: + title: Organize an activity + + edit_activity: + title: Edit your activity + + activity_form: + markdown_hint: You can use Markdown here + name: + label: Give your activity a name + description: + label: Description + hint: "Background information, links, etc." + anytime: + hint: Without a specific date/time + location: + label: Location + hint: "Where does your activity take place, where to meet, …" + limit_of_participants: + hint: Max. participants + requirements: + label: Requirements + hint: "What to bring, to wear etc." + image_url: + hint: Upload an image to e.g. imgur.com and link it here footer_nav: about: From e9f68070a98a3111d0865a51fa4b38422d7d3d53 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Thu, 6 Jun 2013 14:40:23 +0300 Subject: [PATCH 200/499] Reverting tentative decorate. --- app/controllers/activities_controller.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/controllers/activities_controller.rb b/app/controllers/activities_controller.rb index 61a01494..9c22bda2 100644 --- a/app/controllers/activities_controller.rb +++ b/app/controllers/activities_controller.rb @@ -42,8 +42,7 @@ def destroy private def load_resource - @activity = current_event.activity(params[:id]) - @activity.decorate if @activity + @activity = current_event.activity(params[:id]).decorate end def sanitized_params From 3f9b76fa71f4384442f74d9af0e01ab8453eb011 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Thu, 6 Jun 2013 14:40:32 +0300 Subject: [PATCH 201/499] Support turbolinks ready. --- app/assets/javascripts/initializers.coffee | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/assets/javascripts/initializers.coffee b/app/assets/javascripts/initializers.coffee index 997a6cef..0f65a713 100644 --- a/app/assets/javascripts/initializers.coffee +++ b/app/assets/javascripts/initializers.coffee @@ -1,4 +1,4 @@ -$ -> +ready = -> $(document).ajaxError (e, xhr) -> console.error "AJAX ERROR!", arguments if console?.error? window.location.replace(App.paths.login) if xhr.status == 401 @@ -75,3 +75,6 @@ $ -> $search.next('a.clear').on 'click', (e)-> e.preventDefault() $search.val('').trigger 'keyup' + +$(document).ready(ready) +$(document).on('page:load', ready) \ No newline at end of file From cfb6893839b3e440f782a93c7690b8b311ba7ff5 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Thu, 6 Jun 2013 15:18:47 +0300 Subject: [PATCH 202/499] Form improvements: * Darker markdown icon * Live preview for image url --- app/assets/images/shared/checking.gif | Bin 0 -> 258 bytes app/assets/images/shared/markdown.png | Bin 1386 -> 1371 bytes app/assets/javascripts/initializers.coffee | 32 +++++++++++++++++- .../stylesheets/partials/activities/_new.sass | 17 ++++++++++ app/views/activities/_form.html.haml | 7 ++-- 5 files changed, 52 insertions(+), 4 deletions(-) create mode 100644 app/assets/images/shared/checking.gif diff --git a/app/assets/images/shared/checking.gif b/app/assets/images/shared/checking.gif new file mode 100644 index 0000000000000000000000000000000000000000..b041a64cc2dd872d044c83c627486778214eca83 GIT binary patch literal 258 zcmZ?wbhEHblwgox_{_lY=+UF+&!4}4|NiUOufKo){{R2~)2B~AeE9J5=T8P~K=D7f zpKD04vtxj(k)8oFBT$XvKV9F%lvIVJN(Jwt#Ny1{%%VgEztpVcjMTD31 Za=o1V z#FWguw0yn1)Dlew#h)w;TnzsibU+q>+`+)C8L{imKZ8@AtM^*GKKoFDMV-MxAkD>r zsmW|b%Ie;yFPmc5y{~!y-@%g!i*6R(4JRNvcmGe&=u7cbJnW#vn85Lv@eG^NsV%Zf V$9VcRre=ERNcEpcQD_SnX{>(p{tRjqm!`<5E!`_7&x1_norJPl7T65#HnZr zlYu7GBy(JfloX0ff>P70QYIHN3B!!Ez@>V!0kc#6Mh-z!1_s8no-U3d6}R5ZviCmh zAaP7sG+f3=)wMb-lPgp>y($lN+6<=iR)^V(+BE;m7JI;8YV= zdr{(&iP?##=RY5C*2{l?=;`!3Pxme^lV}tB8g1<2pwZ%lj;3Vk`6u81{=B}>qJ8_r zr%ZRw3%olN-uHNqbR$#jq2sJoCq5~!k)Lh9=&un|YG8ky zV4QHn$E3fHT%w<>_m}wW=yi8*%dg1w(XaeJneLe=Fnvb!bDO7pc`oOqrB=ty`^4^P zcey$I%bw*MqnrhG4>Wt+RGKpXNmsV~iu%c)Zu`bAlsWXBv#|5+@-4;cHBI)9BkDWv zZgLcSdt_6bfAZvnZ^kjZ*Oz*;yN0dF{o#4{TEr}-is0A%lmBdM{bHx_+QqsldSa2- z-mKZ}EuK-^IHMShv>wk9+CTB3NG{8nXce`mTQ%2(u4G-vGr3eYsBxP7>PgF=O!Qgd zKCx(J&Ls2S@~_KRZBbZrweD26+-%n!k}8E~Blh?3%#VJZ{kSSYz$Iu$w$Q28JjN4k z7lRn%rlrT)&3QUY2F0JiW*uh!X*7V?t5=YaK z=U2mRoqe({RsH;I^I^j2+q)ls%6|9mtmVG<;Vb6PQebjY6hTJ|LxMA<_}S{4Gh_N5 zm{!;^pTFEZ`@_oDyY#~rIH-S!Yx|e-Gnwy!XT|+%Uv6G_E%fGw!MzUs*A{0ARVPh; z_JsHH^UWKRUteYDxnQWNqPHdTUhSWT?aHt6+OEiNyC0K(GW|oAM@&-O%+FK2cVyiO zHx1ik^OS#5%`#T~SGC^BOWTxYwOYIFbgI|5e^PXR?E<$W`Oi~ZH!;Vz9#P#UdhuNM z&8GX0C#^XccW*(*wTJ5p?_Z6OIrB}RcKg~Abg)>(HwYFg|H`QUgH=KBY@ z_wziA`{m?yUp{r#|M}c&dG_Q@X}>sm+fLrdB~NE8$YVNr`-`FclIaV28!F9`g5vxx zgfFz?NUff==D_+a&HJLN{+`N_0p-0#>n53}_k5Lh{iUFDwer;Bxa`OZiJaqG5A0au zGr#-P@q?wul{z*QTvnTsY|Au(dGSu3Jv#iqeimftE`B=8Nu$L{kZYj;GV;3kRqX%+ XuSeM<77Jxb1|aZs^>bP0l+XkKru`bs diff --git a/app/assets/javascripts/initializers.coffee b/app/assets/javascripts/initializers.coffee index 0f65a713..4b3691f4 100644 --- a/app/assets/javascripts/initializers.coffee +++ b/app/assets/javascripts/initializers.coffee @@ -76,5 +76,35 @@ ready = -> e.preventDefault() $search.val('').trigger 'keyup' + # form + checkImageURL = (url, success, error) -> + try + img = new Image() + img.onload = -> success(url) + img.onerror = -> error(url) + img.src = url + catch error + + # form + $('#new-activity #activity_image_url') + .on 'keyup focus blur', -> + $input = $(@) + $preview = $input.next('.preview').addClass('checking') + + error = -> + $input.addClass 'validation-error' if $input.val().length + $preview + .css('backgroundImage', '') + .removeClass 'checking' + success = (url) -> + $input.removeClass 'validation-error' + $preview + .css('backgroundImage', "url(#{url})") + .removeClass 'checking' + checkImageURL @value, success, error + + .trigger('keyup') + + $(document).ready(ready) -$(document).on('page:load', ready) \ No newline at end of file +$(document).on('page:load', ready) diff --git a/app/assets/stylesheets/partials/activities/_new.sass b/app/assets/stylesheets/partials/activities/_new.sass index 1313546e..5f8288da 100644 --- a/app/assets/stylesheets/partials/activities/_new.sass +++ b/app/assets/stylesheets/partials/activities/_new.sass @@ -57,6 +57,23 @@ form#new-activity input width: 85% + &.image-url + .preview + position: absolute + right: 0.5em + top: 0.5em + z-index: 1000 + height: 3em + width: 3em + border-radius: 1.5em + background-position: center + background-size: cover + + &.checking + background-size: auto !important + background-repeat: no-repeat !important + background-image: image-url('shared/checking.gif') !important + button border: none background: none diff --git a/app/views/activities/_form.html.haml b/app/views/activities/_form.html.haml index aa12c590..67108b2a 100644 --- a/app/views/activities/_form.html.haml +++ b/app/views/activities/_form.html.haml @@ -25,7 +25,7 @@ %label.limit-of-participants> = f.number_field :limit_of_participants, placeholder: "5" - %small + %small = t("activity_form.limit_of_participants.hint") %label.requirements @@ -40,9 +40,10 @@ %label.image-url = f.url_field :image_url, placeholder: "URL for an image" - %small + .preview + %small = t("activity_form.image_url.hint") - + %button(type="submit")> %span> = t("common.submit.#{controller.action_name}") From 2a77ed460685ce09821feef73def2aca2d2fe37b Mon Sep 17 00:00:00 2001 From: pietia Date: Thu, 6 Jun 2013 14:18:56 +0200 Subject: [PATCH 203/499] updated controller specs --- app/controllers/participations_controller.rb | 6 +++--- .../participation_controller_spec.rb | 19 +++++++++++-------- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/app/controllers/participations_controller.rb b/app/controllers/participations_controller.rb index 5a4c5647..dbfd8341 100644 --- a/app/controllers/participations_controller.rb +++ b/app/controllers/participations_controller.rb @@ -4,14 +4,14 @@ class ParticipationsController < ApiController def create @participation = current_activity.new_participation(current_user) @participation.save - respond_with(current_activity.reload, @participation, location: activities_path) + respond_with(current_activity, @participation, location: activities_path) end def destroy @participation = current_activity.participation(current_user) authorize!(:destroy, @participation) - @participation.destroy - respond_with(current_activity.reload, @participation, location: activities_path) + puts @participation.destroy + puts respond_with(current_activity, @participation, location: activities_path) end private diff --git a/spec/controllers/participation_controller_spec.rb b/spec/controllers/participation_controller_spec.rb index 0df5f55b..31440933 100644 --- a/spec/controllers/participation_controller_spec.rb +++ b/spec/controllers/participation_controller_spec.rb @@ -5,36 +5,39 @@ let(:activity) { FactoryGirl.create(:activity) } describe "#create" do - context "format :json" do - subject { post :create, {activity_id: activity.id, format: :json} } + context "with :js format" do + subject { post :create, {activity_id: activity.id, format: :js} } before do sign_in(user) end context "valid parameters" do - its(:status) { should == 201 } + it { should render_template(:create) } end context "user is already a participant" do before do - Participation.create(participant: user, activity: activity) + Participation.create!(participant: user, activity: activity) end - its(:status) { should == 422 } + # TODO: moar specs needed + it { should render_template(:create) } end end end describe "#destroy" do - context "format :json" do - subject { delete :destroy, {activity_id: activity.id, format: :json} } + context "with :js format" do + subject { delete :destroy, {activity_id: activity.id, format: :js} } before do + Participation.create!(participant: user, activity: activity) sign_in(user) end - its(:status) { should == 204 } + # TODO: moar specs needed + it { should render_template(:destroy) } end end From 0d6954e2a10b016a7316bdc2e51824f355801be5 Mon Sep 17 00:00:00 2001 From: pietia Date: Thu, 6 Jun 2013 14:20:27 +0200 Subject: [PATCH 204/499] whoops, cleanups --- app/controllers/participations_controller.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/controllers/participations_controller.rb b/app/controllers/participations_controller.rb index dbfd8341..3687207d 100644 --- a/app/controllers/participations_controller.rb +++ b/app/controllers/participations_controller.rb @@ -10,8 +10,8 @@ def create def destroy @participation = current_activity.participation(current_user) authorize!(:destroy, @participation) - puts @participation.destroy - puts respond_with(current_activity, @participation, location: activities_path) + @participation.destroy + respond_with(current_activity, @participation, location: activities_path) end private From 5052e26949e624fb36457b8c26df0c3412759105 Mon Sep 17 00:00:00 2001 From: pietia Date: Thu, 6 Jun 2013 14:22:25 +0200 Subject: [PATCH 205/499] not needed --- config/locales/en.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/config/locales/en.yml b/config/locales/en.yml index cc39221f..03738c30 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -3,7 +3,6 @@ en: formats: time_only: "%H:%M" date_only: "%d-%m-%Y" - #date_only: "%a, %-d.%-m" long: "%a, %-d.%-m" common: or: Or From 9e3f6b427b6ad38591241976ea925cadfab99e12 Mon Sep 17 00:00:00 2001 From: pietia Date: Thu, 6 Jun 2013 14:48:16 +0200 Subject: [PATCH 206/499] Revert "redirect to account when updating" This reverts commit b9d43adf415ab881962ed888f5311a2b08f26678. --- app/controllers/registrations_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/registrations_controller.rb b/app/controllers/registrations_controller.rb index 939a12de..61b3428b 100644 --- a/app/controllers/registrations_controller.rb +++ b/app/controllers/registrations_controller.rb @@ -20,7 +20,7 @@ def update respond_with resource, :location => after_update_path_for(resource) else clean_up_passwords resource - respond_with(resource, location: edit_user_registration_path) + respond_with resource end end From ac477b93b7e9941f78e27bd5d7cdf6d6e72a92a8 Mon Sep 17 00:00:00 2001 From: pietia Date: Thu, 6 Jun 2013 14:51:00 +0200 Subject: [PATCH 207/499] after_update_path_for added --- app/controllers/registrations_controller.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/controllers/registrations_controller.rb b/app/controllers/registrations_controller.rb index 61b3428b..4b1bfed4 100644 --- a/app/controllers/registrations_controller.rb +++ b/app/controllers/registrations_controller.rb @@ -35,6 +35,10 @@ def build_resource(*args) end end + def after_update_path_for(resource) + edit_user_registration_path + end + def sign_up_params editable_params end From 2f3fb48289dc34164ea4ee96d503838c0820b17f Mon Sep 17 00:00:00 2001 From: Polarblau Date: Thu, 6 Jun 2013 15:56:11 +0300 Subject: [PATCH 208/499] Fixing action partial path. --- app/views/activities/_activity.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/activities/_activity.html.haml b/app/views/activities/_activity.html.haml index f27b35e8..9a61e88c 100644 --- a/app/views/activities/_activity.html.haml +++ b/app/views/activities/_activity.html.haml @@ -13,7 +13,7 @@ %p.time = activity.time.html_safe - = render "action", :activity => activity, :type => type + = render "activities/action", :activity => activity, :type => type %a.details(href="#{activity_path(activity)}" title="#{ t("activities.more_info.title") }") %span From c7c234d6cb59104f6e28960621be67a04dacaa95 Mon Sep 17 00:00:00 2001 From: pietia Date: Thu, 6 Jun 2013 15:13:39 +0200 Subject: [PATCH 209/499] added very basic notifications area --- app/assets/javascripts/initializers.coffee | 5 +++++ app/views/partials/_header.html.haml | 2 ++ app/views/partials/_notifications.html.haml | 3 +++ 3 files changed, 10 insertions(+) create mode 100644 app/views/partials/_notifications.html.haml diff --git a/app/assets/javascripts/initializers.coffee b/app/assets/javascripts/initializers.coffee index 4b3691f4..edd93430 100644 --- a/app/assets/javascripts/initializers.coffee +++ b/app/assets/javascripts/initializers.coffee @@ -106,5 +106,10 @@ ready = -> .trigger('keyup') + $('#notifications').fadeOut('slow') + + $('#notifications').bind 'click', -> + $(this).fadeOut('slow') + $(document).ready(ready) $(document).on('page:load', ready) diff --git a/app/views/partials/_header.html.haml b/app/views/partials/_header.html.haml index ef7296e1..a10d3990 100644 --- a/app/views/partials/_header.html.haml +++ b/app/views/partials/_header.html.haml @@ -1,5 +1,7 @@ %header#header + = render partial: "partials/notifications" + - if current_user %nav.user-nav %ul diff --git a/app/views/partials/_notifications.html.haml b/app/views/partials/_notifications.html.haml new file mode 100644 index 00000000..7d4e7faa --- /dev/null +++ b/app/views/partials/_notifications.html.haml @@ -0,0 +1,3 @@ +#notifications + - flash.each do |key, value| + %div{ :class => key }= value \ No newline at end of file From f28670b3bc2301620d193074a99a1ac11cea3483 Mon Sep 17 00:00:00 2001 From: pietia Date: Thu, 6 Jun 2013 15:28:55 +0200 Subject: [PATCH 210/499] search and filter are acceptable --- app/controllers/activities_controller.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/controllers/activities_controller.rb b/app/controllers/activities_controller.rb index 9c22bda2..426f1d32 100644 --- a/app/controllers/activities_controller.rb +++ b/app/controllers/activities_controller.rb @@ -58,4 +58,8 @@ def sanitized_params :image_url) end + def query_params + params.permit(:search, :filter) + end + end From e61b8ca041df055050ec5a1140469fc76a20c477 Mon Sep 17 00:00:00 2001 From: pietia Date: Thu, 6 Jun 2013 15:33:13 +0200 Subject: [PATCH 211/499] nice formats --- app/helpers/activity_form_helper.rb | 2 +- config/locales/en.yml | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/app/helpers/activity_form_helper.rb b/app/helpers/activity_form_helper.rb index 73f19d9d..af958494 100644 --- a/app/helpers/activity_form_helper.rb +++ b/app/helpers/activity_form_helper.rb @@ -20,7 +20,7 @@ def capture_attributes(field, model, type = 'date') attributes = { type: 'text', class: "#{type}-capture", - placeholder: l(current_event.send(field), format: format), + placeholder: l(current_event.send(field), format: "nice_#{type}".to_sym), data: { target: field, value: parse_date(model.send(field), format) } } diff --git a/config/locales/en.yml b/config/locales/en.yml index 03738c30..011fff11 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -3,6 +3,8 @@ en: formats: time_only: "%H:%M" date_only: "%d-%m-%Y" + nice_time: "%H:%M" + nice_date: "%a, %-d.%-m" long: "%a, %-d.%-m" common: or: Or From ea0252eb170a4d098276226ca280a7ee2119afcd Mon Sep 17 00:00:00 2001 From: pietia Date: Thu, 6 Jun 2013 15:35:56 +0200 Subject: [PATCH 212/499] removed duplicated code --- app/decorators/activity_decorator.rb | 4 ++-- app/views/activities/show.html.haml | 4 ++-- config/locales/en.yml | 1 - 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/app/decorators/activity_decorator.rb b/app/decorators/activity_decorator.rb index e7108486..9cb5fdca 100644 --- a/app/decorators/activity_decorator.rb +++ b/app/decorators/activity_decorator.rb @@ -46,12 +46,12 @@ def time else alpha, omega, out = object.start_time, object.end_time, "" - out << I18n.l(alpha, format: :long) + out << I18n.l(alpha, format: :nice_date) out << " / " out << I18n.l(alpha, format: :time_only) out << " – " unless alpha.to_date == omega.to_date - out << I18n.l(omega, format: :long) + out << I18n.l(omega, format: :nice_date) out << " / " end out << I18n.l(omega, format: :time_only) diff --git a/app/views/activities/show.html.haml b/app/views/activities/show.html.haml index ec7a6d1c..16982f2a 100644 --- a/app/views/activities/show.html.haml +++ b/app/views/activities/show.html.haml @@ -31,11 +31,11 @@ %dt = t("activities.start.label_short") %dd - = l(@activity.start_time, format: :long) + = l(@activity.start_time, format: :nice_date) %dt = t("activities.end.label_short") %dd - = l(@activity.end_time, format: :long) + = l(@activity.end_time, format: :nice_date) %dt = t("activities.location") %dd diff --git a/config/locales/en.yml b/config/locales/en.yml index 011fff11..79a84003 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -5,7 +5,6 @@ en: date_only: "%d-%m-%Y" nice_time: "%H:%M" nice_date: "%a, %-d.%-m" - long: "%a, %-d.%-m" common: or: Or submit: From ecd7735c76dbe55e6ea7d54026c970aaefdd7935 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Thu, 6 Jun 2013 17:07:39 +0300 Subject: [PATCH 213/499] Fixing translation issue. --- .../partials/activities/_form.sass | 95 +++++++++++++++++++ app/views/activities/_form.html.haml | 2 +- 2 files changed, 96 insertions(+), 1 deletion(-) create mode 100644 app/assets/stylesheets/partials/activities/_form.sass diff --git a/app/assets/stylesheets/partials/activities/_form.sass b/app/assets/stylesheets/partials/activities/_form.sass new file mode 100644 index 00000000..bb94cca9 --- /dev/null +++ b/app/assets/stylesheets/partials/activities/_form.sass @@ -0,0 +1,95 @@ +form#new-activity + margin: 0 150px + + input.date-capture, + input.time-capture + display: none + + html.js & + .select + display: none + + input.date-capture, + input.time-capture + display: inline + + span + &.divider + width: 10% + text-align: center + + fieldset + &.start-time, + &.end-time + display: inline-block + width: 45% + position: relative + + &.end-time + float: right + + &.anytime + span + +custom-sans(bold) + text-transform: uppercase + margin: 0 1em 0 15px + + input + &.date-capture + width: 69% + + &.time-capture + width: 4em + text-align: center + width: 30% + + label + &.location + display: inline-block + width: 80% + + &.limit-of-participants + display: inline-block + text-align: right + width: 20% + + input + width: 85% + + &.image-url + .preview + position: absolute + right: 0.5em + top: 0.5em + z-index: 1000 + height: 3em + width: 3em + border-radius: 1.5em + background-position: center + background-size: cover + + &.checking + background-size: auto !important + background-repeat: no-repeat !important + background-image: image-url('shared/checking.gif') !important + + button + border: none + background: none + +custom-sans(bold) + @extend %smaller-text + text-transform: uppercase + color: $green + display: block + margin: 1em auto 4em + + span + display: block + width: 96px + height: 96px + margin: 0 auto 0.75em + background: transparent image-url("shared/buttons_big.png") no-repeat 0 0 + + &:hover + span + background-position: -96px 0 diff --git a/app/views/activities/_form.html.haml b/app/views/activities/_form.html.haml index 67108b2a..86ab10e3 100644 --- a/app/views/activities/_form.html.haml +++ b/app/views/activities/_form.html.haml @@ -46,4 +46,4 @@ %button(type="submit")> %span> - = t("common.submit.#{controller.action_name}") + = t("common.submit.#{ @activity.new_record? ? "save" : "edit" }") From 2ba6562c2638c3109eb21c362f30e3fd1bc33da0 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Thu, 6 Jun 2013 17:16:30 +0300 Subject: [PATCH 214/499] Translating errors. --- app/models/activity.rb | 6 +++--- config/locales/en.yml | 11 +++++++++-- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/app/models/activity.rb b/app/models/activity.rb index 4b2598e3..54f1f6bf 100644 --- a/app/models/activity.rb +++ b/app/models/activity.rb @@ -84,12 +84,12 @@ def clear_time_frame end def time_frame_order - errors.add(:end_time, "in wrong order") if end_time < start_time + errors.add(:end_time, I18n.t("activities.errors.end_time.before_start")) if end_time < start_time end def during_the_event - errors.add(:start_time, "too early") if start_time < event.start_time - errors.add(:end_time, "too late") if end_time > event.end_time + errors.add(:start_time, I18n.t("activities.errors.end_time.too_early")) if start_time < event.start_time + errors.add(:end_time, I18n.t("activities.errors.end_time.too_late")) if end_time > event.end_time end end diff --git a/config/locales/en.yml b/config/locales/en.yml index 79a84003..c6399753 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -5,12 +5,13 @@ en: date_only: "%d-%m-%Y" nice_time: "%H:%M" nice_date: "%a, %-d.%-m" + common: or: Or submit: new: Save edit: Update - + user_nav: account: label: My Account @@ -62,10 +63,16 @@ en: required: Required participants: Participants organized_by: "Organized by %{name}" - room_left: + room_left: none: Sorry. All places are gone. one: "Quick, only one place left." many: "Still room for %{left} people" + errors: + start_time: + too_early: "outside range, too early" + end_time: + before_start: "can't be before start time" + too_late: "outside range, too late" new_activity: title: Organize an activity From 1616b2333703a6e138fef7093ca887b3bebb3b87 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Thu, 6 Jun 2013 17:18:26 +0300 Subject: [PATCH 215/499] Validation errors for start and end time. --- app/assets/stylesheets/partials/activities/_form.sass | 7 +++++++ app/helpers/activity_form_helper.rb | 3 +++ 2 files changed, 10 insertions(+) diff --git a/app/assets/stylesheets/partials/activities/_form.sass b/app/assets/stylesheets/partials/activities/_form.sass index bb94cca9..cff94676 100644 --- a/app/assets/stylesheets/partials/activities/_form.sass +++ b/app/assets/stylesheets/partials/activities/_form.sass @@ -24,6 +24,13 @@ form#new-activity display: inline-block width: 45% position: relative + + span.validation-error-message + position: relative + float: right + line-height: 1.5em + top: 0 + right: 15px &.end-time float: right diff --git a/app/helpers/activity_form_helper.rb b/app/helpers/activity_form_helper.rb index af958494..98aebe23 100644 --- a/app/helpers/activity_form_helper.rb +++ b/app/helpers/activity_form_helper.rb @@ -8,6 +8,9 @@ def fancy_datime_select(form, field, model) end haml_tag :input, capture_attributes(field, model, 'date') haml_tag :input, capture_attributes(field, model, 'time') + if model.errors[field].any? + haml_tag :span, model.errors[field].join(', '), class: 'validation-error-message' + end end end From 1405f3627b4c2e11fd95619067743e58b9d05e31 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Thu, 6 Jun 2013 17:26:34 +0300 Subject: [PATCH 216/499] WIP: general clean-up. --- app/assets/javascripts/application.js | 2 +- app/assets/stylesheets/_base.sass | 26 +++++++++----- app/assets/stylesheets/_utils.sass | 1 + app/assets/stylesheets/application.css.scss | 3 +- .../stylesheets/partials/_activities.sass | 7 ++-- .../partials/activities/_action.sass | 0 app/assets/stylesheets/utils/_buttons.sass | 33 +++++++++++++++++ app/assets/stylesheets/utils/_forms.sass | 3 +- .../stylesheets/utils/_placeholders.sass | 36 +------------------ app/views/activities/edit.html.haml | 18 +++++----- app/views/activities/new.html.haml | 8 +++-- 11 files changed, 73 insertions(+), 64 deletions(-) create mode 100644 app/assets/stylesheets/partials/activities/_action.sass create mode 100644 app/assets/stylesheets/utils/_buttons.sass diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index e07feab8..f9bf96a6 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -18,4 +18,4 @@ //= require pickadate/picker //= require pickadate/picker.date //= require pickadate/picker.time -//= require initializers +//= require initializers \ No newline at end of file diff --git a/app/assets/stylesheets/_base.sass b/app/assets/stylesheets/_base.sass index 761db236..e4763f2e 100644 --- a/app/assets/stylesheets/_base.sass +++ b/app/assets/stylesheets/_base.sass @@ -17,16 +17,24 @@ a:hover #main position: relative - h2 +article + header text-align: center - +custom-sans(light) - font-size: 2em - margin: 0 0 2em 0 + margin: 0 0 4em 0 - small - font-size: 0.5em - display: block + h2 + +custom-sans(light) + font-size: 2em + margin: 0 + @extend %ornamental-divider-after + + &:after + margin-bottom: 0.25em - strong - +custom-sans(medium) + .meta + text-transform: uppercase + @extend %smaller-text + + strong.divider + +custom-sans(bold) margin: 0 0.5em diff --git a/app/assets/stylesheets/_utils.sass b/app/assets/stylesheets/_utils.sass index cc7fd829..f1c5d0c0 100644 --- a/app/assets/stylesheets/_utils.sass +++ b/app/assets/stylesheets/_utils.sass @@ -2,3 +2,4 @@ @import "utils/fonts" @import "utils/placeholders" @import "utils/forms" +@import "utils/buttons" diff --git a/app/assets/stylesheets/application.css.scss b/app/assets/stylesheets/application.css.scss index 7aab8a23..2557b22f 100644 --- a/app/assets/stylesheets/application.css.scss +++ b/app/assets/stylesheets/application.css.scss @@ -13,5 +13,4 @@ @import "partials/header"; @import "partials/footer"; -@import "partials/activities"; -@import "partials/activities/new"; +@import "partials/activities"; \ No newline at end of file diff --git a/app/assets/stylesheets/partials/_activities.sass b/app/assets/stylesheets/partials/_activities.sass index 88bd0c07..70ffc2d1 100644 --- a/app/assets/stylesheets/partials/_activities.sass +++ b/app/assets/stylesheets/partials/_activities.sass @@ -1,4 +1,6 @@ body.activities + @import "activities/action" + &.index @import "activities/filters" @import "activities/index" @@ -7,7 +9,4 @@ body.activities @import "activities/show" &.new, &.edit, &.create, &.update - @import "activities/new" - - #main - margin: 0 150px + @import "activities/form" \ No newline at end of file diff --git a/app/assets/stylesheets/partials/activities/_action.sass b/app/assets/stylesheets/partials/activities/_action.sass new file mode 100644 index 00000000..e69de29b diff --git a/app/assets/stylesheets/utils/_buttons.sass b/app/assets/stylesheets/utils/_buttons.sass new file mode 100644 index 00000000..4933ffba --- /dev/null +++ b/app/assets/stylesheets/utils/_buttons.sass @@ -0,0 +1,33 @@ +$icon-types: ok $green, add $green, remove $red, edit $yellow +$ypos : 0 +@each $type in $icon-types + %icon-button-#{nth($type, 1)} + color: nth($type, 2) + @extend %smaller-text + line-height: 40px + text-transform: uppercase + border: none + display: inline-block + height: 40px + vertical-align: top + +custom-sans(bold) + + span + width: 40px + height: 40px + margin: 0 0 0 15px + display: inline-block + background: image-url("shared/buttons_small.png") no-repeat + background-position: 0 -40px * $ypos + + &:hover + span + background-position: -40px -40px * $ypos + + %icon-#{nth($type, 1)} + width: 40px + height: 40px + display: inline-block + background: image-url("shared/buttons_small.png") no-repeat + background-position: -40px -40px * $ypos + $ypos: $ypos + 1 \ No newline at end of file diff --git a/app/assets/stylesheets/utils/_forms.sass b/app/assets/stylesheets/utils/_forms.sass index a834e18d..1e2b4864 100644 --- a/app/assets/stylesheets/utils/_forms.sass +++ b/app/assets/stylesheets/utils/_forms.sass @@ -1,5 +1,4 @@ form - input, textarea width: 100% padding: 0.75em 15px 1em @@ -84,4 +83,4 @@ form text-align: right display: block color: $medium-gray - padding: 0.5em 15px + padding: 0.5em 15px \ No newline at end of file diff --git a/app/assets/stylesheets/utils/_placeholders.sass b/app/assets/stylesheets/utils/_placeholders.sass index ac43dbee..1ce046fe 100644 --- a/app/assets/stylesheets/utils/_placeholders.sass +++ b/app/assets/stylesheets/utils/_placeholders.sass @@ -7,40 +7,6 @@ %small-text font-size: 0.75em -$icon-types: ok $green, add $green, remove $red, edit $yellow -$ypos : 0 -@each $type in $icon-types - %icon-button-#{nth($type, 1)} - color: nth($type, 2) - @extend %smaller-text - line-height: 40px - text-transform: uppercase - border: none - display: inline-block - height: 40px - vertical-align: top - +custom-sans(bold) - - span - width: 40px - height: 40px - margin: 0 0 0 15px - display: inline-block - background: image-url("shared/buttons_small.png") no-repeat - background-position: 0 -40px * $ypos - - &:hover - span - background-position: -40px -40px * $ypos - - %icon-#{nth($type, 1)} - width: 40px - height: 40px - display: inline-block - background: image-url("shared/buttons_small.png") no-repeat - background-position: -40px -40px * $ypos - $ypos: $ypos + 1 - =ornamental-divider content: "." text-indent: -999em @@ -86,4 +52,4 @@ $ypos : 0 +custom-sans(medium) &:hover - color: $blue + color: $blue \ No newline at end of file diff --git a/app/views/activities/edit.html.haml b/app/views/activities/edit.html.haml index bcff6408..39881de1 100644 --- a/app/views/activities/edit.html.haml +++ b/app/views/activities/edit.html.haml @@ -1,10 +1,12 @@ -%h2 - = t("edit_activity.title") +%article + %header + %h2 + = t("edit_activity.title") - %small - = link_to t("activities.more_info.label_long"), @activity - %strong - = t("common.or").downcase - = link_to t("activities.new.label").downcase, new_activity_path + %p.meta + = link_to t("activities.more_info.label_long"), @activity + %strong.divider + = t("common.or").downcase + = link_to t("activities.new.label").downcase, new_activity_path -= render "form" + = render "form" \ No newline at end of file diff --git a/app/views/activities/new.html.haml b/app/views/activities/new.html.haml index baf78510..986c2159 100644 --- a/app/views/activities/new.html.haml +++ b/app/views/activities/new.html.haml @@ -1,4 +1,6 @@ -%h2 - = t("new_activity.title") +%article + %header + %h2 + = t("new_activity.title") -= render "form" \ No newline at end of file + = render "form" \ No newline at end of file From 67c4e873e13bad5f0b40f28eca5abec736605acd Mon Sep 17 00:00:00 2001 From: Polarblau Date: Thu, 6 Jun 2013 17:26:58 +0300 Subject: [PATCH 217/499] WIP: More clean-up. --- .../stylesheets/partials/activities/_new.sass | 96 ------------------- 1 file changed, 96 deletions(-) delete mode 100644 app/assets/stylesheets/partials/activities/_new.sass diff --git a/app/assets/stylesheets/partials/activities/_new.sass b/app/assets/stylesheets/partials/activities/_new.sass deleted file mode 100644 index 5f8288da..00000000 --- a/app/assets/stylesheets/partials/activities/_new.sass +++ /dev/null @@ -1,96 +0,0 @@ -form#new-activity - input.date-capture, - input.time-capture - display: none - - html.js & - .select - display: none - - input.date-capture, - input.time-capture - display: inline - - span - &.divider - width: 10% - text-align: center - - fieldset - &.start-time, - &.end-time - display: inline-block - width: 45% - position: relative - - &.start-time - //margin-right: 1% - - &.end-time - float: right - - &.anytime - span - +custom-sans(bold) - text-transform: uppercase - margin: 0 1em 0 15px - - input - &.date-capture - width: 69% - - &.time-capture - width: 4em - text-align: center - width: 30% - - label - &.location - display: inline-block - width: 80% - - &.limit-of-participants - display: inline-block - text-align: right - width: 20% - - input - width: 85% - - &.image-url - .preview - position: absolute - right: 0.5em - top: 0.5em - z-index: 1000 - height: 3em - width: 3em - border-radius: 1.5em - background-position: center - background-size: cover - - &.checking - background-size: auto !important - background-repeat: no-repeat !important - background-image: image-url('shared/checking.gif') !important - - button - border: none - background: none - +custom-sans(bold) - @extend %smaller-text - text-transform: uppercase - color: $green - display: block - margin: 1em auto 4em - - span - display: block - width: 96px - height: 96px - margin: 0 auto 0.75em - background: transparent image-url("shared/buttons_big.png") no-repeat 0 0 - - &:hover - span - background-position: -96px 0 From 714bb6f1ef1656dec6b8a2cc622c0794c00d05e2 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Thu, 6 Jun 2013 17:29:08 +0300 Subject: [PATCH 218/499] Fixing faulty translation key. --- app/views/activities/_form.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/activities/_form.html.haml b/app/views/activities/_form.html.haml index 86ab10e3..effa0aba 100644 --- a/app/views/activities/_form.html.haml +++ b/app/views/activities/_form.html.haml @@ -46,4 +46,4 @@ %button(type="submit")> %span> - = t("common.submit.#{ @activity.new_record? ? "save" : "edit" }") + = t("common.submit.#{ @activity.new_record? ? "new" : "edit" }") From 54268e76e063c6a67219cdeed18cf23df3557553 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Thu, 6 Jun 2013 17:31:00 +0300 Subject: [PATCH 219/499] Adding missing translation. --- app/views/activities/_form.html.haml | 2 +- config/locales/en.yml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/app/views/activities/_form.html.haml b/app/views/activities/_form.html.haml index effa0aba..6fb6a1fa 100644 --- a/app/views/activities/_form.html.haml +++ b/app/views/activities/_form.html.haml @@ -39,7 +39,7 @@ = t("activity_form.description.hint") %label.image-url - = f.url_field :image_url, placeholder: "URL for an image" + = f.url_field :image_url, placeholder: t("activity_form.image_url.label") .preview %small = t("activity_form.image_url.hint") diff --git a/config/locales/en.yml b/config/locales/en.yml index c6399753..d1a42617 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -98,6 +98,7 @@ en: label: Requirements hint: "What to bring, to wear etc." image_url: + label: URL for an image hint: Upload an image to e.g. imgur.com and link it here footer_nav: From 5fef22cd76304511d14c5dfd77102a89f68ade66 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Thu, 6 Jun 2013 17:37:11 +0300 Subject: [PATCH 220/499] Default value for limit_of_participants. --- app/models/activity.rb | 11 ++++++++--- spec/models/activity_spec.rb | 11 ++++++++++- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/app/models/activity.rb b/app/models/activity.rb index 54f1f6bf..97c4b92a 100644 --- a/app/models/activity.rb +++ b/app/models/activity.rb @@ -17,9 +17,10 @@ class Activity < ActiveRecord::Base validate :time_frame_order, if: ->{ !anytime && event && start_time.present? && end_time.present? } validate :during_the_event, if: ->{ !anytime && event && start_time.present? && end_time.present? } validates :event, presence: true - + before_validation :clear_time_frame, if: ->{ anytime } - + after_initialize :set_defaults + class << self def recent(limit = DEFAULT_LIMIT) where("start_time >= :t OR anytime = true", t: 1.month.ago) @@ -82,7 +83,11 @@ def participation_source def clear_time_frame self.start_time, self.end_time = nil, nil end - + + def set_defaults + self.limit_of_participants ||= 10 + end + def time_frame_order errors.add(:end_time, I18n.t("activities.errors.end_time.before_start")) if end_time < start_time end diff --git a/spec/models/activity_spec.rb b/spec/models/activity_spec.rb index 6cca10d7..29a26409 100644 --- a/spec/models/activity_spec.rb +++ b/spec/models/activity_spec.rb @@ -47,7 +47,16 @@ it { should == recent_activities } end - + + describe "#limit_of_participants" do + subject { activity.limit_of_participants } + + context "limit of participants is not set" do + it { should == 10 } + end + end + + describe "#full_by" do subject { activity.full_by } From b5a3764fc0f44eac71fc654107efedd1cdc9cfda Mon Sep 17 00:00:00 2001 From: pietia Date: Thu, 6 Jun 2013 16:38:17 +0200 Subject: [PATCH 221/499] basic no-js search --- app/controllers/activities_controller.rb | 2 +- app/models/activity.rb | 4 ++++ app/models/event.rb | 19 ++++++++++++++++++- 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/app/controllers/activities_controller.rb b/app/controllers/activities_controller.rb index 426f1d32..b7c0132e 100644 --- a/app/controllers/activities_controller.rb +++ b/app/controllers/activities_controller.rb @@ -6,7 +6,7 @@ class ActivitiesController < ApiController authorize_resource only: [:edit, :update, :destroy] def index - @activities = current_event.activities + @activities = current_event.search_activities(current_user, query_params[:search], query_params[:filter]) respond_with(@activities) end diff --git a/app/models/activity.rb b/app/models/activity.rb index 97c4b92a..a3c626ad 100644 --- a/app/models/activity.rb +++ b/app/models/activity.rb @@ -36,6 +36,10 @@ def today where(":t between start_time and end_time", t: Date.current) end + def with_name_like(name) + where("name LIKE :q", q: "%#{name}%") + end + def created_by(user) where(creator_id: user) end diff --git a/app/models/event.rb b/app/models/event.rb index b19f46f9..4f47b34a 100644 --- a/app/models/event.rb +++ b/app/models/event.rb @@ -35,7 +35,24 @@ def recent_activities def all_activities fetch_all_activities end - alias_method :activities, :all_activities + + def search_activities(author, query_string, filter) + query = all_activities + # TODO: consider using squeel in the future (doesn't work well with rails 4.rc1 ...) + query = query.with_name_like(query_string) if query_string.present? + if filter.present? + query = if filter == "today" + query.today + elsif filter == "owner" + query.created_by(author) + elsif filter == "participant" + query.participated_by(author) + else + query + end + end + query + end private From a37ca9fa1ead3097b6d8c8ed31614afec7adff17 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Thu, 6 Jun 2013 17:42:06 +0300 Subject: [PATCH 222/499] Don't adjust fontsize with zoom enabled. --- app/assets/stylesheets/partials/activities/_show.sass | 2 -- app/views/activities/show.html.haml | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/app/assets/stylesheets/partials/activities/_show.sass b/app/assets/stylesheets/partials/activities/_show.sass index c44688b2..02f87bf4 100644 --- a/app/assets/stylesheets/partials/activities/_show.sass +++ b/app/assets/stylesheets/partials/activities/_show.sass @@ -20,7 +20,6 @@ &.description +custom-serif - font-size: 1.1em line-height: 1.35em .wrapper @@ -54,7 +53,6 @@ dt, dd float: left line-height: 3em - font-size: 1.1em line-height: 1.35em padding: 1em 0 width: 50% diff --git a/app/views/activities/show.html.haml b/app/views/activities/show.html.haml index 16982f2a..8bb6fd78 100644 --- a/app/views/activities/show.html.haml +++ b/app/views/activities/show.html.haml @@ -2,7 +2,7 @@ %header %h2 = @activity.name - %p + %p.meta = t("activities.organized_by", name: @activity.creator_name).html_safe = render "action", :activity => @activity, :type => @activity.relation_ship_with(current_user) From 694d37e810d166d512d5c6ff433e57cb6ad360c1 Mon Sep 17 00:00:00 2001 From: pietia Date: Thu, 6 Jun 2013 16:46:53 +0200 Subject: [PATCH 223/499] fixed counters (no-js) --- app/controllers/activities_controller.rb | 6 ++++++ app/views/activities/index.html.haml | 8 ++++---- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/app/controllers/activities_controller.rb b/app/controllers/activities_controller.rb index b7c0132e..9f497f6d 100644 --- a/app/controllers/activities_controller.rb +++ b/app/controllers/activities_controller.rb @@ -7,6 +7,12 @@ class ActivitiesController < ApiController def index @activities = current_event.search_activities(current_user, query_params[:search], query_params[:filter]) + @counters = { + today: Activity.today.count, + all: Activity.count, + participant: Activity.participated_by(current_user).count, + owner: Activity.created_by(current_user).count + } respond_with(@activities) end diff --git a/app/views/activities/index.html.haml b/app/views/activities/index.html.haml index bff056dd..8e2ba127 100644 --- a/app/views/activities/index.html.haml +++ b/app/views/activities/index.html.haml @@ -3,22 +3,22 @@ %input(type="radio" name="filter" value="today") = t("activities.filters.today") %strong - = @activities.today.count + = @counters[:today] %label.all.selected> %input(type="radio" name="filter" value="all") = t("activities.filters.all") %strong - = @activities.count + = @counters[:all] %label.participant> %input(type="radio" name="filter" value="participant") = t("activities.filters.participant") %strong - = @activities.participated_by(current_user).count + = @counters[:participant] %label.owner> %input(type="radio" name="filter" value="owner") = t("activities.filters.owner") %strong - = @activities.created_by(current_user).count + = @counters[:owner] %label.search %input(type="text" name="search" placeholder="#{ t("activities.search.placeholder") }") %a.clear(href="#" title="Reset") From 86c07fd42ba797fc530b567ae238c5386d243867 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Thu, 6 Jun 2013 20:16:06 +0300 Subject: [PATCH 224/499] Update opposite pickers based on input. --- app/assets/javascripts/initializers.coffee | 34 ++++++++++++++++++---- app/helpers/activity_form_helper.rb | 8 +++-- 2 files changed, 35 insertions(+), 7 deletions(-) diff --git a/app/assets/javascripts/initializers.coffee b/app/assets/javascripts/initializers.coffee index edd93430..278a7988 100644 --- a/app/assets/javascripts/initializers.coffee +++ b/app/assets/javascripts/initializers.coffee @@ -16,12 +16,23 @@ ready = -> format: 'ddd, d.m.' formatSubmit: 'dd-mm-yyyy' onSet: (e)-> - date = new Date(e.select) - target = @$node.data 'target' - [day, month, year] = [date.getDate(), date.getMonth() + 1, date.getFullYear()] + target = @$node.data 'target' + {year, month, date} = @get 'select' $("#activity_#{target}_1i").val(year) - $("#activity_#{target}_2i").val(month) - $("#activity_#{target}_3i").val(day) + $("#activity_#{target}_2i").val(month + 1) + $("#activity_#{target}_3i").val(date) + onClose: -> + otherSelector = @$node.data('update') + other = $(otherSelector).pickadate('picker') + {year, month, date, pick} = @get 'select' + otherPick = other.get('select').pick + + if otherSelector.match /end-time/ + other.set 'min', [year, month, date] + other.set 'select', [year, month, date] if otherPick < pick + else + other.set 'max', [year, month, date] + other.set 'select', [year, month, date] if otherPick > pick $('.time-capture').pickatime format: 'HH:i' @@ -32,6 +43,19 @@ ready = -> $("#activity_#{target}_4i").val(if hours < 10 then "0#{hours}" else hours) $("#activity_#{target}_5i").val(minutes) + onClose: -> + otherSelector = @$node.data('update') + other = $(otherSelector).pickatime('picker') + console.log $(otherSelector), $(otherSelector).pickatime('picker') + {hour, mins, pick} = @get 'select' + otherPick = other.get('select').pick + + if otherSelector.match /end-time/ + other.set 'min', [hour, mins] + other.set 'select', [hour, mins] if otherPick < pick + else + other.set 'max', [hour, mins] + other.set 'select', [hour, mins] if otherPick > pick # filters $filters = $('form.filters label:not(.search)') diff --git a/app/helpers/activity_form_helper.rb b/app/helpers/activity_form_helper.rb index 98aebe23..cbcb5053 100644 --- a/app/helpers/activity_form_helper.rb +++ b/app/helpers/activity_form_helper.rb @@ -22,9 +22,13 @@ def capture_attributes(field, model, type = 'date') attributes = { type: 'text', - class: "#{type}-capture", + class: "#{type}-capture #{field.to_s.dasherize}", placeholder: l(current_event.send(field), format: "nice_#{type}".to_sym), - data: { target: field, value: parse_date(model.send(field), format) } + data: { + update: ".#{type}-capture.#{field == :start_time ? 'end' : 'start'}-time", + target: field, + value: parse_date(model.send(field), format) + } } if model.errors[field].any? From 328042bbf1aedba0b8cff1fc00cd9bf87dff86ff Mon Sep 17 00:00:00 2001 From: Polarblau Date: Thu, 6 Jun 2013 21:21:50 +0300 Subject: [PATCH 225/499] Remove console. --- app/assets/javascripts/initializers.coffee | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/assets/javascripts/initializers.coffee b/app/assets/javascripts/initializers.coffee index 278a7988..fe4af6b5 100644 --- a/app/assets/javascripts/initializers.coffee +++ b/app/assets/javascripts/initializers.coffee @@ -42,11 +42,9 @@ ready = -> [hours, minutes] = [parseInt((e.select / 60), 10), e.select % 60] $("#activity_#{target}_4i").val(if hours < 10 then "0#{hours}" else hours) $("#activity_#{target}_5i").val(minutes) - onClose: -> otherSelector = @$node.data('update') other = $(otherSelector).pickatime('picker') - console.log $(otherSelector), $(otherSelector).pickatime('picker') {hour, mins, pick} = @get 'select' otherPick = other.get('select').pick From 5cbeb8a7a1fee38b237c4b10b0ea6e9b1127416b Mon Sep 17 00:00:00 2001 From: Polarblau Date: Fri, 7 Jun 2013 00:13:59 +0300 Subject: [PATCH 226/499] Refactoring buttons into proper placeholders. #52. :sunglasses: --- .../stylesheets/partials/_activities.sass | 9 +- .../partials/activities/_index.sass | 41 ++------- app/assets/stylesheets/utils/_buttons.sass | 88 +++++++++++++++---- .../stylesheets/utils/_placeholders.sass | 2 +- 4 files changed, 84 insertions(+), 56 deletions(-) diff --git a/app/assets/stylesheets/partials/_activities.sass b/app/assets/stylesheets/partials/_activities.sass index 70ffc2d1..5bd61cf7 100644 --- a/app/assets/stylesheets/partials/_activities.sass +++ b/app/assets/stylesheets/partials/_activities.sass @@ -1,6 +1,6 @@ body.activities @import "activities/action" - + &.index @import "activities/filters" @import "activities/index" @@ -8,5 +8,8 @@ body.activities &.show @import "activities/show" - &.new, &.edit, &.create, &.update - @import "activities/form" \ No newline at end of file + &.new, + &.edit, + &.create, + &.update + @import "activities/form" diff --git a/app/assets/stylesheets/partials/activities/_index.sass b/app/assets/stylesheets/partials/activities/_index.sass index a2c2064c..9b6ad356 100644 --- a/app/assets/stylesheets/partials/activities/_index.sass +++ b/app/assets/stylesheets/partials/activities/_index.sass @@ -75,6 +75,7 @@ a#new-activity width: 20px height: 1px border-bottom: 1px solid $light-gray + &:before width: 0 margin: 0 auto @@ -114,26 +115,6 @@ a#new-activity +transform(scale(0.9)) +transition(all 0.35s ease-in-out 0) - a, button - border: none - background: none - +custom-sans(bold) - @extend %smaller-text - text-transform: uppercase - color: $green - display: inline-block - - span - display: block - width: 96px - height: 96px - margin: 0 auto 0.75em - background: transparent image-url("shared/buttons_big.png") no-repeat 0 0 - - &:hover - span - background-position: -96px 0 - &:hover cursor: pointer @@ -176,27 +157,19 @@ a#new-activity +transform(scale(1)) +transition(all 0.15s ease-out 0.15s) - // variations - $types: participant $red ok, owner $yellow edit - $xpos : 0 + a, button + @extend %large-icon-button-ok + // variations, map to icon types + $types: participant ok remove, owner edit edit @each $type in $types - $xpos: $xpos + 1 - &.#{nth($type, 1)} span.icon - @extend %icon-#{nth($type, 3)} + @extend %icon-#{nth($type, 2)} .action a, button - color: nth($type, 2) - - span - background-position: 0 -96px * $xpos - - &:hover - span - background-position: -96px -96px * $xpos + @extend %large-icon-button-#{nth($type, 3)} img.progress width: 150px diff --git a/app/assets/stylesheets/utils/_buttons.sass b/app/assets/stylesheets/utils/_buttons.sass index 4933ffba..1b084ecc 100644 --- a/app/assets/stylesheets/utils/_buttons.sass +++ b/app/assets/stylesheets/utils/_buttons.sass @@ -1,33 +1,85 @@ $icon-types: ok $green, add $green, remove $red, edit $yellow $ypos : 0 + +// just the icon without label +%icon + width: 40px + height: 40px + display: inline-block + background: image-url("shared/buttons_small.png") no-repeat + background-position: -40px -40px * $ypos + +// small buttons with labels aside +%icon-button + color: nth(nth($icon-types, 1), 2) + @extend %smaller-text + line-height: 40px + height: 40px + text-transform: uppercase + border: none + display: inline-block + vertical-align: top + +custom-sans(bold) + + span + width: 40px + height: 40px + margin: 0 0 0 15px + display: inline-block + background: image-url("shared/buttons_small.png") no-repeat + background-position: 0 -40px * $ypos + @each $type in $icon-types %icon-button-#{nth($type, 1)} color: nth($type, 2) - @extend %smaller-text - line-height: 40px - text-transform: uppercase - border: none - display: inline-block - height: 40px - vertical-align: top - +custom-sans(bold) + @extend %icon-button span - width: 40px - height: 40px - margin: 0 0 0 15px - display: inline-block - background: image-url("shared/buttons_small.png") no-repeat background-position: 0 -40px * $ypos &:hover span background-position: -40px -40px * $ypos + // just the icon without label %icon-#{nth($type, 1)} - width: 40px - height: 40px - display: inline-block - background: image-url("shared/buttons_small.png") no-repeat + @extend %icon background-position: -40px -40px * $ypos - $ypos: $ypos + 1 \ No newline at end of file + + $ypos: $ypos + 1 + +// ---------------------------------------------------------------------------- + +$button-types: ok $green, remove $red, edit $yellow +$ypos : 0 + +// large buttons with label beneath +%large-icon-button + border: none + background: none + +custom-sans(bold) + @extend %smaller-text + text-transform: uppercase + display: inline-block + color: nth(nth($button-types, 1), 2) + + span + display: block + width: 96px + height: 96px + margin: 0 auto 0.75em + background: transparent image-url("shared/buttons_big.png") no-repeat 0 -96px * $ypos + +@each $type in $button-types + %large-icon-button-#{nth($type, 1)} + color: nth($type, 2) + @extend %large-icon-button + + span + background-position: 0 -96px * $ypos + + &:hover + span + background-position: -96px -96px * $ypos + + $ypos: $ypos + 1 diff --git a/app/assets/stylesheets/utils/_placeholders.sass b/app/assets/stylesheets/utils/_placeholders.sass index 1ce046fe..fd4d4a68 100644 --- a/app/assets/stylesheets/utils/_placeholders.sass +++ b/app/assets/stylesheets/utils/_placeholders.sass @@ -52,4 +52,4 @@ +custom-sans(medium) &:hover - color: $blue \ No newline at end of file + color: $blue From b149cbb28b9920300927e1b940596c8e6b2aaaa9 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Fri, 7 Jun 2013 00:30:51 +0300 Subject: [PATCH 227/499] Refactoring styles. --- .../partials/activities/_index.sass | 99 ++++++++++--------- app/assets/stylesheets/utils/_buttons.sass | 16 +++ 2 files changed, 66 insertions(+), 49 deletions(-) diff --git a/app/assets/stylesheets/partials/activities/_index.sass b/app/assets/stylesheets/partials/activities/_index.sass index 9b6ad356..d45abd80 100644 --- a/app/assets/stylesheets/partials/activities/_index.sass +++ b/app/assets/stylesheets/partials/activities/_index.sass @@ -8,12 +8,17 @@ a#new-activity +unlisted padding: 1em 0 0 0 + // card variations + $types: participant ok remove, owner edit edit + li display: block +span-columns(2) margin: 0 0 1em 0 text-align: center + // icon in the upper right, + // shows up only if owner or participant span.icon position: absolute top: 15px @@ -21,9 +26,19 @@ a#new-activity opacity: 1 +transition(opacity 0.25s) + // select icon based on card variation + @each $type in $types + &.#{nth($type, 1)} + span.icon + @extend %icon-#{nth($type, 2)} + + // links are actually stacked in this case + // as one card has to support many purposes a border: none + // link to show action, usually covers whole card + // on hover appears as secondary button above title &.details display: block position: absolute @@ -34,18 +49,7 @@ a#new-activity left: 0 span - display: inline-block - background: $medium-gray - color: $white - text-shadow: 0 1px 0 $gray - @extend %small-text - padding: 0.25em 1.5em - text-transform: uppercase - border-radius: 3px - margin: 19em 0 0 - opacity: 0 - +transition(all 0.35s ease-in-out) - +custom-sans(medium) + @extend %secondary-button .container background: $white @@ -56,31 +60,51 @@ a#new-activity overflow: hidden +transition(box-shadow 0.25s ease-in-out) + // wrapper around image + canvas + .wrapper + width: 162px + margin: 0 auto + +transition(all 0.15s ease-out) + + canvas + z-index: 99 !important + + // big round image + img.progress + width: 150px + height: 150px + border-radius: 75px + margin: 6px + + // all default text: title, organizer, date etc. .labels margin: 1.5em 0 0 0 color: $extra-dark-gray + // title h4 +custom-sans(light) font-size: 1.375em margin: 0 padding: 0 0.727272em + +transition(all 0.15s ease-out) + @extend %ornamental-divider-before + @extend %ornamental-divider-after &:before, &:after - content: "." - text-indent: -999em - display: block margin: 0.2em auto width: 20px - height: 1px - border-bottom: 1px solid $light-gray &:before width: 0 margin: 0 auto + // creator and date/time rows p + opacity: 1 + +transition(opacity 0.1s) + &.creator text-transform: uppercase +custom-sans(medium) @@ -96,14 +120,6 @@ a#new-activity border-top: 1px solid $light-gray padding: 0.75em 0 0 - .wrapper, - .labels h4 - +transition(all 0.15s ease-out) - - .labels p - opacity: 1 - +transition(opacity 0.1s) - // the big button appearing on hover .action opacity: 0 @@ -115,6 +131,14 @@ a#new-activity +transform(scale(0.9)) +transition(all 0.35s ease-in-out 0) + // select action type based on card variation + @each $type in $types + &.#{nth($type, 1)} + .action + a, button + @extend %large-icon-button-#{nth($type, 3)} + + // HOVER state &:hover cursor: pointer @@ -145,6 +169,7 @@ a#new-activity &:before width: 50% margin: 0 auto 0.5em + &:after width: 0 @@ -159,27 +184,3 @@ a#new-activity a, button @extend %large-icon-button-ok - - // variations, map to icon types - $types: participant ok remove, owner edit edit - @each $type in $types - &.#{nth($type, 1)} - span.icon - @extend %icon-#{nth($type, 2)} - - .action - a, button - @extend %large-icon-button-#{nth($type, 3)} - - img.progress - width: 150px - height: 150px - border-radius: 75px - margin: 6px - - .wrapper - width: 162px - margin: 0 auto - - canvas - z-index: 99 !important diff --git a/app/assets/stylesheets/utils/_buttons.sass b/app/assets/stylesheets/utils/_buttons.sass index 1b084ecc..fe37740b 100644 --- a/app/assets/stylesheets/utils/_buttons.sass +++ b/app/assets/stylesheets/utils/_buttons.sass @@ -83,3 +83,19 @@ $ypos : 0 background-position: -96px -96px * $ypos $ypos: $ypos + 1 + +// ---------------------------------------------------------------------------- + +%secondary-button + display: inline-block + background: $medium-gray + color: $white + text-shadow: 0 1px 0 $gray + @extend %small-text + padding: 0.25em 1.5em + text-transform: uppercase + border-radius: 3px + margin: 19em 0 0 + opacity: 0 + +transition(all 0.35s ease-in-out) + +custom-sans(medium) \ No newline at end of file From 8a0793de37ea290483376dc2a355f3fc825eeb30 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Fri, 7 Jun 2013 00:38:01 +0300 Subject: [PATCH 228/499] More stylesheets clean-up. --- app/assets/stylesheets/partials/_header.sass | 1 - .../stylesheets/partials/activities/_action.sass | 0 .../stylesheets/partials/activities/_index.sass | 11 ++++++----- app/assets/stylesheets/partials/activities/_show.sass | 4 ++-- app/assets/stylesheets/utils/_helpers.sass | 4 ---- app/assets/stylesheets/utils/_placeholders.sass | 8 ++++++++ 6 files changed, 16 insertions(+), 12 deletions(-) delete mode 100644 app/assets/stylesheets/partials/activities/_action.sass delete mode 100644 app/assets/stylesheets/utils/_helpers.sass diff --git a/app/assets/stylesheets/partials/_header.sass b/app/assets/stylesheets/partials/_header.sass index 28ef84ef..9fddcf82 100644 --- a/app/assets/stylesheets/partials/_header.sass +++ b/app/assets/stylesheets/partials/_header.sass @@ -25,7 +25,6 @@ height: 192px display: inline-block text-indent: -999em - background: image-url("header/logo.png") no-repeat +hidpi diff --git a/app/assets/stylesheets/partials/activities/_action.sass b/app/assets/stylesheets/partials/activities/_action.sass deleted file mode 100644 index e69de29b..00000000 diff --git a/app/assets/stylesheets/partials/activities/_index.sass b/app/assets/stylesheets/partials/activities/_index.sass index d45abd80..e54a0f80 100644 --- a/app/assets/stylesheets/partials/activities/_index.sass +++ b/app/assets/stylesheets/partials/activities/_index.sass @@ -5,7 +5,7 @@ a#new-activity #activities clear: both @extend %clearfix - +unlisted + @extend %unlisted padding: 1em 0 0 0 // card variations @@ -51,6 +51,7 @@ a#new-activity span @extend %secondary-button + // card background and wrapper .container background: $white height: 25em @@ -64,7 +65,7 @@ a#new-activity .wrapper width: 162px margin: 0 auto - +transition(all 0.15s ease-out) + @extend %transition-all-15s-ease-out canvas z-index: 99 !important @@ -87,7 +88,7 @@ a#new-activity font-size: 1.375em margin: 0 padding: 0 0.727272em - +transition(all 0.15s ease-out) + @extend %transition-all-15s-ease-out @extend %ornamental-divider-before @extend %ornamental-divider-after @@ -157,13 +158,13 @@ a#new-activity +transition(all 0.25s ease-in-out) .wrapper - +transition(all 0.15s ease-out) + @extend %transition-all-15s-ease-out +transform(translate(0, -9em)) opacity: 0 .labels h4 - +transition(all 0.15s ease-out) + @extend %transition-all-15s-ease-out +transform(translate(0, 2.5em)) &:before diff --git a/app/assets/stylesheets/partials/activities/_show.sass b/app/assets/stylesheets/partials/activities/_show.sass index 02f87bf4..75d32469 100644 --- a/app/assets/stylesheets/partials/activities/_show.sass +++ b/app/assets/stylesheets/partials/activities/_show.sass @@ -1,5 +1,5 @@ #activity - +clearfix + @extend %clearfix header text-align: center @@ -47,7 +47,7 @@ &.details dl.wrapper margin-left: 50px - +clearfix + @extend %clearfix border-bottom: 1px solid $light-gray dt, dd diff --git a/app/assets/stylesheets/utils/_helpers.sass b/app/assets/stylesheets/utils/_helpers.sass deleted file mode 100644 index 3f8e255d..00000000 --- a/app/assets/stylesheets/utils/_helpers.sass +++ /dev/null @@ -1,4 +0,0 @@ -=unlisted - list-style: none - margin: 0 - padding: 0 diff --git a/app/assets/stylesheets/utils/_placeholders.sass b/app/assets/stylesheets/utils/_placeholders.sass index fd4d4a68..458dd0e5 100644 --- a/app/assets/stylesheets/utils/_placeholders.sass +++ b/app/assets/stylesheets/utils/_placeholders.sass @@ -30,6 +30,11 @@ &:before +ornamental-divider +%unlisted + list-style: none + margin: 0 + padding: 0 + %inline-nav-list +unlisted @@ -53,3 +58,6 @@ &:hover color: $blue + +%transition-all-15s-ease-out + +transition(all 0.15s ease-out) From a6d4e3813cbd77c610b0294f3d204ee819931625 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Fri, 7 Jun 2013 00:46:01 +0300 Subject: [PATCH 229/499] Fixing left-over includes. --- app/assets/stylesheets/_utils.sass | 1 - app/assets/stylesheets/partials/_activities.sass | 2 -- 2 files changed, 3 deletions(-) diff --git a/app/assets/stylesheets/_utils.sass b/app/assets/stylesheets/_utils.sass index f1c5d0c0..22adbd1c 100644 --- a/app/assets/stylesheets/_utils.sass +++ b/app/assets/stylesheets/_utils.sass @@ -1,4 +1,3 @@ -@import "utils/helpers" @import "utils/fonts" @import "utils/placeholders" @import "utils/forms" diff --git a/app/assets/stylesheets/partials/_activities.sass b/app/assets/stylesheets/partials/_activities.sass index 5bd61cf7..c1e54c4c 100644 --- a/app/assets/stylesheets/partials/_activities.sass +++ b/app/assets/stylesheets/partials/_activities.sass @@ -1,6 +1,4 @@ body.activities - @import "activities/action" - &.index @import "activities/filters" @import "activities/index" From e79315f231dea3c25c83be6a526bf03cc3af2bd6 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Fri, 7 Jun 2013 00:47:02 +0300 Subject: [PATCH 230/499] Notifications. --- app/assets/stylesheets/utils/_placeholders.sass | 2 +- app/controllers/activities_controller.rb | 8 +++++--- app/views/partials/_header.html.haml | 2 +- app/views/partials/_notifications.html.haml | 3 ++- config/locales/en.yml | 4 ++++ 5 files changed, 13 insertions(+), 6 deletions(-) diff --git a/app/assets/stylesheets/utils/_placeholders.sass b/app/assets/stylesheets/utils/_placeholders.sass index 458dd0e5..e80117a1 100644 --- a/app/assets/stylesheets/utils/_placeholders.sass +++ b/app/assets/stylesheets/utils/_placeholders.sass @@ -36,7 +36,7 @@ padding: 0 %inline-nav-list - +unlisted + @extend %unlisted li display: inline-block diff --git a/app/controllers/activities_controller.rb b/app/controllers/activities_controller.rb index 9f497f6d..725d0753 100644 --- a/app/controllers/activities_controller.rb +++ b/app/controllers/activities_controller.rb @@ -30,13 +30,15 @@ def edit end def create - @activity = current_event.new_activity(current_user, sanitized_params) - @activity.save + @activity = current_event.new_activity(current_user, sanitized_params) + type = @activity.save ? :notice : :error + flash[type] = I18n.t("new_activity.#{type}") respond_with(@activity, location: activities_path) end def update - @activity.update_attributes(sanitized_params) + type = @activity.update_attributes(sanitized_params) ? :notice : :error + flash[type] = I18n.t("edit_activity.#{type}") respond_with(@activity, location: edit_activity_path(@activity)) end diff --git a/app/views/partials/_header.html.haml b/app/views/partials/_header.html.haml index a10d3990..6365a00b 100644 --- a/app/views/partials/_header.html.haml +++ b/app/views/partials/_header.html.haml @@ -1,6 +1,6 @@ %header#header - = render partial: "partials/notifications" + = render "partials/notifications" - if current_user %nav.user-nav diff --git a/app/views/partials/_notifications.html.haml b/app/views/partials/_notifications.html.haml index 7d4e7faa..e494939f 100644 --- a/app/views/partials/_notifications.html.haml +++ b/app/views/partials/_notifications.html.haml @@ -1,3 +1,4 @@ #notifications - flash.each do |key, value| - %div{ :class => key }= value \ No newline at end of file + %div(class=key) + = value \ No newline at end of file diff --git a/config/locales/en.yml b/config/locales/en.yml index d1a42617..2f81a1ea 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -76,9 +76,13 @@ en: new_activity: title: Organize an activity + notice: Your activity has been created! + error: Activity could not be saved. Please check your input. edit_activity: title: Edit your activity + notice: Your activity has been updated! + error: Your changes could not be saved. Please check your input. activity_form: markdown_hint: You can use Markdown here From e2941951c9bbaf75ccbe7d880578a1785654aa93 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Fri, 7 Jun 2013 01:08:49 +0300 Subject: [PATCH 231/499] Style and animations for notifications. --- app/assets/javascripts/initializers.coffee | 7 ++-- app/assets/stylesheets/_base.sass | 22 ++++++++++++ app/assets/stylesheets/_utils.sass | 1 + app/assets/stylesheets/utils/_animations.sass | 35 +++++++++++++++++++ app/views/layouts/application.html.haml | 1 + app/views/partials/_header.html.haml | 2 -- 6 files changed, 63 insertions(+), 5 deletions(-) create mode 100644 app/assets/stylesheets/utils/_animations.sass diff --git a/app/assets/javascripts/initializers.coffee b/app/assets/javascripts/initializers.coffee index fe4af6b5..a3423c95 100644 --- a/app/assets/javascripts/initializers.coffee +++ b/app/assets/javascripts/initializers.coffee @@ -128,10 +128,11 @@ ready = -> .trigger('keyup') - $('#notifications').fadeOut('slow') + hideNotification = -> + $('#notifications').addClass 'hide' - $('#notifications').bind 'click', -> - $(this).fadeOut('slow') + $('#notifications').on 'click', hideNotification + setTimeout hideNotification, 3000 $(document).ready(ready) $(document).on('page:load', ready) diff --git a/app/assets/stylesheets/_base.sass b/app/assets/stylesheets/_base.sass index e4763f2e..2af82bf3 100644 --- a/app/assets/stylesheets/_base.sass +++ b/app/assets/stylesheets/_base.sass @@ -14,6 +14,28 @@ a:hover border: none +transition(all 0.25s) +#notifications + div + background: transparentize($green, 0.2) + color: $white + text-align: center + z-index: 9999 + position: fixed + top: 1em + left: 1em + right: 1em + padding: 0.75em 0 + +custom-sans(bold) + border-radius: 3px + @extend %animated-bounce-in-down + + &.error + background: transparentize($red, 0.2) + + &.hide + div + @extend %animated-bounce-out-up + #main position: relative diff --git a/app/assets/stylesheets/_utils.sass b/app/assets/stylesheets/_utils.sass index 22adbd1c..e05e0258 100644 --- a/app/assets/stylesheets/_utils.sass +++ b/app/assets/stylesheets/_utils.sass @@ -2,3 +2,4 @@ @import "utils/placeholders" @import "utils/forms" @import "utils/buttons" +@import "utils/animations" \ No newline at end of file diff --git a/app/assets/stylesheets/utils/_animations.sass b/app/assets/stylesheets/utils/_animations.sass new file mode 100644 index 00000000..8318804f --- /dev/null +++ b/app/assets/stylesheets/utils/_animations.sass @@ -0,0 +1,35 @@ +%animated + +animation-fill-mode(both) + +animation-duration(1s) + + ++keyframes(bounceInDown) + 0% + opacity: 0 + +transform(translateY(-2000px)) + 60% + opacity: 1 + +transform(translateY(30px)) + 80% + +transform(translateY(-10px)) + 100% + +transform(translateY(0)) + +%animated-bounce-in-down + @extend %animated + +animation-name(bounceInDown) + + ++keyframes(bounceOutUp) + 0% + +transform(translateY(0)) + 20% + opacity: 1 + +transform(translateY(20px)) + 100% + opacity: 0 + +transform(translateY(-2000px)) + +%animated-bounce-out-up + @extend %animated + +animation-name(bounceOutUp) \ No newline at end of file diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml index 7a12f84e..fbe70deb 100644 --- a/app/views/layouts/application.html.haml +++ b/app/views/layouts/application.html.haml @@ -30,6 +30,7 @@ %body(class="#{ body_class(controller.action_name) }") = render "partials/chrome_frame" + = render "partials/notifications" #container = render "partials/header" #main(role="main") diff --git a/app/views/partials/_header.html.haml b/app/views/partials/_header.html.haml index 6365a00b..ef7296e1 100644 --- a/app/views/partials/_header.html.haml +++ b/app/views/partials/_header.html.haml @@ -1,7 +1,5 @@ %header#header - = render "partials/notifications" - - if current_user %nav.user-nav %ul From 58bc14feb79e30c7b863e04711db50e6326adc2e Mon Sep 17 00:00:00 2001 From: Polarblau Date: Fri, 7 Jun 2013 01:12:19 +0300 Subject: [PATCH 232/499] Shorten delay. --- app/assets/stylesheets/_base.sass | 1 + 1 file changed, 1 insertion(+) diff --git a/app/assets/stylesheets/_base.sass b/app/assets/stylesheets/_base.sass index 2af82bf3..fa500039 100644 --- a/app/assets/stylesheets/_base.sass +++ b/app/assets/stylesheets/_base.sass @@ -27,6 +27,7 @@ a:hover padding: 0.75em 0 +custom-sans(bold) border-radius: 3px + +animation-delay(0.25s) @extend %animated-bounce-in-down &.error From e029486fbdaab3001faf9d93712a096e8a613fbd Mon Sep 17 00:00:00 2001 From: Polarblau Date: Fri, 7 Jun 2013 09:12:57 +0300 Subject: [PATCH 233/499] Removing turbolinks for now. --- app/assets/javascripts/application.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index f9bf96a6..7d35ec6b 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -12,10 +12,9 @@ // //= require jquery //= require jquery_ujs -//= require turbolinks //= require jquery.easing.min //= require jquery.progress //= require pickadate/picker //= require pickadate/picker.date //= require pickadate/picker.time -//= require initializers \ No newline at end of file +//= require initializers From 93f46ccf37490a10fb3a95aa1d5d108236507ba9 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Fri, 7 Jun 2013 09:13:08 +0300 Subject: [PATCH 234/499] Fixing jumping. --- app/assets/stylesheets/partials/activities/_index.sass | 6 ++++-- app/assets/stylesheets/utils/_forms.sass | 6 +++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/app/assets/stylesheets/partials/activities/_index.sass b/app/assets/stylesheets/partials/activities/_index.sass index e54a0f80..cef0a59a 100644 --- a/app/assets/stylesheets/partials/activities/_index.sass +++ b/app/assets/stylesheets/partials/activities/_index.sass @@ -132,6 +132,9 @@ a#new-activity +transform(scale(0.9)) +transition(all 0.35s ease-in-out 0) + a, button + @extend %large-icon-button-ok + // select action type based on card variation @each $type in $types &.#{nth($type, 1)} @@ -183,5 +186,4 @@ a#new-activity +transform(scale(1)) +transition(all 0.15s ease-out 0.15s) - a, button - @extend %large-icon-button-ok + diff --git a/app/assets/stylesheets/utils/_forms.sass b/app/assets/stylesheets/utils/_forms.sass index 1e2b4864..c6206fbf 100644 --- a/app/assets/stylesheets/utils/_forms.sass +++ b/app/assets/stylesheets/utils/_forms.sass @@ -1,12 +1,12 @@ form input, textarea width: 100% - padding: 0.75em 15px 1em + padding: 0.75em 15px 0.9em background: $white + border: none border-bottom: 1px solid $white margin: 0 0 2px 0 font-size: 1.25em - border: none outline: none +custom-sans(light) z-index: 1 @@ -83,4 +83,4 @@ form text-align: right display: block color: $medium-gray - padding: 0.5em 15px \ No newline at end of file + padding: 0.5em 15px From cb20ac5682895170fd38a0d1a652f1a04d7d2fd8 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Fri, 7 Jun 2013 09:16:36 +0300 Subject: [PATCH 235/499] Tweaks. --- app/assets/stylesheets/partials/activities/_form.sass | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/assets/stylesheets/partials/activities/_form.sass b/app/assets/stylesheets/partials/activities/_form.sass index cff94676..7518a066 100644 --- a/app/assets/stylesheets/partials/activities/_form.sass +++ b/app/assets/stylesheets/partials/activities/_form.sass @@ -24,7 +24,7 @@ form#new-activity display: inline-block width: 45% position: relative - + span.validation-error-message position: relative float: right @@ -69,9 +69,9 @@ form#new-activity right: 0.5em top: 0.5em z-index: 1000 - height: 3em - width: 3em - border-radius: 1.5em + height: 2.9em + width: 2.9em + border-radius: 1.45em background-position: center background-size: cover From 756beed3d1671c02a39d7e7a24b9bc2e8eb8130a Mon Sep 17 00:00:00 2001 From: Polarblau Date: Fri, 7 Jun 2013 10:41:24 +0300 Subject: [PATCH 236/499] Implementing first version of show, complete. --- app/assets/javascripts/initializers.coffee | 2 +- .../partials/activities/_index.sass | 2 + .../partials/activities/_show.sass | 37 +++++++++++++++++-- app/assets/stylesheets/utils/_buttons.sass | 11 ++++-- app/views/activities/_action.html.haml | 4 +- app/views/activities/index.html.haml | 2 +- app/views/activities/show.html.haml | 11 ++---- .../assets/javascripts/jquery.progress.coffee | 6 +-- 8 files changed, 53 insertions(+), 22 deletions(-) diff --git a/app/assets/javascripts/initializers.coffee b/app/assets/javascripts/initializers.coffee index a3423c95..9373ecfe 100644 --- a/app/assets/javascripts/initializers.coffee +++ b/app/assets/javascripts/initializers.coffee @@ -8,7 +8,7 @@ ready = -> $('#activities img.progress').each -> setTimeout showProgress, Math.random() * 1000 + 500, @ - $('#activity img.progress').progress(strokeWidth: 12) + $('#activity .progress').progress(strokeWidth: 12) $('.date-capture').pickadate min: new Date(App.event.startTime) diff --git a/app/assets/stylesheets/partials/activities/_index.sass b/app/assets/stylesheets/partials/activities/_index.sass index cef0a59a..5afc55e4 100644 --- a/app/assets/stylesheets/partials/activities/_index.sass +++ b/app/assets/stylesheets/partials/activities/_index.sass @@ -50,6 +50,8 @@ a#new-activity span @extend %secondary-button + opacity: 0 + margin: 19em 0 0 // card background and wrapper .container diff --git a/app/assets/stylesheets/partials/activities/_show.sass b/app/assets/stylesheets/partials/activities/_show.sass index 75d32469..3dbfcf47 100644 --- a/app/assets/stylesheets/partials/activities/_show.sass +++ b/app/assets/stylesheets/partials/activities/_show.sass @@ -1,9 +1,13 @@ +$types: participant ok remove, owner edit edit + #activity @extend %clearfix + margin: 0 0 3em 0 header text-align: center margin: 0 0 3em 0 + position: relative h2 margin: 0 @@ -13,6 +17,20 @@ strong +custom-sans(medium) + .action + position: absolute + right: 0 + top: 0 + + a, button + @extend %icon-button-ok + + // select action type based on card variation + @each $type in $types + &.#{nth($type, 1)} + a, button + @extend %icon-button-#{nth($type, 3)} + section width: percentage(1/3) float: left @@ -22,27 +40,38 @@ +custom-serif line-height: 1.35em + h1, h2, h3, h4, h5, h6 + +custom-sans(bold) + text-transform: uppercase + margin: 1.5em 0 0 + font-size: 1em + .wrapper margin-right: 50px &.overview text-align: center - img.progress + .progress width: 250px height: 250px border-radius: 125px - margin: 12px + background-size: cover + background-position: center .wrapper width: 274px + height: 274px + padding: 12px margin: 0 auto p - font-size: 1.5em + margin: 1em 0 0 + font-size: 1.25em a - display: inline-block + @extend %secondary-button + margin: 1em 0 &.details dl.wrapper diff --git a/app/assets/stylesheets/utils/_buttons.sass b/app/assets/stylesheets/utils/_buttons.sass index fe37740b..8eea8bfe 100644 --- a/app/assets/stylesheets/utils/_buttons.sass +++ b/app/assets/stylesheets/utils/_buttons.sass @@ -20,12 +20,15 @@ $ypos : 0 display: inline-block vertical-align: top +custom-sans(bold) + background: none + padding: 0 span width: 40px height: 40px margin: 0 0 0 15px display: inline-block + float: right background: image-url("shared/buttons_small.png") no-repeat background-position: 0 -40px * $ypos @@ -94,8 +97,10 @@ $ypos : 0 @extend %small-text padding: 0.25em 1.5em text-transform: uppercase + border: none border-radius: 3px - margin: 19em 0 0 - opacity: 0 + +custom-sans(medium) +transition(all 0.35s ease-in-out) - +custom-sans(medium) \ No newline at end of file + + &:hover + background: $blue diff --git a/app/views/activities/_action.html.haml b/app/views/activities/_action.html.haml index b5d8e2b7..ec667e9b 100644 --- a/app/views/activities/_action.html.haml +++ b/app/views/activities/_action.html.haml @@ -1,4 +1,4 @@ -.action +.action(class=type) - case type - when "owner" = link_to edit_activity_path(activity), title: t("activities.edit.title") do @@ -11,4 +11,4 @@ - else = button_to activity_participation_path(activity), method: :post, remote: true, title: t("activities.join.title") do %span - = t("activities.join.label") \ No newline at end of file + = t("activities.join.label") diff --git a/app/views/activities/index.html.haml b/app/views/activities/index.html.haml index 8e2ba127..a484f847 100644 --- a/app/views/activities/index.html.haml +++ b/app/views/activities/index.html.haml @@ -27,8 +27,8 @@ = t("activities.filters.submit") %a#new-activity(href="#{ new_activity_path }" title="#{ t("activities.new.title") }") - = t("activities.new.label") %span + = t("activities.new.label") %ul#activities = render @activities.decorate diff --git a/app/views/activities/show.html.haml b/app/views/activities/show.html.haml index 8bb6fd78..db8d1568 100644 --- a/app/views/activities/show.html.haml +++ b/app/views/activities/show.html.haml @@ -10,10 +10,12 @@ %section.description .wrapper = @activity.description_markdown + %h4 + = t("activities.required") + = @activity.requirements_markdown %section.overview - %figure - = image_tag @activity.image_url, alt: @activity.name, class: "progress", data: { progress: @activity.full_by } + .progress(data-progress="#{ @activity.full_by }" style="background-image: url(#{ @activity.image_url })") %p = @activity.room_left %a.participants(href="#" title="") @@ -40,8 +42,3 @@ = t("activities.location") %dd = @activity.location - %dt - = t("activities.required") - %dd - = @activity.requirements - diff --git a/vendor/assets/javascripts/jquery.progress.coffee b/vendor/assets/javascripts/jquery.progress.coffee index 1f13c4fe..2e8330bb 100644 --- a/vendor/assets/javascripts/jquery.progress.coffee +++ b/vendor/assets/javascripts/jquery.progress.coffee @@ -51,7 +51,7 @@ $.fn.extend context = $canvas.get(0).getContext("2d") # animated call to render - $({ progress: 0 }).animate({ + $(progress: 0).animate({ progress: progress },{ duration : settings.duration @@ -78,6 +78,4 @@ $.fn.extend context.lineWidth = settings.strokeWidth context.stroke() - @each -> - # ensure that the image is really loaded - if @complete then init.call(@) else $(@).load(=> init.call(@)) + @each -> init.call(@) From f12c6e7435dbe4a238ff3ff9005ed08b0038e087 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Fri, 7 Jun 2013 11:08:52 +0300 Subject: [PATCH 237/499] Extract profress. --- app/assets/javascripts/initializers.coffee | 5 +---- app/assets/stylesheets/_base.sass | 8 ++++---- .../stylesheets/partials/_activities.sass | 2 ++ .../partials/activities/_index.sass | 18 +----------------- .../partials/activities/_shared.sass | 18 ++++++++++++++++++ .../stylesheets/partials/activities/_show.sass | 13 +++++-------- app/views/activities/_activity.html.haml | 4 ++-- app/views/activities/_progress.html.haml | 1 + app/views/activities/show.html.haml | 2 +- .../assets/javascripts/jquery.progress.coffee | 2 +- 10 files changed, 36 insertions(+), 37 deletions(-) create mode 100644 app/assets/stylesheets/partials/activities/_shared.sass create mode 100644 app/views/activities/_progress.html.haml diff --git a/app/assets/javascripts/initializers.coffee b/app/assets/javascripts/initializers.coffee index 9373ecfe..b603dbae 100644 --- a/app/assets/javascripts/initializers.coffee +++ b/app/assets/javascripts/initializers.coffee @@ -4,10 +4,7 @@ ready = -> window.location.replace(App.paths.login) if xhr.status == 401 # show how "full" an activity is - showProgress = (img)-> $(img).progress() - $('#activities img.progress').each -> - setTimeout showProgress, Math.random() * 1000 + 500, @ - + $('#activities .progress').progress() $('#activity .progress').progress(strokeWidth: 12) $('.date-capture').pickadate diff --git a/app/assets/stylesheets/_base.sass b/app/assets/stylesheets/_base.sass index fa500039..cef38103 100644 --- a/app/assets/stylesheets/_base.sass +++ b/app/assets/stylesheets/_base.sass @@ -29,10 +29,10 @@ a:hover border-radius: 3px +animation-delay(0.25s) @extend %animated-bounce-in-down - + &.error background: transparentize($red, 0.2) - + &.hide div @extend %animated-bounce-out-up @@ -50,14 +50,14 @@ article font-size: 2em margin: 0 @extend %ornamental-divider-after - + &:after margin-bottom: 0.25em .meta text-transform: uppercase @extend %smaller-text - + strong.divider +custom-sans(bold) margin: 0 0.5em diff --git a/app/assets/stylesheets/partials/_activities.sass b/app/assets/stylesheets/partials/_activities.sass index c1e54c4c..ca94393d 100644 --- a/app/assets/stylesheets/partials/_activities.sass +++ b/app/assets/stylesheets/partials/_activities.sass @@ -1,4 +1,6 @@ body.activities + @import "activities/shared" + &.index @import "activities/filters" @import "activities/index" diff --git a/app/assets/stylesheets/partials/activities/_index.sass b/app/assets/stylesheets/partials/activities/_index.sass index 5afc55e4..fde9f976 100644 --- a/app/assets/stylesheets/partials/activities/_index.sass +++ b/app/assets/stylesheets/partials/activities/_index.sass @@ -63,22 +63,6 @@ a#new-activity overflow: hidden +transition(box-shadow 0.25s ease-in-out) - // wrapper around image + canvas - .wrapper - width: 162px - margin: 0 auto - @extend %transition-all-15s-ease-out - - canvas - z-index: 99 !important - - // big round image - img.progress - width: 150px - height: 150px - border-radius: 75px - margin: 6px - // all default text: title, organizer, date etc. .labels margin: 1.5em 0 0 0 @@ -162,7 +146,7 @@ a#new-activity opacity: 1 +transition(all 0.25s ease-in-out) - .wrapper + .progress-wrapper @extend %transition-all-15s-ease-out +transform(translate(0, -9em)) opacity: 0 diff --git a/app/assets/stylesheets/partials/activities/_shared.sass b/app/assets/stylesheets/partials/activities/_shared.sass new file mode 100644 index 00000000..8cd512ea --- /dev/null +++ b/app/assets/stylesheets/partials/activities/_shared.sass @@ -0,0 +1,18 @@ +// wrapper around image + canvas +.progress-wrapper + padding: 6px + width: 162px + width: 162px + margin: 0 auto + @extend %transition-all-15s-ease-out + + canvas + z-index: 99 !important + +// big round image +.progress + width: 150px + height: 150px + border-radius: 75px + background-size: cover + background-position: center diff --git a/app/assets/stylesheets/partials/activities/_show.sass b/app/assets/stylesheets/partials/activities/_show.sass index 3dbfcf47..f5f363d1 100644 --- a/app/assets/stylesheets/partials/activities/_show.sass +++ b/app/assets/stylesheets/partials/activities/_show.sass @@ -52,18 +52,15 @@ $types: participant ok remove, owner edit edit &.overview text-align: center + .progress-wrapper + width: 274px + height: 274px + padding: 12px + .progress width: 250px height: 250px border-radius: 125px - background-size: cover - background-position: center - - .wrapper - width: 274px - height: 274px - padding: 12px - margin: 0 auto p margin: 1em 0 0 diff --git a/app/views/activities/_activity.html.haml b/app/views/activities/_activity.html.haml index 9a61e88c..cf5a827d 100644 --- a/app/views/activities/_activity.html.haml +++ b/app/views/activities/_activity.html.haml @@ -3,7 +3,7 @@ %li(id="activity-#{activity.id}" class="#{type} #{activity.status}") .container - = image_tag "dummies/activity.jpg", alt: activity.name, class: "progress", data: { progress: activity.full_by } + = render "activities/progress", activity: activity .labels %h4 @@ -13,7 +13,7 @@ %p.time = activity.time.html_safe - = render "activities/action", :activity => activity, :type => type + = render "activities/action", activity: activity, type: type %a.details(href="#{activity_path(activity)}" title="#{ t("activities.more_info.title") }") %span diff --git a/app/views/activities/_progress.html.haml b/app/views/activities/_progress.html.haml new file mode 100644 index 00000000..8be93cd0 --- /dev/null +++ b/app/views/activities/_progress.html.haml @@ -0,0 +1 @@ +.progress(data-progress="#{ activity.full_by }" style="background-image: url(#{ activity.image_url })") diff --git a/app/views/activities/show.html.haml b/app/views/activities/show.html.haml index db8d1568..62384d82 100644 --- a/app/views/activities/show.html.haml +++ b/app/views/activities/show.html.haml @@ -15,7 +15,7 @@ = @activity.requirements_markdown %section.overview - .progress(data-progress="#{ @activity.full_by }" style="background-image: url(#{ @activity.image_url })") + = render "activities/progress", activity: @activity %p = @activity.room_left %a.participants(href="#" title="") diff --git a/vendor/assets/javascripts/jquery.progress.coffee b/vendor/assets/javascripts/jquery.progress.coffee index 2e8330bb..13e0abfc 100644 --- a/vendor/assets/javascripts/jquery.progress.coffee +++ b/vendor/assets/javascripts/jquery.progress.coffee @@ -34,7 +34,7 @@ $.fn.extend color = settings.strokeColorFull if progress >= 100 # wrap image - $wrapper = $('
', class: 'wrapper').css + $wrapper = $('
', class: 'progress-wrapper').css position: 'relative' # padding : settings.strokeWidth $el.wrap($wrapper) From 6bf95e0994acdbdf476193aef2d5c5ae01353896 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Fri, 7 Jun 2013 11:35:08 +0300 Subject: [PATCH 238/499] Placeholder image. --- .../images/shared/activity_placeholder.jpg | Bin 0 -> 5208 bytes .../stylesheets/partials/activities/_shared.sass | 1 + app/views/activities/_progress.html.haml | 2 +- 3 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 app/assets/images/shared/activity_placeholder.jpg diff --git a/app/assets/images/shared/activity_placeholder.jpg b/app/assets/images/shared/activity_placeholder.jpg new file mode 100644 index 0000000000000000000000000000000000000000..80d75e76dae21e225ba6816fb367c5322a587fd0 GIT binary patch literal 5208 zcmeHKc~q0x6@On6f`qU{q-9gShNClnsUu-}6@U|e*)fD#-IlMA9*>w=e! z=E9!*siTWZOiXl7WV$2r7#hRR&ri?c;o*jPxG9pQC@;xPs&Fwf1Pc{>xk!eJ5Gkx@ zBtf!vN768Yc;_!JbZdri9}xj1FZsGCmf&eq{NC1$uc2rolt?q%lSgg z-9;ZPV+F~DJQR^{K#(|N71zWfD58i(WN^@WZ}`)29$zHY1B(q2;o+<>sRHFm`NFVZ ze=5e|E)oe?K0ZsmgM1ktK|V|`1|!6S%U#M0#>69-P7m@7@iE2*Bm8)YP>LF31>>>I zDP#4`Adz92gN1TYf>5wbj!0m`qFJJ;=R%*7Z!}gg^;{TJ#?r9K(DYk7VXKW0)<1f6 ztZ}i2vDOz#v3{3hjjekLEDh}n1Qpl;f^-dFA0QG41QP<$#Dr)@#NOs}h{QSOWD?1o zL?T<04Ps5Uw6eCcB-_}`n`dL=YnaF5x*3|t^k;?e_~V~Afjb-8bB>NMB7#HIv1J&Ikd6E`SODN>YF?4_OPDc+rIJS z-#>A@nO{xi`ZmU^vlcqu-&L5A*xu3wcih+3D)&@1E*C2QX4WXkTWd{vanh}%J~amd zlKRRlet9(4qHXwuHu6V%Zn4;bcj(=5lNrna&^xr9Rg5R>9kwc95Q{;Vm0$FKNK0=U zF4cw-Wyo!ZsLNls&CRa$t9R+P+%2UkcU2tB>Nt9)4;(8U4H^lZ>=Rd3$=M#@TT-a6X!Y|{2yH2orB-LF|Q8g?Kn zm%p#VyXk?w>)GMX=jMT<_~b*-qMiIh>>=3hYJ1TNF(;H@aR04@zqOnKKh8wYsn3S8 zn*f+QtjVhy3U%=+P#idHjd4Ns+eNVj3RTLyg3bA{?QrMh{07v9LDBT9qVJXVYKJQx zFqy0x`@DxE{;^NA-30^bP(llO8@WPPnai4Tj%fNd346COYQH_l4-7TipH<6`=p;WW z%2!`>uDjJ_ufnpcV^YzRcEpUSv)Z=TtW*v*oW(C%`M8Q5^R)ms>Wkl4%?Vd)(zcX5 znzuZXNKZJLz1emCsR7rBOHuhhK-k3W-bQ@%dA;1#u!Uw(#p;EUXLlB0X{Qi;K&yXG zzzMBd2ma7xA87O%IT8ZfzEA$hujFRUzjXeZX4R1&=MMLlzrzn*9cr_Bd9N>nbAwhM zsE175GT$_dj_Y>->TJywMfO-*s*GI!Jz8!qED*)kL3u;I9bu13x#3rYmsvHBM%xQm z-lefGG6#sgUME^KtIZzTzdfP@p@%X^V?59egw=duy0*iKBRePo@V=(IoJtNj_~Oct zvdwAm`ixyVs>lRqN$UOSypyA?0t|2 zNzb7uzZwL^p=F3eUAzh%ifn4Dnj zDpV3gFx_1@o)uqxv*)YTkr^w<;$!*G28sRSrWt5!`(MQu|7DQy gU6FQ0$my7f?q9jcXFxgYGFyS!3j7aLz*P6--xK$ttpET3 literal 0 HcmV?d00001 diff --git a/app/assets/stylesheets/partials/activities/_shared.sass b/app/assets/stylesheets/partials/activities/_shared.sass index 8cd512ea..0e9eb757 100644 --- a/app/assets/stylesheets/partials/activities/_shared.sass +++ b/app/assets/stylesheets/partials/activities/_shared.sass @@ -16,3 +16,4 @@ border-radius: 75px background-size: cover background-position: center + background-image: image-url('shared/activity_placeholder.jpg') diff --git a/app/views/activities/_progress.html.haml b/app/views/activities/_progress.html.haml index 8be93cd0..b565c6c6 100644 --- a/app/views/activities/_progress.html.haml +++ b/app/views/activities/_progress.html.haml @@ -1 +1 @@ -.progress(data-progress="#{ activity.full_by }" style="background-image: url(#{ activity.image_url })") +.progress(data-progress="#{ activity.full_by }" style="#{ "background-image: url(#{ activity.image_url })" if activity.image_url }") From 160b2cfb2d58255e8533ff8f43ccff1bc3707a7a Mon Sep 17 00:00:00 2001 From: Polarblau Date: Fri, 7 Jun 2013 11:53:47 +0300 Subject: [PATCH 239/499] Disable anytime when setting date directly. --- app/assets/javascripts/initializers.coffee | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/assets/javascripts/initializers.coffee b/app/assets/javascripts/initializers.coffee index b603dbae..6a7fde7d 100644 --- a/app/assets/javascripts/initializers.coffee +++ b/app/assets/javascripts/initializers.coffee @@ -18,6 +18,7 @@ ready = -> $("#activity_#{target}_1i").val(year) $("#activity_#{target}_2i").val(month + 1) $("#activity_#{target}_3i").val(date) + $('#activity_anytime').attr('checked', false) onClose: -> otherSelector = @$node.data('update') other = $(otherSelector).pickadate('picker') @@ -39,6 +40,7 @@ ready = -> [hours, minutes] = [parseInt((e.select / 60), 10), e.select % 60] $("#activity_#{target}_4i").val(if hours < 10 then "0#{hours}" else hours) $("#activity_#{target}_5i").val(minutes) + $('#activity_anytime').attr('checked', false) onClose: -> otherSelector = @$node.data('update') other = $(otherSelector).pickatime('picker') From 45d155c61bdee14431a3ee59662229ecad7ae219 Mon Sep 17 00:00:00 2001 From: pietia Date: Fri, 7 Jun 2013 16:44:14 +0200 Subject: [PATCH 240/499] added recommended gems --- Gemfile | 2 ++ Gemfile.lock | 14 ++++++++++++++ 2 files changed, 16 insertions(+) diff --git a/Gemfile b/Gemfile index 03364c4d..c34816f0 100644 --- a/Gemfile +++ b/Gemfile @@ -54,4 +54,6 @@ end group :production, :staging do gem 'exception_notification', require: 'exception_notifier', git: 'git://github.com/smartinez87/exception_notification.git' gem 'rack-robotz', '~> 0.0.3' + gem 'rails_log_stdout', github: 'heroku/rails_log_stdout' + gem 'rails3_serve_static_assets', github: 'heroku/rails3_serve_static_assets' end diff --git a/Gemfile.lock b/Gemfile.lock index 900380db..c1690930 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,3 +1,15 @@ +GIT + remote: git://github.com/heroku/rails3_serve_static_assets.git + revision: 84910ceb4ca2851d650e66d3b22f5008acf253f8 + specs: + rails3_serve_static_assets (0.0.1) + +GIT + remote: git://github.com/heroku/rails_log_stdout.git + revision: 01b5bcc572e3baed1618f4068920dd83bd491fb2 + specs: + rails_log_stdout (0.0.1) + GIT remote: git://github.com/smartinez87/exception_notification.git revision: 11d61df3cb435381929757b8d7e47209b5ab2b14 @@ -295,7 +307,9 @@ DEPENDENCIES pg rack-robotz (~> 0.0.3) rails (= 4.0.0.rc1) + rails3_serve_static_assets! rails_html_helpers + rails_log_stdout! redcarpet rspec-rails (~> 2.0) sass-rails (~> 4.0.0.beta1) From 5bd5098697cc2c5d2d62ce1277216a109e94b3c6 Mon Sep 17 00:00:00 2001 From: pietia Date: Fri, 7 Jun 2013 20:36:29 +0200 Subject: [PATCH 241/499] use latest sprockets-rails --- Gemfile | 2 ++ Gemfile.lock | 20 ++++++++++++++------ 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/Gemfile b/Gemfile index c34816f0..c5bf3b53 100644 --- a/Gemfile +++ b/Gemfile @@ -13,6 +13,8 @@ gem 'omniauth-github', '~> 1.1.0' gem 'omniauth-twitter', '~> 0.0.16' gem 'simple_form', '~> 3.0.0.beta1' +gem 'modernizr-rails' +gem 'sprockets-rails', git: 'git://github.com/rails/sprockets-rails.git' gem 'jquery-rails' gem 'turbolinks' gem 'jbuilder', '~> 1.0.1' diff --git a/Gemfile.lock b/Gemfile.lock index c1690930..d47034f0 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -10,6 +10,15 @@ GIT specs: rails_log_stdout (0.0.1) +GIT + remote: git://github.com/rails/sprockets-rails.git + revision: ce61f70669e105d56b65d180cfdc9338b1757577 + specs: + sprockets-rails (2.0.0.rc4) + actionpack (>= 3.0) + activesupport (>= 3.0) + sprockets (~> 2.8) + GIT remote: git://github.com/smartinez87/exception_notification.git revision: 11d61df3cb435381929757b8d7e47209b5ab2b14 @@ -133,7 +142,7 @@ GEM heroku (>= 2) heroku-api (>= 0.1.2) rake - hike (1.2.2) + hike (1.2.3) httpauth (0.2.0) i18n (0.6.4) jbuilder (1.0.2) @@ -156,6 +165,7 @@ GEM treetop (~> 1.4.8) mime-types (1.23) minitest (4.7.3) + modernizr-rails (2.6.2.3) multi_json (1.7.2) multipart-post (1.2.0) neat (1.2.1) @@ -245,15 +255,11 @@ GEM multi_json (~> 1.0) simplecov-html (~> 0.7.1) simplecov-html (0.7.1) - sprockets (2.9.3) + sprockets (2.10.0) hike (~> 1.2) multi_json (~> 1.0) rack (~> 1.0) tilt (~> 1.1, != 1.3.0) - sprockets-rails (2.0.0.rc4) - actionpack (>= 3.0) - activesupport (>= 3.0) - sprockets (~> 2.8) thor (0.18.1) thread_safe (0.1.0) atomic @@ -299,6 +305,7 @@ DEPENDENCIES jbuilder (~> 1.0.1) jquery-rails json_spec + modernizr-rails neat newrelic_rpm omniauth (~> 1.1.4) @@ -316,6 +323,7 @@ DEPENDENCIES settingslogic simple_form (~> 3.0.0.beta1) simplecov + sprockets-rails! turbolinks uglifier (>= 1.0.3) unicorn From d3aaefbc0bd658021f1182d65abca2c75b8ebe81 Mon Sep 17 00:00:00 2001 From: pietia Date: Fri, 7 Jun 2013 20:36:49 +0200 Subject: [PATCH 242/499] user modernizr from gem --- app/views/layouts/application.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml index fbe70deb..25d1053a 100644 --- a/app/views/layouts/application.html.haml +++ b/app/views/layouts/application.html.haml @@ -11,7 +11,7 @@ = csrf_meta_tags = stylesheet_link_tag "application" - = javascript_include_tag "modernizr.min" + = javascript_include_tag :modernizr = javascript_include_tag "//use.typekit.net/vor5lqb.js" :javascript From d21b4eb1c1c0238fcc254945d68f1b6136b25ede Mon Sep 17 00:00:00 2001 From: pietia Date: Fri, 7 Jun 2013 20:51:43 +0200 Subject: [PATCH 243/499] bugfix: longer field --- db/migrate/20130607184825_image_url_must_be_text_field.rb | 5 +++++ db/schema.rb | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) create mode 100644 db/migrate/20130607184825_image_url_must_be_text_field.rb diff --git a/db/migrate/20130607184825_image_url_must_be_text_field.rb b/db/migrate/20130607184825_image_url_must_be_text_field.rb new file mode 100644 index 00000000..42bfe625 --- /dev/null +++ b/db/migrate/20130607184825_image_url_must_be_text_field.rb @@ -0,0 +1,5 @@ +class ImageUrlMustBeTextField < ActiveRecord::Migration + def change + change_column :activities, :image_url, :text + end +end diff --git a/db/schema.rb b/db/schema.rb index fe53ce56..f9794c37 100644 --- a/db/schema.rb +++ b/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: 20130605211503) do +ActiveRecord::Schema.define(version: 20130607184825) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -29,7 +29,7 @@ t.boolean "anytime", default: true, null: false t.text "requirements" t.datetime "end_time" - t.string "image_url" + t.text "image_url" end add_index "activities", ["name"], name: "index_activities_on_name", unique: true, using: :btree From 03df02c7ceac9739e3a1c1125ca6c717565af5ae Mon Sep 17 00:00:00 2001 From: pietia Date: Fri, 7 Jun 2013 21:02:27 +0200 Subject: [PATCH 244/499] lets try this --- config/application.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/config/application.rb b/config/application.rb index de854822..8dd0617a 100644 --- a/config/application.rb +++ b/config/application.rb @@ -21,6 +21,8 @@ class Application < Rails::Application config.autoload_paths += Dir["#{config.root}/lib/**/"] + config.assets.precompile << %w(*.png *.jpg *.jpeg *.gif) + # don't generate any assets config.generators.assets = false From 9ece4263a8b43b647a95e608ad5c6ea695498da3 Mon Sep 17 00:00:00 2001 From: pietia Date: Fri, 7 Jun 2013 21:12:11 +0200 Subject: [PATCH 245/499] enabled a fallback --- config/application.rb | 2 -- config/environments/production.rb | 2 +- config/environments/staging.rb | 2 +- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/config/application.rb b/config/application.rb index 8dd0617a..de854822 100644 --- a/config/application.rb +++ b/config/application.rb @@ -21,8 +21,6 @@ class Application < Rails::Application config.autoload_paths += Dir["#{config.root}/lib/**/"] - config.assets.precompile << %w(*.png *.jpg *.jpeg *.gif) - # don't generate any assets config.generators.assets = false diff --git a/config/environments/production.rb b/config/environments/production.rb index 8ed78b41..8121a938 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -27,7 +27,7 @@ # config.assets.css_compressor = :sass # Whether to fallback to assets pipeline if a precompiled asset is missed. - config.assets.compile = false + config.assets.compile = true # Generate digests for assets URLs. config.assets.digest = true diff --git a/config/environments/staging.rb b/config/environments/staging.rb index 67e7c406..7cf9577e 100644 --- a/config/environments/staging.rb +++ b/config/environments/staging.rb @@ -26,7 +26,7 @@ # config.assets.css_compressor = :sass # Whether to fallback to assets pipeline if a precompiled asset is missed. - config.assets.compile = false + config.assets.compile = true # Generate digests for assets URLs. config.assets.digest = true From b6a8bccefd2d1b0d1bc61ed7cc5442606e21c72c Mon Sep 17 00:00:00 2001 From: pietia Date: Fri, 7 Jun 2013 21:58:37 +0200 Subject: [PATCH 246/499] stuff needed after we changed column to text type --- app/views/activities/_progress.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/activities/_progress.html.haml b/app/views/activities/_progress.html.haml index b565c6c6..5bc7bc9b 100644 --- a/app/views/activities/_progress.html.haml +++ b/app/views/activities/_progress.html.haml @@ -1 +1 @@ -.progress(data-progress="#{ activity.full_by }" style="#{ "background-image: url(#{ activity.image_url })" if activity.image_url }") +.progress(data-progress="#{ activity.full_by }" style="#{ "background-image: url(#{ activity.image_url })" if activity.image_url.present? }") From f921aa233a18fbb86cb74ed901aa7459ea0748d8 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Sat, 8 Jun 2013 00:47:28 +0300 Subject: [PATCH 247/499] Styles for sign-up. --- app/assets/images/user/github.png | Bin 0 -> 4581 bytes app/assets/images/user/github@2x.png | Bin 0 -> 8538 bytes app/assets/images/user/twitter.png | Bin 0 -> 3782 bytes app/assets/images/user/twitter@2x.png | Bin 0 -> 7203 bytes app/assets/javascripts/initializers.coffee | 10 ++ app/assets/stylesheets/application.css.scss | 3 +- app/assets/stylesheets/partials/_user.sass | 2 + .../partials/activities/_form.sass | 18 +--- .../partials/user/_registration.sass | 87 ++++++++++++++++++ .../partials/user/_registrations.sass | 4 + app/assets/stylesheets/utils/_forms.sass | 7 +- app/views/registrations/new.html.haml | 65 +++++++++---- 12 files changed, 161 insertions(+), 35 deletions(-) create mode 100644 app/assets/images/user/github.png create mode 100644 app/assets/images/user/github@2x.png create mode 100644 app/assets/images/user/twitter.png create mode 100644 app/assets/images/user/twitter@2x.png create mode 100644 app/assets/stylesheets/partials/_user.sass create mode 100644 app/assets/stylesheets/partials/user/_registration.sass create mode 100644 app/assets/stylesheets/partials/user/_registrations.sass diff --git a/app/assets/images/user/github.png b/app/assets/images/user/github.png new file mode 100644 index 0000000000000000000000000000000000000000..1196a0765173f519b3e6c68a422c51e3e36cf7a6 GIT binary patch literal 4581 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 Uk^E`$2$+Yg%x%odj(J@AANnDo3jhEB literal 0 HcmV?d00001 diff --git a/app/assets/images/user/twitter.png b/app/assets/images/user/twitter.png new file mode 100644 index 0000000000000000000000000000000000000000..343eb381c8734471753606b6b2f9034007621517 GIT binary patch literal 3782 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 literal 0 HcmV?d00001 diff --git a/app/assets/images/user/twitter@2x.png b/app/assets/images/user/twitter@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..49472549b4fb989765a1a66c943e024632a3dc28 GIT binary patch literal 7203 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 console.error "AJAX ERROR!", arguments if console?.error? window.location.replace(App.paths.login) if xhr.status == 401 + # section toggles + $('.reveal').on 'click', -> + $("##{@href.split('#')[1]}").show() + + $("a.reveal[href='#{window.location.hash}']").trigger('click') + + # hide validation errors on focus + $('input.validation-error').on 'focus', -> + $(@).next('span.validation-error-message').fadeOut() + # show how "full" an activity is $('#activities .progress').progress() $('#activity .progress').progress(strokeWidth: 12) diff --git a/app/assets/stylesheets/application.css.scss b/app/assets/stylesheets/application.css.scss index 2557b22f..2620960f 100644 --- a/app/assets/stylesheets/application.css.scss +++ b/app/assets/stylesheets/application.css.scss @@ -13,4 +13,5 @@ @import "partials/header"; @import "partials/footer"; -@import "partials/activities"; \ No newline at end of file +@import "partials/activities"; +@import "partials/user"; diff --git a/app/assets/stylesheets/partials/_user.sass b/app/assets/stylesheets/partials/_user.sass new file mode 100644 index 00000000..2fadf543 --- /dev/null +++ b/app/assets/stylesheets/partials/_user.sass @@ -0,0 +1,2 @@ +body.registrations + @import "user/registration" diff --git a/app/assets/stylesheets/partials/activities/_form.sass b/app/assets/stylesheets/partials/activities/_form.sass index 7518a066..0ff1c355 100644 --- a/app/assets/stylesheets/partials/activities/_form.sass +++ b/app/assets/stylesheets/partials/activities/_form.sass @@ -81,22 +81,6 @@ form#new-activity background-image: image-url('shared/checking.gif') !important button - border: none - background: none - +custom-sans(bold) - @extend %smaller-text - text-transform: uppercase - color: $green + @extend %large-icon-button-ok display: block margin: 1em auto 4em - - span - display: block - width: 96px - height: 96px - margin: 0 auto 0.75em - background: transparent image-url("shared/buttons_big.png") no-repeat 0 0 - - &:hover - span - background-position: -96px 0 diff --git a/app/assets/stylesheets/partials/user/_registration.sass b/app/assets/stylesheets/partials/user/_registration.sass new file mode 100644 index 00000000..a36c0b47 --- /dev/null +++ b/app/assets/stylesheets/partials/user/_registration.sass @@ -0,0 +1,87 @@ +article + header + p.meta + strong + +custom-sans(medium) + + section + margin: 0 150px + + h4 + +custom-sans(light) + font-size: 1.375em + padding: 0 0.727272em + text-align: center + + &.oauth + @extend %clearfix + + a + width: 50% + display: block + border: none + float: left + color: $extra-dark-gray + + .container + text-align: center + background: $white no-repeat center top + height: 22em + padding: 2.5em 1em 1em 1em + margin: 0 1px + position: relative + overflow: hidden + +transition(box-shadow 0.25s ease-in-out) + + h4 + @extend %ornamental-divider-before + margin: 10em 0 0 0 + + &:before + margin: 0.5em auto + + + &.github + .container + background-image: image-url('user/github.png') + + &.twitter + .container + background-image: image-url('user/twitter.png') + + &:hover + color: $blue + + .container + z-index: 9001 + +transition(box-shadow 0.25s ease-in-out) + box-shadow: 0 0 20px transparentize($black, 0.9) + + + &.form + p + text-align: center + margin: 2em 0 3em 0 + + strong + margin: 0 0 1em 0 + text-transform: uppercase + +custom-sans(medium) + display: block + + a.reveal + @extend %secondary-button + + #sign-up + padding: 2em 0 0 + width: percentage(2/3) + margin: 0 auto + display: none + + h4 + @extend %ornamental-divider-after + + button + margin: 2em auto 4em + display: block + @extend %large-icon-button-ok diff --git a/app/assets/stylesheets/partials/user/_registrations.sass b/app/assets/stylesheets/partials/user/_registrations.sass new file mode 100644 index 00000000..472677e2 --- /dev/null +++ b/app/assets/stylesheets/partials/user/_registrations.sass @@ -0,0 +1,4 @@ +article + section + &.form + display: none diff --git a/app/assets/stylesheets/utils/_forms.sass b/app/assets/stylesheets/utils/_forms.sass index c6206fbf..c2c31f21 100644 --- a/app/assets/stylesheets/utils/_forms.sass +++ b/app/assets/stylesheets/utils/_forms.sass @@ -42,6 +42,11 @@ form border: none padding: 0 + label + span.label + html.js & + display: none + fieldset, label display: block @@ -54,7 +59,7 @@ form top: 0 color: $red z-index: 1000 - line-height: 4.25em + line-height: 4em hr background-color: $light-gray diff --git a/app/views/registrations/new.html.haml b/app/views/registrations/new.html.haml index ee59b74c..cd2e6661 100644 --- a/app/views/registrations/new.html.haml +++ b/app/views/registrations/new.html.haml @@ -1,22 +1,55 @@ -.auth - = simple_form_for(resource, :as => resource_name, :url => registration_path(resource_name)) do |f| - = f.error_notification +%article + %header + %h2 + Register + %p.meta + %strong Have an account already? + — + = link_to 'sign in with password', new_session_path(:user) - .inputs - = f.input :name, :required => true, :autofocus => true - = f.input :email, :required => true + %section.oauth + = link_to "/auth/github", class: "github" do + .container + %h4 + Sign in with Github - - if @user.password_required? - = f.input :password, :required => true - = f.input :password_confirmation, :required => true + = link_to "/auth/twitter", class: "twitter" do + .container + %h4 + Sign in with Twitter + + %section.form + %p + %strong + Or + %a.reveal(href="#sign-up" title="Create a new account") + Create a new account + + - if resource.errors.any? + - style = "display: block" + :javascript + window.location.hash = "sign-up"; - .actions - = f.button :submit, "Sign up", :class => 'btn btn-success' + = form_for resource, as: resource_name, url: registration_path(resource_name), html: { id: "sign-up", style: style } do |f| + %h4 Register new account - = link_to "Sign in with Github", "/auth/github", class: "btn-auth btn-github large" - = link_to "Sign in with Twitter", "/auth/twitter", class: "btn-auth btn-twitter large" + %label.name + %span.label Name + = f.text_field :name, autofocus: true, placeholder: "Name" + + %label.email + %span.label Email address + = f.email_field :email, placeholder: "Email address" + + - if @user.password_required? + %label.password + %span.label Password + = f.password_field :password, placeholder: "Password" -%p.hint - Have an account already? - = link_to 'sign in with password', new_session_path(:user) + %label.password + %span.label Password, once more + = f.password_field :password_confirmation, placeholder: "Password once more" + %button(type="submit")> + %span> + Sign up From bd727c2f3d06c5fbf1f58e7d4ee18ef86a5e1d04 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Sat, 8 Jun 2013 00:54:00 +0300 Subject: [PATCH 248/499] Show only first validation error. --- config/initializers/validation_error.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/config/initializers/validation_error.rb b/config/initializers/validation_error.rb index ed233e6c..97cb9ce9 100644 --- a/config/initializers/validation_error.rb +++ b/config/initializers/validation_error.rb @@ -6,7 +6,7 @@ ActionView::Base.field_error_proc = Proc.new do |html_tag, instance| error_class = "validation-error" error_message_class = "validation-error-message" - + if html_tag =~ /<(input|textarea|select)[^>]+class=/ style_attribute = html_tag =~ /class=['"]/ html_tag.insert(style_attribute + 7, "#{error_class} ") @@ -18,9 +18,9 @@ if html_tag =~ /<(label)/ html_tag elsif instance.error_message.kind_of?(Array) - %(#{html_tag} #{instance.error_message.join(', ')}).html_safe + %(#{html_tag} #{instance.error_message.first}).html_safe else %(#{html_tag} #{instance.error_message}).html_safe end - + end From 2956f792da8ff0ae4f07efa89104b70dccb0a9c8 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Sat, 8 Jun 2013 00:54:12 +0300 Subject: [PATCH 249/499] Label. --- app/views/registrations/new.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/registrations/new.html.haml b/app/views/registrations/new.html.haml index cd2e6661..26cb553f 100644 --- a/app/views/registrations/new.html.haml +++ b/app/views/registrations/new.html.haml @@ -5,7 +5,7 @@ %p.meta %strong Have an account already? — - = link_to 'sign in with password', new_session_path(:user) + = link_to 'Sign in!', new_session_path(:user) %section.oauth = link_to "/auth/github", class: "github" do From 5aca45e92d46bb93511032ab2994b9c101736e06 Mon Sep 17 00:00:00 2001 From: pietia Date: Sat, 8 Jun 2013 12:38:03 +0200 Subject: [PATCH 250/499] added default values for search method --- app/models/event.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/models/event.rb b/app/models/event.rb index 4f47b34a..7897ee79 100644 --- a/app/models/event.rb +++ b/app/models/event.rb @@ -36,7 +36,7 @@ def all_activities fetch_all_activities end - def search_activities(author, query_string, filter) + def search_activities(author = nil, query_string = "", filter = "all") query = all_activities # TODO: consider using squeel in the future (doesn't work well with rails 4.rc1 ...) query = query.with_name_like(query_string) if query_string.present? @@ -53,6 +53,7 @@ def search_activities(author, query_string, filter) end query end + alias_method :activities, :search_activities private From df00b227e13cdcd97e90b7ba9014ea69055c325e Mon Sep 17 00:00:00 2001 From: pietia Date: Sat, 8 Jun 2013 12:47:16 +0200 Subject: [PATCH 251/499] looks like #clone changed its behaviour in rails 4... --- spec/models/activity_spec.rb | 2 +- spec/models/participation_spec.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/models/activity_spec.rb b/spec/models/activity_spec.rb index 29a26409..fe0b9bd2 100644 --- a/spec/models/activity_spec.rb +++ b/spec/models/activity_spec.rb @@ -91,7 +91,7 @@ it { should accept_values_for(:name, "Football game" ) } it { should_not accept_values_for(:name, "", nil) } - specify { expect { FactoryGirl.create(:activity).clone.save! }.to raise_exception(ActiveRecord::RecordInvalid) } + specify { expect { FactoryGirl.create(:activity).dup.save! }.to raise_exception(ActiveRecord::RecordInvalid) } it { should accept_values_for(:description, nil, "", "Wear some solid boots!")} diff --git a/spec/models/participation_spec.rb b/spec/models/participation_spec.rb index 9b8622b2..d5092f27 100644 --- a/spec/models/participation_spec.rb +++ b/spec/models/participation_spec.rb @@ -10,7 +10,7 @@ it { should accept_values_for(:activity_id, 1 ) } it { should_not accept_values_for(:activity_id, "", nil) } - specify { expect { FactoryGirl.create(:participation).clone.save! }.to raise_exception(ActiveRecord::RecordInvalid) } + specify { expect { FactoryGirl.create(:participation).dup.save! }.to raise_exception(ActiveRecord::RecordInvalid) } end From 3b94e855823107a8d10aae37c39c2fc0f28bb284 Mon Sep 17 00:00:00 2001 From: pietia Date: Sat, 8 Jun 2013 12:56:19 +0200 Subject: [PATCH 252/499] cleanups --- app/models/event.rb | 2 +- spec/models/activity_spec.rb | 3 ++- spec/models/participation_spec.rb | 4 ++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/app/models/event.rb b/app/models/event.rb index 7897ee79..8a5604b8 100644 --- a/app/models/event.rb +++ b/app/models/event.rb @@ -38,7 +38,7 @@ def all_activities def search_activities(author = nil, query_string = "", filter = "all") query = all_activities - # TODO: consider using squeel in the future (doesn't work well with rails 4.rc1 ...) + # TODO: consider using squeel in the future (doesn't work well with rails 4.beta1 ...) query = query.with_name_like(query_string) if query_string.present? if filter.present? query = if filter == "today" diff --git a/spec/models/activity_spec.rb b/spec/models/activity_spec.rb index fe0b9bd2..ae9c69e4 100644 --- a/spec/models/activity_spec.rb +++ b/spec/models/activity_spec.rb @@ -89,9 +89,10 @@ describe "validations" do + specify { expect { FactoryGirl.create(:activity).dup.save! }.to raise_exception(ActiveRecord::RecordInvalid) } + it { should accept_values_for(:name, "Football game" ) } it { should_not accept_values_for(:name, "", nil) } - specify { expect { FactoryGirl.create(:activity).dup.save! }.to raise_exception(ActiveRecord::RecordInvalid) } it { should accept_values_for(:description, nil, "", "Wear some solid boots!")} diff --git a/spec/models/participation_spec.rb b/spec/models/participation_spec.rb index d5092f27..0748c9ea 100644 --- a/spec/models/participation_spec.rb +++ b/spec/models/participation_spec.rb @@ -4,14 +4,14 @@ describe "validations" do + specify { expect { FactoryGirl.create(:participation).dup.save! }.to raise_exception(ActiveRecord::RecordInvalid) } + it { should accept_values_for(:user_id, 1 ) } it { should_not accept_values_for(:user_id, "", nil) } it { should accept_values_for(:activity_id, 1 ) } it { should_not accept_values_for(:activity_id, "", nil) } - specify { expect { FactoryGirl.create(:participation).dup.save! }.to raise_exception(ActiveRecord::RecordInvalid) } - end end From 787eaf19b75f0d8064651089e947c72b29afb90f Mon Sep 17 00:00:00 2001 From: pietia Date: Sat, 8 Jun 2013 13:07:57 +0200 Subject: [PATCH 253/499] better way of setting default value --- app/models/activity.rb | 9 ++------- ...130608105751_set_default_for_limit_of_participants.rb | 5 +++++ db/schema.rb | 4 ++-- spec/models/activity_spec.rb | 4 ++++ 4 files changed, 13 insertions(+), 9 deletions(-) create mode 100644 db/migrate/20130608105751_set_default_for_limit_of_participants.rb diff --git a/app/models/activity.rb b/app/models/activity.rb index a3c626ad..40058c59 100644 --- a/app/models/activity.rb +++ b/app/models/activity.rb @@ -19,8 +19,7 @@ class Activity < ActiveRecord::Base validates :event, presence: true before_validation :clear_time_frame, if: ->{ anytime } - after_initialize :set_defaults - + class << self def recent(limit = DEFAULT_LIMIT) where("start_time >= :t OR anytime = true", t: 1.month.ago) @@ -87,11 +86,7 @@ def participation_source def clear_time_frame self.start_time, self.end_time = nil, nil end - - def set_defaults - self.limit_of_participants ||= 10 - end - + def time_frame_order errors.add(:end_time, I18n.t("activities.errors.end_time.before_start")) if end_time < start_time end diff --git a/db/migrate/20130608105751_set_default_for_limit_of_participants.rb b/db/migrate/20130608105751_set_default_for_limit_of_participants.rb new file mode 100644 index 00000000..5cf7117f --- /dev/null +++ b/db/migrate/20130608105751_set_default_for_limit_of_participants.rb @@ -0,0 +1,5 @@ +class SetDefaultForLimitOfParticipants < ActiveRecord::Migration + def change + change_column :activities, :limit_of_participants, :integer, default: 10 + end +end diff --git a/db/schema.rb b/db/schema.rb index f9794c37..f1244032 100644 --- a/db/schema.rb +++ b/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: 20130607184825) do +ActiveRecord::Schema.define(version: 20130608105751) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -21,7 +21,7 @@ t.text "description" t.string "location" t.datetime "start_time" - t.integer "limit_of_participants" + t.integer "limit_of_participants", default: 10 t.datetime "created_at" t.datetime "updated_at" t.integer "creator_id" diff --git a/spec/models/activity_spec.rb b/spec/models/activity_spec.rb index ae9c69e4..bec0382f 100644 --- a/spec/models/activity_spec.rb +++ b/spec/models/activity_spec.rb @@ -6,6 +6,10 @@ let(:creator) { User.new } subject(:activity) { Activity.new } + describe "#new" do + its(:limit_of_participants) { should == 10 } + end + describe "#new_participation" do let(:user) { mock_model(User) } let(:new_participation) { OpenStruct.new } From 7f85be6c6a57ab92dd22a5a3aaf05e04391b946b Mon Sep 17 00:00:00 2001 From: pietia Date: Sat, 8 Jun 2013 16:11:54 +0200 Subject: [PATCH 254/499] more specs for validations: Activity --- spec/models/activity_spec.rb | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/spec/models/activity_spec.rb b/spec/models/activity_spec.rb index bec0382f..53f48570 100644 --- a/spec/models/activity_spec.rb +++ b/spec/models/activity_spec.rb @@ -112,6 +112,23 @@ it { should accept_values_for(:limit_of_participants, nil, 12, 100) } it { should_not accept_values_for(:limit_of_participants, -1, 0) } + context "invalid time frame (wrong order)" do + subject { FactoryGirl.build(:activity, start_time: 10.days.ago.to_time, anytime: false) } + + it { should_not accept_values_for(:end_time, 15.days.ago.to_time) } + end + + context "invalid time frame (out of scope)" do + let(:event) { Event.new("A name", 2.days.ago.to_time, 2.days.from_now.to_time ) } + subject { FactoryGirl.build(:activity, event: event, start_time: 10.days.ago.to_time, anytime: false) } + + it { should accept_values_for(:start_time, 1.days.ago.to_time) } + it { should_not accept_values_for(:start_time, 15.days.ago.to_time) } + + it { should accept_values_for(:end_time, 1.days.from_now.to_time) } + it { should_not accept_values_for(:end_time, 3.days.from_now.to_time) } + end + end end From ce208c202696401030d242a41d545997c71410ce Mon Sep 17 00:00:00 2001 From: pietia Date: Sat, 8 Jun 2013 16:39:09 +0200 Subject: [PATCH 255/499] anybody_can_join? / today? tested --- spec/factories/activities.rb | 1 + spec/models/activity_spec.rb | 52 ++++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+) diff --git a/spec/factories/activities.rb b/spec/factories/activities.rb index 03ae0e0a..9d6f973d 100644 --- a/spec/factories/activities.rb +++ b/spec/factories/activities.rb @@ -5,6 +5,7 @@ name "Party!" start_time "2013/12/12 18:00" end_time "2013/12/13 03:00" + anytime false location "Ballroom" creator { FactoryGirl.create(:user) } event { Event.new } diff --git a/spec/models/activity_spec.rb b/spec/models/activity_spec.rb index 53f48570..6a872d5e 100644 --- a/spec/models/activity_spec.rb +++ b/spec/models/activity_spec.rb @@ -91,6 +91,58 @@ end end + describe "#anybody_can_join?" do + subject { activity.anybody_can_join? } + + context "no limit set" do + before do + activity.stub!(:limit_of_participants).and_return(nil) + end + + it { should == true } + end + + context "limit set" do + before do + activity.stub!(:limit_of_participants).and_return(10) + end + + it { should == false } + end + end + + describe "#today?" do + subject { activity.today? } + + context "anytime set" do + before do + activity.stub!(:anytime?).and_return(true) + end + + it { should == true } + end + + context "today" do + before do + activity.stub!(:start_time).and_return(2.days.ago.to_time) + activity.stub!(:end_time).and_return(2.days.from_now.to_time) + end + + it { should == true } + end + + context "not today" do + before do + activity.stub!(:anytime?).and_return(false) + activity.stub!(:start_time).and_return(2.days.from_now.to_time) + activity.stub!(:end_time).and_return(10.days.from_now.to_time) + end + + it { should == false } + end + + end + describe "validations" do specify { expect { FactoryGirl.create(:activity).dup.save! }.to raise_exception(ActiveRecord::RecordInvalid) } From d673fd563a44689846e02ebaf61aa1d116e70a9e Mon Sep 17 00:00:00 2001 From: pietia Date: Wed, 12 Jun 2013 11:32:39 +0200 Subject: [PATCH 256/499] proper time frame --- spec/factories/activities.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/factories/activities.rb b/spec/factories/activities.rb index 9d6f973d..3730448d 100644 --- a/spec/factories/activities.rb +++ b/spec/factories/activities.rb @@ -8,6 +8,6 @@ anytime false location "Ballroom" creator { FactoryGirl.create(:user) } - event { Event.new } + event { Event.new("Some Conference", Time.parse("2013/12/10 18:00"), Time.parse("2013/12/18 18:00")) } end end From 0dea3e611d8ae94f1b88688640fc2e15803febef Mon Sep 17 00:00:00 2001 From: pietia Date: Wed, 12 Jun 2013 12:36:58 +0200 Subject: [PATCH 257/499] more specs for models --- app/models/authentication.rb | 5 ++ spec/models/activity_spec.rb | 50 +++++++++++----- spec/models/authentication_spec.rb | 16 +++++ spec/models/event_spec.rb | 96 +++++++++++++++++++++++++++++- 4 files changed, 149 insertions(+), 18 deletions(-) create mode 100644 spec/models/authentication_spec.rb diff --git a/app/models/authentication.rb b/app/models/authentication.rb index 69a2df10..39092e56 100644 --- a/app/models/authentication.rb +++ b/app/models/authentication.rb @@ -1,3 +1,8 @@ class Authentication < ActiveRecord::Base belongs_to :user + + validates :user_id, presence: true + validates :provider, presence: true, uniqueness: { scope: [:user_id] } + validates :uid, presence: true + end diff --git a/spec/models/activity_spec.rb b/spec/models/activity_spec.rb index 6a872d5e..ee6d509d 100644 --- a/spec/models/activity_spec.rb +++ b/spec/models/activity_spec.rb @@ -10,21 +10,6 @@ its(:limit_of_participants) { should == 10 } end - describe "#new_participation" do - let(:user) { mock_model(User) } - let(:new_participation) { OpenStruct.new } - let(:args) { {} } - subject { activity.new_participation(user) } - - before do - activity.participation_source = ->{ new_participation } - end - - its(:participant) { should == user } - its(:activity) { should == activity } - it { should == new_participation } - end - describe "#creator" do before do activity.creator = creator @@ -143,6 +128,41 @@ end + describe "#upcoming?" do + subject { activity.upcoming? } + + context "in the future" do + before do + activity.stub!(:start_time).and_return(1.days.from_now.to_time) + end + + it { should == true } + end + + context "not in the future" do + before do + activity.stub!(:start_time).and_return(Time.now) + end + + it { should == false } + end + end + + describe "#new_participation" do + let(:user) { mock_model(User) } + let(:new_participation) { OpenStruct.new } + let(:args) { {} } + subject { activity.new_participation(user) } + + before do + activity.participation_source = ->{ new_participation } + end + + its(:participant) { should == user } + its(:activity) { should == activity } + it { should == new_participation } + end + describe "validations" do specify { expect { FactoryGirl.create(:activity).dup.save! }.to raise_exception(ActiveRecord::RecordInvalid) } diff --git a/spec/models/authentication_spec.rb b/spec/models/authentication_spec.rb new file mode 100644 index 00000000..d20de809 --- /dev/null +++ b/spec/models/authentication_spec.rb @@ -0,0 +1,16 @@ +require 'spec_helper' + +describe Authentication do + subject { Authentication.new } + + specify { expect { FactoryGirl.create(:activity).dup.save! }.to raise_exception(ActiveRecord::RecordInvalid) } + + it { should accept_values_for(:user_id, 10) } + it { should_not accept_values_for(:user_id, nil, "") } + + it { should accept_values_for(:provider, "github", "twitter") } + it { should_not accept_values_for(:provider, nil, "") } + + it { should accept_values_for(:uid, "asd123dasd", "x") } + it { should_not accept_values_for(:uid, nil, "") } +end \ No newline at end of file diff --git a/spec/models/event_spec.rb b/spec/models/event_spec.rb index 2be89c64..41685778 100644 --- a/spec/models/event_spec.rb +++ b/spec/models/event_spec.rb @@ -2,12 +2,12 @@ describe Event do - subject(:event) { Event.new("Eurucamp", Date.parse("2012-10-10"), Date.parse("2012-12-12")) } + subject(:event) { Event.new("Eurucamp", Date.parse("2012-10-10"), Date.parse("2012-12-14")) } describe "#new" do its(:name) { should == "Eurucamp" } its(:start_time) { should == Date.parse("2012-10-10") } - its(:end_time) { should == Date.parse("2012-12-12") } + its(:end_time) { should == Date.parse("2012-12-14") } end describe "#new_activity" do @@ -24,7 +24,26 @@ it { should == new_activity } end - describe "#activities" do + describe "#activity" do + let(:activity) { FactoryGirl.create(:activity, event: event, start_time: "2012-10-11", end_time: "2012-10-13") } + subject { event.activity(activity) } + + it { should == activity } + its(:event) { should == event } + end + + describe "#recent_activities" do + let(:recent_activities) { [mock_model(Activity)] } + subject { event.recent_activities } + + before do + event.recent_activities_fetcher = ->{ recent_activities } + end + + it { should == recent_activities } + end + + describe "#all_activities" do let(:activities) { [mock(:activity1), mock(:activity2)] } let(:event) { Event.new } subject { event.activities } @@ -36,4 +55,75 @@ it { should == activities } end + describe "#search_activities" do + let(:activities) { [mock(:activity1), mock(:activity2)] } + + context "default args" do + subject { event.search_activities } + + before do + event.all_activities_fetcher = ->{ activities } + end + + it { should == activities } + end + + context "by name" do + subject { event.search_activities(nil, "yuppy", "all") } + + before do + Activity.should_receive(:with_name_like).with("yuppy").and_return(activities) + end + + it { should == activities } + end + + context "with filter" do + let(:user) { mock_model(User) } + subject { event.search_activities(user, "", filter) } + + context "owner" do + let(:filter) { "owner" } + + before do + Activity.stub_chain(:all_activities, :created_by).and_return(activities) + end + + it { should == activities } + end + + context "participant" do + let(:filter) { "participant" } + + before do + Activity.stub_chain(:all_activities, :participated_by).and_return(activities) + end + + it { should == activities } + end + + context "today" do + let(:filter) { "today" } + + before do + Activity.stub_chain(:all_activities, :today).and_return(activities) + end + + it { should == activities } + end + + context "wrong filter" do + let(:filter) { "ownerx" } + + before do + Activity.stub_chain(:all_activities).and_return(activities) + end + + it { should == activities } + end + + end + + end + end \ No newline at end of file From bde82c19a0b55838a2801bdc76cc24f07700ffab Mon Sep 17 00:00:00 2001 From: pietia Date: Wed, 12 Jun 2013 12:40:28 +0200 Subject: [PATCH 258/499] cleanups --- app/models/user.rb | 3 --- 1 file changed, 3 deletions(-) diff --git a/app/models/user.rb b/app/models/user.rb index e30c8858..281ccc9e 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -1,7 +1,4 @@ class User < ActiveRecord::Base - # Include default devise modules. Others available are: - # :token_authenticatable, :confirmable, - # :lockable, :timeoutable and :omniauthable devise :database_authenticatable, :registerable, :recoverable, :rememberable, :trackable, :validatable From e8bc719621d9b5849529e248e61cc9bef5871a4e Mon Sep 17 00:00:00 2001 From: pietia Date: Wed, 12 Jun 2013 12:41:05 +0200 Subject: [PATCH 259/499] cleanups --- app/models/user.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/app/models/user.rb b/app/models/user.rb index 281ccc9e..8b5aeda1 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -6,7 +6,6 @@ class User < ActiveRecord::Base has_many :participations, :dependent => :destroy has_many :activities, through: :participations - #validates :name, presence: true validates :email, presence: true # TODO: extract to module and then to a gem / engine From cc827e0daa154638bdfc3b69a52f3aae9378e6c0 Mon Sep 17 00:00:00 2001 From: pietia Date: Wed, 12 Jun 2013 12:45:53 +0200 Subject: [PATCH 260/499] cleanups --- app/models/user.rb | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/app/models/user.rb b/app/models/user.rb index 8b5aeda1..138a2118 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -23,6 +23,7 @@ def apply_omniauth(omniauth) def apply_provider_handle(omniauth) provider, info = omniauth.values_at('provider', 'info') + # su*ks - refactor it case provider when /github/ self.github_handle = info['nickname'] if github_handle.blank? @@ -50,10 +51,6 @@ def no_oauth_connected? !any_oauth_connected? && encrypted_password.present? end - def any_oauth_connected? - authentications.any? - end - def connected_with_twitter? authentications.where(provider: 'twitter').any? end @@ -62,4 +59,10 @@ def connected_with_github? authentications.where(provider: 'github').any? end + private + + def any_oauth_connected? + authentications.any? + end + end From bb5d8b1b9dd45f15d64dabcaf4b2807a969f75fb Mon Sep 17 00:00:00 2001 From: pietia Date: Wed, 12 Jun 2013 13:00:06 +0200 Subject: [PATCH 261/499] better associations names --- app/models/activity.rb | 2 +- app/models/user.rb | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/app/models/activity.rb b/app/models/activity.rb index 40058c59..2882ea3c 100644 --- a/app/models/activity.rb +++ b/app/models/activity.rb @@ -5,7 +5,7 @@ class Activity < ActiveRecord::Base attr_writer :participation_source # DI belongs_to :creator, class_name: "User" - has_many :participations, :dependent => :destroy + has_many :participations, dependent: :destroy has_many :participants, through: :participations, class_name: "User" validates :start_time, presence: true, allow_blank: false, if: ->{ !anytime? } diff --git a/app/models/user.rb b/app/models/user.rb index 138a2118..43293b91 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -2,9 +2,10 @@ class User < ActiveRecord::Base devise :database_authenticatable, :registerable, :recoverable, :rememberable, :trackable, :validatable - has_many :authentications - has_many :participations, :dependent => :destroy - has_many :activities, through: :participations + has_many :authentications, dependent: :destroy + has_many :participations, dependent: :destroy + has_many :created_activities, class_name: "Activity", foreign_key: "creator_id", dependent: :nullify + has_many :activities_participated_in, through: :participations validates :email, presence: true From fa8d873bf3cd55df294750290d01b03de247e3e4 Mon Sep 17 00:00:00 2001 From: pietia Date: Wed, 12 Jun 2013 13:01:51 +0200 Subject: [PATCH 262/499] don't fail if creator removed his account --- app/decorators/activity_decorator.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/decorators/activity_decorator.rb b/app/decorators/activity_decorator.rb index 9cb5fdca..5da24334 100644 --- a/app/decorators/activity_decorator.rb +++ b/app/decorators/activity_decorator.rb @@ -2,7 +2,7 @@ class ActivityDecorator < Draper::Decorator delegate_all def creator_name - creator.name + creator.try(:name) end def relation_ship_with(user) From 88d12b4d9588e6fc74b38a2e313f20027e70462d Mon Sep 17 00:00:00 2001 From: pietia Date: Wed, 12 Jun 2013 16:13:21 +0200 Subject: [PATCH 263/499] User - simple specs added --- app/models/user.rb | 8 ++- spec/models/user_spec.rb | 144 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 149 insertions(+), 3 deletions(-) diff --git a/app/models/user.rb b/app/models/user.rb index 43293b91..15e266fd 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -13,8 +13,8 @@ class User < ActiveRecord::Base def apply_omniauth(omniauth) provider, uid, info = omniauth.values_at('provider', 'uid', 'info') unless info.blank? - self.email = info['email'] if email.blank? - self.name = info['name'] if name.blank? + self.email = info['email'] unless email.blank? + self.name = info['name'] unless name.blank? end apply_provider_handle(omniauth) @@ -22,7 +22,8 @@ def apply_omniauth(omniauth) end def apply_provider_handle(omniauth) - provider, info = omniauth.values_at('provider', 'info') + provider = omniauth['provider'] + info = omniauth['info'] || {} # su*ks - refactor it case provider @@ -31,6 +32,7 @@ def apply_provider_handle(omniauth) when /twitter/ self.twitter_handle = info['nickname'] if twitter_handle.blank? end + self end def update_without_password(params, *options) diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index d65c4482..59b766dd 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -2,6 +2,150 @@ describe User do + let(:user) { FactoryGirl.create(:user) } + + describe "#connected_with_twitter?" do + subject { user.connected_with_twitter? } + + context "not connected with Twitter account" do + it { should == false} + end + + context "connected with Twitter account" do + before do + FactoryGirl.create(:authentication, user: user, provider: "twitter") + end + + it { should == true } + end + + end + + describe "#connected_with_github?" do + subject { user.connected_with_github? } + + context "not connected with Github account" do + it { should == false} + end + + context "connected with Github account" do + before do + FactoryGirl.create(:authentication, user: user, provider: "github") + end + + it { should == true } + end + end + + describe "#apply_provider_handle" do + subject { user.apply_provider_handle(params) } + + context "no data provided" do + let(:params) { {} } + + it { should == user } + end + + context "data provided" do + let(:params) { { "uid" => "123ASD", "provider" => "github", "info" => {"nickname" => "johnny"} } } + + specify { expect{subject}.to change(user, :github_handle).to("johnny") } + + it { should == user } + end + + end + + describe "#no_oauth_connected?" do + subject { user.no_oauth_connected? } + + context "account connected" do + let(:authentication) { FactoryGirl.create(:authentication, user: user) } + + context "password set" do + it { should == true } + end + end + + context "no account connected" do + context "password set" do + it { should == true } + end + + context "password not set" do + let(:user) { User.new } + it { should == false } + end + end + end + + describe "#password_required?" do + subject { user.password_required? } + + context "no account connected" do + it { should == true } + end + + context "account connected" do + let!(:authentication) { FactoryGirl.create(:authentication, user: user) } + + it { should == false } + end + + end + + describe "#update_without_password" do + subject { user.update_without_password(params) } + + context "no password provided" do + let(:params) { {name: "Zbigniew" } } + + specify { expect { subject }.to change(user, :name) } + + specify { expect { subject }.to_not change(user, :encrypted_password) } + end + + context "password" do + let(:params) { {name: "Zbigniew", password: "qweqwe123123", password_confirmation: "qweqweqwe123123" } } + + specify { expect { subject }.to change(user, :name) } + + specify { expect { subject }.to change(user, :encrypted_password) } + end + end + + describe "#apply_omniauth" do + subject { user.apply_omniauth(params) } + + context "no data provided" do + let(:params) { {} } + + it { should be_a_kind_of(Authentication) } + its(:user) { should == user } + it { should be_invalid } + end + + context "data provided" do + context "with info" do + let(:params) { { 'info' => {"email" => "ala@ala.com"} } } + + specify { expect{subject}.to change(user, :email).to("ala@ala.com") } + + it { should be_a_kind_of(Authentication) } + its(:user) { should == user } + end + + context "without info" do + let(:params) { { "uid" => "123ASD", "provider" => "github" } } + + specify { expect{subject}.to_not change(user, :email) } + + it { should be_a_kind_of(Authentication) } + its(:user) { should == user } + end + end + end + describe "validations" do subject { User.new } From c4db039cc98595f7694c0c4ffb1820a626684cb6 Mon Sep 17 00:00:00 2001 From: pietia Date: Fri, 21 Jun 2013 14:29:49 +0200 Subject: [PATCH 264/499] cleanups p.1, refactoring --- app/controllers/activities_controller.rb | 19 +-- app/models/activity.rb | 1 + app/models/event.rb | 9 ++ .../controllers/activities_controller_spec.rb | 147 ++++++++++++++++-- 4 files changed, 150 insertions(+), 26 deletions(-) diff --git a/app/controllers/activities_controller.rb b/app/controllers/activities_controller.rb index 725d0753..0769764c 100644 --- a/app/controllers/activities_controller.rb +++ b/app/controllers/activities_controller.rb @@ -7,12 +7,7 @@ class ActivitiesController < ApiController def index @activities = current_event.search_activities(current_user, query_params[:search], query_params[:filter]) - @counters = { - today: Activity.today.count, - all: Activity.count, - participant: Activity.participated_by(current_user).count, - owner: Activity.created_by(current_user).count - } + @counters = current_event.counters(current_user) respond_with(@activities) end @@ -26,7 +21,7 @@ def new end def edit - respond_with(@activity, status: :not_found) + respond_with(@activity) end def create @@ -37,20 +32,22 @@ def create end def update - type = @activity.update_attributes(sanitized_params) ? :notice : :error - flash[type] = I18n.t("edit_activity.#{type}") + if @activity + type = @activity.update_attributes(sanitized_params) ? :notice : :error + flash[type] = I18n.t("edit_activity.#{type}") + end respond_with(@activity, location: edit_activity_path(@activity)) end def destroy - @activity.destroy + @activity.destroy if @activity respond_with(@activity, location: activities_path) end private def load_resource - @activity = current_event.activity(params[:id]).decorate + @activity = current_event.activity(params[:id]).try(:decorate) end def sanitized_params diff --git a/app/models/activity.rb b/app/models/activity.rb index 2882ea3c..0cc970dd 100644 --- a/app/models/activity.rb +++ b/app/models/activity.rb @@ -46,6 +46,7 @@ def created_by(user) def participated_by(user) includes(:participations).where(participations: { user_id: user }) end + end def full_by diff --git a/app/models/event.rb b/app/models/event.rb index 8a5604b8..35110d9b 100644 --- a/app/models/event.rb +++ b/app/models/event.rb @@ -22,6 +22,15 @@ def new_activity(author, *args) end end + def counters(user) + { + today: Activity.today.count, + all: Activity.count, + participant: Activity.participated_by(user).count, + owner: Activity.created_by(user).count + } + end + def activity(activity_id) all_activities.where(:id => activity_id).first.tap do |activity| activity.event = self if activity diff --git a/spec/controllers/activities_controller_spec.rb b/spec/controllers/activities_controller_spec.rb index ab82bf67..5cc769ad 100644 --- a/spec/controllers/activities_controller_spec.rb +++ b/spec/controllers/activities_controller_spec.rb @@ -1,69 +1,186 @@ require 'spec_helper' describe ActivitiesController do - let(:user) { FactoryGirl.create(:user) } - let(:activity) { FactoryGirl.create(:activity) } + let(:current_user) { mock_model(User) } + + let(:activity) { mock(:activity) } + let(:activity_id) { "1" } + let(:invalid_activity) { mock(:invalid_activity, errors: {name: "no way!"}) } + let(:current_event) { mock(:current_event) } + + def should_authorize(action, subject) + controller.should_receive(:authorize!).with(action, subject).and_return('passed!') + end + + def sign_in(user = double('user')) + if user.nil? + request.env['warden'].stub(:authenticate!). + and_throw(:warden, {:scope => :user}) + controller.stub :current_user => nil + else + request.env['warden'].stub :authenticate! => user + controller.stub :current_user => user + end + end + + before do + controller.stub!(:current_event).and_return(current_event) + end describe "#index" do subject { get :index } + let(:current_user) { nil } + let(:activities) { mock(:activities) } + let(:counters) { mock(:counters) } + + before do + current_event.should_receive(:search_activities).with(current_user, nil, nil).and_return(activities) + current_event.should_receive(:counters).with(current_user).and_return(counters) + end + it { should render_template(:index) } - its(:status){ should == 200 } + end + + describe "#new" do + subject { get :new } + + before do + sign_in(current_user) + current_event.should_receive(:new_activity).with(current_user, {}).and_return(activity) + end + + it { should render_template(:new) } + end + + describe "#edit" do + subject { get :edit, id: activity_id } + + before do + sign_in(current_user) + current_event.should_receive(:activity).with(activity_id).and_return(activity) + end + + context "activity doesn't exist" do + let(:activity) { nil } + + specify { expect { subject }.to raise_error(ActionController::RoutingError) } + end + + context "activity exists" do + + before do + activity.should_receive(:decorate).and_return(activity) + should_authorize(:edit, activity) + end + + it { should render_template(:edit) } + end + end describe "#show" do - subject { get :show, {id: activity.id} } + subject { get :show, id: activity_id } + + before do + sign_in(current_user) + current_event.should_receive(:activity).with(activity_id).and_return(activity) + end + + context "activity doesn't exist" do + let(:activity) { nil } + + specify { expect { subject }.to raise_error(ActionController::RoutingError) } + end - it { should render_template(:show) } - its(:status){ should == 200 } + context "activity exists" do + + before do + activity.should_receive(:decorate).and_return(activity) + end + + it { should render_template(:show) } + end end describe "#create" do subject { post :create, params } before do - sign_in(user) + sign_in(current_user) end context "valid parameters" do - let(:params) { {activity: {name: "Pool party", start_time: 2.days.from_now.to_s, end_time: 2.days.from_now.to_s, location: "Pool" }} } + let(:attributes) { {location: "Location", name: "Name", start_time: 1.day.ago.to_s, end_time: 2.days.ago.to_s} } + let(:params) { {activity: attributes} } + + before do + current_event.should_receive(:new_activity).with(current_user, attributes.with_indifferent_access).and_return(activity) + activity.should_receive(:save).and_return(true) + end + it { should redirect_to activities_path } end context "invalid parameters" do + let(:activity) { invalid_activity } let(:params) { {activity: {x: 10}} } + + before do + current_event.should_receive(:new_activity).with(current_user, {}).and_return(activity) + activity.should_receive(:save).and_return(false) + end + it { should render_template(:new) } end end describe "#update" do subject { put :update, params } - let!(:activity) { FactoryGirl.create(:activity, creator: user) } + let(:params) { {id: activity_id, activity: attributes} } before do - sign_in(user) + sign_in(current_user) + current_event.should_receive(:activity).with(activity_id).and_return(activity) + activity.should_receive(:decorate).and_return(activity) + should_authorize(:update, activity) end context "valid parameters" do - let(:params) { {id: activity.id, activity: {name: "Pool party", start_time: 2.days.from_now.to_s, end_time: 3.days.from_now.to_s, location: "Pool" }} } + let(:attributes) { {location: "Location", name: "Name", start_time: 1.day.ago.to_s, end_time: 2.days.ago.to_s} } + + before do + activity.should_receive(:update_attributes).with(attributes.with_indifferent_access).and_return(true) + end + it { should redirect_to edit_activity_path(activity) } end context "invalid parameters" do - let(:params) { {id: activity.id, activity: {location: ""}} } + let(:attributes) { {location: ""} } + let(:activity) { invalid_activity } + + before do + activity.should_receive(:update_attributes).with(attributes.with_indifferent_access).and_return(false) + end + it { should render_template(:edit) } end end describe "#destroy" do - subject { delete :destroy, {id: activity.id} } - let!(:activity) { FactoryGirl.create(:activity, creator: user) } + subject { delete :destroy, {id: activity_id} } before do - sign_in(user) + sign_in(current_user) + current_event.should_receive(:activity).with(activity_id).and_return(activity) + activity.should_receive(:decorate).and_return(activity) + should_authorize(:destroy, activity) + activity.should_receive(:destroy).and_return(true) end it { should redirect_to activities_path } + end end \ No newline at end of file From 9085d44175627648424ebd5121a9ee07347b4f36 Mon Sep 17 00:00:00 2001 From: pietia Date: Fri, 21 Jun 2013 14:32:10 +0200 Subject: [PATCH 265/499] extracted helpers --- spec/controllers/activities_controller_spec.rb | 15 --------------- spec/spec_helper.rb | 1 + spec/support/helpers/controller_helpers.rb | 16 ++++++++++++++++ 3 files changed, 17 insertions(+), 15 deletions(-) create mode 100644 spec/support/helpers/controller_helpers.rb diff --git a/spec/controllers/activities_controller_spec.rb b/spec/controllers/activities_controller_spec.rb index 5cc769ad..361b0488 100644 --- a/spec/controllers/activities_controller_spec.rb +++ b/spec/controllers/activities_controller_spec.rb @@ -8,21 +8,6 @@ let(:invalid_activity) { mock(:invalid_activity, errors: {name: "no way!"}) } let(:current_event) { mock(:current_event) } - def should_authorize(action, subject) - controller.should_receive(:authorize!).with(action, subject).and_return('passed!') - end - - def sign_in(user = double('user')) - if user.nil? - request.env['warden'].stub(:authenticate!). - and_throw(:warden, {:scope => :user}) - controller.stub :current_user => nil - else - request.env['warden'].stub :authenticate! => user - controller.stub :current_user => user - end - end - before do controller.stub!(:current_event).and_return(current_event) end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index a8efc32f..05d80fd6 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -78,5 +78,6 @@ def self.connection config.order = "random" config.include Devise::TestHelpers, type: :controller + config.include ControllerHelpers, type: :controller config.include JsonSpec::Helpers, type: :controller end diff --git a/spec/support/helpers/controller_helpers.rb b/spec/support/helpers/controller_helpers.rb new file mode 100644 index 00000000..978230e0 --- /dev/null +++ b/spec/support/helpers/controller_helpers.rb @@ -0,0 +1,16 @@ +module ControllerHelpers + def should_authorize(action, subject) + controller.should_receive(:authorize!).with(action, subject).and_return(true) + end + + def sign_in(user = double('user')) + if user.nil? + request.env['warden'].stub(:authenticate!). + and_throw(:warden, {:scope => :user}) + controller.stub :current_user => nil + else + request.env['warden'].stub :authenticate! => user + controller.stub :current_user => user + end + end +end \ No newline at end of file From 43acb54a784f61c8254852e0779a06cee0961c27 Mon Sep 17 00:00:00 2001 From: pietia Date: Fri, 21 Jun 2013 15:14:17 +0200 Subject: [PATCH 266/499] more refactoring in controller specs --- app/controllers/authentications_controller.rb | 1 + app/controllers/participations_controller.rb | 2 +- lib/ability.rb | 1 + .../authentications_controller_spec.rb | 26 ++++++++---- .../participation_controller_spec.rb | 42 +++++++++++++++---- 5 files changed, 54 insertions(+), 18 deletions(-) diff --git a/app/controllers/authentications_controller.rb b/app/controllers/authentications_controller.rb index a088019f..4f98faee 100644 --- a/app/controllers/authentications_controller.rb +++ b/app/controllers/authentications_controller.rb @@ -34,6 +34,7 @@ def create def destroy @authentication = current_user.authentications.find(params[:id]) + authorize!(:destroy, @authentication) @authentication.destroy flash[:notice] = 'Successfully disconnected provider' diff --git a/app/controllers/participations_controller.rb b/app/controllers/participations_controller.rb index 3687207d..abbfc023 100644 --- a/app/controllers/participations_controller.rb +++ b/app/controllers/participations_controller.rb @@ -17,7 +17,7 @@ def destroy private def current_activity - @current_activity ||= current_event.activity(params[:activity_id]).decorate + @current_activity ||= current_event.activity(params[:activity_id]).try(:decorate) end end diff --git a/lib/ability.rb b/lib/ability.rb index ab28f9dd..05df2a23 100644 --- a/lib/ability.rb +++ b/lib/ability.rb @@ -4,5 +4,6 @@ class Ability def initialize(user) can [:edit, :update, :destroy], Activity, creator: user can :destroy, Participation, participant: user + can :destroy, Authentication, user: user end end diff --git a/spec/controllers/authentications_controller_spec.rb b/spec/controllers/authentications_controller_spec.rb index f65dd7f7..b09ef3e1 100644 --- a/spec/controllers/authentications_controller_spec.rb +++ b/spec/controllers/authentications_controller_spec.rb @@ -2,8 +2,14 @@ describe AuthenticationsController do + let(:current_user) { mock_model(User) } + let(:authentications) { mock(:authentications) } + let(:authentication) { mock(:authentication) } + let(:authentication_id) { "1" } + before do @request.env["devise.mapping"] = Devise.mappings[:user] + current_user.stub!(:authentications).and_return(authentications) end describe '#create' do @@ -27,7 +33,10 @@ let(:omniauth_data) { { provider: 'none', uuid: 'none' } } before do - sign_in FactoryGirl.create(:user) + sign_in(current_user) + authentications.should_receive(:find_or_create_by).with({provider: nil, uid: nil}) + current_user.should_receive(:apply_provider_handle).with(omniauth_data) + current_user.should_receive(:save).and_return(true) end context "when HTTP_REFERER set" do @@ -39,25 +48,24 @@ end context "when HTTP_REFERER not set" do - it { should redirect_to edit_user_registration_path } + + it { should redirect_to edit_user_registration_path } end end end describe "#delete" do - subject(:logout_action) { delete :destroy, id: authentication.id } - let(:authentication) { FactoryGirl.create(:authentication) } + subject(:logout_action) { delete :destroy, id: authentication_id } before do - sign_in authentication.user + sign_in(current_user) + authentications.should_receive(:find).with(authentication_id).and_return(authentication) + should_authorize(:destroy, authentication) + authentication.should_receive(:destroy).and_return(true) end it { should redirect_to authentications_path } - - specify do - expect { logout_action }.to change(Authentication, :count).by(-1) - end end end diff --git a/spec/controllers/participation_controller_spec.rb b/spec/controllers/participation_controller_spec.rb index 31440933..317b9ef6 100644 --- a/spec/controllers/participation_controller_spec.rb +++ b/spec/controllers/participation_controller_spec.rb @@ -1,24 +1,43 @@ require 'spec_helper' describe ParticipationsController do - let(:user) { FactoryGirl.create(:user) } - let(:activity) { FactoryGirl.create(:activity) } + let(:current_user) { mock_model(User) } + let(:activity_id) { "1" } + let(:activity) { mock(:activity) } + let(:participation) { mock(:participation) } + let(:invalid_participation) { mock(:participation, errors: {activity_id: "nahhhhh"}) } + let(:current_event) { mock(:current_event) } + + before do + controller.stub!(:current_event).and_return(current_event) + end describe "#create" do context "with :js format" do - subject { post :create, {activity_id: activity.id, format: :js} } + subject { post :create, {activity_id: activity_id, format: :js} } before do - sign_in(user) + sign_in(current_user) + + current_event.should_receive(:activity).with(activity_id).and_return(activity) + activity.should_receive(:decorate).and_return(activity) + activity.should_receive(:new_participation).with(current_user).and_return(participation) end context "valid parameters" do + + before do + participation.should_receive(:save).and_return(true) + end + it { should render_template(:create) } end context "user is already a participant" do + let(:participation) { invalid_participation } + before do - Participation.create!(participant: user, activity: activity) + participation.should_receive(:save).and_return(false) end # TODO: moar specs needed @@ -29,11 +48,18 @@ describe "#destroy" do context "with :js format" do - subject { delete :destroy, {activity_id: activity.id, format: :js} } + subject { delete :destroy, {activity_id: activity_id, format: :js} } before do - Participation.create!(participant: user, activity: activity) - sign_in(user) + sign_in(current_user) + + current_event.should_receive(:activity).with(activity_id).and_return(activity) + activity.should_receive(:decorate).and_return(activity) + + activity.should_receive(:participation).with(current_user).and_return(participation) + should_authorize(:destroy, participation) + + participation.should_receive(:destroy).and_return(true) end # TODO: moar specs needed From 3ab45f59f08263ee579f7594ecbc3118f3575487 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Sat, 22 Jun 2013 01:42:03 +0300 Subject: [PATCH 267/499] User forms. --- app/assets/stylesheets/_base.sass | 17 ++++-- app/assets/stylesheets/partials/_user.sass | 9 +++- .../partials/activities/_form.sass | 7 +-- .../partials/activities/_show.sass | 41 ++++----------- .../partials/user/_registrations.sass | 3 +- .../stylesheets/partials/user/_sessions.sass | 0 .../user/{_registration.sass => _shared.sass} | 52 +++++++------------ app/assets/stylesheets/utils/_buttons.sass | 7 +++ app/assets/stylesheets/utils/_forms.sass | 8 +++ app/views/registrations/_oauth.html.haml | 10 ++++ app/views/registrations/new.html.haml | 18 ++----- app/views/sessions/new.html.haml | 43 +++++++++------ config/environments/development.rb | 10 +++- 13 files changed, 120 insertions(+), 105 deletions(-) create mode 100644 app/assets/stylesheets/partials/user/_sessions.sass rename app/assets/stylesheets/partials/user/{_registration.sass => _shared.sass} (66%) create mode 100644 app/views/registrations/_oauth.html.haml diff --git a/app/assets/stylesheets/_base.sass b/app/assets/stylesheets/_base.sass index cef38103..3237722c 100644 --- a/app/assets/stylesheets/_base.sass +++ b/app/assets/stylesheets/_base.sass @@ -41,9 +41,13 @@ a:hover position: relative article + @extend %clearfix + margin: 0 0 3em 0 + header text-align: center margin: 0 0 4em 0 + position: relative h2 +custom-sans(light) @@ -53,11 +57,14 @@ article &:after margin-bottom: 0.25em - + .meta text-transform: uppercase @extend %smaller-text - - strong.divider - +custom-sans(bold) - margin: 0 0.5em + + strong + +custom-sans(medium) + + &.divider + +custom-sans(bold) + margin: 0 0.5em diff --git a/app/assets/stylesheets/partials/_user.sass b/app/assets/stylesheets/partials/_user.sass index 2fadf543..16a9cd17 100644 --- a/app/assets/stylesheets/partials/_user.sass +++ b/app/assets/stylesheets/partials/_user.sass @@ -1,2 +1,9 @@ +body.registrations, +body.sessions + @import "user/shared" + body.registrations - @import "user/registration" + @import "user/registrations" + +body.sessions + @import "user/sessions" \ No newline at end of file diff --git a/app/assets/stylesheets/partials/activities/_form.sass b/app/assets/stylesheets/partials/activities/_form.sass index 0ff1c355..361f9a6d 100644 --- a/app/assets/stylesheets/partials/activities/_form.sass +++ b/app/assets/stylesheets/partials/activities/_form.sass @@ -78,9 +78,4 @@ form#new-activity &.checking background-size: auto !important background-repeat: no-repeat !important - background-image: image-url('shared/checking.gif') !important - - button - @extend %large-icon-button-ok - display: block - margin: 1em auto 4em + background-image: image-url('shared/checking.gif') !important \ No newline at end of file diff --git a/app/assets/stylesheets/partials/activities/_show.sass b/app/assets/stylesheets/partials/activities/_show.sass index f5f363d1..4a8de6a1 100644 --- a/app/assets/stylesheets/partials/activities/_show.sass +++ b/app/assets/stylesheets/partials/activities/_show.sass @@ -1,35 +1,19 @@ $types: participant ok remove, owner edit edit #activity - @extend %clearfix - margin: 0 0 3em 0 + .action + position: absolute + right: 0 + top: 0 - header - text-align: center - margin: 0 0 3em 0 - position: relative - - h2 - margin: 0 - +custom-sans(medium) - - p - strong - +custom-sans(medium) - - .action - position: absolute - right: 0 - top: 0 + a, button + @extend %icon-button-ok - a, button - @extend %icon-button-ok - - // select action type based on card variation - @each $type in $types - &.#{nth($type, 1)} - a, button - @extend %icon-button-#{nth($type, 3)} + // select action type based on card variation + @each $type in $types + &.#{nth($type, 1)} + a, button + @extend %icon-button-#{nth($type, 3)} section width: percentage(1/3) @@ -91,6 +75,3 @@ $types: participant ok remove, owner edit edit dd +custom-serif margin: 0 - - - diff --git a/app/assets/stylesheets/partials/user/_registrations.sass b/app/assets/stylesheets/partials/user/_registrations.sass index 472677e2..f762b2aa 100644 --- a/app/assets/stylesheets/partials/user/_registrations.sass +++ b/app/assets/stylesheets/partials/user/_registrations.sass @@ -1,4 +1,5 @@ article section &.form - display: none + form + display: none \ No newline at end of file diff --git a/app/assets/stylesheets/partials/user/_sessions.sass b/app/assets/stylesheets/partials/user/_sessions.sass new file mode 100644 index 00000000..e69de29b diff --git a/app/assets/stylesheets/partials/user/_registration.sass b/app/assets/stylesheets/partials/user/_shared.sass similarity index 66% rename from app/assets/stylesheets/partials/user/_registration.sass rename to app/assets/stylesheets/partials/user/_shared.sass index a36c0b47..c0fc1aee 100644 --- a/app/assets/stylesheets/partials/user/_registration.sass +++ b/app/assets/stylesheets/partials/user/_shared.sass @@ -1,18 +1,24 @@ article - header - p.meta - strong - +custom-sans(medium) + margin: 0 150px + text-align: center + + h4 + +custom-sans(light) + font-size: 1.375em + padding: 0 0.727272em + text-align: center + + p + text-align: center + margin: 2em 0 3em 0 + + strong + margin: 0 0 1em 0 + text-transform: uppercase + +custom-sans(medium) + display: block section - margin: 0 150px - - h4 - +custom-sans(light) - font-size: 1.375em - padding: 0 0.727272em - text-align: center - &.oauth @extend %clearfix @@ -40,7 +46,6 @@ article &:before margin: 0.5em auto - &.github .container background-image: image-url('user/github.png') @@ -57,31 +62,12 @@ article +transition(box-shadow 0.25s ease-in-out) box-shadow: 0 0 20px transparentize($black, 0.9) - &.form - p - text-align: center - margin: 2em 0 3em 0 - strong - margin: 0 0 1em 0 - text-transform: uppercase - +custom-sans(medium) - display: block - - a.reveal - @extend %secondary-button - - #sign-up + form padding: 2em 0 0 width: percentage(2/3) margin: 0 auto - display: none h4 @extend %ornamental-divider-after - - button - margin: 2em auto 4em - display: block - @extend %large-icon-button-ok diff --git a/app/assets/stylesheets/utils/_buttons.sass b/app/assets/stylesheets/utils/_buttons.sass index 8eea8bfe..d6e1ba12 100644 --- a/app/assets/stylesheets/utils/_buttons.sass +++ b/app/assets/stylesheets/utils/_buttons.sass @@ -91,6 +91,7 @@ $ypos : 0 %secondary-button display: inline-block + text-align: center background: $medium-gray color: $white text-shadow: 0 1px 0 $gray @@ -101,6 +102,12 @@ $ypos : 0 border-radius: 3px +custom-sans(medium) +transition(all 0.35s ease-in-out) + + &:visited + color: $white &:hover background: $blue + +a.reveal + @extend %secondary-button \ No newline at end of file diff --git a/app/assets/stylesheets/utils/_forms.sass b/app/assets/stylesheets/utils/_forms.sass index c2c31f21..8781459d 100644 --- a/app/assets/stylesheets/utils/_forms.sass +++ b/app/assets/stylesheets/utils/_forms.sass @@ -1,4 +1,6 @@ form + text-align: center + input, textarea width: 100% padding: 0.75em 15px 0.9em @@ -37,6 +39,11 @@ form &.markdown background-image: none + + button + @extend %large-icon-button-ok + display: block + margin: 1em auto 4em fieldset border: none @@ -89,3 +96,4 @@ form display: block color: $medium-gray padding: 0.5em 15px + \ No newline at end of file diff --git a/app/views/registrations/_oauth.html.haml b/app/views/registrations/_oauth.html.haml new file mode 100644 index 00000000..2187ce0a --- /dev/null +++ b/app/views/registrations/_oauth.html.haml @@ -0,0 +1,10 @@ +%section.oauth + = link_to "/auth/github", class: "github" do + .container + %h4 + Sign in with Github + + = link_to "/auth/twitter", class: "twitter" do + .container + %h4 + Sign in with Twitter diff --git a/app/views/registrations/new.html.haml b/app/views/registrations/new.html.haml index 26cb553f..46744317 100644 --- a/app/views/registrations/new.html.haml +++ b/app/views/registrations/new.html.haml @@ -7,23 +7,15 @@ — = link_to 'Sign in!', new_session_path(:user) - %section.oauth - = link_to "/auth/github", class: "github" do - .container - %h4 - Sign in with Github - - = link_to "/auth/twitter", class: "twitter" do - .container - %h4 - Sign in with Twitter + = render "registrations/oauth" %section.form - %p + %p.divider %strong Or - %a.reveal(href="#sign-up" title="Create a new account") - Create a new account + + %a.reveal(href="#sign-up" title="Create a new account") + Create a new account - if resource.errors.any? - style = "display: block" diff --git a/app/views/sessions/new.html.haml b/app/views/sessions/new.html.haml index 3659cc4d..39085d19 100644 --- a/app/views/sessions/new.html.haml +++ b/app/views/sessions/new.html.haml @@ -1,19 +1,32 @@ -.auth - = simple_form_for(resource, as: resource_name, url: session_path(resource_name)) do |f| - = f.error_notification - .inputs - = f.input :email, required: true, autofocus: true - = f.input :password, required: true - .actions - = f.button :submit, "Sign in", class: 'btn btn-success' +%article + %header + %h2 + Sign in + %p.meta + %strong Forgot your password? + — + = link_to 'Reset it here!', new_password_path(:user) - = link_to "Sign in with Github", "/auth/github", class: "btn-auth btn-github large" - = link_to "Sign in with Twitter", "/auth/twitter", class: "btn-auth btn-twitter large" + %section.form + = form_for(resource, as: resource_name, url: session_path(resource_name)) do |f| -%p.hint - You can also - = link_to 'sign up', new_registration_path(:user) - if you don't have account yet, - = link_to 'reset lost password', new_password_path(:user) + %label.email + %span.label Your email address + = f.email_field :email, placeholder: "Your email address" + %label.password + %span.label Your password + = f.password_field :password, placeholder: "Your password" + %button(type="submit")> + %span> + Sign up + + %section.oauth + = render "registrations/oauth" + + %p.divider + %strong + Or + = link_to new_registration_path(:user, anchor: "sign-up"), class: "reveal", title: "Create a new account" do + Create a new account diff --git a/config/environments/development.rb b/config/environments/development.rb index 0cbbd093..9a336683 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -26,4 +26,12 @@ config.assets.debug = true config.action_mailer.default_url_options = { :host => Settings.host } -end + + # Do not compress assets + config.assets.compress = false + + # Enable source maps in the browser + config.sass.debug_info = true + + config.sass.line_comments = true +end \ No newline at end of file From 7ac67186dbd59121642f0ff1464fcb83b9ad557a Mon Sep 17 00:00:00 2001 From: pietia Date: Sat, 22 Jun 2013 01:24:26 +0200 Subject: [PATCH 268/499] cleanups - model specs --- app/models/activity.rb | 42 +++++++++++++++++++++++++++++------- app/models/event.rb | 6 +++++- app/models/user.rb | 12 +++++++++-- spec/models/activity_spec.rb | 7 +++--- spec/models/event_spec.rb | 26 +++++++++++++++------- spec/models/user_spec.rb | 30 ++++++++++++++++++++------ 6 files changed, 94 insertions(+), 29 deletions(-) diff --git a/app/models/activity.rb b/app/models/activity.rb index 0cc970dd..610850f2 100644 --- a/app/models/activity.rb +++ b/app/models/activity.rb @@ -22,31 +22,57 @@ class Activity < ActiveRecord::Base class << self def recent(limit = DEFAULT_LIMIT) - where("start_time >= :t OR anytime = true", t: 1.month.ago) - .limit(limit) - .order("anytime DESC, start_time ASC") + find_recent(limit) end def all_activities(limit = DEFAULT_LIMIT) - limit(limit) + find_all(limit) end def today - where(":t between start_time and end_time", t: Date.current) + find_today end def with_name_like(name) - where("name LIKE :q", q: "%#{name}%") + find_with_name_like(name) end def created_by(user) - where(creator_id: user) + find_created_by(user) end def participated_by(user) - includes(:participations).where(participations: { user_id: user }) + find_participated_by(user) end + private + + def find_all(limit) + limit(limit) + end + + def find_recent(limit) + where("start_time >= :t OR anytime = true", t: 1.month.ago) + .limit(limit) + .order("anytime DESC, start_time ASC") + end + + def find_today + where(":t between start_time and end_time", t: Date.current) + end + + def find_with_name_like(name) + where("name LIKE :q", q: "%#{name}%") + end + + def find_created_by(user) + where(creator_id: user) + end + + def find_participated_by(user) + includes(:participations).where(participations: { user_id: user }) + end + end def full_by diff --git a/app/models/event.rb b/app/models/event.rb index 35110d9b..ecf506ba 100644 --- a/app/models/event.rb +++ b/app/models/event.rb @@ -32,7 +32,7 @@ def counters(user) end def activity(activity_id) - all_activities.where(:id => activity_id).first.tap do |activity| + find_activity(activity_id).tap do |activity| activity.event = self if activity end end @@ -74,6 +74,10 @@ def fetch_all_activities @all_activities_fetcher.() end + def find_activity(activity_id) + all_activities.where(:id => activity_id).first + end + def activity_source @activity_source ||= Activity.public_method(:new) end diff --git a/app/models/user.rb b/app/models/user.rb index 15e266fd..8b419465 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -55,11 +55,11 @@ def no_oauth_connected? end def connected_with_twitter? - authentications.where(provider: 'twitter').any? + provider_connected?('twitter') end def connected_with_github? - authentications.where(provider: 'github').any? + provider_connected?('github') end private @@ -68,4 +68,12 @@ def any_oauth_connected? authentications.any? end + def provider_connected?(provider) + authentications.where(provider: provider).any? + end + + def paper_exists?(paper) + papers.exists?(paper) + end + end diff --git a/spec/models/activity_spec.rb b/spec/models/activity_spec.rb index ee6d509d..859c7a85 100644 --- a/spec/models/activity_spec.rb +++ b/spec/models/activity_spec.rb @@ -3,7 +3,8 @@ describe Activity do let(:event) { mock(:event) } - let(:creator) { User.new } + let(:creator) { mock_model(User) } + subject(:activity) { Activity.new } describe "#new" do @@ -27,11 +28,11 @@ end describe ".recent" do - let!(:recent_activities) { [mock(:activity1), mock(:activity2)] } + let(:recent_activities) { [mock(:activity1), mock(:activity2)] } subject { Activity.recent } before do - Activity.stub_chain(:where, :limit, :order).and_return(recent_activities) + Activity.should_receive(:find_recent).and_return(recent_activities) end it { should == recent_activities } diff --git a/spec/models/event_spec.rb b/spec/models/event_spec.rb index 41685778..e4a01a51 100644 --- a/spec/models/event_spec.rb +++ b/spec/models/event_spec.rb @@ -4,6 +4,8 @@ subject(:event) { Event.new("Eurucamp", Date.parse("2012-10-10"), Date.parse("2012-12-14")) } + let(:proxy) { mock(:proxy) } + describe "#new" do its(:name) { should == "Eurucamp" } its(:start_time) { should == Date.parse("2012-10-10") } @@ -25,8 +27,13 @@ end describe "#activity" do - let(:activity) { FactoryGirl.create(:activity, event: event, start_time: "2012-10-11", end_time: "2012-10-13") } - subject { event.activity(activity) } + let(:activity) { OpenStruct.new } + let(:activity_id) { 1 } + subject { event.activity(activity_id) } + + before do + event.should_receive(:find_activity).with(activity_id).and_return(activity) + end it { should == activity } its(:event) { should == event } @@ -69,10 +76,10 @@ end context "by name" do - subject { event.search_activities(nil, "yuppy", "all") } + subject { event.search_activities(nil, "yummy", "all") } before do - Activity.should_receive(:with_name_like).with("yuppy").and_return(activities) + Activity.should_receive(:with_name_like).with("yummy").and_return(activities) end it { should == activities } @@ -86,7 +93,8 @@ let(:filter) { "owner" } before do - Activity.stub_chain(:all_activities, :created_by).and_return(activities) + Activity.should_receive(:all_activities).and_return(proxy) + proxy.should_receive(:created_by).with(user).and_return(activities) end it { should == activities } @@ -96,7 +104,8 @@ let(:filter) { "participant" } before do - Activity.stub_chain(:all_activities, :participated_by).and_return(activities) + Activity.should_receive(:all_activities).and_return(proxy) + proxy.should_receive(:participated_by).with(user).and_return(activities) end it { should == activities } @@ -106,7 +115,8 @@ let(:filter) { "today" } before do - Activity.stub_chain(:all_activities, :today).and_return(activities) + Activity.should_receive(:all_activities).and_return(proxy) + proxy.should_receive(:today).and_return(activities) end it { should == activities } @@ -116,7 +126,7 @@ let(:filter) { "ownerx" } before do - Activity.stub_chain(:all_activities).and_return(activities) + Activity.should_receive(:all_activities).and_return(activities) end it { should == activities } diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 59b766dd..a3cef06f 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -2,18 +2,21 @@ describe User do - let(:user) { FactoryGirl.create(:user) } - describe "#connected_with_twitter?" do subject { user.connected_with_twitter? } + let(:user) { User.new } context "not connected with Twitter account" do + before do + user.should_receive(:provider_connected?).with("twitter").and_return(false) + end + it { should == false} end context "connected with Twitter account" do before do - FactoryGirl.create(:authentication, user: user, provider: "twitter") + user.should_receive(:provider_connected?).with("twitter").and_return(true) end it { should == true } @@ -23,14 +26,19 @@ describe "#connected_with_github?" do subject { user.connected_with_github? } + let(:user) { User.new } context "not connected with Github account" do + before do + user.should_receive(:provider_connected?).with("github").and_return(false) + end + it { should == false} end context "connected with Github account" do before do - FactoryGirl.create(:authentication, user: user, provider: "github") + user.should_receive(:provider_connected?).with("github").and_return(true) end it { should == true } @@ -39,6 +47,7 @@ describe "#apply_provider_handle" do subject { user.apply_provider_handle(params) } + let(:user) { User.new } context "no data provided" do let(:params) { {} } @@ -58,10 +67,9 @@ describe "#no_oauth_connected?" do subject { user.no_oauth_connected? } + let(:user) { FactoryGirl.create(:user) } context "account connected" do - let(:authentication) { FactoryGirl.create(:authentication, user: user) } - context "password set" do it { should == true } end @@ -81,13 +89,18 @@ describe "#password_required?" do subject { user.password_required? } + let(:user) { FactoryGirl.create(:user) } context "no account connected" do it { should == true } end context "account connected" do - let!(:authentication) { FactoryGirl.create(:authentication, user: user) } + let(:authentications) { [mock(:authentication)] } + + before do + user.should_receive(:authentications).and_return(authentications) + end it { should == false } end @@ -96,6 +109,7 @@ describe "#update_without_password" do subject { user.update_without_password(params) } + let(:user) { FactoryGirl.create(:user) } context "no password provided" do let(:params) { {name: "Zbigniew" } } @@ -114,8 +128,10 @@ end end + describe "#apply_omniauth" do subject { user.apply_omniauth(params) } + let(:user) { FactoryGirl.create(:user) } context "no data provided" do let(:params) { {} } From d17c3aa87c807a64eec3efc1df45463f750b3958 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Sun, 23 Jun 2013 00:29:46 +0300 Subject: [PATCH 269/499] Refactoring into separate layouts. --- app/controllers/application_controller.rb | 5 +++++ app/views/layouts/application.html.haml | 16 +++------------- app/views/layouts/user.html.haml | 21 +++++++++++++++++++++ app/views/partials/_header.html.haml | 17 ++--------------- app/views/partials/_layout_head.html.haml | 13 +++++++++++++ app/views/partials/_navigation.html.haml | 14 ++++++++++++++ 6 files changed, 58 insertions(+), 28 deletions(-) create mode 100644 app/views/layouts/user.html.haml create mode 100644 app/views/partials/_layout_head.html.haml create mode 100644 app/views/partials/_navigation.html.haml diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 75aa0387..343cbd3a 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -3,6 +3,7 @@ class ApplicationController < ActionController::Base # For APIs, you may want to use :null_session instead. protect_from_forgery with: :exception helper_method :current_event + layout :layout_by_resource before_filter :authenticate_user! @@ -31,4 +32,8 @@ def rescue_record_not_found not_found end + def layout_by_resource + devise_controller? ? 'user' : 'application' + end + end diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml index 25d1053a..d56008d2 100644 --- a/app/views/layouts/application.html.haml +++ b/app/views/layouts/application.html.haml @@ -1,19 +1,9 @@ !!! - html_tag :lang => 'en', :class => "no-js" do %head - %meta(charset="utf-8") - %meta(content="IE=edge,chrome=1" http-equiv="X-UA-Compatible") - %title= title("Eurucamp Activities") - %meta(content="" name="description") - %meta(content="" name="author") - %meta(content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0" name="viewport") - - = csrf_meta_tags - - = stylesheet_link_tag "application" - = javascript_include_tag :modernizr - - = javascript_include_tag "//use.typekit.net/vor5lqb.js" + + = render "partials/layout_head" + :javascript try{Typekit.load();}catch(e){} // TODO: partial diff --git a/app/views/layouts/user.html.haml b/app/views/layouts/user.html.haml new file mode 100644 index 00000000..da1e93a0 --- /dev/null +++ b/app/views/layouts/user.html.haml @@ -0,0 +1,21 @@ +!!! +- html_tag :lang => 'en', :class => "no-js" do + %head + + = render "partials/layout_head" + + :javascript + try{Typekit.load();}catch(e){} + + %body(class="user #{ body_class(controller.action_name) }") + = render "partials/chrome_frame" + = render "partials/notifications" + #container + %header#header + = link_to "Backo to eurucamp activites", root_path + = render "partials/navigation" + + #main(role="main") + = yield + = render "partials/footer" + = javascript_include_tag "application" diff --git a/app/views/partials/_header.html.haml b/app/views/partials/_header.html.haml index ef7296e1..9e7ce2d7 100644 --- a/app/views/partials/_header.html.haml +++ b/app/views/partials/_header.html.haml @@ -1,20 +1,7 @@ %header#header - - if current_user - %nav.user-nav - %ul - %li - = link_to t("user_nav.account.label"), edit_user_registration_path, title: t("user_nav.account.title") - %li - = link_to t("user_nav.logout.label"), destroy_user_session_path, title: t("user_nav.logout.title"), method: "delete" - - else - %nav.user-nav - %ul - %li - = link_to t("user_nav.login.label"), new_user_session_path, title: t("user_nav.login.title") - %li - = link_to t("user_nav.register.label"), new_user_registration_path, title: t("user_nav.register.title") - + = render "partials/navigation" + -# the logo, link home %h1 = link_to "/" do diff --git a/app/views/partials/_layout_head.html.haml b/app/views/partials/_layout_head.html.haml new file mode 100644 index 00000000..1d07fdf4 --- /dev/null +++ b/app/views/partials/_layout_head.html.haml @@ -0,0 +1,13 @@ +%meta(charset="utf-8") +%meta(content="IE=edge,chrome=1" http-equiv="X-UA-Compatible") +%title= title("Eurucamp Activities") +%meta(content="" name="description") +%meta(content="" name="author") +%meta(content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0" name="viewport") + += csrf_meta_tags + += stylesheet_link_tag "application" += javascript_include_tag :modernizr + += javascript_include_tag "//use.typekit.net/vor5lqb.js" diff --git a/app/views/partials/_navigation.html.haml b/app/views/partials/_navigation.html.haml new file mode 100644 index 00000000..ed1d5a09 --- /dev/null +++ b/app/views/partials/_navigation.html.haml @@ -0,0 +1,14 @@ +- if current_user + %nav.user-nav + %ul + %li + = link_to t("user_nav.account.label"), edit_user_registration_path, title: t("user_nav.account.title") + %li + = link_to t("user_nav.logout.label"), destroy_user_session_path, title: t("user_nav.logout.title"), method: "delete" +- else + %nav.user-nav + %ul + %li + = link_to t("user_nav.login.label"), new_user_session_path, title: t("user_nav.login.title") + %li + = link_to t("user_nav.register.label"), new_user_registration_path, title: t("user_nav.register.title") \ No newline at end of file From 0beaca048559e51c9eee6479098bebd74529a1b5 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Sun, 23 Jun 2013 00:49:18 +0300 Subject: [PATCH 270/499] Show back link. --- app/views/layouts/user.html.haml | 1 - app/views/partials/_navigation.html.haml | 13 +++++++------ config/locales/en.yml | 3 +++ 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/app/views/layouts/user.html.haml b/app/views/layouts/user.html.haml index da1e93a0..a2563197 100644 --- a/app/views/layouts/user.html.haml +++ b/app/views/layouts/user.html.haml @@ -12,7 +12,6 @@ = render "partials/notifications" #container %header#header - = link_to "Backo to eurucamp activites", root_path = render "partials/navigation" #main(role="main") diff --git a/app/views/partials/_navigation.html.haml b/app/views/partials/_navigation.html.haml index ed1d5a09..a619e2ca 100644 --- a/app/views/partials/_navigation.html.haml +++ b/app/views/partials/_navigation.html.haml @@ -1,13 +1,14 @@ -- if current_user - %nav.user-nav - %ul +%nav.user-nav + %ul + - if devise_controller? + %li.back-home + = link_to t("user_nav.back_home.label"), root_path, title: t("user_nav.back_home.title") + - if current_user %li = link_to t("user_nav.account.label"), edit_user_registration_path, title: t("user_nav.account.title") %li = link_to t("user_nav.logout.label"), destroy_user_session_path, title: t("user_nav.logout.title"), method: "delete" -- else - %nav.user-nav - %ul + - else %li = link_to t("user_nav.login.label"), new_user_session_path, title: t("user_nav.login.title") %li diff --git a/config/locales/en.yml b/config/locales/en.yml index 2f81a1ea..aaf934f3 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -13,6 +13,9 @@ en: edit: Update user_nav: + back_home: + label: Back to eurucamp activities + title: Go back to front page account: label: My Account title: Edit your account details From ac6a7aeb8471b663ad2a809aece728730e46c967 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Mon, 24 Jun 2013 16:25:59 +0300 Subject: [PATCH 271/499] Better forms for user. --- app/assets/images/user/connected.png | Bin 0 -> 1649 bytes app/assets/images/user/github.png | Bin 4581 -> 3084 bytes app/assets/images/user/twitter.png | Bin 3782 -> 2947 bytes app/assets/stylesheets/partials/_footer.sass | 1 + app/assets/stylesheets/partials/_user.sass | 114 ++++++++++++++++-- .../partials/activities/_form.sass | 5 +- .../partials/activities/_index.sass | 5 +- app/assets/stylesheets/utils/_buttons.sass | 27 ++++- app/assets/stylesheets/utils/_forms.sass | 9 +- app/helpers/application_helper.rb | 13 -- app/views/partials/_navigation.html.haml | 7 +- app/views/passwords/new.html.haml | 33 +++-- app/views/registrations/_oauth.html.haml | 35 ++++-- app/views/registrations/edit.html.haml | 51 ++++---- app/views/registrations/new.html.haml | 36 +++--- app/views/sessions/new.html.haml | 29 ++--- config/locales/en.yml | 16 +++ 17 files changed, 264 insertions(+), 117 deletions(-) create mode 100644 app/assets/images/user/connected.png diff --git a/app/assets/images/user/connected.png b/app/assets/images/user/connected.png new file mode 100644 index 0000000000000000000000000000000000000000..3c6af81d8996e54a32970fe78d862e8afc1d9f9a GIT binary patch literal 1649 zcmeAS@N?(olHy`uVBq!ia0vp^dLYcf1|-9GYMTQo$r9IylHmNblJdl&R0hYC{G?O` z&)mfH)S%SFl*+=BsWuD@%qp275hW46K32*3xq68pHF_1f1wh>l3^w)^1&PVosU-?Y zsp*+{wo31J?^jaDOtDo8H}y5}EpSfF$n>ZxN)4{^3rViZPPR-@vbR&PsjvbXkegbP zs8ErclUHn2VXFi-*9yo63F|81#=KlDb#X~hD#E>34K5C;EJ)Q4 zN-fSWElLJPT$(b-ssbzLqSVBa{GyQj{2W*)24v)y-fM)1rW~Nw~8k@OTSeQ7wn7A4l8oC-eIT;x{TUwY{Iy*bM zm^-<^^t$9Hm*%GCmB93-AoRN6)C)=sxdlL*T~doO%TiO^it=+6z@E0s#O)SGoaRCG zrr>sq6HdMQK*#8Vq82HtVM4(417gAxE|3FH`l)%q^j-u^*sPjYCNVHDJ@#~Q45_&F z=B9s#P@oKZLAuP;GZ%bVUy8hO+R*;$!NS=OA}=hMoD=(MLs?*=(hI4OitYvLZX2$@ zsQYI<$D}3M7nUkGEt+N``ysxsHa=s=>m62)dtNv2*PSVLpEtjD&&Tig{Ie`%vaa07 z==HkIwy!b&f%J|&y)$bBd)-|54{Ul9)DSselB+dF#P#hboBY=$R_rr<()LHK{dZ)o zUDAm)k8|9VCjO3E`;Bv#Nafn=uTACp)jQtleQ=Gjtm3`M-&kkm)<64T!j2b3JLfH| zmMm7^dx9~mz$32jkxO#5T~h<~k8316Ue zs$XxyGoJRtc``5al+Hc!Ja;^4US9bF;mL-Gq}VyPkcmN-o>|m&VX0|Ni^y`q69Q@&|YGwi~@??t8p({<5&% zc}?M3Q=k8>`+t2?#nOwxA4;tLN~-r-?~PlpDY~C)%Z2NS+aq+2U2KXl)7|(ZbYZKI zfTqg7V`<-&B?T~h(Sa8$imT)fJZDkr<>s6w>E`H{a zlbj}86}&Lu2TRE;m1NZmK@Z$NtPFV-xA@`@vwauywpX7_*~C+l)N)R{ab9Ifv`D!vqPw=OAZ_~E#X zFKBZ2Bx5_v&rcLpv;V$+k^e_SB)915@%GbU4AY{tva)8&uQ&U^dOMzxjUn)@yZ+2A R99f`Bz|+;wWt~$(69Bh5j(h+B literal 0 HcmV?d00001 diff --git a/app/assets/images/user/github.png b/app/assets/images/user/github.png index 1196a0765173f519b3e6c68a422c51e3e36cf7a6..261feb905ca28b2c091eee58442269c6f77343e4 100644 GIT binary patch delta 2419 zcmZ{mc{Ce{6UReaLC~h|tB$l%E!Gh$L4zbTj#5YKtX5S^9UG}ggo;SDMb(jQQ&d)z zC8(!#P};lk4YNg{qm)AbLaC^BxXCIEnFtS!dM zjWpin>Fprnt}yLKtkZ4CJrv;y#>lW*Ko@0XN-rwsiDdsy3gT4%nvyd7>#IPuailRj z5m}u$3{)!$%|fNw6k({pb5CvB2-V$J7sjVgblmogWL)CTICXpYSiP0-C1=ocGIKIF za`ry&&LAKvW|0mQjY3sq%-6)=_Ea_bt>uY#p)bZ7Uj~PTg>4NKItdnH3p<;VKEQ?| zj_I8|dA{6>bIPi~W2cad2@pWDE(+0QTkJ z+vTo#_>6Lb-HRLxw+CBW{mq1yZc_(7z`S)HPtMKF(Ss;KL6n95_ZtWG^7HfWbY&TT zDq=83CgAR;nN~DtS*F9EJyFoy$=dK|`Dx<~#MKrakLSSgQbqS@Go5Me@qYIO482RH zP9;M>hDJvNZ_63z9Y21&>%qf^j_66P)6C1#Yd-|~-FBo1Z!(#@zPWk(?7BtZ8fop8>}<2|+uP?{Rsozm+_rqobnW#!ql2N|dQh{PHL2xI|R>lSC`&klkq zt%6|=uS8-Z<;v~WUWg3gtGQfmSy9pZ9x9dUIN2<&kqJBDBp#1nnXH?c#hsyxr3z8x z`F#Et(e%UlUSWZOMTacD0Y)#oxVXe@Z!LFXi*Pb&TYY`bMn9Clr7jI3bKRh&J}DbY z5k_y%`a^9MK|RePC4XgRXGc4hmTBT-bt*syzT#i12?bkH;-(KShZigm>0gR8{FUkytn3!S?xHYVfl% zJYy+^zNp*%NEGUGCbuP44ePwWmJDa&bN1?DxNynVpYy?SC6x{MuN$ApiRXY$?;|<@eiuUoDw53oeDL#X@-gafyU1^Cg zC!s+PZ05UcdCR@3Hq%s=+EwDpr03}1pv-IksQMZfOC#jd@N){{{wj`Xz#h-)!#W

mt*9%3EB5F;NfcAx}EuU-;dqx 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 Tb=ER%06)g(%=O>uUJ3gT*I1YM diff --git a/app/assets/images/user/twitter.png b/app/assets/images/user/twitter.png index 343eb381c8734471753606b6b2f9034007621517..537628d3809e22442442c3ea4e0a5d2e2028c134 100644 GIT binary patch delta 2281 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 delta 3122 zcmb_eX;jl!9(@TQP(bWJMMMyAXAuzsAs`^@Ad7~z?5M1PsK^=w{z0lyk;T#!MZ*#V z64^uoDQl?HA_PcK0xFBB7{C@BQBIo_pW#zAraZrAuAw z<{39fg)er00RT{N`n#Pw0I;ni49INT+5#?9!t1pT!8Og0cE-o;ZR~AL?GT2B_Q%bP z5eQp^A=1If)Y!qq%r-v~zUOz4!=Hj4!jB#Kjmh@UK-!vd@$O;asL=daxYF;Gc7KM> zKPF)VXf9^cbl*%?7kRa4AqX_SLV5RFS_<+dO(g4jf%E4d?( zWd9&2K{Wdcxyk4){U~3e@X}q^#@OD%Z!VFfUK|#ebiS)7d9%U*++td1+{A&xu*HP=4p+?yR*2>7VIh zhWhtV)(;odRaKvV;c;es+n65F->;06&$RX(3=Bw9`#cIgioLoR&{Xs#39@pm+?w-s z6pUd~wH|X><^ejNRh<2Ku5*@>YqtLxl=6&&ZO98|Yh6mWr60-dnjd{Eo^D3BKBJ3` zwmZQFbafrD$S!tLR?EY)qqHDSkHVNrs~t35+@9y$7=_MBf(^u~*|`S)G~c5XR-5Zr zp*0#GAYq{RIhzIj3`dD>Q_3dyLdDDw?Jj`AkYYt0u4NZ)7OEyBWz!GkM$8U8PFpCS z5l-wK`sm}#J=#~~)f(~AZu9v&9e~1eWrc4nR4j|hbGs9wT)E{5B29Xe87-UevpX3@^Dq z<+8}0es;Dl2UX7=y)z%)7YRL2e{GHuH*Y@;8^DtMetMrr+3PA9uC2^wf$GlX78NM z+H_<~TZ84NSgPKMp(tEJsX@h)8&fAwyz=QaO|?ojL5V+ZcZ3Z@L#NA^+EVFlM~OIr zz;g9n)Dm4?C9G%Ld}dr3fA9PmWw3cUOi`pJP+Nb+yZzQ29i6l`57m}pi(ksFJ+M6k zmIravYTSez?)^ZMc!4S^L}IHUcGNEV;oONVdNYtD?4m(JZ)bl}Pw;9ghv z-nB1hSYYqAg?g)C=pERxWb6~`MEraXgacT^6k&(DY^6|hNJkV^(319hnQ+u}Ul zqVf2;SAT&|cRyX1|Lq%k<&s~m`J=^e1RnS1FGjXn%?~JngxF9{4wWwv@6njzRW`jo zeUc&V?whhvL7|(_`lnH|lGcG1B<7W++Q{yX;F?GM(C)FRxIKwofVY3*Zx(h&%Y71s z)_~LRL`~01qY*zh$l)#3asT*-AcNHYXmF;WS67s*UDG;M&a%9H0Nl;9c$Jj6q95M)GtMtJ#PmnULL-(cjtcaRdUI0 z1s)7Y)Q33FspD3zHP!MYT{&LRkR%JNA9S-Su5;V`m^+Hd05f!}=&K#wM!V{U30e@4rB8-jhZDi0Y+1C9mn;Y=lZ0bs8W@*Ru8C_4M>s2|G47eIuOSgl!TWYT_P*1 zL|rfH$%oqJoB@Y(X$pDT^GD1MW+El|o@e^Va= z^$&RAmM7$`VQ2iOA1<9TXqhbd)t&L@JtZM@;VC5<{6Qp=Sl;~cft7t=-IlyZs?N~) zdkalS|E*qyAY3O}!lLv@#eSy`l7LLj-pmj3DiM zaISTWoOV*9dt^UR${xzIc!AOo3{M~mOZnBZuhg6?pZG1D8)8hC|FndH3))v3s@P$O zK^qc9c1xH`Ukx(pV((2nXi||nAwIQb{s_k2^;pGu;|xRm!Bz{RPn=OK?)C@NR;O?- z)b-feT>G>j$Vding52kej3-~7W(03lO-N6r+y3dr@1qJ&0$R@ynh<$C*34yV9urhg za;LU>QQvpUK!3F-^jz=L59J*>gYxi@9bFRDyO-S+5A;%H@vv`w6h`!kQ9phsT|W0~ zH&1AR$^M{k>S!PM{?ZPRd6@AoF?>YvGED?$d?6_2aW<5sa2mhmxh8lS}Y zKm%~Ni0VcNR%<+s`TXf|>Qsa6@om~*%?fuXPpIh?^wv{d|>6Kh$o{JK5?7 zjx{kH(-4?(USG*a-N_FZg;;lA87WvNQa_)m*SwM$OXG@L1QvhbY}73$N7 zSrRdOQ!Wy#KTXTyV@C~n-+Klb4S!&XLpk9nh7ssG#FpzeP=5VTYIMn(vy*x|?5#ZB z@M*F)CmYR(a++p4mtsr!<`N^0{J(`M9a) zw0X)0Um#yCgy*HQ(;FWRpW;H6B7d@pn?Z->L+q0I_Qabl!&;iaFDf}18GL_l9y6x> zfX6*Xu~os1lzxieQOr6(^q(fLx@PkCWP)91aviZMD-N!URygjPv=^@~T9GTpr0$lK zmX?~AdDq#z9fn=FC3J=CbmlEM0yWG?clQnNyCVmFTwPpT;jO3dXoK>Y?<)kbavVY? zRJzm%-@(*<9kZ)3Oh;Ep8&aZHdp&m7DHHN%Q_h378M;^hjrB;5qkA*=WjyXUn@ZLD znmpJl`8KQo$ZeoIBpzSUkqGrSjcL%Jvcg%9Y-*YU;4=7@ZJm)g4eO$h1rc5C&0QqX z3yK{1s0-I5;*%@{U<2QPUHYeGQXF-1yI8Z6VKzbdWkssXNORkI1ymaK$pA=N55hxe z7Bcr$*1B_<7nB9MAtc2y>3W#a&}41)(Nw(czE%-QBN=4 zV#X resource_name, :url => password_path(resource_name), :html => { :method => :post }) do |f| - = f.error_notification - .inputs - = f.input :email, :required => true - .actions - = f.button :submit, "Send me reset password instructions", class: 'btn btn-success' - -%p.hint - You can also - = link_to 'sign up', new_registration_path(:user) - if you don't have account yet +%article + %header + %h2 + Reset your password + %section.form + = form_for resource, as: resource_name, url: password_path(resource_name), method: :post do |f| + -#%p Send me reset password instructions to this email address: + + %label.email + %span.label Email address + = f.email_field :email, placeholder: "You email address" + + %button(type="submit")> + Send me instructions + + %p.info + = succeed '.' do + You can also + = link_to new_registration_path(:user), title: "Sign up" do + sign up + if you don't have account yet. \ No newline at end of file diff --git a/app/views/registrations/_oauth.html.haml b/app/views/registrations/_oauth.html.haml index 2187ce0a..17db60f6 100644 --- a/app/views/registrations/_oauth.html.haml +++ b/app/views/registrations/_oauth.html.haml @@ -1,10 +1,27 @@ -%section.oauth - = link_to "/auth/github", class: "github" do - .container - %h4 - Sign in with Github +- domain ||= "new" - = link_to "/auth/twitter", class: "twitter" do - .container - %h4 - Sign in with Twitter +.oauth + %ul + %li + - if resource.connected_with_github? + = link_to "http://github.com/#{resource.github_handle}", class: "github connected" do + = image_tag "user/connected.png" + %h4 + = t("user.#{domain}.oauth.github.connected").html_safe + - else + = link_to "/auth/github", class: "github" do + = image_tag "user/github.png" + %h4 + = t("user.#{domain}.oauth.github.connect").html_safe + + %li + - if resource.connected_with_twitter? + = link_to "http://twitter.com/#{resource.twitter_handle}", class: "twitter connected" do + = image_tag "user/connected.png" + %h4 + = t("user.#{domain}.oauth.twitter.connected").html_safe + - else + = link_to "/auth/twitter", class: "twitter" do + = image_tag "user/twitter.png" + %h4 + = t("user.#{domain}.oauth.twitter.connect").html_safe diff --git a/app/views/registrations/edit.html.haml b/app/views/registrations/edit.html.haml index 3c99af1b..c995ed1a 100644 --- a/app/views/registrations/edit.html.haml +++ b/app/views/registrations/edit.html.haml @@ -1,30 +1,35 @@ -%h1 Edit profile +%article + %header + %h2 + Edit profile -.profile - = simple_form_for @user, :url => registration_path(resource_name) do |f| - = f.error_notification - = devise_error_messages! + %section.form + = form_for resource, as: resource_name, url: registration_path(resource_name) do |f| - .avatar - = image_tag(avatar_url(@user, 64)) + %figure.avatar + = image_tag(avatar_url(@user, 128)) - .inputs - = f.input :name, :hint => "Your name or nick" - = f.input :email + %label.name + %span.label Your (user) name + = f.text_field :name, placeholder: "Your name" - - if @user.no_oauth_connected? - = f.input :password, :required => true - = f.input :password_confirmation, :required => true + %label.email + %span.label Your email address + = f.email_field :email, placeholder: "Your email address" - - if @user.connected_with_github? - = github_link(@user) - - else - = link_to "connect with Github", "/auth/github", class: "btn-auth btn-github" + - if resource.no_oauth_connected? + %label.password + %span.label Your password + = f.password_field :password, placeholder: "Your password" - - if @user.connected_with_twitter? - = twitter_link(@user) - - else - = link_to "connect with Twitter", "/auth/twitter", class: "btn-auth btn-github" + %label.password + %span.label Your password once more + = f.password_field :password_confirmation, placeholder: "Your password once more" - .actions - = f.button :submit, class: 'btn btn-info' + %button(type="submit")> + Update + + %p.divider + %span Connect! + + = render "oauth", :resource => resource, :domain => "edit" \ No newline at end of file diff --git a/app/views/registrations/new.html.haml b/app/views/registrations/new.html.haml index 46744317..4ed89784 100644 --- a/app/views/registrations/new.html.haml +++ b/app/views/registrations/new.html.haml @@ -2,28 +2,16 @@ %header %h2 Register - %p.meta - %strong Have an account already? - — - = link_to 'Sign in!', new_session_path(:user) - - = render "registrations/oauth" %section.form - %p.divider - %strong - Or - %a.reveal(href="#sign-up" title="Create a new account") - Create a new account + = render "registrations/oauth", :resource => resource, :domain => "new" - - if resource.errors.any? - - style = "display: block" - :javascript - window.location.hash = "sign-up"; + %p.divider + %span or - = form_for resource, as: resource_name, url: registration_path(resource_name), html: { id: "sign-up", style: style } do |f| - %h4 Register new account + = form_for resource, as: resource_name, url: registration_path(resource_name) do |f| + %h4 Register a new account %label.name %span.label Name @@ -42,6 +30,14 @@ %span.label Password, once more = f.password_field :password_confirmation, placeholder: "Password once more" - %button(type="submit")> - %span> - Sign up + %button(type="submit")> + Sign up + + %p.info + = succeed '.' do + Have an account already? + = link_to new_session_path(:user), title: "Sign in" do + Sign in + or + = link_to new_password_path(:user), title: "Reset your password" do + reset your password diff --git a/app/views/sessions/new.html.haml b/app/views/sessions/new.html.haml index 39085d19..bc14f94f 100644 --- a/app/views/sessions/new.html.haml +++ b/app/views/sessions/new.html.haml @@ -2,31 +2,32 @@ %header %h2 Sign in - %p.meta - %strong Forgot your password? - — - = link_to 'Reset it here!', new_password_path(:user) %section.form = form_for(resource, as: resource_name, url: session_path(resource_name)) do |f| %label.email %span.label Your email address - = f.email_field :email, placeholder: "Your email address" + = f.email_field :email, autofocus: true, placeholder: "Your email address" %label.password %span.label Your password = f.password_field :password, placeholder: "Your password" %button(type="submit")> - %span> - Sign up + Sign in - %section.oauth - = render "registrations/oauth" + %p.divider + %span or + + = render "registrations/oauth", :resource => resource + + %p.info + = succeed '.' do + Otherwise + = link_to new_registration_path(:user), title: "Create a new account" do + create a new account + or + = link_to new_password_path(:user), title: "Reset your password" do + reset your password - %p.divider - %strong - Or - = link_to new_registration_path(:user, anchor: "sign-up"), class: "reveal", title: "Create a new account" do - Create a new account diff --git a/config/locales/en.yml b/config/locales/en.yml index aaf934f3..9482a8f8 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -29,6 +29,22 @@ en: label: Register title: Register new account + user: + edit: + oauth: + github: + connect: "Connect your Github account" + connected: "You have connected your Github account" + twitter: + connect: "Connect your Twitter account" + connected: "You have connected your Twitter account" + new: + oauth: + github: + connect: "Sign in with your Github account" + twitter: + connect: "Sign in with your Twitter account" + # index and shared translations activities: filters: From 524a732b7d6e1d49731e8635cc42f07fd952d6c6 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Mon, 24 Jun 2013 16:39:46 +0300 Subject: [PATCH 272/499] Revert "User - simple specs added" This reverts commit 88d12b4d9588e6fc74b38a2e313f20027e70462d. --- app/models/user.rb | 8 +-- spec/models/user_spec.rb | 144 --------------------------------------- 2 files changed, 3 insertions(+), 149 deletions(-) diff --git a/app/models/user.rb b/app/models/user.rb index 15e266fd..43293b91 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -13,8 +13,8 @@ class User < ActiveRecord::Base def apply_omniauth(omniauth) provider, uid, info = omniauth.values_at('provider', 'uid', 'info') unless info.blank? - self.email = info['email'] unless email.blank? - self.name = info['name'] unless name.blank? + self.email = info['email'] if email.blank? + self.name = info['name'] if name.blank? end apply_provider_handle(omniauth) @@ -22,8 +22,7 @@ def apply_omniauth(omniauth) end def apply_provider_handle(omniauth) - provider = omniauth['provider'] - info = omniauth['info'] || {} + provider, info = omniauth.values_at('provider', 'info') # su*ks - refactor it case provider @@ -32,7 +31,6 @@ def apply_provider_handle(omniauth) when /twitter/ self.twitter_handle = info['nickname'] if twitter_handle.blank? end - self end def update_without_password(params, *options) diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 59b766dd..d65c4482 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -2,150 +2,6 @@ describe User do - let(:user) { FactoryGirl.create(:user) } - - describe "#connected_with_twitter?" do - subject { user.connected_with_twitter? } - - context "not connected with Twitter account" do - it { should == false} - end - - context "connected with Twitter account" do - before do - FactoryGirl.create(:authentication, user: user, provider: "twitter") - end - - it { should == true } - end - - end - - describe "#connected_with_github?" do - subject { user.connected_with_github? } - - context "not connected with Github account" do - it { should == false} - end - - context "connected with Github account" do - before do - FactoryGirl.create(:authentication, user: user, provider: "github") - end - - it { should == true } - end - end - - describe "#apply_provider_handle" do - subject { user.apply_provider_handle(params) } - - context "no data provided" do - let(:params) { {} } - - it { should == user } - end - - context "data provided" do - let(:params) { { "uid" => "123ASD", "provider" => "github", "info" => {"nickname" => "johnny"} } } - - specify { expect{subject}.to change(user, :github_handle).to("johnny") } - - it { should == user } - end - - end - - describe "#no_oauth_connected?" do - subject { user.no_oauth_connected? } - - context "account connected" do - let(:authentication) { FactoryGirl.create(:authentication, user: user) } - - context "password set" do - it { should == true } - end - end - - context "no account connected" do - context "password set" do - it { should == true } - end - - context "password not set" do - let(:user) { User.new } - it { should == false } - end - end - end - - describe "#password_required?" do - subject { user.password_required? } - - context "no account connected" do - it { should == true } - end - - context "account connected" do - let!(:authentication) { FactoryGirl.create(:authentication, user: user) } - - it { should == false } - end - - end - - describe "#update_without_password" do - subject { user.update_without_password(params) } - - context "no password provided" do - let(:params) { {name: "Zbigniew" } } - - specify { expect { subject }.to change(user, :name) } - - specify { expect { subject }.to_not change(user, :encrypted_password) } - end - - context "password" do - let(:params) { {name: "Zbigniew", password: "qweqwe123123", password_confirmation: "qweqweqwe123123" } } - - specify { expect { subject }.to change(user, :name) } - - specify { expect { subject }.to change(user, :encrypted_password) } - end - end - - describe "#apply_omniauth" do - subject { user.apply_omniauth(params) } - - context "no data provided" do - let(:params) { {} } - - it { should be_a_kind_of(Authentication) } - its(:user) { should == user } - it { should be_invalid } - end - - context "data provided" do - context "with info" do - let(:params) { { 'info' => {"email" => "ala@ala.com"} } } - - specify { expect{subject}.to change(user, :email).to("ala@ala.com") } - - it { should be_a_kind_of(Authentication) } - its(:user) { should == user } - end - - context "without info" do - let(:params) { { "uid" => "123ASD", "provider" => "github" } } - - specify { expect{subject}.to_not change(user, :email) } - - it { should be_a_kind_of(Authentication) } - its(:user) { should == user } - end - end - end - describe "validations" do subject { User.new } From efb059e1ece49e8e52a9a03e36c9419c05a7bf65 Mon Sep 17 00:00:00 2001 From: pietia Date: Mon, 24 Jun 2013 15:42:50 +0200 Subject: [PATCH 273/499] this seems to be failing code --- app/models/user.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/models/user.rb b/app/models/user.rb index 8b419465..447e872f 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -13,8 +13,8 @@ class User < ActiveRecord::Base def apply_omniauth(omniauth) provider, uid, info = omniauth.values_at('provider', 'uid', 'info') unless info.blank? - self.email = info['email'] unless email.blank? - self.name = info['name'] unless name.blank? + self.email = info['email'] if email.blank? + self.name = info['name'] if name.blank? end apply_provider_handle(omniauth) From 0dc55ecf86de2d87def04150b1ba8abce5f60b90 Mon Sep 17 00:00:00 2001 From: pietia Date: Mon, 24 Jun 2013 16:48:08 +0200 Subject: [PATCH 274/499] more bugfixes --- app/models/user.rb | 13 ++++++++----- spec/models/user_spec.rb | 8 +++++--- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/app/models/user.rb b/app/models/user.rb index 05e54a22..a9ac9e7f 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -25,12 +25,15 @@ def apply_provider_handle(omniauth) provider, info = omniauth.values_at('provider', 'info') # su*ks - refactor it - case provider - when /github/ - self.github_handle = info['nickname'] if github_handle.blank? - when /twitter/ - self.twitter_handle = info['nickname'] if twitter_handle.blank? + unless info.blank? + case provider + when /github/ + self.github_handle = info['nickname'] if github_handle.blank? + when /twitter/ + self.twitter_handle = info['nickname'] if twitter_handle.blank? + end end + self end def update_without_password(params, *options) diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index a3cef06f..a99d899d 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -131,7 +131,7 @@ describe "#apply_omniauth" do subject { user.apply_omniauth(params) } - let(:user) { FactoryGirl.create(:user) } + let(:user) { FactoryGirl.create(:user, name: nil) } context "no data provided" do let(:params) { {} } @@ -143,9 +143,11 @@ context "data provided" do context "with info" do - let(:params) { { 'info' => {"email" => "ala@ala.com"} } } + let(:params) { { 'info' => {"email" => "ala@ala.com", "name" => "Ala"} } } + + specify { expect{subject}.to change(user, :name).to("Ala") } - specify { expect{subject}.to change(user, :email).to("ala@ala.com") } + specify { expect{subject}.to_not change(user, :email) } it { should be_a_kind_of(Authentication) } its(:user) { should == user } From 63723d65eaeb56ead3e8afd1288beb3d8262bc96 Mon Sep 17 00:00:00 2001 From: pietia Date: Tue, 25 Jun 2013 09:53:21 +0200 Subject: [PATCH 275/499] quick fix --- app/models/authentication.rb | 5 ----- 1 file changed, 5 deletions(-) diff --git a/app/models/authentication.rb b/app/models/authentication.rb index 39092e56..69a2df10 100644 --- a/app/models/authentication.rb +++ b/app/models/authentication.rb @@ -1,8 +1,3 @@ class Authentication < ActiveRecord::Base belongs_to :user - - validates :user_id, presence: true - validates :provider, presence: true, uniqueness: { scope: [:user_id] } - validates :uid, presence: true - end From 095c116b728ea009a9986ba8077e2feeca003d43 Mon Sep 17 00:00:00 2001 From: pietia Date: Tue, 25 Jun 2013 10:17:53 +0200 Subject: [PATCH 276/499] preinitializer extracted to a partial --- app/views/layouts/application.html.haml | 16 +--------------- app/views/layouts/user.html.haml | 5 +---- app/views/partials/_preinitializers.html.haml | 17 +++++++++++++++++ 3 files changed, 19 insertions(+), 19 deletions(-) create mode 100644 app/views/partials/_preinitializers.html.haml diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml index d56008d2..a6d4d75c 100644 --- a/app/views/layouts/application.html.haml +++ b/app/views/layouts/application.html.haml @@ -1,22 +1,8 @@ !!! - html_tag :lang => 'en', :class => "no-js" do %head - = render "partials/layout_head" - - :javascript - try{Typekit.load();}catch(e){} - // TODO: partial - var App = { - paths: { - login: '#{new_user_session_path}' - }, - event: { - name: '#{current_event.name}', - startTime: #{current_event.start_time.to_i * 1000}, - endTime: #{current_event.end_time.to_i * 1000} - } - }; + = render "partials/preinitializers" %body(class="#{ body_class(controller.action_name) }") = render "partials/chrome_frame" diff --git a/app/views/layouts/user.html.haml b/app/views/layouts/user.html.haml index a2563197..62ea9ac9 100644 --- a/app/views/layouts/user.html.haml +++ b/app/views/layouts/user.html.haml @@ -1,11 +1,8 @@ !!! - html_tag :lang => 'en', :class => "no-js" do %head - = render "partials/layout_head" - - :javascript - try{Typekit.load();}catch(e){} + = render "partials/preinitializers" %body(class="user #{ body_class(controller.action_name) }") = render "partials/chrome_frame" diff --git a/app/views/partials/_preinitializers.html.haml b/app/views/partials/_preinitializers.html.haml new file mode 100644 index 00000000..7498d14f --- /dev/null +++ b/app/views/partials/_preinitializers.html.haml @@ -0,0 +1,17 @@ +:javascript + try { + Typekit.load(); + } catch(e) { + //omnomnom + } + + var App = { + paths: { + login: '#{new_user_session_path}' + }, + event: { + name: '#{current_event.name}', + startTime: #{current_event.start_time.to_i * 1000}, + endTime: #{current_event.end_time.to_i * 1000} + } + }; \ No newline at end of file From 5b373f6113d6e04f0cb2da0efb183f23edc235c3 Mon Sep 17 00:00:00 2001 From: pietia Date: Tue, 25 Jun 2013 11:33:26 +0200 Subject: [PATCH 277/499] be nice and inform user what's going on --- app/controllers/application_controller.rb | 5 +++++ app/controllers/registrations_controller.rb | 7 +++++++ app/views/registrations/new.html.haml | 9 +++++---- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 343cbd3a..9e837492 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -5,6 +5,7 @@ class ApplicationController < ActionController::Base helper_method :current_event layout :layout_by_resource + before_filter :clean_up_session before_filter :authenticate_user! rescue_from CanCan::AccessDenied, :with => :rescue_access_denied @@ -24,6 +25,10 @@ def not_found private + def clean_up_session + session[:omniauth] = nil + end + def rescue_access_denied render :nothing => true, :status => 401 end diff --git a/app/controllers/registrations_controller.rb b/app/controllers/registrations_controller.rb index 4b1bfed4..c6fe4880 100644 --- a/app/controllers/registrations_controller.rb +++ b/app/controllers/registrations_controller.rb @@ -1,5 +1,8 @@ class RegistrationsController < Devise::RegistrationsController skip_before_filter :authenticate_user!, only: :create + skip_before_filter :clean_up_session, only: [:new, :create] + + helper_method :during_oauth_flow? def create super @@ -26,6 +29,10 @@ def update private + def during_oauth_flow? + !current_user && @user.new_record? && session[:omniauth].present? + end + def build_resource(*args) super.tap do |user| if user && session[:omniauth] diff --git a/app/views/registrations/new.html.haml b/app/views/registrations/new.html.haml index 4ed89784..8afc4a28 100644 --- a/app/views/registrations/new.html.haml +++ b/app/views/registrations/new.html.haml @@ -5,13 +5,14 @@ %section.form - = render "registrations/oauth", :resource => resource, :domain => "new" + - unless during_oauth_flow? + = render "registrations/oauth", :resource => resource, :domain => "new" - %p.divider - %span or + %p.divider + %span or = form_for resource, as: resource_name, url: registration_path(resource_name) do |f| - %h4 Register a new account + %h4= during_oauth_flow? ? "One step more - we need your email address" : "Register a new account" %label.name %span.label Name From ce37f011b4c51261e8d7dfe59a40e5f96bd268d4 Mon Sep 17 00:00:00 2001 From: pietia Date: Tue, 25 Jun 2013 16:53:59 +0200 Subject: [PATCH 278/499] upgraded rails --- Gemfile | 2 +- Gemfile.lock | 61 ++++++++++++++++++++++++++-------------------------- 2 files changed, 31 insertions(+), 32 deletions(-) diff --git a/Gemfile b/Gemfile index c5bf3b53..a80b6d51 100644 --- a/Gemfile +++ b/Gemfile @@ -2,7 +2,7 @@ source 'https://rubygems.org' ruby "2.0.0" -gem 'rails', '4.0.0.rc1' +gem 'rails', '4.0.0' gem 'pg' gem 'unicorn' gem 'settingslogic' diff --git a/Gemfile.lock b/Gemfile.lock index d47034f0..2d58399d 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -12,9 +12,9 @@ GIT GIT remote: git://github.com/rails/sprockets-rails.git - revision: ce61f70669e105d56b65d180cfdc9338b1757577 + revision: b522561d2d83b83c99e9aa54c8079fd1241fdf49 specs: - sprockets-rails (2.0.0.rc4) + sprockets-rails (2.0.0) actionpack (>= 3.0) activesupport (>= 3.0) sprockets (~> 2.8) @@ -33,25 +33,25 @@ GEM accept_values_for (0.4.3) activemodel (>= 3.0.0) rspec - actionmailer (4.0.0.rc1) - actionpack (= 4.0.0.rc1) + actionmailer (4.0.0) + actionpack (= 4.0.0) mail (~> 2.5.3) - actionpack (4.0.0.rc1) - activesupport (= 4.0.0.rc1) + actionpack (4.0.0) + activesupport (= 4.0.0) builder (~> 3.1.0) erubis (~> 2.7.0) rack (~> 1.5.2) rack-test (~> 0.6.2) - activemodel (4.0.0.rc1) - activesupport (= 4.0.0.rc1) + activemodel (4.0.0) + activesupport (= 4.0.0) builder (~> 3.1.0) - activerecord (4.0.0.rc1) - activemodel (= 4.0.0.rc1) + activerecord (4.0.0) + activemodel (= 4.0.0) activerecord-deprecated_finders (~> 1.0.2) - activesupport (= 4.0.0.rc1) + activesupport (= 4.0.0) arel (~> 4.0.0) - activerecord-deprecated_finders (1.0.2) - activesupport (4.0.0.rc1) + activerecord-deprecated_finders (1.0.3) + activesupport (4.0.0) i18n (~> 0.6, >= 0.6.4) minitest (~> 4.2) multi_json (~> 1.3) @@ -59,7 +59,7 @@ GEM tzinfo (~> 0.3.37) addressable (2.3.4) arel (4.0.0) - atomic (1.1.8) + atomic (1.1.9) bcrypt-ruby (3.0.1) better_errors (0.8.0) coderay (>= 1.0.0) @@ -159,14 +159,13 @@ GEM kgio (2.8.0) launchy (2.3.0) addressable (~> 2.3) - mail (2.5.3) - i18n (>= 0.4.0) + mail (2.5.4) mime-types (~> 1.16) treetop (~> 1.4.8) mime-types (1.23) - minitest (4.7.3) + minitest (4.7.5) modernizr-rails (2.6.2.3) - multi_json (1.7.2) + multi_json (1.7.7) multipart-post (1.2.0) neat (1.2.1) bourbon (>= 2.1) @@ -204,23 +203,23 @@ GEM rack rack-test (0.6.2) rack (>= 1.0) - rails (4.0.0.rc1) - actionmailer (= 4.0.0.rc1) - actionpack (= 4.0.0.rc1) - activerecord (= 4.0.0.rc1) - activesupport (= 4.0.0.rc1) + rails (4.0.0) + actionmailer (= 4.0.0) + actionpack (= 4.0.0) + activerecord (= 4.0.0) + activesupport (= 4.0.0) bundler (>= 1.3.0, < 2.0) - railties (= 4.0.0.rc1) - sprockets-rails (~> 2.0.0.rc4) + railties (= 4.0.0) + sprockets-rails (~> 2.0.0) rails_html_helpers (0.1.1) railties (>= 3.2) - railties (4.0.0.rc1) - actionpack (= 4.0.0.rc1) - activesupport (= 4.0.0.rc1) + railties (4.0.0) + actionpack (= 4.0.0) + activesupport (= 4.0.0) rake (>= 0.8.7) thor (>= 0.18.1, < 2.0) raindrops (0.11.0) - rake (10.0.4) + rake (10.1.0) redcarpet (2.3.0) request_store (1.0.5) rest-client (1.6.7) @@ -264,7 +263,7 @@ GEM thread_safe (0.1.0) atomic tilt (1.4.1) - treetop (1.4.12) + treetop (1.4.14) polyglot polyglot (>= 0.3.1) turbolinks (1.1.1) @@ -313,7 +312,7 @@ DEPENDENCIES omniauth-twitter (~> 0.0.16) pg rack-robotz (~> 0.0.3) - rails (= 4.0.0.rc1) + rails (= 4.0.0) rails3_serve_static_assets! rails_html_helpers rails_log_stdout! From 8f590c187e5c75c81b2b1614213092367eb2d550 Mon Sep 17 00:00:00 2001 From: pietia Date: Wed, 26 Jun 2013 17:43:09 +0200 Subject: [PATCH 279/499] let's try to fix decorator --- app/decorators/activity_decorator.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/decorators/activity_decorator.rb b/app/decorators/activity_decorator.rb index 5da24334..8405cdac 100644 --- a/app/decorators/activity_decorator.rb +++ b/app/decorators/activity_decorator.rb @@ -34,8 +34,8 @@ def room_left else left = [[object.limit_of_participants - object.participations_count, object.participations_count].min, 0].max if left == 1 then I18n.t("activities.room_left.one") - elsif left > 0 then I18n.t("activities.room_left.none") - else I18n.t("activities.room_left.one") + elsif left > 0 then I18n.t("activities.room_left.many", left: left) + else I18n.t("activities.room_left.none") end end end From 0625a3b9570bf967705c74fa85dc011bc54f0e59 Mon Sep 17 00:00:00 2001 From: pietia Date: Wed, 26 Jun 2013 17:45:01 +0200 Subject: [PATCH 280/499] display email if no name --- app/decorators/activity_decorator.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/decorators/activity_decorator.rb b/app/decorators/activity_decorator.rb index 8405cdac..7fda2025 100644 --- a/app/decorators/activity_decorator.rb +++ b/app/decorators/activity_decorator.rb @@ -2,7 +2,7 @@ class ActivityDecorator < Draper::Decorator delegate_all def creator_name - creator.try(:name) + creator.try(:name) || creator.email end def relation_ship_with(user) From 2555c8fed214cb32ce980f6be421bbd5c72c9f4b Mon Sep 17 00:00:00 2001 From: pietia Date: Wed, 26 Jun 2013 17:52:03 +0200 Subject: [PATCH 281/499] clanup --- app/decorators/activity_decorator.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/decorators/activity_decorator.rb b/app/decorators/activity_decorator.rb index 7fda2025..ce4cfa67 100644 --- a/app/decorators/activity_decorator.rb +++ b/app/decorators/activity_decorator.rb @@ -2,7 +2,9 @@ class ActivityDecorator < Draper::Decorator delegate_all def creator_name - creator.try(:name) || creator.email + if creator + creator.name.blank? ? creator.email : creator.name + end end def relation_ship_with(user) From 30c3512f373dfae599bb124f8469ea94040bc2ce Mon Sep 17 00:00:00 2001 From: pietia Date: Thu, 4 Jul 2013 13:54:43 +0200 Subject: [PATCH 282/499] get rid of assets group --- Gemfile | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/Gemfile b/Gemfile index a80b6d51..a1d3b045 100644 --- a/Gemfile +++ b/Gemfile @@ -25,6 +25,9 @@ gem 'rails_html_helpers' gem 'draper' gem 'cancan' gem 'redcarpet' +gem 'sass-rails', '~> 4.0.0.beta1' +gem 'coffee-rails', '~> 4.0.0.beta1' +gem 'uglifier', '>= 1.0.3' group :development do gem 'debugger', '~> 1.6' @@ -47,12 +50,6 @@ group :test do gem 'json_spec' end -group :assets do - gem 'sass-rails', '~> 4.0.0.beta1' - gem 'coffee-rails', '~> 4.0.0.beta1' - gem 'uglifier', '>= 1.0.3' -end - group :production, :staging do gem 'exception_notification', require: 'exception_notifier', git: 'git://github.com/smartinez87/exception_notification.git' gem 'rack-robotz', '~> 0.0.3' From f9493cb89564625bda3e462d59f39e33ae5a11e9 Mon Sep 17 00:00:00 2001 From: pietia Date: Thu, 18 Jul 2013 09:24:52 +0200 Subject: [PATCH 283/499] fix broken logic in ActivityDecorator#room_left --- app/decorators/activity_decorator.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/decorators/activity_decorator.rb b/app/decorators/activity_decorator.rb index ce4cfa67..0b83cf7e 100644 --- a/app/decorators/activity_decorator.rb +++ b/app/decorators/activity_decorator.rb @@ -34,7 +34,7 @@ def requirements_markdown def room_left if object.anybody_can_join? then "" else - left = [[object.limit_of_participants - object.participations_count, object.participations_count].min, 0].max + left = [[object.limit_of_participants - object.participations_count, 0].max, object.limit_of_participants ].min if left == 1 then I18n.t("activities.room_left.one") elsif left > 0 then I18n.t("activities.room_left.many", left: left) else I18n.t("activities.room_left.none") From 3619719c516640d0e253425798acc43d9385768c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Scha=CC=88fer?= Date: Thu, 18 Jul 2013 10:39:25 +0200 Subject: [PATCH 284/499] Calculation spec --- app/decorators/activity_decorator.rb | 6 +++- spec/decorators/activity_decorator_spec.rb | 35 ++++++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/app/decorators/activity_decorator.rb b/app/decorators/activity_decorator.rb index 0b83cf7e..5b2d7aeb 100644 --- a/app/decorators/activity_decorator.rb +++ b/app/decorators/activity_decorator.rb @@ -34,7 +34,7 @@ def requirements_markdown def room_left if object.anybody_can_join? then "" else - left = [[object.limit_of_participants - object.participations_count, 0].max, object.limit_of_participants ].min + left = open_spots if left == 1 then I18n.t("activities.room_left.one") elsif left > 0 then I18n.t("activities.room_left.many", left: left) else I18n.t("activities.room_left.none") @@ -42,6 +42,10 @@ def room_left end end + def open_spots + [[object.limit_of_participants - object.participations_count, 0].max, object.limit_of_participants ].min + end + def time if object.anytime? I18n.t("activities.anytime") diff --git a/spec/decorators/activity_decorator_spec.rb b/spec/decorators/activity_decorator_spec.rb index 6c620981..c1b86e6b 100644 --- a/spec/decorators/activity_decorator_spec.rb +++ b/spec/decorators/activity_decorator_spec.rb @@ -1,4 +1,39 @@ require 'spec_helper' describe ActivityDecorator do + let (:activity) { FactoryGirl.build_stubbed(:activity, limit_of_participants: 20) } + let (:decorator) { ActivityDecorator.new(activity) } + + describe "#spots_left" do + subject { decorator.open_spots } + + before do + activity.stub(:participations_count).and_return(count) + end + + context "no participants" do + let(:count) { 0 } + + it { should == 20 } + end + + context "some participants" do + let(:count) { 5 } + + it { should == 15 } + end + + context "too many participants" do + let(:count) { 25 } + + it { should == 0 } + end + + context "you're drunk Rails, go home!" do + # it seems Rails counter caches return negative values sometimes + let(:count) { -5 } + + it { should == 20 } + end + end end From d8f2815c577f775d66169c46a2b4c051e02474c5 Mon Sep 17 00:00:00 2001 From: pietia Date: Fri, 19 Jul 2013 00:33:26 +0200 Subject: [PATCH 285/499] add basic travis.yml --- .travis.yml | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000..fc47b848 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,9 @@ +branches: + only: + - master + - railscamp_germany +before_script: cp config/database.yml.example config/database.yml +language: ruby +rvm: + - 2.0.0 +script: bundle exec rake db:create db:migrate spec From 7ba05994fa4132c3c52d183acb2ca34c573264c3 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Sun, 4 Aug 2013 18:06:12 +0200 Subject: [PATCH 286/499] Updating filters and search for use without Javascript. --- .../stylesheets/partials/activities/_filters.sass | 5 ++++- app/assets/stylesheets/utils/_forms.sass | 4 ++++ app/views/activities/index.html.haml | 12 ++++++------ 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/app/assets/stylesheets/partials/activities/_filters.sass b/app/assets/stylesheets/partials/activities/_filters.sass index f2891e93..5a1e6522 100644 --- a/app/assets/stylesheets/partials/activities/_filters.sass +++ b/app/assets/stylesheets/partials/activities/_filters.sass @@ -25,6 +25,9 @@ form.filters margin: 0 0.25em 0.25em 0 line-height: 1px + html.no-js & + margin: 0 0.25em 0.1em 0 + &.participant:before color: $green @@ -72,7 +75,7 @@ form.filters color: $white border: none margin: 1em auto - padding: 0.45em 2em + padding: 0.4em 2em display: block +transition(all 0.15s) diff --git a/app/assets/stylesheets/utils/_forms.sass b/app/assets/stylesheets/utils/_forms.sass index 9defc390..faef096d 100644 --- a/app/assets/stylesheets/utils/_forms.sass +++ b/app/assets/stylesheets/utils/_forms.sass @@ -18,6 +18,10 @@ form +placeholder color: darken($light-gray, 10) + &.radio + +inline-block + width: auto + &.markdown background-image: image-url('shared/markdown.png') background-repeat: no-repeat diff --git a/app/views/activities/index.html.haml b/app/views/activities/index.html.haml index a484f847..be0b7a8b 100644 --- a/app/views/activities/index.html.haml +++ b/app/views/activities/index.html.haml @@ -1,26 +1,26 @@ %form.filters(action=activities_path method="GET" id="activities-filter") %label> - %input(type="radio" name="filter" value="today") + = radio_button_tag "filter", "today", params[:filter] == "today", class: "radio" = t("activities.filters.today") %strong = @counters[:today] %label.all.selected> - %input(type="radio" name="filter" value="all") - = t("activities.filters.all") + = radio_button_tag "filter", "all", params[:filter] == "all" || params[:filter].nil?, class: "radio" + = t("activities.filters.all") %strong = @counters[:all] %label.participant> - %input(type="radio" name="filter" value="participant") + = radio_button_tag "filter", "participant", params[:filter] == "participant", class: "radio" = t("activities.filters.participant") %strong = @counters[:participant] %label.owner> - %input(type="radio" name="filter" value="owner") + = radio_button_tag "filter", "owner", params[:filter] == "owner", class: "radio" = t("activities.filters.owner") %strong = @counters[:owner] %label.search - %input(type="text" name="search" placeholder="#{ t("activities.search.placeholder") }") + %input(type="text" name="search" placeholder="#{ t("activities.search.placeholder") }" value="#{ params[:search] }") %a.clear(href="#" title="Reset") = t("activities.search.clear") %button(type="submit") From 60c9369201c7b053a9d6035533d29a26309c93a1 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Sun, 4 Aug 2013 18:06:47 +0200 Subject: [PATCH 287/499] Fixing layout for activity image without javascript. --- app/assets/stylesheets/partials/activities/_index.sass | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/assets/stylesheets/partials/activities/_index.sass b/app/assets/stylesheets/partials/activities/_index.sass index 7ad303f4..a35a190d 100644 --- a/app/assets/stylesheets/partials/activities/_index.sass +++ b/app/assets/stylesheets/partials/activities/_index.sass @@ -108,6 +108,10 @@ a#new-activity border-top: 1px solid $light-gray padding: 0.75em 0 0 + // + .progress + +inline-block + // the big button appearing on hover .action opacity: 0 From 78b758c287a838d5d0aed02abedc368cff0023e9 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Sun, 4 Aug 2013 19:01:32 +0200 Subject: [PATCH 288/499] Fixing old JS method call. Closes #78. --- app/views/participations/create.js.erb | 2 +- app/views/participations/destroy.js.erb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/participations/create.js.erb b/app/views/participations/create.js.erb index fa2b1019..230c7ade 100644 --- a/app/views/participations/create.js.erb +++ b/app/views/participations/create.js.erb @@ -1,6 +1,6 @@ $('#activity-<%= @current_activity.id %>') .replaceWith('<%= escape_javascript(render partial: 'activities/activity', locals: { activity: @current_activity }) %>'); $('#activity-<%= @current_activity.id %>') - .find('img.progress') + .find('.progress') .progress(); $('#activities-filter .participant strong').text('<%= Activity.participated_by(current_user).count %>') diff --git a/app/views/participations/destroy.js.erb b/app/views/participations/destroy.js.erb index fa2b1019..230c7ade 100644 --- a/app/views/participations/destroy.js.erb +++ b/app/views/participations/destroy.js.erb @@ -1,6 +1,6 @@ $('#activity-<%= @current_activity.id %>') .replaceWith('<%= escape_javascript(render partial: 'activities/activity', locals: { activity: @current_activity }) %>'); $('#activity-<%= @current_activity.id %>') - .find('img.progress') + .find('.progress') .progress(); $('#activities-filter .participant strong').text('<%= Activity.participated_by(current_user).count %>') From 6acf78d42a02125f443a8158c5e02ff957921362 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Sun, 4 Aug 2013 19:17:46 +0200 Subject: [PATCH 289/499] Reload activity. --- app/controllers/participations_controller.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/controllers/participations_controller.rb b/app/controllers/participations_controller.rb index abbfc023..6ca5f134 100644 --- a/app/controllers/participations_controller.rb +++ b/app/controllers/participations_controller.rb @@ -4,14 +4,14 @@ class ParticipationsController < ApiController def create @participation = current_activity.new_participation(current_user) @participation.save - respond_with(current_activity, @participation, location: activities_path) + respond_with(current_activity.reload, @participation, location: activities_path) end def destroy @participation = current_activity.participation(current_user) authorize!(:destroy, @participation) @participation.destroy - respond_with(current_activity, @participation, location: activities_path) + respond_with(current_activity.reload, @participation, location: activities_path) end private From aac5bb188f275f8b3347f204e3e5b20ef9216e99 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Sun, 4 Aug 2013 19:19:42 +0200 Subject: [PATCH 290/499] More no-js fixes. --- app/assets/stylesheets/partials/activities/_index.sass | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/assets/stylesheets/partials/activities/_index.sass b/app/assets/stylesheets/partials/activities/_index.sass index a35a190d..ca85a984 100644 --- a/app/assets/stylesheets/partials/activities/_index.sass +++ b/app/assets/stylesheets/partials/activities/_index.sass @@ -151,7 +151,8 @@ a#new-activity opacity: 1 +transition(all 0.25s ease-in-out) - .progress-wrapper + .progress-wrapper, + .container > .progress @extend %transition-all-15s-ease-out +transform(translate(0, -9em)) opacity: 0 From 8b431b5d69b0508c44db08818ab024a6942a6f2b Mon Sep 17 00:00:00 2001 From: Polarblau Date: Sun, 4 Aug 2013 19:21:29 +0200 Subject: [PATCH 291/499] Remove debugging code. --- app/assets/javascripts/initializers.coffee | 1 - 1 file changed, 1 deletion(-) diff --git a/app/assets/javascripts/initializers.coffee b/app/assets/javascripts/initializers.coffee index 1fe59eff..85a51704 100644 --- a/app/assets/javascripts/initializers.coffee +++ b/app/assets/javascripts/initializers.coffee @@ -1,6 +1,5 @@ ready = -> $(document).ajaxError (e, xhr) -> - console.error "AJAX ERROR!", arguments if console?.error? window.location.replace(App.paths.login) if xhr.status == 401 # section toggles From 13047ea1e81bbbdaa9b189aa15101400bcadf72c Mon Sep 17 00:00:00 2001 From: Polarblau Date: Sun, 4 Aug 2013 19:22:04 +0200 Subject: [PATCH 292/499] Clean-up. --- app/assets/javascripts/initializers.coffee | 6 ------ 1 file changed, 6 deletions(-) diff --git a/app/assets/javascripts/initializers.coffee b/app/assets/javascripts/initializers.coffee index 85a51704..0971f711 100644 --- a/app/assets/javascripts/initializers.coffee +++ b/app/assets/javascripts/initializers.coffee @@ -2,12 +2,6 @@ ready = -> $(document).ajaxError (e, xhr) -> window.location.replace(App.paths.login) if xhr.status == 401 - # section toggles - $('.reveal').on 'click', -> - $("##{@href.split('#')[1]}").show() - - $("a.reveal[href='#{window.location.hash}']").trigger('click') - # hide validation errors on focus $('input.validation-error').on 'focus', -> $(@).next('span.validation-error-message').fadeOut() From 2fa74e1f8c6db4988782f232d12b460983637799 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Sun, 4 Aug 2013 19:24:17 +0200 Subject: [PATCH 293/499] Remove doubled no-js class. --- app/views/layouts/application.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml index a6d4d75c..a6c6b91c 100644 --- a/app/views/layouts/application.html.haml +++ b/app/views/layouts/application.html.haml @@ -1,5 +1,5 @@ !!! -- html_tag :lang => 'en', :class => "no-js" do +- html_tag :lang => 'en' do %head = render "partials/layout_head" = render "partials/preinitializers" From f519c02060b3a8387bce1ad9d02b1a2bb497d820 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Sun, 4 Aug 2013 19:42:53 +0200 Subject: [PATCH 294/499] Participant listing. Closes #70. --- app/assets/javascripts/initializers.coffee | 6 +++++ .../partials/activities/_show.sass | 22 +++++++++++++++++++ app/decorators/activity_decorator.rb | 2 +- app/views/activities/show.html.haml | 16 +++++++++++--- 4 files changed, 42 insertions(+), 4 deletions(-) diff --git a/app/assets/javascripts/initializers.coffee b/app/assets/javascripts/initializers.coffee index 0971f711..be325b31 100644 --- a/app/assets/javascripts/initializers.coffee +++ b/app/assets/javascripts/initializers.coffee @@ -10,6 +10,12 @@ ready = -> $('#activities .progress').progress() $('#activity .progress').progress(strokeWidth: 12) + # show participants list + $('a.participants').on 'click', (e)-> + e.preventDefault() + $(@).hide() + $('section.participants').show() + $('.date-capture').pickadate min: new Date(App.event.startTime) max: new Date(App.event.endTime) diff --git a/app/assets/stylesheets/partials/activities/_show.sass b/app/assets/stylesheets/partials/activities/_show.sass index 4a8de6a1..bf40ce3e 100644 --- a/app/assets/stylesheets/partials/activities/_show.sass +++ b/app/assets/stylesheets/partials/activities/_show.sass @@ -75,3 +75,25 @@ $types: participant ok remove, owner edit edit dd +custom-serif margin: 0 + + &.participants + width: 100% + padding: 2em 0 + clear: both + display: none + + h3 + +custom-sans(light) + text-align: center + @extend %ornamental-divider-after + + ul + @extend %unlisted + text-align: center + + li + padding: 0 5px + +inline-block + + img + border-radius: 24px diff --git a/app/decorators/activity_decorator.rb b/app/decorators/activity_decorator.rb index 5b2d7aeb..22301c40 100644 --- a/app/decorators/activity_decorator.rb +++ b/app/decorators/activity_decorator.rb @@ -19,7 +19,7 @@ def status if object.anytime? then "" elsif object.today? then "today" elsif object.upcoming? then "upcoming" - else "archive" + else "archive" end end diff --git a/app/views/activities/show.html.haml b/app/views/activities/show.html.haml index 62384d82..151dcb20 100644 --- a/app/views/activities/show.html.haml +++ b/app/views/activities/show.html.haml @@ -18,9 +18,10 @@ = render "activities/progress", activity: @activity %p = @activity.room_left - %a.participants(href="#" title="") - %span - = t("activities.participants") + - if @activity.participants.any? + %a.participants(href="#" title="") + %span + = t("activities.participants") %section.details %dl.wrapper @@ -42,3 +43,12 @@ = t("activities.location") %dd = @activity.location + + - if @activity.participants.any? + %section.participants + %h3 + = t("activities.participants") + %ul + - @activity.participants.each do |participant| + %li(title="#{ participant.name }")> + = image_tag(avatar_url(participant, 48)) From 011cae129df636ec5eaa2aeedb0ca7476117fda1 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Sun, 4 Aug 2013 19:53:05 +0200 Subject: [PATCH 295/499] Enable join/cancel button on #show page. Closes #83. --- app/controllers/participations_controller.rb | 4 ++-- app/views/activities/_action.html.haml | 5 +++-- app/views/activities/show.html.haml | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/app/controllers/participations_controller.rb b/app/controllers/participations_controller.rb index 6ca5f134..15e97c26 100644 --- a/app/controllers/participations_controller.rb +++ b/app/controllers/participations_controller.rb @@ -4,14 +4,14 @@ class ParticipationsController < ApiController def create @participation = current_activity.new_participation(current_user) @participation.save - respond_with(current_activity.reload, @participation, location: activities_path) + respond_with(current_activity.reload, @participation, location: request.referer) end def destroy @participation = current_activity.participation(current_user) authorize!(:destroy, @participation) @participation.destroy - respond_with(current_activity.reload, @participation, location: activities_path) + respond_with(current_activity.reload, @participation, location: request.referer) end private diff --git a/app/views/activities/_action.html.haml b/app/views/activities/_action.html.haml index ec667e9b..db06414d 100644 --- a/app/views/activities/_action.html.haml +++ b/app/views/activities/_action.html.haml @@ -1,3 +1,4 @@ +- remote = defined?(remote) ? remote : true .action(class=type) - case type - when "owner" @@ -5,10 +6,10 @@ %span = t("activities.edit.label") - when "participant" - = button_to activity_participation_path(activity), method: :delete, remote: true, title: t("activities.leave.title") do + = button_to activity_participation_path(activity), method: :delete, remote: remote, title: t("activities.leave.title") do %span = t("activities.leave.label") - else - = button_to activity_participation_path(activity), method: :post, remote: true, title: t("activities.join.title") do + = button_to activity_participation_path(activity), method: :post, remote: remote, title: t("activities.join.title") do %span = t("activities.join.label") diff --git a/app/views/activities/show.html.haml b/app/views/activities/show.html.haml index 151dcb20..0968c066 100644 --- a/app/views/activities/show.html.haml +++ b/app/views/activities/show.html.haml @@ -5,7 +5,7 @@ %p.meta = t("activities.organized_by", name: @activity.creator_name).html_safe - = render "action", :activity => @activity, :type => @activity.relation_ship_with(current_user) + = render "action", activity: @activity, type: @activity.relation_ship_with(current_user), remote: false %section.description .wrapper From 1c80a63e0edbb5241a84ecd92495276418a3d0bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20G=C4=99ga?= Date: Mon, 5 Aug 2013 10:08:30 +0200 Subject: [PATCH 296/499] Not needed with latest rack-robotz --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index c588632f..40977747 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,6 @@ ### **ENV** variables: -* `DISABLE_ROBOTS` - set to `true` if you want to block robots from tracking content (via `robots.txt`) * `GITHUB_KEY` * `GITHUB_SECRET` * `TWITTER_KEY` From 3fcff7e8c6f32571609a3703f6c69962984e946a Mon Sep 17 00:00:00 2001 From: moonglum Date: Tue, 23 Jul 2013 11:58:13 +0200 Subject: [PATCH 297/499] Marked some specs as pending #82 > yup. i had to remove validations from Authentication (due to race > conditions caused by circular dependencies and this code: > https://github.com/eurucamp/eurucamp-activities-2013/blob/master/app/models/user.rb#L21 > (btw, omniauth part of this app was copied from our call4papers app)) ) > and now these tests are failing. > Feel free to mark all of them as pending. @pieta --- spec/models/authentication_spec.rb | 17 +++++++++++++---- spec/models/user_spec.rb | 6 +++++- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/spec/models/authentication_spec.rb b/spec/models/authentication_spec.rb index d20de809..3564c566 100644 --- a/spec/models/authentication_spec.rb +++ b/spec/models/authentication_spec.rb @@ -6,11 +6,20 @@ specify { expect { FactoryGirl.create(:activity).dup.save! }.to raise_exception(ActiveRecord::RecordInvalid) } it { should accept_values_for(:user_id, 10) } - it { should_not accept_values_for(:user_id, nil, "") } + it do + pending "Currently Removed" + # should_not accept_values_for(:user_id, nil, "") } + end it { should accept_values_for(:provider, "github", "twitter") } - it { should_not accept_values_for(:provider, nil, "") } + it do + pending "Currently Removed" + # should_not accept_values_for(:provider, nil, "") } + end it { should accept_values_for(:uid, "asd123dasd", "x") } - it { should_not accept_values_for(:uid, nil, "") } -end \ No newline at end of file + it do + pending "Currently Removed" + # should_not accept_values_for(:uid, nil, "") } + end +end diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index a99d899d..28d53e92 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -138,7 +138,11 @@ it { should be_a_kind_of(Authentication) } its(:user) { should == user } - it { should be_invalid } + + it do + pending "Currently Removed" + should be_invalid + end end context "data provided" do From db028604c220e7b78a04ea6292026e5637f5057b Mon Sep 17 00:00:00 2001 From: pietia Date: Mon, 5 Aug 2013 20:02:48 +0200 Subject: [PATCH 298/499] stub reload method --- spec/controllers/participation_controller_spec.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/spec/controllers/participation_controller_spec.rb b/spec/controllers/participation_controller_spec.rb index 317b9ef6..4e6a8917 100644 --- a/spec/controllers/participation_controller_spec.rb +++ b/spec/controllers/participation_controller_spec.rb @@ -9,6 +9,7 @@ let(:current_event) { mock(:current_event) } before do + activity.stub!(:reload).and_return(activity) controller.stub!(:current_event).and_return(current_event) end From 7467b980f25386f6865b7abed3c56d68349dbe9b Mon Sep 17 00:00:00 2001 From: pietia Date: Mon, 5 Aug 2013 20:48:42 +0200 Subject: [PATCH 299/499] add basic tags --- app/views/partials/_layout_head.html.haml | 6 +++--- config/application.yml | 4 ++++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/app/views/partials/_layout_head.html.haml b/app/views/partials/_layout_head.html.haml index 1d07fdf4..23aa463e 100644 --- a/app/views/partials/_layout_head.html.haml +++ b/app/views/partials/_layout_head.html.haml @@ -1,8 +1,8 @@ %meta(charset="utf-8") %meta(content="IE=edge,chrome=1" http-equiv="X-UA-Compatible") -%title= title("Eurucamp Activities") -%meta(content="" name="description") -%meta(content="" name="author") +%title= title(Settings.seo.title) +%meta{content: Settings.seo.description, name: "description"} +%meta{content: Settings.seo.author, name: "author"} %meta(content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0" name="viewport") = csrf_meta_tags diff --git a/config/application.yml b/config/application.yml index c89e2368..769f4cde 100644 --- a/config/application.yml +++ b/config/application.yml @@ -11,6 +11,10 @@ defaults: &defaults to: - "piotr@gega.io" - "florian@polarblau.com" + seo: + title: "Eurucamp Activities" + author: "Eurucamp team" + description: "List of companion events." development: <<: *defaults From 54b81013922679e2538d67ce35f2aa95a03772f8 Mon Sep 17 00:00:00 2001 From: pietia Date: Mon, 5 Aug 2013 21:17:07 +0200 Subject: [PATCH 300/499] add default order --- app/models/activity.rb | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/app/models/activity.rb b/app/models/activity.rb index 610850f2..250ca55d 100644 --- a/app/models/activity.rb +++ b/app/models/activity.rb @@ -45,32 +45,36 @@ def participated_by(user) find_participated_by(user) end + def order_by_start_time + order("anytime DESC, start_time ASC") + end + private def find_all(limit) - limit(limit) + limit(limit).order_by_start_time end def find_recent(limit) where("start_time >= :t OR anytime = true", t: 1.month.ago) .limit(limit) - .order("anytime DESC, start_time ASC") + .order_by_start_time end def find_today - where(":t between start_time and end_time", t: Date.current) + where(":t between start_time and end_time", t: Date.current).order_by_start_time end def find_with_name_like(name) - where("name LIKE :q", q: "%#{name}%") + where("name LIKE :q", q: "%#{name}%").order_by_start_time end def find_created_by(user) - where(creator_id: user) + where(creator_id: user).order_by_start_time end def find_participated_by(user) - includes(:participations).where(participations: { user_id: user }) + includes(:participations).where(participations: { user_id: user }).order_by_start_time end end From 9fd1f5f803c6bf4421096924b77c173ceb78c393 Mon Sep 17 00:00:00 2001 From: pietia Date: Mon, 5 Aug 2013 21:20:41 +0200 Subject: [PATCH 301/499] some notes about customization #10 --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index 40977747..67612629 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,14 @@ * `tail -f log/development.log` * `open http://activities.dev:3000` +### Customization + +*(work in progress)* + +* Fork it +* edit `config/application.yml` +* edit `app/assets/stylesheets/_settings.sass` + ## Deployment ### How to deploy app? From ebfb8fda23e587477d2dffa35e61d5e53a88bfff Mon Sep 17 00:00:00 2001 From: pietia Date: Mon, 5 Aug 2013 21:27:08 +0200 Subject: [PATCH 302/499] make server side caseinsensitive #86 --- app/models/activity.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/activity.rb b/app/models/activity.rb index 250ca55d..bd4000b0 100644 --- a/app/models/activity.rb +++ b/app/models/activity.rb @@ -66,7 +66,7 @@ def find_today end def find_with_name_like(name) - where("name LIKE :q", q: "%#{name}%").order_by_start_time + where("name ILIKE :q", q: "%#{name}%").order_by_start_time end def find_created_by(user) From 32b3012d0f42cd04db80483d416464a5b79c08c5 Mon Sep 17 00:00:00 2001 From: pietia Date: Mon, 5 Aug 2013 22:04:45 +0200 Subject: [PATCH 303/499] add license #2 --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 67612629..1f66178f 100644 --- a/README.md +++ b/README.md @@ -35,5 +35,8 @@ * `TWITTER_KEY` * `TWITTER_SECRET` +## License + +This application is released under [GPL-2.0 license](https://www.gnu.org/licenses/gpl-2.0.txt). From 091df2a4f0693566fac33eb3dad565b3c00b62eb Mon Sep 17 00:00:00 2001 From: pietia Date: Mon, 5 Aug 2013 22:14:28 +0200 Subject: [PATCH 304/499] Revert "add license #2" This reverts commit 32b3012d0f42cd04db80483d416464a5b79c08c5. --- README.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/README.md b/README.md index 1f66178f..67612629 100644 --- a/README.md +++ b/README.md @@ -35,8 +35,5 @@ * `TWITTER_KEY` * `TWITTER_SECRET` -## License - -This application is released under [GPL-2.0 license](https://www.gnu.org/licenses/gpl-2.0.txt). From b51cf1c9ab4bc5a6ca302550ffe8b32c5c0454fb Mon Sep 17 00:00:00 2001 From: pietia Date: Tue, 6 Aug 2013 15:04:09 +0200 Subject: [PATCH 305/499] remove empty links --- app/views/partials/_footer.html.haml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/app/views/partials/_footer.html.haml b/app/views/partials/_footer.html.haml index 648f3c60..0ee91ed2 100644 --- a/app/views/partials/_footer.html.haml +++ b/app/views/partials/_footer.html.haml @@ -3,7 +3,6 @@ -# cross-site navigation, add correct links %nav %ul - - %w(about policy eurucamp).each do |target| - %li - %a(href="#" title="#{t("footer_nav.#{target}.title")}") - = t("footer_nav.#{target}.label") + %li + %a(href="http://eurucamp.org" title="#{t("footer_nav.eurucamp.title")}") + = t("footer_nav.#{target}.label") From 1722db77f5434d9633d8bec0eca9ec6c8d22fac8 Mon Sep 17 00:00:00 2001 From: pietia Date: Tue, 6 Aug 2013 15:04:27 +0200 Subject: [PATCH 306/499] temp fix for too-bright link colors --- app/assets/stylesheets/utils/_placeholders.sass | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/stylesheets/utils/_placeholders.sass b/app/assets/stylesheets/utils/_placeholders.sass index e80117a1..23b606ad 100644 --- a/app/assets/stylesheets/utils/_placeholders.sass +++ b/app/assets/stylesheets/utils/_placeholders.sass @@ -51,7 +51,7 @@ content: none a - color: $light-gray + color: $medium-gray border: none text-transform: uppercase +custom-sans(medium) From a1484a6709bf63086204b07bb8448075e948b140 Mon Sep 17 00:00:00 2001 From: pietia Date: Tue, 6 Aug 2013 15:08:38 +0200 Subject: [PATCH 307/499] bugfix: removed var --- app/views/partials/_footer.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/partials/_footer.html.haml b/app/views/partials/_footer.html.haml index 0ee91ed2..87e1170c 100644 --- a/app/views/partials/_footer.html.haml +++ b/app/views/partials/_footer.html.haml @@ -5,4 +5,4 @@ %ul %li %a(href="http://eurucamp.org" title="#{t("footer_nav.eurucamp.title")}") - = t("footer_nav.#{target}.label") + = t("footer_nav.eurucamp.label") From 59658901a44d9a331460e305d671b1b5742aedc4 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Tue, 6 Aug 2013 16:16:29 +0200 Subject: [PATCH 308/499] Increase contrast. --- app/assets/stylesheets/_settings.sass | 4 ++-- app/assets/stylesheets/utils/_forms.sass | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/assets/stylesheets/_settings.sass b/app/assets/stylesheets/_settings.sass index f4c00e52..50bcc6a4 100644 --- a/app/assets/stylesheets/_settings.sass +++ b/app/assets/stylesheets/_settings.sass @@ -32,7 +32,7 @@ $black: #222222 $extra-dark-gray: #2c2c2c $darker-gray: #797979 $medium-gray: #8a8a8a -$light-gray: #e2e2e2 -$extra-light-gray: #f1f1f1 +$light-gray: darken(#e2e2e2, 10) +$extra-light-gray: darken(#f1f1f1, 5) $white: #ffffff diff --git a/app/assets/stylesheets/utils/_forms.sass b/app/assets/stylesheets/utils/_forms.sass index faef096d..58ac39fd 100644 --- a/app/assets/stylesheets/utils/_forms.sass +++ b/app/assets/stylesheets/utils/_forms.sass @@ -16,7 +16,7 @@ form resize: none +placeholder - color: darken($light-gray, 10) + color: darken($light-gray, 30) &.radio +inline-block From 9ce5a88bbe90145c767d789d44a39b98baeca800 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Tue, 6 Aug 2013 17:03:46 +0200 Subject: [PATCH 309/499] Re-enable remote form. --- app/views/activities/_activity.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/activities/_activity.html.haml b/app/views/activities/_activity.html.haml index cf5a827d..55f78eb2 100644 --- a/app/views/activities/_activity.html.haml +++ b/app/views/activities/_activity.html.haml @@ -13,7 +13,7 @@ %p.time = activity.time.html_safe - = render "activities/action", activity: activity, type: type + = render "activities/action", activity: activity, type: type, remote: true %a.details(href="#{activity_path(activity)}" title="#{ t("activities.more_info.title") }") %span From 978c28eaabb97e6cd2636fa79b50ad3d1ee59555 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Tue, 6 Aug 2013 21:26:23 +0200 Subject: [PATCH 310/499] Locking container width to fix view on mobile temporarily. --- app/assets/stylesheets/_base.sass | 8 ++++---- app/assets/stylesheets/partials/_layout.sass | 5 ++++- app/assets/stylesheets/partials/_user.sass | 6 +++--- app/views/partials/_layout_head.html.haml | 2 +- 4 files changed, 12 insertions(+), 9 deletions(-) diff --git a/app/assets/stylesheets/_base.sass b/app/assets/stylesheets/_base.sass index 3237722c..e39bf8df 100644 --- a/app/assets/stylesheets/_base.sass +++ b/app/assets/stylesheets/_base.sass @@ -43,7 +43,7 @@ a:hover article @extend %clearfix margin: 0 0 3em 0 - + header text-align: center margin: 0 0 4em 0 @@ -57,14 +57,14 @@ article &:after margin-bottom: 0.25em - + .meta text-transform: uppercase @extend %smaller-text - + strong +custom-sans(medium) - + &.divider +custom-sans(bold) margin: 0 0.5em diff --git a/app/assets/stylesheets/partials/_layout.sass b/app/assets/stylesheets/partials/_layout.sass index 962bae43..3c0473ee 100644 --- a/app/assets/stylesheets/partials/_layout.sass +++ b/app/assets/stylesheets/partials/_layout.sass @@ -1,4 +1,7 @@ #container - +outer-container + // locking width + width: $column * 5 + margin: 0 auto + //+outer-container padding: 0 $padding position: relative diff --git a/app/assets/stylesheets/partials/_user.sass b/app/assets/stylesheets/partials/_user.sass index 6f80b683..14de31d0 100644 --- a/app/assets/stylesheets/partials/_user.sass +++ b/app/assets/stylesheets/partials/_user.sass @@ -39,12 +39,12 @@ body.user &:focus box-shadow: none - + .avatar margin: 0 0 2em 0 img border-radius: 64px - + p.divider text-align: center @extend %ornamental-divider-after @@ -89,7 +89,7 @@ body.user display: block border-radius: 0 3px 3px 0 padding: 2em 0 - + &.connected background: $medium-gray diff --git a/app/views/partials/_layout_head.html.haml b/app/views/partials/_layout_head.html.haml index 23aa463e..d6a100c6 100644 --- a/app/views/partials/_layout_head.html.haml +++ b/app/views/partials/_layout_head.html.haml @@ -3,7 +3,7 @@ %title= title(Settings.seo.title) %meta{content: Settings.seo.description, name: "description"} %meta{content: Settings.seo.author, name: "author"} -%meta(content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0" name="viewport") +//%meta(content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0" name="viewport") = csrf_meta_tags From 7c02c039e48f27998ee586d50e5c502ea2b71d53 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Tue, 6 Aug 2013 21:28:28 +0200 Subject: [PATCH 311/499] Fix wrapping for participants link. --- app/views/activities/show.html.haml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/views/activities/show.html.haml b/app/views/activities/show.html.haml index 0968c066..96012805 100644 --- a/app/views/activities/show.html.haml +++ b/app/views/activities/show.html.haml @@ -18,7 +18,8 @@ = render "activities/progress", activity: @activity %p = @activity.room_left - - if @activity.participants.any? + - if @activity.participants.any? + %p %a.participants(href="#" title="") %span = t("activities.participants") From 1841e17016219642ab568360c069a7443b75e996 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Tue, 6 Aug 2013 22:05:41 +0200 Subject: [PATCH 312/499] High contrast. --- app/assets/javascripts/initializers.coffee | 3 + app/assets/stylesheets/_base.sass | 59 ++++++++++++++++++- app/assets/stylesheets/partials/_header.sass | 7 +++ app/assets/stylesheets/partials/_user.sass | 3 + .../partials/activities/_filters.sass | 2 +- .../partials/activities/_index.sass | 2 +- app/assets/stylesheets/utils/_forms.sass | 8 +-- 7 files changed, 76 insertions(+), 8 deletions(-) diff --git a/app/assets/javascripts/initializers.coffee b/app/assets/javascripts/initializers.coffee index be325b31..95d7b8c7 100644 --- a/app/assets/javascripts/initializers.coffee +++ b/app/assets/javascripts/initializers.coffee @@ -21,6 +21,8 @@ ready = -> max: new Date(App.event.endTime) format: 'ddd, d.m.' formatSubmit: 'dd-mm-yyyy' + today: '' + clear: '' onSet: (e)-> target = @$node.data 'target' {year, month, date} = @get 'select' @@ -44,6 +46,7 @@ ready = -> $('.time-capture').pickatime format: 'HH:i' formatSubmit: 'HH:i' + clear: '' onSet: (e)-> target = @$node.data 'target' [hours, minutes] = [parseInt((e.select / 60), 10), e.select % 60] diff --git a/app/assets/stylesheets/_base.sass b/app/assets/stylesheets/_base.sass index e39bf8df..0b39bb3f 100644 --- a/app/assets/stylesheets/_base.sass +++ b/app/assets/stylesheets/_base.sass @@ -1,5 +1,5 @@ html - background: $extra-light-gray + background: $white body +custom-sans @@ -30,7 +30,7 @@ a:hover +animation-delay(0.25s) @extend %animated-bounce-in-down - &.error + &.error, &.alert background: transparentize($red, 0.2) &.hide @@ -68,3 +68,58 @@ article &.divider +custom-sans(bold) margin: 0 0.5em + +// TODO: extract +// Pickadate overrides + +.picker__input.picker__input--active + border: none + +.picker__box + border-radius: 0 !important + +.picker__nav--next, +.picker__nav--prev + display: none !important + +.picker__day--highlighted:hover, +.picker__day--infocus:hover, +.picker__day--outfocus:hover + color: $white !important + background: $blue !important + +.picker--focused .picker__day--highlighted + background: $green !important + color: $white !important + border: none !important + +.picker__day--disabled:hover, +.picker--focused .picker__day--disabled + border: none !important + +.picker__day--disabled:hover, +.picker--focused .picker__day--disabled, + background: $white !important + color: $light-gray !important + font-weight: 100 !important + +.picker__day + color: $black !important + font-weight: 500 !important + background: $extra-light-gray !important + +.picker__list-item + border-color: $light-gray !important + +.picker__list-item:hover + background: $blue !important + border-color: $blue !important + color: $white !important + +.picker__list-item--selected, +.picker__list-item--selected:hover + color: $white !important + background: $green !important + +.picker__list + padding-bottom: 0 !important diff --git a/app/assets/stylesheets/partials/_header.sass b/app/assets/stylesheets/partials/_header.sass index 9fddcf82..0a301d19 100644 --- a/app/assets/stylesheets/partials/_header.sass +++ b/app/assets/stylesheets/partials/_header.sass @@ -12,6 +12,13 @@ ul @extend %inline-nav-list + a + body.user & + color: $white + + &:hover + color: $blue + +media($smartphone-portrait) nav.user-nav diff --git a/app/assets/stylesheets/partials/_user.sass b/app/assets/stylesheets/partials/_user.sass index 14de31d0..7275a1fc 100644 --- a/app/assets/stylesheets/partials/_user.sass +++ b/app/assets/stylesheets/partials/_user.sass @@ -1,4 +1,5 @@ body.user + background: $gray // the "back home" link only shown on the // user pages within the header #header @@ -18,6 +19,7 @@ body.user h2 +custom-sans(bold) font-size: 1.8em + color: $white &:after content: none @@ -36,6 +38,7 @@ body.user input, textarea background: $extra-light-gray + border: none &:focus box-shadow: none diff --git a/app/assets/stylesheets/partials/activities/_filters.sass b/app/assets/stylesheets/partials/activities/_filters.sass index 5a1e6522..b31eef9b 100644 --- a/app/assets/stylesheets/partials/activities/_filters.sass +++ b/app/assets/stylesheets/partials/activities/_filters.sass @@ -6,7 +6,7 @@ form.filters padding: 0 0 4.5em 0 label - background: $light-gray + background: $extra-light-gray @extend %smaller-text color: $gray text-transform: uppercase diff --git a/app/assets/stylesheets/partials/activities/_index.sass b/app/assets/stylesheets/partials/activities/_index.sass index ca85a984..378de501 100644 --- a/app/assets/stylesheets/partials/activities/_index.sass +++ b/app/assets/stylesheets/partials/activities/_index.sass @@ -56,7 +56,7 @@ a#new-activity // card background and wrapper .container - background: $white + border: 3px solid $extra-light-gray height: 25em padding: 2.5em 1em 1em 1em margin: 0 1px diff --git a/app/assets/stylesheets/utils/_forms.sass b/app/assets/stylesheets/utils/_forms.sass index 58ac39fd..952448e7 100644 --- a/app/assets/stylesheets/utils/_forms.sass +++ b/app/assets/stylesheets/utils/_forms.sass @@ -4,9 +4,8 @@ form input, textarea width: 100% padding: 0.75em 15px 0.9em - background: $white - border: none - border-bottom: 1px solid $white + background: $extra-light-gray + border: 1px solid $extra-light-gray margin: 0 0 2px 0 font-size: 1.25em outline: none @@ -16,7 +15,7 @@ form resize: none +placeholder - color: darken($light-gray, 30) + color: $gray &.radio +inline-block @@ -39,6 +38,7 @@ form &:focus z-index: 1000 + border: 1px solid $white box-shadow: 0 0 7px transparentize($black, 0.9) &.markdown From 3899a2bbf9f428d62a082607cd18a8eb8a93420f Mon Sep 17 00:00:00 2001 From: Polarblau Date: Tue, 6 Aug 2013 22:34:36 +0200 Subject: [PATCH 313/499] Lighter cards. --- app/assets/stylesheets/partials/activities/_index.sass | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/assets/stylesheets/partials/activities/_index.sass b/app/assets/stylesheets/partials/activities/_index.sass index 378de501..21c8888c 100644 --- a/app/assets/stylesheets/partials/activities/_index.sass +++ b/app/assets/stylesheets/partials/activities/_index.sass @@ -56,7 +56,8 @@ a#new-activity // card background and wrapper .container - border: 3px solid $extra-light-gray + background: lighten($extra-light-gray, 5) + border: 1px solid $extra-light-gray height: 25em padding: 2.5em 1em 1em 1em margin: 0 1px From e23377e0c712129a38e289f9889f348d8772d945 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Tue, 6 Aug 2013 23:52:32 +0200 Subject: [PATCH 314/499] Updating pickadate. --- .../javascripts/pickadate/picker.date.js | 56 ++++++++++++------- vendor/assets/javascripts/pickadate/picker.js | 21 ++++--- .../javascripts/pickadate/picker.time.js | 4 +- .../assets/stylesheets/pickadate/default.css | 6 ++ .../stylesheets/pickadate/default.date.css | 8 +-- .../stylesheets/pickadate/default.time.css | 2 +- 6 files changed, 62 insertions(+), 35 deletions(-) diff --git a/vendor/assets/javascripts/pickadate/picker.date.js b/vendor/assets/javascripts/pickadate/picker.date.js index 435ce33a..8b700a69 100755 --- a/vendor/assets/javascripts/pickadate/picker.date.js +++ b/vendor/assets/javascripts/pickadate/picker.date.js @@ -1,6 +1,6 @@ /*! - * Date picker for pickadate.js v3.0.4 + * Date picker for pickadate.js v3.1.4 * http://amsul.github.io/pickadate.js/date.htm */ @@ -32,7 +32,10 @@ var DAYS_IN_WEEK = 7, function DatePicker( picker, settings ) { var calendar = this, - elementDataValue = picker.$node.data( 'value' ) + elementValue = picker.$node[ 0 ].value, + elementDataValue = picker.$node.data( 'value' ), + valueString = elementDataValue || elementValue, + formatString = elementDataValue ? settings.formatSubmit : settings.format calendar.settings = settings @@ -64,12 +67,18 @@ function DatePicker( picker, settings ) { // Setting the `select` also sets the `highlight` and `view`. set( 'select', - // If there's a `value` or `data-value`, use that with formatting. - // Otherwise default to selecting “today”. - elementDataValue || picker.$node[ 0 ].value || calendar.item.now, - - // Use the relevant format and data property. - { format: elementDataValue ? settings.formatSubmit : settings.format, data: !!elementDataValue } + // Use the value provided or default to selecting “today”. + valueString || calendar.item.now, + { + // Use the appropriate format. + format: formatString, + + // Set user-provided month data as true when there is a + // “mm” or “m” used in the relative format string. + data: (function( formatArray ) { + return valueString && ( formatArray.indexOf( 'mm' ) > -1 || formatArray.indexOf( 'm' ) > -1 ) + })( calendar.formats.toArray( formatString ) ) + } ) @@ -170,9 +179,11 @@ DatePicker.prototype.create = function( type, value, options ) { value = value.obj } - // If it’s an array, convert it into a date. + // If it’s an array, convert it into a date and make sure + // that it’s a valid date – otherwise default to today. else if ( Array.isArray( value ) ) { value = new Date( value[ 0 ], value[ 1 ], value[ 2 ] ) + value = Picker._.isDate( value ) ? value : calendar.create().obj } // If it’s a number or date object, make a normalized date. @@ -216,14 +227,14 @@ DatePicker.prototype.navigate = function( type, value, options ) { if ( Picker._.isObject( value ) ) { - var targetDateObject = new Date( value.year, value.month + ( options && options.nav ? options.nav : 0 ), 1 ), + var targetDateObject = new Date( value.year, value.month + ( options && options.nav ? options.nav : 0 ), 1 ), year = targetDateObject.getFullYear(), month = targetDateObject.getMonth(), date = value.date - // If the month we’re going to doesn’t have enough days, - // keep decreasing the date until we reach the month’s last date. - while ( new Date( year, month, date ).getMonth() !== month ) { + // Make sure the date is valid and if the month we’re going to doesn’t have enough + // days, keep decreasing the date until we reach the month’s last date. + while ( Picker._.isDate( targetDateObject ) && new Date( year, month, date ).getMonth() !== month ) { date -= 1 } @@ -320,11 +331,11 @@ DatePicker.prototype.validate = function( type, dateObject, options ) { // [3] Out of range. // // Cases to **not** validate for: + // • Navigating months. // • Not inverted and date enabled. // • Inverted and all dates disabled. - // • Navigating months. - // • ..and anything else. - if ( + // • ..and anything else. + if ( !options.nav ) if ( /* 1 */ ( !isInverted && calendar.disabled( dateObject ) ) || /* 2 */ ( isInverted && calendar.disabled( dateObject ) && ( hasEnabledWeekdays || hasEnabledBeforeTarget || hasEnabledAfterTarget ) ) || /* 3 */ ( dateObject.pick <= minLimitObject.pick || dateObject.pick >= maxLimitObject.pick ) @@ -416,7 +427,7 @@ DatePicker.prototype.parse = function( type, value, options ) { var calendar = this, parsingObject = {} - if ( !value || Picker._.isInteger( value ) || Array.isArray( value ) || Picker._.isDate( value ) || Picker._.isObject( value ) && Picker._.isInteger( value.pick ) ) { + if ( !value || Picker._.isInteger( value ) || Array.isArray( value ) || Picker._.isDate( value ) || Picker._.isObject( value ) && Picker._.isInteger( value.pick ) ) { return value } @@ -447,7 +458,7 @@ DatePicker.prototype.parse = function( type, value, options ) { value = value.substr( formatLength ) }) - // If it’s parsing a `data-value`, compensate for month 0index. + // If it’s parsing a user provided month value, compensate for month 0index. return [ parsingObject.yyyy || parsingObject.yy, +( parsingObject.mm || parsingObject.m ) - ( options.data ? 1 : 0 ), parsingObject.dd || parsingObject.d ] } //DatePicker.prototype.parse @@ -458,7 +469,7 @@ DatePicker.prototype.parse = function( type, value, options ) { DatePicker.prototype.formats = (function() { // Return the length of the first word in a collection. - var getWordLengthFromCollection = function( string, collection, dateObject ) { + function getWordLengthFromCollection( string, collection, dateObject ) { // Grab the first word from the string. var word = string.match( /\w+/ )[ 0 ] @@ -472,6 +483,11 @@ DatePicker.prototype.formats = (function() { return word.length } + // Get the length of the first word in a string. + function getFirstWordLength( string ) { + return string.match( /\w+/ )[ 0 ].length + } + return { d: function( string, dateObject ) { @@ -757,7 +773,7 @@ DatePicker.prototype.nodes = function( isOpen ) { highestYear = maxYear } - return Picker._.node( 'select', Picker._.group({ + return Picker._.node( 'select', Picker._.group({ min: lowestYear, max: highestYear, i: 1, diff --git a/vendor/assets/javascripts/pickadate/picker.js b/vendor/assets/javascripts/pickadate/picker.js index ea8e67ef..68469e8b 100755 --- a/vendor/assets/javascripts/pickadate/picker.js +++ b/vendor/assets/javascripts/pickadate/picker.js @@ -1,6 +1,6 @@ /*! - * pickadate.js v3.0.4, 2013/05/25 + * pickadate.js v3.1.4, 2013/07/26 * By Amsul, http://amsul.ca * Hosted on http://amsul.github.io/pickadate.js * Licensed under MIT @@ -87,7 +87,7 @@ function PickerConstructor( ELEMENT, NAME, COMPONENT, OPTIONS ) { // Create a new picker component with the settings. - P.component = new COMPONENT( P, SETTINGS ) + P.component = new COMPONENT( P, SETTINGS ) // Create the picker root with a new wrapped holder and bind the events. @@ -147,7 +147,7 @@ function PickerConstructor( ELEMENT, NAME, COMPONENT, OPTIONS ) { // If there’s a format for the hidden input element, create the element // using the name of the original input plus suffix. Otherwise set it to null. // If the element has a value, use either the `data-value` or `value`. - P._hidden = SETTINGS.formatSubmit ? $( '' )[ 0 ] : undefined + P._hidden = SETTINGS.formatSubmit ? $( '' )[ 0 ] : undefined // Add the class and bind the events on the element. @@ -186,7 +186,7 @@ function PickerConstructor( ELEMENT, NAME, COMPONENT, OPTIONS ) { // If `delete` was pressed, clear the values and close the picker. // Otherwise open the picker. - if ( isKeycodeDelete ) { P.clear().close() } + if ( isKeycodeDelete ) { P.clear().close() } else { P.open() } } }). @@ -194,13 +194,18 @@ function PickerConstructor( ELEMENT, NAME, COMPONENT, OPTIONS ) { // If there’s a `data-value`, update the value of the element. val( $ELEMENT.data( 'value' ) ? PickerConstructor._.trigger( P.component.formats.toString, P.component, [ SETTINGS.format, P.component.item.select ] ) : ELEMENT.value ). - // Insert the root and hidden input after the element. - after( P.$root, P._hidden ). + // Insert the hidden input after the element. + after( P._hidden ). // Store the picker data by component name. data( NAME, P ) + // Insert the root as specified in the settings. + if ( SETTINGS.container ) $( SETTINGS.container ).append( P.$root ) + else $ELEMENT.after( P.$root ) + + // Bind the default component and settings events. P.on({ start: P.component.onStart, @@ -255,7 +260,7 @@ function PickerConstructor( ELEMENT, NAME, COMPONENT, OPTIONS ) { P.close() // Remove the hidden field. - if ( P._hidden ) { + if ( P._hidden ) { P._hidden.parentNode.removeChild( P._hidden ) } @@ -721,7 +726,7 @@ PickerConstructor._ = { * Tell if something is a date object. */ isDate: function( value ) { - return {}.toString.call( value ).indexOf( 'Date' ) > -1 + return {}.toString.call( value ).indexOf( 'Date' ) > -1 && this.isInteger( value.getDate() ) }, diff --git a/vendor/assets/javascripts/pickadate/picker.time.js b/vendor/assets/javascripts/pickadate/picker.time.js index 5f875a57..3e97d84e 100755 --- a/vendor/assets/javascripts/pickadate/picker.time.js +++ b/vendor/assets/javascripts/pickadate/picker.time.js @@ -1,6 +1,6 @@ /*! - * Time picker for pickadate.js v3.0.4 + * Time picker for pickadate.js v3.1.4 * http://amsul.github.io/pickadate.js/time.htm */ @@ -370,7 +370,7 @@ TimePicker.prototype.parse = function( type, value, options ) { clock = this, parsingObject = {} - if ( !value || Picker._.isInteger( value ) || Array.isArray( value ) || Picker._.isDate( value ) || Picker._.isObject( value ) && Picker._.isInteger( value.pick ) ) { + if ( !value || Picker._.isInteger( value ) || Array.isArray( value ) || Picker._.isDate( value ) || Picker._.isObject( value ) && Picker._.isInteger( value.pick ) ) { return value } diff --git a/vendor/assets/stylesheets/pickadate/default.css b/vendor/assets/stylesheets/pickadate/default.css index cea60bd9..ffa808e0 100755 --- a/vendor/assets/stylesheets/pickadate/default.css +++ b/vendor/assets/stylesheets/pickadate/default.css @@ -49,6 +49,12 @@ position: absolute; z-index: 10000; } +/** + * The picker input element. + */ +.picker__input { + cursor: default; +} /** * When the picker is opened, the input element is “activated”. */ diff --git a/vendor/assets/stylesheets/pickadate/default.date.css b/vendor/assets/stylesheets/pickadate/default.date.css index 93006db6..7c9ae6ec 100755 --- a/vendor/assets/stylesheets/pickadate/default.date.css +++ b/vendor/assets/stylesheets/pickadate/default.date.css @@ -227,15 +227,15 @@ background: #0089ec; color: #ffffff; } -.picker__day--disabled:hover, -.picker--focused .picker__day--disabled { +.picker__day--disabled, +.picker__day--disabled:hover { background: #f5f5f5; border-color: #f5f5f5; color: #dddddd; cursor: default; } -.picker__day--highlighted.picker__day--disabled:hover, -.picker--focused .picker__day--highlighted.picker__day--disabled { +.picker__day--highlighted.picker__day--disabled, +.picker__day--highlighted.picker__day--disabled:hover { background: #bbbbbb; } /** diff --git a/vendor/assets/stylesheets/pickadate/default.time.css b/vendor/assets/stylesheets/pickadate/default.time.css index 526ccd0b..3ac41fe0 100755 --- a/vendor/assets/stylesheets/pickadate/default.time.css +++ b/vendor/assets/stylesheets/pickadate/default.time.css @@ -92,7 +92,7 @@ border-color: #f5f5f5; color: #dddddd; cursor: default; - border-color: #777777; + border-color: #dddddd; z-index: auto; } /** From 4ba62ddd16d9e49122afd434ff0f849b907aa65e Mon Sep 17 00:00:00 2001 From: Polarblau Date: Tue, 6 Aug 2013 23:52:40 +0200 Subject: [PATCH 315/499] Tweaking form. --- app/assets/stylesheets/partials/activities/_form.sass | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/assets/stylesheets/partials/activities/_form.sass b/app/assets/stylesheets/partials/activities/_form.sass index 50dbc38c..ac5d0ff9 100644 --- a/app/assets/stylesheets/partials/activities/_form.sass +++ b/app/assets/stylesheets/partials/activities/_form.sass @@ -36,6 +36,8 @@ form#new-activity float: right &.anytime + clear: both + span +custom-sans(bold) text-transform: uppercase From 5b82947861a2cc3f779d06fba36180e6523022da Mon Sep 17 00:00:00 2001 From: Polarblau Date: Tue, 6 Aug 2013 23:59:06 +0200 Subject: [PATCH 316/499] Fixing date picker layout issue. --- app/assets/stylesheets/_base.sass | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/assets/stylesheets/_base.sass b/app/assets/stylesheets/_base.sass index 0b39bb3f..9b83fa7c 100644 --- a/app/assets/stylesheets/_base.sass +++ b/app/assets/stylesheets/_base.sass @@ -71,6 +71,8 @@ article // TODO: extract // Pickadate overrides +.picker + z-index: 1001 !important .picker__input.picker__input--active border: none From e499653233d7181fd41b6ceee4a41ae6878e8053 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Wed, 7 Aug 2013 00:14:45 +0200 Subject: [PATCH 317/499] Fixing date picker. --- app/assets/javascripts/initializers.coffee | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/app/assets/javascripts/initializers.coffee b/app/assets/javascripts/initializers.coffee index 95d7b8c7..cafeea5e 100644 --- a/app/assets/javascripts/initializers.coffee +++ b/app/assets/javascripts/initializers.coffee @@ -49,22 +49,24 @@ ready = -> clear: '' onSet: (e)-> target = @$node.data 'target' - [hours, minutes] = [parseInt((e.select / 60), 10), e.select % 60] - $("#activity_#{target}_4i").val(if hours < 10 then "0#{hours}" else hours) - $("#activity_#{target}_5i").val(minutes) - $('#activity_anytime').attr('checked', false) + if e.select? + [hours, minutes] = [parseInt((e.select / 60), 10), e.select % 60] + + $("#activity_#{target}_4i").val(if hours < 10 then "0#{hours}" else hours) + $("#activity_#{target}_5i").val(if minutes < 10 then "0#{minutes}" else minutes) + $('#activity_anytime').attr('checked', false) onClose: -> otherSelector = @$node.data('update') other = $(otherSelector).pickatime('picker') {hour, mins, pick} = @get 'select' otherPick = other.get('select').pick - + console.log hour, mins if otherSelector.match /end-time/ other.set 'min', [hour, mins] - other.set 'select', [hour, mins] if otherPick < pick + #other.set 'select', [hour, mins] if otherPick < pick else other.set 'max', [hour, mins] - other.set 'select', [hour, mins] if otherPick > pick + #other.set 'select', [hour, mins] if otherPick > pick # filters $filters = $('form.filters label:not(.search)') From 4d10e25f71ada0919dc703a3a50c4154067a4a67 Mon Sep 17 00:00:00 2001 From: pietia Date: Wed, 7 Aug 2013 00:19:59 +0200 Subject: [PATCH 318/499] cleanups --- app/assets/javascripts/initializers.coffee | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/app/assets/javascripts/initializers.coffee b/app/assets/javascripts/initializers.coffee index cafeea5e..bc56bb2f 100644 --- a/app/assets/javascripts/initializers.coffee +++ b/app/assets/javascripts/initializers.coffee @@ -59,14 +59,11 @@ ready = -> otherSelector = @$node.data('update') other = $(otherSelector).pickatime('picker') {hour, mins, pick} = @get 'select' - otherPick = other.get('select').pick - console.log hour, mins + if otherSelector.match /end-time/ other.set 'min', [hour, mins] - #other.set 'select', [hour, mins] if otherPick < pick else other.set 'max', [hour, mins] - #other.set 'select', [hour, mins] if otherPick > pick # filters $filters = $('form.filters label:not(.search)') From fdb06991fa3e411c62e1cb5908a29b6ae93268c4 Mon Sep 17 00:00:00 2001 From: pietia Date: Wed, 7 Aug 2013 00:27:11 +0200 Subject: [PATCH 319/499] remove the param --- app/helpers/application_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 919d359f..75a02a34 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -5,7 +5,7 @@ def gravatar_avatar_url(user, size) end def twitter_avatar_url(handle) - "https://twitter.com/api/users/profile_image/#{handle}?size=bigger" + "https://twitter.com/api/users/profile_image/#{handle}" end def avatar_url(user, size = 64) From 24aa82f7d957197a74fbe3c501315c634226ccaf Mon Sep 17 00:00:00 2001 From: Polarblau Date: Wed, 7 Aug 2013 00:27:44 +0200 Subject: [PATCH 320/499] Forcing image size for participants. --- app/assets/stylesheets/partials/activities/_show.sass | 1 + 1 file changed, 1 insertion(+) diff --git a/app/assets/stylesheets/partials/activities/_show.sass b/app/assets/stylesheets/partials/activities/_show.sass index bf40ce3e..63c43dfc 100644 --- a/app/assets/stylesheets/partials/activities/_show.sass +++ b/app/assets/stylesheets/partials/activities/_show.sass @@ -97,3 +97,4 @@ $types: participant ok remove, owner edit edit img border-radius: 24px + width: 48px From 4f5d4a2acaddc3b150b592d840e2fd8cb0106dee Mon Sep 17 00:00:00 2001 From: Polarblau Date: Wed, 7 Aug 2013 10:06:25 +0200 Subject: [PATCH 321/499] Displaying time in show view. Closes #92. --- app/views/activities/show.html.haml | 4 ++-- config/locales/en.yml | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/app/views/activities/show.html.haml b/app/views/activities/show.html.haml index 96012805..0eb7e026 100644 --- a/app/views/activities/show.html.haml +++ b/app/views/activities/show.html.haml @@ -35,11 +35,11 @@ %dt = t("activities.start.label_short") %dd - = l(@activity.start_time, format: :nice_date) + = l(@activity.start_time, format: :nice_date_and_time) %dt = t("activities.end.label_short") %dd - = l(@activity.end_time, format: :nice_date) + = l(@activity.end_time, format: :nice_date_and_time) %dt = t("activities.location") %dd diff --git a/config/locales/en.yml b/config/locales/en.yml index 9482a8f8..33cdab2a 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -5,6 +5,7 @@ en: date_only: "%d-%m-%Y" nice_time: "%H:%M" nice_date: "%a, %-d.%-m" + nice_date_and_time: "%a, %-d.%-m %H:%M" common: or: Or From 24f00f64ce429ff1a0b1e08d35912a6834cf2610 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Wed, 7 Aug 2013 10:12:15 +0200 Subject: [PATCH 322/499] Tweaking format. --- config/locales/en.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/locales/en.yml b/config/locales/en.yml index 33cdab2a..3903d1f3 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -5,7 +5,7 @@ en: date_only: "%d-%m-%Y" nice_time: "%H:%M" nice_date: "%a, %-d.%-m" - nice_date_and_time: "%a, %-d.%-m %H:%M" + nice_date_and_time: "%a, %-d.%-m — %H:%M" common: or: Or From 5ed83c30f05b99cde850e484c569ec0ca651631d Mon Sep 17 00:00:00 2001 From: Polarblau Date: Wed, 7 Aug 2013 10:12:24 +0200 Subject: [PATCH 323/499] Hide requirements if empty. --- app/views/activities/show.html.haml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/app/views/activities/show.html.haml b/app/views/activities/show.html.haml index 0eb7e026..80b940f2 100644 --- a/app/views/activities/show.html.haml +++ b/app/views/activities/show.html.haml @@ -10,9 +10,11 @@ %section.description .wrapper = @activity.description_markdown - %h4 - = t("activities.required") - = @activity.requirements_markdown + + - unless @activity.requirements.empty? + %h4 + = t("activities.required") + = @activity.requirements_markdown %section.overview = render "activities/progress", activity: @activity From 2b414c49dd6eb170cff5ffa84eda90f528cbfc2b Mon Sep 17 00:00:00 2001 From: pietia Date: Thu, 8 Aug 2013 14:57:53 +0200 Subject: [PATCH 324/499] add smtp configuration --- config/environments/production.rb | 10 ++++++++++ config/environments/staging.rb | 10 ++++++++++ 2 files changed, 20 insertions(+) diff --git a/config/environments/production.rb b/config/environments/production.rb index 8121a938..0248cfb1 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -85,3 +85,13 @@ :sender_address => %{"Exception Notifier" <#{Settings.errors.from}>}, :exception_recipients => Settings.errors.to end + +ActionMailer::Base.smtp_settings = { + :address => 'smtp.sendgrid.net', + :port => '587', + :authentication => :plain, + :user_name => ENV['SENDGRID_USERNAME'], + :password => ENV['SENDGRID_PASSWORD'], + :domain => 'heroku.com', + :enable_starttls_auto => true +} \ No newline at end of file diff --git a/config/environments/staging.rb b/config/environments/staging.rb index 7cf9577e..ed8b4137 100644 --- a/config/environments/staging.rb +++ b/config/environments/staging.rb @@ -86,3 +86,13 @@ :sender_address => %{"Exception Notifier" <#{Settings.errors.from}>}, :exception_recipients => Settings.errors.to end + +ActionMailer::Base.smtp_settings = { + :address => 'smtp.sendgrid.net', + :port => '587', + :authentication => :plain, + :user_name => ENV['SENDGRID_USERNAME'], + :password => ENV['SENDGRID_PASSWORD'], + :domain => 'heroku.com', + :enable_starttls_auto => true +} From f727db2e4da98f1c26d3278a583f8975b023484c Mon Sep 17 00:00:00 2001 From: Polarblau Date: Wed, 7 Aug 2013 11:43:42 +0200 Subject: [PATCH 325/499] Adding eurucamp week bar to activities app. --- app/views/partials/_footer.html.haml | 2 ++ app/views/partials/_layout_head.html.haml | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/app/views/partials/_footer.html.haml b/app/views/partials/_footer.html.haml index 87e1170c..d38b6cb4 100644 --- a/app/views/partials/_footer.html.haml +++ b/app/views/partials/_footer.html.haml @@ -6,3 +6,5 @@ %li %a(href="http://eurucamp.org" title="#{t("footer_nav.eurucamp.title")}") = t("footer_nav.eurucamp.label") + += javascript_include_tag "//eurucamp-week-bar.herokuapp.com/bar.js?group=rand_days&color=e55924" \ No newline at end of file diff --git a/app/views/partials/_layout_head.html.haml b/app/views/partials/_layout_head.html.haml index d6a100c6..19e26e69 100644 --- a/app/views/partials/_layout_head.html.haml +++ b/app/views/partials/_layout_head.html.haml @@ -10,4 +10,4 @@ = stylesheet_link_tag "application" = javascript_include_tag :modernizr -= javascript_include_tag "//use.typekit.net/vor5lqb.js" += javascript_include_tag "//use.typekit.net/vor5lqb.js" \ No newline at end of file From b2819e8110bf8bd9d893f4392ce485adb3b31139 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Thu, 8 Aug 2013 15:27:18 +0200 Subject: [PATCH 326/499] Better focus styles. --- app/assets/stylesheets/utils/_forms.sass | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/stylesheets/utils/_forms.sass b/app/assets/stylesheets/utils/_forms.sass index 952448e7..5b319a19 100644 --- a/app/assets/stylesheets/utils/_forms.sass +++ b/app/assets/stylesheets/utils/_forms.sass @@ -38,7 +38,7 @@ form &:focus z-index: 1000 - border: 1px solid $white + border: 1px solid $blue box-shadow: 0 0 7px transparentize($black, 0.9) &.markdown From 64dea1a238e89472ef24118dc1d9e06cc708ba21 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Thu, 8 Aug 2013 15:56:26 +0200 Subject: [PATCH 327/499] Small style improvements. --- .../stylesheets/partials/activities/_filters.sass | 13 +++++++++++-- .../stylesheets/partials/activities/_index.sass | 11 ++++++----- app/views/activities/index.html.haml | 2 -- 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/app/assets/stylesheets/partials/activities/_filters.sass b/app/assets/stylesheets/partials/activities/_filters.sass index b31eef9b..678a6e8f 100644 --- a/app/assets/stylesheets/partials/activities/_filters.sass +++ b/app/assets/stylesheets/partials/activities/_filters.sass @@ -6,7 +6,7 @@ form.filters padding: 0 0 4.5em 0 label - background: $extra-light-gray + background: lighten($extra-light-gray, 5) @extend %smaller-text color: $gray text-transform: uppercase @@ -45,6 +45,15 @@ form.filters &:hover background: none + input + background: $white image-url('shared/search.png') no-repeat right center + border: 1px solid $light-gray + border-radius: 3px + + &:focus + border-color: $blue + background: none + a position: absolute right: 0 @@ -100,7 +109,7 @@ form.filters strong float: none - &:last-of-type + &.owner border-radius: 0 3px 3px 0 margin: 0 diff --git a/app/assets/stylesheets/partials/activities/_index.sass b/app/assets/stylesheets/partials/activities/_index.sass index 21c8888c..08f57eb1 100644 --- a/app/assets/stylesheets/partials/activities/_index.sass +++ b/app/assets/stylesheets/partials/activities/_index.sass @@ -14,7 +14,7 @@ a#new-activity li display: block +span-columns(2) - margin: 0 0 1em 0 + margin: 0 0 2px 0 text-align: center // icon in the upper right, @@ -57,13 +57,13 @@ a#new-activity // card background and wrapper .container background: lighten($extra-light-gray, 5) - border: 1px solid $extra-light-gray + border: 1px solid lighten($extra-light-gray, 5) height: 25em padding: 2.5em 1em 1em 1em margin: 0 1px position: relative overflow: hidden - +transition(box-shadow 0.25s ease-in-out) + +transition(all 0.35s ease-in-out) // all default text: title, organizer, date etc. .labels @@ -140,8 +140,9 @@ a#new-activity .container z-index: 9001 - +transition(box-shadow 0.25s ease-in-out) - box-shadow: 0 0 20px transparentize($black, 0.9) + box-shadow: 0 0 25px transparentize($black, 0.85) + border-color: $extra-light-gray + background: $white span.icon opacity: 0 diff --git a/app/views/activities/index.html.haml b/app/views/activities/index.html.haml index be0b7a8b..880a94c8 100644 --- a/app/views/activities/index.html.haml +++ b/app/views/activities/index.html.haml @@ -21,8 +21,6 @@ = @counters[:owner] %label.search %input(type="text" name="search" placeholder="#{ t("activities.search.placeholder") }" value="#{ params[:search] }") - %a.clear(href="#" title="Reset") - = t("activities.search.clear") %button(type="submit") = t("activities.filters.submit") From b14fc322899cbb96f100442015b713a452291428 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Thu, 8 Aug 2013 16:00:44 +0200 Subject: [PATCH 328/499] More style tweaks. --- app/assets/stylesheets/_base.sass | 2 +- app/assets/stylesheets/utils/_forms.sass | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/app/assets/stylesheets/_base.sass b/app/assets/stylesheets/_base.sass index 9b83fa7c..420e0826 100644 --- a/app/assets/stylesheets/_base.sass +++ b/app/assets/stylesheets/_base.sass @@ -75,7 +75,7 @@ article z-index: 1001 !important .picker__input.picker__input--active - border: none + border: 1px solid $blue !important .picker__box border-radius: 0 !important diff --git a/app/assets/stylesheets/utils/_forms.sass b/app/assets/stylesheets/utils/_forms.sass index 5b319a19..b6a8ce8f 100644 --- a/app/assets/stylesheets/utils/_forms.sass +++ b/app/assets/stylesheets/utils/_forms.sass @@ -40,6 +40,7 @@ form z-index: 1000 border: 1px solid $blue box-shadow: 0 0 7px transparentize($black, 0.9) + background: $white &.markdown background-image: none From 3f878fe2d50a6dd2a9671996018d5e6265db3850 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Thu, 8 Aug 2013 16:00:52 +0200 Subject: [PATCH 329/499] Adding missing image. --- app/assets/images/shared/search.png | Bin 0 -> 1441 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 app/assets/images/shared/search.png diff --git a/app/assets/images/shared/search.png b/app/assets/images/shared/search.png new file mode 100644 index 0000000000000000000000000000000000000000..117f2cc7ff4e077c02900f7c1fc98bc7332f23cb GIT binary patch literal 1441 zcmeAS@N?(olHy`uVBq!ia0vp^Mj*_=1|;R|J2nC-$r9IylHmNblJdl&R0hYC{G?O` z&)mfH)S%SFl*+=BsWuD@%qp275hW46K32*3xq68pHF_1f1wh>l3^w)^1&PVosU-?Y zsp*+{wo31J?^jaDOtDo8H}y5}EpSfF$n>ZxN)4{^3rViZPPR-@vbR&PsjvbXkegbP zs8ErclUHn2VXFi-*9yo63F|81#=KlDb#X~hD#E>34K5C;EJ)Q4 zN-fSWElLJPT$(b-ssbzLqSVBa{GyQj{2W*)24v)y-fM)1rW~NxV7&uwFxS1HZS(us{8oC-eS{hoox|z8+n_4;< zni?3v^t$9Hm*%GCmB93-AoLpI)C)=sxdlL*T~doO%TiO^it=+6z@E0s#Nrk+OLI#T z6E`=U=0WwQU~vmruc-lUf9L}pqYsK&q^O1o0n-nN2~W5{4m|0n<^j`t5inu5JiWh% zfq_Zb)5S5Q;?|oPXSH1dCEC*^HI$0|M{Gm)$h*~pSS$J=lQK#yZLYJ zU0UAGZ|Oh*9w!QFzBA=*kIr{%s=C0zY z26(NkJhi8-c2jbl#gFGxjtH+{WM)(ek!j-EaHLE%!}RIOs|-ew?-^$+_Js@YXRWyL z?(o?SvlT9#_%v(s5ABZ}zaq~+_`|u|gYk5)!ePybH5sMLIh9uGoY$GRystjl_AJZ9 z>S-Uv&Uas~yPe(C@%*sBW-FCYnb``PPHhr+5@oSIwz?~Kwt~@7&vomJk9<`L329Wj zCO$FyZc8jn{i6;hEw2MI$Llv}ux~!Fzf5Pwlw+$~3#NayTK6DUVea+*#RqsIlHK0) z8|rk}Px^Oj|8KK-nKeu^mUUdMVA%TNuCB0I*ms}sTL)gMzwDf}mUa8B=dUjA?T`*i zIaTM9x>4BY`P^H_PPy%rEm*4B^IBo%TNeYqA|HW|?NY0&RU+Q7Pw@P?y82t~vajrK oR`zZ1E352QLIt00f7Cm`An8;nRDUM49aLy~y85}Sb4q9e0Ej>jYybcN literal 0 HcmV?d00001 From 9b62d7455b7c75035761accf394e57c233455d29 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Thu, 8 Aug 2013 16:10:42 +0200 Subject: [PATCH 330/499] Even more style tweaks. --- app/assets/stylesheets/_base.sass | 1 - app/assets/stylesheets/partials/_footer.sass | 2 +- app/assets/stylesheets/partials/activities/_filters.sass | 1 - app/assets/stylesheets/utils/_forms.sass | 2 +- 4 files changed, 2 insertions(+), 4 deletions(-) diff --git a/app/assets/stylesheets/_base.sass b/app/assets/stylesheets/_base.sass index 420e0826..5029aa86 100644 --- a/app/assets/stylesheets/_base.sass +++ b/app/assets/stylesheets/_base.sass @@ -42,7 +42,6 @@ a:hover article @extend %clearfix - margin: 0 0 3em 0 header text-align: center diff --git a/app/assets/stylesheets/partials/_footer.sass b/app/assets/stylesheets/partials/_footer.sass index 4c5dc672..92821bd2 100644 --- a/app/assets/stylesheets/partials/_footer.sass +++ b/app/assets/stylesheets/partials/_footer.sass @@ -1,5 +1,5 @@ #footer - padding: 0 0 2em 0 + padding: 2em 0 text-align: center @extend %ornamental-divider-before diff --git a/app/assets/stylesheets/partials/activities/_filters.sass b/app/assets/stylesheets/partials/activities/_filters.sass index 678a6e8f..4a14533c 100644 --- a/app/assets/stylesheets/partials/activities/_filters.sass +++ b/app/assets/stylesheets/partials/activities/_filters.sass @@ -48,7 +48,6 @@ form.filters input background: $white image-url('shared/search.png') no-repeat right center border: 1px solid $light-gray - border-radius: 3px &:focus border-color: $blue diff --git a/app/assets/stylesheets/utils/_forms.sass b/app/assets/stylesheets/utils/_forms.sass index b6a8ce8f..69d2b1f3 100644 --- a/app/assets/stylesheets/utils/_forms.sass +++ b/app/assets/stylesheets/utils/_forms.sass @@ -39,7 +39,7 @@ form &:focus z-index: 1000 border: 1px solid $blue - box-shadow: 0 0 7px transparentize($black, 0.9) + box-shadow: 0 0 10px transparentize($black, 0.85) background: $white &.markdown From 0ddd52d1d249a0fad2bae8b2eebba8d0f8ded1e4 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Thu, 8 Aug 2013 16:45:52 +0200 Subject: [PATCH 331/499] Updating user form stylesheets. --- app/assets/stylesheets/partials/_user.sass | 7 ------- 1 file changed, 7 deletions(-) diff --git a/app/assets/stylesheets/partials/_user.sass b/app/assets/stylesheets/partials/_user.sass index 7275a1fc..8595a2b8 100644 --- a/app/assets/stylesheets/partials/_user.sass +++ b/app/assets/stylesheets/partials/_user.sass @@ -36,13 +36,6 @@ body.user background: $white box-shadow: 0 0 7px transparentize($black, 0.9) - input, textarea - background: $extra-light-gray - border: none - - &:focus - box-shadow: none - .avatar margin: 0 0 2em 0 img From 667be7a749d60d7c592bc9ce8fada16975a160c2 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Thu, 8 Aug 2013 16:47:40 +0200 Subject: [PATCH 332/499] Fixing time issue when end time not on same day. Closes #91. --- app/assets/javascripts/initializers.coffee | 28 +++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/app/assets/javascripts/initializers.coffee b/app/assets/javascripts/initializers.coffee index bc56bb2f..3fa6a10f 100644 --- a/app/assets/javascripts/initializers.coffee +++ b/app/assets/javascripts/initializers.coffee @@ -31,6 +31,7 @@ ready = -> $("#activity_#{target}_3i").val(date) $('#activity_anytime').attr('checked', false) onClose: -> + # TODO: Extract? otherSelector = @$node.data('update') other = $(otherSelector).pickadate('picker') {year, month, date, pick} = @get 'select' @@ -43,6 +44,8 @@ ready = -> other.set 'max', [year, month, date] other.set 'select', [year, month, date] if otherPick > pick + if onSameDay() then setTimePickerLimits() else removeTimePickerLimits() + $('.time-capture').pickatime format: 'HH:i' formatSubmit: 'HH:i' @@ -56,15 +59,34 @@ ready = -> $("#activity_#{target}_5i").val(if minutes < 10 then "0#{minutes}" else minutes) $('#activity_anytime').attr('checked', false) onClose: -> - otherSelector = @$node.data('update') - other = $(otherSelector).pickatime('picker') - {hour, mins, pick} = @get 'select' + if onSameDay() then setTimePickerLimits() else removeTimePickerLimits() + + # run through time pickers and + # adjust the corresponding picker's limits + setTimePickerLimits = -> + $('.time-capture').each -> + picker = $(@).pickatime('picker') + otherSelector = picker.$node.data('update') + other = $(otherSelector).pickatime('picker') + {hour, mins} = picker.get 'select' if otherSelector.match /end-time/ other.set 'min', [hour, mins] else other.set 'max', [hour, mins] + # remove min and max values from time pickers + removeTimePickerLimits = (picker)-> + $('.time-capture').each -> + $(@).pickatime('picker').set 'min': null, 'max': null + + onSameDay = -> + $.unique( + $.map($('.date-capture'), (el)-> + $(el).pickadate('picker').get('select').pick + ) + ).length == 1 + # filters $filters = $('form.filters label:not(.search)') $search = $('form.filters label.search input') From 37b199a630c8f55cc587ec431c5848dbb674ce56 Mon Sep 17 00:00:00 2001 From: pietia Date: Thu, 8 Aug 2013 20:59:42 +0200 Subject: [PATCH 333/499] truncate activity name (max 2 lines) --- app/assets/javascripts/application.js | 1 + app/assets/javascripts/initializers.coffee | 3 +++ app/views/activities/_activity.html.haml | 2 +- vendor/assets/javascripts/jquery.dotdotdot.min.js | 15 +++++++++++++++ 4 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 vendor/assets/javascripts/jquery.dotdotdot.min.js diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index 7d35ec6b..9688a314 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -14,6 +14,7 @@ //= require jquery_ujs //= require jquery.easing.min //= require jquery.progress +//= require jquery.dotdotdot.min //= require pickadate/picker //= require pickadate/picker.date //= require pickadate/picker.time diff --git a/app/assets/javascripts/initializers.coffee b/app/assets/javascripts/initializers.coffee index 3fa6a10f..8ff5d238 100644 --- a/app/assets/javascripts/initializers.coffee +++ b/app/assets/javascripts/initializers.coffee @@ -2,6 +2,9 @@ ready = -> $(document).ajaxError (e, xhr) -> window.location.replace(App.paths.login) if xhr.status == 401 + $('#activities .labels h4').dotdotdot + height: 50 + # hide validation errors on focus $('input.validation-error').on 'focus', -> $(@).next('span.validation-error-message').fadeOut() diff --git a/app/views/activities/_activity.html.haml b/app/views/activities/_activity.html.haml index 55f78eb2..18c3d8fb 100644 --- a/app/views/activities/_activity.html.haml +++ b/app/views/activities/_activity.html.haml @@ -15,7 +15,7 @@ = render "activities/action", activity: activity, type: type, remote: true - %a.details(href="#{activity_path(activity)}" title="#{ t("activities.more_info.title") }") + %a.details(href="#{activity_path(activity)}" title="#{activity.name}") %span = t("activities.more_info.label") - unless type == 'default' diff --git a/vendor/assets/javascripts/jquery.dotdotdot.min.js b/vendor/assets/javascripts/jquery.dotdotdot.min.js new file mode 100644 index 00000000..7b0d17a1 --- /dev/null +++ b/vendor/assets/javascripts/jquery.dotdotdot.min.js @@ -0,0 +1,15 @@ +/* + * jQuery dotdotdot 1.6.1 + * + * Copyright (c) 2013 Fred Heusschen + * www.frebsite.nl + * + * Plugin website: + * dotdotdot.frebsite.nl + * + * Dual licensed under the MIT and GPL licenses. + * http://en.wikipedia.org/wiki/MIT_License + * http://en.wikipedia.org/wiki/GNU_General_Public_License + */ + +!function(a){function c(a,b,c){var d=a.children(),e=!1;a.empty();for(var g=0,h=d.length;h>g;g++){var i=d.eq(g);if(a.append(i),c&&a.append(c),f(a,b)){i.remove(),e=!0;break}c&&c.detach()}return e}function d(b,c,g,h,i){var j=b.contents(),k=!1;b.empty();for(var l="table, thead, tbody, tfoot, tr, col, colgroup, object, embed, param, ol, ul, dl, blockquote, select, optgroup, option, textarea, script, style",m=0,n=j.length;n>m&&!k;m++){var o=j[m],p=a(o);"undefined"!=typeof o&&(b.append(p),i&&b[b.is(l)?"after":"append"](i),3==o.nodeType?f(g,h)&&(k=e(p,c,g,h,i)):k=d(p,c,g,h,i),k||i&&i.detach())}return k}function e(a,b,c,d,h){var k=!1,l=a[0];if("undefined"==typeof l)return!1;for(var m=j(l),n=-1!==m.indexOf(" ")?" ":"\u3000",o="letter"==d.wrap?"":n,p=m.split(o),q=-1,r=-1,s=0,t=p.length-1;t>=s&&(0!=s||0!=t);){var u=Math.floor((s+t)/2);if(u==r)break;r=u,i(l,p.slice(0,r+1).join(o)+d.ellipsis),f(c,d)?t=r:(q=r,s=r),t==s&&0==t&&d.fallbackToLetter&&(o="",p=p[0].split(o),q=-1,r=-1,s=0,t=p.length-1)}if(-1==q||1==p.length&&0==p[0].length){var v=a.parent();a.remove();var w=h?h.length:0;if(v.contents().size()>w){var x=v.contents().eq(-1-w);k=e(x,b,c,d,h)}else{var y=v.prev(),l=y.contents().eq(-1)[0];if("undefined"!=typeof l){var m=g(j(l),d);i(l,m),h&&y.append(h),v.remove(),k=!0}}}else m=g(p.slice(0,q+1).join(o),d),k=!0,i(l,m);return k}function f(a,b){return a.innerHeight()>b.maxHeight}function g(b,c){for(;a.inArray(b.slice(-1),c.lastCharacter.remove)>-1;)b=b.slice(0,-1);return a.inArray(b.slice(-1),c.lastCharacter.noEllipsis)<0&&(b+=c.ellipsis),b}function h(a){return{width:a.innerWidth(),height:a.innerHeight()}}function i(a,b){a.innerText?a.innerText=b:a.nodeValue?a.nodeValue=b:a.textContent&&(a.textContent=b)}function j(a){return a.innerText?a.innerText:a.nodeValue?a.nodeValue:a.textContent?a.textContent:""}function k(b,c){return"undefined"==typeof b?!1:b?"string"==typeof b?(b=a(b,c),b.length?b:!1):"object"==typeof b?"undefined"==typeof b.jquery?!1:b:!1:!1}function l(a){for(var b=a.innerHeight(),c=["paddingTop","paddingBottom"],d=0,e=c.length;e>d;d++){var f=parseInt(a.css(c[d]),10);isNaN(f)&&(f=0),b-=f}return b}function m(a,b){return a?(b="string"==typeof b?"dotdotdot: "+b:["dotdotdot:",b],"undefined"!=typeof window.console&&"undefined"!=typeof window.console.log&&window.console.log(b),!1):!1}if(!a.fn.dotdotdot){a.fn.dotdotdot=function(e){if(0==this.length)return e&&e.debug===!1||m(!0,'No element found for "'+this.selector+'".'),this;if(this.length>1)return this.each(function(){a(this).dotdotdot(e)});var g=this;g.data("dotdotdot")&&g.trigger("destroy.dot"),g.data("dotdotdot-style",g.attr("style")),g.css("word-wrap","break-word"),"nowrap"===g.css("white-space")&&g.css("white-space","normal"),g.bind_events=function(){return g.bind("update.dot",function(b,e){b.preventDefault(),b.stopPropagation(),j.maxHeight="number"==typeof j.height?j.height:l(g),j.maxHeight+=j.tolerance,"undefined"!=typeof e&&(("string"==typeof e||e instanceof HTMLElement)&&(e=a("

").append(e).contents()),e instanceof a&&(i=e)),q=g.wrapInner('
').children(),q.empty().append(i.clone(!0)).css({height:"auto",width:"auto",border:"none",padding:0,margin:0});var h=!1,k=!1;return n.afterElement&&(h=n.afterElement.clone(!0),n.afterElement.remove()),f(q,j)&&(k="children"==j.wrap?c(q,j,h):d(q,g,q,j,h)),q.replaceWith(q.contents()),q=null,a.isFunction(j.callback)&&j.callback.call(g[0],k,i),n.isTruncated=k,k}).bind("isTruncated.dot",function(a,b){return a.preventDefault(),a.stopPropagation(),"function"==typeof b&&b.call(g[0],n.isTruncated),n.isTruncated}).bind("originalContent.dot",function(a,b){return a.preventDefault(),a.stopPropagation(),"function"==typeof b&&b.call(g[0],i),i}).bind("destroy.dot",function(a){a.preventDefault(),a.stopPropagation(),g.unwatch().unbind_events().empty().append(i).attr("style",g.data("dotdotdot-style")).data("dotdotdot",!1)}),g},g.unbind_events=function(){return g.unbind(".dot"),g},g.watch=function(){if(g.unwatch(),"window"==j.watch){var b=a(window),c=b.width(),d=b.height();b.bind("resize.dot"+n.dotId,function(){c==b.width()&&d==b.height()&&j.windowResizeFix||(c=b.width(),d=b.height(),p&&clearInterval(p),p=setTimeout(function(){g.trigger("update.dot")},10))})}else o=h(g),p=setInterval(function(){var a=h(g);(o.width!=a.width||o.height!=a.height)&&(g.trigger("update.dot"),o=h(g))},100);return g},g.unwatch=function(){return a(window).unbind("resize.dot"+n.dotId),p&&clearInterval(p),g};var i=g.contents(),j=a.extend(!0,{},a.fn.dotdotdot.defaults,e),n={},o={},p=null,q=null;return j.lastCharacter.remove instanceof Array||(j.lastCharacter.remove=a.fn.dotdotdot.defaultArrays.lastCharacter.remove),j.lastCharacter.noEllipsis instanceof Array||(j.lastCharacter.noEllipsis=a.fn.dotdotdot.defaultArrays.lastCharacter.noEllipsis),n.afterElement=k(j.after,g),n.isTruncated=!1,n.dotId=b++,g.data("dotdotdot",!0).bind_events().trigger("update.dot"),j.watch&&g.watch(),g},a.fn.dotdotdot.defaults={ellipsis:"... ",wrap:"word",fallbackToLetter:!0,lastCharacter:{},tolerance:0,callback:null,after:null,height:null,watch:!1,windowResizeFix:!0,debug:!1},a.fn.dotdotdot.defaultArrays={lastCharacter:{remove:[" ","\u3000",",",";",".","!","?"],noEllipsis:[]}};var b=1,n=a.fn.html;a.fn.html=function(a){return"undefined"!=typeof a?this.data("dotdotdot")&&"function"!=typeof a?this.trigger("update",[a]):n.call(this,a):n.call(this)};var o=a.fn.text;a.fn.text=function(b){if("undefined"!=typeof b){if(this.data("dotdotdot")){var c=a("
");return c.text(b),b=c.html(),c.remove(),this.trigger("update",[b])}return o.call(this,b)}return o.call(this)}}}(jQuery); \ No newline at end of file From 22fe643e7d9ff3b65102ab58bbb63955ec93499f Mon Sep 17 00:00:00 2001 From: pietia Date: Thu, 8 Aug 2013 21:23:17 +0200 Subject: [PATCH 334/499] validate URL --- app/models/activity.rb | 7 +++++++ config/locales/en.yml | 3 +++ spec/models/activity_spec.rb | 3 +++ 3 files changed, 13 insertions(+) diff --git a/app/models/activity.rb b/app/models/activity.rb index bd4000b0..ecc8a519 100644 --- a/app/models/activity.rb +++ b/app/models/activity.rb @@ -17,6 +17,7 @@ class Activity < ActiveRecord::Base validate :time_frame_order, if: ->{ !anytime && event && start_time.present? && end_time.present? } validate :during_the_event, if: ->{ !anytime && event && start_time.present? && end_time.present? } validates :event, presence: true + validate :image_url_valid, if: ->{ image_url.present? } before_validation :clear_time_frame, if: ->{ anytime } @@ -127,4 +128,10 @@ def during_the_event errors.add(:end_time, I18n.t("activities.errors.end_time.too_late")) if end_time > event.end_time end + def image_url_valid + errors.add(:image_url, I18n.t("activities.errors.image_url.protocol_not_supported")) unless URI.parse(image_url).kind_of?(URI::HTTP) + rescue URI::InvalidURIError + errors.add(:image_url, I18n.t("activities.errors.image_url.invalid")) + end + end diff --git a/config/locales/en.yml b/config/locales/en.yml index 3903d1f3..d7d891ec 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -93,6 +93,9 @@ en: end_time: before_start: "can't be before start time" too_late: "outside range, too late" + image_url: + invalid: "not valid URL" + not_supported_protocol: "protocol not supported ;)" new_activity: title: Organize an activity diff --git a/spec/models/activity_spec.rb b/spec/models/activity_spec.rb index 859c7a85..a2e5df8b 100644 --- a/spec/models/activity_spec.rb +++ b/spec/models/activity_spec.rb @@ -185,6 +185,9 @@ it { should accept_values_for(:limit_of_participants, nil, 12, 100) } it { should_not accept_values_for(:limit_of_participants, -1, 0) } + it { should accept_values_for(:image_url, nil, "http://com.com/image.gif", "https://com.com/image.gif") } + it { should_not accept_values_for(:image_url, "http://com.co ge.gif", "ssh://com.com/image.gif", "blah")} + context "invalid time frame (wrong order)" do subject { FactoryGirl.build(:activity, start_time: 10.days.ago.to_time, anytime: false) } From 492b5afa36851141774a16b03ddac97a6fd86273 Mon Sep 17 00:00:00 2001 From: pietia Date: Fri, 9 Aug 2013 00:06:41 +0200 Subject: [PATCH 335/499] let's try 55 --- app/assets/javascripts/initializers.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/javascripts/initializers.coffee b/app/assets/javascripts/initializers.coffee index 8ff5d238..bf21fb3f 100644 --- a/app/assets/javascripts/initializers.coffee +++ b/app/assets/javascripts/initializers.coffee @@ -3,7 +3,7 @@ ready = -> window.location.replace(App.paths.login) if xhr.status == 401 $('#activities .labels h4').dotdotdot - height: 50 + height: 55 # hide validation errors on focus $('input.validation-error').on 'focus', -> From ec70d071c7ee02fe32d3254df05f519a4938f266 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Fri, 9 Aug 2013 08:34:42 +0200 Subject: [PATCH 336/499] Adding map. --- .../stylesheets/partials/activities/_index.sass | 13 +++++++++++++ app/views/activities/index.html.haml | 4 ++++ 2 files changed, 17 insertions(+) diff --git a/app/assets/stylesheets/partials/activities/_index.sass b/app/assets/stylesheets/partials/activities/_index.sass index 08f57eb1..adcfb830 100644 --- a/app/assets/stylesheets/partials/activities/_index.sass +++ b/app/assets/stylesheets/partials/activities/_index.sass @@ -2,6 +2,19 @@ a#new-activity float: right @extend %icon-button-add +.map + margin: 6em 0 0 + + h3 + +custom-sans(light) + text-align: center + @extend %ornamental-divider-after + + iframe + margin: 2em 0 0 + height: 600px + border: none + #activities clear: both @extend %clearfix diff --git a/app/views/activities/index.html.haml b/app/views/activities/index.html.haml index 880a94c8..3a8f31b2 100644 --- a/app/views/activities/index.html.haml +++ b/app/views/activities/index.html.haml @@ -30,3 +30,7 @@ %ul#activities = render @activities.decorate + +.map + %h3 The eurucamp week + %iframe(src="http://mapsengine.google.com/map/u/0/embed?mid=zddEY5tddvgw.kv-7qKkgMPrg" width="100%" height="100%") From 8b2bdeb4b365a5742a35f2ecd787f5e7f30f25af Mon Sep 17 00:00:00 2001 From: Polarblau Date: Fri, 9 Aug 2013 08:41:55 +0200 Subject: [PATCH 337/499] Filter by title. --- app/assets/javascripts/initializers.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/javascripts/initializers.coffee b/app/assets/javascripts/initializers.coffee index bf21fb3f..c0d37bce 100644 --- a/app/assets/javascripts/initializers.coffee +++ b/app/assets/javascripts/initializers.coffee @@ -111,7 +111,7 @@ ready = -> if query = $search.val() $activities .filter(':visible') - .filter(-> !(new RegExp(query, 'i')).test $(@).find('h4').text()) + .filter(-> !(new RegExp(query, 'i')).test $(@).find('a.details').attr('title')) .hide() $filters.on 'click', (e)-> From 13f4424fd0592ea32645f3e7d990921da510ace5 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Fri, 9 Aug 2013 08:59:42 +0200 Subject: [PATCH 338/499] Adding info about week bar and map for owner. --- app/assets/stylesheets/partials/activities/_show.sass | 7 +++++++ app/views/activities/show.html.haml | 9 +++++++++ 2 files changed, 16 insertions(+) diff --git a/app/assets/stylesheets/partials/activities/_show.sass b/app/assets/stylesheets/partials/activities/_show.sass index 63c43dfc..d882cbd4 100644 --- a/app/assets/stylesheets/partials/activities/_show.sass +++ b/app/assets/stylesheets/partials/activities/_show.sass @@ -1,6 +1,13 @@ $types: participant ok remove, owner edit edit #activity + header + p.info + background: $extra-light-gray + border: 1px solid $light-gray + padding: 1em + +custom-sans(medium) + .action position: absolute right: 0 diff --git a/app/views/activities/show.html.haml b/app/views/activities/show.html.haml index 80b940f2..62de9a13 100644 --- a/app/views/activities/show.html.haml +++ b/app/views/activities/show.html.haml @@ -7,6 +7,15 @@ = render "action", activity: @activity, type: @activity.relation_ship_with(current_user), remote: false + - if @activity.relation_ship_with(current_user) == 'owner' + %p.info + Please consider adding this activity to our + = succeed "." do + %a(href="https://mapsengine.google.com/map/edit?mid=zddEY5tddvgw.kv-7qKkgMPrg" title="Edit the map") eurucamp week map + %br + If you’re running a website related to this activity show some eurucamp pride with the + %a(href="https://gist.github.com/polarblau/6172669" title="Get the bar") eurucamp week browser bar! + %section.description .wrapper = @activity.description_markdown From 6d74a535fde09557beb464683dcd0435041f480f Mon Sep 17 00:00:00 2001 From: Polarblau Date: Fri, 9 Aug 2013 09:03:13 +0200 Subject: [PATCH 339/499] Formatting. --- app/views/activities/show.html.haml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/views/activities/show.html.haml b/app/views/activities/show.html.haml index 62de9a13..3d1cf9c7 100644 --- a/app/views/activities/show.html.haml +++ b/app/views/activities/show.html.haml @@ -14,7 +14,8 @@ %a(href="https://mapsengine.google.com/map/edit?mid=zddEY5tddvgw.kv-7qKkgMPrg" title="Edit the map") eurucamp week map %br If you’re running a website related to this activity show some eurucamp pride with the - %a(href="https://gist.github.com/polarblau/6172669" title="Get the bar") eurucamp week browser bar! + = succeed "!" do + %a(href="https://gist.github.com/polarblau/6172669" title="Get the bar") eurucamp week browser bar %section.description .wrapper From b38431e46783f9332cef742153f6915bd6915eb0 Mon Sep 17 00:00:00 2001 From: pietia Date: Fri, 9 Aug 2013 20:07:00 +0200 Subject: [PATCH 340/499] should fix #96 --- app/models/activity.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/activity.rb b/app/models/activity.rb index ecc8a519..500c65d5 100644 --- a/app/models/activity.rb +++ b/app/models/activity.rb @@ -47,7 +47,7 @@ def participated_by(user) end def order_by_start_time - order("anytime DESC, start_time ASC") + order("start_time ASC, name ASC") end private From c87695d59afeb4d46db9ec959850637638066650 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Sat, 10 Aug 2013 17:45:41 +0200 Subject: [PATCH 341/499] Fixing requirements display. --- app/decorators/activity_decorator.rb | 2 +- app/views/activities/show.html.haml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/decorators/activity_decorator.rb b/app/decorators/activity_decorator.rb index 22301c40..7d63852a 100644 --- a/app/decorators/activity_decorator.rb +++ b/app/decorators/activity_decorator.rb @@ -24,7 +24,7 @@ def status end def description_markdown - object.description ? markdown(object.description) : '' + object.description ? markdown(object.description) : 'No description added.'.html_safe end def requirements_markdown diff --git a/app/views/activities/show.html.haml b/app/views/activities/show.html.haml index 3d1cf9c7..0dfd4dbe 100644 --- a/app/views/activities/show.html.haml +++ b/app/views/activities/show.html.haml @@ -21,7 +21,7 @@ .wrapper = @activity.description_markdown - - unless @activity.requirements.empty? + - unless @activity.requirements_markdown.empty? %h4 = t("activities.required") = @activity.requirements_markdown From 95a1a2e64b16ea1ee893444828f014e123d1c6e1 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Sat, 10 Aug 2013 19:31:21 +0200 Subject: [PATCH 342/499] Don't show actions when full. --- .../partials/activities/_index.sass | 8 ++++- app/models/activity.rb | 4 +++ app/views/activities/_action.html.haml | 9 +++--- app/views/activities/_activity.html.haml | 2 +- spec/models/activity_spec.rb | 32 +++++++++++++++++++ 5 files changed, 49 insertions(+), 6 deletions(-) diff --git a/app/assets/stylesheets/partials/activities/_index.sass b/app/assets/stylesheets/partials/activities/_index.sass index adcfb830..500664e8 100644 --- a/app/assets/stylesheets/partials/activities/_index.sass +++ b/app/assets/stylesheets/partials/activities/_index.sass @@ -171,7 +171,13 @@ a#new-activity @extend %transition-all-15s-ease-out +transform(translate(0, -9em)) opacity: 0 - + + &.full + .progress-wrapper, + .container > .progress + +transform(translate(0, 0)) + opacity: 0.5 + .labels h4 @extend %transition-all-15s-ease-out diff --git a/app/models/activity.rb b/app/models/activity.rb index 500c65d5..38373dea 100644 --- a/app/models/activity.rb +++ b/app/models/activity.rb @@ -97,6 +97,10 @@ def today? def upcoming? start_time > Time.now.end_of_day end + + def full? + participations_count >= limit_of_participants + end def new_participation(user) participation_source.call.tap do |participation| diff --git a/app/views/activities/_action.html.haml b/app/views/activities/_action.html.haml index db06414d..21c48fac 100644 --- a/app/views/activities/_action.html.haml +++ b/app/views/activities/_action.html.haml @@ -9,7 +9,8 @@ = button_to activity_participation_path(activity), method: :delete, remote: remote, title: t("activities.leave.title") do %span = t("activities.leave.label") - - else - = button_to activity_participation_path(activity), method: :post, remote: remote, title: t("activities.join.title") do - %span - = t("activities.join.label") + - else + - unless activity.full? + = button_to activity_participation_path(activity), method: :post, remote: remote, title: t("activities.join.title") do + %span + = t("activities.join.label") diff --git a/app/views/activities/_activity.html.haml b/app/views/activities/_activity.html.haml index 18c3d8fb..d1368ca5 100644 --- a/app/views/activities/_activity.html.haml +++ b/app/views/activities/_activity.html.haml @@ -1,6 +1,6 @@ - type = activity.relation_ship_with(current_user) -%li(id="activity-#{activity.id}" class="#{type} #{activity.status}") +%li(id="activity-#{activity.id}" class="#{type} #{activity.status} #{"full" if activity.full?}") .container = render "activities/progress", activity: activity diff --git a/spec/models/activity_spec.rb b/spec/models/activity_spec.rb index a2e5df8b..52beed4b 100644 --- a/spec/models/activity_spec.rb +++ b/spec/models/activity_spec.rb @@ -128,6 +128,38 @@ end end + + describe "#full?" do + subject { activity.full? } + + context "full" do + before do + activity.stub!(:limit_of_participants).and_return(10) + activity.stub!(:participations_count).and_return(10) + end + + it { should == true } + end + + context "too full" do + before do + activity.stub!(:limit_of_participants).and_return(10) + activity.stub!(:participations_count).and_return(11) + end + + it { should == true } + end + + context "not full" do + before do + activity.stub!(:limit_of_participants).and_return(10) + activity.stub!(:participations_count).and_return(8) + end + + it { should == false } + end + + end describe "#upcoming?" do subject { activity.upcoming? } From 18f244e691c737e3d4e6ce297095aa7b4183d9d4 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Sat, 10 Aug 2013 20:09:32 +0200 Subject: [PATCH 343/499] Always show all participants. --- app/assets/javascripts/initializers.coffee | 6 --- .../partials/activities/_index.sass | 1 + .../partials/activities/_show.sass | 44 +++++++++---------- app/views/activities/show.html.haml | 20 +++------ 4 files changed, 30 insertions(+), 41 deletions(-) diff --git a/app/assets/javascripts/initializers.coffee b/app/assets/javascripts/initializers.coffee index c0d37bce..17a7065c 100644 --- a/app/assets/javascripts/initializers.coffee +++ b/app/assets/javascripts/initializers.coffee @@ -13,12 +13,6 @@ ready = -> $('#activities .progress').progress() $('#activity .progress').progress(strokeWidth: 12) - # show participants list - $('a.participants').on 'click', (e)-> - e.preventDefault() - $(@).hide() - $('section.participants').show() - $('.date-capture').pickadate min: new Date(App.event.startTime) max: new Date(App.event.endTime) diff --git a/app/assets/stylesheets/partials/activities/_index.sass b/app/assets/stylesheets/partials/activities/_index.sass index 500664e8..d728f9d1 100644 --- a/app/assets/stylesheets/partials/activities/_index.sass +++ b/app/assets/stylesheets/partials/activities/_index.sass @@ -175,6 +175,7 @@ a#new-activity &.full .progress-wrapper, .container > .progress + @extend %transition-all-15s-ease-out +transform(translate(0, 0)) opacity: 0.5 diff --git a/app/assets/stylesheets/partials/activities/_show.sass b/app/assets/stylesheets/partials/activities/_show.sass index d882cbd4..c8dcac7c 100644 --- a/app/assets/stylesheets/partials/activities/_show.sass +++ b/app/assets/stylesheets/partials/activities/_show.sass @@ -60,7 +60,28 @@ $types: participant ok remove, owner edit edit a @extend %secondary-button margin: 1em 0 - + + .participants + width: 100% + padding: 2em 0 + clear: both + + h3 + +custom-sans(light) + text-align: center + @extend %ornamental-divider-after + + ul + @extend %unlisted + text-align: center + + li + padding: 0 5px + +inline-block + + img + border-radius: 24px + width: 48px &.details dl.wrapper margin-left: 50px @@ -83,25 +104,4 @@ $types: participant ok remove, owner edit edit +custom-serif margin: 0 - &.participants - width: 100% - padding: 2em 0 - clear: both - display: none - - h3 - +custom-sans(light) - text-align: center - @extend %ornamental-divider-after - - ul - @extend %unlisted - text-align: center - - li - padding: 0 5px - +inline-block - img - border-radius: 24px - width: 48px diff --git a/app/views/activities/show.html.haml b/app/views/activities/show.html.haml index 0dfd4dbe..33bf3861 100644 --- a/app/views/activities/show.html.haml +++ b/app/views/activities/show.html.haml @@ -31,10 +31,13 @@ %p = @activity.room_left - if @activity.participants.any? - %p - %a.participants(href="#" title="") - %span - = t("activities.participants") + .participants + %h3 + = t("activities.participants") + %ul + - @activity.participants.each do |participant| + %li(title="#{ participant.name }")> + = image_tag(avatar_url(participant, 48)) %section.details %dl.wrapper @@ -56,12 +59,3 @@ = t("activities.location") %dd = @activity.location - - - if @activity.participants.any? - %section.participants - %h3 - = t("activities.participants") - %ul - - @activity.participants.each do |participant| - %li(title="#{ participant.name }")> - = image_tag(avatar_url(participant, 48)) From cae774318b7171f0e8b94b3ce154e4bc494d69a9 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Sun, 11 Aug 2013 22:52:54 +0200 Subject: [PATCH 344/499] Scoping color. --- vendor/assets/javascripts/jquery.progress.coffee | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/vendor/assets/javascripts/jquery.progress.coffee b/vendor/assets/javascripts/jquery.progress.coffee index 13e0abfc..702a1ddc 100644 --- a/vendor/assets/javascripts/jquery.progress.coffee +++ b/vendor/assets/javascripts/jquery.progress.coffee @@ -19,8 +19,6 @@ $.fn.extend easing : 'easeOutBounce' settings = $.extend settings, options - # we don't want to pass these around with every call to .render - color = settings.strokeColor init = -> $el = $(@) @@ -31,7 +29,7 @@ $.fn.extend progress = settings.progress || parseInt($el.data('progress')) # when the value > 100 use different color - color = settings.strokeColorFull if progress >= 100 + color = if progress >= 100 then settings.strokeColorFull else settings.strokeColor # wrap image $wrapper = $('
', class: 'progress-wrapper').css From 1415fa71154c41a30f59496045ba465cf6d311e7 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Sun, 11 Aug 2013 23:12:16 +0200 Subject: [PATCH 345/499] Passing color. --- vendor/assets/javascripts/jquery.progress.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vendor/assets/javascripts/jquery.progress.coffee b/vendor/assets/javascripts/jquery.progress.coffee index 702a1ddc..bd4efa8b 100644 --- a/vendor/assets/javascripts/jquery.progress.coffee +++ b/vendor/assets/javascripts/jquery.progress.coffee @@ -55,13 +55,13 @@ $.fn.extend duration : settings.duration queue : false easing : settings.easing - step : (percentage) -> render(percentage, context) + step : (percentage) -> render(percentage, context, color) }) percentageToRadians = (percentage) -> (1.5 - (percentage * 2 / 100)) * Math.PI - render = (percentage, context) -> + render = (percentage, context, color) -> canvasSize = context.canvas.width context.clearRect(0, 0, canvasSize, canvasSize) context.beginPath() From f2197ca6dc683f87aefcadb5db007c79236042b6 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Fri, 9 Aug 2013 22:28:10 +0200 Subject: [PATCH 346/499] Responsive index page. Relates to #90, #85, #71. --- app/assets/stylesheets/partials/_layout.sass | 5 +-- .../partials/activities/_filters.sass | 32 ++++++++++++------- .../partials/activities/_index.sass | 22 +++++++++++-- app/views/partials/_layout_head.html.haml | 2 +- 4 files changed, 43 insertions(+), 18 deletions(-) diff --git a/app/assets/stylesheets/partials/_layout.sass b/app/assets/stylesheets/partials/_layout.sass index 3c0473ee..962bae43 100644 --- a/app/assets/stylesheets/partials/_layout.sass +++ b/app/assets/stylesheets/partials/_layout.sass @@ -1,7 +1,4 @@ #container - // locking width - width: $column * 5 - margin: 0 auto - //+outer-container + +outer-container padding: 0 $padding position: relative diff --git a/app/assets/stylesheets/partials/activities/_filters.sass b/app/assets/stylesheets/partials/activities/_filters.sass index 4a14533c..dcf75f25 100644 --- a/app/assets/stylesheets/partials/activities/_filters.sass +++ b/app/assets/stylesheets/partials/activities/_filters.sass @@ -2,8 +2,7 @@ form.filters @extend %clearfix border-radius: 3px overflow: hidden - float: left - padding: 0 0 4.5em 0 + padding: 0 0 2.5em 0 label background: lighten($extra-light-gray, 5) @@ -14,6 +13,8 @@ form.filters border: none display: block padding: 0.75em 1em + margin: 0 0 1px 0 + text-align: left +transition(all 0.5s) &.participant, @@ -31,15 +32,16 @@ form.filters &.participant:before color: $green - &.owner:before - color: $yellow + &.owner + border-radius: 0 0 3px 3px + + &:before + color: $yellow &.search background: none - position: absolute - top: 4em - left: 0 padding: 0 + margin-top: 1em width: 100% &:hover @@ -97,23 +99,31 @@ form.filters display: none - +media($tablet-lite) + +media($tablet-lite-plus) border-radius: none overflow: visible + float: left + padding: 0 0 4.5em 0 label display: inline-block - margin-right: 1px + margin: 0 1px 0 0 strong float: none + &:first-child + border-radius: 3px 0 0 3px + &.owner border-radius: 0 3px 3px 0 margin: 0 - &:first-child - border-radius: 3px 0 0 3px + &.search + position: absolute + top: 4em + margin-top: 0 + left: 0 button margin: 0 0 0 0.5em diff --git a/app/assets/stylesheets/partials/activities/_index.sass b/app/assets/stylesheets/partials/activities/_index.sass index d728f9d1..58775dfe 100644 --- a/app/assets/stylesheets/partials/activities/_index.sass +++ b/app/assets/stylesheets/partials/activities/_index.sass @@ -12,9 +12,13 @@ a#new-activity iframe margin: 2em 0 0 - height: 600px + height: 400px border: none + +media($tablet-lite) + iframe + height: 600px + #activities clear: both @extend %clearfix @@ -26,7 +30,6 @@ a#new-activity li display: block - +span-columns(2) margin: 0 0 2px 0 text-align: center @@ -199,3 +202,18 @@ a#new-activity opacity: 1 +transform(scale(1)) +transition(all 0.15s ease-out 0.15s) + + + +media($smartphone-portrait) + li + +span-columns(2) + + + +media($tablet-lite) + li + +span-columns(2) + + +media($tablet-portrait-plus) + li + +span-columns(2) + diff --git a/app/views/partials/_layout_head.html.haml b/app/views/partials/_layout_head.html.haml index 19e26e69..0bdae6e9 100644 --- a/app/views/partials/_layout_head.html.haml +++ b/app/views/partials/_layout_head.html.haml @@ -3,7 +3,7 @@ %title= title(Settings.seo.title) %meta{content: Settings.seo.description, name: "description"} %meta{content: Settings.seo.author, name: "author"} -//%meta(content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0" name="viewport") +%meta(content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0" name="viewport") = csrf_meta_tags From 4205aefb61726fee62d0ce00e91f2dd405778123 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Mon, 12 Aug 2013 18:29:34 +0200 Subject: [PATCH 347/499] Simple responsive version for show. --- .../partials/activities/_show.sass | 55 ++++++++++++++----- app/views/activities/show.html.haml | 18 +++--- 2 files changed, 49 insertions(+), 24 deletions(-) diff --git a/app/assets/stylesheets/partials/activities/_show.sass b/app/assets/stylesheets/partials/activities/_show.sass index c8dcac7c..a8f7579e 100644 --- a/app/assets/stylesheets/partials/activities/_show.sass +++ b/app/assets/stylesheets/partials/activities/_show.sass @@ -23,13 +23,12 @@ $types: participant ok remove, owner edit edit @extend %icon-button-#{nth($type, 3)} section - width: percentage(1/3) - float: left position: relative &.description +custom-serif line-height: 1.35em + margin-bottom: 2em h1, h2, h3, h4, h5, h6 +custom-sans(bold) @@ -37,21 +36,13 @@ $types: participant ok remove, owner edit edit margin: 1.5em 0 0 font-size: 1em - .wrapper - margin-right: 50px - &.overview text-align: center + margin-bottom: 2em - .progress-wrapper - width: 274px - height: 274px - padding: 12px - + .progress-wrapper, .progress - width: 250px - height: 250px - border-radius: 125px + display: none p margin: 1em 0 0 @@ -60,7 +51,7 @@ $types: participant ok remove, owner edit edit a @extend %secondary-button margin: 1em 0 - + .participants width: 100% padding: 2em 0 @@ -82,9 +73,9 @@ $types: participant ok remove, owner edit edit img border-radius: 24px width: 48px + &.details dl.wrapper - margin-left: 50px @extend %clearfix border-bottom: 1px solid $light-gray @@ -104,4 +95,38 @@ $types: participant ok remove, owner edit edit +custom-serif margin: 0 + +media($smartphone-portrait) + section + &.overview + .progress-wrapper + width: 274px + height: 274px + padding: 12px + display: block + + .progress + width: 250px + height: 250px + display: block + border-radius: 125px + + +media($tablet-portrait) + section + width: percentage(1/3) + + &.overview + position: absolute + left: percentage(1/3) + + &.description + float: left + + .wrapper + margin-right: 50px + + &.details + float: right + dl.wrapper + margin-left: 50px +>>>>>>> Stashed changes diff --git a/app/views/activities/show.html.haml b/app/views/activities/show.html.haml index 33bf3861..165f8969 100644 --- a/app/views/activities/show.html.haml +++ b/app/views/activities/show.html.haml @@ -17,15 +17,6 @@ = succeed "!" do %a(href="https://gist.github.com/polarblau/6172669" title="Get the bar") eurucamp week browser bar - %section.description - .wrapper - = @activity.description_markdown - - - unless @activity.requirements_markdown.empty? - %h4 - = t("activities.required") - = @activity.requirements_markdown - %section.overview = render "activities/progress", activity: @activity %p @@ -39,6 +30,15 @@ %li(title="#{ participant.name }")> = image_tag(avatar_url(participant, 48)) + %section.description + .wrapper + = @activity.description_markdown + + - unless @activity.requirements_markdown.empty? + %h4 + = t("activities.required") + = @activity.requirements_markdown + %section.details %dl.wrapper - if @activity.anytime? From 9d9ef892820139cdb25c14c07004331c87504b72 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Thu, 15 Aug 2013 00:22:20 +0200 Subject: [PATCH 348/499] Quickfix for the counter issue. #105. --- app/models/event.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/models/event.rb b/app/models/event.rb index ecf506ba..f557f8e5 100644 --- a/app/models/event.rb +++ b/app/models/event.rb @@ -26,8 +26,8 @@ def counters(user) { today: Activity.today.count, all: Activity.count, - participant: Activity.participated_by(user).count, - owner: Activity.created_by(user).count + participant: user.nil? ? 0 : Activity.participated_by(user).count, + owner: user.nil? ? 0 : Activity.created_by(user).count } end @@ -82,4 +82,4 @@ def activity_source @activity_source ||= Activity.public_method(:new) end -end \ No newline at end of file +end From 12fef0e89d3114514570634cd06b43f91cb350e2 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Thu, 15 Aug 2013 00:37:04 +0200 Subject: [PATCH 349/499] Fixing today filter and moving css classes into decorator. --- app/decorators/activity_decorator.rb | 7 +++++++ app/views/activities/_activity.html.haml | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/app/decorators/activity_decorator.rb b/app/decorators/activity_decorator.rb index 7d63852a..e15befc8 100644 --- a/app/decorators/activity_decorator.rb +++ b/app/decorators/activity_decorator.rb @@ -1,6 +1,13 @@ class ActivityDecorator < Draper::Decorator delegate_all + def css_classes + classes = [relation_ship_with(h.current_user)] + classes << "today" if object.today? + classes << "full" if object.full? + classes.join(" ") + end + def creator_name if creator creator.name.blank? ? creator.email : creator.name diff --git a/app/views/activities/_activity.html.haml b/app/views/activities/_activity.html.haml index d1368ca5..a67edff5 100644 --- a/app/views/activities/_activity.html.haml +++ b/app/views/activities/_activity.html.haml @@ -1,6 +1,6 @@ - type = activity.relation_ship_with(current_user) -%li(id="activity-#{activity.id}" class="#{type} #{activity.status} #{"full" if activity.full?}") +%li(id="activity-#{activity.id}" class="#{activity.css_classes}") .container = render "activities/progress", activity: activity From 58b77bf10e7194480c30a77ed52f8059c21259cd Mon Sep 17 00:00:00 2001 From: Polarblau Date: Thu, 15 Aug 2013 11:41:31 +0200 Subject: [PATCH 350/499] Fixing floating issue. --- app/assets/stylesheets/partials/activities/_show.sass | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/assets/stylesheets/partials/activities/_show.sass b/app/assets/stylesheets/partials/activities/_show.sass index a8f7579e..69086edc 100644 --- a/app/assets/stylesheets/partials/activities/_show.sass +++ b/app/assets/stylesheets/partials/activities/_show.sass @@ -115,11 +115,12 @@ $types: participant ok remove, owner edit edit width: percentage(1/3) &.overview - position: absolute + float: left left: percentage(1/3) &.description float: left + left: - percentage(1/3) .wrapper margin-right: 50px @@ -129,4 +130,3 @@ $types: participant ok remove, owner edit edit dl.wrapper margin-left: 50px ->>>>>>> Stashed changes From c4fdd13f8e5f7f4b1b8b921ff2180c257f492f42 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Thu, 15 Aug 2013 11:43:43 +0200 Subject: [PATCH 351/499] Disable hover only if not joined yet. --- app/assets/stylesheets/partials/activities/_index.sass | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/assets/stylesheets/partials/activities/_index.sass b/app/assets/stylesheets/partials/activities/_index.sass index 58775dfe..7b275b14 100644 --- a/app/assets/stylesheets/partials/activities/_index.sass +++ b/app/assets/stylesheets/partials/activities/_index.sass @@ -174,14 +174,14 @@ a#new-activity @extend %transition-all-15s-ease-out +transform(translate(0, -9em)) opacity: 0 - - &.full + + &.full.default .progress-wrapper, .container > .progress @extend %transition-all-15s-ease-out +transform(translate(0, 0)) opacity: 0.5 - + .labels h4 @extend %transition-all-15s-ease-out From ae894e45c14ea7428d2ca8acddc031fa40f5ebd7 Mon Sep 17 00:00:00 2001 From: pietia Date: Fri, 16 Aug 2013 12:38:02 +0200 Subject: [PATCH 352/499] ignore db dump --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index d037bedb..f099387d 100644 --- a/.gitignore +++ b/.gitignore @@ -17,4 +17,6 @@ pickle-email-*.html config/database.yml .env +latest.dump +download_db.sh From 1d18e353a90d559e50025387d7bf8e028a9ba55b Mon Sep 17 00:00:00 2001 From: pietia Date: Fri, 16 Aug 2013 12:58:06 +0200 Subject: [PATCH 353/499] fix 'today' tab --- app/models/activity.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/activity.rb b/app/models/activity.rb index 38373dea..5e9506f6 100644 --- a/app/models/activity.rb +++ b/app/models/activity.rb @@ -63,7 +63,7 @@ def find_recent(limit) end def find_today - where(":t between start_time and end_time", t: Date.current).order_by_start_time + where("start_time <= :t2 AND end_time >= :t1", t1: Date.current.beginning_of_day, t2: Date.current.end_of_day).order_by_start_time end def find_with_name_like(name) From 52b1a5e0ea9b3b5d4cc0989d6ba51ce4f0c664ba Mon Sep 17 00:00:00 2001 From: pietia Date: Fri, 16 Aug 2013 13:31:17 +0200 Subject: [PATCH 354/499] today's events first --- app/models/activity.rb | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/app/models/activity.rb b/app/models/activity.rb index 5e9506f6..70afe2e9 100644 --- a/app/models/activity.rb +++ b/app/models/activity.rb @@ -47,7 +47,18 @@ def participated_by(user) end def order_by_start_time - order("start_time ASC, name ASC") + t = Date.current.beginning_of_day + custom_order=< Date: Fri, 16 Aug 2013 13:38:30 +0200 Subject: [PATCH 355/499] fix order --- app/models/activity.rb | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/app/models/activity.rb b/app/models/activity.rb index 70afe2e9..e619602f 100644 --- a/app/models/activity.rb +++ b/app/models/activity.rb @@ -52,8 +52,8 @@ def order_by_start_time *, ( CASE - WHEN end_time <= '#{t}' THEN 10 - WHEN anytime=true THEN 2 + WHEN end_time < '#{t}' THEN 10 + WHEN anytime=true THEN 2 ELSE 1 END ) as CUSTOM_ORDER @@ -74,7 +74,7 @@ def find_recent(limit) end def find_today - where("start_time <= :t2 AND end_time >= :t1", t1: Date.current.beginning_of_day, t2: Date.current.end_of_day).order_by_start_time + where("start_time <= :t2 AND end_time >= :t1 OR anytime=true", t1: Date.current.beginning_of_day, t2: Date.current.end_of_day).order_by_start_time end def find_with_name_like(name) @@ -101,14 +101,13 @@ def anybody_can_join? def today? return true if anytime? - t = Time.now.end_of_day - t > start_time && t < end_time + start_time <= Date.current.end_of_day && end_time >= Date.current.beginning_of_day end def upcoming? start_time > Time.now.end_of_day end - + def full? participations_count >= limit_of_participants end From 1456d29485428777a8a323b4818adab86ccd26d3 Mon Sep 17 00:00:00 2001 From: pietia Date: Fri, 16 Aug 2013 13:42:14 +0200 Subject: [PATCH 356/499] add 'past' class for past events --- app/decorators/activity_decorator.rb | 3 +++ app/models/activity.rb | 5 +++++ 2 files changed, 8 insertions(+) diff --git a/app/decorators/activity_decorator.rb b/app/decorators/activity_decorator.rb index e15befc8..3952aa9c 100644 --- a/app/decorators/activity_decorator.rb +++ b/app/decorators/activity_decorator.rb @@ -4,7 +4,9 @@ class ActivityDecorator < Draper::Decorator def css_classes classes = [relation_ship_with(h.current_user)] classes << "today" if object.today? + classes << "past" if object.in_past? classes << "full" if object.full? + classes << "anytime" if object.anytime? classes.join(" ") end @@ -25,6 +27,7 @@ def relation_ship_with(user) def status if object.anytime? then "" elsif object.today? then "today" + elsif object.in_past? then "past" elsif object.upcoming? then "upcoming" else "archive" end diff --git a/app/models/activity.rb b/app/models/activity.rb index e619602f..c5f3de8f 100644 --- a/app/models/activity.rb +++ b/app/models/activity.rb @@ -108,6 +108,11 @@ def upcoming? start_time > Time.now.end_of_day end + def in_past? + return false if anytime? + end_time < Date.current.beginning_of_day + end + def full? participations_count >= limit_of_participants end From 1327ce30940ea26f325a63e72500cef3806fb63c Mon Sep 17 00:00:00 2001 From: pietia Date: Fri, 16 Aug 2013 14:42:17 +0200 Subject: [PATCH 357/499] hide actions for old activities --- app/views/activities/_action.html.haml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/app/views/activities/_action.html.haml b/app/views/activities/_action.html.haml index 21c48fac..4a0e0468 100644 --- a/app/views/activities/_action.html.haml +++ b/app/views/activities/_action.html.haml @@ -6,11 +6,12 @@ %span = t("activities.edit.label") - when "participant" - = button_to activity_participation_path(activity), method: :delete, remote: remote, title: t("activities.leave.title") do - %span - = t("activities.leave.label") + - unless activity.in_past? + = button_to activity_participation_path(activity), method: :delete, remote: remote, title: t("activities.leave.title") do + %span + = t("activities.leave.label") - else - - unless activity.full? + - unless activity.full? || activity.in_past? = button_to activity_participation_path(activity), method: :post, remote: remote, title: t("activities.join.title") do %span = t("activities.join.label") From eaf61eb6f8d4b68c5a8f4d9b860f0cd7985bf2cb Mon Sep 17 00:00:00 2001 From: pietia Date: Fri, 16 Aug 2013 16:12:32 +0200 Subject: [PATCH 358/499] fix another corner case --- app/models/activity.rb | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/app/models/activity.rb b/app/models/activity.rb index c5f3de8f..95c07c25 100644 --- a/app/models/activity.rb +++ b/app/models/activity.rb @@ -52,7 +52,7 @@ def order_by_start_time *, ( CASE - WHEN end_time < '#{t}' THEN 10 + WHEN end_time <= '#{t}' THEN 10 WHEN anytime=true THEN 2 ELSE 1 END @@ -74,7 +74,7 @@ def find_recent(limit) end def find_today - where("start_time <= :t2 AND end_time >= :t1 OR anytime=true", t1: Date.current.beginning_of_day, t2: Date.current.end_of_day).order_by_start_time + where("(NOT(start_time <= :t1 AND end_time = :t1 ) AND (start_time <= :t2 AND end_time >= :t1)) OR anytime=true", t1: Date.current.beginning_of_day, t2: Date.current.end_of_day).order_by_start_time end def find_with_name_like(name) @@ -101,7 +101,11 @@ def anybody_can_join? def today? return true if anytime? - start_time <= Date.current.end_of_day && end_time >= Date.current.beginning_of_day + if start_time < Date.current.beginning_of_day && end_time == Date.current.beginning_of_day + false + else + start_time <= Date.current.end_of_day && end_time >= Date.current.beginning_of_day + end end def upcoming? From 0eb2c69eff63cc5a1cc1e5a60b4e167d54c1f978 Mon Sep 17 00:00:00 2001 From: pietia Date: Fri, 16 Aug 2013 16:18:22 +0200 Subject: [PATCH 359/499] remove duplication --- app/models/activity.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/models/activity.rb b/app/models/activity.rb index 95c07c25..e65d955f 100644 --- a/app/models/activity.rb +++ b/app/models/activity.rb @@ -101,10 +101,11 @@ def anybody_can_join? def today? return true if anytime? - if start_time < Date.current.beginning_of_day && end_time == Date.current.beginning_of_day + t1, t2 = Date.current.beginning_of_day, Date.current.end_of_day + if start_time <= t1 && end_time == t1 false else - start_time <= Date.current.end_of_day && end_time >= Date.current.beginning_of_day + start_time <= t2 && end_time >= t1 end end From 8b9c97808fe4935ba3415ed7a160dbe3380a8781 Mon Sep 17 00:00:00 2001 From: pietia Date: Fri, 16 Aug 2013 16:31:52 +0200 Subject: [PATCH 360/499] quick fix for no-js version --- app/controllers/participations_controller.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/controllers/participations_controller.rb b/app/controllers/participations_controller.rb index 15e97c26..40f7e287 100644 --- a/app/controllers/participations_controller.rb +++ b/app/controllers/participations_controller.rb @@ -4,7 +4,9 @@ class ParticipationsController < ApiController def create @participation = current_activity.new_participation(current_user) @participation.save - respond_with(current_activity.reload, @participation, location: request.referer) + respond_with(current_activity.reload, @participation) do |format| + format.html { redirect_to :back } + end end def destroy From b5aacaa313f9687b2f954f2ad4688bafc9f1813e Mon Sep 17 00:00:00 2001 From: pietia Date: Fri, 16 Aug 2013 16:55:14 +0200 Subject: [PATCH 361/499] quick fix for failing 'my activities' tab (no-js) --- app/models/activity.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/activity.rb b/app/models/activity.rb index e65d955f..7ba89f9e 100644 --- a/app/models/activity.rb +++ b/app/models/activity.rb @@ -86,7 +86,7 @@ def find_created_by(user) end def find_participated_by(user) - includes(:participations).where(participations: { user_id: user }).order_by_start_time + joins(:participations).where(participations: { user_id: user }).order_by_start_time end end From 56d2d6f8f4d0d1e1ed6c7b6f96ad0bbf214c2cad Mon Sep 17 00:00:00 2001 From: pietia Date: Fri, 16 Aug 2013 16:56:56 +0200 Subject: [PATCH 362/499] change email --- config/application.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/application.yml b/config/application.yml index 769f4cde..a34d92af 100644 --- a/config/application.yml +++ b/config/application.yml @@ -9,7 +9,7 @@ defaults: &defaults errors: from: "exception@eurucamp.org" to: - - "piotr@gega.io" + - "pietia@appgrinder.pl" - "florian@polarblau.com" seo: title: "Eurucamp Activities" From 33dbbf53f33f26587f759431eb00c9f743e0e073 Mon Sep 17 00:00:00 2001 From: Polarblau Date: Sat, 17 Aug 2013 10:52:51 +0200 Subject: [PATCH 363/499] Don't show actions for past activities. --- app/assets/stylesheets/partials/activities/_index.sass | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/assets/stylesheets/partials/activities/_index.sass b/app/assets/stylesheets/partials/activities/_index.sass index 7b275b14..bd382abc 100644 --- a/app/assets/stylesheets/partials/activities/_index.sass +++ b/app/assets/stylesheets/partials/activities/_index.sass @@ -175,7 +175,8 @@ a#new-activity +transform(translate(0, -9em)) opacity: 0 - &.full.default + &.full.default, + &.past .progress-wrapper, .container > .progress @extend %transition-all-15s-ease-out From a81abf6c4f222974fcbfeeeb7e7ec0dcb0e4fb6b Mon Sep 17 00:00:00 2001 From: pietia Date: Sat, 17 Aug 2013 12:05:25 +0200 Subject: [PATCH 364/499] set start/end_time --- app/models/event.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/models/event.rb b/app/models/event.rb index f557f8e5..1921fc03 100644 --- a/app/models/event.rb +++ b/app/models/event.rb @@ -16,6 +16,8 @@ def initialize( def new_activity(author, *args) activity_source.call(*args).tap do |activity| if activity + activity.start_time = @start_time + activity.end_time = @end_time activity.event = self activity.creator = author end From 8037eac9d56ba8ada0eb30d11d02cd50825daaee Mon Sep 17 00:00:00 2001 From: pietia Date: Sat, 17 Aug 2013 12:08:50 +0200 Subject: [PATCH 365/499] quick fix --- app/models/activity.rb | 2 +- spec/models/event_spec.rb | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/app/models/activity.rb b/app/models/activity.rb index 7ba89f9e..d7d2c142 100644 --- a/app/models/activity.rb +++ b/app/models/activity.rb @@ -119,7 +119,7 @@ def in_past? end def full? - participations_count >= limit_of_participants + participations_count >= limit_of_participants rescue false end def new_participation(user) diff --git a/spec/models/event_spec.rb b/spec/models/event_spec.rb index e4a01a51..2589db21 100644 --- a/spec/models/event_spec.rb +++ b/spec/models/event_spec.rb @@ -24,6 +24,7 @@ its(:event) { should == event } it { should == new_activity } + its(:start_time) { should == } end describe "#activity" do From 1b0f78edd2127ff070c80eb24ce41bc0359e691f Mon Sep 17 00:00:00 2001 From: pietia Date: Sat, 17 Aug 2013 12:29:57 +0200 Subject: [PATCH 366/499] add specs for default start_time / end_time --- spec/models/event_spec.rb | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/spec/models/event_spec.rb b/spec/models/event_spec.rb index 2589db21..e3eae705 100644 --- a/spec/models/event_spec.rb +++ b/spec/models/event_spec.rb @@ -2,14 +2,16 @@ describe Event do - subject(:event) { Event.new("Eurucamp", Date.parse("2012-10-10"), Date.parse("2012-12-14")) } + let(:start_time) { Date.parse("2012-10-10") } + let(:end_time) { Date.parse("2012-12-14") } + subject(:event) { Event.new("Eurucamp", start_time, end_time) } let(:proxy) { mock(:proxy) } describe "#new" do its(:name) { should == "Eurucamp" } - its(:start_time) { should == Date.parse("2012-10-10") } - its(:end_time) { should == Date.parse("2012-12-14") } + its(:start_time) { should == start_time } + its(:end_time) { should == end_time } end describe "#new_activity" do @@ -24,7 +26,8 @@ its(:event) { should == event } it { should == new_activity } - its(:start_time) { should == } + its(:start_time) { should == start_time } + its(:end_time) { should == end_time } end describe "#activity" do From 541c2f4fdddc97fc6a42a75cb97d0b04eced5d82 Mon Sep 17 00:00:00 2001 From: Alex Coles Date: Wed, 5 Feb 2014 12:50:43 +0100 Subject: [PATCH 367/499] Prettify README title Signed-off-by: Alex Coles --- README.md | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/README.md b/README.md index 67612629..c5c2624f 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Eurucamp-activities-2013 +# eurucamp Activities [2013+] ## Development @@ -34,6 +34,3 @@ * `GITHUB_SECRET` * `TWITTER_KEY` * `TWITTER_SECRET` - - - From ee388a085ac26a2a64997ee194d8d672b5b2ac02 Mon Sep 17 00:00:00 2001 From: Alex Coles Date: Wed, 5 Feb 2014 13:05:16 +0100 Subject: [PATCH 368/499] Add License: Affero General Public License v3 Closes #2 Signed-off-by: Alex Coles --- COPYRIGHT | 16 ++ GNU-AGPL-3.0 | 661 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 677 insertions(+) create mode 100644 COPYRIGHT create mode 100644 GNU-AGPL-3.0 diff --git a/COPYRIGHT b/COPYRIGHT new file mode 100644 index 00000000..83663514 --- /dev/null +++ b/COPYRIGHT @@ -0,0 +1,16 @@ +eurucamp Activities App + +Copyright (C) 2013 - 2014 Ruby Berlin e.V. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . diff --git a/GNU-AGPL-3.0 b/GNU-AGPL-3.0 new file mode 100644 index 00000000..dba13ed2 --- /dev/null +++ b/GNU-AGPL-3.0 @@ -0,0 +1,661 @@ + GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU Affero General Public License is a free, copyleft license for +software and other kinds of works, specifically designed to ensure +cooperation with the community in the case of network server software. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +our General Public Licenses are intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + Developers that use our General Public Licenses protect your rights +with two steps: (1) assert copyright on the software, and (2) offer +you this License which gives you legal permission to copy, distribute +and/or modify the software. + + A secondary benefit of defending all users' freedom is that +improvements made in alternate versions of the program, if they +receive widespread use, become available for other developers to +incorporate. Many developers of free software are heartened and +encouraged by the resulting cooperation. However, in the case of +software used on network servers, this result may fail to come about. +The GNU General Public License permits making a modified version and +letting the public access it on a server without ever releasing its +source code to the public. + + The GNU Affero General Public License is designed specifically to +ensure that, in such cases, the modified source code becomes available +to the community. It requires the operator of a network server to +provide the source code of the modified version running there to the +users of that server. Therefore, public use of a modified version, on +a publicly accessible server, gives the public access to the source +code of the modified version. + + An older license, called the Affero General Public License and +published by Affero, was designed to accomplish similar goals. This is +a different license, not a version of the Affero GPL, but Affero has +released a new version of the Affero GPL which permits relicensing under +this license. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU Affero General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Remote Network Interaction; Use with the GNU General Public License. + + Notwithstanding any other provision of this License, if you modify the +Program, your modified version must prominently offer all users +interacting with it remotely through a computer network (if your version +supports such interaction) an opportunity to receive the Corresponding +Source of your version by providing access to the Corresponding Source +from a network server at no charge, through some standard or customary +means of facilitating copying of software. This Corresponding Source +shall include the Corresponding Source for any work covered by version 3 +of the GNU General Public License that is incorporated pursuant to the +following paragraph. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the work with which it is combined will remain governed by version +3 of the GNU General Public License. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU Affero General Public License from time to time. Such new versions +will be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU Affero General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU Affero General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU Affero General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If your software can interact with users remotely through a computer +network, you should also make sure that it provides a way for users to +get its source. For example, if your program is a web application, its +interface could display a "Source" link that leads users to an archive +of the code. There are many ways you could offer source, and different +solutions will be better for different programs; see section 13 for the +specific requirements. + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU AGPL, see +. From c90e0950083a1c409c850a75addf16245e9ebe4e Mon Sep 17 00:00:00 2001 From: Alex Coles Date: Sat, 8 Feb 2014 12:55:14 +0100 Subject: [PATCH 369/499] Bump Rails to 4.0.2, update all dependencies * Bump simple_form to latest compatible, 3.0.1. Signed-off-by: Alex Coles --- Gemfile | 8 +- Gemfile.lock | 249 ++++++++++++++++++++++++++------------------------- 2 files changed, 131 insertions(+), 126 deletions(-) diff --git a/Gemfile b/Gemfile index a1d3b045..7ce0cda7 100644 --- a/Gemfile +++ b/Gemfile @@ -2,7 +2,7 @@ source 'https://rubygems.org' ruby "2.0.0" -gem 'rails', '4.0.0' +gem 'rails', '4.0.2' gem 'pg' gem 'unicorn' gem 'settingslogic' @@ -11,7 +11,7 @@ gem 'devise', '~> 3.0.0.rc' gem 'omniauth', '~> 1.1.4' gem 'omniauth-github', '~> 1.1.0' gem 'omniauth-twitter', '~> 0.0.16' -gem 'simple_form', '~> 3.0.0.beta1' +gem 'simple_form', '~> 3.0.1' gem 'modernizr-rails' gem 'sprockets-rails', git: 'git://github.com/rails/sprockets-rails.git' @@ -25,8 +25,8 @@ gem 'rails_html_helpers' gem 'draper' gem 'cancan' gem 'redcarpet' -gem 'sass-rails', '~> 4.0.0.beta1' -gem 'coffee-rails', '~> 4.0.0.beta1' +gem 'sass-rails', '~> 4.0.0' +gem 'coffee-rails', '~> 4.0.0' gem 'uglifier', '>= 1.0.3' group :development do diff --git a/Gemfile.lock b/Gemfile.lock index 2d58399d..2dfb7f70 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -12,18 +12,18 @@ GIT GIT remote: git://github.com/rails/sprockets-rails.git - revision: b522561d2d83b83c99e9aa54c8079fd1241fdf49 + revision: 8bf70f674ec714dc1082d279fd021075fa036a74 specs: - sprockets-rails (2.0.0) + sprockets-rails (2.0.1) actionpack (>= 3.0) activesupport (>= 3.0) sprockets (~> 2.8) GIT remote: git://github.com/smartinez87/exception_notification.git - revision: 11d61df3cb435381929757b8d7e47209b5ab2b14 + revision: 8a8c6a4c1f48fa49aa98328b3379f5a4a5596b32 specs: - exception_notification (3.0.1) + exception_notification (4.0.1) actionmailer (>= 3.0.4) activesupport (>= 3.0.4) @@ -33,45 +33,45 @@ GEM accept_values_for (0.4.3) activemodel (>= 3.0.0) rspec - actionmailer (4.0.0) - actionpack (= 4.0.0) - mail (~> 2.5.3) - actionpack (4.0.0) - activesupport (= 4.0.0) + actionmailer (4.0.2) + actionpack (= 4.0.2) + mail (~> 2.5.4) + actionpack (4.0.2) + activesupport (= 4.0.2) builder (~> 3.1.0) erubis (~> 2.7.0) rack (~> 1.5.2) rack-test (~> 0.6.2) - activemodel (4.0.0) - activesupport (= 4.0.0) + activemodel (4.0.2) + activesupport (= 4.0.2) builder (~> 3.1.0) - activerecord (4.0.0) - activemodel (= 4.0.0) + activerecord (4.0.2) + activemodel (= 4.0.2) activerecord-deprecated_finders (~> 1.0.2) - activesupport (= 4.0.0) + activesupport (= 4.0.2) arel (~> 4.0.0) activerecord-deprecated_finders (1.0.3) - activesupport (4.0.0) + activesupport (4.0.2) i18n (~> 0.6, >= 0.6.4) minitest (~> 4.2) multi_json (~> 1.3) thread_safe (~> 0.1) tzinfo (~> 0.3.37) - addressable (2.3.4) - arel (4.0.0) - atomic (1.1.9) - bcrypt-ruby (3.0.1) - better_errors (0.8.0) + addressable (2.3.5) + arel (4.0.2) + atomic (1.1.14) + bcrypt-ruby (3.1.2) + better_errors (1.1.0) coderay (>= 1.0.0) erubis (>= 2.6.6) - binding_of_caller (0.7.1) + binding_of_caller (0.7.2) debug_inspector (>= 0.0.1) - bourbon (3.1.6) + bourbon (3.1.8) sass (>= 3.2.0) thor builder (3.1.4) cancan (1.6.10) - capybara (2.1.0) + capybara (2.2.1) mime-types (>= 1.16) nokogiri (>= 1.3.3) rack (>= 1.0.0) @@ -80,99 +80,103 @@ GEM capybara-webkit (0.14.2) capybara (~> 2.0, >= 2.0.2) json - coderay (1.0.9) - coffee-rails (4.0.0) + coderay (1.1.0) + coffee-rails (4.0.1) coffee-script (>= 2.2.0) - railties (>= 4.0.0.beta, < 5.0) + railties (>= 4.0.0, < 5.0) coffee-script (2.2.0) coffee-script-source execjs - coffee-script-source (1.6.2) + coffee-script-source (1.7.0) columnize (0.3.6) debug_inspector (0.0.2) - debugger (1.6.0) + debugger (1.6.5) columnize (>= 0.3.1) debugger-linecache (~> 1.2.0) - debugger-ruby_core_source (~> 1.2.1) + debugger-ruby_core_source (~> 1.3.1) debugger-linecache (1.2.0) - debugger-ruby_core_source (1.2.2) - devise (3.0.0.rc) + debugger-ruby_core_source (1.3.1) + devise (3.0.4) bcrypt-ruby (~> 3.0) orm_adapter (~> 0.1) railties (>= 3.2.6, < 5) - warden (~> 1.2.1) - diff-lcs (1.2.4) - dotenv (0.7.0) - draper (1.2.1) + warden (~> 1.2.3) + diff-lcs (1.2.5) + docile (1.1.3) + dotenv (0.9.0) + draper (1.3.0) actionpack (>= 3.0) + activemodel (>= 3.0) activesupport (>= 3.0) request_store (~> 1.0.3) erubis (2.7.0) - excon (0.20.1) - execjs (1.4.0) - multi_json (~> 1.0) - factory_girl (4.2.0) + excon (0.31.0) + execjs (2.0.2) + factory_girl (4.3.0) activesupport (>= 3.0.0) - factory_girl_rails (4.2.1) - factory_girl (~> 4.2.0) + factory_girl_rails (4.3.0) + factory_girl (~> 4.3.0) railties (>= 3.0.0) - faraday (0.8.7) - multipart-post (~> 1.1) + faraday (0.9.0) + multipart-post (>= 1.2, < 3) foreman (0.63.0) dotenv (>= 0.7) thor (>= 0.13.6) - haml (4.0.3) + haml (4.0.5) tilt - haml-rails (0.4) - actionpack (>= 3.1, < 4.1) - activesupport (>= 3.1, < 4.1) - haml (>= 3.1, < 4.1) - railties (>= 3.1, < 4.1) - hashie (2.0.4) - heroku (2.39.2) - heroku-api (~> 0.3.7) + haml-rails (0.5.3) + actionpack (>= 4.0.1) + activesupport (>= 4.0.1) + haml (>= 3.1, < 5.0) + railties (>= 4.0.1) + hashie (2.0.5) + heroku (3.3.0) + heroku-api (~> 0.3.17) launchy (>= 0.3.2) netrc (~> 0.7.7) rest-client (~> 1.6.1) rubyzip - heroku-api (0.3.9) - excon (~> 0.20.1) + heroku-api (0.3.17) + excon (~> 0.27) + multi_json (~> 1.8.2) heroku_san (3.0.4) activesupport heroku (>= 2) heroku-api (>= 0.1.2) rake hike (1.2.3) - httpauth (0.2.0) - i18n (0.6.4) + httpauth (0.2.1) + i18n (0.6.9) jbuilder (1.0.2) activesupport (>= 3.0.0) - jquery-rails (2.2.1) + jquery-rails (3.1.0) railties (>= 3.0, < 5.0) thor (>= 0.14, < 2.0) - json (1.7.7) + json (1.8.1) json_spec (1.1.1) multi_json (~> 1.0) rspec (~> 2.0) - jwt (0.1.8) + jwt (0.1.11) multi_json (>= 1.5) - kgio (2.8.0) - launchy (2.3.0) + kgio (2.9.1) + launchy (2.4.2) addressable (~> 2.3) mail (2.5.4) mime-types (~> 1.16) treetop (~> 1.4.8) - mime-types (1.23) + mime-types (1.25.1) + mini_portile (0.5.2) minitest (4.7.5) - modernizr-rails (2.6.2.3) - multi_json (1.7.7) - multipart-post (1.2.0) - neat (1.2.1) + modernizr-rails (2.7.1) + multi_json (1.8.4) + multipart-post (2.0.0) + neat (1.5.0) bourbon (>= 2.1) sass (>= 3.2) netrc (0.7.7) - newrelic_rpm (3.6.1.88) - nokogiri (1.5.9) + newrelic_rpm (3.7.2.192) + nokogiri (1.6.1) + mini_portile (~> 0.5.0) oauth (0.4.7) oauth2 (0.8.1) faraday (~> 0.8) @@ -183,7 +187,7 @@ GEM omniauth (1.1.4) hashie (>= 1.2, < 3) rack - omniauth-github (1.1.0) + omniauth-github (1.1.1) omniauth (~> 1.0) omniauth-oauth2 (~> 1.1) omniauth-oauth (1.0.1) @@ -192,91 +196,92 @@ GEM omniauth-oauth2 (1.1.1) oauth2 (~> 0.8.0) omniauth (~> 1.0) - omniauth-twitter (0.0.16) + omniauth-twitter (0.0.18) multi_json (~> 1.3) omniauth-oauth (~> 1.0) - orm_adapter (0.4.0) - pg (0.15.1) + orm_adapter (0.5.0) + pg (0.17.1) polyglot (0.3.3) rack (1.5.2) - rack-robotz (0.0.3) + rack-robotz (0.0.4) rack rack-test (0.6.2) rack (>= 1.0) - rails (4.0.0) - actionmailer (= 4.0.0) - actionpack (= 4.0.0) - activerecord (= 4.0.0) - activesupport (= 4.0.0) + rails (4.0.2) + actionmailer (= 4.0.2) + actionpack (= 4.0.2) + activerecord (= 4.0.2) + activesupport (= 4.0.2) bundler (>= 1.3.0, < 2.0) - railties (= 4.0.0) + railties (= 4.0.2) sprockets-rails (~> 2.0.0) rails_html_helpers (0.1.1) railties (>= 3.2) - railties (4.0.0) - actionpack (= 4.0.0) - activesupport (= 4.0.0) + railties (4.0.2) + actionpack (= 4.0.2) + activesupport (= 4.0.2) rake (>= 0.8.7) thor (>= 0.18.1, < 2.0) - raindrops (0.11.0) - rake (10.1.0) - redcarpet (2.3.0) + raindrops (0.12.0) + rake (10.1.1) + redcarpet (3.0.0) request_store (1.0.5) rest-client (1.6.7) mime-types (>= 1.16) - rspec (2.13.0) - rspec-core (~> 2.13.0) - rspec-expectations (~> 2.13.0) - rspec-mocks (~> 2.13.0) - rspec-core (2.13.1) - rspec-expectations (2.13.0) + rspec (2.14.1) + rspec-core (~> 2.14.0) + rspec-expectations (~> 2.14.0) + rspec-mocks (~> 2.14.0) + rspec-core (2.14.7) + rspec-expectations (2.14.5) diff-lcs (>= 1.1.3, < 2.0) - rspec-mocks (2.13.1) - rspec-rails (2.13.1) + rspec-mocks (2.14.5) + rspec-rails (2.14.1) actionpack (>= 3.0) + activemodel (>= 3.0) activesupport (>= 3.0) railties (>= 3.0) - rspec-core (~> 2.13.0) - rspec-expectations (~> 2.13.0) - rspec-mocks (~> 2.13.0) - rubyzip (0.9.9) - sass (3.2.8) - sass-rails (4.0.0.rc1) - railties (>= 4.0.0.beta, < 5.0) + rspec-core (~> 2.14.0) + rspec-expectations (~> 2.14.0) + rspec-mocks (~> 2.14.0) + rubyzip (1.1.0) + sass (3.2.14) + sass-rails (4.0.1) + railties (>= 4.0.0, < 5.0) sass (>= 3.1.10) - sprockets-rails (~> 2.0.0.rc0) - tilt (~> 1.3) + sprockets-rails (~> 2.0.0) settingslogic (2.0.9) - simple_form (3.0.0.beta1) - actionpack (>= 4.0.0.beta, < 4.1) - activemodel (>= 4.0.0.beta, < 4.1) - simplecov (0.7.1) - multi_json (~> 1.0) - simplecov-html (~> 0.7.1) - simplecov-html (0.7.1) - sprockets (2.10.0) + simple_form (3.0.1) + actionpack (>= 4.0.0, < 4.1) + activemodel (>= 4.0.0, < 4.1) + simplecov (0.8.2) + docile (~> 1.1.0) + multi_json + simplecov-html (~> 0.8.0) + simplecov-html (0.8.0) + sprockets (2.10.1) hike (~> 1.2) multi_json (~> 1.0) rack (~> 1.0) tilt (~> 1.1, != 1.3.0) thor (0.18.1) - thread_safe (0.1.0) + thread_safe (0.1.3) atomic tilt (1.4.1) - treetop (1.4.14) + treetop (1.4.15) polyglot polyglot (>= 0.3.1) - turbolinks (1.1.1) + turbolinks (2.2.1) coffee-rails - tzinfo (0.3.37) - uglifier (2.0.1) + tzinfo (0.3.38) + uglifier (2.4.0) execjs (>= 0.3.0) - multi_json (~> 1.0, >= 1.0.2) - unicorn (4.6.2) + json (>= 1.8.0) + unicorn (4.8.2) kgio (~> 2.6) rack raindrops (~> 0.7) - warden (1.2.1) + warden (1.2.3) rack (>= 1.0) xpath (2.0.0) nokogiri (~> 1.3) @@ -292,7 +297,7 @@ DEPENDENCIES cancan capybara (~> 2.1) capybara-webkit (~> 0.14) - coffee-rails (~> 4.0.0.beta1) + coffee-rails (~> 4.0.0) debugger (~> 1.6) devise (~> 3.0.0.rc) draper @@ -312,15 +317,15 @@ DEPENDENCIES omniauth-twitter (~> 0.0.16) pg rack-robotz (~> 0.0.3) - rails (= 4.0.0) + rails (= 4.0.2) rails3_serve_static_assets! rails_html_helpers rails_log_stdout! redcarpet rspec-rails (~> 2.0) - sass-rails (~> 4.0.0.beta1) + sass-rails (~> 4.0.0) settingslogic - simple_form (~> 3.0.0.beta1) + simple_form (~> 3.0.1) simplecov sprockets-rails! turbolinks From 6d5c58fddbc3530d933f58580780b76be588263b Mon Sep 17 00:00:00 2001 From: Alex Coles Date: Sat, 8 Feb 2014 13:12:24 +0100 Subject: [PATCH 370/499] Fix rspec-mocks deprecations Signed-off-by: Alex Coles --- .../controllers/activities_controller_spec.rb | 12 ++--- .../authentications_controller_spec.rb | 6 +-- .../participation_controller_spec.rb | 12 ++--- spec/models/activity_spec.rb | 46 +++++++++---------- spec/models/event_spec.rb | 6 +-- spec/models/user_spec.rb | 2 +- 6 files changed, 42 insertions(+), 42 deletions(-) diff --git a/spec/controllers/activities_controller_spec.rb b/spec/controllers/activities_controller_spec.rb index 361b0488..a50455f2 100644 --- a/spec/controllers/activities_controller_spec.rb +++ b/spec/controllers/activities_controller_spec.rb @@ -3,21 +3,21 @@ describe ActivitiesController do let(:current_user) { mock_model(User) } - let(:activity) { mock(:activity) } + let(:activity) { double(:activity) } let(:activity_id) { "1" } - let(:invalid_activity) { mock(:invalid_activity, errors: {name: "no way!"}) } - let(:current_event) { mock(:current_event) } + let(:invalid_activity) { double(:invalid_activity, errors: {name: "no way!"}) } + let(:current_event) { double(:current_event) } before do - controller.stub!(:current_event).and_return(current_event) + controller.stub(:current_event).and_return(current_event) end describe "#index" do subject { get :index } let(:current_user) { nil } - let(:activities) { mock(:activities) } - let(:counters) { mock(:counters) } + let(:activities) { double(:activities) } + let(:counters) { double(:counters) } before do current_event.should_receive(:search_activities).with(current_user, nil, nil).and_return(activities) diff --git a/spec/controllers/authentications_controller_spec.rb b/spec/controllers/authentications_controller_spec.rb index b09ef3e1..8bb1c914 100644 --- a/spec/controllers/authentications_controller_spec.rb +++ b/spec/controllers/authentications_controller_spec.rb @@ -3,13 +3,13 @@ describe AuthenticationsController do let(:current_user) { mock_model(User) } - let(:authentications) { mock(:authentications) } - let(:authentication) { mock(:authentication) } + let(:authentications) { double(:authentications) } + let(:authentication) { double(:authentication) } let(:authentication_id) { "1" } before do @request.env["devise.mapping"] = Devise.mappings[:user] - current_user.stub!(:authentications).and_return(authentications) + current_user.stub(:authentications).and_return(authentications) end describe '#create' do diff --git a/spec/controllers/participation_controller_spec.rb b/spec/controllers/participation_controller_spec.rb index 4e6a8917..1c02271b 100644 --- a/spec/controllers/participation_controller_spec.rb +++ b/spec/controllers/participation_controller_spec.rb @@ -3,14 +3,14 @@ describe ParticipationsController do let(:current_user) { mock_model(User) } let(:activity_id) { "1" } - let(:activity) { mock(:activity) } - let(:participation) { mock(:participation) } - let(:invalid_participation) { mock(:participation, errors: {activity_id: "nahhhhh"}) } - let(:current_event) { mock(:current_event) } + let(:activity) { double(:activity) } + let(:participation) { double(:participation) } + let(:invalid_participation) { double(:participation, errors: {activity_id: "nahhhhh"}) } + let(:current_event) { double(:current_event) } before do - activity.stub!(:reload).and_return(activity) - controller.stub!(:current_event).and_return(current_event) + activity.stub(:reload).and_return(activity) + controller.stub(:current_event).and_return(current_event) end describe "#create" do diff --git a/spec/models/activity_spec.rb b/spec/models/activity_spec.rb index 52beed4b..1288277e 100644 --- a/spec/models/activity_spec.rb +++ b/spec/models/activity_spec.rb @@ -2,7 +2,7 @@ describe Activity do - let(:event) { mock(:event) } + let(:event) { double(:event) } let(:creator) { mock_model(User) } subject(:activity) { Activity.new } @@ -28,7 +28,7 @@ end describe ".recent" do - let(:recent_activities) { [mock(:activity1), mock(:activity2)] } + let(:recent_activities) { [double(:activity1), double(:activity2)] } subject { Activity.recent } before do @@ -52,7 +52,7 @@ context "limit of participants is not set" do before do - activity.stub!(:limit_of_participants).and_return(nil) + activity.stub(:limit_of_participants).and_return(nil) end it { should == 0 } @@ -60,8 +60,8 @@ context "limit of participants is set" do before do - activity.stub!(:limit_of_participants).and_return(10) - activity.stub!(:participations_count).and_return(8) + activity.stub(:limit_of_participants).and_return(10) + activity.stub(:participations_count).and_return(8) end it { should == 80 } @@ -69,8 +69,8 @@ context "no participants" do before do - activity.stub!(:limit_of_participants).and_return(10) - activity.stub!(:participations_count).and_return(0) + activity.stub(:limit_of_participants).and_return(10) + activity.stub(:participations_count).and_return(0) end it { should == 0 } @@ -82,7 +82,7 @@ context "no limit set" do before do - activity.stub!(:limit_of_participants).and_return(nil) + activity.stub(:limit_of_participants).and_return(nil) end it { should == true } @@ -90,7 +90,7 @@ context "limit set" do before do - activity.stub!(:limit_of_participants).and_return(10) + activity.stub(:limit_of_participants).and_return(10) end it { should == false } @@ -102,7 +102,7 @@ context "anytime set" do before do - activity.stub!(:anytime?).and_return(true) + activity.stub(:anytime?).and_return(true) end it { should == true } @@ -110,8 +110,8 @@ context "today" do before do - activity.stub!(:start_time).and_return(2.days.ago.to_time) - activity.stub!(:end_time).and_return(2.days.from_now.to_time) + activity.stub(:start_time).and_return(2.days.ago.to_time) + activity.stub(:end_time).and_return(2.days.from_now.to_time) end it { should == true } @@ -119,9 +119,9 @@ context "not today" do before do - activity.stub!(:anytime?).and_return(false) - activity.stub!(:start_time).and_return(2.days.from_now.to_time) - activity.stub!(:end_time).and_return(10.days.from_now.to_time) + activity.stub(:anytime?).and_return(false) + activity.stub(:start_time).and_return(2.days.from_now.to_time) + activity.stub(:end_time).and_return(10.days.from_now.to_time) end it { should == false } @@ -134,8 +134,8 @@ context "full" do before do - activity.stub!(:limit_of_participants).and_return(10) - activity.stub!(:participations_count).and_return(10) + activity.stub(:limit_of_participants).and_return(10) + activity.stub(:participations_count).and_return(10) end it { should == true } @@ -143,8 +143,8 @@ context "too full" do before do - activity.stub!(:limit_of_participants).and_return(10) - activity.stub!(:participations_count).and_return(11) + activity.stub(:limit_of_participants).and_return(10) + activity.stub(:participations_count).and_return(11) end it { should == true } @@ -152,8 +152,8 @@ context "not full" do before do - activity.stub!(:limit_of_participants).and_return(10) - activity.stub!(:participations_count).and_return(8) + activity.stub(:limit_of_participants).and_return(10) + activity.stub(:participations_count).and_return(8) end it { should == false } @@ -166,7 +166,7 @@ context "in the future" do before do - activity.stub!(:start_time).and_return(1.days.from_now.to_time) + activity.stub(:start_time).and_return(1.days.from_now.to_time) end it { should == true } @@ -174,7 +174,7 @@ context "not in the future" do before do - activity.stub!(:start_time).and_return(Time.now) + activity.stub(:start_time).and_return(Time.now) end it { should == false } diff --git a/spec/models/event_spec.rb b/spec/models/event_spec.rb index e3eae705..1e422489 100644 --- a/spec/models/event_spec.rb +++ b/spec/models/event_spec.rb @@ -6,7 +6,7 @@ let(:end_time) { Date.parse("2012-12-14") } subject(:event) { Event.new("Eurucamp", start_time, end_time) } - let(:proxy) { mock(:proxy) } + let(:proxy) { double(:proxy) } describe "#new" do its(:name) { should == "Eurucamp" } @@ -55,7 +55,7 @@ end describe "#all_activities" do - let(:activities) { [mock(:activity1), mock(:activity2)] } + let(:activities) { [double(:activity1), double(:activity2)] } let(:event) { Event.new } subject { event.activities } @@ -67,7 +67,7 @@ end describe "#search_activities" do - let(:activities) { [mock(:activity1), mock(:activity2)] } + let(:activities) { [double(:activity1), double(:activity2)] } context "default args" do subject { event.search_activities } diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 28d53e92..72d6301f 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -96,7 +96,7 @@ end context "account connected" do - let(:authentications) { [mock(:authentication)] } + let(:authentications) { [double(:authentication)] } before do user.should_receive(:authentications).and_return(authentications) From 46dc30b7a6a166c7a963e87416f1479a3c4434f0 Mon Sep 17 00:00:00 2001 From: Alex Coles Date: Sat, 8 Feb 2014 13:15:10 +0100 Subject: [PATCH 371/499] Enable i18n enforce_available_locales in config Signed-off-by: Alex Coles --- config/application.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/config/application.rb b/config/application.rb index de854822..83b04a86 100644 --- a/config/application.rb +++ b/config/application.rb @@ -25,5 +25,7 @@ class Application < Rails::Application config.generators.assets = false config.assets.initialize_on_precompile = false + + config.i18n.enforce_available_locales = true end end From 72c4a522206a180ab23cae066e96d060232b429f Mon Sep 17 00:00:00 2001 From: Alex Coles Date: Sat, 8 Feb 2014 13:30:00 +0100 Subject: [PATCH 372/499] Update Exception Notification config to 4.x * Peg gem to latest release. Signed-off-by: Alex Coles --- Gemfile | 2 +- Gemfile.lock | 13 ++++--------- config/environments/production.rb | 9 +++++---- config/environments/staging.rb | 9 +++++---- 4 files changed, 15 insertions(+), 18 deletions(-) diff --git a/Gemfile b/Gemfile index 7ce0cda7..c1a8fadb 100644 --- a/Gemfile +++ b/Gemfile @@ -51,7 +51,7 @@ group :test do end group :production, :staging do - gem 'exception_notification', require: 'exception_notifier', git: 'git://github.com/smartinez87/exception_notification.git' + gem 'exception_notification', '~> 4.0.1' gem 'rack-robotz', '~> 0.0.3' gem 'rails_log_stdout', github: 'heroku/rails_log_stdout' gem 'rails3_serve_static_assets', github: 'heroku/rails3_serve_static_assets' diff --git a/Gemfile.lock b/Gemfile.lock index 2dfb7f70..72750622 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -19,14 +19,6 @@ GIT activesupport (>= 3.0) sprockets (~> 2.8) -GIT - remote: git://github.com/smartinez87/exception_notification.git - revision: 8a8c6a4c1f48fa49aa98328b3379f5a4a5596b32 - specs: - exception_notification (4.0.1) - actionmailer (>= 3.0.4) - activesupport (>= 3.0.4) - GEM remote: https://rubygems.org/ specs: @@ -110,6 +102,9 @@ GEM activesupport (>= 3.0) request_store (~> 1.0.3) erubis (2.7.0) + exception_notification (4.0.1) + actionmailer (>= 3.0.4) + activesupport (>= 3.0.4) excon (0.31.0) execjs (2.0.2) factory_girl (4.3.0) @@ -301,7 +296,7 @@ DEPENDENCIES debugger (~> 1.6) devise (~> 3.0.0.rc) draper - exception_notification! + exception_notification (~> 4.0.1) factory_girl_rails (~> 4.2) foreman haml-rails diff --git a/config/environments/production.rb b/config/environments/production.rb index 0248cfb1..24204ee0 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -80,10 +80,11 @@ # Use default logging formatter so that PID and timestamp are not suppressed. config.log_formatter = ::Logger::Formatter.new - config.middleware.use ExceptionNotifier, - :email_prefix => "[Eurucamp-activities::Exception] ", - :sender_address => %{"Exception Notifier" <#{Settings.errors.from}>}, - :exception_recipients => Settings.errors.to + config.middleware.use ExceptionNotification::Rack, email: { + :email_prefix => "[Eurucamp-activities::Exception] ", + :sender_address => %{"Exception Notifier" <#{Settings.errors.from}>}, + :exception_recipients => Settings.errors.to + } end ActionMailer::Base.smtp_settings = { diff --git a/config/environments/staging.rb b/config/environments/staging.rb index ed8b4137..79321a34 100644 --- a/config/environments/staging.rb +++ b/config/environments/staging.rb @@ -81,10 +81,11 @@ config.middleware.insert_before(::ActionDispatch::Static, ::Rack::Robotz, "User-Agent" => "*", "Disallow" => "/") - config.middleware.use ExceptionNotifier, - :email_prefix => "[Eurucamp-activities-staging::Exception] ", - :sender_address => %{"Exception Notifier" <#{Settings.errors.from}>}, - :exception_recipients => Settings.errors.to + config.middleware.use ExceptionNotification::Rack, email: { + :email_prefix => "[Eurucamp-activities-staging::Exception] ", + :sender_address => %{"Exception Notifier" <#{Settings.errors.from}>}, + :exception_recipients => Settings.errors.to + } end ActionMailer::Base.smtp_settings = { From 5c838fe4619969970ab02edc8b582a745e486ec7 Mon Sep 17 00:00:00 2001 From: Alex Coles Date: Sun, 9 Feb 2014 10:49:04 +0100 Subject: [PATCH 373/499] Cleanup whitespace [ci skip] Signed-off-by: Alex Coles --- app/assets/stylesheets/_settings.sass | 1 - app/assets/stylesheets/_utils.sass | 2 +- app/assets/stylesheets/partials/_header.sass | 1 - .../stylesheets/partials/activities/_index.sass | 1 - .../stylesheets/partials/user/_registrations.sass | 2 +- app/assets/stylesheets/partials/user/_shared.sass | 6 +++--- app/assets/stylesheets/utils/_animations.sass | 4 ++-- app/assets/stylesheets/utils/_buttons.sass | 2 +- app/models/activity.rb | 2 +- app/views/activities/_action.html.haml | 2 +- app/views/activities/edit.html.haml | 2 +- app/views/activities/new.html.haml | 2 +- app/views/layouts/user.html.haml | 2 +- app/views/partials/_footer.html.haml | 2 +- app/views/partials/_header.html.haml | 2 +- app/views/partials/_layout_head.html.haml | 2 +- app/views/partials/_notifications.html.haml | 2 +- app/views/partials/_preinitializers.html.haml | 2 +- app/views/passwords/new.html.haml | 2 +- app/views/registrations/edit.html.haml | 2 +- app/views/sessions/new.html.haml | 1 - config/environments/development.rb | 2 +- config/environments/production.rb | 2 +- spec/controllers/activities_controller_spec.rb | 2 +- spec/controllers/participation_controller_spec.rb | 2 +- spec/factories/sequences.rb | 2 +- spec/lib/settings_spec.rb | 2 +- spec/models/activity_spec.rb | 12 ++++++------ spec/models/event_spec.rb | 2 +- spec/other/factories_spec.rb | 2 +- spec/support/helpers/controller_helpers.rb | 2 +- 31 files changed, 35 insertions(+), 39 deletions(-) diff --git a/app/assets/stylesheets/_settings.sass b/app/assets/stylesheets/_settings.sass index 50bcc6a4..f8845aa0 100644 --- a/app/assets/stylesheets/_settings.sass +++ b/app/assets/stylesheets/_settings.sass @@ -35,4 +35,3 @@ $medium-gray: #8a8a8a $light-gray: darken(#e2e2e2, 10) $extra-light-gray: darken(#f1f1f1, 5) $white: #ffffff - diff --git a/app/assets/stylesheets/_utils.sass b/app/assets/stylesheets/_utils.sass index e05e0258..c97886f4 100644 --- a/app/assets/stylesheets/_utils.sass +++ b/app/assets/stylesheets/_utils.sass @@ -2,4 +2,4 @@ @import "utils/placeholders" @import "utils/forms" @import "utils/buttons" -@import "utils/animations" \ No newline at end of file +@import "utils/animations" diff --git a/app/assets/stylesheets/partials/_header.sass b/app/assets/stylesheets/partials/_header.sass index 0a301d19..58d8bc44 100644 --- a/app/assets/stylesheets/partials/_header.sass +++ b/app/assets/stylesheets/partials/_header.sass @@ -48,4 +48,3 @@ h1 padding: 2em 0 - diff --git a/app/assets/stylesheets/partials/activities/_index.sass b/app/assets/stylesheets/partials/activities/_index.sass index bd382abc..c096a361 100644 --- a/app/assets/stylesheets/partials/activities/_index.sass +++ b/app/assets/stylesheets/partials/activities/_index.sass @@ -217,4 +217,3 @@ a#new-activity +media($tablet-portrait-plus) li +span-columns(2) - diff --git a/app/assets/stylesheets/partials/user/_registrations.sass b/app/assets/stylesheets/partials/user/_registrations.sass index f762b2aa..279ae46e 100644 --- a/app/assets/stylesheets/partials/user/_registrations.sass +++ b/app/assets/stylesheets/partials/user/_registrations.sass @@ -2,4 +2,4 @@ article section &.form form - display: none \ No newline at end of file + display: none diff --git a/app/assets/stylesheets/partials/user/_shared.sass b/app/assets/stylesheets/partials/user/_shared.sass index c0fc1aee..3d47c481 100644 --- a/app/assets/stylesheets/partials/user/_shared.sass +++ b/app/assets/stylesheets/partials/user/_shared.sass @@ -1,13 +1,13 @@ article margin: 0 150px text-align: center - - h4 + + h4 +custom-sans(light) font-size: 1.375em padding: 0 0.727272em text-align: center - + p text-align: center margin: 2em 0 3em 0 diff --git a/app/assets/stylesheets/utils/_animations.sass b/app/assets/stylesheets/utils/_animations.sass index 8318804f..05c4132c 100644 --- a/app/assets/stylesheets/utils/_animations.sass +++ b/app/assets/stylesheets/utils/_animations.sass @@ -10,7 +10,7 @@ 60% opacity: 1 +transform(translateY(30px)) - 80% + 80% +transform(translateY(-10px)) 100% +transform(translateY(0)) @@ -32,4 +32,4 @@ %animated-bounce-out-up @extend %animated - +animation-name(bounceOutUp) \ No newline at end of file + +animation-name(bounceOutUp) diff --git a/app/assets/stylesheets/utils/_buttons.sass b/app/assets/stylesheets/utils/_buttons.sass index 5c462384..3a50c773 100644 --- a/app/assets/stylesheets/utils/_buttons.sass +++ b/app/assets/stylesheets/utils/_buttons.sass @@ -125,4 +125,4 @@ a.reveal @extend %secondary-button text-transform: uppercase font-size: 1.15em - padding: 0.5em 1.5em \ No newline at end of file + padding: 0.5em 1.5em diff --git a/app/models/activity.rb b/app/models/activity.rb index d7d2c142..6a0b37c0 100644 --- a/app/models/activity.rb +++ b/app/models/activity.rb @@ -18,7 +18,7 @@ class Activity < ActiveRecord::Base validate :during_the_event, if: ->{ !anytime && event && start_time.present? && end_time.present? } validates :event, presence: true validate :image_url_valid, if: ->{ image_url.present? } - + before_validation :clear_time_frame, if: ->{ anytime } class << self diff --git a/app/views/activities/_action.html.haml b/app/views/activities/_action.html.haml index 4a0e0468..edf68c54 100644 --- a/app/views/activities/_action.html.haml +++ b/app/views/activities/_action.html.haml @@ -10,7 +10,7 @@ = button_to activity_participation_path(activity), method: :delete, remote: remote, title: t("activities.leave.title") do %span = t("activities.leave.label") - - else + - else - unless activity.full? || activity.in_past? = button_to activity_participation_path(activity), method: :post, remote: remote, title: t("activities.join.title") do %span diff --git a/app/views/activities/edit.html.haml b/app/views/activities/edit.html.haml index 39881de1..594b9b9d 100644 --- a/app/views/activities/edit.html.haml +++ b/app/views/activities/edit.html.haml @@ -9,4 +9,4 @@ = t("common.or").downcase = link_to t("activities.new.label").downcase, new_activity_path - = render "form" \ No newline at end of file + = render "form" diff --git a/app/views/activities/new.html.haml b/app/views/activities/new.html.haml index 986c2159..22cfa623 100644 --- a/app/views/activities/new.html.haml +++ b/app/views/activities/new.html.haml @@ -3,4 +3,4 @@ %h2 = t("new_activity.title") - = render "form" \ No newline at end of file + = render "form" diff --git a/app/views/layouts/user.html.haml b/app/views/layouts/user.html.haml index 62ea9ac9..b63dd10f 100644 --- a/app/views/layouts/user.html.haml +++ b/app/views/layouts/user.html.haml @@ -10,7 +10,7 @@ #container %header#header = render "partials/navigation" - + #main(role="main") = yield = render "partials/footer" diff --git a/app/views/partials/_footer.html.haml b/app/views/partials/_footer.html.haml index d38b6cb4..76e56f0e 100644 --- a/app/views/partials/_footer.html.haml +++ b/app/views/partials/_footer.html.haml @@ -7,4 +7,4 @@ %a(href="http://eurucamp.org" title="#{t("footer_nav.eurucamp.title")}") = t("footer_nav.eurucamp.label") -= javascript_include_tag "//eurucamp-week-bar.herokuapp.com/bar.js?group=rand_days&color=e55924" \ No newline at end of file += javascript_include_tag "//eurucamp-week-bar.herokuapp.com/bar.js?group=rand_days&color=e55924" diff --git a/app/views/partials/_header.html.haml b/app/views/partials/_header.html.haml index 9e7ce2d7..093a1ecd 100644 --- a/app/views/partials/_header.html.haml +++ b/app/views/partials/_header.html.haml @@ -1,7 +1,7 @@ %header#header = render "partials/navigation" - + -# the logo, link home %h1 = link_to "/" do diff --git a/app/views/partials/_layout_head.html.haml b/app/views/partials/_layout_head.html.haml index 0bdae6e9..23aa463e 100644 --- a/app/views/partials/_layout_head.html.haml +++ b/app/views/partials/_layout_head.html.haml @@ -10,4 +10,4 @@ = stylesheet_link_tag "application" = javascript_include_tag :modernizr -= javascript_include_tag "//use.typekit.net/vor5lqb.js" \ No newline at end of file += javascript_include_tag "//use.typekit.net/vor5lqb.js" diff --git a/app/views/partials/_notifications.html.haml b/app/views/partials/_notifications.html.haml index e494939f..93cae0d5 100644 --- a/app/views/partials/_notifications.html.haml +++ b/app/views/partials/_notifications.html.haml @@ -1,4 +1,4 @@ #notifications - flash.each do |key, value| %div(class=key) - = value \ No newline at end of file + = value diff --git a/app/views/partials/_preinitializers.html.haml b/app/views/partials/_preinitializers.html.haml index 7498d14f..3b13dee2 100644 --- a/app/views/partials/_preinitializers.html.haml +++ b/app/views/partials/_preinitializers.html.haml @@ -14,4 +14,4 @@ startTime: #{current_event.start_time.to_i * 1000}, endTime: #{current_event.end_time.to_i * 1000} } - }; \ No newline at end of file + }; diff --git a/app/views/passwords/new.html.haml b/app/views/passwords/new.html.haml index 82d015bd..ff8b0975 100644 --- a/app/views/passwords/new.html.haml +++ b/app/views/passwords/new.html.haml @@ -19,4 +19,4 @@ You can also = link_to new_registration_path(:user), title: "Sign up" do sign up - if you don't have account yet. \ No newline at end of file + if you don't have account yet. diff --git a/app/views/registrations/edit.html.haml b/app/views/registrations/edit.html.haml index c995ed1a..87a3a000 100644 --- a/app/views/registrations/edit.html.haml +++ b/app/views/registrations/edit.html.haml @@ -32,4 +32,4 @@ %p.divider %span Connect! - = render "oauth", :resource => resource, :domain => "edit" \ No newline at end of file + = render "oauth", :resource => resource, :domain => "edit" diff --git a/app/views/sessions/new.html.haml b/app/views/sessions/new.html.haml index bc14f94f..0cfaff0d 100644 --- a/app/views/sessions/new.html.haml +++ b/app/views/sessions/new.html.haml @@ -30,4 +30,3 @@ or = link_to new_password_path(:user), title: "Reset your password" do reset your password - diff --git a/config/environments/development.rb b/config/environments/development.rb index 9a336683..ad3c207b 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -34,4 +34,4 @@ config.sass.debug_info = true config.sass.line_comments = true -end \ No newline at end of file +end diff --git a/config/environments/production.rb b/config/environments/production.rb index 24204ee0..733bf2ac 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -95,4 +95,4 @@ :password => ENV['SENDGRID_PASSWORD'], :domain => 'heroku.com', :enable_starttls_auto => true -} \ No newline at end of file +} diff --git a/spec/controllers/activities_controller_spec.rb b/spec/controllers/activities_controller_spec.rb index a50455f2..385a1b38 100644 --- a/spec/controllers/activities_controller_spec.rb +++ b/spec/controllers/activities_controller_spec.rb @@ -168,4 +168,4 @@ end -end \ No newline at end of file +end diff --git a/spec/controllers/participation_controller_spec.rb b/spec/controllers/participation_controller_spec.rb index 1c02271b..8fdf0c5f 100644 --- a/spec/controllers/participation_controller_spec.rb +++ b/spec/controllers/participation_controller_spec.rb @@ -68,4 +68,4 @@ end end -end \ No newline at end of file +end diff --git a/spec/factories/sequences.rb b/spec/factories/sequences.rb index 14777efd..6e19e88a 100644 --- a/spec/factories/sequences.rb +++ b/spec/factories/sequences.rb @@ -1,4 +1,4 @@ FactoryGirl.define do sequence(:email) { |n| "person#{n}@example.com" } sequence(:uid) { |n| "uid#{n}" } -end \ No newline at end of file +end diff --git a/spec/lib/settings_spec.rb b/spec/lib/settings_spec.rb index 5377fca0..49b3455e 100644 --- a/spec/lib/settings_spec.rb +++ b/spec/lib/settings_spec.rb @@ -17,4 +17,4 @@ end -end \ No newline at end of file +end diff --git a/spec/models/activity_spec.rb b/spec/models/activity_spec.rb index 1288277e..4a291ea0 100644 --- a/spec/models/activity_spec.rb +++ b/spec/models/activity_spec.rb @@ -37,16 +37,16 @@ it { should == recent_activities } end - + describe "#limit_of_participants" do subject { activity.limit_of_participants } - + context "limit of participants is not set" do it { should == 10 } end end - - + + describe "#full_by" do subject { activity.full_by } @@ -128,7 +128,7 @@ end end - + describe "#full?" do subject { activity.full? } @@ -140,7 +140,7 @@ it { should == true } end - + context "too full" do before do activity.stub(:limit_of_participants).and_return(10) diff --git a/spec/models/event_spec.rb b/spec/models/event_spec.rb index 1e422489..71f5482e 100644 --- a/spec/models/event_spec.rb +++ b/spec/models/event_spec.rb @@ -140,4 +140,4 @@ end -end \ No newline at end of file +end diff --git a/spec/other/factories_spec.rb b/spec/other/factories_spec.rb index 6fb3a9aa..e367b623 100644 --- a/spec/other/factories_spec.rb +++ b/spec/other/factories_spec.rb @@ -7,4 +7,4 @@ it { should be_valid, subject.errors.full_messages.join(", ") } end end -end \ No newline at end of file +end diff --git a/spec/support/helpers/controller_helpers.rb b/spec/support/helpers/controller_helpers.rb index 978230e0..9c56b78b 100644 --- a/spec/support/helpers/controller_helpers.rb +++ b/spec/support/helpers/controller_helpers.rb @@ -13,4 +13,4 @@ def sign_in(user = double('user')) controller.stub :current_user => user end end -end \ No newline at end of file +end From 200b40992c362900f91f0e6fea4c00e986d4b5c8 Mon Sep 17 00:00:00 2001 From: Alex Coles Date: Sun, 9 Feb 2014 10:59:13 +0100 Subject: [PATCH 374/499] Use Ruby 1.9 hash syntax Signed-off-by: Alex Coles --- app/controllers/application_controller.rb | 8 ++++---- app/controllers/registrations_controller.rb | 4 ++-- app/models/event.rb | 2 +- lib/api_responder.rb | 2 +- spec/support/helpers/controller_helpers.rb | 8 ++++---- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 9e837492..30c08c07 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -8,8 +8,8 @@ class ApplicationController < ActionController::Base before_filter :clean_up_session before_filter :authenticate_user! - rescue_from CanCan::AccessDenied, :with => :rescue_access_denied - rescue_from ActiveRecord::RecordNotFound, :with => :rescue_record_not_found + rescue_from CanCan::AccessDenied, with: :rescue_access_denied + rescue_from ActiveRecord::RecordNotFound, with: :rescue_record_not_found def current_event @current_event ||= Event.new @@ -17,7 +17,7 @@ def current_event def not_found if request.xhr? - render :nothing => true, :status => 404 + render nothing: true, status: 404 else raise ActionController::RoutingError.new('Not Found') end @@ -30,7 +30,7 @@ def clean_up_session end def rescue_access_denied - render :nothing => true, :status => 401 + render nothing: true, status: 401 end def rescue_record_not_found diff --git a/app/controllers/registrations_controller.rb b/app/controllers/registrations_controller.rb index c6fe4880..afa0523f 100644 --- a/app/controllers/registrations_controller.rb +++ b/app/controllers/registrations_controller.rb @@ -19,8 +19,8 @@ def update :update_needs_confirmation : :updated set_flash_message :notice, flash_key end - sign_in resource_name, resource, :bypass => true - respond_with resource, :location => after_update_path_for(resource) + sign_in resource_name, resource, bypass: true + respond_with resource, location: after_update_path_for(resource) else clean_up_passwords resource respond_with resource diff --git a/app/models/event.rb b/app/models/event.rb index 1921fc03..63baf8e3 100644 --- a/app/models/event.rb +++ b/app/models/event.rb @@ -77,7 +77,7 @@ def fetch_all_activities end def find_activity(activity_id) - all_activities.where(:id => activity_id).first + all_activities.where(id: activity_id).first end def activity_source diff --git a/lib/api_responder.rb b/lib/api_responder.rb index b8357f0d..cc5e89fc 100644 --- a/lib/api_responder.rb +++ b/lib/api_responder.rb @@ -18,7 +18,7 @@ def api_behavior(error) if get? resource.nil? ? display(resource, status: :not_found) : display(resource) elsif post? - display resource, :status => :created, :location => api_location + display resource, status: :created, location: api_location else head :no_content end diff --git a/spec/support/helpers/controller_helpers.rb b/spec/support/helpers/controller_helpers.rb index 9c56b78b..ef865c36 100644 --- a/spec/support/helpers/controller_helpers.rb +++ b/spec/support/helpers/controller_helpers.rb @@ -6,11 +6,11 @@ def should_authorize(action, subject) def sign_in(user = double('user')) if user.nil? request.env['warden'].stub(:authenticate!). - and_throw(:warden, {:scope => :user}) - controller.stub :current_user => nil + and_throw(:warden, {scope: :user}) + controller.stub current_user: nil else - request.env['warden'].stub :authenticate! => user - controller.stub :current_user => user + request.env['warden'].stub authenticate!: user + controller.stub current_user: user end end end From 0479ff69c09f87da5d774802c9d415c1e380cd04 Mon Sep 17 00:00:00 2001 From: Alex Coles Date: Sun, 9 Feb 2014 11:09:51 +0100 Subject: [PATCH 375/499] Always write eurucamp in lowercase Signed-off-by: Alex Coles --- app/views/partials/_header.html.haml | 2 +- config/application.yml | 6 +++--- config/environments/production.rb | 2 +- config/environments/staging.rb | 2 +- spec/models/event_spec.rb | 4 ++-- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/app/views/partials/_header.html.haml b/app/views/partials/_header.html.haml index 093a1ecd..0f913e32 100644 --- a/app/views/partials/_header.html.haml +++ b/app/views/partials/_header.html.haml @@ -5,4 +5,4 @@ -# the logo, link home %h1 = link_to "/" do - = image_tag "header/logo_small.png", :alt => "Eurucamp Activities" + = image_tag "header/logo_small.png", :alt => "eurucamp Activities" diff --git a/config/application.yml b/config/application.yml index a34d92af..2fc70bf7 100644 --- a/config/application.yml +++ b/config/application.yml @@ -1,6 +1,6 @@ defaults: &defaults event: - name: "Eurucamp 2013" + name: "eurucamp 2013" start_time: 2013-08-10 00:00:00 end_time: 2013-08-19 00:00:00 host: "activities.eurucamp.org" @@ -12,8 +12,8 @@ defaults: &defaults - "pietia@appgrinder.pl" - "florian@polarblau.com" seo: - title: "Eurucamp Activities" - author: "Eurucamp team" + title: "eurucamp Activities" + author: "eurucamp team" description: "List of companion events." development: diff --git a/config/environments/production.rb b/config/environments/production.rb index 733bf2ac..b828d41f 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -81,7 +81,7 @@ config.log_formatter = ::Logger::Formatter.new config.middleware.use ExceptionNotification::Rack, email: { - :email_prefix => "[Eurucamp-activities::Exception] ", + :email_prefix => "[eurucamp-activities::Exception] ", :sender_address => %{"Exception Notifier" <#{Settings.errors.from}>}, :exception_recipients => Settings.errors.to } diff --git a/config/environments/staging.rb b/config/environments/staging.rb index 79321a34..499f8159 100644 --- a/config/environments/staging.rb +++ b/config/environments/staging.rb @@ -82,7 +82,7 @@ config.middleware.insert_before(::ActionDispatch::Static, ::Rack::Robotz, "User-Agent" => "*", "Disallow" => "/") config.middleware.use ExceptionNotification::Rack, email: { - :email_prefix => "[Eurucamp-activities-staging::Exception] ", + :email_prefix => "[eurucamp-activities-staging::Exception] ", :sender_address => %{"Exception Notifier" <#{Settings.errors.from}>}, :exception_recipients => Settings.errors.to } diff --git a/spec/models/event_spec.rb b/spec/models/event_spec.rb index 71f5482e..a275f36e 100644 --- a/spec/models/event_spec.rb +++ b/spec/models/event_spec.rb @@ -4,12 +4,12 @@ let(:start_time) { Date.parse("2012-10-10") } let(:end_time) { Date.parse("2012-12-14") } - subject(:event) { Event.new("Eurucamp", start_time, end_time) } + subject(:event) { Event.new("eurucamp", start_time, end_time) } let(:proxy) { double(:proxy) } describe "#new" do - its(:name) { should == "Eurucamp" } + its(:name) { should == "eurucamp" } its(:start_time) { should == start_time } its(:end_time) { should == end_time } end From 8142f7e4c79fb514dde0d96ace57c07f7fefe4f7 Mon Sep 17 00:00:00 2001 From: Florian Gilcher Date: Sun, 9 Mar 2014 20:30:10 +0100 Subject: [PATCH 376/499] Give this thing a proper README --- README.md | 63 ++++++++++++++++++++++++++++++++++++++++--------- screenshot.png | Bin 0 -> 325636 bytes 2 files changed, 52 insertions(+), 11 deletions(-) create mode 100644 screenshot.png diff --git a/README.md b/README.md index c5c2624f..481d3311 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,51 @@ -# eurucamp Activities [2013+] +# eurucamp Activities + +The eurucamp activities app is a small application to allow attendees to organize and plan small event in and around a conference. Users create events or sign up to created ones. Signup works through Github or Twitter. + +The app is mobile friendly and easy to run on a free Heroku account. + +## Donationware + +The app was created by members on the Ruby Berlin e.V. on their free time as a community effort for the eurucamp conference. Ruby Berlin is the body behind RailsGirls Berlin and Hamburg, eurucamp and JRubyConf.eu. + +If you end up using the app, please get in [contact](mailto:info@eurucamp.org) so that we know. Also, spread the word about our [projects](http://rubyberlin.org). Also, consider [donating](https://www.betterplace.org/en/organisations/ruby-berlin/), especially, if you run a commercial conference. We are a registered non-profit, donations are tax deducible. Betterplace handles all paperwork - if in doubt, send us a mail. + +If you cannot or don't want to donate - use it, it's free. + +## Logo + +Don't use the eurucamp logo for your instance to avoid confusion. + +## Examples + +An instance of the app can be seen running at the [eurucamp activities page](http://activities.eurucamp.org). + +Screenshot: ![The activities app](screenshot.png) + +## Running the app on Heroku + +Deploying the app is simple: + +* Clone this repository: `https://github.com/eurucamp/eurucamp-activities-2013/` + +* An account and a created application at Heroku. +* A registered twitter application. Go [here](https://apps.twitter.com/). +* A registered github application. Go [here](https://github.com/settings/applications). +* Add their keys to your Heroku app as described [here](https://devcenter.heroku.com/articles/config-vars). + +* Push the repository to Heroku: `git push git@heroku.com:.git` + +### **ENV** variables used: + +* `GITHUB_KEY`: Your github application key. +* `GITHUB_SECRET`: Your github application secret. +* `TWITTER_KEY`: Your twitter application key. +* `TWITTER_SECRET`: Your twitter application secret. ## Development +An install postgresql instance and a compiler is needed. + ### Basic setup * `echo 'activities.dev localhost' > /etc/hosts` @@ -15,22 +59,19 @@ ### Customization -*(work in progress)* +Customization is currently work in process, so the way to go is to fork the application. * Fork it * edit `config/application.yml` * edit `app/assets/stylesheets/_settings.sass` -## Deployment +## Authors -### How to deploy app? +This app was created by: -* `bundle exec rake staging deploy` -* `bundle exec rake production deploy` +* [Florian Plank](https://twitter.com/polarblau) +* [Piotr Gęga](https://twitter.com/piotrgega) -### **ENV** variables: +## License -* `GITHUB_KEY` -* `GITHUB_SECRET` -* `TWITTER_KEY` -* `TWITTER_SECRET` +GNU-AGPL-3.0, see COPYRIGHT for details. diff --git a/screenshot.png b/screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..188113d00e31f96737bb873f176031c33c7e3345 GIT binary patch literal 325636 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 z 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 z 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%0DBb( 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

D@s_+BIIm8^N(&Ie0u`BBvR`cfS4`SEvT&KRKK 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

qME^VjkdnUHJ&}{1p0+k5k;vL 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 eF^#t5EJkhrrE_} z6?+cc&m2ym7P5*-SeCO}MG|IGsVRsLrBVsH1m0r?^B5~KU?t)~X}}qawCBfUz)@9bP4L`Nmj?m zlzJGEfWP$nlFRLYzk_*+&*Ml<#m$(GP)B$-;3q1Dg<>;7SlK!@hmPu zN%F&@3iz?q)mn`04Q9d%8*E@UbE3g-_6B<_6i9Y^%t-W z7z*fM4>psG_WHSJ^!&a%{>7$$@b)TjeQR-hpZsv|-jOrX4|iz#yYAl#;BQh_Yq!qU zu^a6{Q#iYR#i-mv@6UT$BgChl{p>IN-QRv!>PR1L{)|*az$joadOf?ir$n4h1Yot=rtVtB}3mD}9YL)(cPhZ5XqH0kixHa6DRHr6*bbD1nbi?mjP z!}C77L;xmt>F_@Hi=RDt>di`}!b;R#+T^|6%)9TNySF>gx2{(}uYg{GdsYEji>B59 zG#TB3bz-p5S`;#!R;BAO2LT$GR5YV`H7QguP0}=3V;{P*i9A( z$jJE%t0-NZZs;MT@Q$!~O{Ss_rx%BjUK@imiQ6HuDJCCwx3*HL=yrQT;Q)XIS{Rob z9S5L>46y=2$pjQIVGx5K8WyU+D-czrW`9C+KyG9l4b=Wvu2kzx(cNBV?@f3N_>T(a zOwikDO8`Mwya}d8ANN+-ME!9@9h(MyPy{C7W55U)CESz&IUtt655fpd@p-XdVVf9$ zz@8ZORas2a70l!L1ZV=8zT9>B)I{K85XPrQBZx*k8E1%(Z*46vZ>?O(t}bUQ)ov-@ zDCat--&{^4!V633)O6J6b#OZ1ZMK&19SM+Nuco(`A=90tTi;;~&M zS^GI1BO7rx7}%N<2`WaY?>J!y(ZIna0fqy{84g93A}AZHUZ|>|fRxKE&>K>U0s%j( z7l?)7>L8N=E@J;Y{DBB6EnWe3LBmC+4-A`LSNPfjNvN88<#J|%knkd>1j!O=Yhio~ z2P73waCyT_!M?{1mj{`U$J_7O#+tEKYfx8XGHhvesc?S)rwO1BQjol~YkFZ}ZZTV) zo!!hA%9Tn>YW1<~u^K%F)!YFlk-NZiTxlbxhV7lu4naS`md1oJjly8)LP-|_3#05N zv+dHQt$X%EuWwkdz{gzy9Xk2AE3WU43Jk}?rbd%eznMoY@39(eZpGWl4XmS`d9xOW~(0!J-JqZ_h1yhrUS@eVY;1p?Tr(B$-RrlE*gnUO;3TROr@uj zad?<9zt4*!EVs04n|R=#a0|z|gFd5FDi?}{t?f*X|MK}-wbt!)1Ou27fR#-C7607T ziqI>tz2E-gZyh>x;EfX}cM5L8m`gNqt=1iT+}zbF>KoH5pjSYz!0lB4#H6h;zicSC zh$S|P)usD`iF2SC_g_K|sf7pi=h}3O!2WeD$Q`5dh6RQr6bi;RVdjrkjl>a6c>+yeejA=SRa) zyV+?fQY@=C`|FK#v)Pu~BTPmwUR)~` z>tvo3bJ|EAudU@1$++JWHK>fL;UcE=Ml<{cX8bHAVC#-}hhboH z0q+JU3#%<~xwOt3@xs{H*jU;%6N$z|TCi|uF;=Tpyh2Ie@wmLbp3?7=6YL71SaG02 zg5(1GMxr6XGqqCdt*>2z@{mT267;#TacQ)DfKMI=j0UoaF&u>sLwCbnTHp@~iMbZ0 z{Zx~LVnIT&n7bq5a-2cGA9x~{t7f*#@pvei3PJj_V7KwB`5ebAmLs{~8?8lDnbvYB^u-9tq4hP+- zDWo?p=pSU`1cHfrt%b=5kc!nh_WE7zmPj#XN6|zenh4^X!&&R~NAO2_gAN(K4yS|b z2SqlkIFMr^p_tU}R?98ki7v+Pm+N&2dmVq!3%WGwbXg&T62t-6!984Vo>0s|f5?Tw zuF2o&bqGJ|yWK{Y$6;wAKrGj6q8+nZIiHt=YG2yrJt zJyk4K0mV335TzLe5S!D5Qrt3Ui^P*&b_WcI!|wWOp;04^%2cb4#tQz$1~P*b1xXjO zAZ%-mB-?xLC`P=(Vq`&)tu7D1mJkY22emyv7eXI+y zI&`b0A)`%e8jT&x&SjmT^37)RU;N^4I$iGn{g3~TY@sNY9`5QxGz4sL@()W?)oLBD z{NAg|6Q%td&S{@_uxUJz>>@F{Pu5s;R~Pt&i7uTv$zL4 zxrduvU0Z+T;iLC(2l}S<3g{KkD{yZraJ98)X}4sZ7;S3H+^eXbk%(^L*5UCwN!4L> zoP~ZPq7o*2HcVE@MsX=-rPZ$a0*{-nDB9?ntv$@%g4P z*)$p9BG`azh_=b)3OI=%<|z~kPd@o%uiLK_8{F9lmvKNRaiDn_i=-9f7()tXt-^c4 zWQj+Ek&v&C+N3`Kf{4eQrBW+jXdF4Z$lh>}<&ejkwcqbvys+%^1Qr(-4CB$*Fp}ED z;&c!K&CboZJkG7Hjc#AS=vK2G+YW3uSb!1nDFQkf12b7I4vX2r)>MTZxsZ6s@&Kz5 z;304#Znwye7zUH`Iv=-Ltlg1HP;O5ZZl$UbZB(PtF67y%v0N?=2gW^1=vE>G!8GfH z>2A->r)TDpU=V->Sj^Mo5N}M<3lbQZAhH740oN22f=|8PWG`rr3i!blw<_Q6R91j=G@gG{yT5_RK2LxQadhXERe04SF)liCvq1h6meNCS%%#}JO+ z0H3DUhsR-sHcR8?5>dq136nDoz|_ZLMp4X!Yh7$MQUSG8`-lj%_R!02>TL)Nrl>jFUf$7YGcy1aZwRzK2jdeESAE1_Dml- zJe$i_*4K))T7PrBn90^UAUx#vj8z~WfD$BH3|GK;EYulDm4=xG*$1zhF-M9JX?1n` zk%#x)o1c4qt9k`K)(Ys*$;Vo5eP>rIFw#cS7!#@%QGz$CiOpaeRTQ&iHo)23nGc*t zGubTnmB)`i`Y-?G|A>{wtFOMsZSt-IDA$!ofCJY60$vvlRDS^Z;_~H{tNhigmL*z`d`)Rn|#+ z+`I+r1SeCaVX_WvJ*hWt_Y4MCG8rSDq~2&n<1v5V~;=p)rZl+nUgu=eZ9)DO? z`Vbf!wPttF;+_hZgS#VlCupAz+ioVtuo5cOGO;z>JX6UCx)|=Ld-g89dGcLW_;>G} zLHGmCP^&gbfWhj*Za3qv$#xcI({{T$8VyoYF)UoCXXcvCI;0P*RzMj5EcWc#i)7-= zJEv3e^mCthrq!-8C1yDvID&*@_#G_rgZoTuL+vJIXs~EdH2_jjl-R6zXCMP{)@o9v z+SE>jrvMup-wqK~OmT2_XQHm|Gm^J#P<)oKBVhnun6h5ZMx1#!Hi zl}lGNqne;xHoL0I%|;8S52zC!{AJYv2o!fckJr(W5P*Qrpfh4ogKcWae=3!BxwOt* zI}q@tXOhh4`5u9`c87ca-b3h{&RtkT{gg~bxyf&AUP+{q;3K^rF-TUA*Ta_0Hk&~; zkSN47ADda!ClxQS2mriT4dFArv>>vrY zhJRfoj2_B^jSHX=m_Q^HDHKXDK3I+-`WHmWVU-|vtgUZMj$NcCu}Fnnm?F@QwYlnq zV8;nMMzh9}kD|#=vx`!wLv%VEuH6ZeG8)q3(0air85;!1!qH<=OD_cFgn(yS3%rCC zWmv6}qim*W!y6W>5mKdLsrdYEqIFs=uq`^OL`nbvKmbWZK~xGT3{&DUo5$@UJ_%1_ zm)F)CwnKm{CSmD<8q4Eyvhala8~rJGhu;SV279QvwOQESDtD#%{Rie}W)q3205qOj zt+Tq4TfLH7TQ8AC&57VVw2Yh!l9jOELoTK8Q@~*Yn$XA5)zOPBujtUpz5flU@8M&q zfDWB}EM?Yra$N;L4$$dHTDxoLaR0hGy}j=H?>)7dY*L=Ln2pL^o~3_qU-#%0ZWx7P zkre~5hrN6E{15;AfA`umPk-er|K-x`tdN zdF?8xlBe&(4BYkT9qCoL?INre{`M?~;h~2E9$*mv(6_OaL<7Vhx-?$AwsmkB4w<)z zSH`S~(R2;32Xv?jvTCjbfD&BAxaES9OddlV&XCT+4W`SaIisIItxUOof{ci5PX3$h z?O<5k zpjSYzz~8R`ViCgo+8V1eZH2Y;n>=hro7^(aN9G2~*q(4fz2pYBv9>vqSvl8yK}aRO zL?RjvNATdBU)U9oMZWd7KUiFxkHlijmonK*Zhn3NX$rwQ_-;J((2-m=j~_U|2R9=m zvxDAHYO9Bj>>~b_L=~3aiS=cXf`DBXHT&9%+# zOd^>8+^N^9-~ZkZ)9LBHG?c9hD<}lbz#te6nHAd}I9~M09JKZLV z6`zl;z{F(UHg570slTjOV24#ehfa3b33~vBtBe&giM!)YZ`@UH-rraZ)_SIC>RAkM zM0e(cdmj3cO0|l6K1Tr4^0S}$Gz^rNU-|Jjzxg-I04OF_ite(6=eA094R^Tq2G+YB*uK(2SKL6mZ1^n^;-}kpBxHRv*{QgV#!~aDENHMhApLyn~ z-}u7k_w3$1VdaRSvEWuH(0jqF`e5bmhq2#y46sga{0;q8y#jg#^a|X^6_CgB&@hxV zscY!og3t=2U>Q0CR!_|&vDjWL7V)8AYK>VKGzwn`e8aK0G*>IN|LUvXOiraHN|3^E zFrHwoo^-+Sr1Cwd*G)2vij-Y}nk`Q zM#huTG;BVcoF%)JJY%!d?MLR~K%X*_fkjZ?D6)wFA}Ay<9%km^)FYGG3IwB|13(FI zG#YJ=Xv~QQw`#PS;+^~*8!?Mv5sQ3cSUcb;c8k#yvOf0Mu7@6)uT@(c8`*Q`F6Z)X z{9cxqGsQw}G!koGs&Z=H2_|(zDQ0{2rgHsX^_j+nwgQg|u;O1v(*y26O@- z#D>kQ$>i~dBhlc5T}wvK0L(7nAX*u8V_Ysb=mxQs1vY_N!sWsHhw^}kW`6*3u+wIj z%x0nV!Hx!$2>K7blg(~*d)&C{BDIlPd{>1GWwkjlxB;Ufg~>VVfKOyH;fXV<469tU zh;(2S+if;^NDy_)X?L(IEKG!5i`hU-PrugZy&_H2J*l zfQ(F}kw2iLzV;%5Xq~al7r(7EFZ4ZjS2!3L_It=raeA0a zr@ek(AmD*D(P?$wI(7Q-<4**Fo?ys3ola(R87@)ggV>S6U#OG_XRG7%iTen%SoYS`jHg<|ymtKm{y$%R`Fp1{)oQnS@bh9qZ?oDF z*PtoMXRBN51#)U*(I~TIz!zcQ3Tpx!MUkO|zo$OvjOjz9R*|!(c1pJB7sADl}4x5Dywwv8t7Re88p~UM7oeqK%5_zyV5k|78 zlE@)8qJF|vcZh`tf?r0+>yzoUS;Xha&|#u*6A-N+!9k5e3Nq+v5ecT9&iBA0BO6p&q`eh#gNPC?e%4(Pv~*b)L3kIgn&{xynZjB69OM- ztUj*~3I@jxC4?buXUhOOWB@x|E)l1zkYqeEYJ}t9K|_)7owWsm?x}QaZZ;JThg2|{ zY7+xmm_Cej#bLbtJ#f6AlI8*U-vNB*=_h=64+!2SLp7 zM3iy*NdFDGV{sqc6Oc77Fv0J^0Y|8oK~I1i>>i(+3j&%CrmK)xS)>60vx)E;E3^|f z!l6SRVecrvI7ZMDp1_phJ)Cm9b-5-f2$@G5BVXbZT&tpTCJJiA2}Pj;EM>Jj;N4)J zLWHp@gW3%EoCzryQZE8^yv}GWgli8ePDC=3pw#WkTU_T712do;Lcr|{xLt4)hry9hrb^#!nk$Gtb||nQ4vsY(6s-?GeB`H&6oTl| z;=*&!J#+l{V=O3;gUD@$TjLHs)So)S2lPu$5r_$5q}@xqKL7b&{TKi2pM36fFTyHf z`RJ#P_{Xol_V(MScie=K#_cmNe41#(pE?}i%)u_n>`P7NiW7UWT z6VnQ43vQ2{$uLvv(Lh0JiX?|nUV~-=6T#_YW+uh&|6XqZaia&6pvfp$nZ#l#(jPE6 z_Zmzx{t_m$#pQHQyg|(~(=*LReK?eurU!%ZO1b^YD<^Rp+1jjZZ(=fnFxGSN!p7Tg zU&`kjg<{no4B+C4A_i;YiF9fV`2{(Z z+!;-!IM~y2TwE}bFj5|mnf`Wc66x1oXSd4;O{5BNA`#=DltzrisE|faa-hYFU|L+L zU_E@>YO#-o8ax`3p`neSzM&ujG%=3tKr&_%)+bbqZGxO1XGxrFws0}BSsNRs-H zi6+p`U}H0rFI4bq0p$sV1AMnXK+1z+Or|nGH+J|YUZE80^n3YI*@)nX#1`iweK7#r5UpjMyy)N#!8KH`tL{&BqmKeGbYw-!J1 z&3Ety9Xh##qUj&`NEO(W*K*qSdHHljyXv#?(_6C{?OvlVC`GG3>X>U2zl@)L%Z~XP zr`Ri3u2ieF-Me=Q+xfr$fRjS%&7+SVd*RccI{45*Sd{H{n`Hz+&;9%F)W7;UR^TdJ z!oMhZ=jUf%{LBk~^hdw{r7wN)(PIy@7IQt**k?Ir(&1E8cKCxSY^h1f+QF6|<@4VMTv?yI{~P z?N;CEvZhiIsa3~oLmmy8;PGI-OTLM$j)0O$ashtWzkeT28H3@#X7?iT8OnWTyo*Z< zd-v}`^ugi>AtuDA;M~Ea8jFWuFo+X})h<98X5wa(E4Niyy|NLDh0THjBDb6U?aks3 zzkl-NYvWQTY!Wj}_ndzgZL4GnqHc~1`7vJ4FdGc-YViJi2LJ`0c=KT2J;`wSATkJNc z+iSBs#n@tzSj6Y{xyFwinfd(ZpLy}cW3y>rEaJ5?NR`9wTTPBu4a5Zcf!kocmV$ra}=ESk2R%-zJ_--rSH8dj|{;;E=R zF@w}H+eG|;Czwg?p#|gpoI3{0L%6t^!N~0C>F`5G7LPu>J2e%?B&6A>)+!C*Yt!ls zdbrvvgbJ^%X4Y>Z7{>kPw-{9yezhkPDp*iPxkZwOSI+yFz?4BtyXjT!y2q3+@W`up~DF z1RAlP@gjRh_N9rdVnjp{FbWqiKn)BQade82F%uUJN@6nG3dM3RUjmsyhU4}7?G9(X zDm7}IE6bU!jUt!4-C>0-<@At(?|&& zckiATX0!$%umPv`kz)tvW`%lPf9Mssrxm!qwYV2M(xH=kQB{4Lx2nKp`9e|4T~f~1 zw8{F^ z^2x_v{Ok)yj~>MWo!dSO4#XDfFv?Gr>*M)Sz$HR$EI6{P_#Jxa;4gjd#XtPR-}}-R zf9t8Io=7GVbThXY3GMP<{KZ#F<+A8wcG}~>f&B*$?B8jl*2lZ^DsX*&qeCY<@BH0o zBVJXO^J?~@a;~bCZ#Xnsi;yMaDEpfy4RT2cgTX<#`F0R*bX%Qvi-0+Z8^9(mmz~TM zXbVi_=VsG<$r60IT*(*Uv!H$%XLChz=bXIF<$UM#JAeXqhbtZjp0MHWN>C27WhdBz z+1TyN7>yvBn4MVyqdD`=`C7H^av)XHavP1ePp$mu;bvV$HGDm|P;df7~A zW%){1>cpbKsj2Y7Qk)sCJd~R??O*?2e_6=3;E>=LiV6d%1uHEKfVnG@3V0UiSYU9B z2Aa$115h9`=H!Xq(#M9WEKqtft z;ysU}t;Z9@`Ss+9Gs;Mf#^N@sFlXJ~%8xZOrX;{9&6Wg$u(G-iY6j*7N(2T5(t(bO zC#+`i`-&@MVSZ+5aVFq*VK8H+{OSm*Cf5YnIyQ@o#AQ53%9Zx=<#n8HO66*$QU!CE zo|*=Op=fAZ2rZ@?M0C??wfH2cO0VC+Yz5;LQkh6E0v#z=FxHiU@c0zq2Ld945zNJo zu?`3Y+1P*{NMOJ(CMTVLg{#^GydWZ4^c|CtqXn}ECRQopS}_r03@b|IGAoyx6q8*5 ztO%zZi*0N`x)t<#op4@IoYksLr`?Nt(1o*?L3t9%q*(w`BesC!Z?7w7wo9b#K&_^F zs;u(vWbHMM`=;thW`^%wOD+{Fr9-&)+o z&0gD-4xL;ph5qDYs=ztE~Nnh4rB%d?spa6*5+hj2kTiwbx}xm4GxHynx6 zXxJY#&A#=TkW`hFa@>XwTTqC_!?$V!2o!_QzY>`S>JWjZ|Y~K1?yWs$+>L^HPF!ibY|JLNXoV ziBx<#yIFkmjm*t(}+4QRSHKRKCrZiCUo3x4u1I3 znIFA$cC4CV7@(BVpe$f&VPv9A+K&d?g`^em2#6j`>7gKiGITpyHq&_X)Y;XwwP-w& zOe6t5geQo3Y#{|t!{*%Pc7XXnuCW0aF+~T~5q>A;vBzsorvpciE)f{CxRi1@8J3Ln zrqdq$_{XP*qrrhg^K%Ok(sV~q8;rK$NG6Gy2Bp<%2tdbZKs!^d)Fo+v{{>09SZugm z4mdIt6O;-Y6sgt2Y=k*Kc|!mY4s2%~-bgeIY5;Qt#x86Sx7!1B#SC8P<+ZNUplQ5k@K~WXSR1T&}Hea=sw5pg>{EIIWZ` zbp$TJUgX!{oC4g_At44AT)F@}TwD%f2!YScX2%$qKr=H!qIod1^NCgCPQ$%aYTfDn?EmAQ6`e6eKiIm?We984T4}$p>qqzjALY@Yd)=bzI(1 zR-0P=hI{gwd@1vE=h8sBFf>WGTHC+j$M3fn5xYL~%u|qfE0qchAg{fC^2EuL+*{Ck z+`>_U+hHUUX8r$@&p-Fs7eDjN(@*VMTEdi+j)OH248fS=U-d@e7JI)P@{Smf07`hg zkCnLH?y2diV-FuikMpJ9{;l8p-QWJzU-{+Z#~)3n(|k)*`*t3uPQCT|>u<1Jb~_)s zU)~!Ig+Bf1Pu(x0(Z})NR^a;nMu$!w-1Gb~9ogH%lc<2#m6gU#S__ZSo3u=oPY(*O z4V9{}QG?V#qzkSk!=8ptVtr$Cb#*mg$fc$dxQdo5WjrJB6Cuo%mGoq4nmH~re8Owe zGgDv_$S82!bh~^&1Bge6(E*accI3#B1G3BvwC8j?WOV?h;CA}(mQBP%>C_Ztgi9Az zaX+nAn&@-;y&m?W_?`+o64(t!Ca7l66RbnbMk~QITbtWcscEk#kl8GK{VOjIy4Z`L z%Q;ms=m9?xUU8Qp5h@=q$r9OX-hIC%a(k59G=*X zjg6hyjhX#pA~s@=h>h6T4ad&fUfbiRUm%o=?@BO~-`}_W!Iy@Q+Y?Q9sVx=C2JdP6*q63$N z!o(%@-+TX#J$tvHMGGG%pKoR}87$D>KS(CNYN-xXG&qzXp-;D~1AG{ADy%iQbJ;Qh zEWjQl+JVX;d4ao~cr4r(kAf6nSP&c(N+7c|>I%Xi{1i$#6jcFY5Htyr_t@c-V4)Ui zdVQq(^?QBQTBVT7fI%Rv+d~aT$A`U=VHZ<$z;XQku!eYwq6oU;f>?x-YZ-J2)9u;4 z9exn_7myZfB-p*K)PaHe`eTUqkB^TdeFEsf+(6aZw5?b`0R(XmTdNHo0aQV1p$2?Z zrwdsS-=4jKq1<)Xu94A1I3f}#`D_U_7}`v)p<+w=; z9UV;!5B0m;j$ptG6v84I&_he@(yd6w8pnZA5Zpz})yXp%i-m#ld|r35p9DA|rhZUY zFf|3Z1pHN^56Zp|ILdp{_vlF@WTA;!u0g)l`FG<^*7&n z;NE*a^Y~{D9M~U?L~hg+oOa@OBH?|L`}a|XSppEu($X?3NHa6D3k#`qdc9ng#Cr^K zM*iO3%jSy{xse5K$#G+@z*JdaCK1$=T+u;T!Av4|07^l?9UV&~`dGcWC6~K!@gfU5 zx1_3%zmnCp4^|5J_*-IL(X@bR0n-8>(E^oDsotsSmK#nj99DR>Ev@CThZzb2 z{@}>)@bdDCu66uANF^Wz)@-JF>B9W_S`L2|jsTwFjvW)CD4H)d8kI;4B%PR=3>*sJ zl1jPK?b!Na)aFPeBb91tDVMKTYM*}W(9S)He7=;;)(V+QDZl2&7q+|SXO_;Kx>(Mm z0Mb^}nkahsp?$bhN&sNJCyvSBt zr6i#q#!x04703e&ZV1KN5N<^bl`OkmBu#8uv+8t`mIdVwqLcjjbn83cdhz&?Ghh1h z)6ahK$-ZQ$+pc%IB=I3k#(}mA34AS9`~oV&{pJlb3up;ctAcV6n6qOi_(K2v52&Ad zxODW$sS6h_o;`7ydq+)W`>vs6+-0?TxfM_kRZ|-c^jSSP=)n+abK8MH5|#uZMI8@p z1+vMsM#doV1WBv|;zSH=E|;m*@xa?QN0;<2%!`JGMYcMiMA!NUJVR~}^BqS6d zn}Os6>LI{Y(OBQaww?8cvc68{mvSsZkPB?(p2}~NAfDyiI*O;LhX9B9{9sfLLZ6sf z5rU+(+5lvIiP*%By)K81w>Fzjf1 zi`j(Um+&PLd-hLm1s^$o_VR15p5Tts8Mrp&lDU}OE(~y8k1YDaM@SN6a z25kbDpvXf)8@tn{>BM^1f#H~epnig2uFDCnCeUaqtLYrPBs7D0Y`S(^9jTOh%h|G4 zDh)_S=$jCBR;kiK4H8?10)DHtQ?BHLfiNhHu677(TS?`yE$OHTt>TXQlR?yKQH=o} zDwnDbU@Es0W)q-KsZz2xt>8SZmMb1j#$xfMh1B};+^6n)_#^%~o5f5EY*-7J(8-4F znme{(BqwfKl(r1aAH18P>h{gTkOE3TMQ13>7W`x*omEvmhQIAh_j>SzQan4P?#)LV;llQZT=;gy8scDsN3ChC`OB66^MC*A zmtQ*kwO@bo@z3seyA`5{jB%48lW7A%L%lTyn!*v$&ETl53z>x#nvSL0V&pt@u>YfhMX(r(727Qjv@qSGwDX&{?d6b3#PU&?tzwCsNG<4)wP>6A(g%Kp> zkVE(Si1ZuFL}+qyr#}E@ zqFZf*%yh{u-BN4d6tq0(b3OdX{YmB~R@-W=szW(K&BWyl1JEG6lgla66TnM03xQzh zjqsO*el62K9waUujk-cnKPipqCj@#8NfbLNpD2Jzt2-2QxEvPYm5`Iz-nO(vqbCv< z=@ToGD_z|dL^ZaQhjvnF*6JO-NgPkNz0O|;nUZ-fc?47dEwC<(!8PSA6I<0oDyEJ_ zfpLXmZA&A}2x*=@`*&>JGe-Ownm4*$^thco$^=7!E1f&Na+|*c%<4@Gd>j@qp_7lp z%H}oRZ2?#(O1IIuF?7PxkVAL2s=A|d>)qOSx8&p5XY^QJSw&$EjaXJuo__kN#~*)e zc6R>t*WY;S@H>l(izt(Q|M?$$|M?eK;@G`w*FEuE|#wlrj8duEQuWYccV-H2 z-x?)bNbX`ia_s!jV1fY`4_t4wuPiK_J9{y`mT%N?KHJE4FjNtngV2V6 z5`ZOb_zo7E0~$j|lvZWw11%C_X-HNQ0FHm*2X8Dcq{#$JRvX-C#7#wxgIF33HB+n0 zW_41P#Y)E7HbF5CcqkNVS;2lfHr!emFG7@nI?`;qo!(Z*cKpcf%=FURukHWs-~0TI z-AO*8KJ+QbDa3w<47#E}3lP&G6sUvp3I(hVltE?4MX#f)qI}WckEH9q`ybeWuI>7I zMhGviR%U%QmC8&`-tBWbN_sUJ^Dn0he!r*LAlg_Zhyw+VOH-F85A1?F!_twZT_B?{ zF(4@SGGB6g9HD5CX-vmzXU!uR3}@03^wDnTw{9JU3IRIQ)~p0QMIv!UCA}}Hj`Nz< zU9m4~T6K67N!`G}fQuG)Ed=AJgsXIIZLo-9sk*kBar+$GcaDZbaYxg>wzdX#bKv0a zW{q^qzD`?94h~^UwV_2q5%NK+XmwPYP<;Um7DH*D*8$3K=$@VISR`$bpej&2U;@EH zs@94aof+BK*q)s`#{f_=>GHxtn#?<|r7GWSoA~8jU8dC>by#6<9?wA(Xj21AVlg-#`o4d`hPHq~o(*a~xWys## zw|cGhO+}fVn*%!O9l(L#yLUG~zww*jn4X?Ka^(2YW5-zK$mI%eJ36FRv)rZsCdEnr&U zwpc*1kRr9&hOcphja3;}$K6#r7QN{TwZc(ra$vw=^Nfu4C6k#q-#FT8qZy%x!ePWj z@d|NDTUtkN+X9}x^h)XM>C0%xj*JgD!_Gj^*3qj?O%M13wOXCj#gS;NR&OjWtpHJw zQFmYuB9@?>cz{uFrjzvB3C`%YEpIeUx$@?5?D&10DrDtH( z6!T?tPl@fAoz6;N4ef*If}x2}>RRLRKCmU5qeH3}x`41062vHqGC8>qsT8s)qr=*S z7gVWLDhhJ2Xu*&UhNR2rZVZhKqPI$13C0BTLF8pUQw#H7 zE7K|Ply=vpt8S&{o}VrS{55~Tcjnm4Oki>Mo^A0&#N*zAT@(FLLc*YQjBgnrU}tT$ zNbcW2IDpuRqU(y@hDjT;I{}!0R|4@+FcxWR9alI4!qvfobXC{Z(Aut0P=kIGi$ZE@ zwIPhO6vStW>zUQ{N;a?6>SSiJvzAHE0@%NNu*#ynnO0~+oq(Y!@xy*hA(r2~KyE?E6b3LfCjNse`lM$8dbeyCc z!PtzWNc{Jzl!?`~J$_$%bM+Ww?$teh8T;~{C~wc3v(-xsDEX7ec!%`M;2T2oHE5m zrQWEhT0?#Nl@pn@EbBmobl{DKf?@K(G8}?t;A=wMU^HcAo^g-WdsR_DOziL&AXXT> zMPLQgJ0%AYX?1L5%n8=&b)Xi8U^oS^Ot`v)lw&w|d#r=Qv4P<*@-cn=f%&=8%P*Zicj}Ta2(iUw zw-bKjD;28bo$>oa^^V5Wf*XeJK=VNmATb0(K@?263*<|eQ_G+#?T%KfRSdmSav0nB zhzUa?5y@tXye>9YsUZ}K&^)44pgIs-g8L6p(n{yc6`jBT1Pi_k|-KwEQW9&)=*!SJ(3OT5V)(;NZT!B!MoMYv<2jhAI+` z`SOKKFcc=#D_1J)-@gkXS9F2vwN}s{ak%X*z!`*x1BqtD_a`3X`5dJP|_YC0L0Lwu>c@ zy$p`mGAQVY4@?YH_WYozK)V zYg#?sRy8})!tjv6-N1+FUuZ^jF=vBg@hXrBj}=*UB(X8nD3MoTA_$;GVq=a9GsS?^ zj7OpO05H2F9d)V9bJ{>b#gJR^GI4{-Iy@c)6}(o~{B2rbvs%D}PBv@LZQgb>zxnW` zAz5lstp;?mdGGQkT25+VQm)*4@7)LQI`CtF;ax?THf`HB!Os_e>DgMXv9z>s@zUk< z=P%66%&uoLt1IgS-MsbIJFFi;Cno-O+t#g!n{M4YF*-7Y___BZg!a7aYCo~dq>weX z56SgIX$DbNw^%Y`O2M)c%uN;?gs4d}JxvvTJLYSE1qNC|VWxE~Nw9PMO3VgbFiWrH z!Q(B$cue5P1<1X#RK^G^(;>pKnFTU$G%%Ixr*EPipr^~1uQ1!&L~VY5QxTkd|3UMJ zX#vv$rUf>v1yoD3Wzjx_b%G!0wL8a$5+j|Yn(dIew$^QB)5{i{Js9y%9@sXye@`Y| z#gU$$PrJM}w@TJt@6^-mou5Q^u=>iC}^Og$)W@KT+q=FZ(71$P(`%^jsO^lw~96?gC;R2ZNLz_ z7YPs>$zoLoL=GFl5Ee$+fuZo8T|17QIMrMKXYl|vR=wMj=nL)*T!dVpFHv8zURLB=%GVf9e^r| zq*Jll-3X-!38m`*@G(SWdzp6(pWV==XO6&K2#5k&&?S;cC#;5)JwwC3WHOda1h;NY zUYTJoqokJd#Zt3aF2|E$@+%_~Vni*8-wwxwOu+u2ALm?Y_&i9H>RP2-#+?t4peX3W zT*YD$Y1mvQ6Y~2Z^R_!WQo693g5K}SXs(7>#e1Q0;dpY17kw(IBJCIL|C5jqlB8v7h>@~Y&JVL6A8s< zXP4L3vZO#xB!~S$`&uTQTF!Z#L;~f>^qkACj*SjSBVmd65$O}M^I#nmh1cP5B;p8+ z1PQmA*p`?)xaF(??s8^Ml^tMb(oW>eIVgQPn(NJ7OaB z2KCuG?oita&nVFGYCIb}L_9juH#AWqtq9L@C;)PjJe#N}Kn)A2klSiUtF}t{%KB0& zpGuL&+1XWm&bG@d;x`h<1<6U|bpT8ZVH=|pg#%>3BJ!}c(OA)eo{T(45~2eXbA5TN zw~9uw-sD!W8lX3TmX@x704aL2uF}Yft;1tu^JnOd;2?j*^M%k8r`;)CMHM#8~ju>z>ElF1>EQK@DL0=?_dXe{B)Wov6InR1b= zu8>n|m2v^}1Zo4pCWPKxIJ=xlH|%cP;P4pE;c`Ax zkR&*0rgjkK^ns8x6+B)jmLeAd%7C+p6UsjLNDHY5uH;6#f|UekQxR28RuTA=Spl#D z!~O0(I|c?4=$3k298my7P@7<#!D+L4-Q>*-{)Ne}JooU(a6oT0oj#ZaR7tWS zd}AIffqH-@V8a;CnE2W32*X0SC~QKmA$1HbXP*ShJ9hW)*gaS$_w#IKYHI!T>G>c1 z@SRFAn_u_s*|(icI`vu;l7ijqU;zf9mx@XrUp){Y(~YM;iBK!5x^1F!i89XRK~f}j z^FU&tFYa%5R>>a>=0s*(FdB#5f_!hSRwZ%08_C-juK*saCeFE0z3iHlOzH>A2q$6E)|!uxzt*%m~nJ#5ntPBvp4`w z3HTxjn&@mgjwgf-@T5x_jR+-p$&DJzWpYSlO?uFY@j%eiy(hzX+q+PX;g|I_z>@bas#-*?|VPk;XNcON?N;R+^1T{KEX)-27r{S(M~cD=Vw3M~)t6_R^!Jp!k6>>gee3(9rPc=uo0Bfk-O(>uxQc zatC$It-O^}r%pp^y_Ge75_MIp)lcHG<~2$$f}VHq%ZDz_@M{TWjKif#dNBWch^XwqP8JvI$W&M13UOi zk_-s zAq6a~793ZqvROc5fHsKPF%(_xkP&dM{gF5`Dot;ZokwdbE?5cZ(y~O=(L-LBswsA> z$LRr=a1`^+Z-49PTd!Ps?kf*I^NaU-LhfQYOZuT;m>P6)_4Mcl!Y$}+;xJ3#2Jegl zh>Rj(;*wyZ-f<#>EKUfslaphUljFbe?A^~id+3eV&s56#z<^h6sZeI3;l8HYCFL$K z4=y)h!azmh`?tV5yg(1jsa2q>;o&gaGXP$w@506fF&P{lMc%Al2a^)h3!VXs2p)^xX*QYyZ_p(`b>zqHz_MZ1Oz*>ik|a$;X+a?AG}Usk zmR>28mX_MNtV65D+%1QjZV{LZ5RKarG7+65fmk-tEaBqF*np$JoN=Pt(gH1lNQ+i3 zJYy_mJeG7czhO^tf?y^By+}QBu=i}pzNq%|R6{>uBHtl9(#7+aW@i`fp4@l!u`z#{ z7TB~FFrkx8+jX0^;hKe5j+;X#q<)jNi#ujR>YarlhXeTI)z{v5>&?Skw@p0y>4!i4 z$ipPdWufV&H_900_xmUK*)ska%cm*NyaH(wXeodvaE6zb_`L$EQJ3SnUup0G?d%UV$mHQZvk z_~he{@87@gc7sPO&zw7dUSgLv`37-%liz{aeA5D^1%AFP(13M<*2#yi%BXd`Lyw!h z!FD&nr*5w&9PxvRwAv^*v`HUZFIDj>@gS?EY+G;b-8&2>a_-!8smQ?6sWn8HBek^Z zwxN{b3`c{-QmKWSh}t@J^c-Bn^B3mI`KporMsnGCy>1=L^ zwBQbSgDkF>cpr<6k+JFbwjO$L%YzS0-6#YmGvCDw#~L}tx$7kQB%=VxlYp&&$ryKoScuK zVZTZqWeZX?uwx`yFfl;{zYt{ACSx>D0(v)g!oJ9d)DgK6!nXvN;88&-hCL`sTAQ@O za6@2~NQPODiLAB@@BDi|w5FXJEkBU5w0)>I8wR9LXaIy%DB9MtI^}bEmF(4D(N;h*@ z;1f=eS2@6cAQ+=onZru~dEy1*BdIh@E~A+FVp?DmTj1sn+a|s_v*{bs0zfBiE1{BC zzlh)8M!<8m-XdDo_-=py=bQ2f;|h?+H@@-pOBXM_`r7MjYw0UjrU}FOtH1uvp5411 ze&nJ1@4bhF#LUJ%`t32(l7uqxIG{l9EM^rbp)uS+PC*}qw8tyt;#<#1YT`m+DU}i` zFB2YyXpGy2v}q5cbruejXE7G#A3|&q$gV&tOcVfBP*GuH8fOTz5wF@C9Y1;-Z&^u3 zbnXtc2vJ-9vFyg3mdfQKd4q~2)<#GYR7IstYsnPCY7LVLFB%0NB1I8dibqFBSh~4o zt+_!(Sa@M^kw)I&^G$OYMYm1UR%DG;m}Dr-&k&xLPHTx&`wMT zg9`Hv;$Cg7wm0M?o2s>|maCRPAXcvuDB&6%iIKqgz~ojXVTe z#De90MmP8-PGVOOAAvmJgr_1wI=x;+X2R=1NL6?FQ9KfA8d_^T!d1-M@cN z8)<#6>UOodma-z5e2Cp?HF3YmpWAMzYE83|lcmurg4EzFdr&*+D3O3$#5e?@e0Z|} z06+jqL_t(q>EM$SU4rur_XJ53PHkHW0CF8Wv2_0YpFi>BJ-_+;zr1rFDFStvE7B@) zobid-k*&vF5MR1WEC{p_5F3i&;X|)M*agpC5ht5i6N}r@VIXc>izVYUnl~sU=9okw2CDec+5s!8}Y#^dVmY3IIE|B=y?t-i&D?Baap(xofF1mjzV2L}5}fMT6yzL+5bg=xdo z)IunfK%xaZ&rUCCD&6EDL?sMbMCbba`D_iAh~2JG0A1L{)WWXaJDeW(@V5Ts=;+Cl zSK8@Drl#hbmRMiEJ>2gP!TI3M+d0jWT~$CVS};b1IjrJWMiI z=jujR8yzoL2#1VkfEgjP!mFF{EYNli!E9lJ$ifALu4FftczI`|6UB187T~_iLmHzAh>JNVH*M8;jJ4au6`PGXT zFOlW`^qI3KPoE*R%#Q8b?z!jCJ@?$bZQE8LjQ3Y@(<98x1oa4o#^04mhw+S+s~Yh_ zl`6v_7z#OwNez(C=UL8T(F_1$d1Zy^mN700jZ+3j{#(e8h}ORw@C3pOD?hxD`-UCx z6ywE9ND--U09ODe7?zm@_7Dot3qGJd?`kt+^wl2i4Y4ANXT-L!27;;!4PbS}NXDl& zSm{7s-$?62)HfS1KoU#{se(&LZbPp(90-OZ;aDt+#aIhsCmbdPPk`nz6MfgEKc?MN zr_RvmkLhB!^D?H^$?deRS+{8c(*n2N0tlVRs!Y$R`=IJ&RiqBeta;aj+IjuT& zdPnLteJz!ppII&y>o&W;sg>|fy#X&v|M=UYyNTWePBCf}k_yLRp#Kf1pt6WT2!_)o zgDr!u&Bh2P^5`g+5U+vTiKK_F6L(|nfNn5%=z87WZ95o#-K&esR;TjVXYVCR&CFb; z+|=`hQoTtzYo!L~fY_5RK^9O57>LnwmjTg=(r1G)mozHmT-LQ_sooe&L`3-np$&US z)d5cIJ||a3prz$?JCv@<@LyBx-~BIV-g@WC7oYp|SHAYx$d*0<0w5lUmyXX(WHlo@ z){>0KxX7X@Dg!-^xf^d8sGz4T3!XxTW20Mom`iV-VIiI=p7h6)iMtQ&NTt@m3oc)7 zz5NamFgXAz$#gTxzq*YpGNiA0MjK%SwmZl&(<5e|eb3#p_l?bVgS%=Aj7T0&9I<@VU@j$E+}p2J#FBpS9_LQIpY zmFhquNiwEtQF-zCH(5g(85vqzU1gog6AbtWz0obHLfu|f8?LY`7?^ybpES?#Lllc8 zRTY5}iJAfZ0KWl25mZ4s0Pa8$zgh@JU4o>@=PQpW^AjC>3cQ36oetTQ1UR8Q8<-1( zB^Ik-MG2P#tOaosnUHY;m=yGrHU|WIp(|1E0esF-ibv&q9sqGtM%ZZ!}T zqkpAE;3hrfL_RZG$b-P6EL|(487{@EK|zt{g+}OwVo7$)&9uN~wtxwpZ06qEoNb47 z(&{3HWqgx<&@B+PsVczY488JgeP?V8Af;mI)af%%KKVpnJpPN%{KC^uJ-NKRdhGb| z!-tPdUA|0W;ER{0E?m0wt-ty+o4*S zI2qHS-X+-YMbh$?9a-5kLbz!UGe{#H0Ao1=xM-g-lK>Ba-XdU(SHg^uhzkXSfx!V% z6$&*b5+Wrr2Lb^RZJ{ugyysp%*|*r?_=!`XJfCbcZpqcjD|k!lF)J}GU|PVmz{g_& za1$L#k)D(GL6^jB%&e8i{09iK$QO!kr;>=r+%AbKP{Hx_&RcK3{=j|r4i3cw0T1F1 zrBayWwv5VPD@413NDK{y`uf6!Lb)#%Sy?I_KXN6#R=Rj`8UYO90Fdn0*u)XFlKvOh zR0c{$HzUz-M@LK6$UuijDMO%LMm^RH03R6rXfh>1H3+uhCh&7{vx&lRIc+|ltJds7 zgn>!HRH0s1y#CPmw((bAdkcbK$LdyFU@h- zw$dW>#!{$MHC^iq2i;s=_zzYVMj#+Cy5jCK#Ct`g1riBCKAu9p{U86|AH4SR@vnXT zbI*L~kzg2hi9&pcvC@KvOs*F09#0$Jp2-f=6-H-~-;iC|D8n85@=YOAt5@1?n}eN7)?*Y{KoUJguycT9(Af-p7A2pnwA&>FO?-Y&e`3_u z^;XLDVzZ>TI#_^+m`pAki-!Z@ctfqHt2K2!6CE5uTf`Fxf#5`9-j3U=atAJ(aGdPu z>wtQYx`^k`5LRV=3ucCJ2}lV)Vi}np8Nh}>4JiHUK3zWk*xX0w^I=Pn#ScH+Xti>rj$ zFmz{gZ@+Vd(GB{^#MUjlcJ17=d-t}j6Uk%}OyQ&2`XiJBfMA10VzI<;TAV?W0hI{> z^Gst_z)}h0w`{$0Wl;q^d`8G052x~p%o2&BX1JCSm&Ab*U(H+A+?XYZkRHE237$x= z*|+2sp_%FFnd>a;TT=;JzFL|Yr zl&1v#D*Jx4Kva<Oidn zS_+tpMQJvqQY&t`#O&a-=H8-XHAOG8be&waZ-}vpPAAD$%pcM-mr3QHcN1Q6TAp;*_ zM^rpyU-B(8*}LvbGpT_?VH^HJja3s{GQAj-5F@ zx3*RcAh@F28dWWuE)EX&$D)IPFKg?0f|4P`5|jc#mYsYyefe_pp5Q*x67zO^Jm7Xm z78cS=iy2{iAh{yxlO2$5YgN&dtydZjs{^ErLWpSfLzXkWVH$DVr2?E(N20Bfve<6Gt%(LgCfeZ9sVqwYF*7`tJz!u zV2;@xH)C{|yfEc6&=Y}wq@%7rK+yp>Lz6^JmeaGp=c77 zI>l-gvx{1mvj*Q>hSI}DIA#M_w`+;-iN+kG#$ zynZMru!wJYwVz0ZhK$@Na%J{n5%jN?C9~pp? zp_8YM9Xhz%?ZKzD5MR@^G(G5ywg_Nh_Mx$&Fuo<8Ncb1#ml*m8P4(bdyT*?lz5KU- z{qm)YGY+Shq_*v@D(O+$l2@46QvpYiF9D{ID2%o&`wO+ghUhGn8cP*2@$SLX_@#`e z1gi3u^#lV;umpX6iR3USov;%CWTK>e@q6cIn~)q5eaT7z+DNcgRX_{cxS<+hAULHu zdbfDtT!>6Mm_Z00D0UhyJ>EO|9r(Fi8qXbI1!teuG+X*wE>EN@lui&BBy#b@T}agu zIMUKkKylhVD5`@lJLn?nUt(A)J)tEpzl&`T6C9zAde z#2ng7pb$?8lC(}?!SdqtN|3T7Pi9g_OfhJPi15-5&5vwA5_0L0%=UN;-n+wZJi`+<=(XYU;`wda_xxUw{s|_O-}Swe^N75N$2uM$J1_*Q?53ZgT0L!_>`@863;okR_NrnQBvJ3O`QYnb5=mGpKb6+(jT&Pf|EVm%G?)C)cSf`hH{P4p|5O$* z3!4@&Enr&U6SRP#`S4-$2BsEUw~h@)CdiiS?AqRX`<=dcBpB|i)r;^o@NyBlK;AXz z_qXh}P|#U#5?r8It==8mCR)g>HXFHY{hNRBgO^@9Rj+CeyT7H9u~rxdZjYZC3uF!P zBzcebjL3*4EE2qGJZDrb#632=Xod*@6uf3WHC#&ESsQ-2k5E;w2T#-Qb2pWCQ)%Jm z(jJc&1_xD&qs^;^GA$U720#EI#?D+x7vcbf1-xx0O*RO-(hPJ)TbghIq=AByK>dVs zK$M0!;FJ(=7L3A<#tH-s@PI@-idrevqAH5$7#NCOGNgcINL;Pk*LFCYpeOc5Ut9D2 z==)RWPtSe!nY+L6rALOwk|0gE&*EP9bYUf02k1mdFygx#9|*eA+awkn-VgQrnGC8NaJ3KtJ zZR>9E4d^<}X5-w2OZ|N@kF%S}$N5o^O*oW(k$s6X*etzE@+&_QEr#l_iyrqQLhk`IQ_?E zWU6l`}%o z63UQxcwu3YK;QYfxuxahY&N@+S|O1RPP;J&>!Kh>uAGsh=bot7aJ0n_$+s9A%8H`|_Vp_nofN6nG$^s&-+M74rVDf;gnOaHt zTG5C<7!0AI6|y_r?T#I%(&KgmGK?n2kWU?6;F$)AIFi)yKGPYmZ4oJFtK8Yqf+ z{XR`;s4Wf9htQ4=D2{GN&mAc;yG{26oP_x_l@5^3!czI0fBwTaU%&9h=bn7(3lI6j zc*&Y1Ar?49?nZoX@tbKU9|=sr<-{lkU?61#VKFxGw&7j$dX6(`RgaJ&$zO~(AmLvO z4+ck`efX&-A6Qz>zxl?gS6(?$D0b0{*Bg%PYQ3$v6}4t_w37W1^y)E1L+`qMVE_@A z&ks+d?QoP7Wn}A4x3^E)!8(@R*=UlErlILgt6d@g4B$vS;SYv<$jzh3>vlN;giaYM zzhX)5N}`exY6U(491)D9(-FwbE=C7y;NIy7!5RQ8Bo{T2TVuxDyR#syVga5qp=ysxpx6#gw8{$Aqss|wyhHrPdxq^&V}}gOxnuI+Vb-9 z;u2YLS60{7NSm`Tx45{FvK~3gR0UHr6+uWXnS`E_92o2$97OFd0lX54M1oApP@ns0 z(jos;ZT zgYu;+2hl|3bHEGOEM5M!8agO2N1{=Ja2>@G!kr|Y)9}(24Z#aL0X0P8 z5L(h|qpiaKu0xU*!;6tnCEU}V(~BpMn+#wfh=~Z7z@d?7khb9r;}sk8D`SSm$FvtP zfc(amE&}kCj!ud?7iy_CE3Rx42OarV+-Vy;6lgS{15G#)=ziE89<*+oiuLq~%;l-? zyzsKGST}?N0l^V60MYfHz&d zJPUQjkqmGTtX0qxF1K? zg&EtrP2$U8e$XUR`T~*I?1ytBuGA6dt47sYirl zyDbgHe$r4=RC)m^Sxzyee1&;p#4z!SX}G4~YJz15$|aL#Q7jRgkTVCp5KVIw)*FXk zKD>JQ;?92GmVPu^K*E61P}qSk!{Y$V1CB+#QWF7c5R+O0?n(31mOWo6FD$OErwe2< z6wpNETZ}6hcVP6L(E#aux`}(V*x z)KYss-^pI94{nU!>?r63yDjcD`^sl46;3 zdQ4L5k|C(dk`HaB@v&&^?t2g2ZXkd@ef#iX1=YbjWitbI?v$Q1@58jf&x-|&fE@<6 z8%`Z?>r|x^=(tq1T`W}+eR0$$SU6862hqSlv^)_H!|v2O_Vju&6818>6bh}AM;89{ zkN-Zkly`f*fF_Vdx@4UpvW2h(Rc;<4jD}wYrKNjYTupxRh0l`w_2j?7r861ZH zc8?8|hrtw`4szZ3!;ySG=W_a0wd(hYx(6UkyCpiOT{k&!m3EsLWFK)k@H<|AZR+%y z*{7bq|5tzG$?ZGGg->Hq#j_@GL|!4k81A?9ju1%*=iyVa6dT8ctidMlrAch!hZ}AD zaUq$wfQA^wz#JL_{qBL!O@8L_y{qZs+i#sacXlD0tyU@-NFzw+cC21kAP9}8Y4O!6 zS|gLK)`@{}xO@&@)HM|84+nh#pW6e|g#UI)_Jg6I^z>w3yBe9UMUzA@AleKLL7Na7 z0mqQcH3TL>JfV@&a=|C0E5xE?qo$dfrA3`60@DvPnmS!5q`*Srk>eJ5HpE69d5VN? zV>~8;mJikOH`nHU425dmb+tykZC@#yk}seN1gV?$1qUr<_g z!8DP4O4g1IMP2Zgt^<`7c@E@xlcxoYq-!PH5(3F{bE!MoZd#|@{CFSY*PCA#z*XP2?JBI=uQo?2|w^nu9vyK!$CrL zvYqtxBj%22femN@6FS*|opOgaiSOi_-r?=`b#K~$TUCIG&hPxrZ~W@7e3c3TCLVa; zzIWG*gGxf;Z+`PzM~@vRbMS%v`#$y10|)l+9~kIoy7TUmA7vl@2p|YQyLbP%IF=;Q zY^7W&7K^!DA(zjkC4Wz@R4V65u~;q>1f-r-85P+$jlN-s;{_>rHX|7@>45zx)uXJ( zFBatlR`H>a=J9$VtssZSKQ7KDiE{Yi#NM#VNXmQa?M>aTHw*ZKt#=S3v|5s{hd^vU}%e=0=D68#VPm{n<-L-#OE4>Q1LyA>#|&577z%rD@|4 zk|Bk0%oxmw;6R_EiH~jgoRUBX`~q(ks))Tq@Qc&X{XoeAI)cISaCr1CIE0PJp=Lv2 z_@pLcF@k;{Vk4saY9T=^oD$vI^>Vf3(9pohFkUumBs+G7lu8Zl4F+q8%8>XBhY|CE z=Zj#cN^A{|Z5IH=0C33c!%&Rts{wKV;E<=+piziN&r%AM2r>YopBnIipb#knkkIC4 zE#1Ms2+Fffog}|H!>%Z&2%Z4`fRu~f>2|cBU66&y>2fZ4!z z+7pTX5P>!_&NCZ8xYR(D#0&3DH1NSYXt;nVaq+7k3im-i1q2f951Ba#-$E`y8H_Jl zA1ndELox6Hq5oK5d1sh>>>{irUs6=J{>~X6{HAs z1f>&!JM1nfGJ=@sE-OiLtV9*r+cwYoTK&!EkFK0N{lM4R2P3$3XjDg4SXkuNPUg+eC-Fz=jr&sLyYs;b)JNZht zeEo>IV_IMXTEK)(HejciO_Fc%J7WVBSX@|?S=0wb=v1qgoZ?mD<*=eE9h{AYksu3L}m!Y<+GJ!yR?Ac#c^%4(xltCz~9YNb|! z08=I|y8@fT_y>mr;H9xrE>qaRG&qBaACbLG9jJr0o#{G`kYh^M+S-nz+G7MQ0q;iL>P)^eIou5r*3e!-d5kF^Es^Pje1z{!t2#LRFg4^>2t0z5VK7z%L@^w4 zict#qgW5O`*ud6y_#(qQFQk`N8k)^%2?QMeAhQXtq*StjQ*`|vBBB5gu!l@M3~aVqGA|L2o^jGT-KQH2t`Id=7(+|)CY`0b}+n5 zdANJ|z~~sksVK}D0>jB9iA#%gEWJVjn+*aJ6egZ2lxi%5<`9J=pUU%Pf#~9JmNVL$ z&%ZN!_T)V~VtaNbY)I%B&7lT~CZa-s4`~jk7-@;A30pGd>mux8Ni7%1I#A4&j4VrH zI5DAgl1$h*tG5}`IX;y}$@6T??hF7$ZiO<&@v8-ROF4NXzuA+0IcONf_&{J72tz=^wUFf9a zqa(X_?%KD1&-QIQ1_zUnK7OK#f597}i5LuF2rm}P_>F2}{39^ifLV0Op$HBH@WCKW z8b;xWsEm@l%JPLVOj6JF9b?}ZMUC1yPMkIY;SBGr*D*OW=8=fdGarz5%p`juLd{{7+erxrC;ce*^F64VTB!5GkWyix&3 zM5Dw_ECLQK9yp^S1EN8(5V=-%XaKobdX6XXN?k=(RwF(LC%N0U`vPv=W$}4@U<68C zF$7^9Znpz2ClCN~7?F5?z3VP0P(HvH#KGp)P;y0|gWJ;@jkZDxWH)(TI%yUXy)slbI)49zFWE@fn9Av9Ac zAe_P*37Ij@1!-~;s+-9d7E{HAl4QoH_+mdc6!pd4y+|v$v>fH4Vk635MN&Xar163i$c}u9?SLMrmKzg} zjnI>j;}wVj-ij0v2u2FB0lM@iIlu%239)iS3MKY214UR^D7KEx z-g*Is!0@;fIE?uZ>akaRgTXfZAeaXFkVO%=3E+9hZUjX-XtgZ8SV>v1S`$6_jX zaVk|TmdQNbYo?Ty6XX=RqS3xyf6&@%8}^Xuxn%=hF$}`37#kn(o08Yx(iEzZBL>29 z6_Md{!nfSeYM?ebLWm-JGYOGjq#SincIEC zjDO53__^H|+NAZLK63_z-p%{0exTVm0+;6l8)hCfEnr%}w7>^ifT$HlE20~4z&@}5 z#}yUr)oLj0k8K&lw_Q)KyWIYE8)Xn}|K8n&TMy(k{E7fLa z5kq(=qD-ryjS8j#Ujd&SrxmV6LnTj+Xw&jB>l0!V!%-Hl1%7ge75<*LEaoE74>w9m z8DdmWIHOKZfotj@`lL1t}^Th(_gGCpbgsLmS zkjK^aV}h*=FIr}W^bak;EoZ~? zCk6`Shy26<5#R_!_Xz!mk3a-!e8FU&gZ>KoxAgmn9U^>~>_gCP z$`#TBwd}wpNR1E&B@hbviG?YGiGaIo(12`KVt2$!n$FFDnjrmaC4CK+B)m#Ms1Cuu zLLoubRZbE47+MQJAkYhakIoI#B&2R=ofWG}5Rie8$Q>D49~1`^0REAG0znve%2{ln zD7u>7aQ0vnN(idbGZ(0xflNsWqm@u&qc=6I7{@;IuAnUGOJKv3cFg$~{@bmQ3pJTqYp3cnA&0e}Red)^O zrG>?8zOcTQNvAW1-#Nmd=yEud1Ih8x(e2x}ZrQSBY;**L6f{11OBub3ny&;k+Ic_^;}?zw`+`x6E?zVohFv>{ z$x;VlB$9}&Efv4{AOGq2J0#qZkF-`Dw+|vD8??xJO_6U!U=m~}$ccmS2Y#c_J4C^g zngxu2Az%|)C5|FKFicD!7ZGQdVP8|5_0K)E@Av=puX#Lwd=Bf_)<*dl-;ZID~QhZp+@;3JN@q+!vqnb#KtaS*X{mjmbDCFx_V(MW_F z@Ob^DN>OQ4qmf`D9F;NydD%y z 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

6Ul;4#9H6n)L#7oR&wY#)IyDj0W|!i= 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 E03h{XJOBUy literal 0 HcmV?d00001 From db2d6f32209674d1cbc423e2901f01cbf40064a4 Mon Sep 17 00:00:00 2001 From: Florian Gilcher Date: Mon, 10 Mar 2014 12:16:01 +0100 Subject: [PATCH 377/499] Some style fixes to the README --- README.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 481d3311..6e5a90bd 100644 --- a/README.md +++ b/README.md @@ -24,15 +24,17 @@ Screenshot: ![The activities app](screenshot.png) ## Running the app on Heroku -Deploying the app is simple: +To deploy the app, you need the following: * Clone this repository: `https://github.com/eurucamp/eurucamp-activities-2013/` * An account and a created application at Heroku. * A registered twitter application. Go [here](https://apps.twitter.com/). * A registered github application. Go [here](https://github.com/settings/applications). -* Add their keys to your Heroku app as described [here](https://devcenter.heroku.com/articles/config-vars). +Deploying is as easy as: + +* Add their keys to your Heroku app as described [here](https://devcenter.heroku.com/articles/config-vars) using the env variables described below. * Push the repository to Heroku: `git push git@heroku.com:.git` ### **ENV** variables used: @@ -44,7 +46,7 @@ Deploying the app is simple: ## Development -An install postgresql instance and a compiler is needed. +An installed postgresql instance and a compiler is needed. ### Basic setup From 472c23108b57fdbf38b0ee993a65284916eb1f8f Mon Sep 17 00:00:00 2001 From: Konstantin Haase Date: Tue, 18 Mar 2014 20:15:19 +0900 Subject: [PATCH 378/499] add license badge --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6e5a90bd..b6f39603 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# eurucamp Activities +# eurucamp Activities [![License](http://img.shields.io/:license-AGPL-c83000.svg)](COPYRIGHT) The eurucamp activities app is a small application to allow attendees to organize and plan small event in and around a conference. Users create events or sign up to created ones. Signup works through Github or Twitter. From 6e5d48f0323dd6b334f93eb52407aabdde207217 Mon Sep 17 00:00:00 2001 From: Konstantin Haase Date: Tue, 18 Mar 2014 23:14:21 +0900 Subject: [PATCH 379/499] change badge colour --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b6f39603..d67818de 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# eurucamp Activities [![License](http://img.shields.io/:license-AGPL-c83000.svg)](COPYRIGHT) +# eurucamp Activities [![License](http://img.shields.io/:license-AGPL-0030c8.svg)](COPYRIGHT) The eurucamp activities app is a small application to allow attendees to organize and plan small event in and around a conference. Users create events or sign up to created ones. Signup works through Github or Twitter. From 40bb866d4fbf0aa0c29b5a72dc7a6f18b5c662ff Mon Sep 17 00:00:00 2001 From: Florian Gilcher Date: Wed, 19 Mar 2014 10:00:42 +0000 Subject: [PATCH 380/499] Setup travis again --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index fc47b848..3a14b352 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,6 @@ branches: only: - master - - railscamp_germany before_script: cp config/database.yml.example config/database.yml language: ruby rvm: From 25b51c8218fd62046029afc036bdb78165eb067d Mon Sep 17 00:00:00 2001 From: Florian Gilcher Date: Wed, 19 Mar 2014 10:15:58 +0000 Subject: [PATCH 381/499] Use the right name for the example file --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 3a14b352..c30d33cb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,7 @@ branches: only: - master -before_script: cp config/database.yml.example config/database.yml +before_script: cp config/database.yml.sample config/database.yml language: ruby rvm: - 2.0.0 From c9a0ed6ea4a372b83ae2ae7448c9075a91a7de9b Mon Sep 17 00:00:00 2001 From: Florian Gilcher Date: Wed, 19 Mar 2014 10:26:52 +0000 Subject: [PATCH 382/499] Comment out options you shouldn't use with pgsql --- config/database.yml.sample | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/config/database.yml.sample b/config/database.yml.sample index e3d84d19..75fd13ca 100644 --- a/config/database.yml.sample +++ b/config/database.yml.sample @@ -1,11 +1,11 @@ defaults: &defaults adapter: postgresql encoding: utf8 - host: localhost port: 5432 pool: 5 - username: your_name - password: +# host: localhost +# username: your_name +# password: development: <<: *defaults From dca7c09fadafa61e39d7bbde5c36934f264b3f17 Mon Sep 17 00:00:00 2001 From: Florian Gilcher Date: Wed, 19 Mar 2014 10:45:43 +0000 Subject: [PATCH 383/499] Add travis badge [ci skip] --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d67818de..2fb86437 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# eurucamp Activities [![License](http://img.shields.io/:license-AGPL-0030c8.svg)](COPYRIGHT) +# eurucamp Activities [![License](http://img.shields.io/:license-AGPL-0030c8.svg)](COPYRIGHT) [![Build Status](https://travis-ci.org/eurucamp/eurucamp-activities-2013.png?branch=master)](https://travis-ci.org/eurucamp/eurucamp-activities-2013) The eurucamp activities app is a small application to allow attendees to organize and plan small event in and around a conference. Users create events or sign up to created ones. Signup works through Github or Twitter. From 368bc01a18ff77cac6c10d75419d51e0b2fe4da1 Mon Sep 17 00:00:00 2001 From: Alex Coles Date: Sun, 23 Mar 2014 00:00:53 +0100 Subject: [PATCH 384/499] Target Ruby 2.1.1, bumping debugger Signed-off-by: Alex Coles --- .ruby-version | 2 +- Gemfile | 4 ++-- Gemfile.lock | 8 ++++---- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.ruby-version b/.ruby-version index 359a5b95..3e3c2f1e 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -2.0.0 \ No newline at end of file +2.1.1 diff --git a/Gemfile b/Gemfile index c1a8fadb..d230a173 100644 --- a/Gemfile +++ b/Gemfile @@ -1,6 +1,6 @@ source 'https://rubygems.org' -ruby "2.0.0" +ruby "2.1.1" gem 'rails', '4.0.2' gem 'pg' @@ -30,7 +30,7 @@ gem 'coffee-rails', '~> 4.0.0' gem 'uglifier', '>= 1.0.3' group :development do - gem 'debugger', '~> 1.6' + gem 'debugger', '~> 1.6.6' gem 'heroku_san', '~> 3.0.2' gem 'foreman' gem 'better_errors' diff --git a/Gemfile.lock b/Gemfile.lock index 72750622..67bd9a3e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -82,12 +82,12 @@ GEM coffee-script-source (1.7.0) columnize (0.3.6) debug_inspector (0.0.2) - debugger (1.6.5) + debugger (1.6.6) columnize (>= 0.3.1) debugger-linecache (~> 1.2.0) - debugger-ruby_core_source (~> 1.3.1) + debugger-ruby_core_source (~> 1.3.2) debugger-linecache (1.2.0) - debugger-ruby_core_source (1.3.1) + debugger-ruby_core_source (1.3.2) devise (3.0.4) bcrypt-ruby (~> 3.0) orm_adapter (~> 0.1) @@ -293,7 +293,7 @@ DEPENDENCIES capybara (~> 2.1) capybara-webkit (~> 0.14) coffee-rails (~> 4.0.0) - debugger (~> 1.6) + debugger (~> 1.6.6) devise (~> 3.0.0.rc) draper exception_notification (~> 4.0.1) From 1e57b6396b4e7cb91cc4ccffce2aecb786114bf9 Mon Sep 17 00:00:00 2001 From: Alex Coles Date: Sun, 23 Mar 2014 00:02:37 +0100 Subject: [PATCH 385/499] Remove now deprecated .rvmrc Signed-off-by: Alex Coles --- .rvmrc | 2 -- 1 file changed, 2 deletions(-) delete mode 100644 .rvmrc diff --git a/.rvmrc b/.rvmrc deleted file mode 100644 index d51fd96e..00000000 --- a/.rvmrc +++ /dev/null @@ -1,2 +0,0 @@ -rvm_gemset_create_on_use_flag=1 -rvm use ruby-2.0.0@eurucamp_activities From 2c7bafe5f54caabc526cc3b2da10ba76bcc0f14d Mon Sep 17 00:00:00 2001 From: Alex Coles Date: Sun, 23 Mar 2014 00:03:02 +0100 Subject: [PATCH 386/499] Target Ruby 2.1.1 on Travis Signed-off-by: Alex Coles --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index c30d33cb..05dbef63 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,5 +4,5 @@ branches: before_script: cp config/database.yml.sample config/database.yml language: ruby rvm: - - 2.0.0 + - 2.1.1 script: bundle exec rake db:create db:migrate spec From e7818ba93904148c9352177a1bd86dfe78237d46 Mon Sep 17 00:00:00 2001 From: Bodo Tasche Date: Sat, 5 Jul 2014 22:52:49 +0200 Subject: [PATCH 387/499] removed eurucamp week --- app/views/activities/index.html.haml | 4 ---- app/views/activities/show.html.haml | 10 ---------- app/views/partials/_footer.html.haml | 2 -- 3 files changed, 16 deletions(-) diff --git a/app/views/activities/index.html.haml b/app/views/activities/index.html.haml index 3a8f31b2..880a94c8 100644 --- a/app/views/activities/index.html.haml +++ b/app/views/activities/index.html.haml @@ -30,7 +30,3 @@ %ul#activities = render @activities.decorate - -.map - %h3 The eurucamp week - %iframe(src="http://mapsengine.google.com/map/u/0/embed?mid=zddEY5tddvgw.kv-7qKkgMPrg" width="100%" height="100%") diff --git a/app/views/activities/show.html.haml b/app/views/activities/show.html.haml index 165f8969..7c43c394 100644 --- a/app/views/activities/show.html.haml +++ b/app/views/activities/show.html.haml @@ -7,16 +7,6 @@ = render "action", activity: @activity, type: @activity.relation_ship_with(current_user), remote: false - - if @activity.relation_ship_with(current_user) == 'owner' - %p.info - Please consider adding this activity to our - = succeed "." do - %a(href="https://mapsengine.google.com/map/edit?mid=zddEY5tddvgw.kv-7qKkgMPrg" title="Edit the map") eurucamp week map - %br - If you’re running a website related to this activity show some eurucamp pride with the - = succeed "!" do - %a(href="https://gist.github.com/polarblau/6172669" title="Get the bar") eurucamp week browser bar - %section.overview = render "activities/progress", activity: @activity %p diff --git a/app/views/partials/_footer.html.haml b/app/views/partials/_footer.html.haml index 76e56f0e..87e1170c 100644 --- a/app/views/partials/_footer.html.haml +++ b/app/views/partials/_footer.html.haml @@ -6,5 +6,3 @@ %li %a(href="http://eurucamp.org" title="#{t("footer_nav.eurucamp.title")}") = t("footer_nav.eurucamp.label") - -= javascript_include_tag "//eurucamp-week-bar.herokuapp.com/bar.js?group=rand_days&color=e55924" From 19e5e345b617e52040ff7ef4a4d2297e0a27f181 Mon Sep 17 00:00:00 2001 From: Bodo Tasche Date: Sat, 5 Jul 2014 22:53:04 +0200 Subject: [PATCH 388/499] changed url to euru.camp --- app/views/partials/_footer.html.haml | 2 +- config/locales/en.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/partials/_footer.html.haml b/app/views/partials/_footer.html.haml index 87e1170c..107c14cd 100644 --- a/app/views/partials/_footer.html.haml +++ b/app/views/partials/_footer.html.haml @@ -4,5 +4,5 @@ %nav %ul %li - %a(href="http://eurucamp.org" title="#{t("footer_nav.eurucamp.title")}") + %a(href="http://euru.camp" title="#{t("footer_nav.eurucamp.title")}") = t("footer_nav.eurucamp.label") diff --git a/config/locales/en.yml b/config/locales/en.yml index d7d891ec..dbe5e14c 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -136,5 +136,5 @@ en: label: Policy title: Policy and Code of Conduct eurucamp: - label: eurucamp.org + label: euru.camp title: Head over to the main site From 29ea88f7d68152203fd72a3cd0fe40b0d51eb124 Mon Sep 17 00:00:00 2001 From: Bodo Tasche Date: Sat, 5 Jul 2014 22:53:12 +0200 Subject: [PATCH 389/499] updated date to 2014 --- config/application.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/config/application.yml b/config/application.yml index 2fc70bf7..00b9d8e8 100644 --- a/config/application.yml +++ b/config/application.yml @@ -1,8 +1,8 @@ defaults: &defaults event: - name: "eurucamp 2013" - start_time: 2013-08-10 00:00:00 - end_time: 2013-08-19 00:00:00 + name: "eurucamp 2014" + start_time: 2014-08-01 00:00:00 + end_time: 2014-08-03 23:59:59 host: "activities.eurucamp.org" mailers: from: "activities@eurucamp.org" From b926c62813d1f93add3db70ac7906a97c0d6ee34 Mon Sep 17 00:00:00 2001 From: Bodo Tasche Date: Sat, 5 Jul 2014 22:53:22 +0200 Subject: [PATCH 390/499] changed heroku to new app for 2014 --- config/heroku.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config/heroku.yml b/config/heroku.yml index 7301b204..d46248fc 100644 --- a/config/heroku.yml +++ b/config/heroku.yml @@ -1,11 +1,11 @@ production: stack: cedar - app: eurucamp-activities-2013 + app: eurucamp-activities-2014 config: BUNDLE_WITHOUT: "development:test" staging: stack: cedar - app: eurucamp-activities-2013-dev + app: eurucamp-activities-2014-dev config: &default BUNDLE_WITHOUT: "development:test" From 4f09b3c1f51275ba1cbe7d41503e07e3fdd38bc3 Mon Sep 17 00:00:00 2001 From: Bodo Tasche Date: Sat, 5 Jul 2014 23:56:52 +0200 Subject: [PATCH 391/499] updated logo to 2014 --- app/assets/images/header/logo.png | Bin 11379 -> 6944 bytes app/assets/images/header/logo@2x.png | Bin 22950 -> 10661 bytes app/assets/images/header/logo_small.png | Bin 5215 -> 4536 bytes app/assets/images/header/logo_small@2x.png | Bin 9954 -> 7602 bytes app/assets/stylesheets/partials/_header.sass | 6 +++--- 5 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/assets/images/header/logo.png b/app/assets/images/header/logo.png index b1a4056584defdc3594832c26172882cebe19d0f..2944ee2dfab808d5d3a51d1d69d2422a08fb9927 100644 GIT binary patch delta 6302 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 o4Gr|O*+Sde-67KP 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 diff --git a/app/assets/images/header/logo@2x.png b/app/assets/images/header/logo@2x.png index dfcc714c735247c85a0ec769f6bc2cf75380b851..2a9ad1fb8c59ce6eae1e618b67ab4ef332f105fe 100644 GIT binary patch delta 10045 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 diff --git a/app/assets/images/header/logo_small.png b/app/assets/images/header/logo_small.png index 39c11cd3530432c6f5e416eecdc860d6172e6e5d..6770bcdd98342cb292b5b94545c572d177fb4424 100644 GIT binary patch delta 3848 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 diff --git a/app/assets/images/header/logo_small@2x.png b/app/assets/images/header/logo_small@2x.png index e47191f0bc2dd2d719f3e3b8f22e332c710aea7f..2f7d07c7319a9a12ce521b1d5d9270f720e84664 100644 GIT binary patch delta 6938 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 delta 9258 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 diff --git a/app/assets/stylesheets/partials/_header.sass b/app/assets/stylesheets/partials/_header.sass index 58d8bc44..716367ec 100644 --- a/app/assets/stylesheets/partials/_header.sass +++ b/app/assets/stylesheets/partials/_header.sass @@ -28,14 +28,14 @@ padding: 1em 0 a - width: 262px - height: 192px + width: 257px + height: 102px display: inline-block text-indent: -999em background: image-url("header/logo.png") no-repeat +hidpi - background-size: 262px 192px + background-size: 257px 102px background-image: image-url("header/logo@2x.png") img From 96c1beca06cc103e4144cdf313bded067554388c Mon Sep 17 00:00:00 2001 From: Bodo Tasche Date: Sun, 6 Jul 2014 00:33:35 +0200 Subject: [PATCH 392/499] Upgraded rails, sprockets-rails, sass-rails --- Gemfile | 6 ++-- Gemfile.lock | 82 ++++++++++++++++++++++++---------------------------- 2 files changed, 41 insertions(+), 47 deletions(-) diff --git a/Gemfile b/Gemfile index d230a173..2e9a6c3d 100644 --- a/Gemfile +++ b/Gemfile @@ -2,7 +2,7 @@ source 'https://rubygems.org' ruby "2.1.1" -gem 'rails', '4.0.2' +gem 'rails', '4.0.8' gem 'pg' gem 'unicorn' gem 'settingslogic' @@ -14,7 +14,7 @@ gem 'omniauth-twitter', '~> 0.0.16' gem 'simple_form', '~> 3.0.1' gem 'modernizr-rails' -gem 'sprockets-rails', git: 'git://github.com/rails/sprockets-rails.git' +gem 'sprockets-rails', '~> 2.1.3' gem 'jquery-rails' gem 'turbolinks' gem 'jbuilder', '~> 1.0.1' @@ -25,7 +25,7 @@ gem 'rails_html_helpers' gem 'draper' gem 'cancan' gem 'redcarpet' -gem 'sass-rails', '~> 4.0.0' +gem 'sass-rails', '~> 4.0.3' gem 'coffee-rails', '~> 4.0.0' gem 'uglifier', '>= 1.0.3' diff --git a/Gemfile.lock b/Gemfile.lock index 67bd9a3e..a8c85d29 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -10,48 +10,38 @@ GIT specs: rails_log_stdout (0.0.1) -GIT - remote: git://github.com/rails/sprockets-rails.git - revision: 8bf70f674ec714dc1082d279fd021075fa036a74 - specs: - sprockets-rails (2.0.1) - actionpack (>= 3.0) - activesupport (>= 3.0) - sprockets (~> 2.8) - GEM remote: https://rubygems.org/ specs: accept_values_for (0.4.3) activemodel (>= 3.0.0) rspec - actionmailer (4.0.2) - actionpack (= 4.0.2) + actionmailer (4.0.8) + actionpack (= 4.0.8) mail (~> 2.5.4) - actionpack (4.0.2) - activesupport (= 4.0.2) + actionpack (4.0.8) + activesupport (= 4.0.8) builder (~> 3.1.0) erubis (~> 2.7.0) rack (~> 1.5.2) rack-test (~> 0.6.2) - activemodel (4.0.2) - activesupport (= 4.0.2) + activemodel (4.0.8) + activesupport (= 4.0.8) builder (~> 3.1.0) - activerecord (4.0.2) - activemodel (= 4.0.2) + activerecord (4.0.8) + activemodel (= 4.0.8) activerecord-deprecated_finders (~> 1.0.2) - activesupport (= 4.0.2) + activesupport (= 4.0.8) arel (~> 4.0.0) activerecord-deprecated_finders (1.0.3) - activesupport (4.0.2) - i18n (~> 0.6, >= 0.6.4) + activesupport (4.0.8) + i18n (~> 0.6, >= 0.6.9) minitest (~> 4.2) multi_json (~> 1.3) thread_safe (~> 0.1) tzinfo (~> 0.3.37) addressable (2.3.5) arel (4.0.2) - atomic (1.1.14) bcrypt-ruby (3.1.2) better_errors (1.1.0) coderay (>= 1.0.0) @@ -196,29 +186,29 @@ GEM omniauth-oauth (~> 1.0) orm_adapter (0.5.0) pg (0.17.1) - polyglot (0.3.3) + polyglot (0.3.5) rack (1.5.2) rack-robotz (0.0.4) rack rack-test (0.6.2) rack (>= 1.0) - rails (4.0.2) - actionmailer (= 4.0.2) - actionpack (= 4.0.2) - activerecord (= 4.0.2) - activesupport (= 4.0.2) + rails (4.0.8) + actionmailer (= 4.0.8) + actionpack (= 4.0.8) + activerecord (= 4.0.8) + activesupport (= 4.0.8) bundler (>= 1.3.0, < 2.0) - railties (= 4.0.2) - sprockets-rails (~> 2.0.0) + railties (= 4.0.8) + sprockets-rails (~> 2.0) rails_html_helpers (0.1.1) railties (>= 3.2) - railties (4.0.2) - actionpack (= 4.0.2) - activesupport (= 4.0.2) + railties (4.0.8) + actionpack (= 4.0.8) + activesupport (= 4.0.8) rake (>= 0.8.7) thor (>= 0.18.1, < 2.0) raindrops (0.12.0) - rake (10.1.1) + rake (10.3.2) redcarpet (3.0.0) request_store (1.0.5) rest-client (1.6.7) @@ -241,10 +231,11 @@ GEM rspec-mocks (~> 2.14.0) rubyzip (1.1.0) sass (3.2.14) - sass-rails (4.0.1) + sass-rails (4.0.3) railties (>= 4.0.0, < 5.0) - sass (>= 3.1.10) - sprockets-rails (~> 2.0.0) + sass (~> 3.2.0) + sprockets (~> 2.8, <= 2.11.0) + sprockets-rails (~> 2.0) settingslogic (2.0.9) simple_form (3.0.1) actionpack (>= 4.0.0, < 4.1) @@ -254,21 +245,24 @@ GEM multi_json simplecov-html (~> 0.8.0) simplecov-html (0.8.0) - sprockets (2.10.1) + sprockets (2.11.0) hike (~> 1.2) multi_json (~> 1.0) rack (~> 1.0) tilt (~> 1.1, != 1.3.0) - thor (0.18.1) - thread_safe (0.1.3) - atomic + sprockets-rails (2.1.3) + actionpack (>= 3.0) + activesupport (>= 3.0) + sprockets (~> 2.8) + thor (0.19.1) + thread_safe (0.3.4) tilt (1.4.1) treetop (1.4.15) polyglot polyglot (>= 0.3.1) turbolinks (2.2.1) coffee-rails - tzinfo (0.3.38) + tzinfo (0.3.39) uglifier (2.4.0) execjs (>= 0.3.0) json (>= 1.8.0) @@ -312,17 +306,17 @@ DEPENDENCIES omniauth-twitter (~> 0.0.16) pg rack-robotz (~> 0.0.3) - rails (= 4.0.2) + rails (= 4.0.8) rails3_serve_static_assets! rails_html_helpers rails_log_stdout! redcarpet rspec-rails (~> 2.0) - sass-rails (~> 4.0.0) + sass-rails (~> 4.0.3) settingslogic simple_form (~> 3.0.1) simplecov - sprockets-rails! + sprockets-rails (~> 2.1.3) turbolinks uglifier (>= 1.0.3) unicorn From 3bf5ad86189fce76fc49fb14d547dab4f78f7646 Mon Sep 17 00:00:00 2001 From: Bodo Tasche Date: Thu, 10 Jul 2014 08:00:50 +0200 Subject: [PATCH 393/499] Increased Dates to allow Events around eurucamp --- config/application.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config/application.yml b/config/application.yml index 00b9d8e8..b1d5543b 100644 --- a/config/application.yml +++ b/config/application.yml @@ -1,8 +1,8 @@ defaults: &defaults event: name: "eurucamp 2014" - start_time: 2014-08-01 00:00:00 - end_time: 2014-08-03 23:59:59 + start_time: 2014-07-28 00:00:00 + end_time: 2014-08-05 23:59:59 host: "activities.eurucamp.org" mailers: from: "activities@eurucamp.org" From 14376fa7537cb6fccdc326ac01fe9e705d9e6de5 Mon Sep 17 00:00:00 2001 From: Roman Messer Date: Tue, 12 Aug 2014 12:29:34 +0200 Subject: [PATCH 394/499] New activity accepts supplied start and end dates. Resolves #112. --- app/models/event.rb | 4 ++-- spec/models/event_spec.rb | 20 ++++++++++++++------ 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/app/models/event.rb b/app/models/event.rb index 63baf8e3..53ebc47d 100644 --- a/app/models/event.rb +++ b/app/models/event.rb @@ -16,8 +16,8 @@ def initialize( def new_activity(author, *args) activity_source.call(*args).tap do |activity| if activity - activity.start_time = @start_time - activity.end_time = @end_time + activity.start_time = @start_time if activity.start_time.blank? + activity.end_time = @end_time if activity.end_time.blank? activity.event = self activity.creator = author end diff --git a/spec/models/event_spec.rb b/spec/models/event_spec.rb index a275f36e..311db850 100644 --- a/spec/models/event_spec.rb +++ b/spec/models/event_spec.rb @@ -16,18 +16,26 @@ describe "#new_activity" do let(:user) { mock_model(User) } - let(:new_activity) { OpenStruct.new } let(:args) { {} } subject { event.new_activity(user, args) } before do - event.activity_source = ->(x){ new_activity } + event.activity_source = ->(x){ OpenStruct.new(x) } end - its(:event) { should == event } - it { should == new_activity } - its(:start_time) { should == start_time } - its(:end_time) { should == end_time } + context 'with activity defaults' do + its(:event) { should == event } + it { expect(subject).to be_a_kind_of OpenStruct } + its(:start_time) { should == start_time } + its(:end_time) { should == end_time } + end + + context 'with activity params' do + let(:args) { {name: 'An activity', start_time: 5.hours.from_now, end_time: 12.hours.from_now} } + its(:name) { should == args[:name] } + its(:start_time) { should == args[:start_time] } + its(:end_time) { should == args[:end_time] } + end end describe "#activity" do From 7cb8ff2a13b3f75b5fa80b2fd191ac6fc175baa0 Mon Sep 17 00:00:00 2001 From: Roman Messer Date: Tue, 12 Aug 2014 12:30:43 +0200 Subject: [PATCH 395/499] Comment --- app/models/event.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/app/models/event.rb b/app/models/event.rb index 53ebc47d..e96d4403 100644 --- a/app/models/event.rb +++ b/app/models/event.rb @@ -80,6 +80,7 @@ def find_activity(activity_id) all_activities.where(id: activity_id).first end + # Allow to replace db engine for tests def activity_source @activity_source ||= Activity.public_method(:new) end From 9076749bdc49b666c2e18f6f0b6d66b2c7126957 Mon Sep 17 00:00:00 2001 From: Sebastian Schulze Date: Tue, 17 Feb 2015 18:31:20 +0100 Subject: [PATCH 396/499] Prepare for codeshipping (#117) --- .ruby-version | 2 +- Gemfile | 3 +-- Gemfile.lock | 8 -------- 3 files changed, 2 insertions(+), 11 deletions(-) diff --git a/.ruby-version b/.ruby-version index 3e3c2f1e..cd57a8b9 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -2.1.1 +2.1.5 diff --git a/Gemfile b/Gemfile index 2e9a6c3d..655e053a 100644 --- a/Gemfile +++ b/Gemfile @@ -1,6 +1,6 @@ source 'https://rubygems.org' -ruby "2.1.1" +ruby "2.1.5" gem 'rails', '4.0.8' gem 'pg' @@ -30,7 +30,6 @@ gem 'coffee-rails', '~> 4.0.0' gem 'uglifier', '>= 1.0.3' group :development do - gem 'debugger', '~> 1.6.6' gem 'heroku_san', '~> 3.0.2' gem 'foreman' gem 'better_errors' diff --git a/Gemfile.lock b/Gemfile.lock index a8c85d29..cd27a102 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -70,14 +70,7 @@ GEM coffee-script-source execjs coffee-script-source (1.7.0) - columnize (0.3.6) debug_inspector (0.0.2) - debugger (1.6.6) - columnize (>= 0.3.1) - debugger-linecache (~> 1.2.0) - debugger-ruby_core_source (~> 1.3.2) - debugger-linecache (1.2.0) - debugger-ruby_core_source (1.3.2) devise (3.0.4) bcrypt-ruby (~> 3.0) orm_adapter (~> 0.1) @@ -287,7 +280,6 @@ DEPENDENCIES capybara (~> 2.1) capybara-webkit (~> 0.14) coffee-rails (~> 4.0.0) - debugger (~> 1.6.6) devise (~> 3.0.0.rc) draper exception_notification (~> 4.0.1) From 4ecb99b7b4b096d1ea367574beabf2ab9c1be513 Mon Sep 17 00:00:00 2001 From: Sebastian Schulze Date: Wed, 18 Feb 2015 19:28:21 +0100 Subject: [PATCH 397/499] Update capybara-webkit (and hope to fix the codeship build) #117 --- Gemfile.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index cd27a102..c1969815 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -53,7 +53,7 @@ GEM thor builder (3.1.4) cancan (1.6.10) - capybara (2.2.1) + capybara (2.4.4) mime-types (>= 1.16) nokogiri (>= 1.3.3) rack (>= 1.0.0) @@ -130,7 +130,7 @@ GEM jquery-rails (3.1.0) railties (>= 3.0, < 5.0) thor (>= 0.14, < 2.0) - json (1.8.1) + json (1.8.2) json_spec (1.1.1) multi_json (~> 1.0) rspec (~> 2.0) @@ -143,7 +143,7 @@ GEM mime-types (~> 1.16) treetop (~> 1.4.8) mime-types (1.25.1) - mini_portile (0.5.2) + mini_portile (0.6.2) minitest (4.7.5) modernizr-rails (2.7.1) multi_json (1.8.4) @@ -153,8 +153,8 @@ GEM sass (>= 3.2) netrc (0.7.7) newrelic_rpm (3.7.2.192) - nokogiri (1.6.1) - mini_portile (~> 0.5.0) + nokogiri (1.6.6.2) + mini_portile (~> 0.6.0) oauth (0.4.7) oauth2 (0.8.1) faraday (~> 0.8) @@ -183,7 +183,7 @@ GEM rack (1.5.2) rack-robotz (0.0.4) rack - rack-test (0.6.2) + rack-test (0.6.3) rack (>= 1.0) rails (4.0.8) actionmailer (= 4.0.8) From 1e4ab991dfcc15782871bf8d7179447374e5e47e Mon Sep 17 00:00:00 2001 From: Sebastian Schulze Date: Wed, 18 Feb 2015 19:38:42 +0100 Subject: [PATCH 398/499] Well then. Upgrade capybara-webkit to 1.4 and see what happens (#117) --- Gemfile | 2 +- Gemfile.lock | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Gemfile b/Gemfile index 655e053a..222c610b 100644 --- a/Gemfile +++ b/Gemfile @@ -44,7 +44,7 @@ end group :test do gem 'simplecov', require: false gem 'capybara', '~> 2.1' - gem 'capybara-webkit', '~> 0.14' + gem 'capybara-webkit', '~> 1.4' gem 'accept_values_for' gem 'json_spec' end diff --git a/Gemfile.lock b/Gemfile.lock index c1969815..4e4b27c5 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -59,8 +59,8 @@ GEM rack (>= 1.0.0) rack-test (>= 0.5.4) xpath (~> 2.0) - capybara-webkit (0.14.2) - capybara (~> 2.0, >= 2.0.2) + capybara-webkit (1.4.1) + capybara (>= 2.3.0, < 2.5.0) json coderay (1.1.0) coffee-rails (4.0.1) @@ -278,7 +278,7 @@ DEPENDENCIES bourbon cancan capybara (~> 2.1) - capybara-webkit (~> 0.14) + capybara-webkit (~> 1.4) coffee-rails (~> 4.0.0) devise (~> 3.0.0.rc) draper From 08a25a06230a3c535909e2a1b33d23dd2e853216 Mon Sep 17 00:00:00 2001 From: Sebastian Schulze Date: Wed, 18 Feb 2015 20:15:12 +0100 Subject: [PATCH 399/499] Prepare Shellycloud deployment. Let's see where this leads us. :) --- Cloudfile | 11 +++++++++++ Gemfile | 6 ++---- Gemfile.lock | 55 +++++++++++----------------------------------------- Rakefile | 6 ------ 4 files changed, 24 insertions(+), 54 deletions(-) create mode 100644 Cloudfile diff --git a/Cloudfile b/Cloudfile new file mode 100644 index 00000000..6b82d163 --- /dev/null +++ b/Cloudfile @@ -0,0 +1,11 @@ +eurucamp-activities-staging: + ruby_version: 2.1.5 + environment: production # RAILS_ENV + domains: + - eurucamp-activities-staging.shellyapp.com + servers: + app1: + size: small + thin: 2 + databases: + - postgresql \ No newline at end of file diff --git a/Gemfile b/Gemfile index 222c610b..39279f09 100644 --- a/Gemfile +++ b/Gemfile @@ -4,7 +4,7 @@ ruby "2.1.5" gem 'rails', '4.0.8' gem 'pg' -gem 'unicorn' +gem 'thin' gem 'settingslogic' gem 'newrelic_rpm' gem 'devise', '~> 3.0.0.rc' @@ -30,7 +30,6 @@ gem 'coffee-rails', '~> 4.0.0' gem 'uglifier', '>= 1.0.3' group :development do - gem 'heroku_san', '~> 3.0.2' gem 'foreman' gem 'better_errors' gem 'binding_of_caller' @@ -52,6 +51,5 @@ end group :production, :staging do gem 'exception_notification', '~> 4.0.1' gem 'rack-robotz', '~> 0.0.3' - gem 'rails_log_stdout', github: 'heroku/rails_log_stdout' - gem 'rails3_serve_static_assets', github: 'heroku/rails3_serve_static_assets' + gem 'shelly-dependencies' end diff --git a/Gemfile.lock b/Gemfile.lock index 4e4b27c5..70e98d0b 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,15 +1,3 @@ -GIT - remote: git://github.com/heroku/rails3_serve_static_assets.git - revision: 84910ceb4ca2851d650e66d3b22f5008acf253f8 - specs: - rails3_serve_static_assets (0.0.1) - -GIT - remote: git://github.com/heroku/rails_log_stdout.git - revision: 01b5bcc572e3baed1618f4068920dd83bd491fb2 - specs: - rails_log_stdout (0.0.1) - GEM remote: https://rubygems.org/ specs: @@ -40,7 +28,6 @@ GEM multi_json (~> 1.3) thread_safe (~> 0.1) tzinfo (~> 0.3.37) - addressable (2.3.5) arel (4.0.2) bcrypt-ruby (3.1.2) better_errors (1.1.0) @@ -70,6 +57,7 @@ GEM coffee-script-source execjs coffee-script-source (1.7.0) + daemons (1.1.9) debug_inspector (0.0.2) devise (3.0.4) bcrypt-ruby (~> 3.0) @@ -85,10 +73,10 @@ GEM activesupport (>= 3.0) request_store (~> 1.0.3) erubis (2.7.0) + eventmachine (1.0.3) exception_notification (4.0.1) actionmailer (>= 3.0.4) activesupport (>= 3.0.4) - excon (0.31.0) execjs (2.0.2) factory_girl (4.3.0) activesupport (>= 3.0.0) @@ -108,20 +96,6 @@ GEM haml (>= 3.1, < 5.0) railties (>= 4.0.1) hashie (2.0.5) - heroku (3.3.0) - heroku-api (~> 0.3.17) - launchy (>= 0.3.2) - netrc (~> 0.7.7) - rest-client (~> 1.6.1) - rubyzip - heroku-api (0.3.17) - excon (~> 0.27) - multi_json (~> 1.8.2) - heroku_san (3.0.4) - activesupport - heroku (>= 2) - heroku-api (>= 0.1.2) - rake hike (1.2.3) httpauth (0.2.1) i18n (0.6.9) @@ -136,9 +110,6 @@ GEM rspec (~> 2.0) jwt (0.1.11) multi_json (>= 1.5) - kgio (2.9.1) - launchy (2.4.2) - addressable (~> 2.3) mail (2.5.4) mime-types (~> 1.16) treetop (~> 1.4.8) @@ -151,7 +122,6 @@ GEM neat (1.5.0) bourbon (>= 2.1) sass (>= 3.2) - netrc (0.7.7) newrelic_rpm (3.7.2.192) nokogiri (1.6.6.2) mini_portile (~> 0.6.0) @@ -200,12 +170,9 @@ GEM activesupport (= 4.0.8) rake (>= 0.8.7) thor (>= 0.18.1, < 2.0) - raindrops (0.12.0) rake (10.3.2) redcarpet (3.0.0) request_store (1.0.5) - rest-client (1.6.7) - mime-types (>= 1.16) rspec (2.14.1) rspec-core (~> 2.14.0) rspec-expectations (~> 2.14.0) @@ -222,7 +189,6 @@ GEM rspec-core (~> 2.14.0) rspec-expectations (~> 2.14.0) rspec-mocks (~> 2.14.0) - rubyzip (1.1.0) sass (3.2.14) sass-rails (4.0.3) railties (>= 4.0.0, < 5.0) @@ -230,6 +196,9 @@ GEM sprockets (~> 2.8, <= 2.11.0) sprockets-rails (~> 2.0) settingslogic (2.0.9) + shelly-dependencies (0.2.4) + rake + thin simple_form (3.0.1) actionpack (>= 4.0.0, < 4.1) activemodel (>= 4.0.0, < 4.1) @@ -247,6 +216,10 @@ GEM actionpack (>= 3.0) activesupport (>= 3.0) sprockets (~> 2.8) + thin (1.6.3) + daemons (~> 1.0, >= 1.0.9) + eventmachine (~> 1.0) + rack (~> 1.0) thor (0.19.1) thread_safe (0.3.4) tilt (1.4.1) @@ -259,10 +232,6 @@ GEM uglifier (2.4.0) execjs (>= 0.3.0) json (>= 1.8.0) - unicorn (4.8.2) - kgio (~> 2.6) - rack - raindrops (~> 0.7) warden (1.2.3) rack (>= 1.0) xpath (2.0.0) @@ -286,7 +255,6 @@ DEPENDENCIES factory_girl_rails (~> 4.2) foreman haml-rails - heroku_san (~> 3.0.2) jbuilder (~> 1.0.1) jquery-rails json_spec @@ -299,16 +267,15 @@ DEPENDENCIES pg rack-robotz (~> 0.0.3) rails (= 4.0.8) - rails3_serve_static_assets! rails_html_helpers - rails_log_stdout! redcarpet rspec-rails (~> 2.0) sass-rails (~> 4.0.3) settingslogic + shelly-dependencies simple_form (~> 3.0.1) simplecov sprockets-rails (~> 2.1.3) + thin turbolinks uglifier (>= 1.0.3) - unicorn diff --git a/Rakefile b/Rakefile index 1b103524..32df3527 100644 --- a/Rakefile +++ b/Rakefile @@ -4,9 +4,3 @@ require File.expand_path('../config/application', __FILE__) Activities::Application.load_tasks - -begin - require 'heroku_san/tasks' -rescue LoadError - STDERR.puts "Run `rake gems:install` to install heroku_san" -end \ No newline at end of file From ba79330c23c2db87e9eccf37dc8bcafdeca6eaf5 Mon Sep 17 00:00:00 2001 From: Sebastian Schulze Date: Wed, 27 May 2015 11:02:51 +0200 Subject: [PATCH 400/499] Shellycloud production app for activities --- Cloudfile | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Cloudfile b/Cloudfile index 6b82d163..7eb5cd98 100644 --- a/Cloudfile +++ b/Cloudfile @@ -3,6 +3,17 @@ eurucamp-activities-staging: environment: production # RAILS_ENV domains: - eurucamp-activities-staging.shellyapp.com + servers: + app1: + size: small + thin: 2 + databases: + - postgresql +eurucamp-activities-production: + ruby_version: 2.1.5 + environment: production # RAILS_ENV + domains: + - eurucamp-activities-production.shellyapp.com servers: app1: size: small From 25276c624c85849d9e38af9bc104633dd0ba9dce Mon Sep 17 00:00:00 2001 From: Sebastian Schulze Date: Wed, 27 May 2015 11:06:44 +0200 Subject: [PATCH 401/499] Bump Travis' Ruby version --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 05dbef63..7a8aa61d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,5 +4,5 @@ branches: before_script: cp config/database.yml.sample config/database.yml language: ruby rvm: - - 2.1.1 + - 2.1.5 script: bundle exec rake db:create db:migrate spec From 90ae802d3a31e46e14663869797f89adc44628c3 Mon Sep 17 00:00:00 2001 From: Bodo Tasche Date: Sat, 13 Jun 2015 09:49:04 +0200 Subject: [PATCH 402/499] add domains to cloudfile --- Cloudfile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Cloudfile b/Cloudfile index 7eb5cd98..20df2b49 100644 --- a/Cloudfile +++ b/Cloudfile @@ -3,6 +3,7 @@ eurucamp-activities-staging: environment: production # RAILS_ENV domains: - eurucamp-activities-staging.shellyapp.com + - rubyweek-staging.eurucamp.org servers: app1: size: small @@ -14,9 +15,10 @@ eurucamp-activities-production: environment: production # RAILS_ENV domains: - eurucamp-activities-production.shellyapp.com + - rubyweek.eurucamp.org servers: app1: size: small thin: 2 databases: - - postgresql \ No newline at end of file + - postgresql From 93d2811322e418fb0dc635289fd43fcb3ce57403 Mon Sep 17 00:00:00 2001 From: Bodo Tasche Date: Tue, 16 Jun 2015 09:00:03 +0200 Subject: [PATCH 403/499] add activities.eurucamp.org to Cloudfile --- Cloudfile | 1 + 1 file changed, 1 insertion(+) diff --git a/Cloudfile b/Cloudfile index 20df2b49..d7f1d09e 100644 --- a/Cloudfile +++ b/Cloudfile @@ -16,6 +16,7 @@ eurucamp-activities-production: domains: - eurucamp-activities-production.shellyapp.com - rubyweek.eurucamp.org + - activities.eurucamp.org servers: app1: size: small From 230e1471a8b7a1f6ffd3454200e7a156c6d702db Mon Sep 17 00:00:00 2001 From: Lucas Dohmen Date: Tue, 16 Jun 2015 20:23:58 +0000 Subject: [PATCH 404/499] Rebrand as rubyweek and adjust dates, closes #115 --- Gemfile.lock | 3 ++ README.md | 6 +-- app/assets/images/dummies/activity.jpg | Bin 16756 -> 0 bytes app/assets/images/header/logo.png | Bin 6944 -> 0 bytes app/assets/images/header/logo@2x.png | Bin 10661 -> 0 bytes app/assets/images/header/logo_small.png | Bin 4536 -> 0 bytes app/assets/images/header/logo_small@2x.png | Bin 7602 -> 0 bytes app/assets/images/rails.png | Bin 6646 -> 0 bytes .../images/shared/activity_placeholder.jpg | Bin 5208 -> 14163 bytes app/assets/stylesheets/_settings.sass | 1 + app/assets/stylesheets/partials/_header.sass | 39 ++++-------------- app/views/partials/_footer.html.haml | 5 ++- app/views/partials/_header.html.haml | 3 +- config/application.yml | 15 ++++--- config/locales/en.yml | 9 ++-- 15 files changed, 35 insertions(+), 46 deletions(-) delete mode 100644 app/assets/images/dummies/activity.jpg delete mode 100644 app/assets/images/header/logo.png delete mode 100644 app/assets/images/header/logo@2x.png delete mode 100644 app/assets/images/header/logo_small.png delete mode 100644 app/assets/images/header/logo_small@2x.png delete mode 100644 app/assets/images/rails.png diff --git a/Gemfile.lock b/Gemfile.lock index 70e98d0b..9e019c7f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -279,3 +279,6 @@ DEPENDENCIES thin turbolinks uglifier (>= 1.0.3) + +BUNDLED WITH + 1.10.3 diff --git a/README.md b/README.md index 2fb86437..d110bee3 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -# eurucamp Activities [![License](http://img.shields.io/:license-AGPL-0030c8.svg)](COPYRIGHT) [![Build Status](https://travis-ci.org/eurucamp/eurucamp-activities-2013.png?branch=master)](https://travis-ci.org/eurucamp/eurucamp-activities-2013) +# rubyweek [![License](http://img.shields.io/:license-AGPL-0030c8.svg)](COPYRIGHT) [![Build Status](https://travis-ci.org/eurucamp/eurucamp-activities-2013.png?branch=master)](https://travis-ci.org/eurucamp/eurucamp-activities-2013) -The eurucamp activities app is a small application to allow attendees to organize and plan small event in and around a conference. Users create events or sign up to created ones. Signup works through Github or Twitter. +The rubyweek app is a small application to allow attendees to organize and plan small event in and around a conference. Users create events or sign up to created ones. Signup works through Github or Twitter. The app is mobile friendly and easy to run on a free Heroku account. @@ -18,7 +18,7 @@ Don't use the eurucamp logo for your instance to avoid confusion. ## Examples -An instance of the app can be seen running at the [eurucamp activities page](http://activities.eurucamp.org). +An instance of the app can be seen running at the [rubyweek page](http://rubyweek.eurucamp.org). Screenshot: ![The activities app](screenshot.png) diff --git a/app/assets/images/dummies/activity.jpg b/app/assets/images/dummies/activity.jpg deleted file mode 100644 index 4f3cdbf96a99e3a5a85e5914742e9f08ab2506b6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16756 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 z zD2*;))gmQXI-boX0K|f8%aX}u=xeKssgq@S_{D>=f*e19+X_Xvd#6EBuro6U2x&qZ zFsY9JTRz*zPTA@0$JKRpy7u`ixiKpHI|m|x2pMG~hsZ2Sl2B)rja!O@RgGGI*V&Qn zXfwco&Ya@vEAMtPe-rbJmfzXxNR?r&Yt~wTwFw1dA1q1Tccn?MLy%$NH+Ce zffR@D>R{V7ir@DW$K~B{HzTR>ef2>U7Tuj`3Nb~woS2w6vQ|sM3+9D_N)+hY-m9-| zfnMweKAy4z^CTI<753fP2!^Dyp_O2h?^(O5dwMBqf^=>eepJqz;Dpy5J?7#I zWBNTmPM-j3=WCgX)arGABa>D8RxRbpXWZE@q?A-X>V6^@T|mZ6Pm5p#Ep*xVof-wY zn8XKl?Vi#JAx8YQZ+~7Cdjxb|iuyTajsh75>7pTA67N{a2vEWo$4yym+7yoPEk?QPPm?Ntzd0M1S{GOTCrxDZu=Z#fUYX2lSN;A z62ABX+x618PFg`skey^;4tQQ4j>=SY{|z`9cwiS+J0cDdgCjIj0Tj2Dn4FqHQg+qb zVsny84v*nGDov=-_Ij+`SJ>WB9AbJUW%r)noP}WheI|pBUccY0=QA!bI}r>4zjf)GvH)$L)3y!%|__!7f#$n#%}CZgW+J z55rhwaa*gX_4ZG~6~|*D=gvqi_+Bv72D@gg+pzA3GOaNs?2E(^(eTlA37*$e2~>y- z{o-p`Nc@=clW*nOEt_zMyXnQoWVo$c5{$V%}30XmP!e^v)T%24Bj;<-=zPS0-mr zGIX{fl=JzXqAL5PwRLwHVFafwLxZZh z8mf$FhL~NS{SjRB)(7~e;`v^nzuk^7sDzik!?r=HHrj*D^%u1mCbj=Wx-GE)5dM3T`1vyTQZ^;GnOH?@oo2z;<*fo18C1Ls+ zt#&_@^5rveaZ&fWOg6nn>3Yo~EG^A~RK7(D^q+i13f~pJxn_HDnsNwI>*ppF;N`Z+ z$u=f$yJYPL!13OD#n`c}wXKPpW=?FfJ z^qJynDAw>hYnDfB%f7y%+6!m2_xrklH}5IyB=ch3R;Z~GFjd_hk|i&kiTMQ>LFD=J z^OA@{=!8HGIoZnxQ2^$j;ik2-kK!VHxNr$|e`gRy)6T`ah;K0~=G1mUUkViGEBtzv z+IkawfMkpd0S^T;19lGORHf4&MRt|_Rwf5@23L{DH>ZUw8TDka>Iv3{Idyz0ftlYl zrpj^U;@bI1%03acO9D_Z@%Q4#oE?*E_RCx~3g9I=66LsM~Jv!*GUb;@8+d z=zw)x?wmbbk!z`iSNDLk-9S#_g?14`{%d@nufdVBJ)gh88cf%n(H_1q@*AdraO-9s<=5U6HGKOgV?U#^%?N%#6 zxnWbTyT8HKoin5z9eoGIB?Cf`fqW+8$A#gSBHJ!suo!SIjI0ZpK+e^5#C#*3f@qm! zXC@YeiwB2iL$he64fRWB8TK}^7q4aX3RZ>EV-bu8=NM2cPV*o8;2i#5*E)=~`SB{p z7*o}NA+G?dCjgF!Ge-rZK~%nq*mSK?cB00sE+Zap+=S6x!j{66ZZ!jELu>wRes(S~ z4YN`mx7;!r(gLTE;zrNts$Y%2ZO*Np?_m(6{5+D!8-m#121S@2B++_Guf4{YOens3 z3ycslSr;}uOtf&;vWvb6+LAP+9vOUjkb5^>+R=8ug`5(wQ04vgTIXR1wC>+KQiZGQ XtfwLcF)0AnL&pj_rPAmRpO*g@CqjD} diff --git a/app/assets/images/header/logo.png b/app/assets/images/header/logo.png deleted file mode 100644 index 2944ee2dfab808d5d3a51d1d69d2422a08fb9927..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6944 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 Z^0+;CzPebt_UE6KilVwgv7D*je*wCo#8&_S diff --git a/app/assets/images/header/logo@2x.png b/app/assets/images/header/logo@2x.png deleted file mode 100644 index 2a9ad1fb8c59ce6eae1e618b67ab4ef332f105fe..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10661 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 diff --git a/app/assets/images/header/logo_small.png b/app/assets/images/header/logo_small.png deleted file mode 100644 index 6770bcdd98342cb292b5b94545c572d177fb4424..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4536 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 E0EHI`egFUf diff --git a/app/assets/images/header/logo_small@2x.png b/app/assets/images/header/logo_small@2x.png deleted file mode 100644 index 2f7d07c7319a9a12ce521b1d5d9270f720e84664..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7602 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 AQ~&?~ diff --git a/app/assets/images/shared/activity_placeholder.jpg b/app/assets/images/shared/activity_placeholder.jpg index 80d75e76dae21e225ba6816fb367c5322a587fd0..928f087e9c492ce05759005c325b06dcc4c5899d 100644 GIT binary patch literal 14163 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�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 diff --git a/app/assets/stylesheets/_settings.sass b/app/assets/stylesheets/_settings.sass index f8845aa0..311708e2 100644 --- a/app/assets/stylesheets/_settings.sass +++ b/app/assets/stylesheets/_settings.sass @@ -24,6 +24,7 @@ $tablet-portrait-plus: min-width 860px 6 $red: #e55924 $blue: #0ca9c4 $gray: #5b5e61 +$purple: #671990 $green: #15d701 $yellow: #ffce00 diff --git a/app/assets/stylesheets/partials/_header.sass b/app/assets/stylesheets/partials/_header.sass index 716367ec..7f450856 100644 --- a/app/assets/stylesheets/partials/_header.sass +++ b/app/assets/stylesheets/partials/_header.sass @@ -4,11 +4,20 @@ h1 clear: both + margin-bottom: 0 a border: none + color: $purple + font-size: 180% + + h2 + font-weight: normal + margin-top: 0 nav.user-nav + float: right + ul @extend %inline-nav-list @@ -18,33 +27,3 @@ &:hover color: $blue - - - +media($smartphone-portrait) - nav.user-nav - float: right - - h1 - padding: 1em 0 - - a - width: 257px - height: 102px - display: inline-block - text-indent: -999em - background: image-url("header/logo.png") no-repeat - - +hidpi - background-size: 257px 102px - background-image: image-url("header/logo@2x.png") - - img - display: none - - - +media($smartphone-landscape) - nav.user-nav - float: right - - h1 - padding: 2em 0 diff --git a/app/views/partials/_footer.html.haml b/app/views/partials/_footer.html.haml index 107c14cd..27b283ff 100644 --- a/app/views/partials/_footer.html.haml +++ b/app/views/partials/_footer.html.haml @@ -4,5 +4,8 @@ %nav %ul %li - %a(href="http://euru.camp" title="#{t("footer_nav.eurucamp.title")}") + %a(href="http://eurucamp.org" title="#{t("footer_nav.eurucamp.title")}") = t("footer_nav.eurucamp.label") + %li + %a(href="http://jrubyconf.eu" title="#{t("footer_nav.jrubyconf.title")}") + = t("footer_nav.jrubyconf.label") diff --git a/app/views/partials/_header.html.haml b/app/views/partials/_header.html.haml index 0f913e32..84bc8cbd 100644 --- a/app/views/partials/_header.html.haml +++ b/app/views/partials/_header.html.haml @@ -5,4 +5,5 @@ -# the logo, link home %h1 = link_to "/" do - = image_tag "header/logo_small.png", :alt => "eurucamp Activities" + rubyweek + %h2 Events around eurucamp and JRubyConf diff --git a/config/application.yml b/config/application.yml index b1d5543b..4fd6f14d 100644 --- a/config/application.yml +++ b/config/application.yml @@ -1,20 +1,19 @@ defaults: &defaults event: - name: "eurucamp 2014" - start_time: 2014-07-28 00:00:00 - end_time: 2014-08-05 23:59:59 - host: "activities.eurucamp.org" + name: "rubyweek" + start_time: 2015-07-27 00:00:00 + end_time: 2015-08-09 23:59:59 + host: "rubyweek.eurucamp.org" mailers: from: "activities@eurucamp.org" errors: from: "exception@eurucamp.org" to: - - "pietia@appgrinder.pl" - - "florian@polarblau.com" + - "cfp-errors@eurucamp.or" seo: - title: "eurucamp Activities" + title: "rubyweek" author: "eurucamp team" - description: "List of companion events." + description: "List of events around eurucamp and JRubyConf" development: <<: *defaults diff --git a/config/locales/en.yml b/config/locales/en.yml index dbe5e14c..3c0d695e 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -15,7 +15,7 @@ en: user_nav: back_home: - label: Back to eurucamp activities + label: Back to rubyweek activities title: Go back to front page account: label: My Account @@ -136,5 +136,8 @@ en: label: Policy title: Policy and Code of Conduct eurucamp: - label: euru.camp - title: Head over to the main site + label: eurucamp + title: Head over to the eurucamp site + jrubyconf: + label: JRubyConf + title: Head over to the JRubyConf site From 66ce30f170e7c6e20370ef98c9281d828b665161 Mon Sep 17 00:00:00 2001 From: Lucas Dohmen Date: Wed, 17 Jun 2015 14:46:08 +0200 Subject: [PATCH 405/499] Run on domain rubyweek.org --- Cloudfile | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Cloudfile b/Cloudfile index d7f1d09e..3a1cef9d 100644 --- a/Cloudfile +++ b/Cloudfile @@ -15,8 +15,7 @@ eurucamp-activities-production: environment: production # RAILS_ENV domains: - eurucamp-activities-production.shellyapp.com - - rubyweek.eurucamp.org - - activities.eurucamp.org + - rubyweek.org servers: app1: size: small From 5dbb468d11b348c4f1eb2a12499434ca8d453080 Mon Sep 17 00:00:00 2001 From: Lucas Dohmen Date: Thu, 18 Jun 2015 07:48:00 +0000 Subject: [PATCH 406/499] Add dotenv-rails gem --- Gemfile | 1 + Gemfile.lock | 3 +++ 2 files changed, 4 insertions(+) diff --git a/Gemfile b/Gemfile index 39279f09..cf5479ce 100644 --- a/Gemfile +++ b/Gemfile @@ -12,6 +12,7 @@ gem 'omniauth', '~> 1.1.4' gem 'omniauth-github', '~> 1.1.0' gem 'omniauth-twitter', '~> 0.0.16' gem 'simple_form', '~> 3.0.1' +gem 'dotenv-rails' gem 'modernizr-rails' gem 'sprockets-rails', '~> 2.1.3' diff --git a/Gemfile.lock b/Gemfile.lock index 9e019c7f..64b6cd84 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -67,6 +67,8 @@ GEM diff-lcs (1.2.5) docile (1.1.3) dotenv (0.9.0) + dotenv-rails (0.9.0) + dotenv (= 0.9.0) draper (1.3.0) actionpack (>= 3.0) activemodel (>= 3.0) @@ -250,6 +252,7 @@ DEPENDENCIES capybara-webkit (~> 1.4) coffee-rails (~> 4.0.0) devise (~> 3.0.0.rc) + dotenv-rails draper exception_notification (~> 4.0.1) factory_girl_rails (~> 4.2) From 213764db185a95462137de58cf23d63b99ce2419 Mon Sep 17 00:00:00 2001 From: Lucas Dohmen Date: Thu, 18 Jun 2015 10:57:33 +0000 Subject: [PATCH 407/499] Remove unused domains from Cloudfile They don't work with GitHub and Twitter Auth --- Cloudfile | 2 -- 1 file changed, 2 deletions(-) diff --git a/Cloudfile b/Cloudfile index 3a1cef9d..d7e219e7 100644 --- a/Cloudfile +++ b/Cloudfile @@ -3,7 +3,6 @@ eurucamp-activities-staging: environment: production # RAILS_ENV domains: - eurucamp-activities-staging.shellyapp.com - - rubyweek-staging.eurucamp.org servers: app1: size: small @@ -14,7 +13,6 @@ eurucamp-activities-production: ruby_version: 2.1.5 environment: production # RAILS_ENV domains: - - eurucamp-activities-production.shellyapp.com - rubyweek.org servers: app1: From 9743a607c6979b27f9e47acc7e225e5a80625abf Mon Sep 17 00:00:00 2001 From: Lucas Dohmen Date: Thu, 18 Jun 2015 11:01:45 +0000 Subject: [PATCH 408/499] Remove heroku.yml, because we are not using Heroku --- config/heroku.yml | 11 ----------- 1 file changed, 11 deletions(-) delete mode 100644 config/heroku.yml diff --git a/config/heroku.yml b/config/heroku.yml deleted file mode 100644 index d46248fc..00000000 --- a/config/heroku.yml +++ /dev/null @@ -1,11 +0,0 @@ -production: - stack: cedar - app: eurucamp-activities-2014 - config: - BUNDLE_WITHOUT: "development:test" - -staging: - stack: cedar - app: eurucamp-activities-2014-dev - config: &default - BUNDLE_WITHOUT: "development:test" From 7bf04d1f36a11a592034c3a5dc17e648bf477b9b Mon Sep 17 00:00:00 2001 From: Alex Coles Date: Thu, 18 Jun 2015 10:39:24 +0200 Subject: [PATCH 409/499] Bump rspec-rails to latest (~> 3.3) Signed-off-by: Alex Coles --- Gemfile | 2 +- Gemfile.lock | 47 ++++++++++++++++++++++++++--------------------- 2 files changed, 27 insertions(+), 22 deletions(-) diff --git a/Gemfile b/Gemfile index cf5479ce..fe9d329d 100644 --- a/Gemfile +++ b/Gemfile @@ -37,7 +37,7 @@ group :development do end group :test, :development do - gem "rspec-rails", '~> 2.0' + gem 'rspec-rails', '~> 3.3' gem 'factory_girl_rails','~> 4.2' end diff --git a/Gemfile.lock b/Gemfile.lock index 64b6cd84..c272280f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -107,9 +107,9 @@ GEM railties (>= 3.0, < 5.0) thor (>= 0.14, < 2.0) json (1.8.2) - json_spec (1.1.1) + json_spec (1.1.4) multi_json (~> 1.0) - rspec (~> 2.0) + rspec (>= 2.0, < 4.0) jwt (0.1.11) multi_json (>= 1.5) mail (2.5.4) @@ -119,7 +119,7 @@ GEM mini_portile (0.6.2) minitest (4.7.5) modernizr-rails (2.7.1) - multi_json (1.8.4) + multi_json (1.11.1) multipart-post (2.0.0) neat (1.5.0) bourbon (>= 2.1) @@ -175,22 +175,27 @@ GEM rake (10.3.2) redcarpet (3.0.0) request_store (1.0.5) - rspec (2.14.1) - rspec-core (~> 2.14.0) - rspec-expectations (~> 2.14.0) - rspec-mocks (~> 2.14.0) - rspec-core (2.14.7) - rspec-expectations (2.14.5) - diff-lcs (>= 1.1.3, < 2.0) - rspec-mocks (2.14.5) - rspec-rails (2.14.1) - actionpack (>= 3.0) - activemodel (>= 3.0) - activesupport (>= 3.0) - railties (>= 3.0) - rspec-core (~> 2.14.0) - rspec-expectations (~> 2.14.0) - rspec-mocks (~> 2.14.0) + rspec (3.3.0) + rspec-core (~> 3.3.0) + rspec-expectations (~> 3.3.0) + rspec-mocks (~> 3.3.0) + rspec-core (3.3.0) + rspec-support (~> 3.3.0) + rspec-expectations (3.3.0) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.3.0) + rspec-mocks (3.3.0) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.3.0) + rspec-rails (3.3.1) + actionpack (>= 3.0, < 4.3) + activesupport (>= 3.0, < 4.3) + railties (>= 3.0, < 4.3) + rspec-core (~> 3.3.0) + rspec-expectations (~> 3.3.0) + rspec-mocks (~> 3.3.0) + rspec-support (~> 3.3.0) + rspec-support (3.3.0) sass (3.2.14) sass-rails (4.0.3) railties (>= 4.0.0, < 5.0) @@ -272,7 +277,7 @@ DEPENDENCIES rails (= 4.0.8) rails_html_helpers redcarpet - rspec-rails (~> 2.0) + rspec-rails (~> 3.3) sass-rails (~> 4.0.3) settingslogic shelly-dependencies @@ -284,4 +289,4 @@ DEPENDENCIES uglifier (>= 1.0.3) BUNDLED WITH - 1.10.3 + 1.10.4 From ea7f1bccf62fd42cd1d98b228bb6bd885ecfda51 Mon Sep 17 00:00:00 2001 From: Alex Coles Date: Thu, 18 Jun 2015 19:09:27 +0200 Subject: [PATCH 410/499] Add rspec-its, rspec-activemodel-mocks Restore functionality removed in RSpec 3.x series. Signed-off-by: Alex Coles --- Gemfile | 2 ++ Gemfile.lock | 9 +++++++++ 2 files changed, 11 insertions(+) diff --git a/Gemfile b/Gemfile index fe9d329d..a84c54ec 100644 --- a/Gemfile +++ b/Gemfile @@ -42,6 +42,8 @@ group :test, :development do end group :test do + gem 'rspec-its' + gem 'rspec-activemodel-mocks' gem 'simplecov', require: false gem 'capybara', '~> 2.1' gem 'capybara-webkit', '~> 1.4' diff --git a/Gemfile.lock b/Gemfile.lock index c272280f..fd25f315 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -179,11 +179,18 @@ GEM rspec-core (~> 3.3.0) rspec-expectations (~> 3.3.0) rspec-mocks (~> 3.3.0) + rspec-activemodel-mocks (1.0.1) + activemodel (>= 3.0) + activesupport (>= 3.0) + rspec-mocks (>= 2.99, < 4.0) rspec-core (3.3.0) rspec-support (~> 3.3.0) rspec-expectations (3.3.0) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.3.0) + rspec-its (1.2.0) + rspec-core (>= 3.0.0) + rspec-expectations (>= 3.0.0) rspec-mocks (3.3.0) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.3.0) @@ -277,6 +284,8 @@ DEPENDENCIES rails (= 4.0.8) rails_html_helpers redcarpet + rspec-activemodel-mocks + rspec-its rspec-rails (~> 3.3) sass-rails (~> 4.0.3) settingslogic From 51a44be20d10c065ab6f466b63942bf3041e7d6b Mon Sep 17 00:00:00 2001 From: Alex Coles Date: Thu, 18 Jun 2015 19:14:05 +0200 Subject: [PATCH 411/499] Convert specs to RSpec 3.3.0 syntax with Transpec This conversion is done by Transpec 3.1.0 with the following command: transpec * 79 conversions from: it { should ... } to: it { is_expected.to ... } * 48 conversions from: obj.should_receive(:message) to: expect(obj).to receive(:message) * 40 conversions from: == expected to: eq(expected) * 27 conversions from: obj.stub(:message) to: allow(obj).to receive(:message) * 12 conversions from: it { should_not ... } to: it { is_expected.not_to ... } * 3 conversions from: obj.stub(:message => value) to: allow(obj).to receive_messages(:message => value) * 1 addition of: RSpec.configure { |c| c.infer_spec_type_from_file_location! } For more details: https://github.com/yujinakayama/transpec#supported-conversions --- .../controllers/activities_controller_spec.rb | 56 ++++----- .../authentications_controller_spec.rb | 22 ++-- .../participation_controller_spec.rb | 28 ++--- spec/decorators/activity_decorator_spec.rb | 10 +- spec/models/activity_spec.rb | 110 +++++++++--------- spec/models/authentication_spec.rb | 6 +- spec/models/event_spec.rb | 36 +++--- spec/models/participation_spec.rb | 8 +- spec/models/user_spec.rb | 50 ++++---- spec/other/factories_spec.rb | 2 +- spec/spec_helper.rb | 11 ++ spec/support/helpers/controller_helpers.rb | 10 +- 12 files changed, 180 insertions(+), 169 deletions(-) diff --git a/spec/controllers/activities_controller_spec.rb b/spec/controllers/activities_controller_spec.rb index 385a1b38..bf7ab023 100644 --- a/spec/controllers/activities_controller_spec.rb +++ b/spec/controllers/activities_controller_spec.rb @@ -9,7 +9,7 @@ let(:current_event) { double(:current_event) } before do - controller.stub(:current_event).and_return(current_event) + allow(controller).to receive(:current_event).and_return(current_event) end describe "#index" do @@ -20,11 +20,11 @@ let(:counters) { double(:counters) } before do - current_event.should_receive(:search_activities).with(current_user, nil, nil).and_return(activities) - current_event.should_receive(:counters).with(current_user).and_return(counters) + expect(current_event).to receive(:search_activities).with(current_user, nil, nil).and_return(activities) + expect(current_event).to receive(:counters).with(current_user).and_return(counters) end - it { should render_template(:index) } + it { is_expected.to render_template(:index) } end describe "#new" do @@ -32,10 +32,10 @@ before do sign_in(current_user) - current_event.should_receive(:new_activity).with(current_user, {}).and_return(activity) + expect(current_event).to receive(:new_activity).with(current_user, {}).and_return(activity) end - it { should render_template(:new) } + it { is_expected.to render_template(:new) } end describe "#edit" do @@ -43,7 +43,7 @@ before do sign_in(current_user) - current_event.should_receive(:activity).with(activity_id).and_return(activity) + expect(current_event).to receive(:activity).with(activity_id).and_return(activity) end context "activity doesn't exist" do @@ -55,11 +55,11 @@ context "activity exists" do before do - activity.should_receive(:decorate).and_return(activity) + expect(activity).to receive(:decorate).and_return(activity) should_authorize(:edit, activity) end - it { should render_template(:edit) } + it { is_expected.to render_template(:edit) } end end @@ -69,7 +69,7 @@ before do sign_in(current_user) - current_event.should_receive(:activity).with(activity_id).and_return(activity) + expect(current_event).to receive(:activity).with(activity_id).and_return(activity) end context "activity doesn't exist" do @@ -81,10 +81,10 @@ context "activity exists" do before do - activity.should_receive(:decorate).and_return(activity) + expect(activity).to receive(:decorate).and_return(activity) end - it { should render_template(:show) } + it { is_expected.to render_template(:show) } end end @@ -100,11 +100,11 @@ let(:params) { {activity: attributes} } before do - current_event.should_receive(:new_activity).with(current_user, attributes.with_indifferent_access).and_return(activity) - activity.should_receive(:save).and_return(true) + expect(current_event).to receive(:new_activity).with(current_user, attributes.with_indifferent_access).and_return(activity) + expect(activity).to receive(:save).and_return(true) end - it { should redirect_to activities_path } + it { is_expected.to redirect_to activities_path } end context "invalid parameters" do @@ -112,11 +112,11 @@ let(:params) { {activity: {x: 10}} } before do - current_event.should_receive(:new_activity).with(current_user, {}).and_return(activity) - activity.should_receive(:save).and_return(false) + expect(current_event).to receive(:new_activity).with(current_user, {}).and_return(activity) + expect(activity).to receive(:save).and_return(false) end - it { should render_template(:new) } + it { is_expected.to render_template(:new) } end end @@ -126,8 +126,8 @@ before do sign_in(current_user) - current_event.should_receive(:activity).with(activity_id).and_return(activity) - activity.should_receive(:decorate).and_return(activity) + expect(current_event).to receive(:activity).with(activity_id).and_return(activity) + expect(activity).to receive(:decorate).and_return(activity) should_authorize(:update, activity) end @@ -135,10 +135,10 @@ let(:attributes) { {location: "Location", name: "Name", start_time: 1.day.ago.to_s, end_time: 2.days.ago.to_s} } before do - activity.should_receive(:update_attributes).with(attributes.with_indifferent_access).and_return(true) + expect(activity).to receive(:update_attributes).with(attributes.with_indifferent_access).and_return(true) end - it { should redirect_to edit_activity_path(activity) } + it { is_expected.to redirect_to edit_activity_path(activity) } end context "invalid parameters" do @@ -146,10 +146,10 @@ let(:activity) { invalid_activity } before do - activity.should_receive(:update_attributes).with(attributes.with_indifferent_access).and_return(false) + expect(activity).to receive(:update_attributes).with(attributes.with_indifferent_access).and_return(false) end - it { should render_template(:edit) } + it { is_expected.to render_template(:edit) } end end @@ -158,13 +158,13 @@ before do sign_in(current_user) - current_event.should_receive(:activity).with(activity_id).and_return(activity) - activity.should_receive(:decorate).and_return(activity) + expect(current_event).to receive(:activity).with(activity_id).and_return(activity) + expect(activity).to receive(:decorate).and_return(activity) should_authorize(:destroy, activity) - activity.should_receive(:destroy).and_return(true) + expect(activity).to receive(:destroy).and_return(true) end - it { should redirect_to activities_path } + it { is_expected.to redirect_to activities_path } end diff --git a/spec/controllers/authentications_controller_spec.rb b/spec/controllers/authentications_controller_spec.rb index 8bb1c914..4c2c5eca 100644 --- a/spec/controllers/authentications_controller_spec.rb +++ b/spec/controllers/authentications_controller_spec.rb @@ -9,7 +9,7 @@ before do @request.env["devise.mapping"] = Devise.mappings[:user] - current_user.stub(:authentications).and_return(authentications) + allow(current_user).to receive(:authentications).and_return(authentications) end describe '#create' do @@ -21,12 +21,12 @@ context "no authentication" do let(:omniauth_data) { { provider: 'none', uuid: 'none', info: { email: 'test@john.com', name: 'test'} } } - it { should redirect_to new_user_registration_url } + it { is_expected.to redirect_to new_user_registration_url } end context "no authentication or incorrect details" do let(:omniauth_data) { { provider: 'none', uuid: 'none', info: {} } } - it { should redirect_to new_user_registration_url } + it { is_expected.to redirect_to new_user_registration_url } end context "user already logged in" do @@ -34,9 +34,9 @@ before do sign_in(current_user) - authentications.should_receive(:find_or_create_by).with({provider: nil, uid: nil}) - current_user.should_receive(:apply_provider_handle).with(omniauth_data) - current_user.should_receive(:save).and_return(true) + expect(authentications).to receive(:find_or_create_by).with({provider: nil, uid: nil}) + expect(current_user).to receive(:apply_provider_handle).with(omniauth_data) + expect(current_user).to receive(:save).and_return(true) end context "when HTTP_REFERER set" do @@ -44,12 +44,12 @@ request.env['HTTP_REFERER'] = 'http://backtoreality/' end - it { should redirect_to 'http://backtoreality/' } + it { is_expected.to redirect_to 'http://backtoreality/' } end context "when HTTP_REFERER not set" do - it { should redirect_to edit_user_registration_path } + it { is_expected.to redirect_to edit_user_registration_path } end end @@ -60,12 +60,12 @@ before do sign_in(current_user) - authentications.should_receive(:find).with(authentication_id).and_return(authentication) + expect(authentications).to receive(:find).with(authentication_id).and_return(authentication) should_authorize(:destroy, authentication) - authentication.should_receive(:destroy).and_return(true) + expect(authentication).to receive(:destroy).and_return(true) end - it { should redirect_to authentications_path } + it { is_expected.to redirect_to authentications_path } end end diff --git a/spec/controllers/participation_controller_spec.rb b/spec/controllers/participation_controller_spec.rb index 8fdf0c5f..25925e2e 100644 --- a/spec/controllers/participation_controller_spec.rb +++ b/spec/controllers/participation_controller_spec.rb @@ -9,8 +9,8 @@ let(:current_event) { double(:current_event) } before do - activity.stub(:reload).and_return(activity) - controller.stub(:current_event).and_return(current_event) + allow(activity).to receive(:reload).and_return(activity) + allow(controller).to receive(:current_event).and_return(current_event) end describe "#create" do @@ -20,29 +20,29 @@ before do sign_in(current_user) - current_event.should_receive(:activity).with(activity_id).and_return(activity) - activity.should_receive(:decorate).and_return(activity) - activity.should_receive(:new_participation).with(current_user).and_return(participation) + expect(current_event).to receive(:activity).with(activity_id).and_return(activity) + expect(activity).to receive(:decorate).and_return(activity) + expect(activity).to receive(:new_participation).with(current_user).and_return(participation) end context "valid parameters" do before do - participation.should_receive(:save).and_return(true) + expect(participation).to receive(:save).and_return(true) end - it { should render_template(:create) } + it { is_expected.to render_template(:create) } end context "user is already a participant" do let(:participation) { invalid_participation } before do - participation.should_receive(:save).and_return(false) + expect(participation).to receive(:save).and_return(false) end # TODO: moar specs needed - it { should render_template(:create) } + it { is_expected.to render_template(:create) } end end end @@ -54,17 +54,17 @@ before do sign_in(current_user) - current_event.should_receive(:activity).with(activity_id).and_return(activity) - activity.should_receive(:decorate).and_return(activity) + expect(current_event).to receive(:activity).with(activity_id).and_return(activity) + expect(activity).to receive(:decorate).and_return(activity) - activity.should_receive(:participation).with(current_user).and_return(participation) + expect(activity).to receive(:participation).with(current_user).and_return(participation) should_authorize(:destroy, participation) - participation.should_receive(:destroy).and_return(true) + expect(participation).to receive(:destroy).and_return(true) end # TODO: moar specs needed - it { should render_template(:destroy) } + it { is_expected.to render_template(:destroy) } end end diff --git a/spec/decorators/activity_decorator_spec.rb b/spec/decorators/activity_decorator_spec.rb index c1b86e6b..22b3d3d5 100644 --- a/spec/decorators/activity_decorator_spec.rb +++ b/spec/decorators/activity_decorator_spec.rb @@ -8,32 +8,32 @@ subject { decorator.open_spots } before do - activity.stub(:participations_count).and_return(count) + allow(activity).to receive(:participations_count).and_return(count) end context "no participants" do let(:count) { 0 } - it { should == 20 } + it { is_expected.to eq(20) } end context "some participants" do let(:count) { 5 } - it { should == 15 } + it { is_expected.to eq(15) } end context "too many participants" do let(:count) { 25 } - it { should == 0 } + it { is_expected.to eq(0) } end context "you're drunk Rails, go home!" do # it seems Rails counter caches return negative values sometimes let(:count) { -5 } - it { should == 20 } + it { is_expected.to eq(20) } end end end diff --git a/spec/models/activity_spec.rb b/spec/models/activity_spec.rb index 4a291ea0..33c212f7 100644 --- a/spec/models/activity_spec.rb +++ b/spec/models/activity_spec.rb @@ -32,17 +32,17 @@ subject { Activity.recent } before do - Activity.should_receive(:find_recent).and_return(recent_activities) + expect(Activity).to receive(:find_recent).and_return(recent_activities) end - it { should == recent_activities } + it { is_expected.to eq(recent_activities) } end describe "#limit_of_participants" do subject { activity.limit_of_participants } context "limit of participants is not set" do - it { should == 10 } + it { is_expected.to eq(10) } end end @@ -52,28 +52,28 @@ context "limit of participants is not set" do before do - activity.stub(:limit_of_participants).and_return(nil) + allow(activity).to receive(:limit_of_participants).and_return(nil) end - it { should == 0 } + it { is_expected.to eq(0) } end context "limit of participants is set" do before do - activity.stub(:limit_of_participants).and_return(10) - activity.stub(:participations_count).and_return(8) + allow(activity).to receive(:limit_of_participants).and_return(10) + allow(activity).to receive(:participations_count).and_return(8) end - it { should == 80 } + it { is_expected.to eq(80) } end context "no participants" do before do - activity.stub(:limit_of_participants).and_return(10) - activity.stub(:participations_count).and_return(0) + allow(activity).to receive(:limit_of_participants).and_return(10) + allow(activity).to receive(:participations_count).and_return(0) end - it { should == 0 } + it { is_expected.to eq(0) } end end @@ -82,18 +82,18 @@ context "no limit set" do before do - activity.stub(:limit_of_participants).and_return(nil) + allow(activity).to receive(:limit_of_participants).and_return(nil) end - it { should == true } + it { is_expected.to eq(true) } end context "limit set" do before do - activity.stub(:limit_of_participants).and_return(10) + allow(activity).to receive(:limit_of_participants).and_return(10) end - it { should == false } + it { is_expected.to eq(false) } end end @@ -102,29 +102,29 @@ context "anytime set" do before do - activity.stub(:anytime?).and_return(true) + allow(activity).to receive(:anytime?).and_return(true) end - it { should == true } + it { is_expected.to eq(true) } end context "today" do before do - activity.stub(:start_time).and_return(2.days.ago.to_time) - activity.stub(:end_time).and_return(2.days.from_now.to_time) + allow(activity).to receive(:start_time).and_return(2.days.ago.to_time) + allow(activity).to receive(:end_time).and_return(2.days.from_now.to_time) end - it { should == true } + it { is_expected.to eq(true) } end context "not today" do before do - activity.stub(:anytime?).and_return(false) - activity.stub(:start_time).and_return(2.days.from_now.to_time) - activity.stub(:end_time).and_return(10.days.from_now.to_time) + allow(activity).to receive(:anytime?).and_return(false) + allow(activity).to receive(:start_time).and_return(2.days.from_now.to_time) + allow(activity).to receive(:end_time).and_return(10.days.from_now.to_time) end - it { should == false } + it { is_expected.to eq(false) } end end @@ -134,29 +134,29 @@ context "full" do before do - activity.stub(:limit_of_participants).and_return(10) - activity.stub(:participations_count).and_return(10) + allow(activity).to receive(:limit_of_participants).and_return(10) + allow(activity).to receive(:participations_count).and_return(10) end - it { should == true } + it { is_expected.to eq(true) } end context "too full" do before do - activity.stub(:limit_of_participants).and_return(10) - activity.stub(:participations_count).and_return(11) + allow(activity).to receive(:limit_of_participants).and_return(10) + allow(activity).to receive(:participations_count).and_return(11) end - it { should == true } + it { is_expected.to eq(true) } end context "not full" do before do - activity.stub(:limit_of_participants).and_return(10) - activity.stub(:participations_count).and_return(8) + allow(activity).to receive(:limit_of_participants).and_return(10) + allow(activity).to receive(:participations_count).and_return(8) end - it { should == false } + it { is_expected.to eq(false) } end end @@ -166,18 +166,18 @@ context "in the future" do before do - activity.stub(:start_time).and_return(1.days.from_now.to_time) + allow(activity).to receive(:start_time).and_return(1.days.from_now.to_time) end - it { should == true } + it { is_expected.to eq(true) } end context "not in the future" do before do - activity.stub(:start_time).and_return(Time.now) + allow(activity).to receive(:start_time).and_return(Time.now) end - it { should == false } + it { is_expected.to eq(false) } end end @@ -193,48 +193,48 @@ its(:participant) { should == user } its(:activity) { should == activity } - it { should == new_participation } + it { is_expected.to eq(new_participation) } end describe "validations" do specify { expect { FactoryGirl.create(:activity).dup.save! }.to raise_exception(ActiveRecord::RecordInvalid) } - it { should accept_values_for(:name, "Football game" ) } - it { should_not accept_values_for(:name, "", nil) } + it { is_expected.to accept_values_for(:name, "Football game" ) } + it { is_expected.not_to accept_values_for(:name, "", nil) } - it { should accept_values_for(:description, nil, "", "Wear some solid boots!")} + it { is_expected.to accept_values_for(:description, nil, "", "Wear some solid boots!")} - it { should_not accept_values_for(:event, nil) } + it { is_expected.not_to accept_values_for(:event, nil) } - it { should accept_values_for(:location, "football pitch" ) } - it { should_not accept_values_for(:location, "", nil) } + it { is_expected.to accept_values_for(:location, "football pitch" ) } + it { is_expected.not_to accept_values_for(:location, "", nil) } - it { should accept_values_for(:start_time, Time.now, nil) } + it { is_expected.to accept_values_for(:start_time, Time.now, nil) } - it { should accept_values_for(:end_time, Time.now, nil) } + it { is_expected.to accept_values_for(:end_time, Time.now, nil) } - it { should accept_values_for(:limit_of_participants, nil, 12, 100) } - it { should_not accept_values_for(:limit_of_participants, -1, 0) } + it { is_expected.to accept_values_for(:limit_of_participants, nil, 12, 100) } + it { is_expected.not_to accept_values_for(:limit_of_participants, -1, 0) } - it { should accept_values_for(:image_url, nil, "http://com.com/image.gif", "https://com.com/image.gif") } - it { should_not accept_values_for(:image_url, "http://com.co ge.gif", "ssh://com.com/image.gif", "blah")} + it { is_expected.to accept_values_for(:image_url, nil, "http://com.com/image.gif", "https://com.com/image.gif") } + it { is_expected.not_to accept_values_for(:image_url, "http://com.co ge.gif", "ssh://com.com/image.gif", "blah")} context "invalid time frame (wrong order)" do subject { FactoryGirl.build(:activity, start_time: 10.days.ago.to_time, anytime: false) } - it { should_not accept_values_for(:end_time, 15.days.ago.to_time) } + it { is_expected.not_to accept_values_for(:end_time, 15.days.ago.to_time) } end context "invalid time frame (out of scope)" do let(:event) { Event.new("A name", 2.days.ago.to_time, 2.days.from_now.to_time ) } subject { FactoryGirl.build(:activity, event: event, start_time: 10.days.ago.to_time, anytime: false) } - it { should accept_values_for(:start_time, 1.days.ago.to_time) } - it { should_not accept_values_for(:start_time, 15.days.ago.to_time) } + it { is_expected.to accept_values_for(:start_time, 1.days.ago.to_time) } + it { is_expected.not_to accept_values_for(:start_time, 15.days.ago.to_time) } - it { should accept_values_for(:end_time, 1.days.from_now.to_time) } - it { should_not accept_values_for(:end_time, 3.days.from_now.to_time) } + it { is_expected.to accept_values_for(:end_time, 1.days.from_now.to_time) } + it { is_expected.not_to accept_values_for(:end_time, 3.days.from_now.to_time) } end end diff --git a/spec/models/authentication_spec.rb b/spec/models/authentication_spec.rb index 3564c566..0baa9a1b 100644 --- a/spec/models/authentication_spec.rb +++ b/spec/models/authentication_spec.rb @@ -5,19 +5,19 @@ specify { expect { FactoryGirl.create(:activity).dup.save! }.to raise_exception(ActiveRecord::RecordInvalid) } - it { should accept_values_for(:user_id, 10) } + it { is_expected.to accept_values_for(:user_id, 10) } it do pending "Currently Removed" # should_not accept_values_for(:user_id, nil, "") } end - it { should accept_values_for(:provider, "github", "twitter") } + it { is_expected.to accept_values_for(:provider, "github", "twitter") } it do pending "Currently Removed" # should_not accept_values_for(:provider, nil, "") } end - it { should accept_values_for(:uid, "asd123dasd", "x") } + it { is_expected.to accept_values_for(:uid, "asd123dasd", "x") } it do pending "Currently Removed" # should_not accept_values_for(:uid, nil, "") } diff --git a/spec/models/event_spec.rb b/spec/models/event_spec.rb index 311db850..15cb6d3c 100644 --- a/spec/models/event_spec.rb +++ b/spec/models/event_spec.rb @@ -44,10 +44,10 @@ subject { event.activity(activity_id) } before do - event.should_receive(:find_activity).with(activity_id).and_return(activity) + expect(event).to receive(:find_activity).with(activity_id).and_return(activity) end - it { should == activity } + it { is_expected.to eq(activity) } its(:event) { should == event } end @@ -59,7 +59,7 @@ event.recent_activities_fetcher = ->{ recent_activities } end - it { should == recent_activities } + it { is_expected.to eq(recent_activities) } end describe "#all_activities" do @@ -71,7 +71,7 @@ event.all_activities_fetcher = ->{ activities } end - it { should == activities } + it { is_expected.to eq(activities) } end describe "#search_activities" do @@ -84,17 +84,17 @@ event.all_activities_fetcher = ->{ activities } end - it { should == activities } + it { is_expected.to eq(activities) } end context "by name" do subject { event.search_activities(nil, "yummy", "all") } before do - Activity.should_receive(:with_name_like).with("yummy").and_return(activities) + expect(Activity).to receive(:with_name_like).with("yummy").and_return(activities) end - it { should == activities } + it { is_expected.to eq(activities) } end context "with filter" do @@ -105,43 +105,43 @@ let(:filter) { "owner" } before do - Activity.should_receive(:all_activities).and_return(proxy) - proxy.should_receive(:created_by).with(user).and_return(activities) + expect(Activity).to receive(:all_activities).and_return(proxy) + expect(proxy).to receive(:created_by).with(user).and_return(activities) end - it { should == activities } + it { is_expected.to eq(activities) } end context "participant" do let(:filter) { "participant" } before do - Activity.should_receive(:all_activities).and_return(proxy) - proxy.should_receive(:participated_by).with(user).and_return(activities) + expect(Activity).to receive(:all_activities).and_return(proxy) + expect(proxy).to receive(:participated_by).with(user).and_return(activities) end - it { should == activities } + it { is_expected.to eq(activities) } end context "today" do let(:filter) { "today" } before do - Activity.should_receive(:all_activities).and_return(proxy) - proxy.should_receive(:today).and_return(activities) + expect(Activity).to receive(:all_activities).and_return(proxy) + expect(proxy).to receive(:today).and_return(activities) end - it { should == activities } + it { is_expected.to eq(activities) } end context "wrong filter" do let(:filter) { "ownerx" } before do - Activity.should_receive(:all_activities).and_return(activities) + expect(Activity).to receive(:all_activities).and_return(activities) end - it { should == activities } + it { is_expected.to eq(activities) } end end diff --git a/spec/models/participation_spec.rb b/spec/models/participation_spec.rb index 0748c9ea..a9e730f8 100644 --- a/spec/models/participation_spec.rb +++ b/spec/models/participation_spec.rb @@ -6,11 +6,11 @@ specify { expect { FactoryGirl.create(:participation).dup.save! }.to raise_exception(ActiveRecord::RecordInvalid) } - it { should accept_values_for(:user_id, 1 ) } - it { should_not accept_values_for(:user_id, "", nil) } + it { is_expected.to accept_values_for(:user_id, 1 ) } + it { is_expected.not_to accept_values_for(:user_id, "", nil) } - it { should accept_values_for(:activity_id, 1 ) } - it { should_not accept_values_for(:activity_id, "", nil) } + it { is_expected.to accept_values_for(:activity_id, 1 ) } + it { is_expected.not_to accept_values_for(:activity_id, "", nil) } end diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 72d6301f..e2fe2914 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -8,18 +8,18 @@ context "not connected with Twitter account" do before do - user.should_receive(:provider_connected?).with("twitter").and_return(false) + expect(user).to receive(:provider_connected?).with("twitter").and_return(false) end - it { should == false} + it { is_expected.to eq(false)} end context "connected with Twitter account" do before do - user.should_receive(:provider_connected?).with("twitter").and_return(true) + expect(user).to receive(:provider_connected?).with("twitter").and_return(true) end - it { should == true } + it { is_expected.to eq(true) } end end @@ -30,18 +30,18 @@ context "not connected with Github account" do before do - user.should_receive(:provider_connected?).with("github").and_return(false) + expect(user).to receive(:provider_connected?).with("github").and_return(false) end - it { should == false} + it { is_expected.to eq(false)} end context "connected with Github account" do before do - user.should_receive(:provider_connected?).with("github").and_return(true) + expect(user).to receive(:provider_connected?).with("github").and_return(true) end - it { should == true } + it { is_expected.to eq(true) } end end @@ -52,7 +52,7 @@ context "no data provided" do let(:params) { {} } - it { should == user } + it { is_expected.to eq(user) } end context "data provided" do @@ -60,7 +60,7 @@ specify { expect{subject}.to change(user, :github_handle).to("johnny") } - it { should == user } + it { is_expected.to eq(user) } end end @@ -71,18 +71,18 @@ context "account connected" do context "password set" do - it { should == true } + it { is_expected.to eq(true) } end end context "no account connected" do context "password set" do - it { should == true } + it { is_expected.to eq(true) } end context "password not set" do let(:user) { User.new } - it { should == false } + it { is_expected.to eq(false) } end end end @@ -92,17 +92,17 @@ let(:user) { FactoryGirl.create(:user) } context "no account connected" do - it { should == true } + it { is_expected.to eq(true) } end context "account connected" do let(:authentications) { [double(:authentication)] } before do - user.should_receive(:authentications).and_return(authentications) + expect(user).to receive(:authentications).and_return(authentications) end - it { should == false } + it { is_expected.to eq(false) } end end @@ -136,12 +136,12 @@ context "no data provided" do let(:params) { {} } - it { should be_a_kind_of(Authentication) } + it { is_expected.to be_a_kind_of(Authentication) } its(:user) { should == user } it do pending "Currently Removed" - should be_invalid + is_expected.to be_invalid end end @@ -153,7 +153,7 @@ specify { expect{subject}.to_not change(user, :email) } - it { should be_a_kind_of(Authentication) } + it { is_expected.to be_a_kind_of(Authentication) } its(:user) { should == user } end @@ -162,7 +162,7 @@ specify { expect{subject}.to_not change(user, :email) } - it { should be_a_kind_of(Authentication) } + it { is_expected.to be_a_kind_of(Authentication) } its(:user) { should == user } end end @@ -172,13 +172,13 @@ subject { User.new } - it { should accept_values_for(:email, "xx@xx.com" ) } - it { should_not accept_values_for(:email, "", nil, " x @ x.com", "123", "login@server." ) } + it { is_expected.to accept_values_for(:email, "xx@xx.com" ) } + it { is_expected.not_to accept_values_for(:email, "", nil, " x @ x.com", "123", "login@server." ) } - it { should accept_values_for(:password, "qweqweqwe" ) } - it { should_not accept_values_for(:password, "qweqwe", nil, "") } + it { is_expected.to accept_values_for(:password, "qweqweqwe" ) } + it { is_expected.not_to accept_values_for(:password, "qweqwe", nil, "") } - it { should accept_values_for(:name, nil, "", "Florian Gęga") } + it { is_expected.to accept_values_for(:name, nil, "", "Florian Gęga") } end diff --git a/spec/other/factories_spec.rb b/spec/other/factories_spec.rb index e367b623..6d895d53 100644 --- a/spec/other/factories_spec.rb +++ b/spec/other/factories_spec.rb @@ -4,7 +4,7 @@ FactoryGirl.factories.each do |factory| context "with factory for :#{factory.name}" do subject { FactoryGirl.build(factory.name) } - it { should be_valid, subject.errors.full_messages.join(", ") } + it { is_expected.to be_valid, subject.errors.full_messages.join(", ") } end end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 05d80fd6..8530a301 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -80,4 +80,15 @@ def self.connection config.include Devise::TestHelpers, type: :controller config.include ControllerHelpers, type: :controller config.include JsonSpec::Helpers, type: :controller + + # rspec-rails 3 will no longer automatically infer an example group's spec type + # from the file location. You can explicitly opt-in to the feature using this + # config option. + # To explicitly tag specs without using automatic inference, set the `:type` + # metadata manually: + # + # describe ThingsController, :type => :controller do + # # Equivalent to being in spec/controllers + # end + config.infer_spec_type_from_file_location! end diff --git a/spec/support/helpers/controller_helpers.rb b/spec/support/helpers/controller_helpers.rb index ef865c36..c6c5d3cc 100644 --- a/spec/support/helpers/controller_helpers.rb +++ b/spec/support/helpers/controller_helpers.rb @@ -1,16 +1,16 @@ module ControllerHelpers def should_authorize(action, subject) - controller.should_receive(:authorize!).with(action, subject).and_return(true) + expect(controller).to receive(:authorize!).with(action, subject).and_return(true) end def sign_in(user = double('user')) if user.nil? - request.env['warden'].stub(:authenticate!). + allow(request.env['warden']).to receive(:authenticate!). and_throw(:warden, {scope: :user}) - controller.stub current_user: nil + allow(controller).to receive_messages current_user: nil else - request.env['warden'].stub authenticate!: user - controller.stub current_user: user + allow(request.env['warden']).to receive_messages authenticate!: user + allow(controller).to receive_messages current_user: user end end end From c8b8591f814b7841487d91648d84dfc3a88e885a Mon Sep 17 00:00:00 2001 From: Alex Coles Date: Thu, 18 Jun 2015 19:27:21 +0200 Subject: [PATCH 412/499] Replace #pending w/#skip in Authentication spec Signed-off-by: Alex Coles --- spec/models/authentication_spec.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/spec/models/authentication_spec.rb b/spec/models/authentication_spec.rb index 0baa9a1b..26d3e907 100644 --- a/spec/models/authentication_spec.rb +++ b/spec/models/authentication_spec.rb @@ -7,19 +7,19 @@ it { is_expected.to accept_values_for(:user_id, 10) } it do - pending "Currently Removed" + skip "Currently Removed" # should_not accept_values_for(:user_id, nil, "") } end it { is_expected.to accept_values_for(:provider, "github", "twitter") } it do - pending "Currently Removed" + skip "Currently Removed" # should_not accept_values_for(:provider, nil, "") } end it { is_expected.to accept_values_for(:uid, "asd123dasd", "x") } it do - pending "Currently Removed" + skip "Currently Removed" # should_not accept_values_for(:uid, nil, "") } end end From 3f3ce0c6d1d1e47bcd17a8a51cd8891668178c08 Mon Sep 17 00:00:00 2001 From: Alex Coles Date: Thu, 18 Jun 2015 19:33:28 +0200 Subject: [PATCH 413/499] bundle update accept_values_for Signed-off-by: Alex Coles --- Gemfile.lock | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index fd25f315..db86ffcf 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,9 +1,9 @@ GEM remote: https://rubygems.org/ specs: - accept_values_for (0.4.3) - activemodel (>= 3.0.0) - rspec + accept_values_for (0.7.2) + activemodel (>= 3, < 5) + rspec (>= 2.0, < 4.0) actionmailer (4.0.8) actionpack (= 4.0.8) mail (~> 2.5.4) @@ -100,7 +100,7 @@ GEM hashie (2.0.5) hike (1.2.3) httpauth (0.2.1) - i18n (0.6.9) + i18n (0.7.0) jbuilder (1.0.2) activesupport (>= 3.0.0) jquery-rails (3.1.0) @@ -183,7 +183,7 @@ GEM activemodel (>= 3.0) activesupport (>= 3.0) rspec-mocks (>= 2.99, < 4.0) - rspec-core (3.3.0) + rspec-core (3.3.1) rspec-support (~> 3.3.0) rspec-expectations (3.3.0) diff-lcs (>= 1.2.0, < 2.0) @@ -235,14 +235,14 @@ GEM eventmachine (~> 1.0) rack (~> 1.0) thor (0.19.1) - thread_safe (0.3.4) + thread_safe (0.3.5) tilt (1.4.1) treetop (1.4.15) polyglot polyglot (>= 0.3.1) turbolinks (2.2.1) coffee-rails - tzinfo (0.3.39) + tzinfo (0.3.44) uglifier (2.4.0) execjs (>= 0.3.0) json (>= 1.8.0) From 3602546ec3af0c6f7cea85759d254e4926b8d110 Mon Sep 17 00:00:00 2001 From: Alex Coles Date: Thu, 18 Jun 2015 19:37:08 +0200 Subject: [PATCH 414/499] Remove deprecated rspec/autorun require Signed-off-by: Alex Coles --- spec/spec_helper.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 8530a301..a23ced9e 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -4,7 +4,6 @@ SimpleCov.start 'rails' require File.expand_path("../../config/environment", __FILE__) require 'rspec/rails' -require 'rspec/autorun' require 'capybara/rails' Capybara.configure do |config| From 78706e9160bec843ef85fcdb0e28df5d51161270 Mon Sep 17 00:00:00 2001 From: Alex Coles Date: Thu, 18 Jun 2015 19:41:08 +0200 Subject: [PATCH 415/499] Rename spec_helper.rb to rails_helper.rb * Generate a new, "lighter-weight" `spec_helper.rb` with `rails generate rspec:install`. Signed-off-by: Alex Coles --- .../controllers/activities_controller_spec.rb | 2 +- .../authentications_controller_spec.rb | 2 +- .../participation_controller_spec.rb | 2 +- spec/decorators/activity_decorator_spec.rb | 2 +- spec/lib/settings_spec.rb | 2 +- spec/models/activity_spec.rb | 2 +- spec/models/authentication_spec.rb | 2 +- spec/models/event_spec.rb | 2 +- spec/models/participation_spec.rb | 2 +- spec/models/user_spec.rb | 2 +- spec/other/factories_spec.rb | 2 +- spec/rails_helper.rb | 93 +++++++++++ spec/spec_helper.rb | 157 +++++++++--------- 13 files changed, 182 insertions(+), 90 deletions(-) create mode 100644 spec/rails_helper.rb diff --git a/spec/controllers/activities_controller_spec.rb b/spec/controllers/activities_controller_spec.rb index bf7ab023..fac1e482 100644 --- a/spec/controllers/activities_controller_spec.rb +++ b/spec/controllers/activities_controller_spec.rb @@ -1,4 +1,4 @@ -require 'spec_helper' +require 'rails_helper' describe ActivitiesController do let(:current_user) { mock_model(User) } diff --git a/spec/controllers/authentications_controller_spec.rb b/spec/controllers/authentications_controller_spec.rb index 4c2c5eca..36232315 100644 --- a/spec/controllers/authentications_controller_spec.rb +++ b/spec/controllers/authentications_controller_spec.rb @@ -1,4 +1,4 @@ -require "spec_helper" +require 'rails_helper' describe AuthenticationsController do diff --git a/spec/controllers/participation_controller_spec.rb b/spec/controllers/participation_controller_spec.rb index 25925e2e..530ae81f 100644 --- a/spec/controllers/participation_controller_spec.rb +++ b/spec/controllers/participation_controller_spec.rb @@ -1,4 +1,4 @@ -require 'spec_helper' +require 'rails_helper' describe ParticipationsController do let(:current_user) { mock_model(User) } diff --git a/spec/decorators/activity_decorator_spec.rb b/spec/decorators/activity_decorator_spec.rb index 22b3d3d5..54fae542 100644 --- a/spec/decorators/activity_decorator_spec.rb +++ b/spec/decorators/activity_decorator_spec.rb @@ -1,4 +1,4 @@ -require 'spec_helper' +require 'rails_helper' describe ActivityDecorator do let (:activity) { FactoryGirl.build_stubbed(:activity, limit_of_participants: 20) } diff --git a/spec/lib/settings_spec.rb b/spec/lib/settings_spec.rb index 49b3455e..525b87ca 100644 --- a/spec/lib/settings_spec.rb +++ b/spec/lib/settings_spec.rb @@ -1,4 +1,4 @@ -require 'spec_helper' +require 'rails_helper' describe "Settings" do diff --git a/spec/models/activity_spec.rb b/spec/models/activity_spec.rb index 33c212f7..85d2b3f6 100644 --- a/spec/models/activity_spec.rb +++ b/spec/models/activity_spec.rb @@ -1,4 +1,4 @@ -require 'spec_helper' +require 'rails_helper' describe Activity do diff --git a/spec/models/authentication_spec.rb b/spec/models/authentication_spec.rb index 26d3e907..eb8d4964 100644 --- a/spec/models/authentication_spec.rb +++ b/spec/models/authentication_spec.rb @@ -1,4 +1,4 @@ -require 'spec_helper' +require 'rails_helper' describe Authentication do subject { Authentication.new } diff --git a/spec/models/event_spec.rb b/spec/models/event_spec.rb index 15cb6d3c..fc850a63 100644 --- a/spec/models/event_spec.rb +++ b/spec/models/event_spec.rb @@ -1,4 +1,4 @@ -require 'spec_helper' +require 'rails_helper' describe Event do diff --git a/spec/models/participation_spec.rb b/spec/models/participation_spec.rb index a9e730f8..3c4351c3 100644 --- a/spec/models/participation_spec.rb +++ b/spec/models/participation_spec.rb @@ -1,4 +1,4 @@ -require 'spec_helper' +require 'rails_helper' describe Participation do diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index e2fe2914..add3ee81 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -1,4 +1,4 @@ -require 'spec_helper' +require 'rails_helper' describe User do diff --git a/spec/other/factories_spec.rb b/spec/other/factories_spec.rb index 6d895d53..e2af75ef 100644 --- a/spec/other/factories_spec.rb +++ b/spec/other/factories_spec.rb @@ -1,4 +1,4 @@ -require 'spec_helper' +require 'rails_helper' describe 'validate FactoryGirl factories' do FactoryGirl.factories.each do |factory| diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb new file mode 100644 index 00000000..a23ced9e --- /dev/null +++ b/spec/rails_helper.rb @@ -0,0 +1,93 @@ +# This file is copied to spec/ when you run 'rails generate rspec:install' +ENV["RAILS_ENV"] ||= 'test' +require 'simplecov' +SimpleCov.start 'rails' +require File.expand_path("../../config/environment", __FILE__) +require 'rspec/rails' +require 'capybara/rails' + +Capybara.configure do |config| + config.default_driver = :webkit + config.default_wait_time = 5 + # config.match = :one + # config.exact_options = true + # config.ignore_hidden_elements = true + # config.visible_text_only = true +end + +# Requires supporting ruby files with custom matchers and macros, etc, +# in spec/support/ and its subdirectories. +Dir[Rails.root.join("spec/support/**/*.rb")].each { |f| require f } + +OmniAuth.config.test_mode = true + +OmniAuth.config.add_mock(:github, { + uid: 'xc8b12448990eaef0b420f7153ec8d58', + nickname: 'rockstar', + email: 'user@99cookies.com', + image: 'rockstar.jpg' +}) +OmniAuth.config.add_mock(:twitter, { + uid: 'xc8b12448990eaef0b420f7153ec8d58', + nickname: 'rockstar' +}) + +Devise.stretches = 1 +Rails.logger.level = 4 + +class ActiveRecord::Base + mattr_accessor :shared_connection + @@shared_connection = nil + + def self.connection + @@shared_connection || retrieve_connection + end +end + +# Checks for pending migrations before tests are run. +# If you are not using ActiveRecord, you can remove this line. +ActiveRecord::Migration.check_pending! if defined?(ActiveRecord::Migration) + +RSpec.configure do |config| + # ## Mock Framework + # + # If you prefer to use mocha, flexmock or RR, uncomment the appropriate line: + # + # config.mock_with :mocha + # config.mock_with :flexmock + config.mock_with :rspec + + # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures + config.fixture_path = "#{::Rails.root}/spec/fixtures" + + # If you're not using ActiveRecord, or you'd prefer not to run each of your + # examples within a transaction, remove the following line or assign false + # instead of true. + config.use_transactional_fixtures = true + + # If true, the base class of anonymous controllers will be inferred + # automatically. This will be the default behavior in future versions of + # rspec-rails. + config.infer_base_class_for_anonymous_controllers = false + + # Run specs in random order to surface order dependencies. If you find an + # order dependency and want to debug it, you can fix the order by providing + # the seed, which is printed after each run. + # --seed 1234 + config.order = "random" + + config.include Devise::TestHelpers, type: :controller + config.include ControllerHelpers, type: :controller + config.include JsonSpec::Helpers, type: :controller + + # rspec-rails 3 will no longer automatically infer an example group's spec type + # from the file location. You can explicitly opt-in to the feature using this + # config option. + # To explicitly tag specs without using automatic inference, set the `:type` + # metadata manually: + # + # describe ThingsController, :type => :controller do + # # Equivalent to being in spec/controllers + # end + config.infer_spec_type_from_file_location! +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index a23ced9e..913e28a6 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,93 +1,92 @@ -# This file is copied to spec/ when you run 'rails generate rspec:install' -ENV["RAILS_ENV"] ||= 'test' -require 'simplecov' -SimpleCov.start 'rails' -require File.expand_path("../../config/environment", __FILE__) -require 'rspec/rails' -require 'capybara/rails' - -Capybara.configure do |config| - config.default_driver = :webkit - config.default_wait_time = 5 - # config.match = :one - # config.exact_options = true - # config.ignore_hidden_elements = true - # config.visible_text_only = true -end - -# Requires supporting ruby files with custom matchers and macros, etc, -# in spec/support/ and its subdirectories. -Dir[Rails.root.join("spec/support/**/*.rb")].each { |f| require f } - -OmniAuth.config.test_mode = true - -OmniAuth.config.add_mock(:github, { - uid: 'xc8b12448990eaef0b420f7153ec8d58', - nickname: 'rockstar', - email: 'user@99cookies.com', - image: 'rockstar.jpg' -}) -OmniAuth.config.add_mock(:twitter, { - uid: 'xc8b12448990eaef0b420f7153ec8d58', - nickname: 'rockstar' -}) - -Devise.stretches = 1 -Rails.logger.level = 4 - -class ActiveRecord::Base - mattr_accessor :shared_connection - @@shared_connection = nil +# This file was generated by the `rails generate rspec:install` command. Conventionally, all +# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`. +# The generated `.rspec` file contains `--require spec_helper` which will cause +# this file to always be loaded, without a need to explicitly require it in any +# files. +# +# Given that it is always loaded, you are encouraged to keep this file as +# light-weight as possible. Requiring heavyweight dependencies from this file +# will add to the boot time of your test suite on EVERY test run, even for an +# individual file that may not need all of that loaded. Instead, consider making +# a separate helper file that requires the additional dependencies and performs +# the additional setup, and require it from the spec files that actually need +# it. +# +# The `.rspec` file also contains a few flags that are not defaults but that +# users commonly want. +# +# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration +RSpec.configure do |config| + # rspec-expectations config goes here. You can use an alternate + # assertion/expectation library such as wrong or the stdlib/minitest + # assertions if you prefer. + config.expect_with :rspec do |expectations| + # This option will default to `true` in RSpec 4. It makes the `description` + # and `failure_message` of custom matchers include text for helper methods + # defined using `chain`, e.g.: + # be_bigger_than(2).and_smaller_than(4).description + # # => "be bigger than 2 and smaller than 4" + # ...rather than: + # # => "be bigger than 2" + expectations.include_chain_clauses_in_custom_matcher_descriptions = true + end - def self.connection - @@shared_connection || retrieve_connection + # rspec-mocks config goes here. You can use an alternate test double + # library (such as bogus or mocha) by changing the `mock_with` option here. + config.mock_with :rspec do |mocks| + # Prevents you from mocking or stubbing a method that does not exist on + # a real object. This is generally recommended, and will default to + # `true` in RSpec 4. + mocks.verify_partial_doubles = true end -end -# Checks for pending migrations before tests are run. -# If you are not using ActiveRecord, you can remove this line. -ActiveRecord::Migration.check_pending! if defined?(ActiveRecord::Migration) +# The settings below are suggested to provide a good initial experience +# with RSpec, but feel free to customize to your heart's content. +=begin + # These two settings work together to allow you to limit a spec run + # to individual examples or groups you care about by tagging them with + # `:focus` metadata. When nothing is tagged with `:focus`, all examples + # get run. + config.filter_run :focus + config.run_all_when_everything_filtered = true -RSpec.configure do |config| - # ## Mock Framework - # - # If you prefer to use mocha, flexmock or RR, uncomment the appropriate line: - # - # config.mock_with :mocha - # config.mock_with :flexmock - config.mock_with :rspec + # Allows RSpec to persist some state between runs in order to support + # the `--only-failures` and `--next-failure` CLI options. We recommend + # you configure your source control system to ignore this file. + config.example_status_persistence_file_path = "spec/examples.txt" - # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures - config.fixture_path = "#{::Rails.root}/spec/fixtures" + # Limits the available syntax to the non-monkey patched syntax that is + # recommended. For more details, see: + # - http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax + # - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/ + # - http://myronmars.to/n/dev-blog/2014/05/notable-changes-in-rspec-3#new__config_option_to_disable_rspeccore_monkey_patching + config.disable_monkey_patching! - # If you're not using ActiveRecord, or you'd prefer not to run each of your - # examples within a transaction, remove the following line or assign false - # instead of true. - config.use_transactional_fixtures = true + # Many RSpec users commonly either run the entire suite or an individual + # file, and it's useful to allow more verbose output when running an + # individual spec file. + if config.files_to_run.one? + # Use the documentation formatter for detailed output, + # unless a formatter has already been configured + # (e.g. via a command-line flag). + config.default_formatter = 'doc' + end - # If true, the base class of anonymous controllers will be inferred - # automatically. This will be the default behavior in future versions of - # rspec-rails. - config.infer_base_class_for_anonymous_controllers = false + # Print the 10 slowest examples and example groups at the + # end of the spec run, to help surface which specs are running + # particularly slow. + config.profile_examples = 10 # Run specs in random order to surface order dependencies. If you find an # order dependency and want to debug it, you can fix the order by providing # the seed, which is printed after each run. # --seed 1234 - config.order = "random" - - config.include Devise::TestHelpers, type: :controller - config.include ControllerHelpers, type: :controller - config.include JsonSpec::Helpers, type: :controller + config.order = :random - # rspec-rails 3 will no longer automatically infer an example group's spec type - # from the file location. You can explicitly opt-in to the feature using this - # config option. - # To explicitly tag specs without using automatic inference, set the `:type` - # metadata manually: - # - # describe ThingsController, :type => :controller do - # # Equivalent to being in spec/controllers - # end - config.infer_spec_type_from_file_location! + # Seed global randomization in this process using the `--seed` CLI option. + # Setting this allows you to use `--seed` to deterministically reproduce + # test failures related to randomization by passing the same `--seed` value + # as the one that triggered the failure. + Kernel.srand config.seed +=end end From 64c7d6622b0750fe1884a5527aa7e47e1e30048b Mon Sep 17 00:00:00 2001 From: Alex Coles Date: Thu, 18 Jun 2015 19:50:29 +0200 Subject: [PATCH 416/499] Fix Travis CI badge path --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d110bee3..a2f25996 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# rubyweek [![License](http://img.shields.io/:license-AGPL-0030c8.svg)](COPYRIGHT) [![Build Status](https://travis-ci.org/eurucamp/eurucamp-activities-2013.png?branch=master)](https://travis-ci.org/eurucamp/eurucamp-activities-2013) +# rubyweek [![License](http://img.shields.io/:license-AGPL-0030c8.svg)](COPYRIGHT) [![Build Status](https://travis-ci.org/eurucamp/eurucamp-activities.png?branch=master)](https://travis-ci.org/eurucamp/eurucamp-activities) The rubyweek app is a small application to allow attendees to organize and plan small event in and around a conference. Users create events or sign up to created ones. Signup works through Github or Twitter. From 8d57241b110a897d92e9be378e6366eed96831fd Mon Sep 17 00:00:00 2001 From: Alex Coles Date: Thu, 18 Jun 2015 20:01:54 +0200 Subject: [PATCH 417/499] Uncomment some suggested RSpec config * Consolidate with config in `rails_helper.rb` Signed-off-by: Alex Coles --- spec/rails_helper.rb | 14 -------------- spec/spec_helper.rb | 10 +++------- 2 files changed, 3 insertions(+), 21 deletions(-) diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index a23ced9e..e2b2e761 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -49,14 +49,6 @@ def self.connection ActiveRecord::Migration.check_pending! if defined?(ActiveRecord::Migration) RSpec.configure do |config| - # ## Mock Framework - # - # If you prefer to use mocha, flexmock or RR, uncomment the appropriate line: - # - # config.mock_with :mocha - # config.mock_with :flexmock - config.mock_with :rspec - # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures config.fixture_path = "#{::Rails.root}/spec/fixtures" @@ -70,12 +62,6 @@ def self.connection # rspec-rails. config.infer_base_class_for_anonymous_controllers = false - # Run specs in random order to surface order dependencies. If you find an - # order dependency and want to debug it, you can fix the order by providing - # the seed, which is printed after each run. - # --seed 1234 - config.order = "random" - config.include Devise::TestHelpers, type: :controller config.include ControllerHelpers, type: :controller config.include JsonSpec::Helpers, type: :controller diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 913e28a6..362afbb1 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -40,9 +40,6 @@ mocks.verify_partial_doubles = true end -# The settings below are suggested to provide a good initial experience -# with RSpec, but feel free to customize to your heart's content. -=begin # These two settings work together to allow you to limit a spec run # to individual examples or groups you care about by tagging them with # `:focus` metadata. When nothing is tagged with `:focus`, all examples @@ -53,14 +50,14 @@ # Allows RSpec to persist some state between runs in order to support # the `--only-failures` and `--next-failure` CLI options. We recommend # you configure your source control system to ignore this file. - config.example_status_persistence_file_path = "spec/examples.txt" + # config.example_status_persistence_file_path = "spec/examples.txt" # Limits the available syntax to the non-monkey patched syntax that is # recommended. For more details, see: # - http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax # - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/ # - http://myronmars.to/n/dev-blog/2014/05/notable-changes-in-rspec-3#new__config_option_to_disable_rspeccore_monkey_patching - config.disable_monkey_patching! + # config.disable_monkey_patching! # Many RSpec users commonly either run the entire suite or an individual # file, and it's useful to allow more verbose output when running an @@ -75,7 +72,7 @@ # Print the 10 slowest examples and example groups at the # end of the spec run, to help surface which specs are running # particularly slow. - config.profile_examples = 10 + # config.profile_examples = 10 # Run specs in random order to surface order dependencies. If you find an # order dependency and want to debug it, you can fix the order by providing @@ -88,5 +85,4 @@ # test failures related to randomization by passing the same `--seed` value # as the one that triggered the failure. Kernel.srand config.seed -=end end From 1ed419b330b99898e8d1a0f77eda38a723d8cb7a Mon Sep 17 00:00:00 2001 From: Alex Coles Date: Thu, 18 Jun 2015 20:04:31 +0200 Subject: [PATCH 418/499] Convert specs to RSpec 3.3.1 syntax with Transpec This conversion is done by Transpec 3.1.0 with the following command: transpec -v example_group * 11 conversions from: describe 'something' { } to: RSpec.describe 'something' { } * 1 addition of: RSpec.configure { |c| c.expose_dsl_globally = false } For more details: https://github.com/yujinakayama/transpec#supported-conversions --- spec/controllers/activities_controller_spec.rb | 2 +- spec/controllers/authentications_controller_spec.rb | 2 +- spec/controllers/participation_controller_spec.rb | 2 +- spec/decorators/activity_decorator_spec.rb | 2 +- spec/lib/settings_spec.rb | 2 +- spec/models/activity_spec.rb | 2 +- spec/models/authentication_spec.rb | 2 +- spec/models/event_spec.rb | 2 +- spec/models/participation_spec.rb | 2 +- spec/models/user_spec.rb | 2 +- spec/other/factories_spec.rb | 2 +- spec/rails_helper.rb | 9 +++++++++ 12 files changed, 20 insertions(+), 11 deletions(-) diff --git a/spec/controllers/activities_controller_spec.rb b/spec/controllers/activities_controller_spec.rb index fac1e482..bf99b29e 100644 --- a/spec/controllers/activities_controller_spec.rb +++ b/spec/controllers/activities_controller_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -describe ActivitiesController do +RSpec.describe ActivitiesController do let(:current_user) { mock_model(User) } let(:activity) { double(:activity) } diff --git a/spec/controllers/authentications_controller_spec.rb b/spec/controllers/authentications_controller_spec.rb index 36232315..57c4530b 100644 --- a/spec/controllers/authentications_controller_spec.rb +++ b/spec/controllers/authentications_controller_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -describe AuthenticationsController do +RSpec.describe AuthenticationsController do let(:current_user) { mock_model(User) } let(:authentications) { double(:authentications) } diff --git a/spec/controllers/participation_controller_spec.rb b/spec/controllers/participation_controller_spec.rb index 530ae81f..e4fdc129 100644 --- a/spec/controllers/participation_controller_spec.rb +++ b/spec/controllers/participation_controller_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -describe ParticipationsController do +RSpec.describe ParticipationsController do let(:current_user) { mock_model(User) } let(:activity_id) { "1" } let(:activity) { double(:activity) } diff --git a/spec/decorators/activity_decorator_spec.rb b/spec/decorators/activity_decorator_spec.rb index 54fae542..5ec67c91 100644 --- a/spec/decorators/activity_decorator_spec.rb +++ b/spec/decorators/activity_decorator_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -describe ActivityDecorator do +RSpec.describe ActivityDecorator do let (:activity) { FactoryGirl.build_stubbed(:activity, limit_of_participants: 20) } let (:decorator) { ActivityDecorator.new(activity) } diff --git a/spec/lib/settings_spec.rb b/spec/lib/settings_spec.rb index 525b87ca..e5be4d74 100644 --- a/spec/lib/settings_spec.rb +++ b/spec/lib/settings_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -describe "Settings" do +RSpec.describe "Settings" do subject { Settings } diff --git a/spec/models/activity_spec.rb b/spec/models/activity_spec.rb index 85d2b3f6..ea7782d1 100644 --- a/spec/models/activity_spec.rb +++ b/spec/models/activity_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -describe Activity do +RSpec.describe Activity do let(:event) { double(:event) } let(:creator) { mock_model(User) } diff --git a/spec/models/authentication_spec.rb b/spec/models/authentication_spec.rb index eb8d4964..5d10b205 100644 --- a/spec/models/authentication_spec.rb +++ b/spec/models/authentication_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -describe Authentication do +RSpec.describe Authentication do subject { Authentication.new } specify { expect { FactoryGirl.create(:activity).dup.save! }.to raise_exception(ActiveRecord::RecordInvalid) } diff --git a/spec/models/event_spec.rb b/spec/models/event_spec.rb index fc850a63..3dc4b73a 100644 --- a/spec/models/event_spec.rb +++ b/spec/models/event_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -describe Event do +RSpec.describe Event do let(:start_time) { Date.parse("2012-10-10") } let(:end_time) { Date.parse("2012-12-14") } diff --git a/spec/models/participation_spec.rb b/spec/models/participation_spec.rb index 3c4351c3..27b1c197 100644 --- a/spec/models/participation_spec.rb +++ b/spec/models/participation_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -describe Participation do +RSpec.describe Participation do describe "validations" do diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index add3ee81..608da764 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -describe User do +RSpec.describe User do describe "#connected_with_twitter?" do subject { user.connected_with_twitter? } diff --git a/spec/other/factories_spec.rb b/spec/other/factories_spec.rb index e2af75ef..246f61e1 100644 --- a/spec/other/factories_spec.rb +++ b/spec/other/factories_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -describe 'validate FactoryGirl factories' do +RSpec.describe 'validate FactoryGirl factories' do FactoryGirl.factories.each do |factory| context "with factory for :#{factory.name}" do subject { FactoryGirl.build(factory.name) } diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index e2b2e761..674e6bf6 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -76,4 +76,13 @@ def self.connection # # Equivalent to being in spec/controllers # end config.infer_spec_type_from_file_location! + + # Setting this config option `false` removes rspec-core's monkey patching of the + # top level methods like `describe`, `shared_examples_for` and `shared_context` + # on `main` and `Module`. The methods are always available through the `RSpec` + # module like `RSpec.describe` regardless of this setting. + # For backwards compatibility this defaults to `true`. + # + # https://relishapp.com/rspec/rspec-core/v/3-0/docs/configuration/global-namespace-dsl + config.expose_dsl_globally = false end From ee808a44affd8935b2163dcabdae145660176ba1 Mon Sep 17 00:00:00 2001 From: Alex Coles Date: Thu, 18 Jun 2015 20:06:59 +0200 Subject: [PATCH 419/499] Configure RSpec not to monkey-patch Signed-off-by: Alex Coles --- spec/rails_helper.rb | 9 --------- spec/spec_helper.rb | 2 +- 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index 674e6bf6..e2b2e761 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -76,13 +76,4 @@ def self.connection # # Equivalent to being in spec/controllers # end config.infer_spec_type_from_file_location! - - # Setting this config option `false` removes rspec-core's monkey patching of the - # top level methods like `describe`, `shared_examples_for` and `shared_context` - # on `main` and `Module`. The methods are always available through the `RSpec` - # module like `RSpec.describe` regardless of this setting. - # For backwards compatibility this defaults to `true`. - # - # https://relishapp.com/rspec/rspec-core/v/3-0/docs/configuration/global-namespace-dsl - config.expose_dsl_globally = false end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 362afbb1..a556838a 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -57,7 +57,7 @@ # - http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax # - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/ # - http://myronmars.to/n/dev-blog/2014/05/notable-changes-in-rspec-3#new__config_option_to_disable_rspeccore_monkey_patching - # config.disable_monkey_patching! + config.disable_monkey_patching! # Many RSpec users commonly either run the entire suite or an individual # file, and it's useful to allow more verbose output when running an From 1e86b6e72ffd4c6d9eda4b89340f1312b38ee98e Mon Sep 17 00:00:00 2001 From: Alex Coles Date: Thu, 18 Jun 2015 20:09:48 +0200 Subject: [PATCH 420/499] Explicitly opt-in to Travis container-based infra. Signed-off-by: Alex Coles --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 7a8aa61d..2f1d31dd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,3 +1,4 @@ +sudo: false branches: only: - master From bccea154586a9f3aa5021dd27ef55fbc62d53cd7 Mon Sep 17 00:00:00 2001 From: Alex Coles Date: Thu, 18 Jun 2015 20:16:06 +0200 Subject: [PATCH 421/499] Tell Travis to cache Bundler dependencies Signed-off-by: Alex Coles --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 2f1d31dd..b839cc23 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,6 +2,7 @@ sudo: false branches: only: - master +cache: bundler before_script: cp config/database.yml.sample config/database.yml language: ruby rvm: From 00bf73e978833d3bc686df46c03bae01d18eba90 Mon Sep 17 00:00:00 2001 From: Martin Luder Date: Tue, 23 Jun 2015 19:17:16 +0200 Subject: [PATCH 422/499] Fix exception notifications --- config/application.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/application.yml b/config/application.yml index 4fd6f14d..8dcad5ed 100644 --- a/config/application.yml +++ b/config/application.yml @@ -9,7 +9,7 @@ defaults: &defaults errors: from: "exception@eurucamp.org" to: - - "cfp-errors@eurucamp.or" + - "cfp-errors@eurucamp.org" seo: title: "rubyweek" author: "eurucamp team" From a3e5caf500f2c0ab79b6df9f0de3ddcd562b9d39 Mon Sep 17 00:00:00 2001 From: Martin Luder Date: Tue, 23 Jun 2015 19:51:52 +0200 Subject: [PATCH 423/499] Use sendmail to send emails Emails are sent via sendmail on Shelly. --- config/environments/production.rb | 10 +--------- config/environments/staging.rb | 10 +--------- 2 files changed, 2 insertions(+), 18 deletions(-) diff --git a/config/environments/production.rb b/config/environments/production.rb index b828d41f..a1cf2637 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -87,12 +87,4 @@ } end -ActionMailer::Base.smtp_settings = { - :address => 'smtp.sendgrid.net', - :port => '587', - :authentication => :plain, - :user_name => ENV['SENDGRID_USERNAME'], - :password => ENV['SENDGRID_PASSWORD'], - :domain => 'heroku.com', - :enable_starttls_auto => true -} +ActionMailer::Base.delivery_method = :sendmail diff --git a/config/environments/staging.rb b/config/environments/staging.rb index 499f8159..8c063c99 100644 --- a/config/environments/staging.rb +++ b/config/environments/staging.rb @@ -88,12 +88,4 @@ } end -ActionMailer::Base.smtp_settings = { - :address => 'smtp.sendgrid.net', - :port => '587', - :authentication => :plain, - :user_name => ENV['SENDGRID_USERNAME'], - :password => ENV['SENDGRID_PASSWORD'], - :domain => 'heroku.com', - :enable_starttls_auto => true -} +ActionMailer::Base.delivery_method = :sendmail From ca611010292532a5318fe931f65d7be676af4dab Mon Sep 17 00:00:00 2001 From: Martin Luder Date: Tue, 23 Jun 2015 20:27:27 +0200 Subject: [PATCH 424/499] Use rubyweek.org everywhere --- README.md | 2 +- config/application.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index a2f25996..d054f00c 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ Don't use the eurucamp logo for your instance to avoid confusion. ## Examples -An instance of the app can be seen running at the [rubyweek page](http://rubyweek.eurucamp.org). +An instance of the app can be seen running at the [rubyweek page](http://rubyweek.org). Screenshot: ![The activities app](screenshot.png) diff --git a/config/application.yml b/config/application.yml index 8dcad5ed..e66d42e5 100644 --- a/config/application.yml +++ b/config/application.yml @@ -3,7 +3,7 @@ defaults: &defaults name: "rubyweek" start_time: 2015-07-27 00:00:00 end_time: 2015-08-09 23:59:59 - host: "rubyweek.eurucamp.org" + host: "rubyweek.org" mailers: from: "activities@eurucamp.org" errors: From fda67d7a5ccf2f4eb16c302fd9a402ba4b2466d3 Mon Sep 17 00:00:00 2001 From: Martin Luder Date: Tue, 23 Jun 2015 20:40:35 +0200 Subject: [PATCH 425/499] Run staging Shelly cloud in staging env --- Cloudfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cloudfile b/Cloudfile index d7e219e7..c279aefb 100644 --- a/Cloudfile +++ b/Cloudfile @@ -1,6 +1,6 @@ eurucamp-activities-staging: ruby_version: 2.1.5 - environment: production # RAILS_ENV + environment: staging # RAILS_ENV domains: - eurucamp-activities-staging.shellyapp.com servers: From 65906cb536562c94421c320a495a2311ee10b381 Mon Sep 17 00:00:00 2001 From: Martin Luder Date: Wed, 24 Jun 2015 09:23:17 +0200 Subject: [PATCH 426/499] Set correct staging host --- config/application.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/application.yml b/config/application.yml index e66d42e5..bb479d7a 100644 --- a/config/application.yml +++ b/config/application.yml @@ -24,7 +24,7 @@ test: staging: <<: *defaults - host: "activities-staging.eurucamp.org" + host: "eurucamp-activities-staging.shellyapp.com" production: <<: *defaults From 172dff3eaed6e1fbff0122069a78c8e26ec5743f Mon Sep 17 00:00:00 2001 From: Martin Luder Date: Sun, 28 Jun 2015 14:05:30 +0200 Subject: [PATCH 427/499] Allow activities to require a eurucamp ticket Closes #119 A eurucamp logo is shown for activities that require a eurucamp ticket. --- app/assets/images/shared/eurucamp-2015.svg | 16 ++++++++++++++++ .../stylesheets/partials/activities/_index.sass | 4 ++++ .../stylesheets/partials/activities/_shared.sass | 4 ++++ app/controllers/activities_controller.rb | 1 + app/views/activities/_activity.html.haml | 5 +++++ app/views/activities/_form.html.haml | 5 +++++ app/views/activities/show.html.haml | 6 ++++++ config/locales/en.yml | 3 +++ ...add_requires_eurucamp_ticket_to_activities.rb | 5 +++++ db/schema.rb | 3 ++- 10 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 app/assets/images/shared/eurucamp-2015.svg create mode 100644 db/migrate/20150628112434_add_requires_eurucamp_ticket_to_activities.rb diff --git a/app/assets/images/shared/eurucamp-2015.svg b/app/assets/images/shared/eurucamp-2015.svg new file mode 100644 index 00000000..5d496f2d --- /dev/null +++ b/app/assets/images/shared/eurucamp-2015.svg @@ -0,0 +1,16 @@ + + + + eurucamp-2015 + Created with Sketch. + + + + + + + + + + + \ No newline at end of file diff --git a/app/assets/stylesheets/partials/activities/_index.sass b/app/assets/stylesheets/partials/activities/_index.sass index c096a361..749dc75a 100644 --- a/app/assets/stylesheets/partials/activities/_index.sass +++ b/app/assets/stylesheets/partials/activities/_index.sass @@ -199,6 +199,10 @@ a#new-activity +transition(opacity 0.1s) opacity: 0 + .requires_eurucamp_ticket + +transition(opacity 0.1s) + opacity: 0 + .action opacity: 1 +transform(scale(1)) diff --git a/app/assets/stylesheets/partials/activities/_shared.sass b/app/assets/stylesheets/partials/activities/_shared.sass index 0e9eb757..89c9d8ac 100644 --- a/app/assets/stylesheets/partials/activities/_shared.sass +++ b/app/assets/stylesheets/partials/activities/_shared.sass @@ -17,3 +17,7 @@ background-size: cover background-position: center background-image: image-url('shared/activity_placeholder.jpg') + +img.requires_eurucamp_ticket + width: 40px + height: 40px diff --git a/app/controllers/activities_controller.rb b/app/controllers/activities_controller.rb index 0769764c..99bb9105 100644 --- a/app/controllers/activities_controller.rb +++ b/app/controllers/activities_controller.rb @@ -57,6 +57,7 @@ def sanitized_params :name, :location, :requirements, + :requires_eurucamp_ticket, :description, :limit_of_participants, :anytime, diff --git a/app/views/activities/_activity.html.haml b/app/views/activities/_activity.html.haml index a67edff5..e5eff539 100644 --- a/app/views/activities/_activity.html.haml +++ b/app/views/activities/_activity.html.haml @@ -10,6 +10,11 @@ = activity.name %p.creator = activity.creator_name + - if activity.requires_eurucamp_ticket + = image_tag 'shared/eurucamp-2015.svg', + class: 'requires_eurucamp_ticket', + alt: t('activities.requires_eurucamp_ticket'), + title: t('activities.requires_eurucamp_ticket') %p.time = activity.time.html_safe diff --git a/app/views/activities/_form.html.haml b/app/views/activities/_form.html.haml index 6fb6a1fa..43be1148 100644 --- a/app/views/activities/_form.html.haml +++ b/app/views/activities/_form.html.haml @@ -33,6 +33,11 @@ %small = t("activity_form.requirements.hint") + %fieldset.requires_eurucamp_ticket + %label.inline + = f.check_box :requires_eurucamp_ticket + = t("activity_form.requires_eurucamp_ticket.hint") + %label.description = f.text_area :description, placeholder: t("activity_form.description.label"), rows: 8, class: "markdown", title: t("activity_form.markdown_hint") %small diff --git a/app/views/activities/show.html.haml b/app/views/activities/show.html.haml index 7c43c394..92acdbec 100644 --- a/app/views/activities/show.html.haml +++ b/app/views/activities/show.html.haml @@ -2,6 +2,12 @@ %header %h2 = @activity.name + - if @activity.requires_eurucamp_ticket + %br + = image_tag 'shared/eurucamp-2015.svg', + class: 'requires_eurucamp_ticket', + alt: t('activities.requires_eurucamp_ticket'), + title: t('activities.requires_eurucamp_ticket') %p.meta = t("activities.organized_by", name: @activity.creator_name).html_safe diff --git a/config/locales/en.yml b/config/locales/en.yml index 3c0d695e..9f6aa337 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -81,6 +81,7 @@ en: label_short: End location: Location required: Required + requires_eurucamp_ticket: "During the eurucamp, participants need a ticket" participants: Participants organized_by: "Organized by %{name}" room_left: @@ -124,6 +125,8 @@ en: requirements: label: Requirements hint: "What to bring, to wear etc." + requires_eurucamp_ticket: + hint: "During the eurucamp, participants need a ticket" image_url: label: URL for an image hint: Upload an image to e.g. imgur.com and link it here diff --git a/db/migrate/20150628112434_add_requires_eurucamp_ticket_to_activities.rb b/db/migrate/20150628112434_add_requires_eurucamp_ticket_to_activities.rb new file mode 100644 index 00000000..b24238d8 --- /dev/null +++ b/db/migrate/20150628112434_add_requires_eurucamp_ticket_to_activities.rb @@ -0,0 +1,5 @@ +class AddRequiresEurucampTicketToActivities < ActiveRecord::Migration + def change + add_column :activities, :requires_eurucamp_ticket, :boolean, null: false, default: false + end +end diff --git a/db/schema.rb b/db/schema.rb index f1244032..f2f40c79 100644 --- a/db/schema.rb +++ b/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: 20130608105751) do +ActiveRecord::Schema.define(version: 20150628112434) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -30,6 +30,7 @@ t.text "requirements" t.datetime "end_time" t.text "image_url" + t.boolean "requires_eurucamp_ticket", default: false, null: false end add_index "activities", ["name"], name: "index_activities_on_name", unique: true, using: :btree From 842f69a953a04409eba04b3a88c382fba226d1a7 Mon Sep 17 00:00:00 2001 From: Hauke Klement Date: Sat, 18 Jul 2015 19:19:04 +0200 Subject: [PATCH 428/499] fixed broken Twitter avatar URL --- app/helpers/application_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 75a02a34..d95af63b 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -5,7 +5,7 @@ def gravatar_avatar_url(user, size) end def twitter_avatar_url(handle) - "https://twitter.com/api/users/profile_image/#{handle}" + "https://twitter.com/#{handle}/profile_image?size=normal" end def avatar_url(user, size = 64) From e645af469a0ce5a7613993df8cb16cd667c9ecba Mon Sep 17 00:00:00 2001 From: Hauke Klement Date: Sat, 18 Jul 2015 19:19:20 +0200 Subject: [PATCH 429/499] added specs --- spec/helpers/application_helper_spec.rb | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 spec/helpers/application_helper_spec.rb diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb new file mode 100644 index 00000000..1444b82a --- /dev/null +++ b/spec/helpers/application_helper_spec.rb @@ -0,0 +1,18 @@ +require 'rails_helper' + +RSpec.describe ApplicationHelper do + describe '#avatar_url' do + subject { avatar_url(user, size) } + let(:size) { 64 } + + context 'with a Twitter account' do + let(:user) { User.new(twitter_handle: 'eurucamp') } + it { is_expected.to match %r{//twitter.com/#{user.twitter_handle}/profile_image} } + end + + context 'without a Twitter account' do + let(:user) { User.new } + it { is_expected.to match %r{//gravatar.com/avatar/\w+.png\?s=#{size}} } + end + end +end From 7adc7002606cdd23c9e8f49c7418f2a9cd236036 Mon Sep 17 00:00:00 2001 From: Martin Luder Date: Thu, 23 Jul 2015 23:00:10 +0200 Subject: [PATCH 430/499] Group activities by date The activity tiles are grouped by date. Activities spanning multiple dates are listed for each date. --- .../partials/activities/_index.sass | 7 ++++ app/controllers/activities_controller.rb | 4 +- app/models/activity.rb | 4 ++ app/models/event.rb | 11 +++++ app/views/activities/_activity_day.html.haml | 3 ++ app/views/activities/index.html.haml | 3 +- .../controllers/activities_controller_spec.rb | 4 +- spec/models/activity_spec.rb | 30 +++++++++++++ spec/models/event_spec.rb | 42 +++++++++++++++++++ 9 files changed, 103 insertions(+), 5 deletions(-) create mode 100644 app/views/activities/_activity_day.html.haml diff --git a/app/assets/stylesheets/partials/activities/_index.sass b/app/assets/stylesheets/partials/activities/_index.sass index 749dc75a..f1e8c0bb 100644 --- a/app/assets/stylesheets/partials/activities/_index.sass +++ b/app/assets/stylesheets/partials/activities/_index.sass @@ -28,6 +28,13 @@ a#new-activity // card variations $types: participant ok remove, owner edit edit + .activity-day + clear: both + + h1 + padding-top: 0.67em + margin-top: 0 + li display: block margin: 0 0 2px 0 diff --git a/app/controllers/activities_controller.rb b/app/controllers/activities_controller.rb index 99bb9105..90bd6232 100644 --- a/app/controllers/activities_controller.rb +++ b/app/controllers/activities_controller.rb @@ -6,9 +6,9 @@ class ActivitiesController < ApiController authorize_resource only: [:edit, :update, :destroy] def index - @activities = current_event.search_activities(current_user, query_params[:search], query_params[:filter]) + @activities_per_day = current_event.activities_per_day(current_user, query_params[:search], query_params[:filter]) @counters = current_event.counters(current_user) - respond_with(@activities) + respond_with(@activities_per_day) end def show diff --git a/app/models/activity.rb b/app/models/activity.rb index 6a0b37c0..095ed92a 100644 --- a/app/models/activity.rb +++ b/app/models/activity.rb @@ -133,6 +133,10 @@ def participation(user) participations.find_by(user_id: user) end + def dates + (start_time.to_date..end_time.to_date).to_a + end + private def participation_source diff --git a/app/models/event.rb b/app/models/event.rb index e96d4403..348e41b4 100644 --- a/app/models/event.rb +++ b/app/models/event.rb @@ -66,6 +66,17 @@ def search_activities(author = nil, query_string = "", filter = "all") end alias_method :activities, :search_activities + def activities_per_day(author = nil, query_string = "", filter = "all") + activities = search_activities(author, query_string, filter) + activities.sort_by! { |activity| activity.dates.first } + + activities.each_with_object(Hash.new { [] }) do |activity, grouped_by_day| + activity.dates.each do |date| + grouped_by_day[date] += [activity] + end + end + end + private def fetch_recent diff --git a/app/views/activities/_activity_day.html.haml b/app/views/activities/_activity_day.html.haml new file mode 100644 index 00000000..a3d4ad53 --- /dev/null +++ b/app/views/activities/_activity_day.html.haml @@ -0,0 +1,3 @@ +.activity-day + %h1= I18n.l(day.to_time, format: :nice_date) + = render ActivityDecorator.decorate_collection(activities) diff --git a/app/views/activities/index.html.haml b/app/views/activities/index.html.haml index 880a94c8..6461d514 100644 --- a/app/views/activities/index.html.haml +++ b/app/views/activities/index.html.haml @@ -29,4 +29,5 @@ = t("activities.new.label") %ul#activities - = render @activities.decorate + - @activities_per_day.each do |day, activities| + = render 'activity_day', day: day, activities: activities diff --git a/spec/controllers/activities_controller_spec.rb b/spec/controllers/activities_controller_spec.rb index bf99b29e..36c602e5 100644 --- a/spec/controllers/activities_controller_spec.rb +++ b/spec/controllers/activities_controller_spec.rb @@ -16,11 +16,11 @@ subject { get :index } let(:current_user) { nil } - let(:activities) { double(:activities) } + let(:activities_per_day) { double(:activities_per_day) } let(:counters) { double(:counters) } before do - expect(current_event).to receive(:search_activities).with(current_user, nil, nil).and_return(activities) + expect(current_event).to receive(:activities_per_day).with(current_user, nil, nil).and_return(activities_per_day) expect(current_event).to receive(:counters).with(current_user).and_return(counters) end diff --git a/spec/models/activity_spec.rb b/spec/models/activity_spec.rb index ea7782d1..21eeb62c 100644 --- a/spec/models/activity_spec.rb +++ b/spec/models/activity_spec.rb @@ -196,6 +196,36 @@ it { is_expected.to eq(new_participation) } end + describe '#dates' do + context 'for an activity happening on a single day' do + before do + activity.start_time = Time.zone.local(2015, 7, 30, 10) + activity.end_time = Time.zone.local(2015, 7, 30, 17) + end + + it 'is the day of the activity' do + expect(activity.dates).to eq([Date.new(2015, 7, 30)]) + end + end + + context 'for an activity spanning multiple days' do + before do + activity.start_time = Time.zone.local(2015, 7, 30, 10) + activity.end_time = Time.zone.local(2015, 8, 3, 9) + end + + it 'is all the days the activity spans' do + expect(activity.dates).to eq([ + Date.new(2015, 7, 30), + Date.new(2015, 7, 31), + Date.new(2015, 8, 1), + Date.new(2015, 8, 2), + Date.new(2015, 8, 3) + ]) + end + end + end + describe "validations" do specify { expect { FactoryGirl.create(:activity).dup.save! }.to raise_exception(ActiveRecord::RecordInvalid) } diff --git a/spec/models/event_spec.rb b/spec/models/event_spec.rb index 3dc4b73a..0edcd962 100644 --- a/spec/models/event_spec.rb +++ b/spec/models/event_spec.rb @@ -74,6 +74,48 @@ it { is_expected.to eq(activities) } end + describe '#activities_per_day' do + let(:activity1) { + double(:activity1, + dates: [Date.new(2015, 7, 30)]) + } + let(:activity2) { + double(:activity2, + dates: [Date.new(2015, 7, 31)]) + } + let(:activities) { [activity2, activity1] } + + before do + event.all_activities_fetcher = ->{ activities } + end + + it 'groups the activities per day' do + expect(event).to receive(:search_activities).with('author', 'query string', 'filter').and_return(activities) + expect(event.activities_per_day 'author', 'query string', 'filter') + .to eq({ + Date.new(2015, 7, 30) => [activity1], + Date.new(2015, 7, 31) => [activity2] + }) + end + + it 'is ordered by day' do + allow(event).to receive(:search_activities).and_return(activities) + per_day = event.activities_per_day + expect(per_day.to_a.first.first).to eq(Date.new(2015, 7, 30)) + expect(per_day.to_a.second.first).to eq(Date.new(2015, 7, 31)) + end + + it 'lists activities spanning multiple days for each day' do + activity = double(:activity, dates: [Date.new(2015, 7, 30), Date.new(2015, 7, 31), Date.new(2015, 8, 1)]) + allow(event).to receive(:search_activities).and_return([activity]) + expect(event.activities_per_day).to eq({ + Date.new(2015, 7, 30) => [activity], + Date.new(2015, 7, 31) => [activity], + Date.new(2015, 8, 1) => [activity] + }) + end + end + describe "#search_activities" do let(:activities) { [double(:activity1), double(:activity2)] } From 80eee958f72fc28bfe45360833cba504a61bbf08 Mon Sep 17 00:00:00 2001 From: Martin Luder Date: Fri, 24 Jul 2015 23:25:11 +0200 Subject: [PATCH 431/499] Disable the anytime option for activities Now that the app was extended to the time around eurucamp, the anytime option does not make sense anymore (it used to mean anytime during eurucamp). Next step is removing the feature completely. --- app/views/activities/_form.html.haml | 7 ------- db/migrate/20150724211941_change_anytime_default.rb | 9 +++++++++ db/schema.rb | 10 +++++----- spec/models/activity_spec.rb | 4 ++-- 4 files changed, 16 insertions(+), 14 deletions(-) create mode 100644 db/migrate/20150724211941_change_anytime_default.rb diff --git a/app/views/activities/_form.html.haml b/app/views/activities/_form.html.haml index 43be1148..c68610b3 100644 --- a/app/views/activities/_form.html.haml +++ b/app/views/activities/_form.html.haml @@ -9,13 +9,6 @@ %span.divider> – - fancy_datime_select f, :end_time, @activity - %fieldset.anytime - %span.between - = t("common.or").downcase - %label.inline - = f.check_box :anytime - = t("activity_form.anytime.hint") - %hr %label.location> diff --git a/db/migrate/20150724211941_change_anytime_default.rb b/db/migrate/20150724211941_change_anytime_default.rb new file mode 100644 index 00000000..943fa802 --- /dev/null +++ b/db/migrate/20150724211941_change_anytime_default.rb @@ -0,0 +1,9 @@ +class ChangeAnytimeDefault < ActiveRecord::Migration + def up + change_column_default :activities, :anytime, false + end + + def down + change_column_default :activities, :anytime, true + end +end diff --git a/db/schema.rb b/db/schema.rb index f2f40c79..0972f063 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,22 +11,22 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20150628112434) do +ActiveRecord::Schema.define(version: 20150724211941) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" create_table "activities", force: true do |t| - t.string "name", default: "", null: false + t.string "name", default: "", null: false t.text "description" t.string "location" t.datetime "start_time" - t.integer "limit_of_participants", default: 10 + t.integer "limit_of_participants", default: 10 t.datetime "created_at" t.datetime "updated_at" t.integer "creator_id" - t.integer "participations_count", default: 0, null: false - t.boolean "anytime", default: true, null: false + t.integer "participations_count", default: 0, null: false + t.boolean "anytime", default: false, null: false t.text "requirements" t.datetime "end_time" t.text "image_url" diff --git a/spec/models/activity_spec.rb b/spec/models/activity_spec.rb index ea7782d1..65b47099 100644 --- a/spec/models/activity_spec.rb +++ b/spec/models/activity_spec.rb @@ -210,9 +210,9 @@ it { is_expected.to accept_values_for(:location, "football pitch" ) } it { is_expected.not_to accept_values_for(:location, "", nil) } - it { is_expected.to accept_values_for(:start_time, Time.now, nil) } + it { is_expected.to accept_values_for(:start_time, Time.now) } - it { is_expected.to accept_values_for(:end_time, Time.now, nil) } + it { is_expected.to accept_values_for(:end_time, Time.now) } it { is_expected.to accept_values_for(:limit_of_participants, nil, 12, 100) } it { is_expected.not_to accept_values_for(:limit_of_participants, -1, 0) } From 86f01b771c0501ec37158d0fd4118695f7ee31cd Mon Sep 17 00:00:00 2001 From: Martin Luder Date: Sat, 25 Jul 2015 19:41:12 +0200 Subject: [PATCH 432/499] Order activities by start time within each day Activities used to be ordered by start time. The grouping by day broke that. --- app/models/event.rb | 2 +- spec/models/event_spec.rb | 20 +++++++++++++++++--- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/app/models/event.rb b/app/models/event.rb index 348e41b4..9772a77b 100644 --- a/app/models/event.rb +++ b/app/models/event.rb @@ -68,7 +68,7 @@ def search_activities(author = nil, query_string = "", filter = "all") def activities_per_day(author = nil, query_string = "", filter = "all") activities = search_activities(author, query_string, filter) - activities.sort_by! { |activity| activity.dates.first } + activities.sort_by! { |activity| activity.start_time } activities.each_with_object(Hash.new { [] }) do |activity, grouped_by_day| activity.dates.each do |date| diff --git a/spec/models/event_spec.rb b/spec/models/event_spec.rb index 0edcd962..dfcccd76 100644 --- a/spec/models/event_spec.rb +++ b/spec/models/event_spec.rb @@ -77,11 +77,13 @@ describe '#activities_per_day' do let(:activity1) { double(:activity1, - dates: [Date.new(2015, 7, 30)]) + dates: [Date.new(2015, 7, 30)], + start_time: Time.local(2015, 7, 30, 10)) } let(:activity2) { double(:activity2, - dates: [Date.new(2015, 7, 31)]) + dates: [Date.new(2015, 7, 31)], + start_time: Time.local(2015, 7, 31, 11)) } let(:activities) { [activity2, activity1] } @@ -106,7 +108,7 @@ end it 'lists activities spanning multiple days for each day' do - activity = double(:activity, dates: [Date.new(2015, 7, 30), Date.new(2015, 7, 31), Date.new(2015, 8, 1)]) + activity = double(:activity, dates: [Date.new(2015, 7, 30), Date.new(2015, 7, 31), Date.new(2015, 8, 1)], start_time: Time.local(2015, 7, 30, 10)) allow(event).to receive(:search_activities).and_return([activity]) expect(event.activities_per_day).to eq({ Date.new(2015, 7, 30) => [activity], @@ -114,6 +116,18 @@ Date.new(2015, 8, 1) => [activity] }) end + + it 'orders activities by start time' do + activity1 = double(:activity1, dates: [Date.new(2015, 7, 30), Date.new(2015, 7, 31)], start_time: Time.local(2015, 7, 30, 11)) + activity2 = double(:activity2, dates: [Date.new(2015, 7, 30)], start_time: Time.local(2015, 7, 30, 10)) + activity3 = double(:activity3, dates: [Date.new(2015, 7, 31)], start_time: Time.local(2015, 7, 31, 9)) + allow(event).to receive(:search_activities).and_return([activity1, activity2, activity3]) + + expect(event.activities_per_day).to eq({ + Date.new(2015, 7, 30) => [activity2, activity1], + Date.new(2015, 7, 31) => [activity1, activity3] + }) + end end describe "#search_activities" do From e24e25ef5956cd21dde961377e89248a943d0a40 Mon Sep 17 00:00:00 2001 From: Martin Luder Date: Sat, 25 Jul 2015 22:33:40 +0200 Subject: [PATCH 433/499] Add meeting points map to activity form --- .../images/activities/meeting_points.svg | 274 ++++++++++++++++++ app/assets/javascripts/initializers.coffee | 4 + .../partials/activities/_form.sass | 5 + app/views/activities/_form.html.haml | 3 + config/locales/en.yml | 1 + 5 files changed, 287 insertions(+) create mode 100644 app/assets/images/activities/meeting_points.svg diff --git a/app/assets/images/activities/meeting_points.svg b/app/assets/images/activities/meeting_points.svg new file mode 100644 index 00000000..61151333 --- /dev/null +++ b/app/assets/images/activities/meeting_points.svg @@ -0,0 +1,274 @@ + + + + map + Created with Sketch. + + + + + + + + + + + + + + + + + i + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Hall 5 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Party Venue + + + Hotel Griebnitzsee + + + + First floor + + + Ground floor + + + Rails Girls + Workshop + + + Child + Care + + + Hall 1 + + + Reception + + + + + + + + + + + Elevator + + + + + + + + + WC + + + + + + + + + + + + + + + + + + + + Women + + + Women + + + All + Gender + + + Men + + + Emergency, accessability, support. + If you need help call us: + +49 30 57700332 + + + + + + + + + + + + + + Activity Meeting Point + + + + + + 1 + + + 2 + + + Leasure area + + + + + + + + + + + + 3 + + + \ No newline at end of file diff --git a/app/assets/javascripts/initializers.coffee b/app/assets/javascripts/initializers.coffee index 17a7065c..3ce33345 100644 --- a/app/assets/javascripts/initializers.coffee +++ b/app/assets/javascripts/initializers.coffee @@ -163,5 +163,9 @@ ready = -> $('#notifications').on 'click', hideNotification setTimeout hideNotification, 3000 + toggleMap = -> + $('#meeting-point-map').toggle() + $('#show-map').on 'click', toggleMap + $(document).ready(ready) $(document).on('page:load', ready) diff --git a/app/assets/stylesheets/partials/activities/_form.sass b/app/assets/stylesheets/partials/activities/_form.sass index ac5d0ff9..2e955a6a 100644 --- a/app/assets/stylesheets/partials/activities/_form.sass +++ b/app/assets/stylesheets/partials/activities/_form.sass @@ -84,3 +84,8 @@ form#new-activity button font-size: 1.5em + + .meeting-point-map + display: none + width: 80% + padding: 3em 0 diff --git a/app/views/activities/_form.html.haml b/app/views/activities/_form.html.haml index c68610b3..85f5e41d 100644 --- a/app/views/activities/_form.html.haml +++ b/app/views/activities/_form.html.haml @@ -15,12 +15,15 @@ = f.text_field :location, placeholder: t("activity_form.location.label") %small = t("activity_form.location.hint") + %a#show-map= t("activity_form.location.show-map") %label.limit-of-participants> = f.number_field :limit_of_participants, placeholder: "5" %small = t("activity_form.limit_of_participants.hint") + = image_tag 'activities/meeting_points.svg', class: 'meeting-point-map', id: 'meeting-point-map' + %label.requirements = f.text_area :requirements, placeholder: t("activity_form.requirements.label"), rows: 6, class: "markdown", title: t("activity_form.markdown_hint") %small diff --git a/config/locales/en.yml b/config/locales/en.yml index 9f6aa337..cdd012c3 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -120,6 +120,7 @@ en: location: label: Location hint: "Where does your activity take place, where to meet, …" + show-map: 'Show meeting point map' limit_of_participants: hint: Max. participants requirements: From 8da1470748db4d3df83a46a645124ddf4a473c78 Mon Sep 17 00:00:00 2001 From: Martin Luder Date: Sat, 25 Jul 2015 22:49:54 +0200 Subject: [PATCH 434/499] Add meeting point map to activity detail page --- app/assets/stylesheets/partials/activities/_show.sass | 7 +++++++ app/views/activities/show.html.haml | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/app/assets/stylesheets/partials/activities/_show.sass b/app/assets/stylesheets/partials/activities/_show.sass index 69086edc..a644aac4 100644 --- a/app/assets/stylesheets/partials/activities/_show.sass +++ b/app/assets/stylesheets/partials/activities/_show.sass @@ -130,3 +130,10 @@ $types: participant ok remove, owner edit edit dl.wrapper margin-left: 50px + + .meeting-point-map + margin-left: 50px + display: none + + img + width: 100% diff --git a/app/views/activities/show.html.haml b/app/views/activities/show.html.haml index 92acdbec..d5afc3f3 100644 --- a/app/views/activities/show.html.haml +++ b/app/views/activities/show.html.haml @@ -55,3 +55,10 @@ = t("activities.location") %dd = @activity.location + %br + %br + %a#show-map= t("activity_form.location.show-map") + + #meeting-point-map.meeting-point-map + = image_tag 'activities/meeting_points.svg' + From c736f18909702fdc7f0154463d389b34a0e27b52 Mon Sep 17 00:00:00 2001 From: Martin Luder Date: Sat, 25 Jul 2015 23:22:12 +0200 Subject: [PATCH 435/499] Add UserDecorator --- app/decorators/user_decorator.rb | 9 +++++++++ app/views/activities/show.html.haml | 4 ++-- spec/decorators/user_decorator_spec.rb | 23 +++++++++++++++++++++++ 3 files changed, 34 insertions(+), 2 deletions(-) create mode 100644 app/decorators/user_decorator.rb create mode 100644 spec/decorators/user_decorator_spec.rb diff --git a/app/decorators/user_decorator.rb b/app/decorators/user_decorator.rb new file mode 100644 index 00000000..3bdbf6bb --- /dev/null +++ b/app/decorators/user_decorator.rb @@ -0,0 +1,9 @@ +class UserDecorator < Draper::Decorator + def name + object.name + end + + def avatar_url(size) + h.avatar_url(object, size) + end +end diff --git a/app/views/activities/show.html.haml b/app/views/activities/show.html.haml index 92acdbec..8832219b 100644 --- a/app/views/activities/show.html.haml +++ b/app/views/activities/show.html.haml @@ -22,9 +22,9 @@ %h3 = t("activities.participants") %ul - - @activity.participants.each do |participant| + - @activity.participants.decorate.each do |participant| %li(title="#{ participant.name }")> - = image_tag(avatar_url(participant, 48)) + = image_tag(participant.avatar_url(48)) %section.description .wrapper diff --git a/spec/decorators/user_decorator_spec.rb b/spec/decorators/user_decorator_spec.rb new file mode 100644 index 00000000..59d99550 --- /dev/null +++ b/spec/decorators/user_decorator_spec.rb @@ -0,0 +1,23 @@ +require 'rails_helper' + +RSpec.describe UserDecorator do + let(:user) { User.new } + + subject do + UserDecorator.new(user) + end + + describe '#name' do + it 'delegates to the user' do + user.name = 'Blubb' + expect(subject.name).to eq(user.name) + end + end + + describe '#avatar_url' do + it 'returns the avatar url' do + user.twitter_handle = 'foobar' + expect(subject.avatar_url(48)).to match /http.*foobar/ + end + end +end From f027c2ee7e72a0ebaf71f2f4944715990983b9e3 Mon Sep 17 00:00:00 2001 From: Martin Luder Date: Sun, 26 Jul 2015 00:53:20 +0200 Subject: [PATCH 436/499] Fix typo --- app/assets/images/activities/meeting_points.svg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/images/activities/meeting_points.svg b/app/assets/images/activities/meeting_points.svg index 61151333..e7810b68 100644 --- a/app/assets/images/activities/meeting_points.svg +++ b/app/assets/images/activities/meeting_points.svg @@ -227,7 +227,7 @@ 2 - Leasure area + Leisure area From e3b4c99a6f63cfe9ef08142ae02650a29475a878 Mon Sep 17 00:00:00 2001 From: Martin Luder Date: Sat, 25 Jul 2015 23:41:57 +0200 Subject: [PATCH 437/499] Add option to hide participation in activities Not everyone wants to show publicly where and who they are. So we provide an option to show a users participations as anonymous. --- app/assets/stylesheets/partials/_user.sass | 2 ++ app/assets/stylesheets/partials/user/_edit.sass | 3 +++ app/controllers/registrations_controller.rb | 2 +- app/decorators/user_decorator.rb | 4 ++++ app/views/registrations/edit.html.haml | 4 ++++ .../20150725210346_add_show_participation.rb | 5 +++++ db/schema.rb | 7 ++++--- spec/decorators/user_decorator_spec.rb | 17 +++++++++++++++++ 8 files changed, 40 insertions(+), 4 deletions(-) create mode 100644 app/assets/stylesheets/partials/user/_edit.sass create mode 100644 db/migrate/20150725210346_add_show_participation.rb diff --git a/app/assets/stylesheets/partials/_user.sass b/app/assets/stylesheets/partials/_user.sass index 8595a2b8..156dc714 100644 --- a/app/assets/stylesheets/partials/_user.sass +++ b/app/assets/stylesheets/partials/_user.sass @@ -1,3 +1,5 @@ +@import 'partials/user/edit' + body.user background: $gray // the "back home" link only shown on the diff --git a/app/assets/stylesheets/partials/user/_edit.sass b/app/assets/stylesheets/partials/user/_edit.sass new file mode 100644 index 00000000..f9eab8d6 --- /dev/null +++ b/app/assets/stylesheets/partials/user/_edit.sass @@ -0,0 +1,3 @@ +label.show_participation + margin-bottom: 1.25em + display: inline-block !important diff --git a/app/controllers/registrations_controller.rb b/app/controllers/registrations_controller.rb index afa0523f..c5dab019 100644 --- a/app/controllers/registrations_controller.rb +++ b/app/controllers/registrations_controller.rb @@ -51,7 +51,7 @@ def sign_up_params end def editable_params - params.require(:user).permit(:name, :email, :password, :password_confirmation) + params.require(:user).permit(:name, :email, :password, :password_confirmation, :show_participation) end end diff --git a/app/decorators/user_decorator.rb b/app/decorators/user_decorator.rb index 3bdbf6bb..814907ea 100644 --- a/app/decorators/user_decorator.rb +++ b/app/decorators/user_decorator.rb @@ -1,9 +1,13 @@ class UserDecorator < Draper::Decorator def name + return 'Anonymous' unless object.show_participation object.name end + DEFAULT_AVATAR = 'http://www.gravatar.com/avatar/00000000000000000000000000000000.png?s=%{size}' + def avatar_url(size) + return DEFAULT_AVATAR % { size: size } unless object.show_participation h.avatar_url(object, size) end end diff --git a/app/views/registrations/edit.html.haml b/app/views/registrations/edit.html.haml index 87a3a000..1920f9ae 100644 --- a/app/views/registrations/edit.html.haml +++ b/app/views/registrations/edit.html.haml @@ -17,6 +17,10 @@ %span.label Your email address = f.email_field :email, placeholder: "Your email address" + %label.inline.show_participation + = f.check_box :show_participation + Show participations publicly? + - if resource.no_oauth_connected? %label.password %span.label Your password diff --git a/db/migrate/20150725210346_add_show_participation.rb b/db/migrate/20150725210346_add_show_participation.rb new file mode 100644 index 00000000..a019dfbf --- /dev/null +++ b/db/migrate/20150725210346_add_show_participation.rb @@ -0,0 +1,5 @@ +class AddShowParticipation < ActiveRecord::Migration + def change + add_column :users, 'show_participation', :boolean, null: false, default: true + end +end diff --git a/db/schema.rb b/db/schema.rb index 0972f063..4105880d 100644 --- a/db/schema.rb +++ b/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: 20150724211941) do +ActiveRecord::Schema.define(version: 20150725210346) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -51,8 +51,8 @@ end create_table "users", force: true do |t| - t.string "email", default: "", null: false - t.string "encrypted_password", default: "", null: false + t.string "email", default: "", null: false + t.string "encrypted_password", default: "", null: false t.string "reset_password_token" t.datetime "reset_password_sent_at" t.datetime "remember_created_at" @@ -66,6 +66,7 @@ t.string "name" t.string "twitter_handle" t.string "github_handle" + t.boolean "show_participation", default: true, null: false end add_index "users", ["email"], name: "index_users_on_email", unique: true, using: :btree diff --git a/spec/decorators/user_decorator_spec.rb b/spec/decorators/user_decorator_spec.rb index 59d99550..ebce6edb 100644 --- a/spec/decorators/user_decorator_spec.rb +++ b/spec/decorators/user_decorator_spec.rb @@ -12,6 +12,14 @@ user.name = 'Blubb' expect(subject.name).to eq(user.name) end + + context 'when the user does not want to be shown in activities' do + it 'is "Anonymous"' do + user.name = 'Blubb' + user.show_participation = false + expect(subject.name).to eq('Anonymous') + end + end end describe '#avatar_url' do @@ -19,5 +27,14 @@ user.twitter_handle = 'foobar' expect(subject.avatar_url(48)).to match /http.*foobar/ end + + context 'when the user does not want to be shown in activities' do + it 'is the default Gravatar image' do + default = 'http://www.gravatar.com/avatar/00000000000000000000000000000000.png?s=48' + user.twitter_handle = 'foobar' + user.show_participation = false + expect(subject.avatar_url(48)).to eq(default) + end + end end end From deb48aaf0b852344dbcea67008bfec3a34f146ef Mon Sep 17 00:00:00 2001 From: Martin Luder Date: Sun, 26 Jul 2015 18:45:27 +0200 Subject: [PATCH 438/499] Rename UserDecorator to ParticipantDecorator --- .../{user_decorator.rb => participant_decorator.rb} | 2 +- app/views/activities/show.html.haml | 2 +- .../{user_decorator_spec.rb => participant_decorator_spec.rb} | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) rename app/decorators/{user_decorator.rb => participant_decorator.rb} (87%) rename spec/decorators/{user_decorator_spec.rb => participant_decorator_spec.rb} (93%) diff --git a/app/decorators/user_decorator.rb b/app/decorators/participant_decorator.rb similarity index 87% rename from app/decorators/user_decorator.rb rename to app/decorators/participant_decorator.rb index 814907ea..8a7a8318 100644 --- a/app/decorators/user_decorator.rb +++ b/app/decorators/participant_decorator.rb @@ -1,4 +1,4 @@ -class UserDecorator < Draper::Decorator +class ParticipantDecorator < Draper::Decorator def name return 'Anonymous' unless object.show_participation object.name diff --git a/app/views/activities/show.html.haml b/app/views/activities/show.html.haml index 8832219b..993f0c38 100644 --- a/app/views/activities/show.html.haml +++ b/app/views/activities/show.html.haml @@ -22,7 +22,7 @@ %h3 = t("activities.participants") %ul - - @activity.participants.decorate.each do |participant| + - ParticipantDecorator.decorate_collection(@activity.participants).each do |participant| %li(title="#{ participant.name }")> = image_tag(participant.avatar_url(48)) diff --git a/spec/decorators/user_decorator_spec.rb b/spec/decorators/participant_decorator_spec.rb similarity index 93% rename from spec/decorators/user_decorator_spec.rb rename to spec/decorators/participant_decorator_spec.rb index ebce6edb..31052371 100644 --- a/spec/decorators/user_decorator_spec.rb +++ b/spec/decorators/participant_decorator_spec.rb @@ -1,10 +1,10 @@ require 'rails_helper' -RSpec.describe UserDecorator do +RSpec.describe ParticipantDecorator do let(:user) { User.new } subject do - UserDecorator.new(user) + ParticipantDecorator.new(user) end describe '#name' do From 2a465035b697db82dd23629d8ac416587a3253c2 Mon Sep 17 00:00:00 2001 From: Martin Luder Date: Sun, 26 Jul 2015 19:46:14 +0200 Subject: [PATCH 439/499] Set up Warden test mode for feature tests --- spec/rails_helper.rb | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index e2b2e761..d964b631 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -63,6 +63,13 @@ def self.connection config.infer_base_class_for_anonymous_controllers = false config.include Devise::TestHelpers, type: :controller + config.include Warden::Test::Helpers + config.before :suite do + Warden.test_mode! + end + config.after :each do + Warden.test_reset! + end config.include ControllerHelpers, type: :controller config.include JsonSpec::Helpers, type: :controller From 2c09835c1ea4c33adf07aec070fad07a3a63d041 Mon Sep 17 00:00:00 2001 From: Martin Luder Date: Sun, 26 Jul 2015 20:34:38 +0200 Subject: [PATCH 440/499] Remove unused Capybara webkit driver It is not needed and is causing problems. --- Gemfile | 1 - Gemfile.lock | 6 +----- spec/rails_helper.rb | 1 - 3 files changed, 1 insertion(+), 7 deletions(-) diff --git a/Gemfile b/Gemfile index a84c54ec..49ab563e 100644 --- a/Gemfile +++ b/Gemfile @@ -46,7 +46,6 @@ group :test do gem 'rspec-activemodel-mocks' gem 'simplecov', require: false gem 'capybara', '~> 2.1' - gem 'capybara-webkit', '~> 1.4' gem 'accept_values_for' gem 'json_spec' end diff --git a/Gemfile.lock b/Gemfile.lock index db86ffcf..b214fba8 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -46,9 +46,6 @@ GEM rack (>= 1.0.0) rack-test (>= 0.5.4) xpath (~> 2.0) - capybara-webkit (1.4.1) - capybara (>= 2.3.0, < 2.5.0) - json coderay (1.1.0) coffee-rails (4.0.1) coffee-script (>= 2.2.0) @@ -261,7 +258,6 @@ DEPENDENCIES bourbon cancan capybara (~> 2.1) - capybara-webkit (~> 1.4) coffee-rails (~> 4.0.0) devise (~> 3.0.0.rc) dotenv-rails @@ -298,4 +294,4 @@ DEPENDENCIES uglifier (>= 1.0.3) BUNDLED WITH - 1.10.4 + 1.10.5 diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index d964b631..c917f543 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -7,7 +7,6 @@ require 'capybara/rails' Capybara.configure do |config| - config.default_driver = :webkit config.default_wait_time = 5 # config.match = :one # config.exact_options = true From 87677183185d22bfa2f2adba18b36a79d5e2294a Mon Sep 17 00:00:00 2001 From: Martin Luder Date: Sun, 26 Jul 2015 19:52:30 +0200 Subject: [PATCH 441/499] Add integration test --- spec/features/activites_spec.rb | 55 +++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 spec/features/activites_spec.rb diff --git a/spec/features/activites_spec.rb b/spec/features/activites_spec.rb new file mode 100644 index 00000000..3167b23d --- /dev/null +++ b/spec/features/activites_spec.rb @@ -0,0 +1,55 @@ +require 'rails_helper' + +RSpec.describe "Activities", :type => :feature do + let!(:event) { Event.new } + let!(:creator) do + User.create!( + name: 'Creatoratorius', + email: 'test@example.com', + password: 'foobarbaz' + ) + end + let!(:participant) do + User.create!( + name: 'Participanto', + email: 'toast@example.com', + password: 'foobarbaz' + ) + end + let!(:anonymous_participant) do + User.create!( + name: 'AnonUser', + email: 'anon@example.com', + password: 'foobarbaz', + show_participation: false + ) + end + let!(:activity) do + Activity.create!( + start_time: Time.zone.local(2015, 7, 30, 10), + end_time: Time.zone.local(2015, 7, 30, 11), + name: 'Test Activity', + location: 'There', + creator_id: creator.id, + event: event + ) + end + + before do + activity.participants << participant + activity.participants << anonymous_participant + end + + + context 'viewing an activity' do + it "view activity" do + login_as(participant) + visit "/activities/#{activity.id}" + + expect(page.status_code).to eq(200) + expect(page).to have_xpath('//li[@title="Participanto"]') + expect(page).not_to have_xpath('//li[@title="AnonUser"]') + expect(page).to have_xpath('//li[@title="Anonymous"]') + end + end +end From 571b6452514aec60525d6369c3336de5c0ae5406 Mon Sep 17 00:00:00 2001 From: polarblau Date: Thu, 30 Jul 2015 12:38:45 +0200 Subject: [PATCH 442/499] Updating Typekit kit ID. --- app/views/partials/_layout_head.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/partials/_layout_head.html.haml b/app/views/partials/_layout_head.html.haml index 23aa463e..0833e9fb 100644 --- a/app/views/partials/_layout_head.html.haml +++ b/app/views/partials/_layout_head.html.haml @@ -10,4 +10,4 @@ = stylesheet_link_tag "application" = javascript_include_tag :modernizr -= javascript_include_tag "//use.typekit.net/vor5lqb.js" += javascript_include_tag "//use.typekit.net/ubd3myx.js" From ad9565af4c55dbe46d8b10864dd2780c74513aee Mon Sep 17 00:00:00 2001 From: Gustavo Guimaraes Date: Sat, 1 Aug 2015 15:48:26 +0200 Subject: [PATCH 443/499] Fix issue #98 * shiftKey/filter reset issue: * https://github.com/eurucamp/eurucamp-activities/issues/98 --- app/assets/javascripts/initializers.coffee | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/app/assets/javascripts/initializers.coffee b/app/assets/javascripts/initializers.coffee index 3ce33345..c7431e56 100644 --- a/app/assets/javascripts/initializers.coffee +++ b/app/assets/javascripts/initializers.coffee @@ -111,13 +111,10 @@ ready = -> $filters.on 'click', (e)-> e.preventDefault() $filter = $(@) - if $filter.hasClass('selected') - $filters.filter('.all').trigger('click') - else - if !e.shiftKey || (e.shiftKey && $filter.find('input').val() == 'all') - $filters.removeClass 'selected' - $filter.addClass('selected') - filterActivities() + if !e.shiftKey || (e.shiftKey && $filter.find('input').val() == 'all') + $filters.removeClass 'selected' + $filter.addClass('selected') + filterActivities() # search # TODO: debounce From 53a26600c0e70a6d06b5f1713bc63f41f6f4d3f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fe=C3=B1a=20Agar?= Date: Sat, 1 Aug 2015 15:58:14 +0200 Subject: [PATCH 444/499] Prevents users to participate in already full activities. --- app/controllers/participations_controller.rb | 7 +++++-- spec/controllers/participation_controller_spec.rb | 13 ++++++++++++- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/app/controllers/participations_controller.rb b/app/controllers/participations_controller.rb index 40f7e287..db1ca008 100644 --- a/app/controllers/participations_controller.rb +++ b/app/controllers/participations_controller.rb @@ -2,8 +2,11 @@ class ParticipationsController < ApiController respond_to :js, :html def create - @participation = current_activity.new_participation(current_user) - @participation.save + unless current_activity.full? + @participation = current_activity.new_participation(current_user) + @participation.save + end + respond_with(current_activity.reload, @participation) do |format| format.html { redirect_to :back } end diff --git a/spec/controllers/participation_controller_spec.rb b/spec/controllers/participation_controller_spec.rb index e4fdc129..794d9506 100644 --- a/spec/controllers/participation_controller_spec.rb +++ b/spec/controllers/participation_controller_spec.rb @@ -22,12 +22,13 @@ expect(current_event).to receive(:activity).with(activity_id).and_return(activity) expect(activity).to receive(:decorate).and_return(activity) - expect(activity).to receive(:new_participation).with(current_user).and_return(participation) + allow(activity).to receive(:new_participation).with(current_user).and_return(participation) end context "valid parameters" do before do + expect(activity).to receive(:full?).and_return(false) expect(participation).to receive(:save).and_return(true) end @@ -38,12 +39,22 @@ let(:participation) { invalid_participation } before do + expect(activity).to receive(:full?).and_return(false) expect(participation).to receive(:save).and_return(false) end # TODO: moar specs needed it { is_expected.to render_template(:create) } end + + context "activity is already full" do + before do + expect(activity).to receive(:full?).and_return(true) + expect(participation).not_to receive(:save) + end + + it { is_expected.to render_template(:create) } + end end end From 1ca655cb86dfc6b27a21109a16b7400fa03af538 Mon Sep 17 00:00:00 2001 From: Tilmann Singer Date: Sat, 1 Aug 2015 14:47:52 +0200 Subject: [PATCH 445/499] Minor fixes to README Use >> instead of > so that an existing /etc/hosts file is not overwritten. --- README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index d054f00c..5bfac7ec 100644 --- a/README.md +++ b/README.md @@ -50,11 +50,12 @@ An installed postgresql instance and a compiler is needed. ### Basic setup -* `echo 'activities.dev localhost' > /etc/hosts` +* `echo 'activities.dev localhost' >> /etc/hosts` * `cp .env.sample .env` * `cp config/database.yml.sample config/database.yml` -* update config files: `config/application.yml`, `.env` (see ENV variables listed below) -* run migration scripts +* update config files: `config/application.yml`, `.env` (see ENV variables listed above) +* `bundle exec rake db:create` +* `bundle exec rake db:migrate` * `bundle exec foreman start` * `tail -f log/development.log` * `open http://activities.dev:3000` From 947fd0717f3783c34e18ac380a4282fdb7caaf0e Mon Sep 17 00:00:00 2001 From: Tilmann Singer Date: Sat, 1 Aug 2015 14:53:55 +0200 Subject: [PATCH 446/499] Remove specific ruby version from Gemfile Previously, it wouldn't start when having 2.1.6 locally, and I don't think it should make a difference. Also there is a .ruby-version file as well anyway. --- Gemfile | 2 -- 1 file changed, 2 deletions(-) diff --git a/Gemfile b/Gemfile index 49ab563e..51afd9c7 100644 --- a/Gemfile +++ b/Gemfile @@ -1,7 +1,5 @@ source 'https://rubygems.org' -ruby "2.1.5" - gem 'rails', '4.0.8' gem 'pg' gem 'thin' From f513f75b4bb0125c0e891814f55acab4ee6f697c Mon Sep 17 00:00:00 2001 From: Tilmann Singer Date: Sat, 1 Aug 2015 14:54:22 +0200 Subject: [PATCH 447/499] Change thin to unicorn This was necessary to get it to run locally with the existing Procfile. --- Gemfile | 2 +- Gemfile.lock | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/Gemfile b/Gemfile index 51afd9c7..d8c6f539 100644 --- a/Gemfile +++ b/Gemfile @@ -2,7 +2,7 @@ source 'https://rubygems.org' gem 'rails', '4.0.8' gem 'pg' -gem 'thin' +gem 'unicorn' gem 'settingslogic' gem 'newrelic_rpm' gem 'devise', '~> 3.0.0.rc' diff --git a/Gemfile.lock b/Gemfile.lock index b214fba8..92b40e2c 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -109,6 +109,7 @@ GEM rspec (>= 2.0, < 4.0) jwt (0.1.11) multi_json (>= 1.5) + kgio (2.9.2) mail (2.5.4) mime-types (~> 1.16) treetop (~> 1.4.8) @@ -169,6 +170,7 @@ GEM activesupport (= 4.0.8) rake (>= 0.8.7) thor (>= 0.18.1, < 2.0) + raindrops (0.13.0) rake (10.3.2) redcarpet (3.0.0) request_store (1.0.5) @@ -243,6 +245,10 @@ GEM uglifier (2.4.0) execjs (>= 0.3.0) json (>= 1.8.0) + unicorn (4.8.2) + kgio (~> 2.6) + rack + raindrops (~> 0.7) warden (1.2.3) rack (>= 1.0) xpath (2.0.0) @@ -289,9 +295,9 @@ DEPENDENCIES simple_form (~> 3.0.1) simplecov sprockets-rails (~> 2.1.3) - thin turbolinks uglifier (>= 1.0.3) + unicorn BUNDLED WITH 1.10.5 From 5e0a4948ec8453ecb62ee32369d4aa6f6689b7bc Mon Sep 17 00:00:00 2001 From: Tilmann Singer Date: Sat, 1 Aug 2015 14:57:36 +0200 Subject: [PATCH 448/499] Minor uppercase spelling fixes to README --- README.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 5bfac7ec..1aaf9fbd 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # rubyweek [![License](http://img.shields.io/:license-AGPL-0030c8.svg)](COPYRIGHT) [![Build Status](https://travis-ci.org/eurucamp/eurucamp-activities.png?branch=master)](https://travis-ci.org/eurucamp/eurucamp-activities) -The rubyweek app is a small application to allow attendees to organize and plan small event in and around a conference. Users create events or sign up to created ones. Signup works through Github or Twitter. +The rubyweek app is a small application to allow attendees to organize and plan small event in and around a conference. Users create events or sign up to created ones. Signup works through GitHub or Twitter. The app is mobile friendly and easy to run on a free Heroku account. @@ -29,8 +29,8 @@ To deploy the app, you need the following: * Clone this repository: `https://github.com/eurucamp/eurucamp-activities-2013/` * An account and a created application at Heroku. -* A registered twitter application. Go [here](https://apps.twitter.com/). -* A registered github application. Go [here](https://github.com/settings/applications). +* A registered Twitter application. Go [here](https://apps.twitter.com/). +* A registered GitHub application. Go [here](https://github.com/settings/applications). Deploying is as easy as: @@ -39,10 +39,10 @@ Deploying is as easy as: ### **ENV** variables used: -* `GITHUB_KEY`: Your github application key. -* `GITHUB_SECRET`: Your github application secret. -* `TWITTER_KEY`: Your twitter application key. -* `TWITTER_SECRET`: Your twitter application secret. +* `GITHUB_KEY`: Your GitHub application key. +* `GITHUB_SECRET`: Your GitHub application secret. +* `TWITTER_KEY`: Your Twitter application key. +* `TWITTER_SECRET`: Your Twitter application secret. ## Development From b07b2f4c4792e38cfb8fcb5f9aaff39db769d5ab Mon Sep 17 00:00:00 2001 From: Tilmann Singer Date: Sat, 1 Aug 2015 14:59:24 +0200 Subject: [PATCH 449/499] Add note to run specs to README --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 1aaf9fbd..b33303a0 100644 --- a/README.md +++ b/README.md @@ -59,6 +59,7 @@ An installed postgresql instance and a compiler is needed. * `bundle exec foreman start` * `tail -f log/development.log` * `open http://activities.dev:3000` +* run the specs: `bundle exec rake` ### Customization From 344e2775c19fa36b1b1ec45f21ae67335740f999 Mon Sep 17 00:00:00 2001 From: Tilmann Singer Date: Sat, 1 Aug 2015 15:58:58 +0200 Subject: [PATCH 450/499] Remove unused sass partials --- .../partials/user/_registrations.sass | 5 -- .../stylesheets/partials/user/_sessions.sass | 0 .../stylesheets/partials/user/_shared.sass | 73 ------------------- 3 files changed, 78 deletions(-) delete mode 100644 app/assets/stylesheets/partials/user/_registrations.sass delete mode 100644 app/assets/stylesheets/partials/user/_sessions.sass delete mode 100644 app/assets/stylesheets/partials/user/_shared.sass diff --git a/app/assets/stylesheets/partials/user/_registrations.sass b/app/assets/stylesheets/partials/user/_registrations.sass deleted file mode 100644 index 279ae46e..00000000 --- a/app/assets/stylesheets/partials/user/_registrations.sass +++ /dev/null @@ -1,5 +0,0 @@ -article - section - &.form - form - display: none diff --git a/app/assets/stylesheets/partials/user/_sessions.sass b/app/assets/stylesheets/partials/user/_sessions.sass deleted file mode 100644 index e69de29b..00000000 diff --git a/app/assets/stylesheets/partials/user/_shared.sass b/app/assets/stylesheets/partials/user/_shared.sass deleted file mode 100644 index 3d47c481..00000000 --- a/app/assets/stylesheets/partials/user/_shared.sass +++ /dev/null @@ -1,73 +0,0 @@ -article - margin: 0 150px - text-align: center - - h4 - +custom-sans(light) - font-size: 1.375em - padding: 0 0.727272em - text-align: center - - p - text-align: center - margin: 2em 0 3em 0 - - strong - margin: 0 0 1em 0 - text-transform: uppercase - +custom-sans(medium) - display: block - - section - &.oauth - @extend %clearfix - - a - width: 50% - display: block - border: none - float: left - color: $extra-dark-gray - - .container - text-align: center - background: $white no-repeat center top - height: 22em - padding: 2.5em 1em 1em 1em - margin: 0 1px - position: relative - overflow: hidden - +transition(box-shadow 0.25s ease-in-out) - - h4 - @extend %ornamental-divider-before - margin: 10em 0 0 0 - - &:before - margin: 0.5em auto - - &.github - .container - background-image: image-url('user/github.png') - - &.twitter - .container - background-image: image-url('user/twitter.png') - - &:hover - color: $blue - - .container - z-index: 9001 - +transition(box-shadow 0.25s ease-in-out) - box-shadow: 0 0 20px transparentize($black, 0.9) - - &.form - - form - padding: 2em 0 0 - width: percentage(2/3) - margin: 0 auto - - h4 - @extend %ornamental-divider-after From dcfea95e10913e6871d4b98b7915542a4d8c9f21 Mon Sep 17 00:00:00 2001 From: Tilmann Singer Date: Sun, 9 Aug 2015 12:49:26 +0200 Subject: [PATCH 451/499] Seeds: create event with date range in the future Previously it was using the defaults from config/application.yml, which now lies in the past and caused a validation failure. --- db/seeds.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/db/seeds.rb b/db/seeds.rb index 5b717940..a7a27444 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -1,5 +1,5 @@ User.transaction do |tx| - event = Event.new + event = Event.new("Test Event", Date.today, 14.days.from_now) ultra_secure_password = "qweqweqwe" creator = User.new( From 5e9c03d6ea93eb8584deaa674911648cc9568bdb Mon Sep 17 00:00:00 2001 From: Tilmann Singer Date: Sun, 9 Aug 2015 15:40:18 +0200 Subject: [PATCH 452/499] Update draper gem To get rid of this deprecation warning when running specs: Filtering by an `:example_group` subhash is deprecated. Use the subhash to filter directly instead. Called from /home/tils/.gem/ruby/2.1.6/gems/draper-1.3.0/lib/draper/test/rspec_integration.rb:10:in `block in '. --- Gemfile.lock | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 92b40e2c..f562be6d 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -66,11 +66,11 @@ GEM dotenv (0.9.0) dotenv-rails (0.9.0) dotenv (= 0.9.0) - draper (1.3.0) + draper (2.1.0) actionpack (>= 3.0) activemodel (>= 3.0) activesupport (>= 3.0) - request_store (~> 1.0.3) + request_store (~> 1.0) erubis (2.7.0) eventmachine (1.0.3) exception_notification (4.0.1) @@ -117,7 +117,7 @@ GEM mini_portile (0.6.2) minitest (4.7.5) modernizr-rails (2.7.1) - multi_json (1.11.1) + multi_json (1.11.2) multipart-post (2.0.0) neat (1.5.0) bourbon (>= 2.1) @@ -150,7 +150,7 @@ GEM orm_adapter (0.5.0) pg (0.17.1) polyglot (0.3.5) - rack (1.5.2) + rack (1.5.5) rack-robotz (0.0.4) rack rack-test (0.6.3) @@ -173,7 +173,7 @@ GEM raindrops (0.13.0) rake (10.3.2) redcarpet (3.0.0) - request_store (1.0.5) + request_store (1.2.0) rspec (3.3.0) rspec-core (~> 3.3.0) rspec-expectations (~> 3.3.0) From 2323787d95dab29acf86d26438706e9862045cad Mon Sep 17 00:00:00 2001 From: Tilmann Singer Date: Sat, 1 Aug 2015 16:48:17 +0200 Subject: [PATCH 453/499] Add form to delete an activity Previously it wasn't possible to delete your own activities, which bit me once when I created a test activity by accident on the production system instead of locally. This adds a danger zone area at the bottom of the edit form, with a checkbox that needs to be checked to confirm the deletion. --- .../partials/activities/_form.sass | 7 ++++ app/controllers/activities_controller.rb | 8 +++- app/views/activities/_delete_form.html.haml | 8 ++++ app/views/activities/edit.html.haml | 9 +++++ config/locales/en.yml | 14 +++++++ .../controllers/activities_controller_spec.rb | 4 +- spec/features/activites_spec.rb | 37 +++++++++++++++++++ 7 files changed, 83 insertions(+), 4 deletions(-) create mode 100644 app/views/activities/_delete_form.html.haml diff --git a/app/assets/stylesheets/partials/activities/_form.sass b/app/assets/stylesheets/partials/activities/_form.sass index 2e955a6a..2ed48371 100644 --- a/app/assets/stylesheets/partials/activities/_form.sass +++ b/app/assets/stylesheets/partials/activities/_form.sass @@ -89,3 +89,10 @@ form#new-activity display: none width: 80% padding: 3em 0 + +.delete-activity + margin-top: 4em + header + margin-bottom: 0.5em + button + background-color: $red diff --git a/app/controllers/activities_controller.rb b/app/controllers/activities_controller.rb index 90bd6232..2de96322 100644 --- a/app/controllers/activities_controller.rb +++ b/app/controllers/activities_controller.rb @@ -40,8 +40,12 @@ def update end def destroy - @activity.destroy if @activity - respond_with(@activity, location: activities_path) + if @activity && params[:confirm_delete] && @activity.destroy + redirect_to root_path, notice: I18n.t("destroy_activity.notice") + else + flash[:error] = I18n.t("destroy_activity.error") + render :edit + end end private diff --git a/app/views/activities/_delete_form.html.haml b/app/views/activities/_delete_form.html.haml new file mode 100644 index 00000000..efdb3d4e --- /dev/null +++ b/app/views/activities/_delete_form.html.haml @@ -0,0 +1,8 @@ += form_tag activity_path(@activity), method: :delete do + + %fieldset + %label.inline + = check_box_tag :confirm_delete + = t("delete_activity_form.confirmation", count: @activity.participants.count) + + = button_tag t("delete_activity_form.submit") diff --git a/app/views/activities/edit.html.haml b/app/views/activities/edit.html.haml index 594b9b9d..d099ccc0 100644 --- a/app/views/activities/edit.html.haml +++ b/app/views/activities/edit.html.haml @@ -10,3 +10,12 @@ = link_to t("activities.new.label").downcase, new_activity_path = render "form" + + +%article.delete-activity + %header + %h2= t("activities.danger_zone") + %p.meta + %strong= t("delete_activity_form.title") + + = render "delete_form" diff --git a/config/locales/en.yml b/config/locales/en.yml index cdd012c3..bb77c03e 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -97,6 +97,7 @@ en: image_url: invalid: "not valid URL" not_supported_protocol: "protocol not supported ;)" + danger_zone: Danger Zone new_activity: title: Organize an activity @@ -108,6 +109,10 @@ en: notice: Your activity has been updated! error: Your changes could not be saved. Please check your input. + destroy_activity: + notice: Your activity has been deleted! + error: Your activity could not be deleted. Did you check the confirmation? + activity_form: markdown_hint: You can use Markdown here name: @@ -132,6 +137,15 @@ en: label: URL for an image hint: Upload an image to e.g. imgur.com and link it here + delete_activity_form: + title: + Delete activity + confirmation: + one: Really delete this activity (with currently one participant) + other: Really delete this activity (with currently %{count} participants) + submit: + Delete activity + footer_nav: about: label: About diff --git a/spec/controllers/activities_controller_spec.rb b/spec/controllers/activities_controller_spec.rb index 36c602e5..c353da9f 100644 --- a/spec/controllers/activities_controller_spec.rb +++ b/spec/controllers/activities_controller_spec.rb @@ -154,7 +154,7 @@ end describe "#destroy" do - subject { delete :destroy, {id: activity_id} } + subject { delete :destroy, {id: activity_id, confirm_delete: true} } before do sign_in(current_user) @@ -164,7 +164,7 @@ expect(activity).to receive(:destroy).and_return(true) end - it { is_expected.to redirect_to activities_path } + it { is_expected.to redirect_to root_path } end diff --git a/spec/features/activites_spec.rb b/spec/features/activites_spec.rb index 3167b23d..a5934017 100644 --- a/spec/features/activites_spec.rb +++ b/spec/features/activites_spec.rb @@ -52,4 +52,41 @@ expect(page).to have_xpath('//li[@title="Anonymous"]') end end + + context 'deleting an activity' do + it 'deletes activity when checkbox is checked' do + login_as(creator) + visit "/activities/#{activity.id}" + click_link 'Edit' + check 'Really delete this activity (with currently 2 participants)' + click_button 'Delete activity' + + get_redirected_to_homepage + activity_is_deleted + end + + it 'does not delete activity when checkbox is not checked' do + login_as(creator) + visit "/activities/#{activity.id}" + click_link 'Edit' + + click_button 'Delete activity' + + activity_is_not_deleted + end + end + + def activity_is_not_deleted + expect(page).to have_content('Your activity could not be deleted.') + expect(Activity.exists?(activity.id)).to be_truthy + end + + def get_redirected_to_homepage + expect(current_path).to eq(root_path) + end + + def activity_is_deleted + expect(page).to have_content('Your activity has been deleted!') + expect(Activity.exists?(activity.id)).to be_falsy + end end From c53f91850d392816026497075ffafc9d0ba3a77f Mon Sep 17 00:00:00 2001 From: Tilmann Singer Date: Sat, 1 Aug 2015 15:33:24 +0200 Subject: [PATCH 454/499] Remove margin from form This makes the form a bit more usable on narrow devices. Previously, the fields were squashed horizontally to unreadable once the width got below ~ 600px. --- app/assets/stylesheets/partials/activities/_form.sass | 1 - 1 file changed, 1 deletion(-) diff --git a/app/assets/stylesheets/partials/activities/_form.sass b/app/assets/stylesheets/partials/activities/_form.sass index 2e955a6a..5c39f7d7 100644 --- a/app/assets/stylesheets/partials/activities/_form.sass +++ b/app/assets/stylesheets/partials/activities/_form.sass @@ -1,5 +1,4 @@ form#new-activity - margin: 0 150px input.date-capture, input.time-capture From 57bde55c4c22831e488bca232bd82677ca6a3aa6 Mon Sep 17 00:00:00 2001 From: Daniel Carral Date: Tue, 18 Aug 2015 21:10:06 +0200 Subject: [PATCH 455/499] Upgrade pickadate.js to v3.5.6 --- .../javascripts/pickadate/picker.date.js | 891 +++++++++++++----- vendor/assets/javascripts/pickadate/picker.js | 759 +++++++++++---- .../javascripts/pickadate/picker.time.js | 768 +++++++++++---- .../assets/stylesheets/pickadate/default.css | 112 +-- .../stylesheets/pickadate/default.date.css | 175 ++-- .../stylesheets/pickadate/default.time.css | 95 +- 6 files changed, 1881 insertions(+), 919 deletions(-) mode change 100755 => 100644 vendor/assets/javascripts/pickadate/picker.date.js mode change 100755 => 100644 vendor/assets/javascripts/pickadate/picker.js mode change 100755 => 100644 vendor/assets/javascripts/pickadate/picker.time.js mode change 100755 => 100644 vendor/assets/stylesheets/pickadate/default.css mode change 100755 => 100644 vendor/assets/stylesheets/pickadate/default.date.css mode change 100755 => 100644 vendor/assets/stylesheets/pickadate/default.time.css diff --git a/vendor/assets/javascripts/pickadate/picker.date.js b/vendor/assets/javascripts/pickadate/picker.date.js old mode 100755 new mode 100644 index 8b700a69..b34e8739 --- a/vendor/assets/javascripts/pickadate/picker.date.js +++ b/vendor/assets/javascripts/pickadate/picker.date.js @@ -1,28 +1,30 @@ - /*! - * Date picker for pickadate.js v3.1.4 + * Date picker for pickadate.js v3.5.6 * http://amsul.github.io/pickadate.js/date.htm */ -/*jshint - debug: true, - devel: true, - browser: true, - asi: true, - unused: true, - boss: true - */ +(function ( factory ) { + + // AMD. + if ( typeof define == 'function' && define.amd ) + define( ['picker', 'jquery'], factory ) + // Node.js/browserify. + else if ( typeof exports == 'object' ) + module.exports = factory( require('./picker.js'), require('jquery') ) -// Create a new scope. -(function() { + // Browser globals. + else factory( Picker, jQuery ) + +}(function( Picker, $ ) { /** * Globals and constants */ var DAYS_IN_WEEK = 7, - WEEKS_IN_CALENDAR = 6 + WEEKS_IN_CALENDAR = 6, + _ = Picker._ @@ -32,12 +34,24 @@ var DAYS_IN_WEEK = 7, function DatePicker( picker, settings ) { var calendar = this, - elementValue = picker.$node[ 0 ].value, + element = picker.$node[ 0 ], + elementValue = element.value, elementDataValue = picker.$node.data( 'value' ), valueString = elementDataValue || elementValue, - formatString = elementDataValue ? settings.formatSubmit : settings.format + formatString = elementDataValue ? settings.formatSubmit : settings.format, + isRTL = function() { + + return element.currentStyle ? + + // For IE. + element.currentStyle.direction == 'rtl' : + + // For normal browsers. + getComputedStyle( picker.$root[0] ).direction == 'rtl' + } calendar.settings = settings + calendar.$node = picker.$node // The queue of methods that will be used to build item objects. calendar.queue = { @@ -45,15 +59,16 @@ function DatePicker( picker, settings ) { max: 'measure create', now: 'now create', select: 'parse create validate', - highlight: 'navigate create validate', - view: 'create validate viewset', - disable: 'flipItem', - enable: 'flipItem' + highlight: 'parse navigate create validate', + view: 'parse create validate viewset', + disable: 'deactivate', + enable: 'activate' } // The component's item object. calendar.item = {} + calendar.item.clear = null calendar.item.disable = ( settings.disable || [] ).slice( 0 ) calendar.item.enable = -(function( collectionDisabled ) { return collectionDisabled[ 0 ] === true ? collectionDisabled.shift() : -1 @@ -62,34 +77,39 @@ function DatePicker( picker, settings ) { calendar. set( 'min', settings.min ). set( 'max', settings.max ). - set( 'now' ). - - // Setting the `select` also sets the `highlight` and `view`. - set( 'select', - - // Use the value provided or default to selecting “today”. - valueString || calendar.item.now, - { - // Use the appropriate format. - format: formatString, - - // Set user-provided month data as true when there is a - // “mm” or “m” used in the relative format string. - data: (function( formatArray ) { - return valueString && ( formatArray.indexOf( 'mm' ) > -1 || formatArray.indexOf( 'm' ) > -1 ) - })( calendar.formats.toArray( formatString ) ) - } - ) + set( 'now' ) + + // When there’s a value, set the `select`, which in turn + // also sets the `highlight` and `view`. + if ( valueString ) { + calendar.set( 'select', valueString, { + format: formatString, + defaultValue: true + }) + } + + // If there’s no value, default to highlighting “today”. + else { + calendar. + set( 'select', null ). + set( 'highlight', calendar.item.now ) + } // The keycode to movement mapping. calendar.key = { 40: 7, // Down 38: -7, // Up - 39: 1, // Right - 37: -1, // Left + 39: function() { return isRTL() ? -1 : 1 }, // Right + 37: function() { return isRTL() ? 1 : -1 }, // Left go: function( timeChange ) { - calendar.set( 'highlight', [ calendar.item.highlight.year, calendar.item.highlight.month, calendar.item.highlight.date + timeChange ], { interval: timeChange } ) + var highlightedObject = calendar.item.highlight, + targetDate = new Date( highlightedObject.year, highlightedObject.month, highlightedObject.date + timeChange ) + calendar.set( + 'highlight', + targetDate, + { interval: timeChange } + ) this.render() } } @@ -99,20 +119,30 @@ function DatePicker( picker, settings ) { picker. on( 'render', function() { picker.$root.find( '.' + settings.klass.selectMonth ).on( 'change', function() { - picker.set( 'highlight', [ picker.get( 'view' ).year, this.value, picker.get( 'highlight' ).date ] ) - picker.$root.find( '.' + settings.klass.selectMonth ).focus() + var value = this.value + if ( value ) { + picker.set( 'highlight', [ picker.get( 'view' ).year, value, picker.get( 'highlight' ).date ] ) + picker.$root.find( '.' + settings.klass.selectMonth ).trigger( 'focus' ) + } }) picker.$root.find( '.' + settings.klass.selectYear ).on( 'change', function() { - picker.set( 'highlight', [ this.value, picker.get( 'view' ).month, picker.get( 'highlight' ).date ] ) - picker.$root.find( '.' + settings.klass.selectYear ).focus() + var value = this.value + if ( value ) { + picker.set( 'highlight', [ value, picker.get( 'view' ).month, picker.get( 'highlight' ).date ] ) + picker.$root.find( '.' + settings.klass.selectYear ).trigger( 'focus' ) + } }) - }). + }, 1 ). on( 'open', function() { - picker.$root.find( 'button, select' ).attr( 'disabled', false ) - }). + var includeToday = '' + if ( calendar.disabled( calendar.get('now') ) ) { + includeToday = ':not(.' + settings.klass.buttonToday + ')' + } + picker.$root.find( 'button' + includeToday + ', select' ).attr( 'disabled', false ) + }, 1 ). on( 'close', function() { picker.$root.find( 'button, select' ).attr( 'disabled', true ) - }) + }, 1 ) } //DatePicker @@ -122,27 +152,39 @@ function DatePicker( picker, settings ) { */ DatePicker.prototype.set = function( type, value, options ) { - var calendar = this + var calendar = this, + calendarItem = calendar.item + + // If the value is `null` just set it immediately. + if ( value === null ) { + if ( type == 'clear' ) type = 'select' + calendarItem[ type ] = value + return calendar + } - // Go through the queue of methods, and invoke the function. Update this - // as the time unit, and set the final resultant as this item type. + // Otherwise go through the queue of methods, and invoke the functions. + // Update this as the time unit, and set the final value as this item. // * In the case of `enable`, keep the queue but set `disable` instead. // And in the case of `flip`, keep the queue but set `enable` instead. - calendar.item[ ( type == 'enable' ? 'disable' : type == 'flip' ? 'enable' : type ) ] = calendar.queue[ type ].split( ' ' ).map( function( method ) { - return value = calendar[ method ]( type, value, options ) + calendarItem[ ( type == 'enable' ? 'disable' : type == 'flip' ? 'enable' : type ) ] = calendar.queue[ type ].split( ' ' ).map( function( method ) { + value = calendar[ method ]( type, value, options ) + return value }).pop() // Check if we need to cascade through more updates. if ( type == 'select' ) { - calendar.set( 'highlight', calendar.item.select, options ) + calendar.set( 'highlight', calendarItem.select, options ) } else if ( type == 'highlight' ) { - calendar.set( 'view', calendar.item.highlight, options ) + calendar.set( 'view', calendarItem.highlight, options ) } - else if ( ( type == 'flip' || type == 'min' || type == 'max' || type == 'disable' || type == 'enable' ) && calendar.item.select && calendar.item.highlight ) { - calendar. - set( 'select', calendar.item.select, options ). - set( 'highlight', calendar.item.highlight, options ) + else if ( type.match( /^(flip|min|max|disable|enable)$/ ) ) { + if ( calendarItem.select && calendar.disabled( calendarItem.select ) ) { + calendar.set( 'select', calendarItem.select, options ) + } + if ( calendarItem.highlight && calendar.disabled( calendarItem.highlight ) ) { + calendar.set( 'highlight', calendarItem.highlight, options ) + } } return calendar @@ -175,19 +217,19 @@ DatePicker.prototype.create = function( type, value, options ) { } // If it’s an object, use the native date object. - else if ( Picker._.isObject( value ) && Picker._.isInteger( value.pick ) ) { + else if ( $.isPlainObject( value ) && _.isInteger( value.pick ) ) { value = value.obj } // If it’s an array, convert it into a date and make sure // that it’s a valid date – otherwise default to today. - else if ( Array.isArray( value ) ) { + else if ( $.isArray( value ) ) { value = new Date( value[ 0 ], value[ 1 ], value[ 2 ] ) - value = Picker._.isDate( value ) ? value : calendar.create().obj + value = _.isDate( value ) ? value : calendar.create().obj } // If it’s a number or date object, make a normalized date. - else if ( Picker._.isInteger( value ) || Picker._.isDate( value ) ) { + else if ( _.isInteger( value ) || _.isDate( value ) ) { value = calendar.normalize( new Date( value ), options ) } @@ -208,6 +250,68 @@ DatePicker.prototype.create = function( type, value, options ) { } //DatePicker.prototype.create +/** + * Create a range limit object using an array, date object, + * literal “true”, or integer relative to another time. + */ +DatePicker.prototype.createRange = function( from, to ) { + + var calendar = this, + createDate = function( date ) { + if ( date === true || $.isArray( date ) || _.isDate( date ) ) { + return calendar.create( date ) + } + return date + } + + // Create objects if possible. + if ( !_.isInteger( from ) ) { + from = createDate( from ) + } + if ( !_.isInteger( to ) ) { + to = createDate( to ) + } + + // Create relative dates. + if ( _.isInteger( from ) && $.isPlainObject( to ) ) { + from = [ to.year, to.month, to.date + from ]; + } + else if ( _.isInteger( to ) && $.isPlainObject( from ) ) { + to = [ from.year, from.month, from.date + to ]; + } + + return { + from: createDate( from ), + to: createDate( to ) + } +} //DatePicker.prototype.createRange + + +/** + * Check if a date unit falls within a date range object. + */ +DatePicker.prototype.withinRange = function( range, dateUnit ) { + range = this.createRange(range.from, range.to) + return dateUnit.pick >= range.from.pick && dateUnit.pick <= range.to.pick +} + + +/** + * Check if two date range objects overlap. + */ +DatePicker.prototype.overlapRanges = function( one, two ) { + + var calendar = this + + // Convert the ranges into comparable dates. + one = calendar.createRange( one.from, one.to ) + two = calendar.createRange( two.from, two.to ) + + return calendar.withinRange( one, two.from ) || calendar.withinRange( one, two.to ) || + calendar.withinRange( two, one.from ) || calendar.withinRange( two, one.to ) +} + + /** * Get the date today. */ @@ -217,7 +321,7 @@ DatePicker.prototype.now = function( type, value, options ) { value.setDate( value.getDate() + options.rel ) } return this.normalize( value, options ) -} //DatePicker.prototype.now +} /** @@ -225,20 +329,52 @@ DatePicker.prototype.now = function( type, value, options ) { */ DatePicker.prototype.navigate = function( type, value, options ) { - if ( Picker._.isObject( value ) ) { + var targetDateObject, + targetYear, + targetMonth, + targetDate, + isTargetArray = $.isArray( value ), + isTargetObject = $.isPlainObject( value ), + viewsetObject = this.item.view/*, + safety = 100*/ + + + if ( isTargetArray || isTargetObject ) { + + if ( isTargetObject ) { + targetYear = value.year + targetMonth = value.month + targetDate = value.date + } + else { + targetYear = +value[0] + targetMonth = +value[1] + targetDate = +value[2] + } - var targetDateObject = new Date( value.year, value.month + ( options && options.nav ? options.nav : 0 ), 1 ), - year = targetDateObject.getFullYear(), - month = targetDateObject.getMonth(), - date = value.date + // If we’re navigating months but the view is in a different + // month, navigate to the view’s year and month. + if ( options && options.nav && viewsetObject && viewsetObject.month !== targetMonth ) { + targetYear = viewsetObject.year + targetMonth = viewsetObject.month + } - // Make sure the date is valid and if the month we’re going to doesn’t have enough - // days, keep decreasing the date until we reach the month’s last date. - while ( Picker._.isDate( targetDateObject ) && new Date( year, month, date ).getMonth() !== month ) { - date -= 1 + // Figure out the expected target year and month. + targetDateObject = new Date( targetYear, targetMonth + ( options && options.nav ? options.nav : 0 ), 1 ) + targetYear = targetDateObject.getFullYear() + targetMonth = targetDateObject.getMonth() + + // If the month we’re going to doesn’t have enough days, + // keep decreasing the date until we reach the month’s last date. + while ( /*safety &&*/ new Date( targetYear, targetMonth, targetDate ).getMonth() !== targetMonth ) { + targetDate -= 1 + /*safety -= 1 + if ( !safety ) { + throw 'Fell into an infinite loop while navigating to ' + new Date( targetYear, targetMonth, targetDate ) + '.' + }*/ } - value = [ year, month, date ] + value = [ targetYear, targetMonth, targetDate ] } return value @@ -261,13 +397,18 @@ DatePicker.prototype.measure = function( type, value/*, options*/ ) { var calendar = this - // If it's anything false-y, remove the limits. + // If it’s anything false-y, remove the limits. if ( !value ) { value = type == 'min' ? -Infinity : Infinity } + // If it’s a string, parse it. + else if ( typeof value == 'string' ) { + value = calendar.parse( type, value ) + } + // If it's an integer, get a date relative to today. - else if ( Picker._.isInteger( value ) ) { + else if ( _.isInteger( value ) ) { value = calendar.now( type, value, { rel: value } ) } @@ -297,7 +438,7 @@ DatePicker.prototype.validate = function( type, dateObject, options ) { interval = options && options.interval ? options.interval : 1, // Check if the calendar enabled dates are inverted. - isInverted = calendar.item.enable === -1, + isFlippedBase = calendar.item.enable === -1, // Check if we have any enabled dates after/before now. hasEnabledBeforeTarget, hasEnabledAfterTarget, @@ -310,64 +451,81 @@ DatePicker.prototype.validate = function( type, dateObject, options ) { reachedMin, reachedMax, // Check if the calendar is inverted and at least one weekday is enabled. - hasEnabledWeekdays = isInverted && calendar.item.disable.filter( function( value ) { + hasEnabledWeekdays = isFlippedBase && calendar.item.disable.filter( function( value ) { // If there’s a date, check where it is relative to the target. - if ( Array.isArray( value ) ) { + if ( $.isArray( value ) ) { var dateTime = calendar.create( value ).pick if ( dateTime < dateObject.pick ) hasEnabledBeforeTarget = true else if ( dateTime > dateObject.pick ) hasEnabledAfterTarget = true } // Return only integers for enabled weekdays. - return Picker._.isInteger( value ) - }).length + return _.isInteger( value ) + }).length/*, + + safety = 100*/ // Cases to validate for: // [1] Not inverted and date disabled. // [2] Inverted and some dates enabled. - // [3] Out of range. + // [3] Not inverted and out of range. // // Cases to **not** validate for: // • Navigating months. // • Not inverted and date enabled. // • Inverted and all dates disabled. // • ..and anything else. - if ( !options.nav ) if ( - /* 1 */ ( !isInverted && calendar.disabled( dateObject ) ) || - /* 2 */ ( isInverted && calendar.disabled( dateObject ) && ( hasEnabledWeekdays || hasEnabledBeforeTarget || hasEnabledAfterTarget ) ) || - /* 3 */ ( dateObject.pick <= minLimitObject.pick || dateObject.pick >= maxLimitObject.pick ) + if ( !options || (!options.nav && !options.defaultValue) ) if ( + /* 1 */ ( !isFlippedBase && calendar.disabled( dateObject ) ) || + /* 2 */ ( isFlippedBase && calendar.disabled( dateObject ) && ( hasEnabledWeekdays || hasEnabledBeforeTarget || hasEnabledAfterTarget ) ) || + /* 3 */ ( !isFlippedBase && (dateObject.pick <= minLimitObject.pick || dateObject.pick >= maxLimitObject.pick) ) ) { // When inverted, flip the direction if there aren’t any enabled weekdays // and there are no enabled dates in the direction of the interval. - if ( isInverted && !hasEnabledWeekdays && ( ( !hasEnabledAfterTarget && interval > 0 ) || ( !hasEnabledBeforeTarget && interval < 0 ) ) ) { + if ( isFlippedBase && !hasEnabledWeekdays && ( ( !hasEnabledAfterTarget && interval > 0 ) || ( !hasEnabledBeforeTarget && interval < 0 ) ) ) { interval *= -1 } // Keep looping until we reach an enabled date. - while ( calendar.disabled( dateObject ) ) { + while ( /*safety &&*/ calendar.disabled( dateObject ) ) { + + /*safety -= 1 + if ( !safety ) { + throw 'Fell into an infinite loop while validating ' + dateObject.obj + '.' + }*/ - // If we’ve looped into the next/prev month, return to the original date and flatten the interval. + // If we’ve looped into the next/prev month with a large interval, return to the original date and flatten the interval. if ( Math.abs( interval ) > 1 && ( dateObject.month < originalDateObject.month || dateObject.month > originalDateObject.month ) ) { dateObject = originalDateObject - interval = Math.abs( interval ) / interval + interval = interval > 0 ? 1 : -1 } - // If we’ve reached the min/max limit, reverse the direction and flatten the interval. + // If we’ve reached the min/max limit, reverse the direction, flatten the interval and set it to the limit. if ( dateObject.pick <= minLimitObject.pick ) { reachedMin = true interval = 1 + dateObject = calendar.create([ + minLimitObject.year, + minLimitObject.month, + minLimitObject.date + (dateObject.pick === minLimitObject.pick ? 0 : -1) + ]) } else if ( dateObject.pick >= maxLimitObject.pick ) { reachedMax = true interval = -1 + dateObject = calendar.create([ + maxLimitObject.year, + maxLimitObject.month, + maxLimitObject.date + (dateObject.pick === maxLimitObject.pick ? 0 : 1) + ]) } @@ -390,32 +548,44 @@ DatePicker.prototype.validate = function( type, dateObject, options ) { /** - * Check if an object is disabled. + * Check if a date is disabled. */ -DatePicker.prototype.disabled = function( dateObject ) { +DatePicker.prototype.disabled = function( dateToVerify ) { - var calendar = this, + var + calendar = this, // Filter through the disabled dates to check if this is one. - isDisabledDate = calendar.item.disable.filter( function( dateToDisable ) { + isDisabledMatch = calendar.item.disable.filter( function( dateToDisable ) { // If the date is a number, match the weekday with 0index and `firstDay` check. - if ( Picker._.isInteger( dateToDisable ) ) { - return dateObject.day === ( calendar.settings.firstDay ? dateToDisable : dateToDisable - 1 ) % 7 + if ( _.isInteger( dateToDisable ) ) { + return dateToVerify.day === ( calendar.settings.firstDay ? dateToDisable : dateToDisable - 1 ) % 7 } - // If it's an array, create the object and match the exact date. - if ( Array.isArray( dateToDisable ) ) { - return dateObject.pick === calendar.create( dateToDisable ).pick + // If it’s an array or a native JS date, create and match the exact date. + if ( $.isArray( dateToDisable ) || _.isDate( dateToDisable ) ) { + return dateToVerify.pick === calendar.create( dateToDisable ).pick } - }).length + // If it’s an object, match a date within the “from” and “to” range. + if ( $.isPlainObject( dateToDisable ) ) { + return calendar.withinRange( dateToDisable, dateToVerify ) + } + }) + + // If this date matches a disabled date, confirm it’s not inverted. + isDisabledMatch = isDisabledMatch.length && !isDisabledMatch.filter(function( dateToDisable ) { + return $.isArray( dateToDisable ) && dateToDisable[3] == 'inverted' || + $.isPlainObject( dateToDisable ) && dateToDisable.inverted + }).length + + // Check the calendar “enabled” flag and respectively flip the + // disabled state. Then also check if it’s beyond the min/max limits. + return calendar.item.enable === -1 ? !isDisabledMatch : isDisabledMatch || + dateToVerify.pick < calendar.item.min.pick || + dateToVerify.pick > calendar.item.max.pick - // It’s disabled beyond the min/max limits. If within the limits, check the - // calendar “enabled” flag is flipped and respectively flip the condition. - return dateObject.pick < calendar.item.min.pick || - dateObject.pick > calendar.item.max.pick || - calendar.item.enable === -1 ? !isDisabledDate : isDisabledDate } //DatePicker.prototype.disabled @@ -427,14 +597,15 @@ DatePicker.prototype.parse = function( type, value, options ) { var calendar = this, parsingObject = {} - if ( !value || Picker._.isInteger( value ) || Array.isArray( value ) || Picker._.isDate( value ) || Picker._.isObject( value ) && Picker._.isInteger( value.pick ) ) { + // If it’s already parsed, we’re good. + if ( !value || typeof value != 'string' ) { return value } - // We need a `.format` to parse the value. + // We need a `.format` to parse the value with. if ( !( options && options.format ) ) { - // should probably default to the default format. - throw "Need a formatting option to parse this.." + options = options || {} + options.format = calendar.settings.format } // Convert the format into an array and then map through it. @@ -446,7 +617,7 @@ DatePicker.prototype.parse = function( type, value, options ) { // The format length is from the formatting label function or the // label length without the escaping exclamation (!) mark. - formatLength = formattingLabel ? Picker._.trigger( formattingLabel, calendar, [ value, parsingObject ] ) : label.replace( /^!/, '' ).length + formatLength = formattingLabel ? _.trigger( formattingLabel, calendar, [ value, parsingObject ] ) : label.replace( /^!/, '' ).length // If there's a format label, split the value up to the format length. // Then add it to the parsing object with appropriate label. @@ -458,8 +629,12 @@ DatePicker.prototype.parse = function( type, value, options ) { value = value.substr( formatLength ) }) - // If it’s parsing a user provided month value, compensate for month 0index. - return [ parsingObject.yyyy || parsingObject.yy, +( parsingObject.mm || parsingObject.m ) - ( options.data ? 1 : 0 ), parsingObject.dd || parsingObject.d ] + // Compensate for month 0index. + return [ + parsingObject.yyyy || parsingObject.yy, + +( parsingObject.mm || parsingObject.m ) - 1, + parsingObject.dd || parsingObject.d + ] } //DatePicker.prototype.parse @@ -472,11 +647,12 @@ DatePicker.prototype.formats = (function() { function getWordLengthFromCollection( string, collection, dateObject ) { // Grab the first word from the string. - var word = string.match( /\w+/ )[ 0 ] + // Regex pattern from http://stackoverflow.com/q/150033 + var word = string.match( /[^\x00-\x7F]+|\w+/ )[ 0 ] // If there's no month index, add it to the date object if ( !dateObject.mm && !dateObject.m ) { - dateObject.m = collection.indexOf( word ) + dateObject.m = collection.indexOf( word ) + 1 } // Return the length of the word. @@ -494,13 +670,13 @@ DatePicker.prototype.formats = (function() { // If there's string, then get the digits length. // Otherwise return the selected date. - return string ? Picker._.digits( string ) : dateObject.date + return string ? _.digits( string ) : dateObject.date }, dd: function( string, dateObject ) { // If there's a string, then the length is always 2. // Otherwise return the selected date with a leading zero. - return string ? 2 : Picker._.lead( dateObject.date ) + return string ? 2 : _.lead( dateObject.date ) }, ddd: function( string, dateObject ) { @@ -518,13 +694,13 @@ DatePicker.prototype.formats = (function() { // If there's a string, then get the length of the digits // Otherwise return the selected month with 0index compensation. - return string ? Picker._.digits( string ) : dateObject.month + 1 + return string ? _.digits( string ) : dateObject.month + 1 }, mm: function( string, dateObject ) { // If there's a string, then the length is always 2. // Otherwise return the selected month with 0index and leading zero. - return string ? 2 : Picker._.lead( dateObject.month + 1 ) + return string ? 2 : _.lead( dateObject.month + 1 ) }, mmm: function( string, dateObject ) { @@ -562,76 +738,232 @@ DatePicker.prototype.formats = (function() { toString: function ( formatString, itemObject ) { var calendar = this return calendar.formats.toArray( formatString ).map( function( label ) { - return Picker._.trigger( calendar.formats[ label ], calendar, [ 0, itemObject ] ) || label.replace( /^!/, '' ) + return _.trigger( calendar.formats[ label ], calendar, [ 0, itemObject ] ) || label.replace( /^!/, '' ) }).join( '' ) } } })() //DatePicker.prototype.formats + + /** - * Flip an item as enabled or disabled. + * Check if two date units are the exact. */ -DatePicker.prototype.flipItem = function( type, value/*, options*/ ) { +DatePicker.prototype.isDateExact = function( one, two ) { - var calendar = this, - collection = calendar.item.disable, - isInverted = calendar.item.enable === -1 + var calendar = this - // Flip the enabled and disabled dates. - if ( value == 'flip' ) { - calendar.item.enable = isInverted ? 1 : -1 + // When we’re working with weekdays, do a direct comparison. + if ( + ( _.isInteger( one ) && _.isInteger( two ) ) || + ( typeof one == 'boolean' && typeof two == 'boolean' ) + ) { + return one === two } - // Check if we have to add/remove from collection. - else if ( !isInverted && type == 'enable' || isInverted && type == 'disable' ) { - collection = calendar.removeDisabled( collection, value ) + // When we’re working with date representations, compare the “pick” value. + if ( + ( _.isDate( one ) || $.isArray( one ) ) && + ( _.isDate( two ) || $.isArray( two ) ) + ) { + return calendar.create( one ).pick === calendar.create( two ).pick } - else if ( !isInverted && type == 'disable' || isInverted && type == 'enable' ) { - collection = calendar.addDisabled( collection, value ) + + // When we’re working with range objects, compare the “from” and “to”. + if ( $.isPlainObject( one ) && $.isPlainObject( two ) ) { + return calendar.isDateExact( one.from, two.from ) && calendar.isDateExact( one.to, two.to ) } - return collection -} //DatePicker.prototype.flipItem + return false +} /** - * Add an item to the disabled collection. + * Check if two date units overlap. */ -DatePicker.prototype.addDisabled = function( collection, item ) { - var calendar = this - item.map( function( timeUnit ) { - if ( !calendar.filterDisabled( collection, timeUnit ).length ) { - collection.push( timeUnit ) - } - }) - return collection -} //DatePicker.prototype.addDisabled +DatePicker.prototype.isDateOverlap = function( one, two ) { + + var calendar = this, + firstDay = calendar.settings.firstDay ? 1 : 0 + + // When we’re working with a weekday index, compare the days. + if ( _.isInteger( one ) && ( _.isDate( two ) || $.isArray( two ) ) ) { + one = one % 7 + firstDay + return one === calendar.create( two ).day + 1 + } + if ( _.isInteger( two ) && ( _.isDate( one ) || $.isArray( one ) ) ) { + two = two % 7 + firstDay + return two === calendar.create( one ).day + 1 + } + + // When we’re working with range objects, check if the ranges overlap. + if ( $.isPlainObject( one ) && $.isPlainObject( two ) ) { + return calendar.overlapRanges( one, two ) + } + + return false +} /** - * Remove an item from the disabled collection. + * Flip the “enabled” state. */ -DatePicker.prototype.removeDisabled = function( collection, item ) { - var calendar = this - item.map( function( timeUnit ) { - collection = calendar.filterDisabled( collection, timeUnit, 1 ) - }) - return collection -} //DatePicker.prototype.removeDisabled +DatePicker.prototype.flipEnable = function(val) { + var itemObject = this.item + itemObject.enable = val || (itemObject.enable == -1 ? 1 : -1) +} /** - * Filter through the disabled collection to find a time unit. + * Mark a collection of dates as “disabled”. */ -DatePicker.prototype.filterDisabled = function( collection, timeUnit, isRemoving ) { - var timeIsArray = Array.isArray( timeUnit ) - return collection.filter( function( disabledTimeUnit ) { - var isMatch = !timeIsArray && timeUnit === disabledTimeUnit || - timeIsArray && Array.isArray( disabledTimeUnit ) && timeUnit.toString() === disabledTimeUnit.toString() - return isRemoving ? !isMatch : isMatch - }) -} //DatePicker.prototype.filterDisabled +DatePicker.prototype.deactivate = function( type, datesToDisable ) { + + var calendar = this, + disabledItems = calendar.item.disable.slice(0) + + + // If we’re flipping, that’s all we need to do. + if ( datesToDisable == 'flip' ) { + calendar.flipEnable() + } + + else if ( datesToDisable === false ) { + calendar.flipEnable(1) + disabledItems = [] + } + + else if ( datesToDisable === true ) { + calendar.flipEnable(-1) + disabledItems = [] + } + + // Otherwise go through the dates to disable. + else { + + datesToDisable.map(function( unitToDisable ) { + + var matchFound + + // When we have disabled items, check for matches. + // If something is matched, immediately break out. + for ( var index = 0; index < disabledItems.length; index += 1 ) { + if ( calendar.isDateExact( unitToDisable, disabledItems[index] ) ) { + matchFound = true + break + } + } + + // If nothing was found, add the validated unit to the collection. + if ( !matchFound ) { + if ( + _.isInteger( unitToDisable ) || + _.isDate( unitToDisable ) || + $.isArray( unitToDisable ) || + ( $.isPlainObject( unitToDisable ) && unitToDisable.from && unitToDisable.to ) + ) { + disabledItems.push( unitToDisable ) + } + } + }) + } + + // Return the updated collection. + return disabledItems +} //DatePicker.prototype.deactivate + + +/** + * Mark a collection of dates as “enabled”. + */ +DatePicker.prototype.activate = function( type, datesToEnable ) { + + var calendar = this, + disabledItems = calendar.item.disable, + disabledItemsCount = disabledItems.length + + // If we’re flipping, that’s all we need to do. + if ( datesToEnable == 'flip' ) { + calendar.flipEnable() + } + + else if ( datesToEnable === true ) { + calendar.flipEnable(1) + disabledItems = [] + } + + else if ( datesToEnable === false ) { + calendar.flipEnable(-1) + disabledItems = [] + } + + // Otherwise go through the disabled dates. + else { + + datesToEnable.map(function( unitToEnable ) { + + var matchFound, + disabledUnit, + index, + isExactRange + + // Go through the disabled items and try to find a match. + for ( index = 0; index < disabledItemsCount; index += 1 ) { + + disabledUnit = disabledItems[index] + + // When an exact match is found, remove it from the collection. + if ( calendar.isDateExact( disabledUnit, unitToEnable ) ) { + matchFound = disabledItems[index] = null + isExactRange = true + break + } + + // When an overlapped match is found, add the “inverted” state to it. + else if ( calendar.isDateOverlap( disabledUnit, unitToEnable ) ) { + if ( $.isPlainObject( unitToEnable ) ) { + unitToEnable.inverted = true + matchFound = unitToEnable + } + else if ( $.isArray( unitToEnable ) ) { + matchFound = unitToEnable + if ( !matchFound[3] ) matchFound.push( 'inverted' ) + } + else if ( _.isDate( unitToEnable ) ) { + matchFound = [ unitToEnable.getFullYear(), unitToEnable.getMonth(), unitToEnable.getDate(), 'inverted' ] + } + break + } + } + + // If a match was found, remove a previous duplicate entry. + if ( matchFound ) for ( index = 0; index < disabledItemsCount; index += 1 ) { + if ( calendar.isDateExact( disabledItems[index], unitToEnable ) ) { + disabledItems[index] = null + break + } + } + + // In the event that we’re dealing with an exact range of dates, + // make sure there are no “inverted” dates because of it. + if ( isExactRange ) for ( index = 0; index < disabledItemsCount; index += 1 ) { + if ( calendar.isDateOverlap( disabledItems[index], unitToEnable ) ) { + disabledItems[index] = null + break + } + } + + // If something is still matched, add it into the collection. + if ( matchFound ) { + disabledItems.push( matchFound ) + } + }) + } + + // Return the updated collection. + return disabledItems.filter(function( val ) { return val != null }) +} //DatePicker.prototype.activate /** @@ -642,48 +974,54 @@ DatePicker.prototype.nodes = function( isOpen ) { var calendar = this, settings = calendar.settings, - nowObject = calendar.item.now, - selectedObject = calendar.item.select, - highlightedObject = calendar.item.highlight, - viewsetObject = calendar.item.view, - disabledCollection = calendar.item.disable, - minLimitObject = calendar.item.min, - maxLimitObject = calendar.item.max, + calendarItem = calendar.item, + nowObject = calendarItem.now, + selectedObject = calendarItem.select, + highlightedObject = calendarItem.highlight, + viewsetObject = calendarItem.view, + disabledCollection = calendarItem.disable, + minLimitObject = calendarItem.min, + maxLimitObject = calendarItem.max, // Create the calendar table head using a copy of weekday labels collection. // * We do a copy so we don't mutate the original array. - tableHead = (function( collection ) { + tableHead = (function( collection, fullCollection ) { // If the first day should be Monday, move Sunday to the end. if ( settings.firstDay ) { collection.push( collection.shift() ) + fullCollection.push( fullCollection.shift() ) } // Create and return the table head group. - return Picker._.node( + return _.node( 'thead', - Picker._.group({ - min: 0, - max: DAYS_IN_WEEK - 1, - i: 1, - node: 'th', - item: function( counter ) { - return [ - collection[ counter ], - settings.klass.weekdays - ] - } - }) + _.node( + 'tr', + _.group({ + min: 0, + max: DAYS_IN_WEEK - 1, + i: 1, + node: 'th', + item: function( counter ) { + return [ + collection[ counter ], + settings.klass.weekdays, + 'scope=col title="' + fullCollection[ counter ] + '"' + ] + } + }) + ) ) //endreturn - })( ( settings.showWeekdaysFull ? settings.weekdaysFull : settings.weekdaysShort ).slice( 0 ) ), //tableHead + })( ( settings.showWeekdaysFull ? settings.weekdaysFull : settings.weekdaysShort ).slice( 0 ), settings.weekdaysFull.slice( 0 ) ), //tableHead // Create the nav for next/prev month. createMonthNav = function( next ) { // Otherwise, return the created month tag. - return Picker._.node( + return _.node( 'div', ' ', settings.klass[ 'nav' + ( next ? 'Next' : 'Prev' ) ] + ( @@ -693,46 +1031,59 @@ DatePicker.prototype.nodes = function( isOpen ) { ( !next && viewsetObject.year <= minLimitObject.year && viewsetObject.month <= minLimitObject.month ) ? ' ' + settings.klass.navDisabled : '' ), - 'data-nav=' + ( next || -1 ) + 'data-nav=' + ( next || -1 ) + ' ' + + _.ariaAttr({ + role: 'button', + controls: calendar.$node[0].id + '_table' + }) + ' ' + + 'title="' + (next ? settings.labelMonthNext : settings.labelMonthPrev ) + '"' ) //endreturn }, //createMonthNav // Create the month label. - createMonthLabel = function( monthsCollection ) { + createMonthLabel = function() { + + var monthsCollection = settings.showMonthsShort ? settings.monthsShort : settings.monthsFull // If there are months to select, add a dropdown menu. if ( settings.selectMonths ) { - return Picker._.node( 'select', Picker._.group({ - min: 0, - max: 11, - i: 1, - node: 'option', - item: function( loopedMonth ) { + return _.node( 'select', + _.group({ + min: 0, + max: 11, + i: 1, + node: 'option', + item: function( loopedMonth ) { - return [ + return [ - // The looped month and no classes. - monthsCollection[ loopedMonth ], 0, + // The looped month and no classes. + monthsCollection[ loopedMonth ], 0, - // Set the value and selected index. - 'value=' + loopedMonth + - ( viewsetObject.month == loopedMonth ? ' selected' : '' ) + - ( + // Set the value and selected index. + 'value=' + loopedMonth + + ( viewsetObject.month == loopedMonth ? ' selected' : '' ) + ( - ( viewsetObject.year == minLimitObject.year && loopedMonth < minLimitObject.month ) || - ( viewsetObject.year == maxLimitObject.year && loopedMonth > maxLimitObject.month ) - ) ? - ' disabled' : '' - ) - ] - } - }), settings.klass.selectMonth, isOpen ? '' : 'disabled' ) + ( + ( viewsetObject.year == minLimitObject.year && loopedMonth < minLimitObject.month ) || + ( viewsetObject.year == maxLimitObject.year && loopedMonth > maxLimitObject.month ) + ) ? + ' disabled' : '' + ) + ] + } + }), + settings.klass.selectMonth, + ( isOpen ? '' : 'disabled' ) + ' ' + + _.ariaAttr({ controls: calendar.$node[0].id + '_table' }) + ' ' + + 'title="' + settings.labelMonthSelect + '"' + ) } // If there's a need for a month selector - return Picker._.node( 'div', monthsCollection[ viewsetObject.month ], settings.klass.month ) + return _.node( 'div', monthsCollection[ viewsetObject.month ], settings.klass.month ) }, //createMonthLabel @@ -773,42 +1124,46 @@ DatePicker.prototype.nodes = function( isOpen ) { highestYear = maxYear } - return Picker._.node( 'select', Picker._.group({ - min: lowestYear, - max: highestYear, - i: 1, - node: 'option', - item: function( loopedYear ) { - return [ - - // The looped year and no classes. - loopedYear, 0, - - // Set the value and selected index. - 'value=' + loopedYear + ( focusedYear == loopedYear ? ' selected' : '' ) - ] - } - }), settings.klass.selectYear, isOpen ? '' : 'disabled' ) + return _.node( 'select', + _.group({ + min: lowestYear, + max: highestYear, + i: 1, + node: 'option', + item: function( loopedYear ) { + return [ + + // The looped year and no classes. + loopedYear, 0, + + // Set the value and selected index. + 'value=' + loopedYear + ( focusedYear == loopedYear ? ' selected' : '' ) + ] + } + }), + settings.klass.selectYear, + ( isOpen ? '' : 'disabled' ) + ' ' + _.ariaAttr({ controls: calendar.$node[0].id + '_table' }) + ' ' + + 'title="' + settings.labelYearSelect + '"' + ) } // Otherwise just return the year focused - return Picker._.node( 'div', focusedYear, settings.klass.year ) + return _.node( 'div', focusedYear, settings.klass.year ) } //createYearLabel // Create and return the entire calendar. - return Picker._.node( + return _.node( 'div', - createMonthNav() + createMonthNav( 1 ) + - createMonthLabel( settings.showMonthsShort ? settings.monthsShort : settings.monthsFull ) + - createYearLabel(), + ( settings.selectYears ? createYearLabel() + createMonthLabel() : createMonthLabel() + createYearLabel() ) + + createMonthNav() + createMonthNav( 1 ), settings.klass.header - ) + Picker._.node( + ) + _.node( 'table', tableHead + - Picker._.node( + _.node( 'tbody', - Picker._.group({ + _.group({ min: 0, max: WEEKS_IN_CALENDAR - 1, i: 1, @@ -819,7 +1174,7 @@ DatePicker.prototype.nodes = function( isOpen ) { var shiftDateBy = settings.firstDay && calendar.create([ viewsetObject.year, viewsetObject.month, 1 ]).day === 0 ? -7 : 0 return [ - Picker._.group({ + _.group({ min: DAYS_IN_WEEK * rowCounter - viewsetObject.day + shiftDateBy + 1, // Add 1 for weekday 0index max: function() { return this.min + DAYS_IN_WEEK - 1 @@ -831,8 +1186,13 @@ DatePicker.prototype.nodes = function( isOpen ) { // Convert the time date from a relative date to a target date. targetDate = calendar.create([ viewsetObject.year, viewsetObject.month, targetDate + ( settings.firstDay ? 1 : 0 ) ]) + var isSelected = selectedObject && selectedObject.pick == targetDate.pick, + isHighlighted = highlightedObject && highlightedObject.pick == targetDate.pick, + isDisabled = disabledCollection && calendar.disabled( targetDate ) || targetDate.pick < minLimitObject.pick || targetDate.pick > maxLimitObject.pick, + formattedDate = _.trigger( calendar.formats.toString, calendar, [ settings.format, targetDate ] ) + return [ - Picker._.node( + _.node( 'div', targetDate.date, (function( klasses ) { @@ -846,24 +1206,32 @@ DatePicker.prototype.nodes = function( isOpen ) { } // Add the `selected` class if something's selected and the time matches. - if ( selectedObject && selectedObject.pick == targetDate.pick ) { + if ( isSelected ) { klasses.push( settings.klass.selected ) } // Add the `highlighted` class if something's highlighted and the time matches. - if ( highlightedObject && highlightedObject.pick == targetDate.pick ) { + if ( isHighlighted ) { klasses.push( settings.klass.highlighted ) } // Add the `disabled` class if something's disabled and the object matches. - if ( disabledCollection && calendar.disabled( targetDate ) || targetDate.pick < minLimitObject.pick || targetDate.pick > maxLimitObject.pick ) { + if ( isDisabled ) { klasses.push( settings.klass.disabled ) } return klasses.join( ' ' ) })([ settings.klass.day ]), - 'data-pick=' + targetDate.pick - ) + 'data-pick=' + targetDate.pick + ' ' + _.ariaAttr({ + role: 'gridcell', + label: formattedDate, + selected: isSelected && calendar.$node.val() === formattedDate ? true : null, + activedescendant: isHighlighted ? true : null, + disabled: isDisabled ? true : null + }) + ), + '', + _.ariaAttr({ role: 'presentation' }) ] //endreturn } }) @@ -871,13 +1239,29 @@ DatePicker.prototype.nodes = function( isOpen ) { } }) ), - settings.klass.table + settings.klass.table, + 'id="' + calendar.$node[0].id + '_table' + '" ' + _.ariaAttr({ + role: 'grid', + controls: calendar.$node[0].id, + readonly: true + }) ) + - Picker._.node( + // * For Firefox forms to submit, make sure to set the buttons’ `type` attributes as “button”. + _.node( 'div', - Picker._.node( 'button', settings.today, settings.klass.buttonToday, 'data-pick=' + nowObject.pick + ( isOpen ? '' : ' disabled' ) ) + - Picker._.node( 'button', settings.clear, settings.klass.buttonClear, 'data-clear=1' + ( isOpen ? '' : ' disabled' ) ), + _.node( 'button', settings.today, settings.klass.buttonToday, + 'type=button data-pick=' + nowObject.pick + + ( isOpen && !calendar.disabled(nowObject) ? '' : ' disabled' ) + ' ' + + _.ariaAttr({ controls: calendar.$node[0].id }) ) + + _.node( 'button', settings.clear, settings.klass.buttonClear, + 'type=button data-clear=1' + + ( isOpen ? '' : ' disabled' ) + ' ' + + _.ariaAttr({ controls: calendar.$node[0].id }) ) + + _.node('button', settings.close, settings.klass.buttonClose, + 'type=button data-close=true ' + + ( isOpen ? '' : ' disabled' ) + ' ' + + _.ariaAttr({ controls: calendar.$node[0].id }) ), settings.klass.footer ) //endreturn } //DatePicker.prototype.nodes @@ -892,6 +1276,14 @@ DatePicker.defaults = (function( prefix ) { return { + // The title label to use for the month nav buttons + labelMonthNext: 'Next month', + labelMonthPrev: 'Previous month', + + // The title label to use for the dropdown selectors + labelMonthSelect: 'Select a month', + labelYearSelect: 'Select a year', + // Months and weekdays monthsFull: [ 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December' ], monthsShort: [ 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' ], @@ -901,6 +1293,11 @@ DatePicker.defaults = (function( prefix ) { // Today and clear today: 'Today', clear: 'Clear', + close: 'Close', + + // Picker close behavior + closeOnSelect: true, + closeOnClear: true, // The format to show on the `input` element format: 'd mmmm, yyyy', @@ -935,7 +1332,8 @@ DatePicker.defaults = (function( prefix ) { footer: prefix + 'footer', buttonClear: prefix + 'button--clear', - buttonToday: prefix + 'button--today' + buttonToday: prefix + 'button--today', + buttonClose: prefix + 'button--close' } } })( Picker.klasses().picker + '__' ) @@ -950,8 +1348,7 @@ DatePicker.defaults = (function( prefix ) { Picker.extend( 'pickadate', DatePicker ) -// Close the scope. -})(); +})); diff --git a/vendor/assets/javascripts/pickadate/picker.js b/vendor/assets/javascripts/pickadate/picker.js old mode 100755 new mode 100644 index 68469e8b..27906617 --- a/vendor/assets/javascripts/pickadate/picker.js +++ b/vendor/assets/javascripts/pickadate/picker.js @@ -1,24 +1,29 @@ - /*! - * pickadate.js v3.1.4, 2013/07/26 + * pickadate.js v3.5.6, 2015/04/20 * By Amsul, http://amsul.ca * Hosted on http://amsul.github.io/pickadate.js * Licensed under MIT */ -/*jshint - debug: true, - devel: true, - browser: true, - asi: true, - unused: true, - boss: true, - eqnull: true - */ +(function ( factory ) { + + // AMD. + if ( typeof define == 'function' && define.amd ) + define( 'picker', ['jquery'], factory ) + + // Node.js/browserify. + else if ( typeof exports == 'object' ) + module.exports = factory( require('jquery') ) + // Browser globals. + else this.Picker = factory( jQuery ) -// Create a global scope. -window.Picker = (function( $, $document, undefined ) { +}(function( $ ) { + +var $window = $( window ) +var $document = $( document ) +var $html = $( document.documentElement ) +var supportsTransitions = document.documentElement.style.transition != null /** @@ -31,9 +36,12 @@ function PickerConstructor( ELEMENT, NAME, COMPONENT, OPTIONS ) { var + IS_DEFAULT_THEME = false, + + // The state of the picker. STATE = { - id: Math.abs( ~~( Math.random() * 1e9 ) ) + id: ELEMENT.id || 'P' + Math.abs( ~~(Math.random() * new Date()) ) }, @@ -79,126 +87,43 @@ function PickerConstructor( ELEMENT, NAME, COMPONENT, OPTIONS ) { STATE.type = ELEMENT.type - // Confirm focus state, save original type, convert into text input - // to remove UA stylings, and set as readonly to prevent keyboard popup. - ELEMENT.autofocus = ELEMENT == document.activeElement - ELEMENT.type = 'text' - ELEMENT.readOnly = true + // Confirm focus state, convert into text input to remove UA stylings, + // and set as readonly to prevent keyboard popup. + ELEMENT.autofocus = ELEMENT == getActiveElement() + ELEMENT.readOnly = !SETTINGS.editable + ELEMENT.id = ELEMENT.id || STATE.id + if ( ELEMENT.type != 'text' ) { + ELEMENT.type = 'text' + } // Create a new picker component with the settings. - P.component = new COMPONENT( P, SETTINGS ) - - - // Create the picker root with a new wrapped holder and bind the events. - P.$root = $( PickerConstructor._.node( 'div', createWrappedComponent(), CLASSES.picker ) ).on({ - - // When something within the root is focused, stop from bubbling - // to the doc and remove the “focused” state from the root. - focusin: function( event ) { - P.$root.removeClass( CLASSES.focused ) - event.stopPropagation() - }, - - // If the event is not on the root holder, stop it from bubbling to the doc. - mousedown: function( event ) { - if ( event.target != P.$root.children()[ 0 ] ) { - event.stopPropagation() - } - }, + P.component = new COMPONENT(P, SETTINGS) - // When something within the root holder is clicked, handle the various event. - click: function( event ) { - - var target = event.target, - $target = target.attributes.length ? $( target ) : $( target ).closest( '[data-pick]' ), - targetData = $target.data() - - // If the event is not on the root holder itself, handle the clicks within. - if ( target != P.$root.children()[ 0 ] ) { - - // Stop it from propagating to the doc. - event.stopPropagation() - - // If nothing inside is actively focused, re-focus the element. - if ( !P.$root.find( document.activeElement ).length ) { - ELEMENT.focus() - } - - // If something is superficially changed, update the `highlight` based on the `nav`. - if ( targetData.nav && !$target.hasClass( CLASSES.navDisabled ) ) { - P.set( 'highlight', P.component.item.highlight, { nav: targetData.nav } ) - } - - // If something is picked, set `select` then close with focus. - else if ( PickerConstructor._.isInteger( targetData.pick ) && !$target.hasClass( CLASSES.disabled ) ) { - P.set( 'select', targetData.pick ).close( true ) - } - - // If a “clear” button is pressed, empty the values and close with focus. - else if ( targetData.clear ) { - P.clear().close( true ) - } - } - } - }) //P.$root + // Create the picker root and then prepare it. + P.$root = $( '

' ) + prepareElementRoot() - // If there’s a format for the hidden input element, create the element - // using the name of the original input plus suffix. Otherwise set it to null. - // If the element has a value, use either the `data-value` or `value`. - P._hidden = SETTINGS.formatSubmit ? $( '' )[ 0 ] : undefined + // Create the picker holder and then prepare it. + P.$holder = $( createWrappedComponent() ).appendTo( P.$root ) + prepareElementHolder() - // Add the class and bind the events on the element. - $ELEMENT.addClass( CLASSES.input ). - // On focus/click, open the picker and adjust the root “focused” state. - on( 'focus.P' + STATE.id + ' click.P' + STATE.id, focusToOpen ). - - // If the value changes, update the hidden input with the correct format. - on( 'change.P' + STATE.id, function() { - if ( P._hidden ) { - P._hidden.value = ELEMENT.value ? PickerConstructor._.trigger( P.component.formats.toString, P.component, [ SETTINGS.formatSubmit, P.component.item.select ] ) : '' - } - }). - - // Handle keyboard event based on the picker being opened or not. - on( 'keydown.P' + STATE.id, function( event ) { - - var keycode = event.keyCode, - - // Check if one of the delete keys was pressed. - isKeycodeDelete = /^(8|46)$/.test( keycode ) - - // For some reason IE clears the input value on “escape”. - if ( keycode == 27 ) { - P.close() - return false - } - - // Check if `space` or `delete` was pressed or the picker is closed with a key movement. - if ( keycode == 32 || isKeycodeDelete || !STATE.open && P.component.key[ keycode ] ) { - - // Prevent it from moving the page and bubbling to doc. - event.preventDefault() - event.stopPropagation() + // If there’s a format for the hidden input element, create the element. + if ( SETTINGS.formatSubmit ) { + prepareElementHidden() + } - // If `delete` was pressed, clear the values and close the picker. - // Otherwise open the picker. - if ( isKeycodeDelete ) { P.clear().close() } - else { P.open() } - } - }). - // If there’s a `data-value`, update the value of the element. - val( $ELEMENT.data( 'value' ) ? PickerConstructor._.trigger( P.component.formats.toString, P.component, [ SETTINGS.format, P.component.item.select ] ) : ELEMENT.value ). + // Prepare the input element. + prepareElement() - // Insert the hidden input after the element. - after( P._hidden ). - // Store the picker data by component name. - data( NAME, P ) + // Insert the hidden input as specified in the settings. + if ( SETTINGS.containerHidden ) $( SETTINGS.containerHidden ).append( P._hidden ) + else $ELEMENT.after( P._hidden ) // Insert the root as specified in the settings. @@ -224,6 +149,10 @@ function PickerConstructor( ELEMENT, NAME, COMPONENT, OPTIONS ) { }) + // Once we’re all set, check the theme in use. + IS_DEFAULT_THEME = isUsingDefaultTheme( P.$holder[0] ) + + // If the element has autofocus, open the picker. if ( ELEMENT.autofocus ) { P.open() @@ -236,12 +165,17 @@ function PickerConstructor( ELEMENT, NAME, COMPONENT, OPTIONS ) { /** - * Render a new picker within the root + * Render a new picker */ - render: function() { + render: function( entireComponent ) { - // Insert a new component holder in the root. - P.$root.html( createWrappedComponent() ) + // Insert a new component holder in the root or box. + if ( entireComponent ) { + P.$holder = $( createWrappedComponent() ) + prepareElementHolder() + P.$root.html( P.$holder ) + } + else P.$root.find( '.' + CLASSES.box ).html( P.component.nodes( STATE.open ) ) // Trigger the queued “render” events. return P.trigger( 'render' ) @@ -267,8 +201,12 @@ function PickerConstructor( ELEMENT, NAME, COMPONENT, OPTIONS ) { // Remove the root. P.$root.remove() - // Remove the input class, unbind the events, and remove the stored data. - $ELEMENT.removeClass( CLASSES.input ).off( '.P' + STATE.id ).removeData( NAME ) + // Remove the input class, remove the stored data, and unbind + // the events (after a tick for IE - see `P.close`). + $ELEMENT.removeClass( CLASSES.input ).removeData( NAME ) + setTimeout( function() { + $ELEMENT.off( '.' + STATE.id ) + }, 0) // Restore the element state ELEMENT.type = STATE.type @@ -285,7 +223,7 @@ function PickerConstructor( ELEMENT, NAME, COMPONENT, OPTIONS ) { }, //stop - /* + /** * Open up the picker */ open: function( dontGiveFocus ) { @@ -295,9 +233,18 @@ function PickerConstructor( ELEMENT, NAME, COMPONENT, OPTIONS ) { // Add the “active” class. $ELEMENT.addClass( CLASSES.active ) + aria( ELEMENT, 'expanded', true ) - // Add the “opened” class to the picker root. - P.$root.addClass( CLASSES.opened ) + // * A Firefox bug, when `html` has `overflow:hidden`, results in + // killing transitions :(. So add the “opened” state on the next tick. + // Bug: https://bugzilla.mozilla.org/show_bug.cgi?id=625289 + setTimeout( function() { + + // Add the “opened” class to the picker root. + P.$root.addClass( CLASSES.opened ) + aria( P.$root[0], 'hidden', false ) + + }, 0 ) // If we have to give focus, bind the element and doc events. if ( dontGiveFocus !== false ) { @@ -305,19 +252,36 @@ function PickerConstructor( ELEMENT, NAME, COMPONENT, OPTIONS ) { // Set it as open. STATE.open = true - // Pass focus to the element’s jQuery object. - $ELEMENT.focus() + // Prevent the page from scrolling. + if ( IS_DEFAULT_THEME ) { + $html. + css( 'overflow', 'hidden' ). + css( 'padding-right', '+=' + getScrollbarWidth() ) + } + + // Pass focus to the root element’s jQuery object. + focusPickerOnceOpened() // Bind the document events. - $document.on( 'click.P' + STATE.id + ' focusin.P' + STATE.id, function( event ) { + $document.on( 'click.' + STATE.id + ' focusin.' + STATE.id, function( event ) { + + var target = event.target // If the target of the event is not the element, close the picker picker. // * Don’t worry about clicks or focusins on the root because those don’t bubble up. // Also, for Firefox, a click on an `option` element bubbles up directly // to the doc. So make sure the target wasn't the doc. - if ( event.target != ELEMENT && event.target != document ) P.close() + // * In Firefox stopPropagation() doesn’t prevent right-click events from bubbling, + // which causes the picker to unexpectedly close when right-clicking it. So make + // sure the event wasn’t a right-click. + if ( target != ELEMENT && target != document && event.which != 3 ) { + + // If the target was the holder that covers the screen, + // keep the element focused to maintain tabindex. + P.close( target === P.$holder[0] ) + } - }).on( 'keydown.P' + STATE.id, function( event ) { + }).on( 'keydown.' + STATE.id, function( event ) { var // Get the keycode. @@ -337,26 +301,29 @@ function PickerConstructor( ELEMENT, NAME, COMPONENT, OPTIONS ) { // Check if there is a key movement or “enter” keypress on the element. - else if ( target == ELEMENT && ( keycodeToMove || keycode == 13 ) ) { + else if ( target == P.$holder[0] && ( keycodeToMove || keycode == 13 ) ) { // Prevent the default action to stop page movement. event.preventDefault() // Trigger the key movement action. if ( keycodeToMove ) { - PickerConstructor._.trigger( P.component.key.go, P, [ keycodeToMove ] ) + PickerConstructor._.trigger( P.component.key.go, P, [ PickerConstructor._.trigger( keycodeToMove ) ] ) } // On “enter”, if the highlighted item isn’t disabled, set the value and close. else if ( !P.$root.find( '.' + CLASSES.highlighted ).hasClass( CLASSES.disabled ) ) { - P.set( 'select', P.component.item.highlight ).close() + P.set( 'select', P.component.item.highlight ) + if ( SETTINGS.closeOnSelect ) { + P.close( true ) + } } } // If the target is within the root and “enter” is pressed, // prevent the default action and trigger a click on the target instead. - else if ( P.$root.find( target ).length && keycode == 13 ) { + else if ( $.contains( P.$root[0], target ) && keycode == 13 ) { event.preventDefault() target.click() } @@ -375,31 +342,51 @@ function PickerConstructor( ELEMENT, NAME, COMPONENT, OPTIONS ) { // If we need to give focus, do it before changing states. if ( giveFocus ) { - // ....ah yes! It would’ve been incomplete without a crazy workaround for IE :| - // The focus is triggered *after* the close has completed - causing it - // to open again. So unbind and rebind the event at the next tick. - $ELEMENT.off( 'focus.P' + STATE.id ).focus() - setTimeout( function() { - $ELEMENT.on( 'focus.P' + STATE.id, focusToOpen ) - }, 0 ) + if ( SETTINGS.editable ) { + ELEMENT.focus() + } + else { + // ....ah yes! It would’ve been incomplete without a crazy workaround for IE :| + // The focus is triggered *after* the close has completed - causing it + // to open again. So unbind and rebind the event at the next tick. + P.$holder.off( 'focus.toOpen' ).focus() + setTimeout( function() { + P.$holder.on( 'focus.toOpen', handleFocusToOpenEvent ) + }, 0 ) + } } // Remove the “active” class. $ELEMENT.removeClass( CLASSES.active ) + aria( ELEMENT, 'expanded', false ) + + // * A Firefox bug, when `html` has `overflow:hidden`, results in + // killing transitions :(. So remove the “opened” state on the next tick. + // Bug: https://bugzilla.mozilla.org/show_bug.cgi?id=625289 + setTimeout( function() { - // Remove the “opened” and “focused” class from the picker root. - P.$root.removeClass( CLASSES.opened + ' ' + CLASSES.focused ) + // Remove the “opened” and “focused” class from the picker root. + P.$root.removeClass( CLASSES.opened + ' ' + CLASSES.focused ) + aria( P.$root[0], 'hidden', true ) - // If it’s open, update the state. - if ( STATE.open ) { + }, 0 ) - // Set it as closed. - STATE.open = false + // If it’s already closed, do nothing more. + if ( !STATE.open ) return P - // Unbind the document events. - $document.off( '.P' + STATE.id ) + // Set it as closed. + STATE.open = false + + // Allow the page to scroll. + if ( IS_DEFAULT_THEME ) { + $html. + css( 'overflow', '' ). + css( 'padding-right', '-=' + getScrollbarWidth() ) } + // Unbind the document events. + $document.off( '.' + STATE.id ) + // Trigger the queued “close” events. return P.trigger( 'close' ) }, //close @@ -408,8 +395,8 @@ function PickerConstructor( ELEMENT, NAME, COMPONENT, OPTIONS ) { /** * Clear the values */ - clear: function() { - return P.set( 'clear' ) + clear: function( options ) { + return P.set( 'clear', null, options ) }, //clear @@ -419,9 +406,12 @@ function PickerConstructor( ELEMENT, NAME, COMPONENT, OPTIONS ) { set: function( thing, value, options ) { var thingItem, thingValue, - thingIsObject = PickerConstructor._.isObject( thing ), + thingIsObject = $.isPlainObject( thing ), thingObject = thingIsObject ? thing : {} + // Make sure we have usable options. + options = thingIsObject && $.isPlainObject( value ) ? value : options || {} + if ( thing ) { // If the thing isn’t an object, make it one. @@ -436,15 +426,16 @@ function PickerConstructor( ELEMENT, NAME, COMPONENT, OPTIONS ) { thingValue = thingObject[ thingItem ] // First, if the item exists and there’s a value, set it. - if ( P.component.item[ thingItem ] ) { - P.component.set( thingItem, thingValue, options || {} ) + if ( thingItem in P.component.item ) { + if ( thingValue === undefined ) thingValue = null + P.component.set( thingItem, thingValue, options ) } // Then, check to update the element value and broadcast a change. if ( thingItem == 'select' || thingItem == 'clear' ) { - $ELEMENT.val( thingItem == 'clear' ? '' : - PickerConstructor._.trigger( P.component.formats.toString, P.component, [ SETTINGS.format, P.component.get( thingItem ) ] ) - ).trigger( 'change' ) + $ELEMENT. + val( thingItem == 'clear' ? '' : P.get( thingItem, SETTINGS.format ) ). + trigger( 'change' ) } } @@ -452,8 +443,8 @@ function PickerConstructor( ELEMENT, NAME, COMPONENT, OPTIONS ) { P.render() } - // Trigger queued “set” events and pass the `thingObject`. - return P.trigger( 'set', thingObject ) + // When the method isn’t muted, trigger queued “set” events and pass the `thingObject`. + return options.muted ? P : P.trigger( 'set', thingObject ) }, //set @@ -470,15 +461,29 @@ function PickerConstructor( ELEMENT, NAME, COMPONENT, OPTIONS ) { return STATE[ thing ] } + // Return the submission value, if that. + if ( thing == 'valueSubmit' ) { + if ( P._hidden ) { + return P._hidden.value + } + thing = 'value' + } + // Return the value, if that. if ( thing == 'value' ) { return ELEMENT.value } // Check if a component item exists, return that. - if ( P.component.item[ thing ] ) { + if ( thing in P.component.item ) { if ( typeof format == 'string' ) { - return PickerConstructor._.trigger( P.component.formats.toString, P.component, [ format, P.component.get( thing ) ] ) + var thingValue = P.component.get( thing ) + return thingValue ? + PickerConstructor._.trigger( + P.component.formats.toString, + P.component, + [ format, thingValue ] + ) : '' } return P.component.get( thing ) } @@ -489,10 +494,10 @@ function PickerConstructor( ELEMENT, NAME, COMPONENT, OPTIONS ) { /** * Bind events on the things. */ - on: function( thing, method ) { + on: function( thing, method, internal ) { var thingName, thingMethod, - thingIsObject = PickerConstructor._.isObject( thing ), + thingIsObject = $.isPlainObject( thing ), thingObject = thingIsObject ? thing : {} if ( thing ) { @@ -508,6 +513,11 @@ function PickerConstructor( ELEMENT, NAME, COMPONENT, OPTIONS ) { // Grab the method of the thing. thingMethod = thingObject[ thingName ] + // If it was an internal binding, prefix it. + if ( internal ) { + thingName = '_' + thingName + } + // Make sure the thing methods collection exists. STATE.methods[ thingName ] = STATE.methods[ thingName ] || [] @@ -520,16 +530,37 @@ function PickerConstructor( ELEMENT, NAME, COMPONENT, OPTIONS ) { }, //on + + /** + * Unbind events on the things. + */ + off: function() { + var i, thingName, + names = arguments; + for ( i = 0, namesCount = names.length; i < namesCount; i += 1 ) { + thingName = names[i] + if ( thingName in STATE.methods ) { + delete STATE.methods[thingName] + } + } + return P + }, + + /** * Fire off method events. */ trigger: function( name, data ) { - var methodList = STATE.methods[ name ] - if ( methodList ) { - methodList.map( function( method ) { - PickerConstructor._.trigger( method, P, [ data ] ) - }) + var _trigger = function( name ) { + var methodList = STATE.methods[ name ] + if ( methodList ) { + methodList.map( function( method ) { + PickerConstructor._.trigger( method, P, [ data ] ) + }) + } } + _trigger( '_' + name ) + _trigger( name ) return P } //trigger } //PickerInstance.prototype @@ -568,25 +599,275 @@ function PickerConstructor( ELEMENT, NAME, COMPONENT, OPTIONS ) { ), // Picker holder class - CLASSES.holder + CLASSES.holder, + + 'tabindex="-1"' ) //endreturn } //createWrappedComponent - // Separated for IE - function focusToOpen( event ) { + + /** + * Prepare the input element with all bindings. + */ + function prepareElement() { + + $ELEMENT. + + // Store the picker data by component name. + data(NAME, P). + + // Add the “input” class name. + addClass(CLASSES.input). + + // If there’s a `data-value`, update the value of the element. + val( $ELEMENT.data('value') ? + P.get('select', SETTINGS.format) : + ELEMENT.value + ) + + + // Only bind keydown events if the element isn’t editable. + if ( !SETTINGS.editable ) { + + $ELEMENT. + + // On focus/click, open the picker. + on( 'focus.' + STATE.id + ' click.' + STATE.id, function(event) { + event.preventDefault() + P.open() + }). + + // Handle keyboard event based on the picker being opened or not. + on( 'keydown.' + STATE.id, handleKeydownEvent ) + } + + + // Update the aria attributes. + aria(ELEMENT, { + haspopup: true, + expanded: false, + readonly: false, + owns: ELEMENT.id + '_root' + }) + } + + + /** + * Prepare the root picker element with all bindings. + */ + function prepareElementRoot() { + aria( P.$root[0], 'hidden', true ) + } + + + /** + * Prepare the holder picker element with all bindings. + */ + function prepareElementHolder() { + + P.$holder. + + on({ + + // For iOS8. + keydown: handleKeydownEvent, + + 'focus.toOpen': handleFocusToOpenEvent, + + blur: function() { + // Remove the “target” class. + $ELEMENT.removeClass( CLASSES.target ) + }, + + // When something within the holder is focused, stop from bubbling + // to the doc and remove the “focused” state from the root. + focusin: function( event ) { + P.$root.removeClass( CLASSES.focused ) + event.stopPropagation() + }, + + // When something within the holder is clicked, stop it + // from bubbling to the doc. + 'mousedown click': function( event ) { + + var target = event.target + + // Make sure the target isn’t the root holder so it can bubble up. + if ( target != P.$holder[0] ) { + + event.stopPropagation() + + // * For mousedown events, cancel the default action in order to + // prevent cases where focus is shifted onto external elements + // when using things like jQuery mobile or MagnificPopup (ref: #249 & #120). + // Also, for Firefox, don’t prevent action on the `option` element. + if ( event.type == 'mousedown' && !$( target ).is( 'input, select, textarea, button, option' )) { + + event.preventDefault() + + // Re-focus onto the holder so that users can click away + // from elements focused within the picker. + P.$holder[0].focus() + } + } + } + + }). + + // If there’s a click on an actionable element, carry out the actions. + on( 'click', '[data-pick], [data-nav], [data-clear], [data-close]', function() { + + var $target = $( this ), + targetData = $target.data(), + targetDisabled = $target.hasClass( CLASSES.navDisabled ) || $target.hasClass( CLASSES.disabled ), + + // * For IE, non-focusable elements can be active elements as well + // (http://stackoverflow.com/a/2684561). + activeElement = getActiveElement() + activeElement = activeElement && ( activeElement.type || activeElement.href ) + + // If it’s disabled or nothing inside is actively focused, re-focus the element. + if ( targetDisabled || activeElement && !$.contains( P.$root[0], activeElement ) ) { + P.$holder[0].focus() + } + + // If something is superficially changed, update the `highlight` based on the `nav`. + if ( !targetDisabled && targetData.nav ) { + P.set( 'highlight', P.component.item.highlight, { nav: targetData.nav } ) + } + + // If something is picked, set `select` then close with focus. + else if ( !targetDisabled && 'pick' in targetData ) { + P.set( 'select', targetData.pick ) + if ( SETTINGS.closeOnSelect ) { + P.close( true ) + } + } + + // If a “clear” button is pressed, empty the values and close with focus. + else if ( targetData.clear ) { + P.clear() + if ( SETTINGS.closeOnClear ) { + P.close( true ) + } + } + + else if ( targetData.close ) { + P.close( true ) + } + + }) //P.$holder + + } + + + /** + * Prepare the hidden input element along with all bindings. + */ + function prepareElementHidden() { + + var name + + if ( SETTINGS.hiddenName === true ) { + name = ELEMENT.name + ELEMENT.name = '' + } + else { + name = [ + typeof SETTINGS.hiddenPrefix == 'string' ? SETTINGS.hiddenPrefix : '', + typeof SETTINGS.hiddenSuffix == 'string' ? SETTINGS.hiddenSuffix : '_submit' + ] + name = name[0] + ELEMENT.name + name[1] + } + + P._hidden = $( + '' + )[0] + + $ELEMENT. + + // If the value changes, update the hidden input with the correct format. + on('change.' + STATE.id, function() { + P._hidden.value = ELEMENT.value ? + P.get('select', SETTINGS.formatSubmit) : + '' + }) + } + + + // Wait for transitions to end before focusing the holder. Otherwise, while + // using the `container` option, the view jumps to the container. + function focusPickerOnceOpened() { + + if (IS_DEFAULT_THEME && supportsTransitions) { + P.$holder.find('.' + CLASSES.frame).one('transitionend', function() { + P.$holder[0].focus() + }) + } + else { + P.$holder[0].focus() + } + } + + + function handleFocusToOpenEvent(event) { // Stop the event from propagating to the doc. event.stopPropagation() - // If it’s a focus event, add the “focused” class to the root. - if ( event.type == 'focus' ) P.$root.addClass( CLASSES.focused ) + // Add the “target” class. + $ELEMENT.addClass( CLASSES.target ) + + // Add the “focused” class to the root. + P.$root.addClass( CLASSES.focused ) // And then finally open the picker. P.open() } + // For iOS8. + function handleKeydownEvent( event ) { + + var keycode = event.keyCode, + + // Check if one of the delete keys was pressed. + isKeycodeDelete = /^(8|46)$/.test(keycode) + + // For some reason IE clears the input value on “escape”. + if ( keycode == 27 ) { + P.close( true ) + return false + } + + // Check if `space` or `delete` was pressed or the picker is closed with a key movement. + if ( keycode == 32 || isKeycodeDelete || !STATE.open && P.component.key[keycode] ) { + + // Prevent it from moving the page and bubbling to doc. + event.preventDefault() + event.stopPropagation() + + // If `delete` was pressed, clear the values and close the picker. + // Otherwise open the picker. + if ( isKeycodeDelete ) { P.clear().close() } + else { P.open() } + } + } + + // Return a new picker instance. return new PickerInstance() } //PickerConstructor @@ -606,6 +887,7 @@ PickerConstructor.klasses = function( prefix ) { input: prefix + '__input', active: prefix + '__input--active', + target: prefix + '__input--target', holder: prefix + '__holder', @@ -618,6 +900,63 @@ PickerConstructor.klasses = function( prefix ) { +/** + * Check if the default theme is being used. + */ +function isUsingDefaultTheme( element ) { + + var theme, + prop = 'position' + + // For IE. + if ( element.currentStyle ) { + theme = element.currentStyle[prop] + } + + // For normal browsers. + else if ( window.getComputedStyle ) { + theme = getComputedStyle( element )[prop] + } + + return theme == 'fixed' +} + + + +/** + * Get the width of the browser’s scrollbar. + * Taken from: https://github.com/VodkaBears/Remodal/blob/master/src/jquery.remodal.js + */ +function getScrollbarWidth() { + + if ( $html.height() <= $window.height() ) { + return 0 + } + + var $outer = $( '
' ). + appendTo( 'body' ) + + // Get the width without scrollbars. + var widthWithoutScroll = $outer[0].offsetWidth + + // Force adding scrollbars. + $outer.css( 'overflow', 'scroll' ) + + // Add the inner div. + var $inner = $( '
' ).appendTo( $outer ) + + // Get the width with scrollbars. + var widthWithScroll = $inner[0].offsetWidth + + // Remove the divs. + $outer.remove() + + // Return the difference between the widths. + return widthWithoutScroll - widthWithScroll +} + + + /** * PickerConstructor helper methods. */ @@ -677,7 +1016,7 @@ PickerConstructor._ = { if ( !item ) return '' // If the item is an array, do a join - item = Array.isArray( item ) ? item.join( '' ) : item + item = $.isArray( item ) ? item.join( '' ) : item // Check for the class klass = klass ? ' class="' + klass + '"' : '' @@ -714,14 +1053,6 @@ PickerConstructor._ = { }, - /** - * Tell if something is an object. - */ - isObject: function( value ) { - return {}.toString.call( value ).indexOf( 'Object' ) > -1 - }, - - /** * Tell if something is a date object. */ @@ -735,7 +1066,13 @@ PickerConstructor._ = { */ isInteger: function( value ) { return {}.toString.call( value ).indexOf( 'Number' ) > -1 && value % 1 === 0 - } + }, + + + /** + * Create ARIA attribute strings. + */ + ariaAttr: ariaAttr } //PickerConstructor._ @@ -758,8 +1095,7 @@ PickerConstructor.extend = function( name, Component ) { // If the component data exists and `options` is a string, carry out the action. if ( componentData && typeof options == 'string' ) { - PickerConstructor._.trigger( componentData[ options ], componentData, [ action ] ) - return this + return PickerConstructor._.trigger( componentData[ options ], componentData, [ action ] ) } // Otherwise go through each matched element and if the component @@ -779,12 +1115,49 @@ PickerConstructor.extend = function( name, Component ) { -// Return the picker constructor. +function aria(element, attribute, value) { + if ( $.isPlainObject(attribute) ) { + for ( var key in attribute ) { + ariaSet(element, key, attribute[key]) + } + } + else { + ariaSet(element, attribute, value) + } +} +function ariaSet(element, attribute, value) { + element.setAttribute( + (attribute == 'role' ? '' : 'aria-') + attribute, + value + ) +} +function ariaAttr(attribute, data) { + if ( !$.isPlainObject(attribute) ) { + attribute = { attribute: data } + } + data = '' + for ( var key in attribute ) { + var attr = (key == 'role' ? '' : 'aria-') + key, + attrVal = attribute[key] + data += attrVal == null ? '' : attr + '="' + attribute[key] + '"' + } + return data +} + +// IE8 bug throws an error for activeElements within iframes. +function getActiveElement() { + try { + return document.activeElement + } catch ( err ) { } +} + + + +// Expose the picker constructor. return PickerConstructor -// Close the global scope. -})( jQuery, jQuery( document ) ); +})); diff --git a/vendor/assets/javascripts/pickadate/picker.time.js b/vendor/assets/javascripts/pickadate/picker.time.js old mode 100755 new mode 100644 index 3e97d84e..99f15482 --- a/vendor/assets/javascripts/pickadate/picker.time.js +++ b/vendor/assets/javascripts/pickadate/picker.time.js @@ -1,21 +1,22 @@ - /*! - * Time picker for pickadate.js v3.1.4 + * Time picker for pickadate.js v3.5.6 * http://amsul.github.io/pickadate.js/time.htm */ -/*jshint - debug: true, - devel: true, - browser: true, - asi: true, - unused: true, - boss: true - */ +(function ( factory ) { + + // AMD. + if ( typeof define == 'function' && define.amd ) + define( ['picker', 'jquery'], factory ) + // Node.js/browserify. + else if ( typeof exports == 'object' ) + module.exports = factory( require('./picker.js'), require('jquery') ) -// Create a new scope. -(function() { + // Browser globals. + else factory( Picker, jQuery ) + +}(function( Picker, $ ) { /** @@ -24,7 +25,8 @@ var HOURS_IN_DAY = 24, MINUTES_IN_HOUR = 60, HOURS_TO_NOON = 12, - MINUTES_IN_DAY = HOURS_IN_DAY * MINUTES_IN_HOUR + MINUTES_IN_DAY = HOURS_IN_DAY * MINUTES_IN_HOUR, + _ = Picker._ @@ -34,9 +36,13 @@ var HOURS_IN_DAY = 24, function TimePicker( picker, settings ) { var clock = this, - elementDataValue = picker.$node.data( 'value' ) + elementValue = picker.$node[ 0 ].value, + elementDataValue = picker.$node.data( 'value' ), + valueString = elementDataValue || elementValue, + formatString = elementDataValue ? settings.formatSubmit : settings.format clock.settings = settings + clock.$node = picker.$node // The queue of methods that will be used to build item objects. clock.queue = { @@ -45,15 +51,16 @@ function TimePicker( picker, settings ) { max: 'measure create', now: 'now create', select: 'parse create validate', - highlight: 'create validate', - view: 'create validate', - disable: 'flipItem', - enable: 'flipItem' + highlight: 'parse create validate', + view: 'parse create validate', + disable: 'deactivate', + enable: 'activate' } // The component's item object. clock.item = {} + clock.item.clear = null clock.item.interval = settings.interval || 30 clock.item.disable = ( settings.disable || [] ).slice( 0 ) clock.item.enable = -(function( collectionDisabled ) { @@ -63,18 +70,22 @@ function TimePicker( picker, settings ) { clock. set( 'min', settings.min ). set( 'max', settings.max ). - set( 'now' ). - - // Setting the `select` also sets the `highlight` and `view`. - set( 'select', + set( 'now' ) - // If there's a `value` or `data-value`, use that with formatting. - // Otherwise default to the minimum selectable time. - elementDataValue || picker.$node[ 0 ].value || clock.item.min, + // When there’s a value, set the `select`, which in turn + // also sets the `highlight` and `view`. + if ( valueString ) { + clock.set( 'select', valueString, { + format: formatString + }) + } - // Use the relevant format. - { format: elementDataValue ? settings.formatSubmit : settings.format } - ) + // If there’s no value, default to highlighting “today”. + else { + clock. + set( 'select', null ). + set( 'highlight', clock.item.now ) + } // The keycode to movement mapping. clock.key = { @@ -83,7 +94,11 @@ function TimePicker( picker, settings ) { 39: 1, // Right 37: -1, // Left go: function( timeChange ) { - clock.set( 'highlight', clock.item.highlight.pick + timeChange * clock.item.interval, { interval: timeChange * clock.item.interval } ) + clock.set( + 'highlight', + clock.item.highlight.pick + timeChange * clock.item.interval, + { interval: timeChange * clock.item.interval } + ) this.render() } } @@ -93,20 +108,32 @@ function TimePicker( picker, settings ) { picker. on( 'render', function() { var $pickerHolder = picker.$root.children(), - $viewset = $pickerHolder.find( '.' + settings.klass.viewset ) + $viewset = $pickerHolder.find( '.' + settings.klass.viewset ), + vendors = function( prop ) { + return ['webkit', 'moz', 'ms', 'o', ''].map(function( vendor ) { + return ( vendor ? '-' + vendor + '-' : '' ) + prop + }) + }, + animations = function( $el, state ) { + vendors( 'transform' ).map(function( prop ) { + $el.css( prop, state ) + }) + vendors( 'transition' ).map(function( prop ) { + $el.css( prop, state ) + }) + } if ( $viewset.length ) { - $pickerHolder[ 0 ].scrollTop = ~~( $viewset.position().top - ( $viewset[ 0 ].clientHeight * 2 ) ) - } - else { - console.warn( 'Nothing to viewset with', clock.item.view ) + animations( $pickerHolder, 'none' ) + $pickerHolder[ 0 ].scrollTop = ~~$viewset.position().top - ( $viewset[ 0 ].clientHeight * 2 ) + animations( $pickerHolder, '' ) } - }). + }, 1 ). on( 'open', function() { - picker.$root.find( 'button' ).attr( 'disable', false ) - }). + picker.$root.find( 'button' ).attr( 'disabled', false ) + }, 1 ). on( 'close', function() { - picker.$root.find( 'button' ).attr( 'disable', true ) - }) + picker.$root.find( 'button' ).attr( 'disabled', true ) + }, 1 ) } //TimePicker @@ -116,35 +143,47 @@ function TimePicker( picker, settings ) { */ TimePicker.prototype.set = function( type, value, options ) { - var clock = this + var clock = this, + clockItem = clock.item - // Go through the queue of methods, and invoke the function. Update this - // as the time unit, and set the final resultant as this item type. + // If the value is `null` just set it immediately. + if ( value === null ) { + if ( type == 'clear' ) type = 'select' + clockItem[ type ] = value + return clock + } + + // Otherwise go through the queue of methods, and invoke the functions. + // Update this as the time unit, and set the final value as this item. // * In the case of `enable`, keep the queue but set `disable` instead. // And in the case of `flip`, keep the queue but set `enable` instead. - clock.item[ ( type == 'enable' ? 'disable' : type == 'flip' ? 'enable' : type ) ] = clock.queue[ type ].split( ' ' ).map( function( method ) { - return value = clock[ method ]( type, value, options ) + clockItem[ ( type == 'enable' ? 'disable' : type == 'flip' ? 'enable' : type ) ] = clock.queue[ type ].split( ' ' ).map( function( method ) { + value = clock[ method ]( type, value, options ) + return value }).pop() // Check if we need to cascade through more updates. if ( type == 'select' ) { - clock.set( 'highlight', clock.item.select, options ) + clock.set( 'highlight', clockItem.select, options ) } else if ( type == 'highlight' ) { - clock.set( 'view', clock.item.highlight, options ) + clock.set( 'view', clockItem.highlight, options ) } else if ( type == 'interval' ) { clock. - set( 'min', clock.item.min, options ). - set( 'max', clock.item.max, options ) + set( 'min', clockItem.min, options ). + set( 'max', clockItem.max, options ) } - else if ( ( type == 'flip' || type == 'min' || type == 'max' || type == 'disable' || type == 'enable' ) && clock.item.select && clock.item.highlight ) { + else if ( type.match( /^(flip|min|max|disable|enable)$/ ) ) { + if ( clockItem.select && clock.disabled( clockItem.select ) ) { + clock.set( 'select', value, options ) + } + if ( clockItem.highlight && clock.disabled( clockItem.highlight ) ) { + clock.set( 'highlight', value, options ) + } if ( type == 'min' ) { - clock.set( 'max', clock.item.max, options ) + clock.set( 'max', clockItem.max, options ) } - clock. - set( 'select', clock.item.select, options ). - set( 'highlight', clock.item.highlight, options ) } return clock @@ -166,31 +205,42 @@ TimePicker.prototype.create = function( type, value, options ) { var clock = this - // If there's no value, use the type as the value. + // If there’s no value, use the type as the value. value = value === undefined ? type : value - // If it's an object, use the "pick" value. - if ( Picker._.isObject( value ) && Picker._.isInteger( value.pick ) ) { + // If it’s a date object, convert it into an array. + if ( _.isDate( value ) ) { + value = [ value.getHours(), value.getMinutes() ] + } + + // If it’s an object, use the “pick” value. + if ( $.isPlainObject( value ) && _.isInteger( value.pick ) ) { value = value.pick } - // If it's an array, convert it into minutes. - else if ( Array.isArray( value ) ) { + // If it’s an array, convert it into minutes. + else if ( $.isArray( value ) ) { value = +value[ 0 ] * MINUTES_IN_HOUR + (+value[ 1 ]) } - // If no valid value is passed, set it to "now". - else if ( !Picker._.isInteger( value ) ) { + // If no valid value is passed, set it to “now”. + else if ( !_.isInteger( value ) ) { value = clock.now( type, value, options ) } - // If we're setting the max, make sure it's greater than the min. + // If we’re setting the max, make sure it’s greater than the min. if ( type == 'max' && value < clock.item.min.pick ) { value += MINUTES_IN_DAY } - // Normalize it into a "reachable" interval. - value = clock.normalize( value, options ) + // If the value doesn’t fall directly on the interval, + // add one interval to indicate it as “passed”. + if ( type != 'min' && type != 'max' && (value - clock.item.min.pick) % clock.item.interval !== 0 ) { + value += clock.item.interval + } + + // Normalize it into a “reachable” interval. + value = clock.normalize( type, value, options ) // Return the compiled object. return { @@ -204,43 +254,123 @@ TimePicker.prototype.create = function( type, value, options ) { // The time in total minutes. time: ( MINUTES_IN_DAY + value ) % MINUTES_IN_DAY, - // Reference to the "relative" value to pick. - pick: value + // Reference to the “relative” value to pick. + pick: value % MINUTES_IN_DAY } } //TimePicker.prototype.create +/** + * Create a range limit object using an array, date object, + * literal “true”, or integer relative to another time. + */ +TimePicker.prototype.createRange = function( from, to ) { + + var clock = this, + createTime = function( time ) { + if ( time === true || $.isArray( time ) || _.isDate( time ) ) { + return clock.create( time ) + } + return time + } + + // Create objects if possible. + if ( !_.isInteger( from ) ) { + from = createTime( from ) + } + if ( !_.isInteger( to ) ) { + to = createTime( to ) + } + + // Create relative times. + if ( _.isInteger( from ) && $.isPlainObject( to ) ) { + from = [ to.hour, to.mins + ( from * clock.settings.interval ) ]; + } + else if ( _.isInteger( to ) && $.isPlainObject( from ) ) { + to = [ from.hour, from.mins + ( to * clock.settings.interval ) ]; + } + + return { + from: createTime( from ), + to: createTime( to ) + } +} //TimePicker.prototype.createRange + + +/** + * Check if a time unit falls within a time range object. + */ +TimePicker.prototype.withinRange = function( range, timeUnit ) { + range = this.createRange(range.from, range.to) + return timeUnit.pick >= range.from.pick && timeUnit.pick <= range.to.pick +} + + +/** + * Check if two time range objects overlap. + */ +TimePicker.prototype.overlapRanges = function( one, two ) { + + var clock = this + + // Convert the ranges into comparable times. + one = clock.createRange( one.from, one.to ) + two = clock.createRange( two.from, two.to ) + + return clock.withinRange( one, two.from ) || clock.withinRange( one, two.to ) || + clock.withinRange( two, one.from ) || clock.withinRange( two, one.to ) +} + + /** * Get the time relative to now. */ TimePicker.prototype.now = function( type, value/*, options*/ ) { - var date = new Date(), - dateMinutes = date.getHours() * MINUTES_IN_HOUR + date.getMinutes() + var interval = this.item.interval, + date = new Date(), + nowMinutes = date.getHours() * MINUTES_IN_HOUR + date.getMinutes(), + isValueInteger = _.isInteger( value ), + isBelowInterval - // If the value is a number, adjust by that many intervals because - // the time has passed. In the case of “midnight” and a negative `min`, - // increase the value by 2. Otherwise increase it by 1. - if ( Picker._.isInteger( value ) ) { - value += type == 'min' && value < 0 && dateMinutes === 0 ? 2 : 1 - } + // Make sure “now” falls within the interval range. + nowMinutes -= nowMinutes % interval - // If the value isn’t a number, default to 1 passed interval. - else { - value = 1 + // Check if the difference is less than the interval itself. + isBelowInterval = value < 0 && interval * value + nowMinutes <= -interval + + // Add an interval because the time has “passed”. + nowMinutes += type == 'min' && isBelowInterval ? 0 : interval + + // If the value is a number, adjust by that many intervals. + if ( isValueInteger ) { + nowMinutes += interval * ( + isBelowInterval && type != 'max' ? + value + 1 : + value + ) } - // Calculate the final relative time. - return value * this.item.interval + dateMinutes + // Return the final calculation. + return nowMinutes } //TimePicker.prototype.now /** - * Normalize minutes or an object to be "reachable" based on the interval. + * Normalize minutes to be “reachable” based on the min and interval. */ -TimePicker.prototype.normalize = function( value/*, options*/ ) { - // If it's a negative value, add one interval to keep it as "passed". - return value - ( ( value < 0 ? this.item.interval : 0 ) + value % this.item.interval ) +TimePicker.prototype.normalize = function( type, value/*, options*/ ) { + + var interval = this.item.interval, + minTime = this.item.min && this.item.min.pick || 0 + + // If setting min time, don’t shift anything. + // Otherwise get the value and min difference and then + // normalize the difference with the interval. + value -= type == 'min' ? 0 : ( value - minTime ) % interval + + // Return the adjusted value. + return value } //TimePicker.prototype.normalize @@ -251,19 +381,24 @@ TimePicker.prototype.measure = function( type, value, options ) { var clock = this - // If it's anything false-y, set it to the default. + // If it’s anything false-y, set it to the default. if ( !value ) { value = type == 'min' ? [ 0, 0 ] : [ HOURS_IN_DAY - 1, MINUTES_IN_HOUR - 1 ] } - // If it's a literal true, or an integer, make it relative to now. - else if ( value === true || Picker._.isInteger( value ) ) { + // If it’s a string, parse it. + if ( typeof value == 'string' ) { + value = clock.parse( type, value ) + } + + // If it’s a literal true, or an integer, make it relative to now. + else if ( value === true || _.isInteger( value ) ) { value = clock.now( type, value, options ) } - // If it's an object already, just normalize it. - else if ( Picker._.isObject( value ) && Picker._.isInteger( value.pick ) ) { - value = clock.normalize( value.pick, options ) + // If it’s an object already, just normalize it. + else if ( $.isPlainObject( value ) && _.isInteger( value.pick ) ) { + value = clock.normalize( type, value.pick, options ) } return value @@ -302,27 +437,39 @@ TimePicker.prototype.validate = function( type, timeObject, options ) { /** * Check if an object is disabled. */ -TimePicker.prototype.disabled = function( timeObject ) { +TimePicker.prototype.disabled = function( timeToVerify ) { - var - clock = this, + var clock = this, // Filter through the disabled times to check if this is one. - isDisabledTime = clock.item.disable.filter( function( timeToDisable ) { + isDisabledMatch = clock.item.disable.filter( function( timeToDisable ) { // If the time is a number, match the hours. - if ( Picker._.isInteger( timeToDisable ) ) { - return timeObject.hour == timeToDisable + if ( _.isInteger( timeToDisable ) ) { + return timeToVerify.hour == timeToDisable + } + + // If it’s an array, create the object and match the times. + if ( $.isArray( timeToDisable ) || _.isDate( timeToDisable ) ) { + return timeToVerify.pick == clock.create( timeToDisable ).pick } - // If it's an array, create the object and match the times. - if ( Array.isArray( timeToDisable ) ) { - return timeObject.pick == clock.create( timeToDisable ).pick + // If it’s an object, match a time within the “from” and “to” range. + if ( $.isPlainObject( timeToDisable ) ) { + return clock.withinRange( timeToDisable, timeToVerify ) } - }).length + }) + + // If this time matches a disabled time, confirm it’s not inverted. + isDisabledMatch = isDisabledMatch.length && !isDisabledMatch.filter(function( timeToDisable ) { + return $.isArray( timeToDisable ) && timeToDisable[2] == 'inverted' || + $.isPlainObject( timeToDisable ) && timeToDisable.inverted + }).length // If the clock is "enabled" flag is flipped, flip the condition. - return clock.item.enable === -1 ? !isDisabledTime : isDisabledTime + return clock.item.enable === -1 ? !isDisabledMatch : isDisabledMatch || + timeToVerify.pick < clock.item.min.pick || + timeToVerify.pick > clock.item.max.pick } //TimePicker.prototype.disabled @@ -331,17 +478,26 @@ TimePicker.prototype.disabled = function( timeObject ) { */ TimePicker.prototype.shift = function( timeObject, interval ) { - var - clock = this + var clock = this, + minLimit = clock.item.min.pick, + maxLimit = clock.item.max.pick/*, + safety = 1000*/ + + interval = interval || clock.item.interval // Keep looping as long as the time is disabled. - while ( clock.disabled( timeObject ) ) { + while ( /*safety &&*/ clock.disabled( timeObject ) ) { + + /*safety -= 1 + if ( !safety ) { + throw 'Fell into an infinite loop while shifting to ' + timeObject.hour + ':' + timeObject.mins + '.' + }*/ // Increase/decrease the time by the interval and keep looping. - timeObject = clock.create( timeObject.pick += interval || clock.item.interval ) + timeObject = clock.create( timeObject.pick += interval ) // If we've looped beyond the limits, break out of the loop. - if ( timeObject.pick <= clock.item.min.pick || timeObject.pick >= clock.item.max.pick ) { + if ( timeObject.pick <= minLimit || timeObject.pick >= maxLimit ) { break } } @@ -366,46 +522,68 @@ TimePicker.prototype.scope = function( timeObject ) { */ TimePicker.prototype.parse = function( type, value, options ) { - var + var hour, minutes, isPM, item, parseValue, clock = this, parsingObject = {} - if ( !value || Picker._.isInteger( value ) || Array.isArray( value ) || Picker._.isDate( value ) || Picker._.isObject( value ) && Picker._.isInteger( value.pick ) ) { + // If it’s already parsed, we’re good. + if ( !value || typeof value != 'string' ) { return value } - // We need a `.format` to parse the value. + // We need a `.format` to parse the value with. if ( !( options && options.format ) ) { - throw "Need a formatting option to parse this.." + options = options || {} + options.format = clock.settings.format } // Convert the format into an array and then map through it. clock.formats.toArray( options.format ).map( function( label ) { var + substring, + // Grab the formatting label. formattingLabel = clock.formats[ label ], // The format length is from the formatting label function or the // label length without the escaping exclamation (!) mark. - formatLength = formattingLabel ? Picker._.trigger( formattingLabel, clock, [ value, parsingObject ] ) : label.replace( /^!/, '' ).length + formatLength = formattingLabel ? + _.trigger( formattingLabel, clock, [ value, parsingObject ] ) : + label.replace( /^!/, '' ).length // If there's a format label, split the value up to the format length. // Then add it to the parsing object with appropriate label. if ( formattingLabel ) { - parsingObject[ label ] = value.substr( 0, formatLength ) + substring = value.substr( 0, formatLength ) + parsingObject[ label ] = substring.match(/^\d+$/) ? +substring : substring } // Update the time value as the substring from format length to end. value = value.substr( formatLength ) }) - return +parsingObject.i + MINUTES_IN_HOUR * ( - - +( parsingObject.H || parsingObject.HH ) || + // Grab the hour and minutes from the parsing object. + for ( item in parsingObject ) { + parseValue = parsingObject[item] + if ( _.isInteger(parseValue) ) { + if ( item.match(/^(h|hh)$/i) ) { + hour = parseValue + if ( item == 'h' || item == 'hh' ) { + hour %= 12 + } + } + else if ( item == 'i' ) { + minutes = parseValue + } + } + else if ( item.match(/^a$/i) && parseValue.match(/^p/i) && ('h' in parsingObject || 'hh' in parsingObject) ) { + isPM = true + } + } - ( +( parsingObject.h || parsingObject.hh ) % 12 + ( /^p/i.test( parsingObject.A || parsingObject.a ) ? 12 : 0 ) ) - ) + // Calculate it in minutes and return. + return (isPM ? hour + 12 : hour) * MINUTES_IN_HOUR + minutes } //TimePicker.prototype.parse @@ -418,31 +596,31 @@ TimePicker.prototype.formats = { // If there's string, then get the digits length. // Otherwise return the selected hour in "standard" format. - return string ? Picker._.digits( string ) : timeObject.hour % HOURS_TO_NOON || HOURS_TO_NOON + return string ? _.digits( string ) : timeObject.hour % HOURS_TO_NOON || HOURS_TO_NOON }, hh: function( string, timeObject ) { // If there's a string, then the length is always 2. // Otherwise return the selected hour in "standard" format with a leading zero. - return string ? 2 : Picker._.lead( timeObject.hour % HOURS_TO_NOON || HOURS_TO_NOON ) + return string ? 2 : _.lead( timeObject.hour % HOURS_TO_NOON || HOURS_TO_NOON ) }, H: function( string, timeObject ) { // If there's string, then get the digits length. // Otherwise return the selected hour in "military" format as a string. - return string ? Picker._.digits( string ) : '' + timeObject.hour + return string ? _.digits( string ) : '' + ( timeObject.hour % 24 ) }, HH: function( string, timeObject ) { // If there's string, then get the digits length. // Otherwise return the selected hour in "military" format with a leading zero. - return string ? Picker._.digits( string ) : Picker._.lead( timeObject.hour ) + return string ? _.digits( string ) : _.lead( timeObject.hour % 24 ) }, i: function( string, timeObject ) { // If there's a string, then the length is always 2. // Otherwise return the selected minutes. - return string ? 2 : Picker._.lead( timeObject.mins ) + return string ? 2 : _.lead( timeObject.mins ) }, a: function( string, timeObject ) { @@ -464,82 +642,235 @@ TimePicker.prototype.formats = { toString: function ( formatString, itemObject ) { var clock = this return clock.formats.toArray( formatString ).map( function( label ) { - return Picker._.trigger( clock.formats[ label ], clock, [ 0, itemObject ] ) || label.replace( /^!/, '' ) + return _.trigger( clock.formats[ label ], clock, [ 0, itemObject ] ) || label.replace( /^!/, '' ) }).join( '' ) } } //TimePicker.prototype.formats + + /** - * Flip an item as enabled or disabled. + * Check if two time units are the exact. */ -TimePicker.prototype.flipItem = function( type, value/*, options*/ ) { +TimePicker.prototype.isTimeExact = function( one, two ) { - var clock = this, - collection = clock.item.disable, - isFlipped = clock.item.enable === -1 + var clock = this - // Flip the enabled and disabled times. - if ( value == 'flip' ) { - clock.item.enable = isFlipped ? 1 : -1 + // When we’re working with minutes, do a direct comparison. + if ( + ( _.isInteger( one ) && _.isInteger( two ) ) || + ( typeof one == 'boolean' && typeof two == 'boolean' ) + ) { + return one === two } - // Check if we have to add/remove from collection. - else if ( !isFlipped && type == 'enable' || isFlipped && type == 'disable' ) { - collection = clock.removeDisabled( collection, value ) + // When we’re working with time representations, compare the “pick” value. + if ( + ( _.isDate( one ) || $.isArray( one ) ) && + ( _.isDate( two ) || $.isArray( two ) ) + ) { + return clock.create( one ).pick === clock.create( two ).pick } - else if ( !isFlipped && type == 'disable' || isFlipped && type == 'enable' ) { - collection = clock.addDisabled( collection, value ) + + // When we’re working with range objects, compare the “from” and “to”. + if ( $.isPlainObject( one ) && $.isPlainObject( two ) ) { + return clock.isTimeExact( one.from, two.from ) && clock.isTimeExact( one.to, two.to ) } - return collection -} //TimePicker.prototype.flipItem + return false +} /** - * Add an item to the disabled collection. + * Check if two time units overlap. */ -TimePicker.prototype.addDisabled = function( collection, item ) { +TimePicker.prototype.isTimeOverlap = function( one, two ) { + var clock = this - item.map( function( timeUnit ) { - if ( !clock.filterDisabled( collection, timeUnit ).length ) { - collection.push( timeUnit ) - } - }) - return collection -} //TimePicker.prototype.addDisabled + + // When we’re working with an integer, compare the hours. + if ( _.isInteger( one ) && ( _.isDate( two ) || $.isArray( two ) ) ) { + return one === clock.create( two ).hour + } + if ( _.isInteger( two ) && ( _.isDate( one ) || $.isArray( one ) ) ) { + return two === clock.create( one ).hour + } + + // When we’re working with range objects, check if the ranges overlap. + if ( $.isPlainObject( one ) && $.isPlainObject( two ) ) { + return clock.overlapRanges( one, two ) + } + + return false +} /** - * Remove an item from the disabled collection. + * Flip the “enabled” state. */ -TimePicker.prototype.removeDisabled = function( collection, item ) { - var clock = this - item.map( function( timeUnit ) { - collection = clock.filterDisabled( collection, timeUnit, 1 ) - }) - return collection -} //TimePicker.prototype.removeDisabled +TimePicker.prototype.flipEnable = function(val) { + var itemObject = this.item + itemObject.enable = val || (itemObject.enable == -1 ? 1 : -1) +} /** - * Filter through the disabled collection to find a time unit. + * Mark a collection of times as “disabled”. */ -TimePicker.prototype.filterDisabled = function( collection, timeUnit, isRemoving ) { - var timeIsArray = Array.isArray( timeUnit ) - return collection.filter( function( disabledTimeUnit ) { - var isMatch = !timeIsArray && timeUnit === disabledTimeUnit || - timeIsArray && Array.isArray( disabledTimeUnit ) && timeUnit.toString() === disabledTimeUnit.toString() - return isRemoving ? !isMatch : isMatch - }) -} //TimePicker.prototype.filterDisabled +TimePicker.prototype.deactivate = function( type, timesToDisable ) { + + var clock = this, + disabledItems = clock.item.disable.slice(0) + + + // If we’re flipping, that’s all we need to do. + if ( timesToDisable == 'flip' ) { + clock.flipEnable() + } + + else if ( timesToDisable === false ) { + clock.flipEnable(1) + disabledItems = [] + } + + else if ( timesToDisable === true ) { + clock.flipEnable(-1) + disabledItems = [] + } + + // Otherwise go through the times to disable. + else { + + timesToDisable.map(function( unitToDisable ) { + + var matchFound + + // When we have disabled items, check for matches. + // If something is matched, immediately break out. + for ( var index = 0; index < disabledItems.length; index += 1 ) { + if ( clock.isTimeExact( unitToDisable, disabledItems[index] ) ) { + matchFound = true + break + } + } + + // If nothing was found, add the validated unit to the collection. + if ( !matchFound ) { + if ( + _.isInteger( unitToDisable ) || + _.isDate( unitToDisable ) || + $.isArray( unitToDisable ) || + ( $.isPlainObject( unitToDisable ) && unitToDisable.from && unitToDisable.to ) + ) { + disabledItems.push( unitToDisable ) + } + } + }) + } + + // Return the updated collection. + return disabledItems +} //TimePicker.prototype.deactivate + + +/** + * Mark a collection of times as “enabled”. + */ +TimePicker.prototype.activate = function( type, timesToEnable ) { + + var clock = this, + disabledItems = clock.item.disable, + disabledItemsCount = disabledItems.length + + // If we’re flipping, that’s all we need to do. + if ( timesToEnable == 'flip' ) { + clock.flipEnable() + } + + else if ( timesToEnable === true ) { + clock.flipEnable(1) + disabledItems = [] + } + + else if ( timesToEnable === false ) { + clock.flipEnable(-1) + disabledItems = [] + } + + // Otherwise go through the disabled times. + else { + + timesToEnable.map(function( unitToEnable ) { + + var matchFound, + disabledUnit, + index, + isRangeMatched + + // Go through the disabled items and try to find a match. + for ( index = 0; index < disabledItemsCount; index += 1 ) { + + disabledUnit = disabledItems[index] + + // When an exact match is found, remove it from the collection. + if ( clock.isTimeExact( disabledUnit, unitToEnable ) ) { + matchFound = disabledItems[index] = null + isRangeMatched = true + break + } + + // When an overlapped match is found, add the “inverted” state to it. + else if ( clock.isTimeOverlap( disabledUnit, unitToEnable ) ) { + if ( $.isPlainObject( unitToEnable ) ) { + unitToEnable.inverted = true + matchFound = unitToEnable + } + else if ( $.isArray( unitToEnable ) ) { + matchFound = unitToEnable + if ( !matchFound[2] ) matchFound.push( 'inverted' ) + } + else if ( _.isDate( unitToEnable ) ) { + matchFound = [ unitToEnable.getFullYear(), unitToEnable.getMonth(), unitToEnable.getDate(), 'inverted' ] + } + break + } + } + + // If a match was found, remove a previous duplicate entry. + if ( matchFound ) for ( index = 0; index < disabledItemsCount; index += 1 ) { + if ( clock.isTimeExact( disabledItems[index], unitToEnable ) ) { + disabledItems[index] = null + break + } + } + + // In the event that we’re dealing with an overlap of range times, + // make sure there are no “inverted” times because of it. + if ( isRangeMatched ) for ( index = 0; index < disabledItemsCount; index += 1 ) { + if ( clock.isTimeOverlap( disabledItems[index], unitToEnable ) ) { + disabledItems[index] = null + break + } + } + + // If something is still matched, add it into the collection. + if ( matchFound ) { + disabledItems.push( matchFound ) + } + }) + } + + // Return the updated collection. + return disabledItems.filter(function( val ) { return val != null }) +} //TimePicker.prototype.activate /** * The division to use for the range intervals. */ TimePicker.prototype.i = function( type, value/*, options*/ ) { - return Picker._.isInteger( value ) && value > 0 ? value : this.item.interval + return _.isInteger( value ) && value > 0 ? value : this.item.interval } @@ -556,39 +887,68 @@ TimePicker.prototype.nodes = function( isOpen ) { viewsetObject = clock.item.view, disabledCollection = clock.item.disable - return Picker._.node( 'ul', Picker._.group({ - min: clock.item.min.pick, - max: clock.item.max.pick, - i: clock.item.interval, - node: 'li', - item: function( loopedTime ) { - loopedTime = clock.create( loopedTime ) - return [ - Picker._.trigger( clock.formats.toString, clock, [ Picker._.trigger( settings.formatLabel, clock, [ loopedTime ] ) || settings.format, loopedTime ] ), - (function( klasses, timeMinutes ) { - - if ( selectedObject && selectedObject.pick == timeMinutes ) { - klasses.push( settings.klass.selected ) - } - - if ( highlightedObject && highlightedObject.pick == timeMinutes ) { - klasses.push( settings.klass.highlighted ) - } - - if ( viewsetObject && viewsetObject.pick == timeMinutes ) { - klasses.push( settings.klass.viewset ) - } - - if ( disabledCollection && clock.disabled( loopedTime ) ) { - klasses.push( settings.klass.disabled ) - } - - return klasses.join( ' ' ) - })( [ settings.klass.listItem ], loopedTime.pick ), - 'data-pick=' + loopedTime.pick - ] - } - }) + Picker._.node( 'li', Picker._.node( 'button', settings.clear, settings.klass.buttonClear, 'data-clear=1' + ( isOpen ? '' : ' disable' ) ) ), settings.klass.list ) + return _.node( + 'ul', + _.group({ + min: clock.item.min.pick, + max: clock.item.max.pick, + i: clock.item.interval, + node: 'li', + item: function( loopedTime ) { + loopedTime = clock.create( loopedTime ) + var timeMinutes = loopedTime.pick, + isSelected = selectedObject && selectedObject.pick == timeMinutes, + isHighlighted = highlightedObject && highlightedObject.pick == timeMinutes, + isDisabled = disabledCollection && clock.disabled( loopedTime ), + formattedTime = _.trigger( clock.formats.toString, clock, [ settings.format, loopedTime ] ) + return [ + _.trigger( clock.formats.toString, clock, [ _.trigger( settings.formatLabel, clock, [ loopedTime ] ) || settings.format, loopedTime ] ), + (function( klasses ) { + + if ( isSelected ) { + klasses.push( settings.klass.selected ) + } + + if ( isHighlighted ) { + klasses.push( settings.klass.highlighted ) + } + + if ( viewsetObject && viewsetObject.pick == timeMinutes ) { + klasses.push( settings.klass.viewset ) + } + + if ( isDisabled ) { + klasses.push( settings.klass.disabled ) + } + + return klasses.join( ' ' ) + })( [ settings.klass.listItem ] ), + 'data-pick=' + loopedTime.pick + ' ' + _.ariaAttr({ + role: 'option', + label: formattedTime, + selected: isSelected && clock.$node.val() === formattedTime ? true : null, + activedescendant: isHighlighted ? true : null, + disabled: isDisabled ? true : null + }) + ] + } + }) + + + // * For Firefox forms to submit, make sure to set the button’s `type` attribute as “button”. + _.node( + 'li', + _.node( + 'button', + settings.clear, + settings.klass.buttonClear, + 'type=button data-clear=1' + ( isOpen ? '' : ' disabled' ) + ' ' + + _.ariaAttr({ controls: clock.$node[0].id }) + ), + '', _.ariaAttr({ role: 'presentation' }) + ), + settings.klass.list, + _.ariaAttr({ role: 'listbox', controls: clock.$node[0].id }) + ) } //TimePicker.prototype.nodes @@ -597,10 +957,9 @@ TimePicker.prototype.nodes = function( isOpen ) { -/* ========================================================================== - Extend the picker to add the component with the defaults. - ========================================================================== */ - +/** + * Extend the picker to add the component with the defaults. + */ TimePicker.defaults = (function( prefix ) { return { @@ -614,6 +973,10 @@ TimePicker.defaults = (function( prefix ) { // The interval between each time interval: 30, + // Picker close behavior + closeOnSelect: true, + closeOnClear: true, + // Classes klass: { @@ -639,13 +1002,12 @@ TimePicker.defaults = (function( prefix ) { /** - * Extend the picker to add the date picker. + * Extend the picker to add the time picker. */ Picker.extend( 'pickatime', TimePicker ) -// Close the scope. -})(); +})); diff --git a/vendor/assets/stylesheets/pickadate/default.css b/vendor/assets/stylesheets/pickadate/default.css old mode 100755 new mode 100644 index ffa808e0..20cf5cf0 --- a/vendor/assets/stylesheets/pickadate/default.css +++ b/vendor/assets/stylesheets/pickadate/default.css @@ -1,45 +1,8 @@ /* ========================================================================== $BASE-PICKER ========================================================================== */ -/* ========================================================================== - $VARIABLES - ========================================================================== */ -/** - * Base colors - */ -/** - * Backgrounds - */ -/** - * Borders - */ -/** - * Buttons - */ -/** - * Picker base - */ -/** - * Date picker options - */ -/** - * Theme configurations - */ -/* ========================================================================== - $MIXINS - ========================================================================== */ -/** - * Common picker item states - */ /** - * Opacity - */ -/** - * Vendor prefixes - */ -/** - * Note: the root picker element should __NOT__ be styled - * more than what’s here. Style the `.picker__holder` instead. + * Note: the root picker element should *NOT* be styled more than what’s here. */ .picker { font-size: 16px; @@ -48,6 +11,10 @@ color: #000000; position: absolute; z-index: 10000; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; } /** * The picker input element. @@ -72,62 +39,32 @@ /*! * Default mobile-first, responsive styling for pickadate.js - * Demo: http://amsul.github.io/pickadate.js/themes.htm#default + * Demo: http://amsul.github.io/pickadate.js */ -/* ========================================================================== - $VARIABLES - ========================================================================== */ /** - * Base colors - */ -/** - * Backgrounds - */ -/** - * Borders - */ -/** - * Buttons - */ -/** - * Picker base - */ -/** - * Date picker options - */ -/** - * Theme configurations - */ -/* ========================================================================== - $MIXINS - ========================================================================== */ -/** - * Common picker item states - */ -/** - * Opacity - */ -/** - * Vendor prefixes + * Note: the root picker element should *NOT* be styled more than what’s here. */ /** * Make the holder and frame fullscreen. */ .picker__holder, .picker__frame { + top: 0; bottom: 0; left: 0; right: 0; - top: 100%; + -webkit-transform: translateY(100%); + -ms-transform: translateY(100%); + transform: translateY(100%); } /** * The holder should overlay the entire screen. */ .picker__holder { position: fixed; - -webkit-transition: background 0.15s ease-out, top 0s 0.15s; - -moz-transition: background 0.15s ease-out, top 0s 0.15s; - transition: background 0.15s ease-out, top 0s 0.15s; + transition: background 0.15s ease-out, -webkit-transform 0s 0.15s; + transition: background 0.15s ease-out, transform 0s 0.15s; + -webkit-backface-visibility: hidden; } /** * The frame that bounds the box contents of the picker. @@ -142,8 +79,6 @@ filter: alpha(opacity=0); -moz-opacity: 0; opacity: 0; - -webkit-transition: all 0.15s ease-out; - -moz-transition: all 0.15s ease-out; transition: all 0.15s ease-out; } @media (min-height: 33.875em) { @@ -192,11 +127,7 @@ border: 1px solid #777777; border-top-color: #898989; border-bottom-width: 0; - -webkit-border-radius: 5px 5px 0 0; - -moz-border-radius: 5px 5px 0 0; border-radius: 5px 5px 0 0; - -webkit-box-shadow: 0 12px 36px 16px rgba(0, 0, 0, 0.24); - -moz-box-shadow: 0 12px 36px 16px rgba(0, 0, 0, 0.24); box-shadow: 0 12px 36px 16px rgba(0, 0, 0, 0.24); } } @@ -204,8 +135,6 @@ .picker__box { font-size: 1.5em; border-bottom-width: 1px; - -webkit-border-radius: 5px; - -moz-border-radius: 5px; border-radius: 5px; } } @@ -213,17 +142,19 @@ * When the picker opens... */ .picker--opened .picker__holder { - top: 0; + -webkit-transform: translateY(0); + -ms-transform: translateY(0); + transform: translateY(0); background: transparent; -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr=#1E000000,endColorstr=#1E000000)"; zoom: 1; background: rgba(0, 0, 0, 0.32); - -webkit-transition: background 0.15s ease-out; - -moz-transition: background 0.15s ease-out; transition: background 0.15s ease-out; } .picker--opened .picker__frame { - top: 0; + -webkit-transform: translateY(0); + -ms-transform: translateY(0); + transform: translateY(0); -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=100)"; filter: alpha(opacity=100); -moz-opacity: 1; @@ -235,6 +166,3 @@ bottom: 0; } } -/** - * For `large` screens, transform into an inline picker. - */ diff --git a/vendor/assets/stylesheets/pickadate/default.date.css b/vendor/assets/stylesheets/pickadate/default.date.css old mode 100755 new mode 100644 index 7c9ae6ec..0b297577 --- a/vendor/assets/stylesheets/pickadate/default.date.css +++ b/vendor/assets/stylesheets/pickadate/default.date.css @@ -1,42 +1,6 @@ /* ========================================================================== $BASE-DATE-PICKER ========================================================================== */ -/* ========================================================================== - $VARIABLES - ========================================================================== */ -/** - * Base colors - */ -/** - * Backgrounds - */ -/** - * Borders - */ -/** - * Buttons - */ -/** - * Picker base - */ -/** - * Date picker options - */ -/** - * Theme configurations - */ -/* ========================================================================== - $MIXINS - ========================================================================== */ -/** - * Common picker item states - */ -/** - * Opacity - */ -/** - * Vendor prefixes - */ /** * The picker box. */ @@ -71,13 +35,17 @@ */ .picker__select--month, .picker__select--year { - font-size: .8em; border: 1px solid #b7b7b7; - height: 2.5em; - padding: .66em .25em; + height: 2em; + padding: .5em; margin-left: .25em; margin-right: .25em; - margin-top: -0.5em; +} +@media (min-width: 24.5em) { + .picker__select--month, + .picker__select--year { + margin-top: -0.5em; + } } .picker__select--month { width: 35%; @@ -95,18 +63,35 @@ .picker__nav--prev, .picker__nav--next { position: absolute; - top: -0.33em; - padding: .5em 1.33em; + padding: .5em 1.25em; width: 1em; height: 1em; + box-sizing: content-box; + top: -0.25em; +} +@media (min-width: 24.5em) { + .picker__nav--prev, + .picker__nav--next { + top: -0.33em; + } } .picker__nav--prev { left: -1em; - padding-right: 1.5em; + padding-right: 1.25em; +} +@media (min-width: 24.5em) { + .picker__nav--prev { + padding-right: 1.5em; + } } .picker__nav--next { right: -1em; - padding-left: 1.5em; + padding-left: 1.25em; +} +@media (min-width: 24.5em) { + .picker__nav--next { + padding-left: 1.5em; + } } .picker__nav--prev:before, .picker__nav--next:before { @@ -170,7 +155,6 @@ color: #999999; font-weight: 500; /* Increase the spacing a tad */ - } @media (min-height: 33.875em) { .picker__weekday { @@ -186,7 +170,6 @@ border: 1px solid transparent; } .picker__day--today { - color: #0089ec; position: relative; } .picker__day--today:before { @@ -199,22 +182,11 @@ border-top: 0.5em solid #0059bc; border-left: .5em solid transparent; } -.picker__day--selected, -.picker__day--selected:hover { - border-color: #0089ec; -} -.picker__day--highlighted { - background: #b1dcfb; -} .picker__day--disabled:before { border-top-color: #aaaaaa; } .picker__day--outfocus { color: #dddddd; - -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=66)"; - filter: alpha(opacity=66); - -moz-opacity: 0.66; - opacity: 0.66; } .picker__day--infocus:hover, .picker__day--outfocus:hover { @@ -222,13 +194,24 @@ color: #000000; background: #b1dcfb; } +.picker__day--highlighted { + border-color: #0089ec; +} .picker__day--highlighted:hover, .picker--focused .picker__day--highlighted { + cursor: pointer; + color: #000000; + background: #b1dcfb; +} +.picker__day--selected, +.picker__day--selected:hover, +.picker--focused .picker__day--selected { background: #0089ec; color: #ffffff; } .picker__day--disabled, -.picker__day--disabled:hover { +.picker__day--disabled:hover, +.picker--focused .picker__day--disabled { background: #f5f5f5; border-color: #f5f5f5; color: #dddddd; @@ -239,94 +222,80 @@ background: #bbbbbb; } /** - * The footer containing the "today" and "clear" buttons. + * The footer containing the "today", "clear", and "close" buttons. */ .picker__footer { text-align: center; } .picker__button--today, -.picker__button--clear { +.picker__button--clear, +.picker__button--close { border: 1px solid #ffffff; background: #ffffff; font-size: .8em; padding: .66em 0; font-weight: bold; - width: 50%; + width: 33%; display: inline-block; vertical-align: bottom; } .picker__button--today:hover, -.picker__button--clear:hover { +.picker__button--clear:hover, +.picker__button--close:hover { cursor: pointer; color: #000000; background: #b1dcfb; border-bottom-color: #b1dcfb; } .picker__button--today:focus, -.picker__button--clear:focus { +.picker__button--clear:focus, +.picker__button--close:focus { background: #b1dcfb; border-color: #0089ec; outline: none; } .picker__button--today:before, -.picker__button--clear:before { +.picker__button--clear:before, +.picker__button--close:before { position: relative; display: inline-block; height: 0; } -.picker__button--today:before { +.picker__button--today:before, +.picker__button--clear:before { content: " "; margin-right: .45em; +} +.picker__button--today:before { top: -0.05em; width: 0; border-top: 0.66em solid #0059bc; border-left: .66em solid transparent; } .picker__button--clear:before { + top: -0.25em; + width: .66em; + border-top: 3px solid #ee2200; +} +.picker__button--close:before { content: "\D7"; - margin-right: .35em; top: -0.1em; - color: #ee2200; vertical-align: top; font-size: 1.1em; + margin-right: .35em; + color: #777777; +} +.picker__button--today[disabled], +.picker__button--today[disabled]:hover { + background: #f5f5f5; + border-color: #f5f5f5; + color: #dddddd; + cursor: default; +} +.picker__button--today[disabled]:before { + border-top-color: #aaaaaa; } /* ========================================================================== $DEFAULT-DATE-PICKER ========================================================================== */ -/* ========================================================================== - $VARIABLES - ========================================================================== */ -/** - * Base colors - */ -/** - * Backgrounds - */ -/** - * Borders - */ -/** - * Buttons - */ -/** - * Picker base - */ -/** - * Date picker options - */ -/** - * Theme configurations - */ -/* ========================================================================== - $MIXINS - ========================================================================== */ -/** - * Common picker item states - */ -/** - * Opacity - */ -/** - * Vendor prefixes - */ diff --git a/vendor/assets/stylesheets/pickadate/default.time.css b/vendor/assets/stylesheets/pickadate/default.time.css old mode 100755 new mode 100644 index 3ac41fe0..624a7549 --- a/vendor/assets/stylesheets/pickadate/default.time.css +++ b/vendor/assets/stylesheets/pickadate/default.time.css @@ -1,42 +1,6 @@ /* ========================================================================== $BASE-TIME-PICKER ========================================================================== */ -/* ========================================================================== - $VARIABLES - ========================================================================== */ -/** - * Base colors - */ -/** - * Backgrounds - */ -/** - * Borders - */ -/** - * Buttons - */ -/** - * Picker base - */ -/** - * Date picker options - */ -/** - * Theme configurations - */ -/* ========================================================================== - $MIXINS - ========================================================================== */ -/** - * Common picker item states - */ -/** - * Opacity - */ -/** - * Vendor prefixes - */ /** * The list of times. */ @@ -69,25 +33,29 @@ border-color: #0089ec; z-index: 10; } -/* Selected time */ -.picker__list-item--selected, -.picker__list-item--selected:hover { +/* Highlighted and hovered/focused time */ +.picker__list-item--highlighted { border-color: #0089ec; z-index: 10; } -/* Highlighted time */ -.picker__list-item--highlighted { - background: #b1dcfb; -} -/* Highlighted and hovered/focused time */ .picker__list-item--highlighted:hover, .picker--focused .picker__list-item--highlighted { + cursor: pointer; + color: #000000; + background: #b1dcfb; +} +/* Selected and hovered/focused time */ +.picker__list-item--selected, +.picker__list-item--selected:hover, +.picker--focused .picker__list-item--selected { background: #0089ec; color: #ffffff; + z-index: 10; } /* Disabled time */ .picker__list-item--disabled, -.picker__list-item--disabled:hover { +.picker__list-item--disabled:hover, +.picker--focused .picker__list-item--disabled { background: #f5f5f5; border-color: #f5f5f5; color: #dddddd; @@ -130,47 +98,12 @@ .picker--time .picker__button--clear:hover:before, .picker--time .picker__button--clear:focus:before { color: #ffffff; + border-color: #ffffff; } /* ========================================================================== $DEFAULT-TIME-PICKER ========================================================================== */ -/* ========================================================================== - $VARIABLES - ========================================================================== */ -/** - * Base colors - */ -/** - * Backgrounds - */ -/** - * Borders - */ -/** - * Buttons - */ -/** - * Picker base - */ -/** - * Date picker options - */ -/** - * Theme configurations - */ -/* ========================================================================== - $MIXINS - ========================================================================== */ -/** - * Common picker item states - */ -/** - * Opacity - */ -/** - * Vendor prefixes - */ /** * The frame the bounds the time picker. */ From 0d61714014498f370b471e4b1d67e7341edd7731 Mon Sep 17 00:00:00 2001 From: Daniel Carral Date: Tue, 18 Aug 2015 21:38:17 +0200 Subject: [PATCH 456/499] Extract pickadate's style overrides to its own partial --- app/assets/stylesheets/_base.sass | 57 ------------------- .../stylesheets/_pickadate_overrides.sass | 54 ++++++++++++++++++ app/assets/stylesheets/application.css.scss | 1 + 3 files changed, 55 insertions(+), 57 deletions(-) create mode 100644 app/assets/stylesheets/_pickadate_overrides.sass diff --git a/app/assets/stylesheets/_base.sass b/app/assets/stylesheets/_base.sass index 5029aa86..a671ab8d 100644 --- a/app/assets/stylesheets/_base.sass +++ b/app/assets/stylesheets/_base.sass @@ -67,60 +67,3 @@ article &.divider +custom-sans(bold) margin: 0 0.5em - -// TODO: extract -// Pickadate overrides -.picker - z-index: 1001 !important - -.picker__input.picker__input--active - border: 1px solid $blue !important - -.picker__box - border-radius: 0 !important - -.picker__nav--next, -.picker__nav--prev - display: none !important - -.picker__day--highlighted:hover, -.picker__day--infocus:hover, -.picker__day--outfocus:hover - color: $white !important - background: $blue !important - -.picker--focused .picker__day--highlighted - background: $green !important - color: $white !important - border: none !important - -.picker__day--disabled:hover, -.picker--focused .picker__day--disabled - border: none !important - -.picker__day--disabled:hover, -.picker--focused .picker__day--disabled, - background: $white !important - color: $light-gray !important - font-weight: 100 !important - -.picker__day - color: $black !important - font-weight: 500 !important - background: $extra-light-gray !important - -.picker__list-item - border-color: $light-gray !important - -.picker__list-item:hover - background: $blue !important - border-color: $blue !important - color: $white !important - -.picker__list-item--selected, -.picker__list-item--selected:hover - color: $white !important - background: $green !important - -.picker__list - padding-bottom: 0 !important diff --git a/app/assets/stylesheets/_pickadate_overrides.sass b/app/assets/stylesheets/_pickadate_overrides.sass new file mode 100644 index 00000000..150bbb76 --- /dev/null +++ b/app/assets/stylesheets/_pickadate_overrides.sass @@ -0,0 +1,54 @@ +.picker + z-index: 1001 !important + +.picker__input.picker__input--active + border: 1px solid $blue !important + +.picker__box + border-radius: 0 !important + +.picker__nav--next, +.picker__nav--prev + display: none !important + +.picker__day--highlighted:hover, +.picker__day--infocus:hover, +.picker__day--outfocus:hover + color: $white !important + background: $blue !important + +.picker--focused .picker__day--highlighted + background: $green !important + color: $white !important + border: none !important + +.picker__day--disabled:hover, +.picker--focused .picker__day--disabled + border: none !important + +.picker__day--disabled:hover, +.picker--focused .picker__day--disabled, + background: $white !important + color: $light-gray !important + font-weight: 100 !important + +.picker__day + color: $black !important + font-weight: 500 !important + background: $extra-light-gray !important + +.picker__list-item + border-color: $light-gray !important + +.picker__list-item:hover + background: $blue !important + border-color: $blue !important + color: $white !important + +.picker__list-item--selected, +.picker__list-item--selected:hover + color: $white !important + background: $green !important + +.picker__list + padding-bottom: 0 !important diff --git a/app/assets/stylesheets/application.css.scss b/app/assets/stylesheets/application.css.scss index 2620960f..d318af9a 100644 --- a/app/assets/stylesheets/application.css.scss +++ b/app/assets/stylesheets/application.css.scss @@ -4,6 +4,7 @@ @import "neat"; @import "utils"; @import "base"; +@import "pickadate_overrides"; @import "pickadate/default"; @import "pickadate/default.date"; From 885d644c3df4f0a035e4ef6cb102b6f67d71bf19 Mon Sep 17 00:00:00 2001 From: Daniel Carral Date: Wed, 19 Aug 2015 01:11:11 +0200 Subject: [PATCH 457/499] Import bourbon before settings, thus allowing visual-grid's usage --- app/assets/stylesheets/application.css.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/stylesheets/application.css.scss b/app/assets/stylesheets/application.css.scss index 2620960f..ddc8733f 100644 --- a/app/assets/stylesheets/application.css.scss +++ b/app/assets/stylesheets/application.css.scss @@ -1,6 +1,6 @@ @import "normalize"; -@import "settings"; @import "bourbon"; +@import "settings"; @import "neat"; @import "utils"; @import "base"; From 9915dff946ae8d2bff8c90d9aabb11e9f2ae467f Mon Sep 17 00:00:00 2001 From: Daniel Carral Date: Wed, 19 Aug 2015 01:12:06 +0200 Subject: [PATCH 458/499] More appealing Bourbon's visual-grid (dev. mode) --- app/assets/stylesheets/_settings.sass | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/assets/stylesheets/_settings.sass b/app/assets/stylesheets/_settings.sass index 311708e2..9870485b 100644 --- a/app/assets/stylesheets/_settings.sass +++ b/app/assets/stylesheets/_settings.sass @@ -2,6 +2,9 @@ // DEV $visual-grid: false +$visual-grid-opacity: 0.2 +$visual-grid-index: front +$visual-grid-color: blue // grid set-up $column: 225px From 8c9aa1827fa2172fc02afc74e169b2fc5d16ab45 Mon Sep 17 00:00:00 2001 From: Daniel Carral Date: Wed, 19 Aug 2015 01:12:50 +0200 Subject: [PATCH 459/499] Delete unneeded media queries in activities listing --- app/assets/stylesheets/partials/activities/_index.sass | 9 --------- 1 file changed, 9 deletions(-) diff --git a/app/assets/stylesheets/partials/activities/_index.sass b/app/assets/stylesheets/partials/activities/_index.sass index f1e8c0bb..d7197e9a 100644 --- a/app/assets/stylesheets/partials/activities/_index.sass +++ b/app/assets/stylesheets/partials/activities/_index.sass @@ -216,15 +216,6 @@ a#new-activity +transition(all 0.15s ease-out 0.15s) - +media($smartphone-portrait) - li - +span-columns(2) - - +media($tablet-lite) li +span-columns(2) - - +media($tablet-portrait-plus) - li - +span-columns(2) From 107111960d5855aca75e03aaae877c550f973035 Mon Sep 17 00:00:00 2001 From: Daniel Carral Date: Wed, 19 Aug 2015 01:40:05 +0200 Subject: [PATCH 460/499] Tweak responsive design in activities' form by forcing location input to take the whole grid's with in smaller devices --- .../stylesheets/partials/activities/_form.sass | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/app/assets/stylesheets/partials/activities/_form.sass b/app/assets/stylesheets/partials/activities/_form.sass index 3eed2741..0c9bd822 100644 --- a/app/assets/stylesheets/partials/activities/_form.sass +++ b/app/assets/stylesheets/partials/activities/_form.sass @@ -54,15 +54,19 @@ form#new-activity label &.location display: inline-block - width: 80% + +span-columns(6) + +media($tablet-lite) + +span-columns(3) &.limit-of-participants display: inline-block text-align: right - width: 20% + +span-columns(6) - input - width: 85% + +media($tablet-lite) + +span-columns(1) + input + width: 85% &.image-url .preview From e989013532b7cfdc9bb3ab39e0f3d72f4820ad0c Mon Sep 17 00:00:00 2001 From: Daniel Carral Date: Wed, 19 Aug 2015 01:56:12 +0200 Subject: [PATCH 461/499] Avoid using fixed with in user forms, thus improving appearance in smaller devices --- app/assets/stylesheets/partials/_user.sass | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/stylesheets/partials/_user.sass b/app/assets/stylesheets/partials/_user.sass index 156dc714..61e80b89 100644 --- a/app/assets/stylesheets/partials/_user.sass +++ b/app/assets/stylesheets/partials/_user.sass @@ -32,7 +32,7 @@ body.user font-size: 1.5em section.form - width: 640px + +span-columns(6) padding: 70px margin: 0 auto background: $white From 79320345c872ff70293eb2144e5bbe1879cdd194 Mon Sep 17 00:00:00 2001 From: Daniel Carral Date: Wed, 19 Aug 2015 01:58:24 +0200 Subject: [PATCH 462/499] Tweak user forms' margin, improving their appearance in smaller devices --- app/assets/stylesheets/partials/_user.sass | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/assets/stylesheets/partials/_user.sass b/app/assets/stylesheets/partials/_user.sass index 61e80b89..d21581a8 100644 --- a/app/assets/stylesheets/partials/_user.sass +++ b/app/assets/stylesheets/partials/_user.sass @@ -33,7 +33,9 @@ body.user section.form +span-columns(6) - padding: 70px + padding: 10px + +media($smartphone-landscape) + padding: 70px margin: 0 auto background: $white box-shadow: 0 0 7px transparentize($black, 0.9) From 12472e4144954fbc9fcec2a37e53bca318c43269 Mon Sep 17 00:00:00 2001 From: Daniel Carral Date: Wed, 19 Aug 2015 02:24:31 +0200 Subject: [PATCH 463/499] Add client-side validation to the 'limit_of_participants' numerical input, improving UX (#167) --- app/views/activities/_form.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/activities/_form.html.haml b/app/views/activities/_form.html.haml index 85f5e41d..313735ff 100644 --- a/app/views/activities/_form.html.haml +++ b/app/views/activities/_form.html.haml @@ -18,7 +18,7 @@ %a#show-map= t("activity_form.location.show-map") %label.limit-of-participants> - = f.number_field :limit_of_participants, placeholder: "5" + = f.number_field :limit_of_participants, placeholder: 5, min: 1 %small = t("activity_form.limit_of_participants.hint") From 0dd8a4ec6fde01e01c6775be3988fbdfe0b7b3ac Mon Sep 17 00:00:00 2001 From: Daniel Carral Date: Thu, 20 Aug 2015 20:36:17 +0200 Subject: [PATCH 464/499] Change 'limit_of_participants' input placeholder to 10 The default value of limit_of_participants' attribute is 10, so it makes sense to use the same value here. --- app/views/activities/_form.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/activities/_form.html.haml b/app/views/activities/_form.html.haml index 313735ff..ece864da 100644 --- a/app/views/activities/_form.html.haml +++ b/app/views/activities/_form.html.haml @@ -18,7 +18,7 @@ %a#show-map= t("activity_form.location.show-map") %label.limit-of-participants> - = f.number_field :limit_of_participants, placeholder: 5, min: 1 + = f.number_field :limit_of_participants, placeholder: 10, min: 1 %small = t("activity_form.limit_of_participants.hint") From 29193d79b49732758db20c1ddb9519d859fc1814 Mon Sep 17 00:00:00 2001 From: Daniel Carral Date: Thu, 20 Aug 2015 21:03:29 +0200 Subject: [PATCH 465/499] Allow months navigation in datepicker Fixes https://github.com/eurucamp/eurucamp-activities/issues/166 --- app/assets/stylesheets/_pickadate_overrides.sass | 4 ---- 1 file changed, 4 deletions(-) diff --git a/app/assets/stylesheets/_pickadate_overrides.sass b/app/assets/stylesheets/_pickadate_overrides.sass index 150bbb76..d1f75969 100644 --- a/app/assets/stylesheets/_pickadate_overrides.sass +++ b/app/assets/stylesheets/_pickadate_overrides.sass @@ -7,10 +7,6 @@ .picker__box border-radius: 0 !important -.picker__nav--next, -.picker__nav--prev - display: none !important - .picker__day--highlighted:hover, .picker__day--infocus:hover, .picker__day--outfocus:hover From d598aad65be6334a12999bd3b31b7de31737cbb1 Mon Sep 17 00:00:00 2001 From: Daniel Carral Date: Sun, 23 Aug 2015 13:22:35 +0200 Subject: [PATCH 466/499] Hide days without activities when searching @ client-side Mimics the server-side search behavior: days without activities aren't displayed at all. --- app/assets/javascripts/initializers.coffee | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/app/assets/javascripts/initializers.coffee b/app/assets/javascripts/initializers.coffee index c7431e56..cd0148ba 100644 --- a/app/assets/javascripts/initializers.coffee +++ b/app/assets/javascripts/initializers.coffee @@ -88,6 +88,7 @@ ready = -> $filters = $('form.filters label:not(.search)') $search = $('form.filters label.search input') $activities = $('#activities li') + $days = $('.activity-day') $filters.attr('unselectable', 'on') .css('user-select', 'none') @@ -95,6 +96,7 @@ ready = -> filterActivities = -> $activities.show() + $days.show() # the filter tabs values = $filters.filter('.selected') .find('input[type=radio]') @@ -107,6 +109,12 @@ ready = -> .filter(':visible') .filter(-> !(new RegExp(query, 'i')).test $(@).find('a.details').attr('title')) .hide() + hideEmptyDays() + + hideEmptyDays = -> + $days + .filter(-> $(@).find('li:visible').length == 0) + .hide() $filters.on 'click', (e)-> e.preventDefault() From dceac629bf046f42592960832109578681edeadd Mon Sep 17 00:00:00 2001 From: Alex Coles Date: Sun, 23 Aug 2015 19:27:38 +0200 Subject: [PATCH 467/499] Revert name change of Activities app itself * rubyweek.org was/is an instance of the application. * Activities works better as a generic name for the app itself. --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index b33303a0..91ff4816 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -# rubyweek [![License](http://img.shields.io/:license-AGPL-0030c8.svg)](COPYRIGHT) [![Build Status](https://travis-ci.org/eurucamp/eurucamp-activities.png?branch=master)](https://travis-ci.org/eurucamp/eurucamp-activities) +# eurucamp Activities [![License](http://img.shields.io/:license-AGPL-0030c8.svg)](COPYRIGHT) [![Build Status](https://travis-ci.org/eurucamp/eurucamp-activities.png?branch=master)](https://travis-ci.org/eurucamp/eurucamp-activities) -The rubyweek app is a small application to allow attendees to organize and plan small event in and around a conference. Users create events or sign up to created ones. Signup works through GitHub or Twitter. +The Activities app is a small application to allow attendees to organize and plan small event in and around a conference. Users create events or sign up to created ones. Signup works through GitHub or Twitter. The app is mobile friendly and easy to run on a free Heroku account. From 10937c0c1fe8ad21acf3cc0203953ee669e4802c Mon Sep 17 00:00:00 2001 From: Daniel Carral Date: Sun, 23 Aug 2015 23:39:42 +0200 Subject: [PATCH 468/499] Show 3 activities per row over tablet-portrait-plus size The relevant media query was wrongly deleted @ commit 8c9aa1827fa2172fc02afc74e169b2fc5d16ab45. This made that just 2 activities per row were being displayed over tablet-portrait-plus size. --- app/assets/stylesheets/partials/activities/_index.sass | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/assets/stylesheets/partials/activities/_index.sass b/app/assets/stylesheets/partials/activities/_index.sass index d7197e9a..ef7d8f6e 100644 --- a/app/assets/stylesheets/partials/activities/_index.sass +++ b/app/assets/stylesheets/partials/activities/_index.sass @@ -219,3 +219,7 @@ a#new-activity +media($tablet-lite) li +span-columns(2) + + +media($tablet-portrait-plus) + li + +span-columns(2) From f88e92adff1ac7d27e239b8018cb83a1688f2aaf Mon Sep 17 00:00:00 2001 From: Andrew Hobden Date: Thu, 25 Aug 2016 20:53:22 +0200 Subject: [PATCH 469/499] Bump Gemfile.lock --- Gemfile.lock | 203 +++++++++++++++++++++++++++------------------------ 1 file changed, 109 insertions(+), 94 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index f562be6d..11731b44 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -21,40 +21,45 @@ GEM activerecord-deprecated_finders (~> 1.0.2) activesupport (= 4.0.8) arel (~> 4.0.0) - activerecord-deprecated_finders (1.0.3) + activerecord-deprecated_finders (1.0.4) activesupport (4.0.8) i18n (~> 0.6, >= 0.6.9) minitest (~> 4.2) multi_json (~> 1.3) thread_safe (~> 0.1) tzinfo (~> 0.3.37) + addressable (2.4.0) arel (4.0.2) - bcrypt-ruby (3.1.2) - better_errors (1.1.0) + bcrypt (3.1.11) + bcrypt-ruby (3.1.5) + bcrypt (>= 3.1.3) + better_errors (2.1.1) coderay (>= 1.0.0) erubis (>= 2.6.6) + rack (>= 0.9.0) binding_of_caller (0.7.2) debug_inspector (>= 0.0.1) - bourbon (3.1.8) - sass (>= 3.2.0) + bourbon (3.2.4) + sass (~> 3.2) thor builder (3.1.4) cancan (1.6.10) - capybara (2.4.4) + capybara (2.8.1) + addressable mime-types (>= 1.16) nokogiri (>= 1.3.3) rack (>= 1.0.0) rack-test (>= 0.5.4) xpath (~> 2.0) - coderay (1.1.0) + coderay (1.1.1) coffee-rails (4.0.1) coffee-script (>= 2.2.0) railties (>= 4.0.0, < 5.0) - coffee-script (2.2.0) + coffee-script (2.4.1) coffee-script-source execjs - coffee-script-source (1.7.0) - daemons (1.1.9) + coffee-script-source (1.10.0) + daemons (1.2.4) debug_inspector (0.0.2) devise (3.0.4) bcrypt-ruby (~> 3.0) @@ -62,70 +67,77 @@ GEM railties (>= 3.2.6, < 5) warden (~> 1.2.3) diff-lcs (1.2.5) - docile (1.1.3) - dotenv (0.9.0) - dotenv-rails (0.9.0) - dotenv (= 0.9.0) + docile (1.1.5) + dotenv (2.1.1) + dotenv-rails (2.1.1) + dotenv (= 2.1.1) + railties (>= 4.0, < 5.1) draper (2.1.0) actionpack (>= 3.0) activemodel (>= 3.0) activesupport (>= 3.0) request_store (~> 1.0) erubis (2.7.0) - eventmachine (1.0.3) + eventmachine (1.2.0.1) exception_notification (4.0.1) actionmailer (>= 3.0.4) activesupport (>= 3.0.4) - execjs (2.0.2) - factory_girl (4.3.0) + execjs (2.7.0) + factory_girl (4.7.0) activesupport (>= 3.0.0) - factory_girl_rails (4.3.0) - factory_girl (~> 4.3.0) + factory_girl_rails (4.7.0) + factory_girl (~> 4.7.0) railties (>= 3.0.0) - faraday (0.9.0) + faraday (0.9.2) multipart-post (>= 1.2, < 3) - foreman (0.63.0) - dotenv (>= 0.7) - thor (>= 0.13.6) - haml (4.0.5) + foreman (0.82.0) + thor (~> 0.19.1) + haml (4.0.7) tilt - haml-rails (0.5.3) + haml-rails (0.9.0) actionpack (>= 4.0.1) activesupport (>= 4.0.1) - haml (>= 3.1, < 5.0) + haml (>= 4.0.6, < 5.0) + html2haml (>= 1.0.1) railties (>= 4.0.1) - hashie (2.0.5) + hashie (2.1.2) hike (1.2.3) + html2haml (2.0.0) + erubis (~> 2.7.0) + haml (~> 4.0.0) + nokogiri (~> 1.6.0) + ruby_parser (~> 3.5) httpauth (0.2.1) i18n (0.7.0) jbuilder (1.0.2) activesupport (>= 3.0.0) - jquery-rails (3.1.0) + jquery-rails (3.1.4) railties (>= 3.0, < 5.0) thor (>= 0.14, < 2.0) - json (1.8.2) + json (2.0.2) json_spec (1.1.4) multi_json (~> 1.0) rspec (>= 2.0, < 4.0) - jwt (0.1.11) + jwt (0.1.13) multi_json (>= 1.5) - kgio (2.9.2) + kgio (2.10.0) mail (2.5.4) mime-types (~> 1.16) treetop (~> 1.4.8) mime-types (1.25.1) - mini_portile (0.6.2) + mini_portile2 (2.1.0) minitest (4.7.5) modernizr-rails (2.7.1) - multi_json (1.11.2) + multi_json (1.12.1) multipart-post (2.0.0) - neat (1.5.0) - bourbon (>= 2.1) - sass (>= 3.2) - newrelic_rpm (3.7.2.192) - nokogiri (1.6.6.2) - mini_portile (~> 0.6.0) - oauth (0.4.7) + neat (1.5.1) + bourbon (>= 3.1) + sass (~> 3.2.19) + newrelic_rpm (3.16.2.321) + nokogiri (1.6.8) + mini_portile2 (~> 2.1.0) + pkg-config (~> 1.1.7) + oauth (0.5.1) oauth2 (0.8.1) faraday (~> 0.8) httpauth (~> 0.1) @@ -135,10 +147,10 @@ GEM omniauth (1.1.4) hashie (>= 1.2, < 3) rack - omniauth-github (1.1.1) + omniauth-github (1.1.2) omniauth (~> 1.0) omniauth-oauth2 (~> 1.1) - omniauth-oauth (1.0.1) + omniauth-oauth (1.1.0) oauth omniauth (~> 1.0) omniauth-oauth2 (1.1.1) @@ -148,7 +160,8 @@ GEM multi_json (~> 1.3) omniauth-oauth (~> 1.0) orm_adapter (0.5.0) - pg (0.17.1) + pg (0.18.4) + pkg-config (1.1.7) polyglot (0.3.5) rack (1.5.5) rack-robotz (0.0.4) @@ -170,86 +183,88 @@ GEM activesupport (= 4.0.8) rake (>= 0.8.7) thor (>= 0.18.1, < 2.0) - raindrops (0.13.0) - rake (10.3.2) - redcarpet (3.0.0) - request_store (1.2.0) - rspec (3.3.0) - rspec-core (~> 3.3.0) - rspec-expectations (~> 3.3.0) - rspec-mocks (~> 3.3.0) - rspec-activemodel-mocks (1.0.1) + raindrops (0.17.0) + rake (11.2.2) + redcarpet (3.3.4) + request_store (1.3.1) + rspec (3.5.0) + rspec-core (~> 3.5.0) + rspec-expectations (~> 3.5.0) + rspec-mocks (~> 3.5.0) + rspec-activemodel-mocks (1.0.3) activemodel (>= 3.0) activesupport (>= 3.0) rspec-mocks (>= 2.99, < 4.0) - rspec-core (3.3.1) - rspec-support (~> 3.3.0) - rspec-expectations (3.3.0) + rspec-core (3.5.2) + rspec-support (~> 3.5.0) + rspec-expectations (3.5.0) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.3.0) + rspec-support (~> 3.5.0) rspec-its (1.2.0) rspec-core (>= 3.0.0) rspec-expectations (>= 3.0.0) - rspec-mocks (3.3.0) + rspec-mocks (3.5.0) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.3.0) - rspec-rails (3.3.1) - actionpack (>= 3.0, < 4.3) - activesupport (>= 3.0, < 4.3) - railties (>= 3.0, < 4.3) - rspec-core (~> 3.3.0) - rspec-expectations (~> 3.3.0) - rspec-mocks (~> 3.3.0) - rspec-support (~> 3.3.0) - rspec-support (3.3.0) - sass (3.2.14) - sass-rails (4.0.3) + rspec-support (~> 3.5.0) + rspec-rails (3.5.1) + actionpack (>= 3.0) + activesupport (>= 3.0) + railties (>= 3.0) + rspec-core (~> 3.5.0) + rspec-expectations (~> 3.5.0) + rspec-mocks (~> 3.5.0) + rspec-support (~> 3.5.0) + rspec-support (3.5.0) + ruby_parser (3.8.2) + sexp_processor (~> 4.1) + sass (3.2.19) + sass-rails (4.0.5) railties (>= 4.0.0, < 5.0) - sass (~> 3.2.0) - sprockets (~> 2.8, <= 2.11.0) + sass (~> 3.2.2) + sprockets (~> 2.8, < 3.0) sprockets-rails (~> 2.0) settingslogic (2.0.9) - shelly-dependencies (0.2.4) + sexp_processor (4.7.0) + shelly-dependencies (0.2.5) rake thin - simple_form (3.0.1) - actionpack (>= 4.0.0, < 4.1) - activemodel (>= 4.0.0, < 4.1) - simplecov (0.8.2) + simple_form (3.0.4) + actionpack (~> 4.0) + activemodel (~> 4.0) + simplecov (0.12.0) docile (~> 1.1.0) - multi_json - simplecov-html (~> 0.8.0) - simplecov-html (0.8.0) - sprockets (2.11.0) + json (>= 1.8, < 3) + simplecov-html (~> 0.10.0) + simplecov-html (0.10.0) + sprockets (2.12.4) hike (~> 1.2) multi_json (~> 1.0) rack (~> 1.0) tilt (~> 1.1, != 1.3.0) - sprockets-rails (2.1.3) + sprockets-rails (2.1.4) actionpack (>= 3.0) activesupport (>= 3.0) sprockets (~> 2.8) - thin (1.6.3) + thin (1.7.0) daemons (~> 1.0, >= 1.0.9) - eventmachine (~> 1.0) - rack (~> 1.0) + eventmachine (~> 1.0, >= 1.0.4) + rack (>= 1, < 3) thor (0.19.1) thread_safe (0.3.5) tilt (1.4.1) treetop (1.4.15) polyglot polyglot (>= 0.3.1) - turbolinks (2.2.1) - coffee-rails - tzinfo (0.3.44) - uglifier (2.4.0) - execjs (>= 0.3.0) - json (>= 1.8.0) - unicorn (4.8.2) + turbolinks (5.0.1) + turbolinks-source (~> 5) + turbolinks-source (5.0.0) + tzinfo (0.3.51) + uglifier (3.0.2) + execjs (>= 0.3.0, < 3) + unicorn (5.1.0) kgio (~> 2.6) - rack raindrops (~> 0.7) - warden (1.2.3) + warden (1.2.6) rack (>= 1.0) xpath (2.0.0) nokogiri (~> 1.3) @@ -300,4 +315,4 @@ DEPENDENCIES unicorn BUNDLED WITH - 1.10.5 + 1.11.2 From 4e88967ab587f329b62dcbe309351d9539b068a7 Mon Sep 17 00:00:00 2001 From: Alex Coles Date: Sat, 11 Mar 2017 21:51:40 +0000 Subject: [PATCH 470/499] Bump Rails to 4.2.8 --- Gemfile | 2 +- Gemfile.lock | 127 ++++++++++++++++++++++++++++++--------------------- 2 files changed, 76 insertions(+), 53 deletions(-) diff --git a/Gemfile b/Gemfile index d8c6f539..222be48a 100644 --- a/Gemfile +++ b/Gemfile @@ -1,6 +1,6 @@ source 'https://rubygems.org' -gem 'rails', '4.0.8' +gem 'rails', '4.2.8' gem 'pg' gem 'unicorn' gem 'settingslogic' diff --git a/Gemfile.lock b/Gemfile.lock index 11731b44..3bf6ca7e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -4,32 +4,42 @@ GEM accept_values_for (0.7.2) activemodel (>= 3, < 5) rspec (>= 2.0, < 4.0) - actionmailer (4.0.8) - actionpack (= 4.0.8) - mail (~> 2.5.4) - actionpack (4.0.8) - activesupport (= 4.0.8) - builder (~> 3.1.0) - erubis (~> 2.7.0) - rack (~> 1.5.2) + actionmailer (4.2.8) + actionpack (= 4.2.8) + actionview (= 4.2.8) + activejob (= 4.2.8) + mail (~> 2.5, >= 2.5.4) + rails-dom-testing (~> 1.0, >= 1.0.5) + actionpack (4.2.8) + actionview (= 4.2.8) + activesupport (= 4.2.8) + rack (~> 1.6) rack-test (~> 0.6.2) - activemodel (4.0.8) - activesupport (= 4.0.8) - builder (~> 3.1.0) - activerecord (4.0.8) - activemodel (= 4.0.8) - activerecord-deprecated_finders (~> 1.0.2) - activesupport (= 4.0.8) - arel (~> 4.0.0) - activerecord-deprecated_finders (1.0.4) - activesupport (4.0.8) - i18n (~> 0.6, >= 0.6.9) - minitest (~> 4.2) - multi_json (~> 1.3) - thread_safe (~> 0.1) - tzinfo (~> 0.3.37) + rails-dom-testing (~> 1.0, >= 1.0.5) + rails-html-sanitizer (~> 1.0, >= 1.0.2) + actionview (4.2.8) + activesupport (= 4.2.8) + builder (~> 3.1) + erubis (~> 2.7.0) + rails-dom-testing (~> 1.0, >= 1.0.5) + rails-html-sanitizer (~> 1.0, >= 1.0.3) + activejob (4.2.8) + activesupport (= 4.2.8) + globalid (>= 0.3.0) + activemodel (4.2.8) + activesupport (= 4.2.8) + builder (~> 3.1) + activerecord (4.2.8) + activemodel (= 4.2.8) + activesupport (= 4.2.8) + arel (~> 6.0) + activesupport (4.2.8) + i18n (~> 0.7) + minitest (~> 5.1) + thread_safe (~> 0.3, >= 0.3.4) + tzinfo (~> 1.1) addressable (2.4.0) - arel (4.0.2) + arel (6.0.4) bcrypt (3.1.11) bcrypt-ruby (3.1.5) bcrypt (>= 3.1.3) @@ -42,7 +52,7 @@ GEM bourbon (3.2.4) sass (~> 3.2) thor - builder (3.1.4) + builder (3.2.3) cancan (1.6.10) capybara (2.8.1) addressable @@ -92,6 +102,8 @@ GEM multipart-post (>= 1.2, < 3) foreman (0.82.0) thor (~> 0.19.1) + globalid (0.3.7) + activesupport (>= 4.1.0) haml (4.0.7) tilt haml-rails (0.9.0) @@ -108,7 +120,7 @@ GEM nokogiri (~> 1.6.0) ruby_parser (~> 3.5) httpauth (0.2.1) - i18n (0.7.0) + i18n (0.8.1) jbuilder (1.0.2) activesupport (>= 3.0.0) jquery-rails (3.1.4) @@ -121,12 +133,15 @@ GEM jwt (0.1.13) multi_json (>= 1.5) kgio (2.10.0) - mail (2.5.4) - mime-types (~> 1.16) - treetop (~> 1.4.8) - mime-types (1.25.1) + loofah (2.0.3) + nokogiri (>= 1.5.9) + mail (2.6.4) + mime-types (>= 1.16, < 4) + mime-types (3.1) + mime-types-data (~> 3.2015) + mime-types-data (3.2016.0521) mini_portile2 (2.1.0) - minitest (4.7.5) + minitest (5.10.1) modernizr-rails (2.7.1) multi_json (1.12.1) multipart-post (2.0.0) @@ -162,29 +177,39 @@ GEM orm_adapter (0.5.0) pg (0.18.4) pkg-config (1.1.7) - polyglot (0.3.5) - rack (1.5.5) + rack (1.6.5) rack-robotz (0.0.4) rack rack-test (0.6.3) rack (>= 1.0) - rails (4.0.8) - actionmailer (= 4.0.8) - actionpack (= 4.0.8) - activerecord (= 4.0.8) - activesupport (= 4.0.8) + rails (4.2.8) + actionmailer (= 4.2.8) + actionpack (= 4.2.8) + actionview (= 4.2.8) + activejob (= 4.2.8) + activemodel (= 4.2.8) + activerecord (= 4.2.8) + activesupport (= 4.2.8) bundler (>= 1.3.0, < 2.0) - railties (= 4.0.8) - sprockets-rails (~> 2.0) + railties (= 4.2.8) + sprockets-rails + rails-deprecated_sanitizer (1.0.3) + activesupport (>= 4.2.0.alpha) + rails-dom-testing (1.0.8) + activesupport (>= 4.2.0.beta, < 5.0) + nokogiri (~> 1.6) + rails-deprecated_sanitizer (>= 1.0.1) + rails-html-sanitizer (1.0.3) + loofah (~> 2.0) rails_html_helpers (0.1.1) railties (>= 3.2) - railties (4.0.8) - actionpack (= 4.0.8) - activesupport (= 4.0.8) + railties (4.2.8) + actionpack (= 4.2.8) + activesupport (= 4.2.8) rake (>= 0.8.7) thor (>= 0.18.1, < 2.0) raindrops (0.17.0) - rake (11.2.2) + rake (12.0.0) redcarpet (3.3.4) request_store (1.3.1) rspec (3.5.0) @@ -249,16 +274,14 @@ GEM daemons (~> 1.0, >= 1.0.9) eventmachine (~> 1.0, >= 1.0.4) rack (>= 1, < 3) - thor (0.19.1) - thread_safe (0.3.5) + thor (0.19.4) + thread_safe (0.3.6) tilt (1.4.1) - treetop (1.4.15) - polyglot - polyglot (>= 0.3.1) turbolinks (5.0.1) turbolinks-source (~> 5) turbolinks-source (5.0.0) - tzinfo (0.3.51) + tzinfo (1.2.2) + thread_safe (~> 0.1) uglifier (3.0.2) execjs (>= 0.3.0, < 3) unicorn (5.1.0) @@ -298,7 +321,7 @@ DEPENDENCIES omniauth-twitter (~> 0.0.16) pg rack-robotz (~> 0.0.3) - rails (= 4.0.8) + rails (= 4.2.8) rails_html_helpers redcarpet rspec-activemodel-mocks @@ -315,4 +338,4 @@ DEPENDENCIES unicorn BUNDLED WITH - 1.11.2 + 1.14.3 From 2bf679e690252f13648935d8dd28dd49343e6114 Mon Sep 17 00:00:00 2001 From: Alex Coles Date: Sat, 11 Mar 2017 21:51:40 +0000 Subject: [PATCH 471/499] Remove mutator sort on ActiveRecord::Relation https://github.com/rails/rails/pull/13314 removed mutator methods (e.g., `Array#sort!`) from relations. --- app/models/event.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/event.rb b/app/models/event.rb index 9772a77b..9341a943 100644 --- a/app/models/event.rb +++ b/app/models/event.rb @@ -68,7 +68,7 @@ def search_activities(author = nil, query_string = "", filter = "all") def activities_per_day(author = nil, query_string = "", filter = "all") activities = search_activities(author, query_string, filter) - activities.sort_by! { |activity| activity.start_time } + activities = activities.to_a.sort_by { |activity| activity.start_time } activities.each_with_object(Hash.new { [] }) do |activity, grouped_by_day| activity.dates.each do |date| From 1ffe42bc0fef2fc0e527aa0583c4599e7782e507 Mon Sep 17 00:00:00 2001 From: Alex Coles Date: Sat, 11 Mar 2017 21:51:40 +0000 Subject: [PATCH 472/499] Fix invalid aggregate SQL: use count(:all) See https://github.com/rails/rails/issues/13648 --- app/models/event.rb | 6 +++--- app/views/participations/create.js.erb | 2 +- app/views/participations/destroy.js.erb | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/models/event.rb b/app/models/event.rb index 9341a943..7aabb33e 100644 --- a/app/models/event.rb +++ b/app/models/event.rb @@ -26,10 +26,10 @@ def new_activity(author, *args) def counters(user) { - today: Activity.today.count, + today: Activity.today.count(:all), all: Activity.count, - participant: user.nil? ? 0 : Activity.participated_by(user).count, - owner: user.nil? ? 0 : Activity.created_by(user).count + participant: user.nil? ? 0 : Activity.participated_by(user).count(:all), + owner: user.nil? ? 0 : Activity.created_by(user).count(:all) } end diff --git a/app/views/participations/create.js.erb b/app/views/participations/create.js.erb index 230c7ade..56a89284 100644 --- a/app/views/participations/create.js.erb +++ b/app/views/participations/create.js.erb @@ -3,4 +3,4 @@ $('#activity-<%= @current_activity.id %>') $('#activity-<%= @current_activity.id %>') .find('.progress') .progress(); -$('#activities-filter .participant strong').text('<%= Activity.participated_by(current_user).count %>') +$('#activities-filter .participant strong').text('<%= Activity.participated_by(current_user).count(:all) %>') diff --git a/app/views/participations/destroy.js.erb b/app/views/participations/destroy.js.erb index 230c7ade..56a89284 100644 --- a/app/views/participations/destroy.js.erb +++ b/app/views/participations/destroy.js.erb @@ -3,4 +3,4 @@ $('#activity-<%= @current_activity.id %>') $('#activity-<%= @current_activity.id %>') .find('.progress') .progress(); -$('#activities-filter .participant strong').text('<%= Activity.participated_by(current_user).count %>') +$('#activities-filter .participant strong').text('<%= Activity.participated_by(current_user).count(:all) %>') From bc2e80fec5700380fbd4b6482840e2487a85b13b Mon Sep 17 00:00:00 2001 From: Alex Coles Date: Sat, 11 Mar 2017 21:51:40 +0000 Subject: [PATCH 473/499] Bump Devise to latest, 4.2.x --- Gemfile | 2 +- Gemfile.lock | 19 +++++++++---------- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/Gemfile b/Gemfile index 222be48a..d09e968f 100644 --- a/Gemfile +++ b/Gemfile @@ -5,7 +5,7 @@ gem 'pg' gem 'unicorn' gem 'settingslogic' gem 'newrelic_rpm' -gem 'devise', '~> 3.0.0.rc' +gem 'devise', '~> 4.2.0' gem 'omniauth', '~> 1.1.4' gem 'omniauth-github', '~> 1.1.0' gem 'omniauth-twitter', '~> 0.0.16' diff --git a/Gemfile.lock b/Gemfile.lock index 3bf6ca7e..4f45d1b8 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -41,8 +41,6 @@ GEM addressable (2.4.0) arel (6.0.4) bcrypt (3.1.11) - bcrypt-ruby (3.1.5) - bcrypt (>= 3.1.3) better_errors (2.1.1) coderay (>= 1.0.0) erubis (>= 2.6.6) @@ -71,10 +69,11 @@ GEM coffee-script-source (1.10.0) daemons (1.2.4) debug_inspector (0.0.2) - devise (3.0.4) - bcrypt-ruby (~> 3.0) + devise (4.2.0) + bcrypt (~> 3.0) orm_adapter (~> 0.1) - railties (>= 3.2.6, < 5) + railties (>= 4.1.0, < 5.1) + responders warden (~> 1.2.3) diff-lcs (1.2.5) docile (1.1.5) @@ -149,9 +148,8 @@ GEM bourbon (>= 3.1) sass (~> 3.2.19) newrelic_rpm (3.16.2.321) - nokogiri (1.6.8) + nokogiri (1.6.8.1) mini_portile2 (~> 2.1.0) - pkg-config (~> 1.1.7) oauth (0.5.1) oauth2 (0.8.1) faraday (~> 0.8) @@ -176,7 +174,6 @@ GEM omniauth-oauth (~> 1.0) orm_adapter (0.5.0) pg (0.18.4) - pkg-config (1.1.7) rack (1.6.5) rack-robotz (0.0.4) rack @@ -212,6 +209,8 @@ GEM rake (12.0.0) redcarpet (3.3.4) request_store (1.3.1) + responders (2.3.0) + railties (>= 4.2.0, < 5.1) rspec (3.5.0) rspec-core (~> 3.5.0) rspec-expectations (~> 3.5.0) @@ -287,7 +286,7 @@ GEM unicorn (5.1.0) kgio (~> 2.6) raindrops (~> 0.7) - warden (1.2.6) + warden (1.2.7) rack (>= 1.0) xpath (2.0.0) nokogiri (~> 1.3) @@ -303,7 +302,7 @@ DEPENDENCIES cancan capybara (~> 2.1) coffee-rails (~> 4.0.0) - devise (~> 3.0.0.rc) + devise (~> 4.2.0) dotenv-rails draper exception_notification (~> 4.0.1) From bf99fcbe97abfecdeacb405eb776a067c96502f3 Mon Sep 17 00:00:00 2001 From: Alex Coles Date: Sat, 11 Mar 2017 21:51:41 +0000 Subject: [PATCH 474/499] Regenerate Devise config --- config/initializers/devise.rb | 126 +++++++++++++++++++++------------- 1 file changed, 79 insertions(+), 47 deletions(-) diff --git a/config/initializers/devise.rb b/config/initializers/devise.rb index 207b7557..b2f33ecf 100644 --- a/config/initializers/devise.rb +++ b/config/initializers/devise.rb @@ -1,13 +1,24 @@ # Use this hook to configure devise mailer, warden hooks and so forth. # Many of these configuration options can be set straight in your model. Devise.setup do |config| + # The secret key used by Devise. Devise uses this key to generate + # random tokens. Changing this key will render invalid all existing + # confirmation, reset password and unlock tokens in the database. + # Devise will use the `secret_key_base` as its `secret_key` + # by default. You can change it below and use your own secret key. + # config.secret_key = '99cc8d08d0de127049cddd1b558aeff34974fb980754b7e46d0d1ab742b067ba56317ff666fd8c1792d4d4e3c858cd28a2965570ab85e4057097e4270a62eb8a' + # ==> Mailer Configuration # Configure the e-mail address which will be shown in Devise::Mailer, - # note that it will be overwritten if you use your own mailer class with default "from" parameter. + # note that it will be overwritten if you use your own mailer class + # with default "from" parameter. config.mailer_sender = Settings.mailers.from # Configure the class responsible to send e-mails. - # config.mailer = "Devise::Mailer" + # config.mailer = 'Devise::Mailer' + + # Configure the parent class responsible to send e-mails. + # config.parent_mailer = 'ActionMailer::Base' # ==> ORM configuration # Load and configure the ORM. Supports :active_record (default) and @@ -23,7 +34,7 @@ # session. If you need permissions, you should implement that in a before filter. # You can also supply a hash where the value is a boolean determining whether # or not authentication should be aborted when the value is not present. - # config.authentication_keys = [ :email ] + # config.authentication_keys = [:email] # Configure parameters from the request object used for authentication. Each entry # given should be a request method and it will automatically be passed to the @@ -35,12 +46,12 @@ # Configure which authentication keys should be case-insensitive. # These keys will be downcased upon creating or modifying a user and when used # to authenticate or find a user. Default is :email. - config.case_insensitive_keys = [ :email ] + config.case_insensitive_keys = [:email] # Configure which authentication keys should have whitespace stripped. # These keys will have whitespace before and after removed upon creating or # modifying a user and when used to authenticate or find a user. Default is :email. - config.strip_whitespace_keys = [ :email ] + config.strip_whitespace_keys = [:email] # Tell if authentication through request.params is enabled. True by default. # It can be set to an array that will enable params authentication only for the @@ -48,17 +59,18 @@ # enable it only for database (email + password) authentication. # config.params_authenticatable = true - # Tell if authentication through HTTP Basic Auth is enabled. False by default. + # Tell if authentication through HTTP Auth is enabled. False by default. # It can be set to an array that will enable http authentication only for the - # given strategies, for example, `config.http_authenticatable = [:token]` will - # enable it only for token authentication. + # given strategies, for example, `config.http_authenticatable = [:database]` will + # enable it only for database authentication. The supported strategies are: + # :database = Support basic authentication with authentication key + password # config.http_authenticatable = false - # If http headers should be returned for AJAX requests. True by default. + # If 401 status code should be returned for AJAX requests. True by default. # config.http_authenticatable_on_xhr = true - # The realm used in Http Basic Authentication. "Application" by default. - # config.http_authentication_realm = "Application" + # The realm used in Http Basic Authentication. 'Application' by default. + # config.http_authentication_realm = 'Application' # It will change confirmation, password recovery and other workflows # to behave the same regardless if the e-mail provided was right or wrong. @@ -66,30 +78,47 @@ # config.paranoid = true # By default Devise will store the user in session. You can skip storage for - # :http_auth and :token_auth by adding those symbols to the array below. + # particular strategies by setting this option. # Notice that if you are skipping storage for all authentication paths, you # may want to disable generating routes to Devise's sessions controller by - # passing :skip => :sessions to `devise_for` in your config/routes.rb + # passing skip: :sessions to `devise_for` in your config/routes.rb config.skip_session_storage = [:http_auth] + # By default, Devise cleans up the CSRF token on authentication to + # avoid CSRF token fixation attacks. This means that, when using AJAX + # requests for sign in and sign up, you need to get a new CSRF token + # from the server. You can disable this option at your own risk. + # config.clean_up_csrf_token_on_authentication = true + + # When false, Devise will not attempt to reload routes on eager load. + # This can reduce the time taken to boot the app but if your application + # requires the Devise mappings to be loaded during boot time the application + # won't boot properly. + # config.reload_routes = true + # ==> Configuration for :database_authenticatable - # For bcrypt, this is the cost for hashing the password and defaults to 10. If - # using other encryptors, it sets how many times you want the password re-encrypted. + # For bcrypt, this is the cost for hashing the password and defaults to 11. If + # using other algorithms, it sets how many times you want the password to be hashed. # # Limiting the stretches to just one in testing will increase the performance of # your test suite dramatically. However, it is STRONGLY RECOMMENDED to not use - # a value less than 10 in other environments. - config.stretches = Rails.env.test? ? 1 : 10 + # a value less than 10 in other environments. Note that, for bcrypt (the default + # algorithm), the cost increases exponentially with the number of stretches (e.g. + # a value of 20 is already extremely slow: approx. 60 seconds for 1 calculation). + config.stretches = Rails.env.test? ? 1 : 11 + + # Set up a pepper to generate the hashed password. + # config.pepper = '2fb8ef4e815cfcbb05feec0a622ec011fe47bffcb5b36631bcfc81d47a77a62850e6055b3b41fc4c2c9a332c0e6fc58a315635eccddad5f8170569b877e28e44' - # Setup a pepper to generate the encrypted password. - # config.pepper = "490e70f9a671c7ab836267420ded76e5be5d4b24fedffec1f528ea91a536e308891dfad15f22a5ff8d746fa5df4025786b0d1d9ed6bd6762edd45df4e7682ac1" + # Send a notification email when the user's password is changed + # config.send_password_change_notification = false # ==> Configuration for :confirmable # A period that the user is allowed to access the website even without - # confirming his account. For instance, if set to 2.days, the user will be - # able to access the website for two days without confirming his account, + # confirming their account. For instance, if set to 2.days, the user will be + # able to access the website for two days without confirming their account, # access will be blocked just in the third day. Default is 0.days, meaning - # the user cannot access the website without confirming his account. + # the user cannot access the website without confirming their account. # config.allow_unconfirmed_access_for = 2.days # A period that the user is allowed to confirm their account before their @@ -102,41 +131,41 @@ # If true, requires any email changes to be confirmed (exactly the same way as # initial account confirmation) to be applied. Requires additional unconfirmed_email - # db field (see migrations). Until confirmed new email is stored in - # unconfirmed email column, and copied to email column on successful confirmation. + # db field (see migrations). Until confirmed, new email is stored in + # unconfirmed_email column, and copied to email column on successful confirmation. config.reconfirmable = true # Defines which key will be used when confirming an account - # config.confirmation_keys = [ :email ] + # config.confirmation_keys = [:email] # ==> Configuration for :rememberable # The time the user will be remembered without asking for credentials again. # config.remember_for = 2.weeks + # Invalidates all the remember me tokens when the user signs out. + config.expire_all_remember_me_on_sign_out = true + # If true, extends the user's remember period when remembered via cookie. # config.extend_remember_period = false # Options to be passed to the created cookie. For instance, you can set - # :secure => true in order to force SSL only cookies. + # secure: true in order to force SSL only cookies. # config.rememberable_options = {} # ==> Configuration for :validatable - # Range for password length. Default is 8..128. + # Range for password length. config.password_length = 8..128 # Email regex used to validate email formats. It simply asserts that # one (and only one) @ exists in the given string. This is mainly # to give user feedback and not to assert the e-mail validity. - # config.email_regexp = /\A[^@]+@[^@]+\z/ + config.email_regexp = /\A[^@\s]+@[^@\s]+\z/ # ==> Configuration for :timeoutable # The time you want to timeout the user session without activity. After this # time the user will be asked for credentials again. Default is 30 minutes. # config.timeout_in = 30.minutes - # If true, expires auth token on session timeout. - # config.expire_auth_token_on_timeout = false - # ==> Configuration for :lockable # Defines which strategy will be used to lock an account. # :failed_attempts = Locks an account after a number of failed attempts to sign in. @@ -144,7 +173,7 @@ # config.lock_strategy = :failed_attempts # Defines which key will be used when locking and unlocking an account - # config.unlock_keys = [ :email ] + # config.unlock_keys = [:email] # Defines which strategy will be used to unlock an account. # :email = Sends an unlock link to the user email @@ -160,30 +189,33 @@ # Time interval to unlock the account if :time is enabled as unlock_strategy. # config.unlock_in = 1.hour + # Warn on the last attempt before the account is locked. + # config.last_attempt_warning = true + # ==> Configuration for :recoverable # # Defines which key will be used when recovering the password for an account - # config.reset_password_keys = [ :email ] + # config.reset_password_keys = [:email] # Time interval you can reset your password with a reset password key. # Don't put a too small interval or your users won't have the time to # change their passwords. config.reset_password_within = 6.hours + # When set to false, does not sign a user in automatically after their password is + # reset. Defaults to true, so a user is signed in automatically after a reset. + # config.sign_in_after_reset_password = true + # ==> Configuration for :encryptable - # Allow you to use another encryption algorithm besides bcrypt (default). You can use - # :sha1, :sha512 or encryptors from others authentication tools as :clearance_sha1, - # :authlogic_sha512 (then you should set stretches above to 20 for default behavior) - # and :restful_authentication_sha1 (then you should set stretches to 10, and copy - # REST_AUTH_SITE_KEY to pepper). + # Allow you to use another hashing or encryption algorithm besides bcrypt (default). + # You can use :sha1, :sha512 or algorithms from others authentication tools as + # :clearance_sha1, :authlogic_sha512 (then you should set stretches above to 20 + # for default behavior) and :restful_authentication_sha1 (then you should set + # stretches to 10, and copy REST_AUTH_SITE_KEY to pepper). # # Require the `devise-encryptable` gem when using anything other than bcrypt # config.encryptor = :sha512 - # ==> Configuration for :token_authenticatable - # Defines name of the authentication token params key - # config.token_authentication_key = :auth_token - # ==> Scopes configuration # Turn scoped views on. Before rendering "sessions/new", it will first check for # "users/sessions/new". It's turned off by default because it's slower if you @@ -207,7 +239,7 @@ # should add them to the navigational formats lists. # # The "*/*" below is required to match Internet Explorer requests. - # config.navigational_formats = ["*/*", :html] + # config.navigational_formats = ['*/*', :html] # The default HTTP method used to sign out a resource. Default is :delete. config.sign_out_via = :delete @@ -224,7 +256,7 @@ # # config.warden do |manager| # manager.intercept_401 = false - # manager.default_strategies(:scope => :user).unshift :some_external_strategy + # manager.default_strategies(scope: :user).unshift :some_external_strategy # end # ==> Mountable engine configurations @@ -232,12 +264,12 @@ # is mountable, there are some extra configurations to be taken into account. # The following options are available, assuming the engine is mounted as: # - # mount MyEngine, at: "/my_engine" + # mount MyEngine, at: '/my_engine' # # The router that invoked `devise_for`, in the example above, would be: # config.router_name = :my_engine # - # When using omniauth, Devise cannot automatically set Omniauth path, + # When using OmniAuth, Devise cannot automatically set OmniAuth path, # so you need to do it manually. For the users scope, it would be: - # config.omniauth_path_prefix = "/my_engine/users/auth" + # config.omniauth_path_prefix = '/my_engine/users/auth' end From d6b4d8412855713cc1e8ddf0193b292fdd98fd22 Mon Sep 17 00:00:00 2001 From: Alex Coles Date: Sat, 11 Mar 2017 21:51:41 +0000 Subject: [PATCH 475/499] Update User spec for more lenient email validation --- spec/models/user_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 608da764..0e0713e6 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -173,7 +173,7 @@ subject { User.new } it { is_expected.to accept_values_for(:email, "xx@xx.com" ) } - it { is_expected.not_to accept_values_for(:email, "", nil, " x @ x.com", "123", "login@server." ) } + it { is_expected.not_to accept_values_for(:email, "", nil, " x @ x.com", "123") } it { is_expected.to accept_values_for(:password, "qweqweqwe" ) } it { is_expected.not_to accept_values_for(:password, "qweqwe", nil, "") } From 3d66fd17ebd02102b9d78778ac11986d3fb166a7 Mon Sep 17 00:00:00 2001 From: Alex Coles Date: Sat, 11 Mar 2017 22:02:43 +0000 Subject: [PATCH 476/499] Replace deprecated Devise spec helper --- spec/rails_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index c917f543..c30176fc 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -61,7 +61,7 @@ def self.connection # rspec-rails. config.infer_base_class_for_anonymous_controllers = false - config.include Devise::TestHelpers, type: :controller + config.include Devise::Test::ControllerHelpers, type: :controller config.include Warden::Test::Helpers config.before :suite do Warden.test_mode! From c1c0392ce9c759a47a7c9ea6c503c1676b7091c3 Mon Sep 17 00:00:00 2001 From: Alex Coles Date: Sat, 11 Mar 2017 22:04:31 +0000 Subject: [PATCH 477/499] Regenerate binstubs Signed-off-by: Alex Coles --- bin/rails | 2 +- bin/setup | 29 +++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) create mode 100755 bin/setup diff --git a/bin/rails b/bin/rails index 728cd85a..5191e692 100755 --- a/bin/rails +++ b/bin/rails @@ -1,4 +1,4 @@ #!/usr/bin/env ruby -APP_PATH = File.expand_path('../../config/application', __FILE__) +APP_PATH = File.expand_path('../../config/application', __FILE__) require_relative '../config/boot' require 'rails/commands' diff --git a/bin/setup b/bin/setup new file mode 100755 index 00000000..acdb2c13 --- /dev/null +++ b/bin/setup @@ -0,0 +1,29 @@ +#!/usr/bin/env ruby +require 'pathname' + +# path to your application root. +APP_ROOT = Pathname.new File.expand_path('../../', __FILE__) + +Dir.chdir APP_ROOT do + # This script is a starting point to setup your application. + # Add necessary setup steps to this file: + + puts "== Installing dependencies ==" + system "gem install bundler --conservative" + system "bundle check || bundle install" + + # puts "\n== Copying sample files ==" + # unless File.exist?("config/database.yml") + # system "cp config/database.yml.sample config/database.yml" + # end + + puts "\n== Preparing database ==" + system "bin/rake db:setup" + + puts "\n== Removing old logs and tempfiles ==" + system "rm -f log/*" + system "rm -rf tmp/cache" + + puts "\n== Restarting application server ==" + system "touch tmp/restart.txt" +end From 770c6f79c6757c7304190a819f374b18e3f4c848 Mon Sep 17 00:00:00 2001 From: Alex Coles Date: Sat, 11 Mar 2017 23:17:30 +0000 Subject: [PATCH 478/499] Update Rails config; Disable lib autoloading Signed-off-by: Alex Coles --- {lib => app/models}/ability.rb | 0 {lib => app/models}/settings.rb | 0 config/application.rb | 15 ++---- config/boot.rb | 3 +- config/environment.rb | 6 +-- config/environments/development.rb | 22 +++++--- config/environments/production.rb | 59 ++++++++++----------- config/environments/staging.rb | 62 +++++++++++------------ config/environments/test.rb | 15 ++++-- config/initializers/assets.rb | 11 ++++ config/initializers/cookies_serializer.rb | 3 ++ config/initializers/mime_types.rb | 1 - config/initializers/session_store.rb | 2 +- config/routes.rb | 2 +- config/secrets.yml | 22 ++++++++ 15 files changed, 128 insertions(+), 95 deletions(-) rename {lib => app/models}/ability.rb (100%) rename {lib => app/models}/settings.rb (100%) create mode 100644 config/initializers/assets.rb create mode 100644 config/initializers/cookies_serializer.rb create mode 100644 config/secrets.yml diff --git a/lib/ability.rb b/app/models/ability.rb similarity index 100% rename from lib/ability.rb rename to app/models/ability.rb diff --git a/lib/settings.rb b/app/models/settings.rb similarity index 100% rename from lib/settings.rb rename to app/models/settings.rb diff --git a/config/application.rb b/config/application.rb index 83b04a86..bede8302 100644 --- a/config/application.rb +++ b/config/application.rb @@ -2,8 +2,9 @@ require 'rails/all' -# Assets should be precompiled for production (so we don't need the gems loaded then) -Bundler.require(*Rails.groups(assets: %w(development test))) +# Require the gems listed in Gemfile, including any gems +# you've limited to :test, :development, or :production. +Bundler.require(*Rails.groups) module Activities class Application < Rails::Application @@ -19,13 +20,7 @@ class Application < Rails::Application # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s] # config.i18n.default_locale = :de - config.autoload_paths += Dir["#{config.root}/lib/**/"] - - # don't generate any assets - config.generators.assets = false - - config.assets.initialize_on_precompile = false - - config.i18n.enforce_available_locales = true + # Do not swallow errors in after_commit/after_rollback callbacks. + config.active_record.raise_in_transactional_callbacks = true end end diff --git a/config/boot.rb b/config/boot.rb index 35967366..6b750f00 100644 --- a/config/boot.rb +++ b/config/boot.rb @@ -1,4 +1,3 @@ -# Set up gems listed in the Gemfile. ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) -require 'bundler/setup' if File.exists?(ENV['BUNDLE_GEMFILE']) +require 'bundler/setup' # Set up gems listed in the Gemfile. diff --git a/config/environment.rb b/config/environment.rb index c78a58a0..ee8d90dc 100644 --- a/config/environment.rb +++ b/config/environment.rb @@ -1,5 +1,5 @@ -# Load the rails application. +# Load the Rails application. require File.expand_path('../application', __FILE__) -# Initialize the rails application. -Activities::Application.initialize! +# Initialize the Rails application. +Rails.application.initialize! diff --git a/config/environments/development.rb b/config/environments/development.rb index ad3c207b..04df9511 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -1,4 +1,4 @@ -Activities::Application.configure do +Rails.application.configure do # Settings specified here will take precedence over those in config/application.rb. # In the development environment your application's code is reloaded on @@ -19,19 +19,25 @@ # Print deprecation notices to the Rails logger. config.active_support.deprecation = :log - # Raise an error on page load if there are pending migrations + # Raise an error on page load if there are pending migrations. config.active_record.migration_error = :page_load # Debug mode disables concatenation and preprocessing of assets. + # This option may cause significant delays in view rendering with a large + # number of complex assets. config.assets.debug = true - config.action_mailer.default_url_options = { :host => Settings.host } + # Asset digests allow you to set far-future HTTP expiration dates on all assets, + # yet still be able to expire them through the digest params. + config.assets.digest = true - # Do not compress assets - config.assets.compress = false + # Adds additional error checking when serving assets at runtime. + # Checks for improperly declared sprockets dependencies. + # Raises helpful error messages. + config.assets.raise_runtime_errors = true - # Enable source maps in the browser - config.sass.debug_info = true + # Raises error for missing translations + # config.action_view.raise_on_missing_translations = true - config.sass.line_comments = true + config.action_mailer.default_url_options = { host: Settings.host } end diff --git a/config/environments/production.rb b/config/environments/production.rb index a1cf2637..8cd881e4 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -1,11 +1,11 @@ -Activities::Application.configure do +Rails.application.configure do # Settings specified here will take precedence over those in config/application.rb. # Code is not reloaded between requests. config.cache_classes = true # Eager load code on boot. This eager loads most of Rails and - # your application in memory, allowing both thread web servers + # your application in memory, allowing both threaded web servers # and those relying on copy on write to perform better. # Rake tasks automatically ignore this option for performance. config.eager_load = true @@ -16,33 +16,36 @@ # Enable Rack::Cache to put a simple HTTP cache in front of your application # Add `rack-cache` to your Gemfile before enabling this. - # For large-scale production use, consider using a caching reverse proxy like nginx, varnish or squid. + # For large-scale production use, consider using a caching reverse proxy like + # NGINX, varnish or squid. # config.action_dispatch.rack_cache = true - # Disable Rails's static asset server (Apache or nginx will already do this). - config.serve_static_assets = false + # Disable serving static files from the `/public` folder by default since + # Apache or NGINX already handles this. + config.serve_static_files = ENV['RAILS_SERVE_STATIC_FILES'].present? # Compress JavaScripts and CSS. - config.assets.js_compressor = :uglifier + config.assets.js_compressor = :uglifier # config.assets.css_compressor = :sass - # Whether to fallback to assets pipeline if a precompiled asset is missed. - config.assets.compile = true + # Do not fallback to assets pipeline if a precompiled asset is missed. + config.assets.compile = false - # Generate digests for assets URLs. + # Asset digests allow you to set far-future HTTP expiration dates on all assets, + # yet still be able to expire them through the digest params. config.assets.digest = true - # Version of your assets, change this if you want to expire all your assets. - config.assets.version = '1.0' + # `config.assets.precompile` and `config.assets.version` have moved to config/initializers/assets.rb # Specifies the header that your server uses for sending files. - # config.action_dispatch.x_sendfile_header = "X-Sendfile" # for apache - # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for nginx + # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache + # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. # config.force_ssl = true - # Set to :debug to see everything in the log. + # Use the lowest log level to ensure availability of diagnostic information + # when problems arise. config.log_level = :info # Prepend all log lines with the following tags. @@ -55,36 +58,30 @@ # config.cache_store = :mem_cache_store # Enable serving of images, stylesheets, and JavaScripts from an asset server. - # config.action_controller.asset_host = "http://assets.example.com" - - # Precompile additional assets. - # application.js, application.css, and all non-JS/CSS in app/assets folder are already added. - # config.assets.precompile += %w( search.js ) + # config.action_controller.asset_host = 'http://assets.example.com' # Ignore bad email addresses and do not raise email delivery errors. # Set this to true and configure the email server for immediate delivery to raise delivery errors. # config.action_mailer.raise_delivery_errors = false + config.action_mailer.delivery_method = :sendmail + config.action_mailer.default_url_options = { host: Settings.host } # Enable locale fallbacks for I18n (makes lookups for any locale fall back to - # the I18n.default_locale when a translation can not be found). + # the I18n.default_locale when a translation cannot be found). config.i18n.fallbacks = true # Send deprecation notices to registered listeners. config.active_support.deprecation = :notify - # Disable automatic flushing of the log to improve performance. - # config.autoflush_log = false - - config.action_mailer.default_url_options = { :host => Settings.host } - # Use default logging formatter so that PID and timestamp are not suppressed. config.log_formatter = ::Logger::Formatter.new config.middleware.use ExceptionNotification::Rack, email: { - :email_prefix => "[eurucamp-activities::Exception] ", - :sender_address => %{"Exception Notifier" <#{Settings.errors.from}>}, - :exception_recipients => Settings.errors.to - } -end + email_prefix: '[eurucamp-activities::Exception] ', + sender_address: %("Exception Notifier" <#{Settings.errors.from}>), + exception_recipients: Settings.errors.to + } -ActionMailer::Base.delivery_method = :sendmail + # Do not dump schema after migrations. + config.active_record.dump_schema_after_migration = false +end diff --git a/config/environments/staging.rb b/config/environments/staging.rb index 8c063c99..c0025814 100644 --- a/config/environments/staging.rb +++ b/config/environments/staging.rb @@ -1,10 +1,11 @@ -Activities::Application.configure do +Rails.application.configure do + # Settings specified here will take precedence over those in config/application.rb. # Code is not reloaded between requests. config.cache_classes = true # Eager load code on boot. This eager loads most of Rails and - # your application in memory, allowing both thread web servers + # your application in memory, allowing both threaded web servers # and those relying on copy on write to perform better. # Rake tasks automatically ignore this option for performance. config.eager_load = true @@ -15,33 +16,36 @@ # Enable Rack::Cache to put a simple HTTP cache in front of your application # Add `rack-cache` to your Gemfile before enabling this. - # For large-scale production use, consider using a caching reverse proxy like nginx, varnish or squid. + # For large-scale production use, consider using a caching reverse proxy like + # NGINX, varnish or squid. # config.action_dispatch.rack_cache = true - # Disable Rails's static asset server (Apache or nginx will already do this). - config.serve_static_assets = false + # Disable serving static files from the `/public` folder by default since + # Apache or NGINX already handles this. + config.serve_static_files = ENV['RAILS_SERVE_STATIC_FILES'].present? # Compress JavaScripts and CSS. - config.assets.js_compressor = :uglifier + config.assets.js_compressor = :uglifier # config.assets.css_compressor = :sass - # Whether to fallback to assets pipeline if a precompiled asset is missed. - config.assets.compile = true + # Do not fallback to assets pipeline if a precompiled asset is missed. + config.assets.compile = false - # Generate digests for assets URLs. + # Asset digests allow you to set far-future HTTP expiration dates on all assets, + # yet still be able to expire them through the digest params. config.assets.digest = true - # Version of your assets, change this if you want to expire all your assets. - config.assets.version = '1.0' + # `config.assets.precompile` and `config.assets.version` have moved to config/initializers/assets.rb # Specifies the header that your server uses for sending files. - # config.action_dispatch.x_sendfile_header = "X-Sendfile" # for apache - # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for nginx + # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache + # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. # config.force_ssl = true - # Set to :debug to see everything in the log. + # Use the lowest log level to ensure availability of diagnostic information + # when problems arise. config.log_level = :info # Prepend all log lines with the following tags. @@ -54,38 +58,30 @@ # config.cache_store = :mem_cache_store # Enable serving of images, stylesheets, and JavaScripts from an asset server. - # config.action_controller.asset_host = "http://assets.example.com" - - # Precompile additional assets. - # application.js, application.css, and all non-JS/CSS in app/assets folder are already added. - # config.assets.precompile += %w( search.js ) + # config.action_controller.asset_host = 'http://assets.example.com' # Ignore bad email addresses and do not raise email delivery errors. # Set this to true and configure the email server for immediate delivery to raise delivery errors. # config.action_mailer.raise_delivery_errors = false + config.action_mailer.delivery_method = :sendmail + config.action_mailer.default_url_options = { host: Settings.host } # Enable locale fallbacks for I18n (makes lookups for any locale fall back to - # the I18n.default_locale when a translation can not be found). + # the I18n.default_locale when a translation cannot be found). config.i18n.fallbacks = true # Send deprecation notices to registered listeners. config.active_support.deprecation = :notify - # Disable automatic flushing of the log to improve performance. - # config.autoflush_log = false - # Use default logging formatter so that PID and timestamp are not suppressed. config.log_formatter = ::Logger::Formatter.new - config.action_mailer.default_url_options = { :host => Settings.host } - - config.middleware.insert_before(::ActionDispatch::Static, ::Rack::Robotz, "User-Agent" => "*", "Disallow" => "/") - config.middleware.use ExceptionNotification::Rack, email: { - :email_prefix => "[eurucamp-activities-staging::Exception] ", - :sender_address => %{"Exception Notifier" <#{Settings.errors.from}>}, - :exception_recipients => Settings.errors.to - } -end + email_prefix: '[eurucamp-activities-staging::Exception] ', + sender_address: %("Exception Notifier" <#{Settings.errors.from}>), + exception_recipients: Settings.errors.to + } -ActionMailer::Base.delivery_method = :sendmail + # Do not dump schema after migrations. + config.active_record.dump_schema_after_migration = false +end diff --git a/config/environments/test.rb b/config/environments/test.rb index d8826545..a46b51e3 100644 --- a/config/environments/test.rb +++ b/config/environments/test.rb @@ -1,4 +1,4 @@ -Activities::Application.configure do +Rails.application.configure do # Settings specified here will take precedence over those in config/application.rb. # The test environment is used exclusively to run your application's @@ -12,9 +12,9 @@ # preloads Rails for running tests, you may have to set it to true. config.eager_load = false - # Configure static asset server for tests with Cache-Control for performance. - config.serve_static_assets = true - config.static_cache_control = "public, max-age=3600" + # Configure static file server for tests with Cache-Control for performance. + config.serve_static_files = true + config.static_cache_control = 'public, max-age=3600' # Show full error reports and disable caching. config.consider_all_requests_local = true @@ -30,9 +30,14 @@ # The :test delivery method accumulates sent emails in the # ActionMailer::Base.deliveries array. config.action_mailer.delivery_method = :test + config.action_mailer.default_url_options = { host: Settings.host } + + # Randomize the order test cases are executed. + config.active_support.test_order = :random # Print deprecation notices to the stderr. config.active_support.deprecation = :stderr - config.action_mailer.default_url_options = { :host => Settings.host } + # Raises error for missing translations + # config.action_view.raise_on_missing_translations = true end diff --git a/config/initializers/assets.rb b/config/initializers/assets.rb new file mode 100644 index 00000000..01ef3e66 --- /dev/null +++ b/config/initializers/assets.rb @@ -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( search.js ) diff --git a/config/initializers/cookies_serializer.rb b/config/initializers/cookies_serializer.rb new file mode 100644 index 00000000..ac5f8b66 --- /dev/null +++ b/config/initializers/cookies_serializer.rb @@ -0,0 +1,3 @@ +# Be sure to restart your server when you modify this file. + +Rails.application.config.action_dispatch.cookies_serializer = :marshal diff --git a/config/initializers/mime_types.rb b/config/initializers/mime_types.rb index 72aca7e4..dc189968 100644 --- a/config/initializers/mime_types.rb +++ b/config/initializers/mime_types.rb @@ -2,4 +2,3 @@ # Add new mime types for use in respond_to blocks: # Mime::Type.register "text/richtext", :rtf -# Mime::Type.register_alias "text/html", :iphone diff --git a/config/initializers/session_store.rb b/config/initializers/session_store.rb index ec789fa6..ba0a3f43 100644 --- a/config/initializers/session_store.rb +++ b/config/initializers/session_store.rb @@ -1,3 +1,3 @@ # Be sure to restart your server when you modify this file. -Activities::Application.config.session_store :cookie_store, key: '_activities_session' +Rails.application.config.session_store :cookie_store, key: '_activities_session' diff --git a/config/routes.rb b/config/routes.rb index 784928a6..ec57518c 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,4 +1,4 @@ -Activities::Application.routes.draw do +Rails.application.routes.draw do resources :authentications, only: [:create, :destroy] get '/auth/:provider/callback' => 'authentications#create' diff --git a/config/secrets.yml b/config/secrets.yml new file mode 100644 index 00000000..af941eed --- /dev/null +++ b/config/secrets.yml @@ -0,0 +1,22 @@ +# Be sure to restart your server when you modify this file. + +# Your secret key is used for verifying the integrity of signed cookies. +# If you change this key, all old signed cookies will become invalid! + +# Make sure the secret is at least 30 characters and all random, +# no regular words or you'll be exposed to dictionary attacks. +# You can use `rake secret` to generate a secure secret key. + +# Make sure the secrets in this file are kept private +# if you're sharing your code publicly. + +development: + secret_key_base: ded20a08273981cd34e9a8d7e878e0316572770cc10f0bbb6c829e86887e458a9f6c9dd9c86ac4a35524b0ea1d174312a8f828e70189b5abf88bd4087030b233 + +test: + secret_key_base: 31ad73f9b171ed04c13c6ac584e1301175c5ea1822a79b139729bf806a01bd50db87148f7d1bbe64d7e63a6c79246dc4b1953a7a7d644eb8a62f862f7e0a8bff + +# Do not keep production secrets in the repository, +# instead read values from the environment. +production: + secret_key_base: <%= ENV["SECRET_KEY_BASE"] %> From c5f5144e238da370c3559beb9745f16015f1674e Mon Sep 17 00:00:00 2001 From: Alex Coles Date: Sat, 11 Mar 2017 23:22:09 +0000 Subject: [PATCH 479/499] Add explicit require to ApiController Signed-off-by: Alex Coles --- app/controllers/api_controller.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/controllers/api_controller.rb b/app/controllers/api_controller.rb index ff83be7e..82bd5817 100644 --- a/app/controllers/api_controller.rb +++ b/app/controllers/api_controller.rb @@ -1,3 +1,5 @@ +require_dependency 'api_responder' + class ApiController < ApplicationController self.responder = ApiResponder From 60d2abdcbcb8728921e2f17dcf24505cd1e49b60 Mon Sep 17 00:00:00 2001 From: Alex Coles Date: Sat, 11 Mar 2017 23:30:05 +0000 Subject: [PATCH 480/499] Update deprecated Capybara config --- spec/rails_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index c30176fc..dd04ca27 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -7,7 +7,7 @@ require 'capybara/rails' Capybara.configure do |config| - config.default_wait_time = 5 + config.default_max_wait_time = 5 # config.match = :one # config.exact_options = true # config.ignore_hidden_elements = true From 668e1efc4192d50f76d6d756104b8df97addb340 Mon Sep 17 00:00:00 2001 From: Alex Coles Date: Sat, 11 Mar 2017 23:37:07 +0000 Subject: [PATCH 481/499] Remove obsolete shelly-dependencies --- Gemfile | 1 - Gemfile.lock | 10 ---------- 2 files changed, 11 deletions(-) diff --git a/Gemfile b/Gemfile index d09e968f..4d1ed907 100644 --- a/Gemfile +++ b/Gemfile @@ -51,5 +51,4 @@ end group :production, :staging do gem 'exception_notification', '~> 4.0.1' gem 'rack-robotz', '~> 0.0.3' - gem 'shelly-dependencies' end diff --git a/Gemfile.lock b/Gemfile.lock index 4f45d1b8..92fdddf5 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -67,7 +67,6 @@ GEM coffee-script-source execjs coffee-script-source (1.10.0) - daemons (1.2.4) debug_inspector (0.0.2) devise (4.2.0) bcrypt (~> 3.0) @@ -87,7 +86,6 @@ GEM activesupport (>= 3.0) request_store (~> 1.0) erubis (2.7.0) - eventmachine (1.2.0.1) exception_notification (4.0.1) actionmailer (>= 3.0.4) activesupport (>= 3.0.4) @@ -249,9 +247,6 @@ GEM sprockets-rails (~> 2.0) settingslogic (2.0.9) sexp_processor (4.7.0) - shelly-dependencies (0.2.5) - rake - thin simple_form (3.0.4) actionpack (~> 4.0) activemodel (~> 4.0) @@ -269,10 +264,6 @@ GEM actionpack (>= 3.0) activesupport (>= 3.0) sprockets (~> 2.8) - thin (1.7.0) - daemons (~> 1.0, >= 1.0.9) - eventmachine (~> 1.0, >= 1.0.4) - rack (>= 1, < 3) thor (0.19.4) thread_safe (0.3.6) tilt (1.4.1) @@ -328,7 +319,6 @@ DEPENDENCIES rspec-rails (~> 3.3) sass-rails (~> 4.0.3) settingslogic - shelly-dependencies simple_form (~> 3.0.1) simplecov sprockets-rails (~> 2.1.3) From c5c42214a8abefcd5d12b0f75334d116d428e2bb Mon Sep 17 00:00:00 2001 From: Alex Coles Date: Sun, 12 Mar 2017 00:45:41 +0000 Subject: [PATCH 482/499] Target Ruby 2.3.3 --- .ruby-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ruby-version b/.ruby-version index cd57a8b9..0bee604d 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -2.1.5 +2.3.3 From ebebd47c0bee1b40d45d62bc289a56fc0265cf37 Mon Sep 17 00:00:00 2001 From: Alex Coles Date: Sun, 12 Mar 2017 00:46:10 +0000 Subject: [PATCH 483/499] Bump Travis' Ruby version to 2.3.3 --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index b839cc23..d92f755e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,5 +6,5 @@ cache: bundler before_script: cp config/database.yml.sample config/database.yml language: ruby rvm: - - 2.1.5 + - 2.3.3 script: bundle exec rake db:create db:migrate spec From 813ceed1f2e5f9ffab898ea22085e8152bf75429 Mon Sep 17 00:00:00 2001 From: Alex Coles Date: Sat, 11 Mar 2017 23:40:42 +0000 Subject: [PATCH 484/499] Bump Rails to 5.0.1 * Bump related Sprockets and asset pipeline gems * Bump OmniAuth to compatible version * Bump Draper to compatible (pre-release) version * Add activemodel-serializers-xml and rails-controller-testing for backwards compatibility. --- Gemfile | 21 ++-- Gemfile.lock | 306 +++++++++++++++++++++++++++------------------------ 2 files changed, 175 insertions(+), 152 deletions(-) diff --git a/Gemfile b/Gemfile index 4d1ed907..7343d460 100644 --- a/Gemfile +++ b/Gemfile @@ -1,19 +1,19 @@ source 'https://rubygems.org' -gem 'rails', '4.2.8' +gem 'rails', '5.0.1' gem 'pg' gem 'unicorn' gem 'settingslogic' gem 'newrelic_rpm' gem 'devise', '~> 4.2.0' -gem 'omniauth', '~> 1.1.4' -gem 'omniauth-github', '~> 1.1.0' -gem 'omniauth-twitter', '~> 0.0.16' -gem 'simple_form', '~> 3.0.1' +gem 'omniauth', '~> 1.6.1' +gem 'omniauth-github' +gem 'omniauth-twitter' +gem 'simple_form', '~> 3.4.0' gem 'dotenv-rails' gem 'modernizr-rails' -gem 'sprockets-rails', '~> 2.1.3' +gem 'sprockets-rails', '~> 3.2.0' gem 'jquery-rails' gem 'turbolinks' gem 'jbuilder', '~> 1.0.1' @@ -21,13 +21,15 @@ gem 'bourbon' gem 'neat' gem 'haml-rails' gem 'rails_html_helpers' -gem 'draper' +gem 'draper', '~> 3.0.0.pre1' gem 'cancan' gem 'redcarpet' -gem 'sass-rails', '~> 4.0.3' -gem 'coffee-rails', '~> 4.0.0' +gem 'sass-rails', '~> 5.0.6' +gem 'coffee-rails', '~> 4.2.1' gem 'uglifier', '>= 1.0.3' +gem 'activemodel-serializers-xml' + group :development do gem 'foreman' gem 'better_errors' @@ -40,6 +42,7 @@ group :test, :development do end group :test do + gem 'rails-controller-testing' gem 'rspec-its' gem 'rspec-activemodel-mocks' gem 'simplecov', require: false diff --git a/Gemfile.lock b/Gemfile.lock index 92fdddf5..fc596837 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,45 +1,54 @@ GEM remote: https://rubygems.org/ specs: - accept_values_for (0.7.2) - activemodel (>= 3, < 5) + accept_values_for (0.7.3) + activemodel (>= 4.2, < 5.1) rspec (>= 2.0, < 4.0) - actionmailer (4.2.8) - actionpack (= 4.2.8) - actionview (= 4.2.8) - activejob (= 4.2.8) + actioncable (5.0.1) + actionpack (= 5.0.1) + nio4r (~> 1.2) + websocket-driver (~> 0.6.1) + actionmailer (5.0.1) + actionpack (= 5.0.1) + actionview (= 5.0.1) + activejob (= 5.0.1) mail (~> 2.5, >= 2.5.4) - rails-dom-testing (~> 1.0, >= 1.0.5) - actionpack (4.2.8) - actionview (= 4.2.8) - activesupport (= 4.2.8) - rack (~> 1.6) - rack-test (~> 0.6.2) - rails-dom-testing (~> 1.0, >= 1.0.5) + rails-dom-testing (~> 2.0) + actionpack (5.0.1) + actionview (= 5.0.1) + activesupport (= 5.0.1) + rack (~> 2.0) + rack-test (~> 0.6.3) + rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.0, >= 1.0.2) - actionview (4.2.8) - activesupport (= 4.2.8) + actionview (5.0.1) + activesupport (= 5.0.1) builder (~> 3.1) erubis (~> 2.7.0) - rails-dom-testing (~> 1.0, >= 1.0.5) - rails-html-sanitizer (~> 1.0, >= 1.0.3) - activejob (4.2.8) - activesupport (= 4.2.8) - globalid (>= 0.3.0) - activemodel (4.2.8) - activesupport (= 4.2.8) + rails-dom-testing (~> 2.0) + rails-html-sanitizer (~> 1.0, >= 1.0.2) + activejob (5.0.1) + activesupport (= 5.0.1) + globalid (>= 0.3.6) + activemodel (5.0.1) + activesupport (= 5.0.1) + activemodel-serializers-xml (1.0.1) + activemodel (> 5.x) + activerecord (> 5.x) + activesupport (> 5.x) builder (~> 3.1) - activerecord (4.2.8) - activemodel (= 4.2.8) - activesupport (= 4.2.8) - arel (~> 6.0) - activesupport (4.2.8) + activerecord (5.0.1) + activemodel (= 5.0.1) + activesupport (= 5.0.1) + arel (~> 7.0) + activesupport (5.0.1) + concurrent-ruby (~> 1.0, >= 1.0.2) i18n (~> 0.7) minitest (~> 5.1) - thread_safe (~> 0.3, >= 0.3.4) tzinfo (~> 1.1) - addressable (2.4.0) - arel (6.0.4) + addressable (2.5.0) + public_suffix (~> 2.0, >= 2.0.2) + arel (7.1.4) bcrypt (3.1.11) better_errors (2.1.1) coderay (>= 1.0.0) @@ -47,12 +56,12 @@ GEM rack (>= 0.9.0) binding_of_caller (0.7.2) debug_inspector (>= 0.0.1) - bourbon (3.2.4) - sass (~> 3.2) - thor + bourbon (4.3.3) + sass (~> 3.4) + thor (~> 0.19) builder (3.2.3) cancan (1.6.10) - capybara (2.8.1) + capybara (2.12.1) addressable mime-types (>= 1.16) nokogiri (>= 1.3.3) @@ -60,13 +69,14 @@ GEM rack-test (>= 0.5.4) xpath (~> 2.0) coderay (1.1.1) - coffee-rails (4.0.1) + coffee-rails (4.2.1) coffee-script (>= 2.2.0) - railties (>= 4.0.0, < 5.0) + railties (>= 4.0.0, < 5.2.x) coffee-script (2.4.1) coffee-script-source execjs - coffee-script-source (1.10.0) + coffee-script-source (1.12.2) + concurrent-ruby (1.0.5) debug_inspector (0.0.2) devise (4.2.0) bcrypt (~> 3.0) @@ -74,30 +84,31 @@ GEM railties (>= 4.1.0, < 5.1) responders warden (~> 1.2.3) - diff-lcs (1.2.5) + diff-lcs (1.3) docile (1.1.5) - dotenv (2.1.1) - dotenv-rails (2.1.1) - dotenv (= 2.1.1) - railties (>= 4.0, < 5.1) - draper (2.1.0) - actionpack (>= 3.0) - activemodel (>= 3.0) - activesupport (>= 3.0) + dotenv (2.2.0) + dotenv-rails (2.2.0) + dotenv (= 2.2.0) + railties (>= 3.2, < 5.1) + draper (3.0.0.pre1) + actionpack (~> 5.0) + activemodel (~> 5.0) + activemodel-serializers-xml (~> 1.0) + activesupport (~> 5.0) request_store (~> 1.0) erubis (2.7.0) exception_notification (4.0.1) actionmailer (>= 3.0.4) activesupport (>= 3.0.4) execjs (2.7.0) - factory_girl (4.7.0) + factory_girl (4.8.0) activesupport (>= 3.0.0) - factory_girl_rails (4.7.0) - factory_girl (~> 4.7.0) + factory_girl_rails (4.8.0) + factory_girl (~> 4.8.0) railties (>= 3.0.0) - faraday (0.9.2) + faraday (0.11.0) multipart-post (>= 1.2, < 3) - foreman (0.82.0) + foreman (0.83.0) thor (~> 0.19.1) globalid (0.3.7) activesupport (>= 4.1.0) @@ -109,31 +120,30 @@ GEM haml (>= 4.0.6, < 5.0) html2haml (>= 1.0.1) railties (>= 4.0.1) - hashie (2.1.2) - hike (1.2.3) - html2haml (2.0.0) + hashie (3.5.5) + html2haml (2.1.0) erubis (~> 2.7.0) - haml (~> 4.0.0) - nokogiri (~> 1.6.0) + haml (~> 4.0) + nokogiri (>= 1.6.0) ruby_parser (~> 3.5) - httpauth (0.2.1) i18n (0.8.1) jbuilder (1.0.2) activesupport (>= 3.0.0) - jquery-rails (3.1.4) - railties (>= 3.0, < 5.0) + jquery-rails (4.2.2) + rails-dom-testing (>= 1, < 3) + railties (>= 4.2.0) thor (>= 0.14, < 2.0) - json (2.0.2) + json (2.0.3) json_spec (1.1.4) multi_json (~> 1.0) rspec (>= 2.0, < 4.0) - jwt (0.1.13) - multi_json (>= 1.5) - kgio (2.10.0) + jwt (1.5.6) + kgio (2.11.0) loofah (2.0.3) nokogiri (>= 1.5.9) mail (2.6.4) mime-types (>= 1.16, < 4) + method_source (0.8.2) mime-types (3.1) mime-types-data (~> 3.2015) mime-types-data (3.2016.0521) @@ -141,72 +151,78 @@ GEM minitest (5.10.1) modernizr-rails (2.7.1) multi_json (1.12.1) + multi_xml (0.6.0) multipart-post (2.0.0) - neat (1.5.1) - bourbon (>= 3.1) - sass (~> 3.2.19) - newrelic_rpm (3.16.2.321) - nokogiri (1.6.8.1) + neat (2.0.0) + sass (~> 3.4) + thor (~> 0.19) + newrelic_rpm (3.18.1.330) + nio4r (1.2.1) + nokogiri (1.7.0.1) mini_portile2 (~> 2.1.0) oauth (0.5.1) - oauth2 (0.8.1) - faraday (~> 0.8) - httpauth (~> 0.1) - jwt (~> 0.1.4) - multi_json (~> 1.0) - rack (~> 1.2) - omniauth (1.1.4) - hashie (>= 1.2, < 3) - rack - omniauth-github (1.1.2) - omniauth (~> 1.0) - omniauth-oauth2 (~> 1.1) + oauth2 (1.3.1) + faraday (>= 0.8, < 0.12) + jwt (~> 1.0) + multi_json (~> 1.3) + multi_xml (~> 0.5) + rack (>= 1.2, < 3) + omniauth (1.6.1) + hashie (>= 3.4.6, < 3.6.0) + rack (>= 1.6.2, < 3) + omniauth-github (1.2.3) + omniauth (~> 1.5) + omniauth-oauth2 (>= 1.4.0, < 2.0) omniauth-oauth (1.1.0) oauth omniauth (~> 1.0) - omniauth-oauth2 (1.1.1) - oauth2 (~> 0.8.0) - omniauth (~> 1.0) - omniauth-twitter (0.0.18) - multi_json (~> 1.3) - omniauth-oauth (~> 1.0) + omniauth-oauth2 (1.4.0) + oauth2 (~> 1.0) + omniauth (~> 1.2) + omniauth-twitter (1.4.0) + omniauth-oauth (~> 1.1) + rack orm_adapter (0.5.0) - pg (0.18.4) - rack (1.6.5) + pg (0.20.0) + public_suffix (2.0.5) + rack (2.0.1) rack-robotz (0.0.4) rack rack-test (0.6.3) rack (>= 1.0) - rails (4.2.8) - actionmailer (= 4.2.8) - actionpack (= 4.2.8) - actionview (= 4.2.8) - activejob (= 4.2.8) - activemodel (= 4.2.8) - activerecord (= 4.2.8) - activesupport (= 4.2.8) + rails (5.0.1) + actioncable (= 5.0.1) + actionmailer (= 5.0.1) + actionpack (= 5.0.1) + actionview (= 5.0.1) + activejob (= 5.0.1) + activemodel (= 5.0.1) + activerecord (= 5.0.1) + activesupport (= 5.0.1) bundler (>= 1.3.0, < 2.0) - railties (= 4.2.8) - sprockets-rails - rails-deprecated_sanitizer (1.0.3) - activesupport (>= 4.2.0.alpha) - rails-dom-testing (1.0.8) - activesupport (>= 4.2.0.beta, < 5.0) + railties (= 5.0.1) + sprockets-rails (>= 2.0.0) + rails-controller-testing (1.0.1) + actionpack (~> 5.x) + actionview (~> 5.x) + activesupport (~> 5.x) + rails-dom-testing (2.0.2) + activesupport (>= 4.2.0, < 6.0) nokogiri (~> 1.6) - rails-deprecated_sanitizer (>= 1.0.1) rails-html-sanitizer (1.0.3) loofah (~> 2.0) rails_html_helpers (0.1.1) railties (>= 3.2) - railties (4.2.8) - actionpack (= 4.2.8) - activesupport (= 4.2.8) + railties (5.0.1) + actionpack (= 5.0.1) + activesupport (= 5.0.1) + method_source rake (>= 0.8.7) thor (>= 0.18.1, < 2.0) raindrops (0.17.0) rake (12.0.0) - redcarpet (3.3.4) - request_store (1.3.1) + redcarpet (3.4.0) + request_store (1.3.2) responders (2.3.0) railties (>= 4.2.0, < 5.1) rspec (3.5.0) @@ -217,7 +233,7 @@ GEM activemodel (>= 3.0) activesupport (>= 3.0) rspec-mocks (>= 2.99, < 4.0) - rspec-core (3.5.2) + rspec-core (3.5.4) rspec-support (~> 3.5.0) rspec-expectations (3.5.0) diff-lcs (>= 1.2.0, < 2.0) @@ -228,7 +244,7 @@ GEM rspec-mocks (3.5.0) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.5.0) - rspec-rails (3.5.1) + rspec-rails (3.5.2) actionpack (>= 3.0) activesupport (>= 3.0) railties (>= 3.0) @@ -237,48 +253,50 @@ GEM rspec-mocks (~> 3.5.0) rspec-support (~> 3.5.0) rspec-support (3.5.0) - ruby_parser (3.8.2) + ruby_parser (3.8.4) sexp_processor (~> 4.1) - sass (3.2.19) - sass-rails (4.0.5) - railties (>= 4.0.0, < 5.0) - sass (~> 3.2.2) - sprockets (~> 2.8, < 3.0) - sprockets-rails (~> 2.0) + sass (3.4.23) + sass-rails (5.0.6) + railties (>= 4.0.0, < 6) + sass (~> 3.1) + sprockets (>= 2.8, < 4.0) + sprockets-rails (>= 2.0, < 4.0) + tilt (>= 1.1, < 3) settingslogic (2.0.9) - sexp_processor (4.7.0) - simple_form (3.0.4) - actionpack (~> 4.0) - activemodel (~> 4.0) - simplecov (0.12.0) + sexp_processor (4.8.0) + simple_form (3.4.0) + actionpack (> 4, < 5.1) + activemodel (> 4, < 5.1) + simplecov (0.13.0) docile (~> 1.1.0) json (>= 1.8, < 3) simplecov-html (~> 0.10.0) simplecov-html (0.10.0) - sprockets (2.12.4) - hike (~> 1.2) - multi_json (~> 1.0) - rack (~> 1.0) - tilt (~> 1.1, != 1.3.0) - sprockets-rails (2.1.4) - actionpack (>= 3.0) - activesupport (>= 3.0) - sprockets (~> 2.8) + sprockets (3.7.1) + concurrent-ruby (~> 1.0) + rack (> 1, < 3) + sprockets-rails (3.2.0) + actionpack (>= 4.0) + activesupport (>= 4.0) + sprockets (>= 3.0.0) thor (0.19.4) thread_safe (0.3.6) - tilt (1.4.1) + tilt (2.0.6) turbolinks (5.0.1) turbolinks-source (~> 5) turbolinks-source (5.0.0) tzinfo (1.2.2) thread_safe (~> 0.1) - uglifier (3.0.2) + uglifier (3.1.7) execjs (>= 0.3.0, < 3) - unicorn (5.1.0) + unicorn (5.2.0) kgio (~> 2.6) raindrops (~> 0.7) warden (1.2.7) rack (>= 1.0) + websocket-driver (0.6.5) + websocket-extensions (>= 0.1.0) + websocket-extensions (0.1.2) xpath (2.0.0) nokogiri (~> 1.3) @@ -287,15 +305,16 @@ PLATFORMS DEPENDENCIES accept_values_for + activemodel-serializers-xml better_errors binding_of_caller bourbon cancan capybara (~> 2.1) - coffee-rails (~> 4.0.0) + coffee-rails (~> 4.2.1) devise (~> 4.2.0) dotenv-rails - draper + draper (~> 3.0.0.pre1) exception_notification (~> 4.0.1) factory_girl_rails (~> 4.2) foreman @@ -306,22 +325,23 @@ DEPENDENCIES modernizr-rails neat newrelic_rpm - omniauth (~> 1.1.4) - omniauth-github (~> 1.1.0) - omniauth-twitter (~> 0.0.16) + omniauth (~> 1.6.1) + omniauth-github + omniauth-twitter pg rack-robotz (~> 0.0.3) - rails (= 4.2.8) + rails (= 5.0.1) + rails-controller-testing rails_html_helpers redcarpet rspec-activemodel-mocks rspec-its rspec-rails (~> 3.3) - sass-rails (~> 4.0.3) + sass-rails (~> 5.0.6) settingslogic - simple_form (~> 3.0.1) + simple_form (~> 3.4.0) simplecov - sprockets-rails (~> 2.1.3) + sprockets-rails (~> 3.2.0) turbolinks uglifier (>= 1.0.3) unicorn From 67d9c23f5b1510bc1140089ae4510dc9c315eeba Mon Sep 17 00:00:00 2001 From: Alex Coles Date: Sun, 12 Mar 2017 00:25:16 +0000 Subject: [PATCH 485/499] Peg Neat to 1.8.0 --- Gemfile | 2 +- Gemfile.lock | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Gemfile b/Gemfile index 7343d460..bc301511 100644 --- a/Gemfile +++ b/Gemfile @@ -18,7 +18,7 @@ gem 'jquery-rails' gem 'turbolinks' gem 'jbuilder', '~> 1.0.1' gem 'bourbon' -gem 'neat' +gem 'neat', '~> 1.8.0' gem 'haml-rails' gem 'rails_html_helpers' gem 'draper', '~> 3.0.0.pre1' diff --git a/Gemfile.lock b/Gemfile.lock index fc596837..ca0ab5aa 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -153,8 +153,8 @@ GEM multi_json (1.12.1) multi_xml (0.6.0) multipart-post (2.0.0) - neat (2.0.0) - sass (~> 3.4) + neat (1.8.0) + sass (>= 3.3) thor (~> 0.19) newrelic_rpm (3.18.1.330) nio4r (1.2.1) @@ -323,7 +323,7 @@ DEPENDENCIES jquery-rails json_spec modernizr-rails - neat + neat (~> 1.8.0) newrelic_rpm omniauth (~> 1.6.1) omniauth-github From 91a2b0fc6e54696a2d9d5ef0ae6daf0bf304ed2d Mon Sep 17 00:00:00 2001 From: Alex Coles Date: Sun, 12 Mar 2017 00:25:16 +0000 Subject: [PATCH 486/499] Update ActivitiesController spec for Parameters API --- spec/controllers/activities_controller_spec.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/spec/controllers/activities_controller_spec.rb b/spec/controllers/activities_controller_spec.rb index c353da9f..61ffd8d7 100644 --- a/spec/controllers/activities_controller_spec.rb +++ b/spec/controllers/activities_controller_spec.rb @@ -100,7 +100,7 @@ let(:params) { {activity: attributes} } before do - expect(current_event).to receive(:new_activity).with(current_user, attributes.with_indifferent_access).and_return(activity) + expect(current_event).to receive(:new_activity).with(current_user, ActionController::Parameters.new(attributes).permit!).and_return(activity) expect(activity).to receive(:save).and_return(true) end @@ -112,7 +112,7 @@ let(:params) { {activity: {x: 10}} } before do - expect(current_event).to receive(:new_activity).with(current_user, {}).and_return(activity) + expect(current_event).to receive(:new_activity).with(current_user, ActionController::Parameters.new({}).permit!).and_return(activity) expect(activity).to receive(:save).and_return(false) end @@ -135,7 +135,7 @@ let(:attributes) { {location: "Location", name: "Name", start_time: 1.day.ago.to_s, end_time: 2.days.ago.to_s} } before do - expect(activity).to receive(:update_attributes).with(attributes.with_indifferent_access).and_return(true) + expect(activity).to receive(:update_attributes).with(ActionController::Parameters.new(attributes).permit!).and_return(true) end it { is_expected.to redirect_to edit_activity_path(activity) } @@ -146,7 +146,7 @@ let(:activity) { invalid_activity } before do - expect(activity).to receive(:update_attributes).with(attributes.with_indifferent_access).and_return(false) + expect(activity).to receive(:update_attributes).with(ActionController::Parameters.new(attributes).permit!).and_return(false) end it { is_expected.to render_template(:edit) } From e9693a89857c11eefeeb87981a65757e5ae79e05 Mon Sep 17 00:00:00 2001 From: Alex Coles Date: Sun, 12 Mar 2017 00:25:16 +0000 Subject: [PATCH 487/499] Replace deprecated #before_filter calls --- app/controllers/activities_controller.rb | 4 ++-- app/controllers/application_controller.rb | 4 ++-- app/controllers/authentications_controller.rb | 2 +- app/controllers/passwords_controller.rb | 2 +- app/controllers/registrations_controller.rb | 4 ++-- app/controllers/sessions_controller.rb | 2 +- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/app/controllers/activities_controller.rb b/app/controllers/activities_controller.rb index 2de96322..23e57a3f 100644 --- a/app/controllers/activities_controller.rb +++ b/app/controllers/activities_controller.rb @@ -1,8 +1,8 @@ class ActivitiesController < ApiController respond_to :html - skip_before_filter :authenticate_user!, only: [:index, :show] - before_filter :load_resource, only: [:show, :edit, :update, :destroy] + skip_before_action :authenticate_user!, only: [:index, :show] + before_action :load_resource, only: [:show, :edit, :update, :destroy] authorize_resource only: [:edit, :update, :destroy] def index diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 30c08c07..efac8ae2 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -5,8 +5,8 @@ class ApplicationController < ActionController::Base helper_method :current_event layout :layout_by_resource - before_filter :clean_up_session - before_filter :authenticate_user! + before_action :clean_up_session + before_action :authenticate_user! rescue_from CanCan::AccessDenied, with: :rescue_access_denied rescue_from ActiveRecord::RecordNotFound, with: :rescue_record_not_found diff --git a/app/controllers/authentications_controller.rb b/app/controllers/authentications_controller.rb index 4f98faee..fa4a0fe1 100644 --- a/app/controllers/authentications_controller.rb +++ b/app/controllers/authentications_controller.rb @@ -1,5 +1,5 @@ class AuthenticationsController < ApplicationController - skip_before_filter :authenticate_user! + skip_before_action :authenticate_user! rescue_from ActionController::RedirectBackError do |exception| redirect_to edit_user_registration_path diff --git a/app/controllers/passwords_controller.rb b/app/controllers/passwords_controller.rb index 7272ed88..575ea90d 100644 --- a/app/controllers/passwords_controller.rb +++ b/app/controllers/passwords_controller.rb @@ -1,3 +1,3 @@ class PasswordsController < Devise::PasswordsController - skip_before_filter :authenticate_user! + skip_before_action :authenticate_user! end diff --git a/app/controllers/registrations_controller.rb b/app/controllers/registrations_controller.rb index c5dab019..f9c946b1 100644 --- a/app/controllers/registrations_controller.rb +++ b/app/controllers/registrations_controller.rb @@ -1,6 +1,6 @@ class RegistrationsController < Devise::RegistrationsController - skip_before_filter :authenticate_user!, only: :create - skip_before_filter :clean_up_session, only: [:new, :create] + skip_before_action :authenticate_user!, only: :create + skip_before_action :clean_up_session, only: [:new, :create] helper_method :during_oauth_flow? diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb index 01abd8b7..c743da04 100644 --- a/app/controllers/sessions_controller.rb +++ b/app/controllers/sessions_controller.rb @@ -1,3 +1,3 @@ class SessionsController < Devise::SessionsController - skip_before_filter :authenticate_user! + skip_before_action :authenticate_user! end From a3f974c8a17d48c42c4fc94f4329605b883e7b14 Mon Sep 17 00:00:00 2001 From: Alex Coles Date: Sun, 12 Mar 2017 00:30:21 +0000 Subject: [PATCH 488/499] Regenerate binstubs --- bin/rails | 2 +- bin/setup | 29 +++++++++++++++++------------ bin/update | 29 +++++++++++++++++++++++++++++ 3 files changed, 47 insertions(+), 13 deletions(-) create mode 100755 bin/update diff --git a/bin/rails b/bin/rails index 5191e692..07396602 100755 --- a/bin/rails +++ b/bin/rails @@ -1,4 +1,4 @@ #!/usr/bin/env ruby -APP_PATH = File.expand_path('../../config/application', __FILE__) +APP_PATH = File.expand_path('../config/application', __dir__) require_relative '../config/boot' require 'rails/commands' diff --git a/bin/setup b/bin/setup index acdb2c13..e620b4da 100755 --- a/bin/setup +++ b/bin/setup @@ -1,29 +1,34 @@ #!/usr/bin/env ruby require 'pathname' +require 'fileutils' +include FileUtils # path to your application root. -APP_ROOT = Pathname.new File.expand_path('../../', __FILE__) +APP_ROOT = Pathname.new File.expand_path('../../', __FILE__) -Dir.chdir APP_ROOT do +def system!(*args) + system(*args) || abort("\n== Command #{args} failed ==") +end + +chdir APP_ROOT do # This script is a starting point to setup your application. - # Add necessary setup steps to this file: + # Add necessary setup steps to this file. - puts "== Installing dependencies ==" - system "gem install bundler --conservative" - system "bundle check || bundle install" + puts '== Installing dependencies ==' + system! 'gem install bundler --conservative' + system('bundle check') || system!('bundle install') # puts "\n== Copying sample files ==" - # unless File.exist?("config/database.yml") - # system "cp config/database.yml.sample config/database.yml" + # unless File.exist?('config/database.yml') + # cp 'config/database.yml.sample', 'config/database.yml' # end puts "\n== Preparing database ==" - system "bin/rake db:setup" + system! 'bin/rails db:setup' puts "\n== Removing old logs and tempfiles ==" - system "rm -f log/*" - system "rm -rf tmp/cache" + system! 'bin/rails log:clear tmp:clear' puts "\n== Restarting application server ==" - system "touch tmp/restart.txt" + system! 'bin/rails restart' end diff --git a/bin/update b/bin/update new file mode 100755 index 00000000..a8e4462f --- /dev/null +++ b/bin/update @@ -0,0 +1,29 @@ +#!/usr/bin/env ruby +require 'pathname' +require 'fileutils' +include FileUtils + +# path to your application root. +APP_ROOT = Pathname.new File.expand_path('../../', __FILE__) + +def system!(*args) + system(*args) || abort("\n== Command #{args} failed ==") +end + +chdir APP_ROOT do + # This script is a way to update your development environment automatically. + # Add necessary update steps to this file. + + puts '== Installing dependencies ==' + system! 'gem install bundler --conservative' + system('bundle check') || system!('bundle install') + + puts "\n== Updating database ==" + system! 'bin/rails db:migrate' + + puts "\n== Removing old logs and tempfiles ==" + system! 'bin/rails log:clear tmp:clear' + + puts "\n== Restarting application server ==" + system! 'bin/rails restart' +end From a257ae1fa9ea3c185212a2c0e2c980f36fc45ced Mon Sep 17 00:00:00 2001 From: Alex Coles Date: Sun, 12 Mar 2017 00:33:48 +0000 Subject: [PATCH 489/499] Update Rails config Signed-off-by: Alex Coles --- config/application.rb | 13 +---- config/boot.rb | 2 +- config/cable.yml | 9 ++++ config/environment.rb | 2 +- config/environments/development.rb | 37 ++++++++++----- config/environments/production.rb | 41 +++++++++------- config/environments/staging.rb | 41 +++++++++------- config/environments/test.rb | 12 ++--- .../application_controller_renderer.rb | 6 +++ config/initializers/cookies_serializer.rb | 4 +- config/initializers/new_framework_defaults.rb | 23 +++++++++ config/initializers/wrap_parameters.rb | 4 +- config/puma.rb | 47 +++++++++++++++++++ config/secrets.yml | 6 +-- config/spring.rb | 6 +++ 15 files changed, 181 insertions(+), 72 deletions(-) create mode 100644 config/cable.yml create mode 100644 config/initializers/application_controller_renderer.rb create mode 100644 config/initializers/new_framework_defaults.rb create mode 100644 config/puma.rb create mode 100644 config/spring.rb diff --git a/config/application.rb b/config/application.rb index bede8302..01563e39 100644 --- a/config/application.rb +++ b/config/application.rb @@ -1,4 +1,4 @@ -require File.expand_path('../boot', __FILE__) +require_relative 'boot' require 'rails/all' @@ -11,16 +11,5 @@ class Application < Rails::Application # Settings in config/environments/* take precedence over those specified here. # Application configuration should go into files in config/initializers # -- all .rb files in that directory are automatically loaded. - - # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone. - # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC. - # config.time_zone = 'Central Time (US & Canada)' - - # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded. - # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s] - # config.i18n.default_locale = :de - - # Do not swallow errors in after_commit/after_rollback callbacks. - config.active_record.raise_in_transactional_callbacks = true end end diff --git a/config/boot.rb b/config/boot.rb index 6b750f00..30f5120d 100644 --- a/config/boot.rb +++ b/config/boot.rb @@ -1,3 +1,3 @@ -ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) +ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__) require 'bundler/setup' # Set up gems listed in the Gemfile. diff --git a/config/cable.yml b/config/cable.yml new file mode 100644 index 00000000..0bbde6f7 --- /dev/null +++ b/config/cable.yml @@ -0,0 +1,9 @@ +development: + adapter: async + +test: + adapter: async + +production: + adapter: redis + url: redis://localhost:6379/1 diff --git a/config/environment.rb b/config/environment.rb index ee8d90dc..426333bb 100644 --- a/config/environment.rb +++ b/config/environment.rb @@ -1,5 +1,5 @@ # Load the Rails application. -require File.expand_path('../application', __FILE__) +require_relative 'application' # Initialize the Rails application. Rails.application.initialize! diff --git a/config/environments/development.rb b/config/environments/development.rb index 04df9511..16ae52a7 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -9,13 +9,30 @@ # Do not eager load code on boot. config.eager_load = false - # Show full error reports and disable caching. - config.consider_all_requests_local = true - config.action_controller.perform_caching = false + # Show full error reports. + config.consider_all_requests_local = true + + # Enable/disable caching. By default caching is disabled. + if Rails.root.join('tmp/caching-dev.txt').exist? + config.action_controller.perform_caching = true + + config.cache_store = :memory_store + config.public_file_server.headers = { + 'Cache-Control' => 'public, max-age=172800' + } + else + config.action_controller.perform_caching = false + + config.cache_store = :null_store + end # Don't care if the mailer can't send. config.action_mailer.raise_delivery_errors = false + config.action_mailer.perform_caching = false + + config.action_mailer.default_url_options = { host: Settings.host } + # Print deprecation notices to the Rails logger. config.active_support.deprecation = :log @@ -27,17 +44,13 @@ # number of complex assets. config.assets.debug = true - # Asset digests allow you to set far-future HTTP expiration dates on all assets, - # yet still be able to expire them through the digest params. - config.assets.digest = true - - # Adds additional error checking when serving assets at runtime. - # Checks for improperly declared sprockets dependencies. - # Raises helpful error messages. - config.assets.raise_runtime_errors = true + # Suppress logger output for asset requests. + config.assets.quiet = true # Raises error for missing translations # config.action_view.raise_on_missing_translations = true - config.action_mailer.default_url_options = { host: Settings.host } + # Use an evented file watcher to asynchronously detect changes in source code, + # routes, locales, etc. This feature depends on the listen gem. + # config.file_watcher = ActiveSupport::EventedFileUpdateChecker end diff --git a/config/environments/production.rb b/config/environments/production.rb index 8cd881e4..7e156f97 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -14,15 +14,9 @@ config.consider_all_requests_local = false config.action_controller.perform_caching = true - # Enable Rack::Cache to put a simple HTTP cache in front of your application - # Add `rack-cache` to your Gemfile before enabling this. - # For large-scale production use, consider using a caching reverse proxy like - # NGINX, varnish or squid. - # config.action_dispatch.rack_cache = true - # Disable serving static files from the `/public` folder by default since # Apache or NGINX already handles this. - config.serve_static_files = ENV['RAILS_SERVE_STATIC_FILES'].present? + config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present? # Compress JavaScripts and CSS. config.assets.js_compressor = :uglifier @@ -31,16 +25,20 @@ # Do not fallback to assets pipeline if a precompiled asset is missed. config.assets.compile = false - # Asset digests allow you to set far-future HTTP expiration dates on all assets, - # yet still be able to expire them through the digest params. - config.assets.digest = true - # `config.assets.precompile` and `config.assets.version` have moved to config/initializers/assets.rb + # Enable serving of images, stylesheets, and JavaScripts from an asset server. + # config.action_controller.asset_host = 'http://assets.example.com' + # Specifies the header that your server uses for sending files. # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX + # Mount Action Cable outside main process or domain + # config.action_cable.mount_path = nil + # config.action_cable.url = 'wss://example.com/cable' + # config.action_cable.allowed_request_origins = [ 'http://example.com', /http:\/\/example.*/ ] + # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. # config.force_ssl = true @@ -49,16 +47,15 @@ config.log_level = :info # Prepend all log lines with the following tags. - # config.log_tags = [ :subdomain, :uuid ] - - # Use a different logger for distributed setups. - # config.logger = ActiveSupport::TaggedLogging.new(SyslogLogger.new) + config.log_tags = [:request_id] # Use a different cache store in production. # config.cache_store = :mem_cache_store - # Enable serving of images, stylesheets, and JavaScripts from an asset server. - # config.action_controller.asset_host = 'http://assets.example.com' + # Use a real queuing backend for Active Job (and separate queues per environment) + # config.active_job.queue_adapter = :resque + # config.active_job.queue_name_prefix = "activities_#{Rails.env}" + config.action_mailer.perform_caching = false # Ignore bad email addresses and do not raise email delivery errors. # Set this to true and configure the email server for immediate delivery to raise delivery errors. @@ -76,6 +73,16 @@ # Use default logging formatter so that PID and timestamp are not suppressed. config.log_formatter = ::Logger::Formatter.new + # Use a different logger for distributed setups. + # require 'syslog/logger' + # config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new 'app-name') + + if ENV['RAILS_LOG_TO_STDOUT'].present? + logger = ActiveSupport::Logger.new(STDOUT) + logger.formatter = config.log_formatter + config.logger = ActiveSupport::TaggedLogging.new(logger) + end + config.middleware.use ExceptionNotification::Rack, email: { email_prefix: '[eurucamp-activities::Exception] ', sender_address: %("Exception Notifier" <#{Settings.errors.from}>), diff --git a/config/environments/staging.rb b/config/environments/staging.rb index c0025814..3541a743 100644 --- a/config/environments/staging.rb +++ b/config/environments/staging.rb @@ -14,15 +14,9 @@ config.consider_all_requests_local = false config.action_controller.perform_caching = true - # Enable Rack::Cache to put a simple HTTP cache in front of your application - # Add `rack-cache` to your Gemfile before enabling this. - # For large-scale production use, consider using a caching reverse proxy like - # NGINX, varnish or squid. - # config.action_dispatch.rack_cache = true - # Disable serving static files from the `/public` folder by default since # Apache or NGINX already handles this. - config.serve_static_files = ENV['RAILS_SERVE_STATIC_FILES'].present? + config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present? # Compress JavaScripts and CSS. config.assets.js_compressor = :uglifier @@ -31,16 +25,20 @@ # Do not fallback to assets pipeline if a precompiled asset is missed. config.assets.compile = false - # Asset digests allow you to set far-future HTTP expiration dates on all assets, - # yet still be able to expire them through the digest params. - config.assets.digest = true - # `config.assets.precompile` and `config.assets.version` have moved to config/initializers/assets.rb + # Enable serving of images, stylesheets, and JavaScripts from an asset server. + # config.action_controller.asset_host = 'http://assets.example.com' + # Specifies the header that your server uses for sending files. # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX + # Mount Action Cable outside main process or domain + # config.action_cable.mount_path = nil + # config.action_cable.url = 'wss://example.com/cable' + # config.action_cable.allowed_request_origins = [ 'http://example.com', /http:\/\/example.*/ ] + # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. # config.force_ssl = true @@ -49,16 +47,15 @@ config.log_level = :info # Prepend all log lines with the following tags. - # config.log_tags = [ :subdomain, :uuid ] - - # Use a different logger for distributed setups. - # config.logger = ActiveSupport::TaggedLogging.new(SyslogLogger.new) + config.log_tags = [:request_id] # Use a different cache store in production. # config.cache_store = :mem_cache_store - # Enable serving of images, stylesheets, and JavaScripts from an asset server. - # config.action_controller.asset_host = 'http://assets.example.com' + # Use a real queuing backend for Active Job (and separate queues per environment) + # config.active_job.queue_adapter = :resque + # config.active_job.queue_name_prefix = "activities_#{Rails.env}" + config.action_mailer.perform_caching = false # Ignore bad email addresses and do not raise email delivery errors. # Set this to true and configure the email server for immediate delivery to raise delivery errors. @@ -76,6 +73,16 @@ # Use default logging formatter so that PID and timestamp are not suppressed. config.log_formatter = ::Logger::Formatter.new + # Use a different logger for distributed setups. + # require 'syslog/logger' + # config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new 'app-name') + + if ENV['RAILS_LOG_TO_STDOUT'].present? + logger = ActiveSupport::Logger.new(STDOUT) + logger.formatter = config.log_formatter + config.logger = ActiveSupport::TaggedLogging.new(logger) + end + config.middleware.use ExceptionNotification::Rack, email: { email_prefix: '[eurucamp-activities-staging::Exception] ', sender_address: %("Exception Notifier" <#{Settings.errors.from}>), diff --git a/config/environments/test.rb b/config/environments/test.rb index a46b51e3..7cb43f84 100644 --- a/config/environments/test.rb +++ b/config/environments/test.rb @@ -12,9 +12,11 @@ # preloads Rails for running tests, you may have to set it to true. config.eager_load = false - # Configure static file server for tests with Cache-Control for performance. - config.serve_static_files = true - config.static_cache_control = 'public, max-age=3600' + # Configure public file server for tests with Cache-Control for performance. + config.public_file_server.enabled = true + config.public_file_server.headers = { + 'Cache-Control' => 'public, max-age=3600' + } # Show full error reports and disable caching. config.consider_all_requests_local = true @@ -25,6 +27,7 @@ # Disable request forgery protection in test environment. config.action_controller.allow_forgery_protection = false + config.action_mailer.perform_caching = false # Tell Action Mailer not to deliver emails to the real world. # The :test delivery method accumulates sent emails in the @@ -32,9 +35,6 @@ config.action_mailer.delivery_method = :test config.action_mailer.default_url_options = { host: Settings.host } - # Randomize the order test cases are executed. - config.active_support.test_order = :random - # Print deprecation notices to the stderr. config.active_support.deprecation = :stderr diff --git a/config/initializers/application_controller_renderer.rb b/config/initializers/application_controller_renderer.rb new file mode 100644 index 00000000..51639b67 --- /dev/null +++ b/config/initializers/application_controller_renderer.rb @@ -0,0 +1,6 @@ +# Be sure to restart your server when you modify this file. + +# ApplicationController.renderer.defaults.merge!( +# http_host: 'example.org', +# https: false +# ) diff --git a/config/initializers/cookies_serializer.rb b/config/initializers/cookies_serializer.rb index ac5f8b66..5a6a32d3 100644 --- a/config/initializers/cookies_serializer.rb +++ b/config/initializers/cookies_serializer.rb @@ -1,3 +1,5 @@ # Be sure to restart your server when you modify this file. -Rails.application.config.action_dispatch.cookies_serializer = :marshal +# Specify a serializer for the signed and encrypted cookie jars. +# Valid options are :json, :marshal, and :hybrid. +Rails.application.config.action_dispatch.cookies_serializer = :json diff --git a/config/initializers/new_framework_defaults.rb b/config/initializers/new_framework_defaults.rb new file mode 100644 index 00000000..4415dc3f --- /dev/null +++ b/config/initializers/new_framework_defaults.rb @@ -0,0 +1,23 @@ +# Be sure to restart your server when you modify this file. +# +# This file contains migration options to ease your Rails 5.0 upgrade. +# +# Once upgraded flip defaults one by one to migrate to the new default. +# +# Read the Guide for Upgrading Ruby on Rails for more info on each option. + +# Enable per-form CSRF tokens. Previous versions had false. +Rails.application.config.action_controller.per_form_csrf_tokens = false + +# Enable origin-checking CSRF mitigation. Previous versions had false. +Rails.application.config.action_controller.forgery_protection_origin_check = false + +# Make Ruby 2.4 preserve the timezone of the receiver when calling `to_time`. +# Previous versions had false. +ActiveSupport.to_time_preserves_timezone = false + +# Require `belongs_to` associations by default. Previous versions had false. +Rails.application.config.active_record.belongs_to_required_by_default = false + +# Do not halt callback chains when a callback returns false. Previous versions had true. +ActiveSupport.halt_callback_chains_on_return_false = true diff --git a/config/initializers/wrap_parameters.rb b/config/initializers/wrap_parameters.rb index 33725e95..bbfc3961 100644 --- a/config/initializers/wrap_parameters.rb +++ b/config/initializers/wrap_parameters.rb @@ -5,10 +5,10 @@ # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array. ActiveSupport.on_load(:action_controller) do - wrap_parameters format: [:json] if respond_to?(:wrap_parameters) + wrap_parameters format: [:json] end # To enable root element in JSON for ActiveRecord objects. # ActiveSupport.on_load(:active_record) do -# self.include_root_in_json = true +# self.include_root_in_json = true # end diff --git a/config/puma.rb b/config/puma.rb new file mode 100644 index 00000000..786af492 --- /dev/null +++ b/config/puma.rb @@ -0,0 +1,47 @@ +# Puma can serve each request in a thread from an internal thread pool. +# The `threads` method setting takes two numbers a minimum and maximum. +# Any libraries that use thread pools should be configured to match +# the maximum value specified for Puma. Default is set to 5 threads for minimum +# and maximum, this matches the default thread size of Active Record. +# +threads_count = ENV.fetch('RAILS_MAX_THREADS') { 5 }.to_i +threads threads_count, threads_count + +# Specifies the `port` that Puma will listen on to receive requests, default is 3000. +# +port ENV.fetch('PORT') { 3000 } + +# Specifies the `environment` that Puma will run in. +# +environment ENV.fetch('RAILS_ENV') { 'development' } + +# Specifies the number of `workers` to boot in clustered mode. +# Workers are forked webserver processes. If using threads and workers together +# the concurrency of the application would be max `threads` * `workers`. +# Workers do not work on JRuby or Windows (both of which do not support +# processes). +# +# workers ENV.fetch("WEB_CONCURRENCY") { 2 } + +# Use the `preload_app!` method when specifying a `workers` number. +# This directive tells Puma to first boot the application and load code +# before forking the application. This takes advantage of Copy On Write +# process behavior so workers use less memory. If you use this option +# you need to make sure to reconnect any threads in the `on_worker_boot` +# block. +# +# preload_app! + +# The code in the `on_worker_boot` will be called if you are using +# clustered mode by specifying a number of `workers`. After each worker +# process is booted this block will be run, if you are using `preload_app!` +# option you will want to use this block to reconnect to any threads +# or connections that may have been created at application boot, Ruby +# cannot share connections between processes. +# +# on_worker_boot do +# ActiveRecord::Base.establish_connection if defined?(ActiveRecord) +# end + +# Allow puma to be restarted by `rails restart` command. +plugin :tmp_restart diff --git a/config/secrets.yml b/config/secrets.yml index af941eed..2e6a271e 100644 --- a/config/secrets.yml +++ b/config/secrets.yml @@ -5,16 +5,16 @@ # Make sure the secret is at least 30 characters and all random, # no regular words or you'll be exposed to dictionary attacks. -# You can use `rake secret` to generate a secure secret key. +# You can use `rails secret` to generate a secure secret key. # Make sure the secrets in this file are kept private # if you're sharing your code publicly. development: - secret_key_base: ded20a08273981cd34e9a8d7e878e0316572770cc10f0bbb6c829e86887e458a9f6c9dd9c86ac4a35524b0ea1d174312a8f828e70189b5abf88bd4087030b233 + secret_key_base: f75dadf70cc2bc0cbff56359e28cee1e869ca1e86367e9233cfce38ebb3b86f672e4bee56708dd004d1a68ce761795329ea8a4d955a02c8de4d82ee8be162c83 test: - secret_key_base: 31ad73f9b171ed04c13c6ac584e1301175c5ea1822a79b139729bf806a01bd50db87148f7d1bbe64d7e63a6c79246dc4b1953a7a7d644eb8a62f862f7e0a8bff + secret_key_base: 626305f29b7acfcf56b95898061e106412583c141fcefed2604926f7a5aeee74883f2bb44899a95397401c6acf909b20415ac0cbf14d0634d44a27a82f2f4045 # Do not keep production secrets in the repository, # instead read values from the environment. diff --git a/config/spring.rb b/config/spring.rb new file mode 100644 index 00000000..c9119b40 --- /dev/null +++ b/config/spring.rb @@ -0,0 +1,6 @@ +%w( + .ruby-version + .rbenv-vars + tmp/restart.txt + tmp/caching-dev.txt +).each { |path| Spring.watch(path) } From acc449fa69343e386793fee2fb1449cd9c655605 Mon Sep 17 00:00:00 2001 From: Alex Coles Date: Sun, 12 Mar 2017 00:57:43 +0000 Subject: [PATCH 490/499] Fix deprecated positional args in controller specs --- spec/controllers/activities_controller_spec.rb | 10 +++++----- spec/controllers/authentications_controller_spec.rb | 2 +- spec/controllers/participation_controller_spec.rb | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/spec/controllers/activities_controller_spec.rb b/spec/controllers/activities_controller_spec.rb index 61ffd8d7..fb9c062b 100644 --- a/spec/controllers/activities_controller_spec.rb +++ b/spec/controllers/activities_controller_spec.rb @@ -39,7 +39,7 @@ end describe "#edit" do - subject { get :edit, id: activity_id } + subject { get :edit, params: { id: activity_id } } before do sign_in(current_user) @@ -65,7 +65,7 @@ end describe "#show" do - subject { get :show, id: activity_id } + subject { get :show, params: { id: activity_id } } before do sign_in(current_user) @@ -89,7 +89,7 @@ end describe "#create" do - subject { post :create, params } + subject { post :create, params: params } before do sign_in(current_user) @@ -121,7 +121,7 @@ end describe "#update" do - subject { put :update, params } + subject { put :update, params: params } let(:params) { {id: activity_id, activity: attributes} } before do @@ -154,7 +154,7 @@ end describe "#destroy" do - subject { delete :destroy, {id: activity_id, confirm_delete: true} } + subject { delete :destroy, params: {id: activity_id, confirm_delete: true} } before do sign_in(current_user) diff --git a/spec/controllers/authentications_controller_spec.rb b/spec/controllers/authentications_controller_spec.rb index 57c4530b..3a7e86a6 100644 --- a/spec/controllers/authentications_controller_spec.rb +++ b/spec/controllers/authentications_controller_spec.rb @@ -56,7 +56,7 @@ end describe "#delete" do - subject(:logout_action) { delete :destroy, id: authentication_id } + subject(:logout_action) { delete :destroy, params: { id: authentication_id } } before do sign_in(current_user) diff --git a/spec/controllers/participation_controller_spec.rb b/spec/controllers/participation_controller_spec.rb index 794d9506..5636947b 100644 --- a/spec/controllers/participation_controller_spec.rb +++ b/spec/controllers/participation_controller_spec.rb @@ -15,7 +15,7 @@ describe "#create" do context "with :js format" do - subject { post :create, {activity_id: activity_id, format: :js} } + subject { post :create, params: {activity_id: activity_id, format: :js} } before do sign_in(current_user) @@ -60,7 +60,7 @@ describe "#destroy" do context "with :js format" do - subject { delete :destroy, {activity_id: activity_id, format: :js} } + subject { delete :destroy, params: { activity_id: activity_id }, format: :js } before do sign_in(current_user) From 84cac4f97d9b1407926a2d1fd852509d90c8ef65 Mon Sep 17 00:00:00 2001 From: Alex Coles Date: Sun, 12 Mar 2017 01:07:23 +0000 Subject: [PATCH 491/499] Replace deprecated redirect_to :back, add fallback --- app/controllers/authentications_controller.rb | 2 +- app/controllers/participations_controller.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/controllers/authentications_controller.rb b/app/controllers/authentications_controller.rb index fa4a0fe1..db26c69b 100644 --- a/app/controllers/authentications_controller.rb +++ b/app/controllers/authentications_controller.rb @@ -18,7 +18,7 @@ def create current_user.apply_provider_handle(omniauth) current_user.save flash[:notice] = 'Connected successfully' - redirect_to :back + redirect_back(fallback_location: edit_user_registration_url) else user = User.new user.apply_omniauth(omniauth) diff --git a/app/controllers/participations_controller.rb b/app/controllers/participations_controller.rb index db1ca008..70684bde 100644 --- a/app/controllers/participations_controller.rb +++ b/app/controllers/participations_controller.rb @@ -8,7 +8,7 @@ def create end respond_with(current_activity.reload, @participation) do |format| - format.html { redirect_to :back } + format.html { redirect_back(fallback_location: activity_url(current_activity)) } end end From 8c9e4cb38a221b28e02110d79897a2805911a152 Mon Sep 17 00:00:00 2001 From: Alex Coles Date: Tue, 20 Mar 2018 11:55:09 +0000 Subject: [PATCH 492/499] Remove Cloudfile used by defunct cloud platform Shelly Cloud is no in operation. --- Cloudfile | 22 ---------------------- 1 file changed, 22 deletions(-) delete mode 100644 Cloudfile diff --git a/Cloudfile b/Cloudfile deleted file mode 100644 index c279aefb..00000000 --- a/Cloudfile +++ /dev/null @@ -1,22 +0,0 @@ -eurucamp-activities-staging: - ruby_version: 2.1.5 - environment: staging # RAILS_ENV - domains: - - eurucamp-activities-staging.shellyapp.com - servers: - app1: - size: small - thin: 2 - databases: - - postgresql -eurucamp-activities-production: - ruby_version: 2.1.5 - environment: production # RAILS_ENV - domains: - - rubyweek.org - servers: - app1: - size: small - thin: 2 - databases: - - postgresql From 2ecf0bef0cf83b944af7e2abbe3edf4dc6c771b3 Mon Sep 17 00:00:00 2001 From: Alex Coles Date: Tue, 20 Mar 2018 11:35:37 +0000 Subject: [PATCH 493/499] Update event branding, strings for Isle of Ruby --- README.md | 12 +++++------- app/assets/images/shared/IoR_illustration.svg | 1 + app/assets/images/shared/eurucamp-2015.svg | 16 ---------------- app/views/activities/_activity.html.haml | 2 +- app/views/activities/show.html.haml | 2 +- app/views/partials/_footer.html.haml | 7 ++----- app/views/partials/_header.html.haml | 4 ++-- config/application.yml | 18 +++++++++--------- config/environments/production.rb | 2 +- config/environments/staging.rb | 2 +- config/locales/en.yml | 15 ++++++--------- spec/helpers/application_helper_spec.rb | 2 +- spec/models/event_spec.rb | 4 ++-- 13 files changed, 32 insertions(+), 55 deletions(-) create mode 100644 app/assets/images/shared/IoR_illustration.svg delete mode 100644 app/assets/images/shared/eurucamp-2015.svg diff --git a/README.md b/README.md index 91ff4816..77f8066f 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# eurucamp Activities [![License](http://img.shields.io/:license-AGPL-0030c8.svg)](COPYRIGHT) [![Build Status](https://travis-ci.org/eurucamp/eurucamp-activities.png?branch=master)](https://travis-ci.org/eurucamp/eurucamp-activities) +# eurucamp Activities [![License](http://img.shields.io/:license-AGPL-0030c8.svg)](COPYRIGHT) [![Build Status](https://travis-ci.org/isleofruby/isleofruby-activities.png?branch=master)](https://travis-ci.org/isleofruby/isleofruby-activities) The Activities app is a small application to allow attendees to organize and plan small event in and around a conference. Users create events or sign up to created ones. Signup works through GitHub or Twitter. @@ -6,27 +6,25 @@ The app is mobile friendly and easy to run on a free Heroku account. ## Donationware -The app was created by members on the Ruby Berlin e.V. on their free time as a community effort for the eurucamp conference. Ruby Berlin is the body behind RailsGirls Berlin and Hamburg, eurucamp and JRubyConf.eu. +The app was created by members on the Ruby Berlin e.V. on their free time as a community effort initially for the eurucamp conference. Ruby Berlin is the body behind RailsGirls Berlin and Hamburg, eurucamp and JRubyConf.eu. -If you end up using the app, please get in [contact](mailto:info@eurucamp.org) so that we know. Also, spread the word about our [projects](http://rubyberlin.org). Also, consider [donating](https://www.betterplace.org/en/organisations/ruby-berlin/), especially, if you run a commercial conference. We are a registered non-profit, donations are tax deducible. Betterplace handles all paperwork - if in doubt, send us a mail. +If you end up using the app, please get in [contact](mailto:info@isleofruby.org) so that we know. Also, spread the word about our [projects](http://rubyberlin.org). Also, consider [donating](https://www.betterplace.org/en/organisations/ruby-berlin/), especially, if you run a commercial conference. We are a registered non-profit, donations are tax deducible. Betterplace handles all paperwork - if in doubt, send us a mail. If you cannot or don't want to donate - use it, it's free. ## Logo -Don't use the eurucamp logo for your instance to avoid confusion. +Don't use the Isle of Ruby logo for your instance to avoid confusion. ## Examples -An instance of the app can be seen running at the [rubyweek page](http://rubyweek.org). - Screenshot: ![The activities app](screenshot.png) ## Running the app on Heroku To deploy the app, you need the following: -* Clone this repository: `https://github.com/eurucamp/eurucamp-activities-2013/` +* Clone this repository: `https://github.com/isleofruby/isleofruby-activities/` * An account and a created application at Heroku. * A registered Twitter application. Go [here](https://apps.twitter.com/). diff --git a/app/assets/images/shared/IoR_illustration.svg b/app/assets/images/shared/IoR_illustration.svg new file mode 100644 index 00000000..6a4ef1dc --- /dev/null +++ b/app/assets/images/shared/IoR_illustration.svg @@ -0,0 +1 @@ +Ior_illustration \ No newline at end of file diff --git a/app/assets/images/shared/eurucamp-2015.svg b/app/assets/images/shared/eurucamp-2015.svg deleted file mode 100644 index 5d496f2d..00000000 --- a/app/assets/images/shared/eurucamp-2015.svg +++ /dev/null @@ -1,16 +0,0 @@ - - - - eurucamp-2015 - Created with Sketch. - - - - - - - - - - - \ No newline at end of file diff --git a/app/views/activities/_activity.html.haml b/app/views/activities/_activity.html.haml index e5eff539..c94b13a7 100644 --- a/app/views/activities/_activity.html.haml +++ b/app/views/activities/_activity.html.haml @@ -11,7 +11,7 @@ %p.creator = activity.creator_name - if activity.requires_eurucamp_ticket - = image_tag 'shared/eurucamp-2015.svg', + = image_tag 'shared/isleofruby-2015.svg', class: 'requires_eurucamp_ticket', alt: t('activities.requires_eurucamp_ticket'), title: t('activities.requires_eurucamp_ticket') diff --git a/app/views/activities/show.html.haml b/app/views/activities/show.html.haml index 710abfef..076759b8 100644 --- a/app/views/activities/show.html.haml +++ b/app/views/activities/show.html.haml @@ -4,7 +4,7 @@ = @activity.name - if @activity.requires_eurucamp_ticket %br - = image_tag 'shared/eurucamp-2015.svg', + = image_tag 'shared/isleofruby-2015.svg', class: 'requires_eurucamp_ticket', alt: t('activities.requires_eurucamp_ticket'), title: t('activities.requires_eurucamp_ticket') diff --git a/app/views/partials/_footer.html.haml b/app/views/partials/_footer.html.haml index 27b283ff..b30afd89 100644 --- a/app/views/partials/_footer.html.haml +++ b/app/views/partials/_footer.html.haml @@ -4,8 +4,5 @@ %nav %ul %li - %a(href="http://eurucamp.org" title="#{t("footer_nav.eurucamp.title")}") - = t("footer_nav.eurucamp.label") - %li - %a(href="http://jrubyconf.eu" title="#{t("footer_nav.jrubyconf.title")}") - = t("footer_nav.jrubyconf.label") + %a(href="https://isleofruby.org" title="#{t("footer_nav.isleofruby.title")}") + = t("footer_nav.isleofruby.label") diff --git a/app/views/partials/_header.html.haml b/app/views/partials/_header.html.haml index 84bc8cbd..453aa1f3 100644 --- a/app/views/partials/_header.html.haml +++ b/app/views/partials/_header.html.haml @@ -5,5 +5,5 @@ -# the logo, link home %h1 = link_to "/" do - rubyweek - %h2 Events around eurucamp and JRubyConf + Isle of Ruby + %h2 Events around Isle of Ruby diff --git a/config/application.yml b/config/application.yml index bb479d7a..7c4f8617 100644 --- a/config/application.yml +++ b/config/application.yml @@ -1,19 +1,19 @@ defaults: &defaults event: - name: "rubyweek" + name: "Isle of Ruby" start_time: 2015-07-27 00:00:00 end_time: 2015-08-09 23:59:59 - host: "rubyweek.org" + host: "activities.isleofruby.org" mailers: - from: "activities@eurucamp.org" + from: "activities@isleofruby.org" errors: - from: "exception@eurucamp.org" + from: "exception@isleofruby.org" to: - - "cfp-errors@eurucamp.org" + - "cfp-errors@isleofruby.org" seo: - title: "rubyweek" - author: "eurucamp team" - description: "List of events around eurucamp and JRubyConf" + title: "Isle of Ruby" + author: "Isle of Ruby team" + description: "List of events at and around Isle of Ruby" development: <<: *defaults @@ -24,7 +24,7 @@ test: staging: <<: *defaults - host: "eurucamp-activities-staging.shellyapp.com" + host: "TBD" production: <<: *defaults diff --git a/config/environments/production.rb b/config/environments/production.rb index 7e156f97..683e487b 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -84,7 +84,7 @@ end config.middleware.use ExceptionNotification::Rack, email: { - email_prefix: '[eurucamp-activities::Exception] ', + email_prefix: '[isleofruby-activities::Exception] ', sender_address: %("Exception Notifier" <#{Settings.errors.from}>), exception_recipients: Settings.errors.to } diff --git a/config/environments/staging.rb b/config/environments/staging.rb index 3541a743..a506e111 100644 --- a/config/environments/staging.rb +++ b/config/environments/staging.rb @@ -84,7 +84,7 @@ end config.middleware.use ExceptionNotification::Rack, email: { - email_prefix: '[eurucamp-activities-staging::Exception] ', + email_prefix: '[isleofruby-activities-staging::Exception] ', sender_address: %("Exception Notifier" <#{Settings.errors.from}>), exception_recipients: Settings.errors.to } diff --git a/config/locales/en.yml b/config/locales/en.yml index bb77c03e..10383b2e 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -15,7 +15,7 @@ en: user_nav: back_home: - label: Back to rubyweek activities + label: Back to Isle of Ruby activities title: Go back to front page account: label: My Account @@ -81,7 +81,7 @@ en: label_short: End location: Location required: Required - requires_eurucamp_ticket: "During the eurucamp, participants need a ticket" + requires_eurucamp_ticket: "During the Isle of Ruby participants need a ticket" participants: Participants organized_by: "Organized by %{name}" room_left: @@ -132,7 +132,7 @@ en: label: Requirements hint: "What to bring, to wear etc." requires_eurucamp_ticket: - hint: "During the eurucamp, participants need a ticket" + hint: "During the Isle of Ruby participants need a ticket" image_url: label: URL for an image hint: Upload an image to e.g. imgur.com and link it here @@ -153,9 +153,6 @@ en: policy: label: Policy title: Policy and Code of Conduct - eurucamp: - label: eurucamp - title: Head over to the eurucamp site - jrubyconf: - label: JRubyConf - title: Head over to the JRubyConf site + isleofruby: + label: Isle of Ruby + title: Head over to Isle of Ruby site diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb index 1444b82a..5352949d 100644 --- a/spec/helpers/application_helper_spec.rb +++ b/spec/helpers/application_helper_spec.rb @@ -6,7 +6,7 @@ let(:size) { 64 } context 'with a Twitter account' do - let(:user) { User.new(twitter_handle: 'eurucamp') } + let(:user) { User.new(twitter_handle: 'isleofruby') } it { is_expected.to match %r{//twitter.com/#{user.twitter_handle}/profile_image} } end diff --git a/spec/models/event_spec.rb b/spec/models/event_spec.rb index dfcccd76..6744099c 100644 --- a/spec/models/event_spec.rb +++ b/spec/models/event_spec.rb @@ -4,12 +4,12 @@ let(:start_time) { Date.parse("2012-10-10") } let(:end_time) { Date.parse("2012-12-14") } - subject(:event) { Event.new("eurucamp", start_time, end_time) } + subject(:event) { Event.new("isleofruby", start_time, end_time) } let(:proxy) { double(:proxy) } describe "#new" do - its(:name) { should == "eurucamp" } + its(:name) { should == "isleofruby" } its(:start_time) { should == start_time } its(:end_time) { should == end_time } end From 7a3a601123d082e287c737d92ef8d3b1609c4525 Mon Sep 17 00:00:00 2001 From: Alex Coles Date: Wed, 21 Mar 2018 19:00:51 +0000 Subject: [PATCH 494/499] Force SSL in staging, production --- config/environments/production.rb | 2 +- config/environments/staging.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/config/environments/production.rb b/config/environments/production.rb index 683e487b..ff29a518 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -40,7 +40,7 @@ # config.action_cable.allowed_request_origins = [ 'http://example.com', /http:\/\/example.*/ ] # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. - # config.force_ssl = true + config.force_ssl = true # Use the lowest log level to ensure availability of diagnostic information # when problems arise. diff --git a/config/environments/staging.rb b/config/environments/staging.rb index a506e111..4c9aa3ec 100644 --- a/config/environments/staging.rb +++ b/config/environments/staging.rb @@ -40,7 +40,7 @@ # config.action_cable.allowed_request_origins = [ 'http://example.com', /http:\/\/example.*/ ] # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. - # config.force_ssl = true + config.force_ssl = true # Use the lowest log level to ensure availability of diagnostic information # when problems arise. From 89b0b6c0256904c2a73393d20670ff599c1c0e55 Mon Sep 17 00:00:00 2001 From: Alex Coles Date: Wed, 28 Mar 2018 09:51:16 +0100 Subject: [PATCH 495/499] Update dates for Isle of Ruby 2018 --- config/application.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config/application.yml b/config/application.yml index 7c4f8617..da6a863f 100644 --- a/config/application.yml +++ b/config/application.yml @@ -1,8 +1,8 @@ defaults: &defaults event: name: "Isle of Ruby" - start_time: 2015-07-27 00:00:00 - end_time: 2015-08-09 23:59:59 + start_time: 2017-04-11 00:00:00 + end_time: 2017-04-16 23:59:59 host: "activities.isleofruby.org" mailers: from: "activities@isleofruby.org" From 7449ea89d6db1df9667aa5d20321c0b720870b67 Mon Sep 17 00:00:00 2001 From: Guillermo Tirelli Date: Tue, 3 Apr 2018 22:03:45 +0100 Subject: [PATCH 496/499] Target Ruby 2.4.1 --- .ruby-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ruby-version b/.ruby-version index 0bee604d..005119ba 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -2.3.3 +2.4.1 From c55907450aaf2c65e8ecd629b3aba85d1869c570 Mon Sep 17 00:00:00 2001 From: Guillermo Tirelli Date: Tue, 3 Apr 2018 22:16:07 +0100 Subject: [PATCH 497/499] Add title images --- app/assets/images/activities/activities.svg | 1 + app/assets/images/activities/create.svg | 1 + app/assets/images/activities/edit.svg | 1 + app/assets/images/activities/register.svg | 1 + app/assets/images/activities/schedule.svg | 1 + app/assets/images/activities/sign in.svg | 1 + 6 files changed, 6 insertions(+) create mode 100644 app/assets/images/activities/activities.svg create mode 100644 app/assets/images/activities/create.svg create mode 100644 app/assets/images/activities/edit.svg create mode 100644 app/assets/images/activities/register.svg create mode 100644 app/assets/images/activities/schedule.svg create mode 100644 app/assets/images/activities/sign in.svg diff --git a/app/assets/images/activities/activities.svg b/app/assets/images/activities/activities.svg new file mode 100644 index 00000000..f559d4da --- /dev/null +++ b/app/assets/images/activities/activities.svg @@ -0,0 +1 @@ +actrivities \ No newline at end of file diff --git a/app/assets/images/activities/create.svg b/app/assets/images/activities/create.svg new file mode 100644 index 00000000..b11ff7bd --- /dev/null +++ b/app/assets/images/activities/create.svg @@ -0,0 +1 @@ +create \ No newline at end of file diff --git a/app/assets/images/activities/edit.svg b/app/assets/images/activities/edit.svg new file mode 100644 index 00000000..65f805a3 --- /dev/null +++ b/app/assets/images/activities/edit.svg @@ -0,0 +1 @@ +edit \ No newline at end of file diff --git a/app/assets/images/activities/register.svg b/app/assets/images/activities/register.svg new file mode 100644 index 00000000..977ffdbb --- /dev/null +++ b/app/assets/images/activities/register.svg @@ -0,0 +1 @@ +register \ No newline at end of file diff --git a/app/assets/images/activities/schedule.svg b/app/assets/images/activities/schedule.svg new file mode 100644 index 00000000..e3db7879 --- /dev/null +++ b/app/assets/images/activities/schedule.svg @@ -0,0 +1 @@ +schedule \ No newline at end of file diff --git a/app/assets/images/activities/sign in.svg b/app/assets/images/activities/sign in.svg new file mode 100644 index 00000000..f068f193 --- /dev/null +++ b/app/assets/images/activities/sign in.svg @@ -0,0 +1 @@ +sign in \ No newline at end of file From 9fc0b7fe9d14ec49b9720e3bfd6076c96fa2bd5b Mon Sep 17 00:00:00 2001 From: Alex Coles Date: Wed, 4 Apr 2018 18:11:45 +0100 Subject: [PATCH 498/499] Upgrade Bourbon to latest, 5.0.0; Remove neat --- Gemfile | 1 - Gemfile.lock | 18 +++++++++++------- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/Gemfile b/Gemfile index bc301511..63082458 100644 --- a/Gemfile +++ b/Gemfile @@ -18,7 +18,6 @@ gem 'jquery-rails' gem 'turbolinks' gem 'jbuilder', '~> 1.0.1' gem 'bourbon' -gem 'neat', '~> 1.8.0' gem 'haml-rails' gem 'rails_html_helpers' gem 'draper', '~> 3.0.0.pre1' diff --git a/Gemfile.lock b/Gemfile.lock index ca0ab5aa..0bda513f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -56,7 +56,7 @@ GEM rack (>= 0.9.0) binding_of_caller (0.7.2) debug_inspector (>= 0.0.1) - bourbon (4.3.3) + bourbon (5.0.0) sass (~> 3.4) thor (~> 0.19) builder (3.2.3) @@ -108,6 +108,7 @@ GEM railties (>= 3.0.0) faraday (0.11.0) multipart-post (>= 1.2, < 3) + ffi (1.9.23) foreman (0.83.0) thor (~> 0.19.1) globalid (0.3.7) @@ -153,9 +154,6 @@ GEM multi_json (1.12.1) multi_xml (0.6.0) multipart-post (2.0.0) - neat (1.8.0) - sass (>= 3.3) - thor (~> 0.19) newrelic_rpm (3.18.1.330) nio4r (1.2.1) nokogiri (1.7.0.1) @@ -221,6 +219,9 @@ GEM thor (>= 0.18.1, < 2.0) raindrops (0.17.0) rake (12.0.0) + rb-fsevent (0.10.3) + rb-inotify (0.9.10) + ffi (>= 0.5.0, < 2) redcarpet (3.4.0) request_store (1.3.2) responders (2.3.0) @@ -255,7 +256,11 @@ GEM rspec-support (3.5.0) ruby_parser (3.8.4) sexp_processor (~> 4.1) - sass (3.4.23) + sass (3.5.6) + sass-listen (~> 4.0.0) + sass-listen (4.0.0) + rb-fsevent (~> 0.9, >= 0.9.4) + rb-inotify (~> 0.9, >= 0.9.7) sass-rails (5.0.6) railties (>= 4.0.0, < 6) sass (~> 3.1) @@ -323,7 +328,6 @@ DEPENDENCIES jquery-rails json_spec modernizr-rails - neat (~> 1.8.0) newrelic_rpm omniauth (~> 1.6.1) omniauth-github @@ -347,4 +351,4 @@ DEPENDENCIES unicorn BUNDLED WITH - 1.14.3 + 1.16.1 From 506df242c2b726f13032157af9d42e9c50492e6c Mon Sep 17 00:00:00 2001 From: Alex Coles Date: Mon, 2 Apr 2018 17:33:53 +0100 Subject: [PATCH 499/499] [WIP] Update branding / L&F for Isle of Ruby 2018 --- app/assets/images/hero/IoR_illustration.svg | 1 + app/assets/images/hero/IoR_text.svg | 1 + app/assets/images/layout/bg.png | Bin 0 -> 59586 bytes app/assets/stylesheets/_base.sass | 69 --- app/assets/stylesheets/_base.scss | 75 +++ app/assets/stylesheets/_fonts.scss | 0 .../stylesheets/_pickadate_overrides.sass | 50 -- app/assets/stylesheets/_settings.sass | 41 -- app/assets/stylesheets/_settings.scss | 7 + app/assets/stylesheets/_utils.sass | 5 - app/assets/stylesheets/_utils.scss | 5 + app/assets/stylesheets/application.css.scss | 18 +- app/assets/stylesheets/base/_base.scss | 14 + app/assets/stylesheets/base/_buttons.scss | 43 ++ app/assets/stylesheets/base/_forms.scss | 82 +++ app/assets/stylesheets/base/_layout.scss | 50 ++ app/assets/stylesheets/base/_lists.scss | 19 + app/assets/stylesheets/base/_media.scss | 9 + app/assets/stylesheets/base/_tables.scss | 45 ++ app/assets/stylesheets/base/_typography.scss | 51 ++ app/assets/stylesheets/base/_variables.scss | 56 ++ .../stylesheets/partials/_activities.sass | 15 - .../stylesheets/partials/_activities.scss | 103 ++++ app/assets/stylesheets/partials/_footer.sass | 8 - app/assets/stylesheets/partials/_footer.scss | 96 ++++ app/assets/stylesheets/partials/_header.sass | 29 - app/assets/stylesheets/partials/_hero.scss | 95 ++++ app/assets/stylesheets/partials/_home.scss | 84 +++ app/assets/stylesheets/partials/_layout.sass | 4 - .../stylesheets/partials/_schedule.scss | 3 + app/assets/stylesheets/partials/_user.sass | 109 ---- .../partials/activities/_filters.sass | 131 ----- .../partials/activities/_filters.scss | 165 ++++++ .../partials/activities/_form.sass | 101 ---- .../partials/activities/_form.scss | 136 +++++ .../partials/activities/_index.sass | 225 -------- .../partials/activities/_index.scss | 306 +++++++++++ .../partials/activities/_shared.sass | 23 - .../partials/activities/_shared.scss | 28 + .../partials/activities/_show.sass | 139 ----- .../partials/activities/_show.scss | 190 +++++++ .../stylesheets/partials/user/_edit.sass | 3 - .../stylesheets/partials/user/_edit.scss | 4 + app/assets/stylesheets/utils/_animations.sass | 35 -- app/assets/stylesheets/utils/_buttons.sass | 128 ----- app/assets/stylesheets/utils/_fonts.sass | 29 - app/assets/stylesheets/utils/_forms.sass | 103 ---- .../stylesheets/utils/_placeholders.sass | 63 --- .../stylesheets/vendor/animate-custom.scss | 127 +++++ app/assets/stylesheets/vendor/normalize.css | 504 ++++++++++++++++++ app/views/activities/_activity_day.html.haml | 4 +- app/views/activities/edit.html.haml | 29 +- app/views/activities/index.html.haml | 2 +- app/views/activities/new.html.haml | 8 +- app/views/activities/show.html.haml | 113 ++-- app/views/layouts/application.html.haml | 15 +- app/views/partials/_header.html.haml | 15 +- app/views/partials/_layout_head.html.haml | 2 - 58 files changed, 2402 insertions(+), 1413 deletions(-) create mode 100644 app/assets/images/hero/IoR_illustration.svg create mode 100644 app/assets/images/hero/IoR_text.svg create mode 100644 app/assets/images/layout/bg.png delete mode 100644 app/assets/stylesheets/_base.sass create mode 100644 app/assets/stylesheets/_base.scss create mode 100644 app/assets/stylesheets/_fonts.scss delete mode 100644 app/assets/stylesheets/_pickadate_overrides.sass delete mode 100644 app/assets/stylesheets/_settings.sass create mode 100644 app/assets/stylesheets/_settings.scss delete mode 100644 app/assets/stylesheets/_utils.sass create mode 100644 app/assets/stylesheets/_utils.scss create mode 100644 app/assets/stylesheets/base/_base.scss create mode 100644 app/assets/stylesheets/base/_buttons.scss create mode 100644 app/assets/stylesheets/base/_forms.scss create mode 100644 app/assets/stylesheets/base/_layout.scss create mode 100644 app/assets/stylesheets/base/_lists.scss create mode 100644 app/assets/stylesheets/base/_media.scss create mode 100644 app/assets/stylesheets/base/_tables.scss create mode 100644 app/assets/stylesheets/base/_typography.scss create mode 100644 app/assets/stylesheets/base/_variables.scss delete mode 100644 app/assets/stylesheets/partials/_activities.sass create mode 100644 app/assets/stylesheets/partials/_activities.scss delete mode 100644 app/assets/stylesheets/partials/_footer.sass create mode 100644 app/assets/stylesheets/partials/_footer.scss delete mode 100644 app/assets/stylesheets/partials/_header.sass create mode 100644 app/assets/stylesheets/partials/_hero.scss create mode 100644 app/assets/stylesheets/partials/_home.scss delete mode 100644 app/assets/stylesheets/partials/_layout.sass create mode 100644 app/assets/stylesheets/partials/_schedule.scss delete mode 100644 app/assets/stylesheets/partials/_user.sass delete mode 100644 app/assets/stylesheets/partials/activities/_filters.sass create mode 100644 app/assets/stylesheets/partials/activities/_filters.scss delete mode 100644 app/assets/stylesheets/partials/activities/_form.sass create mode 100644 app/assets/stylesheets/partials/activities/_form.scss delete mode 100644 app/assets/stylesheets/partials/activities/_index.sass create mode 100644 app/assets/stylesheets/partials/activities/_index.scss delete mode 100644 app/assets/stylesheets/partials/activities/_shared.sass create mode 100644 app/assets/stylesheets/partials/activities/_shared.scss delete mode 100644 app/assets/stylesheets/partials/activities/_show.sass create mode 100644 app/assets/stylesheets/partials/activities/_show.scss delete mode 100644 app/assets/stylesheets/partials/user/_edit.sass create mode 100644 app/assets/stylesheets/partials/user/_edit.scss delete mode 100644 app/assets/stylesheets/utils/_animations.sass delete mode 100644 app/assets/stylesheets/utils/_buttons.sass delete mode 100644 app/assets/stylesheets/utils/_fonts.sass delete mode 100644 app/assets/stylesheets/utils/_forms.sass delete mode 100644 app/assets/stylesheets/utils/_placeholders.sass create mode 100644 app/assets/stylesheets/vendor/animate-custom.scss create mode 100644 app/assets/stylesheets/vendor/normalize.css diff --git a/app/assets/images/hero/IoR_illustration.svg b/app/assets/images/hero/IoR_illustration.svg new file mode 100644 index 00000000..6a4ef1dc --- /dev/null +++ b/app/assets/images/hero/IoR_illustration.svg @@ -0,0 +1 @@ +Ior_illustration \ No newline at end of file diff --git a/app/assets/images/hero/IoR_text.svg b/app/assets/images/hero/IoR_text.svg new file mode 100644 index 00000000..21332d9a --- /dev/null +++ b/app/assets/images/hero/IoR_text.svg @@ -0,0 +1 @@ +IoR_text \ No newline at end of file diff --git a/app/assets/images/layout/bg.png b/app/assets/images/layout/bg.png new file mode 100644 index 0000000000000000000000000000000000000000..4ac33ebfaea5ab4717754c74a82f99aa5c14dc6e GIT binary patch literal 59586 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 B>7xJu literal 0 HcmV?d00001 diff --git a/app/assets/stylesheets/_base.sass b/app/assets/stylesheets/_base.sass deleted file mode 100644 index a671ab8d..00000000 --- a/app/assets/stylesheets/_base.sass +++ /dev/null @@ -1,69 +0,0 @@ -html - background: $white - -body - +custom-sans - -a, a:visited - color: $blue - text-decoration: none - border-bottom: 1px dotted $blue - +transition(all 0.25s) - -a:hover - border: none - +transition(all 0.25s) - -#notifications - div - background: transparentize($green, 0.2) - color: $white - text-align: center - z-index: 9999 - position: fixed - top: 1em - left: 1em - right: 1em - padding: 0.75em 0 - +custom-sans(bold) - border-radius: 3px - +animation-delay(0.25s) - @extend %animated-bounce-in-down - - &.error, &.alert - background: transparentize($red, 0.2) - - &.hide - div - @extend %animated-bounce-out-up - -#main - position: relative - -article - @extend %clearfix - - header - text-align: center - margin: 0 0 4em 0 - position: relative - - h2 - +custom-sans(light) - font-size: 2em - margin: 0 - @extend %ornamental-divider-after - - &:after - margin-bottom: 0.25em - - .meta - text-transform: uppercase - @extend %smaller-text - - strong - +custom-sans(medium) - - &.divider - +custom-sans(bold) - margin: 0 0.5em diff --git a/app/assets/stylesheets/_base.scss b/app/assets/stylesheets/_base.scss new file mode 100644 index 00000000..39adc009 --- /dev/null +++ b/app/assets/stylesheets/_base.scss @@ -0,0 +1,75 @@ +body { + background-image: image-url('layout/bg.png'); + background-size: 20vh; +} + + +.page-section { + &__wrapper { + width: 100vw; + min-height: calc(100vh - 50px); + display: flex; + flex-direction: column; + flex-wrap: wrap; + align-content: center; + align-items: stretch; + @media (min-width: $mobile-tablet-landscape){ + flex-direction: row; + justify-content: space-between; + } + @media (min-width: $desktop-medium) { + max-width: $desktop-medium; + margin: 0 auto; + } + } + + &__image { + text-align: center; + padding: 2rem 0; + @media (min-width: $mobile-tablet-landscape){ + display: flex; + align-content: center; + padding: 0; + flex-wrap: wrap; + justify-content: space-between; + width: 10vw; + align-items: stretch; + } + .page-section__image--horizontal { + max-width: 90%; + @media (min-width: $mobile-tablet-portrait) { + max-width: 60%; + } + @media (min-width: $mobile-tablet-landscape){ + display: none; + } + } + .page-section__image--vertical { + width: 70%; + display: none; + align-self: flex-start; + padding: 8rem 0 4rem; // same as inner + @media (min-width: $mobile-tablet-landscape){ + display: block; + } + } + } + + &__inner { + width: 100vw; + padding: 2rem; + @media (min-width: $mobile-tablet-landscape){ + width: 90vw; + max-width: 1000px; + padding: 8rem 2rem 5rem; + } + } +} + +.page-section--sub-page { + .page-section__image { + @media (max-width: $mobile-tablet-landscape){ + margin-top: 140px; //page-header-mobile-sky height + } + } +} diff --git a/app/assets/stylesheets/_fonts.scss b/app/assets/stylesheets/_fonts.scss new file mode 100644 index 00000000..e69de29b diff --git a/app/assets/stylesheets/_pickadate_overrides.sass b/app/assets/stylesheets/_pickadate_overrides.sass deleted file mode 100644 index d1f75969..00000000 --- a/app/assets/stylesheets/_pickadate_overrides.sass +++ /dev/null @@ -1,50 +0,0 @@ -.picker - z-index: 1001 !important - -.picker__input.picker__input--active - border: 1px solid $blue !important - -.picker__box - border-radius: 0 !important - -.picker__day--highlighted:hover, -.picker__day--infocus:hover, -.picker__day--outfocus:hover - color: $white !important - background: $blue !important - -.picker--focused .picker__day--highlighted - background: $green !important - color: $white !important - border: none !important - -.picker__day--disabled:hover, -.picker--focused .picker__day--disabled - border: none !important - -.picker__day--disabled:hover, -.picker--focused .picker__day--disabled, - background: $white !important - color: $light-gray !important - font-weight: 100 !important - -.picker__day - color: $black !important - font-weight: 500 !important - background: $extra-light-gray !important - -.picker__list-item - border-color: $light-gray !important - -.picker__list-item:hover - background: $blue !important - border-color: $blue !important - color: $white !important - -.picker__list-item--selected, -.picker__list-item--selected:hover - color: $white !important - background: $green !important - -.picker__list - padding-bottom: 0 !important diff --git a/app/assets/stylesheets/_settings.sass b/app/assets/stylesheets/_settings.sass deleted file mode 100644 index 9870485b..00000000 --- a/app/assets/stylesheets/_settings.sass +++ /dev/null @@ -1,41 +0,0 @@ -@import "neat-helpers" - -// DEV -$visual-grid: false -$visual-grid-opacity: 0.2 -$visual-grid-index: front -$visual-grid-color: blue - -// grid set-up -$column: 225px -$gutter: 0px -$padding: 20px -$grid-columns: 6 -$min-width: em(900) - -// breakpoints -$low-end: min-width 160px 1 -$mid-range: min-width 240px 1 -$smartphone-portrait: min-width 320px 2 -$smartphone-landscape: min-width 480px 2 -$tablet-lite: min-width 640px 4 -$tablet-lite-plus: min-width 750px 4 -$tablet-portrait: min-width 768px 6 -$tablet-portrait-plus: min-width 860px 6 - -// colors -$red: #e55924 -$blue: #0ca9c4 -$gray: #5b5e61 -$purple: #671990 - -$green: #15d701 -$yellow: #ffce00 - -$black: #222222 -$extra-dark-gray: #2c2c2c -$darker-gray: #797979 -$medium-gray: #8a8a8a -$light-gray: darken(#e2e2e2, 10) -$extra-light-gray: darken(#f1f1f1, 5) -$white: #ffffff diff --git a/app/assets/stylesheets/_settings.scss b/app/assets/stylesheets/_settings.scss new file mode 100644 index 00000000..c83c1192 --- /dev/null +++ b/app/assets/stylesheets/_settings.scss @@ -0,0 +1,7 @@ +// Colours +$light-blue: #CEEBFB; +$mid-blue: #A3D6F5; +$dark-blue: #66A7C5; +$red: #EE3233; +$light-grey: #F0ECEB; +$dark-grey: #6C7476; diff --git a/app/assets/stylesheets/_utils.sass b/app/assets/stylesheets/_utils.sass deleted file mode 100644 index c97886f4..00000000 --- a/app/assets/stylesheets/_utils.sass +++ /dev/null @@ -1,5 +0,0 @@ -@import "utils/fonts" -@import "utils/placeholders" -@import "utils/forms" -@import "utils/buttons" -@import "utils/animations" diff --git a/app/assets/stylesheets/_utils.scss b/app/assets/stylesheets/_utils.scss new file mode 100644 index 00000000..58b6617b --- /dev/null +++ b/app/assets/stylesheets/_utils.scss @@ -0,0 +1,5 @@ +@mixin unlisted { + list-style: none; + margin: 0; + padding: 0; +} diff --git a/app/assets/stylesheets/application.css.scss b/app/assets/stylesheets/application.css.scss index 706f3dae..3e5212e4 100644 --- a/app/assets/stylesheets/application.css.scss +++ b/app/assets/stylesheets/application.css.scss @@ -1,18 +1,24 @@ -@import "normalize"; +@import url("https://use.typekit.net/saw4kew.css"); + +@import "vendor/normalize.css"; +@import "vendor/animate-custom"; @import "bourbon"; + +@import "base/base"; + @import "settings"; -@import "neat"; + +@import "fonts"; @import "utils"; @import "base"; -@import "pickadate_overrides"; @import "pickadate/default"; @import "pickadate/default.date"; @import "pickadate/default.time"; -@import "partials/layout"; -@import "partials/header"; +@import "partials/hero"; @import "partials/footer"; +@import "partials/home"; @import "partials/activities"; -@import "partials/user"; +@import "partials/schedule"; diff --git a/app/assets/stylesheets/base/_base.scss b/app/assets/stylesheets/base/_base.scss new file mode 100644 index 00000000..d1eccee8 --- /dev/null +++ b/app/assets/stylesheets/base/_base.scss @@ -0,0 +1,14 @@ +// Bitters 1.5.0 +// http://bitters.bourbon.io +// Copyright 2013-2015 thoughtbot, inc. +// MIT License + +@import "variables"; + +@import "buttons"; +@import "forms"; +@import "layout"; +@import "lists"; +@import "media"; +@import "tables"; +@import "typography"; diff --git a/app/assets/stylesheets/base/_buttons.scss b/app/assets/stylesheets/base/_buttons.scss new file mode 100644 index 00000000..dc0ebb95 --- /dev/null +++ b/app/assets/stylesheets/base/_buttons.scss @@ -0,0 +1,43 @@ +#{$all-buttons}, .button { + appearance: none; + // background-color: $action-color; + border: 5px solid $action-color; + // border-radius: $base-border-radius; + color: $action-color; + cursor: pointer; + display: inline-block; + font-family: $base-font-family; + font-size: 1em; + -webkit-font-smoothing: antialiased; + font-weight: 600; + line-height: 1; + padding: $small-spacing $base-spacing; + text-align: center; + text-decoration: none; + transition: background-color $base-duration $base-timing; + user-select: none; + vertical-align: middle; + white-space: nowrap; + + &:hover, + &:focus { + background-color: shade($action-color, 20%); + color: white; + opacity: .8; + } + + &:disabled { + cursor: not-allowed; + opacity: 0.5; + + &:hover { + background-color: $action-color; + } + } +} + +@media (max-width: 500px) { + #{$all-buttons}, .button { + font-size: .8em; + } +} \ No newline at end of file diff --git a/app/assets/stylesheets/base/_forms.scss b/app/assets/stylesheets/base/_forms.scss new file mode 100644 index 00000000..a50f6a65 --- /dev/null +++ b/app/assets/stylesheets/base/_forms.scss @@ -0,0 +1,82 @@ +fieldset { + background-color: transparent; + border: 0; + margin: 0; + padding: 0; +} + +legend { + font-weight: 600; + margin-bottom: $small-spacing / 2; + padding: 0; +} + +label { + display: block; + font-weight: 600; + margin-bottom: $small-spacing / 2; +} + +input, +select, +textarea { + display: block; + font-family: $base-font-family; + font-size: $base-font-size; +} + +#{$all-text-inputs} { + appearance: none; + background-color: $base-background-color; + border: $base-border; + border-radius: $base-border-radius; + box-shadow: $form-box-shadow; + box-sizing: border-box; + margin-bottom: $small-spacing; + padding: $base-spacing / 3; + transition: border-color $base-duration $base-timing; + width: 100%; + + &:hover { + border-color: shade($base-border-color, 20%); + } + + &:focus { + border-color: $action-color; + box-shadow: $form-box-shadow-focus; + outline: none; + } + + &:disabled { + background-color: shade($base-background-color, 5%); + cursor: not-allowed; + + &:hover { + border: $base-border; + } + } + + &::placeholder { + color: tint($base-font-color, 40%); + } +} + +textarea { + resize: vertical; +} + +[type="checkbox"], +[type="radio"] { + display: inline; + margin-right: $small-spacing / 2; +} + +[type="file"] { + margin-bottom: $small-spacing; + width: 100%; +} + +select { + margin-bottom: $small-spacing; + width: 100%; +} diff --git a/app/assets/stylesheets/base/_layout.scss b/app/assets/stylesheets/base/_layout.scss new file mode 100644 index 00000000..723e80eb --- /dev/null +++ b/app/assets/stylesheets/base/_layout.scss @@ -0,0 +1,50 @@ +html { + box-sizing: border-box; +} + +*, +*::before, +*::after { + box-sizing: inherit; +} + +html, +body { + height: 100%; +} + +.ribbon { + background-color: #EF5255; + overflow: hidden; + white-space: nowrap; + /* top left corner */ + position: fixed; + left: -60px; + top: 22px; + width: 200px; + /* 45 deg ccw rotation */ + -webkit-transform: rotate(-45deg); + -moz-transform: rotate(-45deg); + -ms-transform: rotate(-45deg); + -o-transform: rotate(-45deg); + transform: rotate(-45deg); + /* shadow */ + -webkit-box-shadow: 0 0 10px #888; + -moz-box-shadow: 0 0 10px #888; + box-shadow: 0 0 10px #888; + a { + border: 1px solid #faa; + color: #fff; + display: block; + font: bold 81.25% 'Helvetica Neue', Helvetica, Arial, sans-serif; + margin: 1px 0; + padding: 10px 50px; + text-align: center; + text-decoration: none; + /* shadow */ + text-shadow: 0 0 5px #444; + &:hover { + color: #E0F3FA; + } + } +} \ No newline at end of file diff --git a/app/assets/stylesheets/base/_lists.scss b/app/assets/stylesheets/base/_lists.scss new file mode 100644 index 00000000..06a7c0a8 --- /dev/null +++ b/app/assets/stylesheets/base/_lists.scss @@ -0,0 +1,19 @@ +ul, +ol { + list-style-type: none; + margin: 0; + padding: 0; +} + +dl { + margin: 0; +} + +dt { + font-weight: 600; + margin: 0; +} + +dd { + margin: 0; +} diff --git a/app/assets/stylesheets/base/_media.scss b/app/assets/stylesheets/base/_media.scss new file mode 100644 index 00000000..dfa22eae --- /dev/null +++ b/app/assets/stylesheets/base/_media.scss @@ -0,0 +1,9 @@ +figure { + margin: 0; +} + +img, +picture { + margin: 0; + max-width: 100%; +} diff --git a/app/assets/stylesheets/base/_tables.scss b/app/assets/stylesheets/base/_tables.scss new file mode 100644 index 00000000..b3816653 --- /dev/null +++ b/app/assets/stylesheets/base/_tables.scss @@ -0,0 +1,45 @@ +table { + border-collapse: collapse; + margin: $small-spacing 0; + table-layout: fixed; + width: 100%; + font-size: 1.1rem; +} + +th { + border-bottom: $base-border; + font-weight: 600; + padding: $small-spacing 0; + text-align: left; + &.-center { + text-align: center; + } +} + +td { + border-bottom: $base-border; + padding: $small-spacing 0; + text-align: center; + &.-left { + text-align: left; + } + &.-crossed_out { + text-decoration: line-through; + color: $medium-gray; + } + &.-grayed_out { + color: $medium-gray; + } +} + +tr, +td, +th { + vertical-align: middle; +} + +@media (max-width: 700px) { + table { + font-size: .6rem; + } +} \ No newline at end of file diff --git a/app/assets/stylesheets/base/_typography.scss b/app/assets/stylesheets/base/_typography.scss new file mode 100644 index 00000000..2dff5d3c --- /dev/null +++ b/app/assets/stylesheets/base/_typography.scss @@ -0,0 +1,51 @@ +body { + color: $base-font-color; + font-family: $base-font-family; + font-size: $base-font-size; + line-height: $base-line-height; + letter-spacing: .1px; +} + +h1, +h2, +h3, +h4, +h5, +h6 { + font-family: $heading-font-family; + font-size: modular-scale(1); + line-height: $heading-line-height; + margin: 0 0 $small-spacing; +} + +p, ul { + margin: 0 0 $small-spacing; +} + +a { + color: $action-color; + text-decoration: none; + transition: color $base-duration $base-timing; + + &:active, + &:focus, + &:hover { + color: shade($action-color, 25%); + } +} + +hr { + border-bottom: $base-border; + border-left: 0; + border-right: 0; + border-top: 0; + margin: $base-spacing 0; +} + +.small { + font-size: 80%; +} + +ul { + list-style: square; +} \ No newline at end of file diff --git a/app/assets/stylesheets/base/_variables.scss b/app/assets/stylesheets/base/_variables.scss new file mode 100644 index 00000000..1e02ecdf --- /dev/null +++ b/app/assets/stylesheets/base/_variables.scss @@ -0,0 +1,56 @@ +// Breakpoints +$medium-screen: 600px; +$large-screen: 900px; + +// Typography +$base-font-family: "abril-text", serif; +$heading-font-family: $base-font-family; + +// Font Sizes +$base-font-size: 1.4em; + +// Line height +$base-line-height: 1.5; +$heading-line-height: 1.2; + +// Other Sizes +$base-border-radius: 3px; +$base-spacing: $base-line-height * 1em; +$small-spacing: $base-spacing / 2; +$base-z-index: 0; + +// Colors +$blue: #0C8DB7; +$dark-gray: #444; +$medium-gray: #999; +$light-gray: #ddd; + +// Font Colors +$base-font-color: $dark-gray; +$action-color: $blue; + +// Border +$base-border-color: $light-gray; +$base-border: 1px solid $base-border-color; + +// Background Colors +$base-background-color: #fff; +$secondary-background-color: tint($base-border-color, 75%); + +// Forms +$form-box-shadow: inset 0 1px 3px rgba(#000, 0.06); +$form-box-shadow-focus: $form-box-shadow, 0 0 5px adjust-color($action-color, $lightness: -5%, $alpha: -0.3); + +// Animations +$base-duration: 150ms; +$base-timing: ease; + +// Media Queries + +$mobile-phone: 475px; +$mobile-tablet-portrait: 768px; +$mobile-tablet-landscape: 1024px; +$desktop-medium: 1280px; + +$extra-light-gray: #eee; +$light-gray: #ccc; diff --git a/app/assets/stylesheets/partials/_activities.sass b/app/assets/stylesheets/partials/_activities.sass deleted file mode 100644 index ca94393d..00000000 --- a/app/assets/stylesheets/partials/_activities.sass +++ /dev/null @@ -1,15 +0,0 @@ -body.activities - @import "activities/shared" - - &.index - @import "activities/filters" - @import "activities/index" - - &.show - @import "activities/show" - - &.new, - &.edit, - &.create, - &.update - @import "activities/form" diff --git a/app/assets/stylesheets/partials/_activities.scss b/app/assets/stylesheets/partials/_activities.scss new file mode 100644 index 00000000..01051eaf --- /dev/null +++ b/app/assets/stylesheets/partials/_activities.scss @@ -0,0 +1,103 @@ + +body.activities { + @import "activities/shared"; + + &.index { + @import "activities/filters"; + @import "activities/index"; + } + + &.show { + @import "activities/show"; + } + + &.new, + &.edit, + &.create, + &.update { + @import "activities/form"; + } +} + +.page-section--activities { + background: linear-gradient(180deg, rgba(140, 208, 240, .2), rgba(140, 208, 240, .4)); +} + +.activity-list { + @include unlisted; + display: flex; + flex-direction: column; + flex-wrap: wrap; + text-align: center; + @media (min-width: $mobile-phone) { + flex-direction: row; + } + + &__item { + box-sizing: border-box; + padding: 20px 0; + @media (min-width: $mobile-phone) { + flex-basis: 33%; + max-width: 33%; + } + @media (min-width: $mobile-tablet-landscape) { + flex-basis: 25%; + max-width: 25%; + } + } +} + +.activity-avatar { + align-items: center; + display: flex; + margin-bottom: 2em; + @media (max-width: $mobile-phone) { + flex-direction: column; + } + &__caption { + flex: 1; + @media (max-width: $mobile-phone) { + text-align: center; + } + } + &__name { + font-size: 2.5rem; + margin-bottom: 0; + } + &__photo { + border: 2px solid #fff; + border-radius: 50%; + margin-right: 1em; + width: 20%; + @media (max-width: $mobile-phone) { + margin-bottom: 1em; + margin-right: 0; + width: 60%; + } + } + &__twitter, + &__company { + margin-bottom: 0; + } + &__company { + color: lighten($dark-gray, 15); + } +} + +.activity-avatar--list { + margin-bottom: 0; + .activity-avatar__photo { + margin-right: 0; + width: 65%; + @media (max-width: $mobile-phone) { + margin-bottom: 0.5em; + } + } + .activity-avatar__name { + font-size: 1.3rem; + } +} + +.session { + margin: 3em 0; +} diff --git a/app/assets/stylesheets/partials/_footer.sass b/app/assets/stylesheets/partials/_footer.sass deleted file mode 100644 index 92821bd2..00000000 --- a/app/assets/stylesheets/partials/_footer.sass +++ /dev/null @@ -1,8 +0,0 @@ -#footer - padding: 2em 0 - text-align: center - @extend %ornamental-divider-before - - nav - ul - @extend %inline-nav-list diff --git a/app/assets/stylesheets/partials/_footer.scss b/app/assets/stylesheets/partials/_footer.scss new file mode 100644 index 00000000..d0a0a53b --- /dev/null +++ b/app/assets/stylesheets/partials/_footer.scss @@ -0,0 +1,96 @@ +.footer { + background-color: darken(rgba(79, 113, 154, .8), 40%); + display: flex; + flex-direction: row; + flex-wrap: wrap; + justify-content: flex-start; + align-content: stretch; + align-items: flex-start; + color: #999; + text-align: center; + font-size: 1rem; + .footer-signature, .footer-social, .footer-newsletter { + flex-grow: 1; + flex-basis: 250px; + padding: 2rem; + } + .footer-signature { + img { + filter: grayscale(100%); + max-width: 125px; + display: block; + margin: 0 auto; + } + .colophon { + padding-top: 1rem; + } + } +} + +.footer-social { + &__list { + @include unlisted; + } + + &__list-item { + display: inline; + } + + &__list-link { + width: 40px; + height: 40px; + display: inline-block; + text-indent: -999em; + background: transparent url(/images/footer/social_sprite.png) no-repeat; + + $x: 0px; + @each $item in twitter, facebook, github, instagram, medium { + &.-#{$item} { + background-position: $x 0; + $x: $x - 40px; + } + } + + &:hover { + background-position-y: -40px; + } + } + .footer-navigation { + padding-top: 1rem; + &__list { + @include unlisted; + } + + &__list-item { + // display: inline; + } + + &__list-link { + // color: white; + } + } +} + +.footer-newsletter { + p, input, label { + font-size: 1rem; + font-weight: 400; + } + input { + max-width: 250px; + margin: .5rem auto; + } + .button { + background: transparent; + text-transform: uppercase; + letter-spacing: 1px; + font-family: 'Adonis-web'; + font-weight: 700; + &:hover, + &:focus { + background-color: shade($action-color, 20%); + color: white; + opacity: .8; + } + } +} \ No newline at end of file diff --git a/app/assets/stylesheets/partials/_header.sass b/app/assets/stylesheets/partials/_header.sass deleted file mode 100644 index 7f450856..00000000 --- a/app/assets/stylesheets/partials/_header.sass +++ /dev/null @@ -1,29 +0,0 @@ -#header - text-align: center - padding: 0.5em 0 - - h1 - clear: both - margin-bottom: 0 - - a - border: none - color: $purple - font-size: 180% - - h2 - font-weight: normal - margin-top: 0 - - nav.user-nav - float: right - - ul - @extend %inline-nav-list - - a - body.user & - color: $white - - &:hover - color: $blue diff --git a/app/assets/stylesheets/partials/_hero.scss b/app/assets/stylesheets/partials/_hero.scss new file mode 100644 index 00000000..06de99df --- /dev/null +++ b/app/assets/stylesheets/partials/_hero.scss @@ -0,0 +1,95 @@ +.page-header-mobile { + .page-header-mobile-sky { + position: fixed; + top: 0; + width: 100vw; + height: 60px; + background: #ACE2F7; + background-image: linear-gradient(to bottom, #71C4EC 0%, #ACE2F7 100%); + opacity: .9; + } + .page-header-mobile-logo { + position: fixed; + top: -32px; + left: calc(50vw - 100px); //25vw; + width: 200px; //50vw; + height: 200px; //50vw; + background-image: image-url('hero/IoR_illustration.svg'); + background-repeat: no-repeat; + background-position: center; + z-index: 100; + } + .page-header-mobile-text { + padding-top: 150px; + text-align: center; + img { + width: 80%; + max-width: 500px; + } + } +} +.page-header { + display: none; +} + +@media (min-width: 700px) and (min-height: 650px) { + .page-header-mobile { + display: none; + } + .page-header { + display: block; + min-height: 25vh; + + &--hero { + position: fixed; + min-height: 90vh; + } + + &__sky { + position: absolute; + top: 0; + width: 100vw; + height: 8vw; + background: #ACE2F7; + background-image: linear-gradient(to bottom, #71C4EC 0%, #ACE2F7 100%); + opacity: .9; + + .page-header--hero & { + height: 200px; // 20vw; + } + } + + &__logo { + position: absolute; + top: -0.5vw; + left: 40vw; + width: 20vw; + height: 20vw; + + background-image: image-url('hero/IoR_illustration.svg'); + background-repeat: no-repeat; + background-position: center; + z-index: 100; + + .page-header--hero & { + top: -120px; + left: calc(50vw - 350px); //25vw; + width: 700px; //50vw; + height: 700px; //50vw; + } + } + + &__logo-text { + display: none; + + .page-header--hero & { + display: block; + } + + img { + width: 700px; + margin: 550px 0; //vw 0; + } + } + } +} diff --git a/app/assets/stylesheets/partials/_home.scss b/app/assets/stylesheets/partials/_home.scss new file mode 100644 index 00000000..258cd899 --- /dev/null +++ b/app/assets/stylesheets/partials/_home.scss @@ -0,0 +1,84 @@ +#home { + .page-section__inner { + display:flex; + flex-direction: row; + flex-wrap: wrap; + justify-content: flex-start; + align-content: flex-end; + align-items: flex-end; + height: 50vh; + width: 100vw; + max-width: 100vw; + padding-bottom: 2vh; + } + .page-section__image { + display: none; + } +} + +.home--intro { + text-align: center; + width: 100%; + z-index: 100; + h1, h2 { + margin: 0; + } + h1 { + font-size: .8em; + } + h1.isleofruby { + animation-duration: 3s; + animation-name: changeopacity; + animation-iteration-count: infinite; + animation-direction: alternate; + letter-spacing: 0px; + } + h1.iloveruby { + animation-duration: 3s; + animation-name: changeopacityreversed; + animation-iteration-count: infinite; + animation-direction: alternate; + margin-top: -1.35rem; + letter-spacing: .9px; + } + @keyframes changeopacity { + from { + opacity: 0; + } + to { + opacity: 1; + } + } + @keyframes changeopacityreversed { + from { + opacity: 1; + } + to { + opacity: 0; + } + } + + h2 { + font-size: 1em; + } +} + +@media (min-width: 700px) and (min-height: 650px) { + #home { + .page-section__inner { + height: 100vh; + } + } +} + +.home--intro-tickets { + padding-bottom: 1rem; + margin: 0; +} + +.home--intro-link { + display: block; + padding-top: 1rem; + font-size: 14px; + text-decoration: underline; +} \ No newline at end of file diff --git a/app/assets/stylesheets/partials/_layout.sass b/app/assets/stylesheets/partials/_layout.sass deleted file mode 100644 index 962bae43..00000000 --- a/app/assets/stylesheets/partials/_layout.sass +++ /dev/null @@ -1,4 +0,0 @@ -#container - +outer-container - padding: 0 $padding - position: relative diff --git a/app/assets/stylesheets/partials/_schedule.scss b/app/assets/stylesheets/partials/_schedule.scss new file mode 100644 index 00000000..e390ea63 --- /dev/null +++ b/app/assets/stylesheets/partials/_schedule.scss @@ -0,0 +1,3 @@ +.page-section--schedule { + background-color: $light-grey; +} diff --git a/app/assets/stylesheets/partials/_user.sass b/app/assets/stylesheets/partials/_user.sass deleted file mode 100644 index d21581a8..00000000 --- a/app/assets/stylesheets/partials/_user.sass +++ /dev/null @@ -1,109 +0,0 @@ -@import 'partials/user/edit' - -body.user - background: $gray - // the "back home" link only shown on the - // user pages within the header - #header - .back-home - float: left - - &:after - content: none - - article - clear: both - padding: 3em 0 - - header - margin: 0 0 2em 0 - - h2 - +custom-sans(bold) - font-size: 1.8em - color: $white - - &:after - content: none - - h3, h4 - +custom-sans(medium) - text-transform: uppercase - font-size: 1.5em - - section.form - +span-columns(6) - padding: 10px - +media($smartphone-landscape) - padding: 70px - margin: 0 auto - background: $white - box-shadow: 0 0 7px transparentize($black, 0.9) - - .avatar - margin: 0 0 2em 0 - img - border-radius: 64px - - p.divider - text-align: center - @extend %ornamental-divider-after - text-transform: uppercase - +custom-sans(bold) - margin: 5em 0 5em - - span - display: inline-block - background: $white - padding: 0 10px - position: relative - z-index: 2 - - &:after - margin: -0.75em auto 0 - z-index: 1 - - p.info - text-align: center - border-top: 1px solid $extra-light-gray - margin: 2em 0 0 0 - padding: 1em 0 0 0 - color: $gray - - .oauth - ul - @extend %unlisted - @extend %clearfix - - li - width: percentage(1/2) - float: left - - &:first-child - a - margin-right: 1px - border-radius: 3px 0 0 3px - - a - @extend %secondary-button - display: block - border-radius: 0 3px 3px 0 - padding: 2em 0 - - &.connected - background: $medium-gray - - img - margin: 0 0 0.5em 0 - - h4 - +custom-serif - font-size: 1.25em - margin: 0 - line-height: 1.25em - - strong - +custom-sans(medium) - text-transform: none - display: block - font-size: 1.5em diff --git a/app/assets/stylesheets/partials/activities/_filters.sass b/app/assets/stylesheets/partials/activities/_filters.sass deleted file mode 100644 index dcf75f25..00000000 --- a/app/assets/stylesheets/partials/activities/_filters.sass +++ /dev/null @@ -1,131 +0,0 @@ -form.filters - @extend %clearfix - border-radius: 3px - overflow: hidden - padding: 0 0 2.5em 0 - - label - background: lighten($extra-light-gray, 5) - @extend %smaller-text - color: $gray - text-transform: uppercase - +custom-sans(medium) - border: none - display: block - padding: 0.75em 1em - margin: 0 0 1px 0 - text-align: left - +transition(all 0.5s) - - &.participant, - &.owner - &:before - content: "•" - font-size: 2.5em - display: inline-block - margin: 0 0.25em 0.25em 0 - line-height: 1px - - html.no-js & - margin: 0 0.25em 0.1em 0 - - &.participant:before - color: $green - - &.owner - border-radius: 0 0 3px 3px - - &:before - color: $yellow - - &.search - background: none - padding: 0 - margin-top: 1em - width: 100% - - &:hover - background: none - - input - background: $white image-url('shared/search.png') no-repeat right center - border: 1px solid $light-gray - - &:focus - border-color: $blue - background: none - - a - position: absolute - right: 0 - top: 1em - - strong - +custom-sans(light) - float: right - margin: 0 0 0 0.75em - - &:hover - color: $white - background: $blue - cursor: pointer - +transition(all 0.15s) - - &.selected - color: $white - background: $medium-gray - - input - &[type=radio] - margin: 0 0.75em 0 0 - - button - background: $medium-gray - border-radius: 3px - color: $white - border: none - margin: 1em auto - padding: 0.4em 2em - display: block - +transition(all 0.15s) - - &:hover - +transition(all 0.15s) - color: $white - background: $blue - - button, input[type=radio] - html.js & - display: none - - - +media($tablet-lite-plus) - border-radius: none - overflow: visible - float: left - padding: 0 0 4.5em 0 - - label - display: inline-block - margin: 0 1px 0 0 - - strong - float: none - - &:first-child - border-radius: 3px 0 0 3px - - &.owner - border-radius: 0 3px 3px 0 - margin: 0 - - &.search - position: absolute - top: 4em - margin-top: 0 - left: 0 - - button - margin: 0 0 0 0.5em - padding: 0.45em 0.75em - display: inline-block diff --git a/app/assets/stylesheets/partials/activities/_filters.scss b/app/assets/stylesheets/partials/activities/_filters.scss new file mode 100644 index 00000000..c3e3e7ca --- /dev/null +++ b/app/assets/stylesheets/partials/activities/_filters.scss @@ -0,0 +1,165 @@ +form.filters { + border-radius: 3px; + overflow: hidden; + padding: 0 0 2.5em 0; + + label { + background: lighten($extra-light-gray, 5); + border: 0; + color: #808080; + + //+custom-sans(medium) + display: block; + margin: 0 0 1px 0; + padding: 0.75em 1em; + text-align: left; + text-transform: uppercase; + + transition: all 0.5s; + + &.participant, + &.owner { + &:before { + content: "•"; + display: inline-block; + font-size: 2.5em; + line-height: 1px; + margin: 0 0.25em 0.25em 0; + + html.no-js & { + margin: 0 0.25em 0.1em 0; + } + } + } + + &.participant:before { + color: #008000; + } + + &.owner { + border-radius: 0 0 3px 3px; + + &:before { + color: #ff0; + } + } + + &.search { + background: none; + margin-top: 1em; + padding: 0; + width: 100%; + + &:hover { + background: none; + } + + input { + background: #fff image-url("shared/search.png") no-repeat right center; + border: 1px solid $light-gray; + + &:focus { + background: none; + border-color: #00f; + } + } + + a { + position: absolute; + right: 0; + top: 1em; + } + } + + strong { + //+custom-sans(light) + float: right; + margin: 0 0 0 0.75em; + } + + &:hover { + background: #00f; + color: #fff; + cursor: pointer; + + transition: all 0.15s; + } + + &.selected { + background: $medium-gray; + color: #fff; + } + } + + input { + &[type="radio"] { + margin: 0 0.75em 0 0; + } + } + + button { + background: $medium-gray; + border: 0; + border-radius: 3px; + color: #fff; + display: block; + margin: 1em auto; + padding: 0.4em 2em; + + transition: all 0.15s; + + &:hover { + background: #00f; + + color: #fff; + transition: all 0.15s; + } + } + + button, input[type="radio"] { + html.js & { + display: none; + } + } + + + @media (min-width: $mobile-tablet-landscape) { + border-radius: none; + float: left; + overflow: visible; + padding: 0 0 4.5em 0; + + label { + display: inline-block; + margin: 0 1px 0 0; + + strong { + float: none + } + + &:first-child { + border-radius: 3px 0 0 3px + } + + &.owner { + border-radius: 0 3px 3px 0; + margin: 0; + } + + &.search { + left: 0; + margin-top: 0; + position: absolute; + top: 4em; + } + } + + button { + display: inline-block; + margin: 0 0 0 0.5em; + padding: 0.45em 0.75em; + } + + + } +} diff --git a/app/assets/stylesheets/partials/activities/_form.sass b/app/assets/stylesheets/partials/activities/_form.sass deleted file mode 100644 index 0c9bd822..00000000 --- a/app/assets/stylesheets/partials/activities/_form.sass +++ /dev/null @@ -1,101 +0,0 @@ -form#new-activity - - input.date-capture, - input.time-capture - display: none - - html.js & - .select - display: none - - input.date-capture, - input.time-capture - display: inline - - span - &.divider - width: 10% - text-align: center - - fieldset - &.start-time, - &.end-time - display: inline-block - width: 45% - position: relative - - span.validation-error-message - position: relative - float: right - line-height: 1.5em - top: 0 - right: 15px - - &.end-time - float: right - - &.anytime - clear: both - - span - +custom-sans(bold) - text-transform: uppercase - margin: 0 1em 0 15px - - input - &.date-capture - width: 69% - - &.time-capture - width: 4em - text-align: center - width: 30% - - label - &.location - display: inline-block - +span-columns(6) - +media($tablet-lite) - +span-columns(3) - - &.limit-of-participants - display: inline-block - text-align: right - +span-columns(6) - - +media($tablet-lite) - +span-columns(1) - input - width: 85% - - &.image-url - .preview - position: absolute - right: 0.5em - top: 0.5em - z-index: 1000 - height: 2.9em - width: 2.9em - border-radius: 1.45em - background-position: center - background-size: cover - - &.checking - background-size: auto !important - background-repeat: no-repeat !important - background-image: image-url('shared/checking.gif') !important - - button - font-size: 1.5em - - .meeting-point-map - display: none - width: 80% - padding: 3em 0 - -.delete-activity - margin-top: 4em - header - margin-bottom: 0.5em - button - background-color: $red diff --git a/app/assets/stylesheets/partials/activities/_form.scss b/app/assets/stylesheets/partials/activities/_form.scss new file mode 100644 index 00000000..147c297d --- /dev/null +++ b/app/assets/stylesheets/partials/activities/_form.scss @@ -0,0 +1,136 @@ +form#new-activity { + input.date-capture, + input.time-capture { + display: none; + } + + html.js & { + .select { + display: none; + } + + input.date-capture, + input.time-capture { + display: inline; + } + } + + span { + &.divider { + text-align: center; + width: 10%; + } + } + + fieldset { + &.start-time, + &.end-time { + display: inline-block; + position: relative; + width: 45%; + + span.validation-error-message { + float: right; + line-height: 1.5em; + position: relative; + right: 15px; + top: 0; + } + } + + &.end-time { + float: right; + } + + &.anytime { + clear: both; + + span { + // @include custom-sans(bold); + + margin: 0 1em 0 15px; + text-transform: uppercase; + } + } + } + + input { + &.date-capture { + width: 69%; + } + + &.time-capture { + text-align: center; + width: 4em; + width: 30%; + } + } + + label { + &.location { + display: inline-block; + + // @include span-columns(6); + @media (min-width: $mobile-tablet-portrait) { + // @include span-columns(3); + } + } + + &.limit-of-participants { + display: inline-block; + text-align: right; + + // @include span-columns(6); + + @media (min-width: $mobile-tablet-portrait) { + // @include span-columns(1); + + input { + width: 85%; + } + } + } + + &.image-url { + .preview { + background-position: center; + background-size: cover; + border-radius: 1.45em; + height: 2.9em; + position: absolute; + right: 0.5em; + top: 0.5em; + width: 2.9em; + z-index: 1000; + + &.checking { + background-image: image-url("shared/checking.gif") !important; + background-repeat: no-repeat !important; + background-size: auto !important; + } + } + } + } + + button { + font-size: 1.5em; + } + + .meeting-point-map { + display: none; + padding: 3em 0; + width: 80%; + } +} + +.delete-activity { + margin-top: 4em; + + header { + margin-bottom: 0.5em; + } + + button { + background-color: $red; + } +} diff --git a/app/assets/stylesheets/partials/activities/_index.sass b/app/assets/stylesheets/partials/activities/_index.sass deleted file mode 100644 index ef7d8f6e..00000000 --- a/app/assets/stylesheets/partials/activities/_index.sass +++ /dev/null @@ -1,225 +0,0 @@ -a#new-activity - float: right - @extend %icon-button-add - -.map - margin: 6em 0 0 - - h3 - +custom-sans(light) - text-align: center - @extend %ornamental-divider-after - - iframe - margin: 2em 0 0 - height: 400px - border: none - - +media($tablet-lite) - iframe - height: 600px - -#activities - clear: both - @extend %clearfix - @extend %unlisted - padding: 1em 0 0 0 - - // card variations - $types: participant ok remove, owner edit edit - - .activity-day - clear: both - - h1 - padding-top: 0.67em - margin-top: 0 - - li - display: block - margin: 0 0 2px 0 - text-align: center - - // icon in the upper right, - // shows up only if owner or participant - span.icon - position: absolute - top: 15px - right: 15px - opacity: 1 - +transition(opacity 0.25s) - - // select icon based on card variation - @each $type in $types - &.#{nth($type, 1)} - span.icon - @extend %icon-#{nth($type, 2)} - - // links are actually stacked in this case - // as one card has to support many purposes - a - border: none - - // link to show action, usually covers whole card - // on hover appears as secondary button above title - &.details - display: block - position: absolute - z-index: 100 - width: 100% - height: 100% - top: 0 - left: 0 - - span - @extend %secondary-button - background: $medium-gray - opacity: 0 - margin: 20em 0 0 - - // card background and wrapper - .container - background: lighten($extra-light-gray, 5) - border: 1px solid lighten($extra-light-gray, 5) - height: 25em - padding: 2.5em 1em 1em 1em - margin: 0 1px - position: relative - overflow: hidden - +transition(all 0.35s ease-in-out) - - // all default text: title, organizer, date etc. - .labels - margin: 1.5em 0 0 0 - color: $extra-dark-gray - - // title - h4 - +custom-sans(light) - font-size: 1.375em - margin: 0 - padding: 0 0.727272em - @extend %transition-all-15s-ease-out - @extend %ornamental-divider-before - @extend %ornamental-divider-after - - &:before, - &:after - margin: 0.2em auto - width: 20px - - &:before - width: 0 - margin: 0 auto - - // creator and date/time rows - p - opacity: 1 - +transition(opacity 0.1s) - - &.creator - text-transform: uppercase - +custom-sans(medium) - @extend %smaller-text - margin: 0.5em 0 - - &.time - position: absolute - right: 1em - bottom: 0 - left: 1em - +custom-serif(bold) - border-top: 1px solid $light-gray - padding: 0.75em 0 0 - - // - .progress - +inline-block - - // the big button appearing on hover - .action - opacity: 0 - position: absolute - left: 0 - right: 0 - top: 5em - z-index: 1001 - +transform(scale(0.9)) - +transition(all 0.35s ease-in-out 0) - - a, button - @extend %large-icon-button-ok - - // select action type based on card variation - @each $type in $types - &.#{nth($type, 1)} - .action - a, button - @extend %large-icon-button-#{nth($type, 3)} - - // HOVER state - &:hover - cursor: pointer - - .container - z-index: 9001 - box-shadow: 0 0 25px transparentize($black, 0.85) - border-color: $extra-light-gray - background: $white - - span.icon - opacity: 0 - +transition(opacity 0.25s) - - a.details - span - opacity: 1 - +transition(all 0.25s ease-in-out) - - .progress-wrapper, - .container > .progress - @extend %transition-all-15s-ease-out - +transform(translate(0, -9em)) - opacity: 0 - - &.full.default, - &.past - .progress-wrapper, - .container > .progress - @extend %transition-all-15s-ease-out - +transform(translate(0, 0)) - opacity: 0.5 - - .labels - h4 - @extend %transition-all-15s-ease-out - +transform(translate(0, 2.5em)) - - &:before - width: 50% - margin: 0 auto 0.5em - - &:after - width: 0 - - p - +transition(opacity 0.1s) - opacity: 0 - - .requires_eurucamp_ticket - +transition(opacity 0.1s) - opacity: 0 - - .action - opacity: 1 - +transform(scale(1)) - +transition(all 0.15s ease-out 0.15s) - - - +media($tablet-lite) - li - +span-columns(2) - - +media($tablet-portrait-plus) - li - +span-columns(2) diff --git a/app/assets/stylesheets/partials/activities/_index.scss b/app/assets/stylesheets/partials/activities/_index.scss new file mode 100644 index 00000000..3683375d --- /dev/null +++ b/app/assets/stylesheets/partials/activities/_index.scss @@ -0,0 +1,306 @@ +a#new-activity { + float: right; + + // @extend %icon-button-add; +} + +.map { + margin: 6em 0 0; + + h3 { + // @include custom-sans(light); + + text-align: center; + + // @extend %ornamental-divider-after; + } + + iframe { + border: 0; + height: 400px; + margin: 2em 0 0; + } + + @media (min-width: $mobile-tablet-portrait) { + iframe { + height: 600px; + } + } +} + +#activities { + // clear: both; + + // @extend %clearfix; + + @include unlisted; + + padding: 1em 0 0 0; + + // card variations + $types: participant ok remove, owner edit edit; + + .activity-day { + clear: both; + + h1 { + margin-top: 0; + padding-top: 0.67em; + } + } + + li { + display: block; + margin: 0 0 2px 0; + text-align: center; + + // icon in the upper right, + // shows up only if owner or participant + span.icon { + opacity: 1; + position: absolute; + right: 15px; + top: 15px; + + transition: opacity 0.25s; + } + + // select icon based on card variation + // @each $type in $types { + // &.#{nth($type, 1)} { + // span.icon { + // @extend %icon-#{nth($type, 2)}; + // } + // } + // } + + // links are actually stacked in this case + // as one card has to support many purposes + a { + border: 0; + + // link to show action, usually covers whole card + // on hover appears as secondary button above title + &.details { + display: block; + height: 100%; + left: 0; + position: absolute; + top: 0; + width: 100%; + z-index: 100; + + span { + // @extend %secondary-button; + + background: $medium-gray; + margin: 20em 0 0; + opacity: 0; + } + } + } + + // card background and wrapper + .container { + background: lighten($extra-light-gray, 5); + border: 1px solid lighten($extra-light-gray, 5); + height: 25em; + margin: 0 1px; + overflow: hidden; + padding: 2.5em 1em 1em 1em; + position: relative; + + transition: all 0.35s ease-in-out; + } + + // all default text: title, organizer, date etc. + .labels { + color: #ccc; + margin: 1.5em 0 0 0; + + // title + h4 { + // @include custom-sans(light); + + font-size: 1.375em; + margin: 0; + padding: 0 0.72727em; + + transition: all 0.15s ease-out;; + + // @extend %ornamental-divider-before; + + // @extend %ornamental-divider-after; + + &:before, + &:after { + margin: 0.2em auto; + width: 20px; + } + + &:before { + margin: 0 auto; + width: 0; + } + } + + // creator and date/time rows + p { + opacity: 1; + + transition: opacity 0.1s; + + &.creator { + margin: 0.5em 0; + + // @include custom-sans(medium); + + // @extend %smaller-text; + + text-transform: uppercase; + } + + &.time { + border-top: 1px solid $light-gray; + bottom: 0; + left: 1em; + padding: 0.75em 0 0; + + // @include custom-serif(bold); + + position: absolute; + right: 1em; + } + } + } + + // + .progress { + display: inline-block; + } + + // the big button appearing on hover + .action { + left: 0; + opacity: 0; + position: absolute; + right: 0; + top: 5em; + transform: scale 0.9; + + transition: all 0.35s ease-in-out 0; + z-index: 1001; + + a, button { + // @extend %large-icon-button-ok; + } + } + + // select action type based on card variation + @each $type in $types { + &.#{nth($type, 1)} { + .action { + a, button { + // @extend %large-icon-button-#{nth($type, 3)}; + } + } + } + } + + // HOVER state + &:hover { + cursor: pointer; + + .container { + background: white; + border-color: $extra-light-gray; + box-shadow: 0 0 25px transparentize(#000, 0.85); + z-index: 9001; + } + + span.icon { + opacity: 0; + + transition: opacity 0.25s; + } + + a.details { + span { + opacity: 1; + + transition: all 0.25s ease-in-out; + } + } + + .progress-wrapper, + .container > .progress { + transition: all 0.15s ease-out;; + + opacity: 0; + + transform: translate(0, -9em); + } + + &.full.default, + &.past { + .progress-wrapper, + .container > .progress { + transition: all 0.15s ease-out;; + + opacity: 0.5; + + transform: translate(0, 0); + } + } + + .labels { + h4 { + transition: all 0.15s ease-out;; + + transform: translate(0, 2.5em); + + &:before { + margin: 0 auto 0.5em; + width: 50%; + } + + &:after { + width: 0; + } + } + + p { + opacity: 0; + + transition: opacity 0.1s; + } + + .requires_eurucamp_ticket { + opacity: 0; + + transition: opacity 0.1s; + } + } + + .action { + opacity: 1; + + transform: scale(1); + transition: all 0.15s ease-out 0.15s; + } + } + } + + @media (min-width: $mobile-tablet-portrait) { + li { + // @include span-columns(2); + } + } + + + @media (min-width: $mobile-tablet-landscape) { + li { + // @include span-columns(2); + } + } +} diff --git a/app/assets/stylesheets/partials/activities/_shared.sass b/app/assets/stylesheets/partials/activities/_shared.sass deleted file mode 100644 index 89c9d8ac..00000000 --- a/app/assets/stylesheets/partials/activities/_shared.sass +++ /dev/null @@ -1,23 +0,0 @@ -// wrapper around image + canvas -.progress-wrapper - padding: 6px - width: 162px - width: 162px - margin: 0 auto - @extend %transition-all-15s-ease-out - - canvas - z-index: 99 !important - -// big round image -.progress - width: 150px - height: 150px - border-radius: 75px - background-size: cover - background-position: center - background-image: image-url('shared/activity_placeholder.jpg') - -img.requires_eurucamp_ticket - width: 40px - height: 40px diff --git a/app/assets/stylesheets/partials/activities/_shared.scss b/app/assets/stylesheets/partials/activities/_shared.scss new file mode 100644 index 00000000..400e7133 --- /dev/null +++ b/app/assets/stylesheets/partials/activities/_shared.scss @@ -0,0 +1,28 @@ +// wrapper around image + canvas +.progress-wrapper { + margin: 0 auto; + padding: 6px; + transition: all 0.15s ease-out; + width: 162px; + + width: 162px; + + canvas { + z-index: 99 !important; + } +} + +// big round image +.progress { + background-image: image-url("shared/activity_placeholder.jpg"); + background-position: center; + background-size: cover; + border-radius: 75px; + height: 150px; + width: 150px; +} + +img.requires_eurucamp_ticket { + height: 40px; + width: 40px; +} diff --git a/app/assets/stylesheets/partials/activities/_show.sass b/app/assets/stylesheets/partials/activities/_show.sass deleted file mode 100644 index a644aac4..00000000 --- a/app/assets/stylesheets/partials/activities/_show.sass +++ /dev/null @@ -1,139 +0,0 @@ -$types: participant ok remove, owner edit edit - -#activity - header - p.info - background: $extra-light-gray - border: 1px solid $light-gray - padding: 1em - +custom-sans(medium) - - .action - position: absolute - right: 0 - top: 0 - - a, button - @extend %icon-button-ok - - // select action type based on card variation - @each $type in $types - &.#{nth($type, 1)} - a, button - @extend %icon-button-#{nth($type, 3)} - - section - position: relative - - &.description - +custom-serif - line-height: 1.35em - margin-bottom: 2em - - h1, h2, h3, h4, h5, h6 - +custom-sans(bold) - text-transform: uppercase - margin: 1.5em 0 0 - font-size: 1em - - &.overview - text-align: center - margin-bottom: 2em - - .progress-wrapper, - .progress - display: none - - p - margin: 1em 0 0 - font-size: 1.25em - - a - @extend %secondary-button - margin: 1em 0 - - .participants - width: 100% - padding: 2em 0 - clear: both - - h3 - +custom-sans(light) - text-align: center - @extend %ornamental-divider-after - - ul - @extend %unlisted - text-align: center - - li - padding: 0 5px - +inline-block - - img - border-radius: 24px - width: 48px - - &.details - dl.wrapper - @extend %clearfix - border-bottom: 1px solid $light-gray - - dt, dd - float: left - line-height: 3em - line-height: 1.35em - padding: 1em 0 - width: 50% - border-top: 1px solid $light-gray - - dt - +custom-sans(medium) - clear: left - - dd - +custom-serif - margin: 0 - - +media($smartphone-portrait) - section - &.overview - .progress-wrapper - width: 274px - height: 274px - padding: 12px - display: block - - .progress - width: 250px - height: 250px - display: block - border-radius: 125px - - +media($tablet-portrait) - section - width: percentage(1/3) - - &.overview - float: left - left: percentage(1/3) - - &.description - float: left - left: - percentage(1/3) - - .wrapper - margin-right: 50px - - &.details - float: right - - dl.wrapper - margin-left: 50px - - .meeting-point-map - margin-left: 50px - display: none - - img - width: 100% diff --git a/app/assets/stylesheets/partials/activities/_show.scss b/app/assets/stylesheets/partials/activities/_show.scss new file mode 100644 index 00000000..a79e38a8 --- /dev/null +++ b/app/assets/stylesheets/partials/activities/_show.scss @@ -0,0 +1,190 @@ +$types: participant ok remove, owner edit edit; + +#activity { + header { + p.info { + background: $extra-light-gray; + border: 1px solid $light-gray; + padding: 1em; + + // @include custom-sans(medium); + } + } + + .action { + position: absolute; + right: 0; + top: 0; + + a, button { + // @extend %icon-button-ok; + } + + // select action type based on card variation + // @each $type in $types { + // &.#{nth($type, 1)} { + // a, button { + // @extend %icon-button-#{nth($type, 3)}; + // } + // } + // } + } + + section { + position: relative; + + &.description { + // @include custom-serif; + + line-height: 1.35em; + margin-bottom: 2em; + + h1, h2, h3, h4, h5, h6 { + // @include custom-sans(bold); + + font-size: 1em; + margin: 1.5em 0 0; + text-transform: uppercase; + } + } + + &.overview { + margin-bottom: 2em; + text-align: center; + + .progress-wrapper, + .progress { + display: none; + } + + p { + font-size: 1.25em; + margin: 1em 0 0; + } + + a { + // @extend %secondary-button; + + margin: 1em 0; + } + + .participants { + clear: both; + padding: 2em 0; + width: 100%; + + h3 { + // @include custom-sans(light); + + text-align: center; + + // @extend %ornamental-divider-after; + } + + ul { + @include unlisted; + + text-align: center; + } + + li { + display: inline-block; + padding: 0 5px; + } + + img { + border-radius: 24px; + width: 48px; + } + } + } + + &.details { + dl.wrapper { + // @extend %clearfix; + + border-bottom: 1px solid $light-gray; + } + + dt, dd { + border-top: 1px solid $light-gray; + float: left; + line-height: 3em; + line-height: 1.35em; + padding: 1em 0; + width: 50%; + } + + dt { + // @include custom-sans(medium); + + clear: left; + } + + dd { + // @include custom-serif; + + margin: 0; + } + } + } + + @media (min-width: $mobile-phone) { + section { + &.overview { + .progress-wrapper { + display: block; + height: 274px; + padding: 12px; + width: 274px; + } + + .progress { + border-radius: 125px; + display: block; + height: 250px; + width: 250px; + } + } + } + } + + + @media (min-width: $mobile-tablet-portrait) { + section { + width: percentage(1 / 3); + + &.overview { + float: left; + left: percentage(1 / 3); + } + + &.description { + float: left; + left: -(percentage(1 / 3)); + + .wrapper { + margin-right: 50px; + } + } + + &.details { + float: right; + + dl.wrapper { + margin-left: 50px; + } + } + } + } + + + .meeting-point-map { + display: none; + margin-left: 50px; + + img { + width: 100%; + } + } +} diff --git a/app/assets/stylesheets/partials/user/_edit.sass b/app/assets/stylesheets/partials/user/_edit.sass deleted file mode 100644 index f9eab8d6..00000000 --- a/app/assets/stylesheets/partials/user/_edit.sass +++ /dev/null @@ -1,3 +0,0 @@ -label.show_participation - margin-bottom: 1.25em - display: inline-block !important diff --git a/app/assets/stylesheets/partials/user/_edit.scss b/app/assets/stylesheets/partials/user/_edit.scss new file mode 100644 index 00000000..f249396c --- /dev/null +++ b/app/assets/stylesheets/partials/user/_edit.scss @@ -0,0 +1,4 @@ +label.show_participation { + display: inline-block !important; + margin-bottom: 1.25em; +} diff --git a/app/assets/stylesheets/utils/_animations.sass b/app/assets/stylesheets/utils/_animations.sass deleted file mode 100644 index 05c4132c..00000000 --- a/app/assets/stylesheets/utils/_animations.sass +++ /dev/null @@ -1,35 +0,0 @@ -%animated - +animation-fill-mode(both) - +animation-duration(1s) - - -+keyframes(bounceInDown) - 0% - opacity: 0 - +transform(translateY(-2000px)) - 60% - opacity: 1 - +transform(translateY(30px)) - 80% - +transform(translateY(-10px)) - 100% - +transform(translateY(0)) - -%animated-bounce-in-down - @extend %animated - +animation-name(bounceInDown) - - -+keyframes(bounceOutUp) - 0% - +transform(translateY(0)) - 20% - opacity: 1 - +transform(translateY(20px)) - 100% - opacity: 0 - +transform(translateY(-2000px)) - -%animated-bounce-out-up - @extend %animated - +animation-name(bounceOutUp) diff --git a/app/assets/stylesheets/utils/_buttons.sass b/app/assets/stylesheets/utils/_buttons.sass deleted file mode 100644 index 3a50c773..00000000 --- a/app/assets/stylesheets/utils/_buttons.sass +++ /dev/null @@ -1,128 +0,0 @@ -$icon-types: ok $green, add $green, remove $red, edit $yellow -$ypos : 0 - -// just the icon without label -%icon - width: 40px - height: 40px - display: inline-block - background: image-url("shared/buttons_small.png") no-repeat - background-position: -40px -40px * $ypos - -// small buttons with labels aside -%icon-button - color: nth(nth($icon-types, 1), 2) - @extend %smaller-text - line-height: 40px - height: 40px - text-transform: uppercase - border: none - display: inline-block - vertical-align: top - +custom-sans(bold) - background: none - padding: 0 - text-shadow: none - box-shadow: none - - span - width: 40px - height: 40px - margin: 0 0 0 15px - display: inline-block - float: right - background: image-url("shared/buttons_small.png") no-repeat - background-position: 0 -40px * $ypos - -@each $type in $icon-types - %icon-button-#{nth($type, 1)} - color: nth($type, 2) - @extend %icon-button - - span - background-position: 0 -40px * $ypos - - &:hover - span - background-position: -40px -40px * $ypos - - // just the icon without label - %icon-#{nth($type, 1)} - @extend %icon - background-position: -40px -40px * $ypos - - $ypos: $ypos + 1 - -// ---------------------------------------------------------------------------- - -$button-types: ok $green, remove $red, edit $yellow -$ypos : 0 - -// large buttons with label beneath -%large-icon-button - border: none - background: none - +custom-sans(bold) - @extend %smaller-text - text-transform: uppercase - display: inline-block - color: nth(nth($button-types, 1), 2) - text-shadow: none - box-shadow: none - - span - display: block - width: 96px - height: 96px - margin: 0 auto 0.75em - background: transparent image-url("shared/buttons_big.png") no-repeat 0 -96px * $ypos - -@each $type in $button-types - %large-icon-button-#{nth($type, 1)} - color: nth($type, 2) - @extend %large-icon-button - - span - background-position: 0 -96px * $ypos - - &:hover - span - background-position: -96px -96px * $ypos - - $ypos: $ypos + 1 - -// ---------------------------------------------------------------------------- - -%secondary-button - display: inline-block - text-align: center - background: $blue - color: $white - text-shadow: 0 1px 1px transparentize($black, 0.8) - @extend %small-text - padding: 0.25em 1.5em - text-transform: uppercase - border: none - border-radius: 3px - +custom-sans(medium) - +transition(background 0.25s ease-in-out) - box-shadow: 0 2px 3px $light-gray - - &:visited - color: $white - - &:hover - background: darken($blue, 10%) - - &:active - +transform(translateY(1px)) - box-shadow: 0 1px 1px $light-gray - -a.reveal - @extend %secondary-button - -%secondary-button-large - @extend %secondary-button - text-transform: uppercase - font-size: 1.15em - padding: 0.5em 1.5em diff --git a/app/assets/stylesheets/utils/_fonts.sass b/app/assets/stylesheets/utils/_fonts.sass deleted file mode 100644 index 55126de4..00000000 --- a/app/assets/stylesheets/utils/_fonts.sass +++ /dev/null @@ -1,29 +0,0 @@ -$weights: bold bold, medium normal, normal normal - -@each $weight in $weights - +font-face(sonar-#{nth($weight, 1)}, '/fonts/sonar-#{nth($weight, 1)}-webfont', nth($weight, 2)) - -=custom-sans($weight: normal) - font-family: "omnes-pro", Helvetica, Arial, sans-serif - @if $weight == thin - font-weight: 100 - @else if $weight == light - font-weight: 300 - @else if $weight == medium - font-weight: 500 - @else if $weight == bold - font-weight: 600 - @else if $weight == black - font-weight: 900 - @else - font-weight: 400 - -=custom-serif($weight: normal, $style: normal) - font-family: "freight-text-pro", Georgia, serif - @if $weight == bold - font-weight: 700 - @else if $weight == light - font-weight: 300 - @else - font-weight: 400 - font-style: $style diff --git a/app/assets/stylesheets/utils/_forms.sass b/app/assets/stylesheets/utils/_forms.sass deleted file mode 100644 index 69d2b1f3..00000000 --- a/app/assets/stylesheets/utils/_forms.sass +++ /dev/null @@ -1,103 +0,0 @@ -form - text-align: center - - input, textarea - width: 100% - padding: 0.75em 15px 0.9em - background: $extra-light-gray - border: 1px solid $extra-light-gray - margin: 0 0 2px 0 - font-size: 1.25em - outline: none - +custom-sans(light) - z-index: 1 - position: relative - resize: none - - +placeholder - color: $gray - - &.radio - +inline-block - width: auto - - &.markdown - background-image: image-url('shared/markdown.png') - background-repeat: no-repeat - background-position: top right - - &.title - font-size: 1.35em - +custom-sans(medium) - - &.validation-error - border-bottom: 1px solid $red - - +placeholder - color: lighten($red, 30) - - &:focus - z-index: 1000 - border: 1px solid $blue - box-shadow: 0 0 10px transparentize($black, 0.85) - background: $white - - &.markdown - background-image: none - - button - display: block - margin: 1em auto - @extend %secondary-button-large - - fieldset - border: none - padding: 0 - - label - span.label - html.js & - display: none - - fieldset, - label - display: block - margin: 0 0 1.25em - position: relative - - span.validation-error-message - position: absolute - right: 15px - top: 0 - color: $red - z-index: 1000 - line-height: 4em - - hr - background-color: $light-gray - border-width: 0 - color: $light-gray - height: 1px - line-height: 1em - margin: 1.25em auto - text-align: center - width: 50% - - span.divider - display: inline-block - - label.inline - display: inline - - input - display: inline - width: auto - margin: 0 0.25em 0 0 - vertical-align: baseline - - small - font-size: 0.85em - text-align: right - display: block - color: $medium-gray - padding: 0.5em 15px diff --git a/app/assets/stylesheets/utils/_placeholders.sass b/app/assets/stylesheets/utils/_placeholders.sass deleted file mode 100644 index 23b606ad..00000000 --- a/app/assets/stylesheets/utils/_placeholders.sass +++ /dev/null @@ -1,63 +0,0 @@ -%clearfix - +clearfix - -%smaller-text - font-size: 0.825em - -%small-text - font-size: 0.75em - -=ornamental-divider - content: "." - text-indent: -999em - display: block - margin: 0.5em auto 1.5em - width: 100% - height: 1px - border-bottom: 1px solid $light-gray - - +media($mid-range) - width: 200px - - +media($smartphone-portrait) - width: 280px - -%ornamental-divider-after - &:after - +ornamental-divider - -%ornamental-divider-before - &:before - +ornamental-divider - -%unlisted - list-style: none - margin: 0 - padding: 0 - -%inline-nav-list - @extend %unlisted - - li - display: inline-block - @extend %smaller-text - - &:after - content: "•" - color: $light-gray - padding: 0 1em - - &:last-child:after - content: none - - a - color: $medium-gray - border: none - text-transform: uppercase - +custom-sans(medium) - - &:hover - color: $blue - -%transition-all-15s-ease-out - +transition(all 0.15s ease-out) diff --git a/app/assets/stylesheets/vendor/animate-custom.scss b/app/assets/stylesheets/vendor/animate-custom.scss new file mode 100644 index 00000000..13b9f74e --- /dev/null +++ b/app/assets/stylesheets/vendor/animate-custom.scss @@ -0,0 +1,127 @@ +@mixin animated { + animation-fill-mode:both; + animation-duration:1s; +} + +@keyframes flipInX { + 0% { + transform: perspective(400px) rotateX(90deg); + opacity: 0; + } + + 40% { + transform: perspective(400px) rotateX(-10deg); + } + + 70% { + transform: perspective(400px) rotateX(10deg); + } + + 100% { + transform: perspective(400px) rotateX(0deg); + opacity: 1; + } +} + +@mixin flip-in-x { + backface-visibility: visible !important; + animation-name: flipInX; +} + +@keyframes flipInY { + 0% { + transform: perspective(400px) rotateY(90deg); + opacity: 0; + } + + 40% { + transform: perspective(400px) rotateY(-10deg); + } + + 70% { + transform: perspective(400px) rotateY(10deg); + } + + 100% { + transform: perspective(400px) rotateY(0deg); + opacity: 1; + } +} + +@mixin flip-in-y { + backface-visibility: visible !important; + animation-name: flipInY; +} + +@keyframes fadeIn { + 0% {opacity: 0;} + 100% {opacity: 1;} +} + +@keyframes fadeInLeft { + 0% { + opacity: 0; + transform: translateX(-20px); + } + + 100% { + opacity: 1; + transform: translateX(0); + } +} + +@mixin fade-in-left { + animation-name: fadeInLeft; +} + +@mixin fade-in { + animation-name: fadeIn; +} + +@keyframes fadeInUp { + 0% { + opacity: 0; + transform: translateY(20px); + } + + 100% { + opacity: 1; + transform: translateY(0); + } +} + +@mixin fade-in-up { + animation-name: fadeInUp; +} + +@keyframes fadeInDown { + 0% { + opacity: 0; + transform: translateY(-20px); + } + + 100% { + opacity: 1; + transform: translateY(0); + } +} + +@mixin fade-in-down { + animation-name: fadeInDown; +} + +@keyframes fadeInRight { + 0% { + opacity: 0; + transform: translateX(20px); + } + + 100% { + opacity: 1; + transform: translateX(0); + } +} + +@mixin fade-in-right { + animation-name: fadeInRight; +} diff --git a/app/assets/stylesheets/vendor/normalize.css b/app/assets/stylesheets/vendor/normalize.css new file mode 100644 index 00000000..d4210aac --- /dev/null +++ b/app/assets/stylesheets/vendor/normalize.css @@ -0,0 +1,504 @@ +/*! normalize.css v1.0.1 | MIT License | git.io/normalize */ + +/* ========================================================================== + HTML5 display definitions + ========================================================================== */ + +/* + * Corrects `block` display not defined in IE 6/7/8/9 and Firefox 3. + */ + +article, +aside, +details, +figcaption, +figure, +footer, +header, +hgroup, +nav, +section, +summary { + display: block; +} + +/* + * Corrects `inline-block` display not defined in IE 6/7/8/9 and Firefox 3. + */ + +audio, +canvas, +video { + display: inline-block; + *display: inline; + *zoom: 1; +} + +/* + * Prevents modern browsers from displaying `audio` without controls. + * Remove excess height in iOS 5 devices. + */ + +audio:not([controls]) { + display: none; + height: 0; +} + +/* + * Addresses styling for `hidden` attribute not present in IE 7/8/9, Firefox 3, + * and Safari 4. + * Known issue: no IE 6 support. + */ + +[hidden] { + display: none; +} + +/* ========================================================================== + Base + ========================================================================== */ + +/* + * 1. Corrects text resizing oddly in IE 6/7 when body `font-size` is set using + * `em` units. + * 2. Prevents iOS text size adjust after orientation change, without disabling + * user zoom. + */ + +html { + font-size: 100%; /* 1 */ + -webkit-text-size-adjust: 100%; /* 2 */ + -ms-text-size-adjust: 100%; /* 2 */ +} + +/* + * Addresses `font-family` inconsistency between `textarea` and other form + * elements. + */ + +html, +button, +input, +select, +textarea { + font-family: sans-serif; +} + +/* + * Addresses margins handled incorrectly in IE 6/7. + */ + +body { + margin: 0; +} + +/* ========================================================================== + Links + ========================================================================== */ + +/* + * Addresses `outline` inconsistency between Chrome and other browsers. + */ + +a:focus { + outline: thin dotted; +} + +/* + * Improves readability when focused and also mouse hovered in all browsers. + */ + +a:active, +a:hover { + outline: 0; +} + +/* ========================================================================== + Typography + ========================================================================== */ + +/* + * Addresses font sizes and margins set differently in IE 6/7. + * Addresses font sizes within `section` and `article` in Firefox 4+, Safari 5, + * and Chrome. + */ + +h1 { + font-size: 2em; + margin: 0.67em 0; +} + +h2 { + font-size: 1.5em; + margin: 0.83em 0; +} + +h3 { + font-size: 1.17em; + margin: 1em 0; +} + +h4 { + font-size: 1em; + margin: 1.33em 0; +} + +h5 { + font-size: 0.83em; + margin: 1.67em 0; +} + +h6 { + font-size: 0.75em; + margin: 2.33em 0; +} + +/* + * Addresses styling not present in IE 7/8/9, Safari 5, and Chrome. + */ + +abbr[title] { + border-bottom: 1px dotted; +} + +/* + * Addresses style set to `bolder` in Firefox 3+, Safari 4/5, and Chrome. + */ + +b, +strong { + font-weight: bold; +} + +blockquote { + margin: 1em 40px; +} + +/* + * Addresses styling not present in Safari 5 and Chrome. + */ + +dfn { + font-style: italic; +} + +/* + * Addresses styling not present in IE 6/7/8/9. + */ + +mark { + background: #ff0; + color: #000; +} + +/* + * Addresses margins set differently in IE 6/7. + */ + +p, +pre { + margin: 1em 0; +} + +/* + * Corrects font family set oddly in IE 6, Safari 4/5, and Chrome. + */ + +code, +kbd, +pre, +samp { + font-family: monospace, serif; + _font-family: 'courier new', monospace; + font-size: 1em; +} + +/* + * Improves readability of pre-formatted text in all browsers. + */ + +pre { + white-space: pre; + white-space: pre-wrap; + word-wrap: break-word; +} + +/* + * Addresses CSS quotes not supported in IE 6/7. + */ + +q { + quotes: none; +} + +/* + * Addresses `quotes` property not supported in Safari 4. + */ + +q:before, +q:after { + content: ''; + content: none; +} + +/* + * Addresses inconsistent and variable font size in all browsers. + */ + +small { + font-size: 80%; +} + +/* + * Prevents `sub` and `sup` affecting `line-height` in all browsers. + */ + +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} + +sup { + top: -0.5em; +} + +sub { + bottom: -0.25em; +} + +/* ========================================================================== + Lists + ========================================================================== */ + +/* + * Addresses margins set differently in IE 6/7. + */ + +dl, +menu, +ol, +ul { + margin: 1em 0; +} + +dd { + margin: 0 0 0 40px; +} + +/* + * Addresses paddings set differently in IE 6/7. + */ + +menu, +ol, +ul { + padding: 0 0 0 40px; +} + +/* + * Corrects list images handled incorrectly in IE 7. + */ + +nav ul, +nav ol { + list-style: none; + list-style-image: none; +} + +/* ========================================================================== + Embedded content + ========================================================================== */ + +/* + * 1. Removes border when inside `a` element in IE 6/7/8/9 and Firefox 3. + * 2. Improves image quality when scaled in IE 7. + */ + +img { + border: 0; /* 1 */ + -ms-interpolation-mode: bicubic; /* 2 */ +} + +/* + * Corrects overflow displayed oddly in IE 9. + */ + +svg:not(:root) { + overflow: hidden; +} + +/* ========================================================================== + Figures + ========================================================================== */ + +/* + * Addresses margin not present in IE 6/7/8/9, Safari 5, and Opera 11. + */ + +figure { + margin: 0; +} + +/* ========================================================================== + Forms + ========================================================================== */ + +/* + * Corrects margin displayed oddly in IE 6/7. + */ + +form { + margin: 0; +} + +/* + * Define consistent border, margin, and padding. + */ + +fieldset { + border: 1px solid #c0c0c0; + margin: 0 2px; + padding: 0.35em 0.625em 0.75em; +} + +/* + * 1. Corrects color not being inherited in IE 6/7/8/9. + * 2. Corrects text not wrapping in Firefox 3. + * 3. Corrects alignment displayed oddly in IE 6/7. + */ + +legend { + border: 0; /* 1 */ + padding: 0; + white-space: normal; /* 2 */ + *margin-left: -7px; /* 3 */ +} + +/* + * 1. Corrects font size not being inherited in all browsers. + * 2. Addresses margins set differently in IE 6/7, Firefox 3+, Safari 5, + * and Chrome. + * 3. Improves appearance and consistency in all browsers. + */ + +button, +input, +select, +textarea { + font-size: 100%; /* 1 */ + margin: 0; /* 2 */ + vertical-align: baseline; /* 3 */ + *vertical-align: middle; /* 3 */ +} + +/* + * Addresses Firefox 3+ setting `line-height` on `input` using `!important` in + * the UA stylesheet. + */ + +button, +input { + line-height: normal; +} + +/* + * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` + * and `video` controls. + * 2. Corrects inability to style clickable `input` types in iOS. + * 3. Improves usability and consistency of cursor style between image-type + * `input` and others. + * 4. Removes inner spacing in IE 7 without affecting normal text inputs. + * Known issue: inner spacing remains in IE 6. + */ + +button, +html input[type="button"], /* 1 */ +input[type="reset"], +input[type="submit"] { + -webkit-appearance: button; /* 2 */ + cursor: pointer; /* 3 */ + *overflow: visible; /* 4 */ +} + +/* + * Re-set default cursor for disabled elements. + */ + +button[disabled], +input[disabled] { + cursor: default; +} + +/* + * 1. Addresses box sizing set to content-box in IE 8/9. + * 2. Removes excess padding in IE 8/9. + * 3. Removes excess padding in IE 7. + * Known issue: excess padding remains in IE 6. + */ + +input[type="checkbox"], +input[type="radio"] { + box-sizing: border-box; /* 1 */ + padding: 0; /* 2 */ + *height: 13px; /* 3 */ + *width: 13px; /* 3 */ +} + +/* + * 1. Addresses `appearance` set to `searchfield` in Safari 5 and Chrome. + * 2. Addresses `box-sizing` set to `border-box` in Safari 5 and Chrome + * (include `-moz` to future-proof). + */ + +input[type="search"] { + -webkit-appearance: textfield; /* 1 */ + -moz-box-sizing: content-box; + -webkit-box-sizing: content-box; /* 2 */ + box-sizing: content-box; +} + +/* + * Removes inner padding and search cancel button in Safari 5 and Chrome + * on OS X. + */ + +input[type="search"]::-webkit-search-cancel-button, +input[type="search"]::-webkit-search-decoration { + -webkit-appearance: none; +} + +/* + * Removes inner padding and border in Firefox 3+. + */ + +button::-moz-focus-inner, +input::-moz-focus-inner { + border: 0; + padding: 0; +} + +/* + * 1. Removes default vertical scrollbar in IE 6/7/8/9. + * 2. Improves readability and alignment in all browsers. + */ + +textarea { + overflow: auto; /* 1 */ + vertical-align: top; /* 2 */ +} + +/* ========================================================================== + Tables + ========================================================================== */ + +/* + * Remove most spacing between table cells. + */ + +table { + border-collapse: collapse; + border-spacing: 0; +} diff --git a/app/views/activities/_activity_day.html.haml b/app/views/activities/_activity_day.html.haml index a3d4ad53..73ed1474 100644 --- a/app/views/activities/_activity_day.html.haml +++ b/app/views/activities/_activity_day.html.haml @@ -1,3 +1,5 @@ .activity-day %h1= I18n.l(day.to_time, format: :nice_date) - = render ActivityDecorator.decorate_collection(activities) + + %ul.activity-list + = render ActivityDecorator.decorate_collection(activities) diff --git a/app/views/activities/edit.html.haml b/app/views/activities/edit.html.haml index d099ccc0..1c6715da 100644 --- a/app/views/activities/edit.html.haml +++ b/app/views/activities/edit.html.haml @@ -1,21 +1,16 @@ -%article - %header - %h2 - = t("edit_activity.title") +%h2 + = t("edit_activity.title") - %p.meta - = link_to t("activities.more_info.label_long"), @activity - %strong.divider - = t("common.or").downcase - = link_to t("activities.new.label").downcase, new_activity_path +%p.meta + = link_to t("activities.more_info.label_long"), @activity + %strong.divider + = t("common.or").downcase + = link_to t("activities.new.label").downcase, new_activity_path - = render "form" += render "form" +%h2= t("activities.danger_zone") +%p.meta + %strong= t("delete_activity_form.title") -%article.delete-activity - %header - %h2= t("activities.danger_zone") - %p.meta - %strong= t("delete_activity_form.title") - - = render "delete_form" += render "delete_form" diff --git a/app/views/activities/index.html.haml b/app/views/activities/index.html.haml index 6461d514..da86527c 100644 --- a/app/views/activities/index.html.haml +++ b/app/views/activities/index.html.haml @@ -28,6 +28,6 @@ %span = t("activities.new.label") -%ul#activities +%section - @activities_per_day.each do |day, activities| = render 'activity_day', day: day, activities: activities diff --git a/app/views/activities/new.html.haml b/app/views/activities/new.html.haml index 22cfa623..91e8ee96 100644 --- a/app/views/activities/new.html.haml +++ b/app/views/activities/new.html.haml @@ -1,6 +1,4 @@ -%article - %header - %h2 - = t("new_activity.title") +%h2 + = t("new_activity.title") - = render "form" += render "form" diff --git a/app/views/activities/show.html.haml b/app/views/activities/show.html.haml index 076759b8..dee70ef4 100644 --- a/app/views/activities/show.html.haml +++ b/app/views/activities/show.html.haml @@ -1,64 +1,61 @@ -%article#activity - %header - %h2 - = @activity.name - - if @activity.requires_eurucamp_ticket - %br - = image_tag 'shared/isleofruby-2015.svg', - class: 'requires_eurucamp_ticket', - alt: t('activities.requires_eurucamp_ticket'), - title: t('activities.requires_eurucamp_ticket') - %p.meta - = t("activities.organized_by", name: @activity.creator_name).html_safe +%h2 + = @activity.name + - if @activity.requires_eurucamp_ticket + %br + = image_tag 'shared/IoR_illustration.svg', + class: 'requires_eurucamp_ticket', + alt: t('activities.requires_eurucamp_ticket'), + title: t('activities.requires_eurucamp_ticket') +%p.meta + = t("activities.organized_by", name: @activity.creator_name).html_safe - = render "action", activity: @activity, type: @activity.relation_ship_with(current_user), remote: false += render "action", activity: @activity, type: @activity.relation_ship_with(current_user), remote: false - %section.overview - = render "activities/progress", activity: @activity - %p - = @activity.room_left - - if @activity.participants.any? - .participants - %h3 - = t("activities.participants") - %ul - - ParticipantDecorator.decorate_collection(@activity.participants).each do |participant| - %li(title="#{ participant.name }")> - = image_tag(participant.avatar_url(48)) +%section.overview += render "activities/progress", activity: @activity +%p + = @activity.room_left +- if @activity.participants.any? + .participants + %h3 + = t("activities.participants") + %ul + - ParticipantDecorator.decorate_collection(@activity.participants).each do |participant| + %li(title="#{ participant.name }")> + = image_tag(participant.avatar_url(48)) - %section.description - .wrapper - = @activity.description_markdown +%section.description +.wrapper + = @activity.description_markdown - - unless @activity.requirements_markdown.empty? - %h4 - = t("activities.required") - = @activity.requirements_markdown + - unless @activity.requirements_markdown.empty? + %h4 + = t("activities.required") + = @activity.requirements_markdown - %section.details - %dl.wrapper - - if @activity.anytime? - %dt - = t("activities.time_and_date") - %dd - = t("activities.anytime") - - else - %dt - = t("activities.start.label_short") - %dd - = l(@activity.start_time, format: :nice_date_and_time) - %dt - = t("activities.end.label_short") - %dd - = l(@activity.end_time, format: :nice_date_and_time) - %dt - = t("activities.location") - %dd - = @activity.location - %br - %br - %a#show-map= t("activity_form.location.show-map") - - #meeting-point-map.meeting-point-map - = image_tag 'activities/meeting_points.svg' +%section.details +%dl.wrapper + - if @activity.anytime? + %dt + = t("activities.time_and_date") + %dd + = t("activities.anytime") + - else + %dt + = t("activities.start.label_short") + %dd + = l(@activity.start_time, format: :nice_date_and_time) + %dt + = t("activities.end.label_short") + %dd + = l(@activity.end_time, format: :nice_date_and_time) + %dt + = t("activities.location") + %dd + = @activity.location + %br + %br + %a#show-map= t("activity_form.location.show-map") +#meeting-point-map.meeting-point-map + = image_tag 'activities/meeting_points.svg' diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml index a6c6b91c..01b20cae 100644 --- a/app/views/layouts/application.html.haml +++ b/app/views/layouts/application.html.haml @@ -7,9 +7,14 @@ %body(class="#{ body_class(controller.action_name) }") = render "partials/chrome_frame" = render "partials/notifications" - #container - = render "partials/header" - #main(role="main") - = yield - = render "partials/footer" + = render "partials/header" + #main(role="main") + %article.page-section.page-section--sub-page + .page-section__wrapper + .page-section__image + = image_tag "activities/activities.svg", class: "page-section__image--vertical" + -#= image_tag "activities/activities_mobile.svg", class: "page-section__image--horizontal" + .page-section__inner + = yield + = render "partials/footer" = javascript_include_tag "application" diff --git a/app/views/partials/_header.html.haml b/app/views/partials/_header.html.haml index 453aa1f3..66c5b7c7 100644 --- a/app/views/partials/_header.html.haml +++ b/app/views/partials/_header.html.haml @@ -1,9 +1,8 @@ -%header#header +%header.page-header + .page-header__sky + .page-header__logo +%header.page-header-mobile + .page-header-mobile-sky + .page-header-mobile-logo - = render "partials/navigation" - - -# the logo, link home - %h1 - = link_to "/" do - Isle of Ruby - %h2 Events around Isle of Ruby + -#= render "partials/navigation" diff --git a/app/views/partials/_layout_head.html.haml b/app/views/partials/_layout_head.html.haml index 0833e9fb..54dc2760 100644 --- a/app/views/partials/_layout_head.html.haml +++ b/app/views/partials/_layout_head.html.haml @@ -8,6 +8,4 @@ = csrf_meta_tags = stylesheet_link_tag "application" -= javascript_include_tag :modernizr -= javascript_include_tag "//use.typekit.net/ubd3myx.js"