Merge pull request #560 from hennevogel/feature-user-view

Various small UI fixes
This commit is contained in:
Henne Vogelsang 2014-12-09 15:52:44 +01:00
commit b65a33f047
64 changed files with 932 additions and 2243 deletions

View file

@ -46,6 +46,7 @@ gem 'uglifier', '>= 1.3.0'
# as the front-end framework
gem 'bootstrap-sass'
gem 'autoprefixer-rails'
gem 'formtastic-bootstrap'
gem 'formtastic', '~> 2.3.0.rc3'
gem 'momentjs-rails', '>= 2.8.1'

View file

@ -56,6 +56,8 @@ GEM
uuidtools
arel (5.0.1.20140414130214)
ast (2.0.0)
autoprefixer-rails (4.0.1)
execjs
awesome_nested_set (3.0.0.rc.5)
activerecord (>= 4.0.0, < 5)
axlsx (2.0.1)
@ -432,6 +434,7 @@ DEPENDENCIES
acts_as_commentable_with_threading
acts_as_list
ahoy_matey
autoprefixer-rails
awesome_nested_set (~> 3.0.0.rc.5)
axlsx_rails
bootstrap-sass

View file

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Before After
Before After

View file

@ -12,6 +12,7 @@
//
//= require jquery
//= require jquery_ujs
//= require waypoints
//= require dataTables/jquery.dataTables
//= require dataTables/bootstrap/3/jquery.dataTables.bootstrap
//= require cocoon

File diff suppressed because it is too large Load diff

View file

@ -1,20 +1,47 @@
$(function () {
$("#registration-arrival-datepicker").datetimepicker({
pickTime: true,
format: "YYYY-MM-DD hh:mm"
useCurrent: false,
minuteStepping: 15,
sideBySide: true,
format: "YYYY-MM-DD HH:mm"
});
$("#registration-departure-datepicker").datetimepicker({
pickTime: true,
format: "YYYY-MM-DD hh:mm"
useCurrent: false,
minuteStepping: 15,
sideBySide: true,
format: "YYYY-MM-DD HH:mm"
});
$("#registration-arrival-datepicker").on("dp.change",function (e) {
console.log (e.date)
$('#registration-departure-datepicker').data("DateTimePicker").setDate(e.date);
$('#registration-departure-datepicker').data("DateTimePicker").setMinDate(e.date);
});
$("#conference-end-datepicker").on("dp.change",function (e) {
$('#registration-arrival-datepicker').data("DateTimePicker").setMaxDate(e.date);
});
$("#conference-start-datepicker").datetimepicker({
pickTime: false,
useCurrent: false,
format: "YYYY-MM-DD"
});
$("#conference-end-datepicker").datetimepicker({
pickTime: false,
useCurrent: false,
format: "YYYY-MM-DD"
});
$("#conference-start-datepicker").on("dp.change",function (e) {
console.log (e.date)
$('#conference-end-datepicker').data("DateTimePicker").setDate(e.date);
$('#conference-end-datepicker').data("DateTimePicker").setMinDate(e.date);
});
$("#conference-end-datepicker").on("dp.change",function (e) {
$('#conference-start-datepicker').data("DateTimePicker").setMaxDate(e.date);
});
$("#registration-period-start-datepicker").datetimepicker({
format: "YYYY-MM-DD",
pickTime: false,
@ -25,6 +52,15 @@ $(function () {
pickTime: false,
pickSeconds: false
});
$("#registration-period-start-datepicker").on("dp.change",function (e) {
console.log (e.date)
$('#registration-period-end-datepicker').data("DateTimePicker").setDate(e.date);
$('#registration-period-end-datepicker').data("DateTimePicker").setMinDate(e.date);
});
$("#registration-period-end-datepicker").on("dp.change",function (e) {
$('#registration-period-start-datepicker').data("DateTimePicker").setMaxDate(e.date);
});
$(".target-due-date-datepicker").datetimepicker({
pickTime: false,
format: "YYYY-MM-DD"

View file

@ -0,0 +1,517 @@
// Generated by CoffeeScript 1.6.2
/*!
jQuery Waypoints - v2.0.5
Copyright (c) 2011-2014 Caleb Troughton
Licensed under the MIT license.
https://github.com/imakewebthings/jquery-waypoints/blob/master/licenses.txt
*/
(function() {
var __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; },
__slice = [].slice;
(function(root, factory) {
if (typeof define === 'function' && define.amd) {
return define('waypoints', ['jquery'], function($) {
return factory($, root);
});
} else {
return factory(root.jQuery, root);
}
})(window, function($, window) {
var $w, Context, Waypoint, allWaypoints, contextCounter, contextKey, contexts, isTouch, jQMethods, methods, resizeEvent, scrollEvent, waypointCounter, waypointKey, wp, wps;
$w = $(window);
isTouch = __indexOf.call(window, 'ontouchstart') >= 0;
allWaypoints = {
horizontal: {},
vertical: {}
};
contextCounter = 1;
contexts = {};
contextKey = 'waypoints-context-id';
resizeEvent = 'resize.waypoints';
scrollEvent = 'scroll.waypoints';
waypointCounter = 1;
waypointKey = 'waypoints-waypoint-ids';
wp = 'waypoint';
wps = 'waypoints';
Context = (function() {
function Context($element) {
var _this = this;
this.$element = $element;
this.element = $element[0];
this.didResize = false;
this.didScroll = false;
this.id = 'context' + contextCounter++;
this.oldScroll = {
x: $element.scrollLeft(),
y: $element.scrollTop()
};
this.waypoints = {
horizontal: {},
vertical: {}
};
this.element[contextKey] = this.id;
contexts[this.id] = this;
$element.bind(scrollEvent, function() {
var scrollHandler;
if (!(_this.didScroll || isTouch)) {
_this.didScroll = true;
scrollHandler = function() {
_this.doScroll();
return _this.didScroll = false;
};
return window.setTimeout(scrollHandler, $[wps].settings.scrollThrottle);
}
});
$element.bind(resizeEvent, function() {
var resizeHandler;
if (!_this.didResize) {
_this.didResize = true;
resizeHandler = function() {
$[wps]('refresh');
return _this.didResize = false;
};
return window.setTimeout(resizeHandler, $[wps].settings.resizeThrottle);
}
});
}
Context.prototype.doScroll = function() {
var axes,
_this = this;
axes = {
horizontal: {
newScroll: this.$element.scrollLeft(),
oldScroll: this.oldScroll.x,
forward: 'right',
backward: 'left'
},
vertical: {
newScroll: this.$element.scrollTop(),
oldScroll: this.oldScroll.y,
forward: 'down',
backward: 'up'
}
};
if (isTouch && (!axes.vertical.oldScroll || !axes.vertical.newScroll)) {
$[wps]('refresh');
}
$.each(axes, function(aKey, axis) {
var direction, isForward, triggered;
triggered = [];
isForward = axis.newScroll > axis.oldScroll;
direction = isForward ? axis.forward : axis.backward;
$.each(_this.waypoints[aKey], function(wKey, waypoint) {
var _ref, _ref1;
if ((axis.oldScroll < (_ref = waypoint.offset) && _ref <= axis.newScroll)) {
return triggered.push(waypoint);
} else if ((axis.newScroll < (_ref1 = waypoint.offset) && _ref1 <= axis.oldScroll)) {
return triggered.push(waypoint);
}
});
triggered.sort(function(a, b) {
return a.offset - b.offset;
});
if (!isForward) {
triggered.reverse();
}
return $.each(triggered, function(i, waypoint) {
if (waypoint.options.continuous || i === triggered.length - 1) {
return waypoint.trigger([direction]);
}
});
});
return this.oldScroll = {
x: axes.horizontal.newScroll,
y: axes.vertical.newScroll
};
};
Context.prototype.refresh = function() {
var axes, cOffset, isWin,
_this = this;
isWin = $.isWindow(this.element);
cOffset = this.$element.offset();
this.doScroll();
axes = {
horizontal: {
contextOffset: isWin ? 0 : cOffset.left,
contextScroll: isWin ? 0 : this.oldScroll.x,
contextDimension: this.$element.width(),
oldScroll: this.oldScroll.x,
forward: 'right',
backward: 'left',
offsetProp: 'left'
},
vertical: {
contextOffset: isWin ? 0 : cOffset.top,
contextScroll: isWin ? 0 : this.oldScroll.y,
contextDimension: isWin ? $[wps]('viewportHeight') : this.$element.height(),
oldScroll: this.oldScroll.y,
forward: 'down',
backward: 'up',
offsetProp: 'top'
}
};
return $.each(axes, function(aKey, axis) {
return $.each(_this.waypoints[aKey], function(i, waypoint) {
var adjustment, elementOffset, oldOffset, _ref, _ref1;
adjustment = waypoint.options.offset;
oldOffset = waypoint.offset;
elementOffset = $.isWindow(waypoint.element) ? 0 : waypoint.$element.offset()[axis.offsetProp];
if ($.isFunction(adjustment)) {
adjustment = adjustment.apply(waypoint.element);
} else if (typeof adjustment === 'string') {
adjustment = parseFloat(adjustment);
if (waypoint.options.offset.indexOf('%') > -1) {
adjustment = Math.ceil(axis.contextDimension * adjustment / 100);
}
}
waypoint.offset = elementOffset - axis.contextOffset + axis.contextScroll - adjustment;
if ((waypoint.options.onlyOnScroll && (oldOffset != null)) || !waypoint.enabled) {
return;
}
if (oldOffset !== null && (oldOffset < (_ref = axis.oldScroll) && _ref <= waypoint.offset)) {
return waypoint.trigger([axis.backward]);
} else if (oldOffset !== null && (oldOffset > (_ref1 = axis.oldScroll) && _ref1 >= waypoint.offset)) {
return waypoint.trigger([axis.forward]);
} else if (oldOffset === null && axis.oldScroll >= waypoint.offset) {
return waypoint.trigger([axis.forward]);
}
});
});
};
Context.prototype.checkEmpty = function() {
if ($.isEmptyObject(this.waypoints.horizontal) && $.isEmptyObject(this.waypoints.vertical)) {
this.$element.unbind([resizeEvent, scrollEvent].join(' '));
return delete contexts[this.id];
}
};
return Context;
})();
Waypoint = (function() {
function Waypoint($element, context, options) {
var idList, _ref;
if (options.offset === 'bottom-in-view') {
options.offset = function() {
var contextHeight;
contextHeight = $[wps]('viewportHeight');
if (!$.isWindow(context.element)) {
contextHeight = context.$element.height();
}
return contextHeight - $(this).outerHeight();
};
}
this.$element = $element;
this.element = $element[0];
this.axis = options.horizontal ? 'horizontal' : 'vertical';
this.callback = options.handler;
this.context = context;
this.enabled = options.enabled;
this.id = 'waypoints' + waypointCounter++;
this.offset = null;
this.options = options;
context.waypoints[this.axis][this.id] = this;
allWaypoints[this.axis][this.id] = this;
idList = (_ref = this.element[waypointKey]) != null ? _ref : [];
idList.push(this.id);
this.element[waypointKey] = idList;
}
Waypoint.prototype.trigger = function(args) {
if (!this.enabled) {
return;
}
if (this.callback != null) {
this.callback.apply(this.element, args);
}
if (this.options.triggerOnce) {
return this.destroy();
}
};
Waypoint.prototype.disable = function() {
return this.enabled = false;
};
Waypoint.prototype.enable = function() {
this.context.refresh();
return this.enabled = true;
};
Waypoint.prototype.destroy = function() {
delete allWaypoints[this.axis][this.id];
delete this.context.waypoints[this.axis][this.id];
return this.context.checkEmpty();
};
Waypoint.getWaypointsByElement = function(element) {
var all, ids;
ids = element[waypointKey];
if (!ids) {
return [];
}
all = $.extend({}, allWaypoints.horizontal, allWaypoints.vertical);
return $.map(ids, function(id) {
return all[id];
});
};
return Waypoint;
})();
methods = {
init: function(f, options) {
var _ref;
options = $.extend({}, $.fn[wp].defaults, options);
if ((_ref = options.handler) == null) {
options.handler = f;
}
this.each(function() {
var $this, context, contextElement, _ref1;
$this = $(this);
contextElement = (_ref1 = options.context) != null ? _ref1 : $.fn[wp].defaults.context;
if (!$.isWindow(contextElement)) {
contextElement = $this.closest(contextElement);
}
contextElement = $(contextElement);
context = contexts[contextElement[0][contextKey]];
if (!context) {
context = new Context(contextElement);
}
return new Waypoint($this, context, options);
});
$[wps]('refresh');
return this;
},
disable: function() {
return methods._invoke.call(this, 'disable');
},
enable: function() {
return methods._invoke.call(this, 'enable');
},
destroy: function() {
return methods._invoke.call(this, 'destroy');
},
prev: function(axis, selector) {
return methods._traverse.call(this, axis, selector, function(stack, index, waypoints) {
if (index > 0) {
return stack.push(waypoints[index - 1]);
}
});
},
next: function(axis, selector) {
return methods._traverse.call(this, axis, selector, function(stack, index, waypoints) {
if (index < waypoints.length - 1) {
return stack.push(waypoints[index + 1]);
}
});
},
_traverse: function(axis, selector, push) {
var stack, waypoints;
if (axis == null) {
axis = 'vertical';
}
if (selector == null) {
selector = window;
}
waypoints = jQMethods.aggregate(selector);
stack = [];
this.each(function() {
var index;
index = $.inArray(this, waypoints[axis]);
return push(stack, index, waypoints[axis]);
});
return this.pushStack(stack);
},
_invoke: function(method) {
this.each(function() {
var waypoints;
waypoints = Waypoint.getWaypointsByElement(this);
return $.each(waypoints, function(i, waypoint) {
waypoint[method]();
return true;
});
});
return this;
}
};
$.fn[wp] = function() {
var args, method;
method = arguments[0], args = 2 <= arguments.length ? __slice.call(arguments, 1) : [];
if (methods[method]) {
return methods[method].apply(this, args);
} else if ($.isFunction(method)) {
return methods.init.apply(this, arguments);
} else if ($.isPlainObject(method)) {
return methods.init.apply(this, [null, method]);
} else if (!method) {
return $.error("jQuery Waypoints needs a callback function or handler option.");
} else {
return $.error("The " + method + " method does not exist in jQuery Waypoints.");
}
};
$.fn[wp].defaults = {
context: window,
continuous: true,
enabled: true,
horizontal: false,
offset: 0,
triggerOnce: false
};
jQMethods = {
refresh: function() {
return $.each(contexts, function(i, context) {
return context.refresh();
});
},
viewportHeight: function() {
var _ref;
return (_ref = window.innerHeight) != null ? _ref : $w.height();
},
aggregate: function(contextSelector) {
var collection, waypoints, _ref;
collection = allWaypoints;
if (contextSelector) {
collection = (_ref = contexts[$(contextSelector)[0][contextKey]]) != null ? _ref.waypoints : void 0;
}
if (!collection) {
return [];
}
waypoints = {
horizontal: [],
vertical: []
};
$.each(waypoints, function(axis, arr) {
$.each(collection[axis], function(key, waypoint) {
return arr.push(waypoint);
});
arr.sort(function(a, b) {
return a.offset - b.offset;
});
waypoints[axis] = $.map(arr, function(waypoint) {
return waypoint.element;
});
return waypoints[axis] = $.unique(waypoints[axis]);
});
return waypoints;
},
above: function(contextSelector) {
if (contextSelector == null) {
contextSelector = window;
}
return jQMethods._filter(contextSelector, 'vertical', function(context, waypoint) {
return waypoint.offset <= context.oldScroll.y;
});
},
below: function(contextSelector) {
if (contextSelector == null) {
contextSelector = window;
}
return jQMethods._filter(contextSelector, 'vertical', function(context, waypoint) {
return waypoint.offset > context.oldScroll.y;
});
},
left: function(contextSelector) {
if (contextSelector == null) {
contextSelector = window;
}
return jQMethods._filter(contextSelector, 'horizontal', function(context, waypoint) {
return waypoint.offset <= context.oldScroll.x;
});
},
right: function(contextSelector) {
if (contextSelector == null) {
contextSelector = window;
}
return jQMethods._filter(contextSelector, 'horizontal', function(context, waypoint) {
return waypoint.offset > context.oldScroll.x;
});
},
enable: function() {
return jQMethods._invoke('enable');
},
disable: function() {
return jQMethods._invoke('disable');
},
destroy: function() {
return jQMethods._invoke('destroy');
},
extendFn: function(methodName, f) {
return methods[methodName] = f;
},
_invoke: function(method) {
var waypoints;
waypoints = $.extend({}, allWaypoints.vertical, allWaypoints.horizontal);
return $.each(waypoints, function(key, waypoint) {
waypoint[method]();
return true;
});
},
_filter: function(selector, axis, test) {
var context, waypoints;
context = contexts[$(selector)[0][contextKey]];
if (!context) {
return [];
}
waypoints = [];
$.each(context.waypoints[axis], function(i, waypoint) {
if (test(context, waypoint)) {
return waypoints.push(waypoint);
}
});
waypoints.sort(function(a, b) {
return a.offset - b.offset;
});
return $.map(waypoints, function(waypoint) {
return waypoint.element;
});
}
};
$[wps] = function() {
var args, method;
method = arguments[0], args = 2 <= arguments.length ? __slice.call(arguments, 1) : [];
if (jQMethods[method]) {
return jQMethods[method].apply(null, args);
} else {
return jQMethods.aggregate.call(null, method);
}
};
$[wps].settings = {
resizeThrottle: 100,
scrollThrottle: 30
};
return $w.on('load.waypoints', function() {
return $[wps]('refresh');
});
});
}).call(this);

View file

@ -7,9 +7,9 @@
*= require osem-schedule
*= require osem-schedule-print
*= require osem-dashboard
*= require osem-fonts
*= require osem-splash
*= require font-awesome
*= require osem-fonts
*= require bootstrap-markdown.min
*= require bootstrap-datetimepicker
*= require leaflet

View file

@ -3,11 +3,11 @@
@font-face {
font-family: "suse-icons";
src:url("fonts/suse-icons.eot");
src:url("fonts/suse-icons.eot?#iefix") format("embedded-opentype"),
url("fonts/suse-icons.woff") format("woff"),
url("fonts/suse-icons.ttf") format("truetype"),
url("fonts/suse-icons.svg#suse-icons") format("svg");
src:font-url("suse-icons.eot");
src:font-url("suse-icons.eot?#iefix") format("embedded-opentype"),
font-url("suse-icons.woff") format("woff"),
font-url("suse-icons.ttf") format("truetype"),
font-url("suse-icons.svg#suse-icons") format("svg");
font-weight: normal;
font-style: normal;
@ -22,8 +22,7 @@
text-transform: none !important;
speak: none;
line-height: 1;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
font-smoothing: antialiased;
}
/* We have to repeat this for every symbol that extends font awesome. So if
@ -38,8 +37,7 @@
text-transform: none !important;
speak: none;
line-height: 1;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
font-smoothing: antialiased;
}
.fa-suse:before {

View file

@ -49,4 +49,8 @@ body {
.col-top {
vertical-align:top;
}
fieldset {
margin: 20px 0 20px 0;
}

View file

@ -6,6 +6,7 @@ $navbar-default-link-color: #ffffff;
$navbar-default-link-hover-color: #000000;
@import 'bootstrap-datetimepicker';
@import "bootstrap-sprockets";
//
// Bootstrap components
@ -59,4 +60,4 @@ $navbar-default-link-hover-color: #000000;
// Utility classes
@import "bootstrap/utilities";
@import "bootstrap/responsive-utilities";
@import "bootstrap/responsive-utilities";

View file

@ -11,6 +11,18 @@ class ConferenceController < ApplicationController
@keynote_speakers = @conference.keynote_speakers
end
def schedule
@rooms = @conference.rooms
@events = @conference.events
@dates = @conference.start_date..@conference.end_date
if @dates == Date.current
@today = Date.current.strftime('%Y-%m-%d')
else
@today = @conference.start_date.strftime('%Y-%m-%d')
end
end
def gallery_photos
@photos = @conference.photos
render 'photos', formats: [:js]

View file

@ -9,24 +9,22 @@ class ConferenceRegistrationsController < ApplicationController
end
def show
@workshops = @registration.workshops if @registration
@workshops = @registration.workshops
@total_price = Ticket.total_price(@conference, current_user)
@tickets = current_user.ticket_purchases.where(conference_id: @conference.id)
end
def edit; end
def create
user_attributes = registration_params[:user_attributes]
params[:registration].delete :user_attributes
@registration = current_user.registrations.build(registration_params)
@registration.conference_id = @conference.id
if @registration.save && current_user.update_attributes(user_attributes)
if @registration.save
# Trigger ahoy event
ahoy.track 'Registered', title: 'New registration'
if @conference.tickets.any?
if @conference.tickets.any? && !current_user.supports?(@conference)
redirect_to conference_tickets_path(@conference.short_title),
notice: 'You are now registered and will be receiving E-Mail notifications.'
else
@ -66,6 +64,9 @@ class ConferenceRegistrationsController < ApplicationController
def set_registration
@registration = current_user.registrations.find_by(conference_id: @conference.id)
if !@registration
redirect_to new_conference_conference_registrations_path(@conference.short_title)
end
end
def registration_params

View file

@ -1,19 +0,0 @@
class ScheduleController < ApplicationController
authorize_resource class: false
layout 'application'
def index
@conference = Conference.
includes(:rooms, events: [:speakers, :track, :event_type]).
where(conferences: { short_title: params[:conference_id] }).first
@rooms = @conference.rooms
@events = @conference.events
@dates = @conference.start_date..@conference.end_date
if @dates == Date.current
@today = Date.current.strftime('%Y-%m-%d')
else
@today = @conference.start_date.strftime('%Y-%m-%d')
end
end
end

View file

@ -6,9 +6,12 @@ class TicketPurchasesController < ApplicationController
def create
message = TicketPurchase.purchase(@conference, current_user, params[:tickets][0])
if message.blank?
redirect_to conference_conference_registrations_path(@conference.short_title),
notice: 'Congratulations, you have successfully purchased a ticket! ' \
"You can pay for it in cash when you arrive! Thank you for supporting #{@conference.title}!"
if current_user.ticket_purchases.any?
redirect_to conference_conference_registrations_path(@conference.short_title),
notice: "Thank you for supporting #{@conference.title} by purchasing a ticket."
else
redirect_to conference_conference_registrations_path(@conference.short_title)
end
else
redirect_to conference_conference_registrations_path(@conference.short_title),
alert: "Oops, something went wrong with your purchase! #{message}"
@ -16,7 +19,7 @@ class TicketPurchasesController < ApplicationController
end
def destroy
@ticket_purchases = current_user.ticket_purchases.find_by(ticket_id: params[:id])
@ticket_purchases = current_user.ticket_purchases.find(params[:id])
if @ticket_purchases.destroy
redirect_to conference_conference_registrations_path(@conference.short_title),
notice: 'Ticket successfully deleted.'

View file

@ -1,8 +1,15 @@
class TicketsController < ApplicationController
before_filter :authenticate_user!
load_resource :conference, find_by: :short_title
load_resource :tickets, class: Ticket
load_resource :ticket, through: :conference
authorize_resource :conference_registrations, class: Registration
before_filter :check_load_resource, only: :index
def index; end
def check_load_resource
if @tickets.empty?
redirect_to root_path, notice: "There are no tickets available for #{@conference.title}!"
end
end
end

View file

@ -199,6 +199,17 @@ module ApplicationHelper
return ts
end
# rubocop:disable Lint/EndAlignment
def word_pluralize(count, singular, plural = nil)
word = if (count == 1 || count =~ /^1(\.0+)?$/)
singular
else
plural || singular.pluralize
end
"#{word}"
end
def markdown(text)
options = {
autolink: true,

View file

@ -61,7 +61,6 @@ class Ability
signed_in(user) # Inherit abilities from signed user
# User with role
can :manage, User if user.is_admin # ??? || (user.has_role? :organizer, :any)
can [:new, :create], Conference if user.is_admin || (user.has_role? :organizer, :any)
can [:index, :show, :gallery_photos], Conference
can :manage, Conference, id: conf_ids_for_organizer
@ -100,45 +99,47 @@ class Ability
can :manage, CallForPaper, conference_id: conf_ids_for_organizer + conf_ids_for_cfp
can :manage, Venue, conference_id: conf_ids_for_organizer
can :index, Venue, conference_id: conf_ids_for_organizer + conf_ids_for_cfp
can :manage, :all if user.is_admin
end
# Abilities for everyone, even guests (not logged in users)
def guest
## Abilities for everyone, even guests (not logged in users)
can [:index, :show], Conference do |conference|
# can view conferences
can [:index], Conference
can [:show], Conference do |conference|
conference.splashpage && conference.splashpage.public == true
end
can [:schedule], Conference do |conference|
conference.call_for_paper && conference.call_for_paper.schedule_public
end
# see commercials too
# can view confirmed Events
can :show, Event do |event|
event.state == 'confirmed'
end
can :index, :schedule # show?
# can view Commercials of confirmed Events
can :show, Commercial, commercialable_type: 'Event', commercialable_id: Event.where(state: 'confirmed').pluck(:id)
# can view others
can :show, User
end
def signed_in(user)
guest # Inherits abilities of guest
# Can subscribe, unsubscribe to a conference
# subscribe, unsubscribe to a Conference
can [:create, :destroy], Subscription, user_id: user.id
# Conference Registration
# can manage their own Registration
can :manage, Registration, user_id: user.id
# can manage their own User
can :manage, User, id: user.id
can :show, User
## Proposals
# Users can manage their own proposals
# can manage their own Event
can :manage, Event, id: user.events.pluck(:id)
# Submit proposals only for conferences that are not over yet
# can submit Events for conferences that are not over yet
can :create, Event, conference_id: Conference.where('end_date >= ?', Date.today).pluck(:id)
# Users can manage their own commercials
# can manage their own commercials
can :manage, Commercial, commercialable_type: 'Event', commercialable_id: user.events.pluck(:id)
# View commercials of confirmed events
can :show, Commercial, commercialable_type: 'Event', commercialable_id: Event.where(state: 'confirmed').pluck(:id)
end
end

View file

@ -101,6 +101,7 @@ class Conference < ActiveRecord::Base
validates_format_of :short_title, with: /\A[a-zA-Z0-9_-]*\z/
before_create :generate_guid
before_create :create_event_types
before_create :create_difficulty_levels
before_create :create_email_settings
before_create :add_color
@ -897,6 +898,22 @@ class Conference < ActiveRecord::Base
true
end
##
# Creates default DifficultyLevels for this Conference. Used as before_create.
#
def create_difficulty_levels
difficulty_levels << DifficultyLevel.create(title: 'Easy',
description: 'Events are understandable for everyone without knowledge of the topic.',
color: '#70EF69')
difficulty_levels << DifficultyLevel.create(title: 'Medium',
description: 'Events require a basic understanding of the topic.',
color: '#EEEF69')
difficulty_levels << DifficultyLevel.create(title: 'Hard',
description: 'Events require expert knowledge of the topic.',
color: '#EF6E69')
true
end
##
# Creates a EmailSettings association proxy. Used as before_create.
#

View file

@ -27,7 +27,7 @@ class EmailSettings < ActiveRecord::Base
conference.short_title, host: CONFIG['url_for_emails']),
'cfp_start_date' => conference.call_for_papers.start_date,
'cfp_end_date' => conference.call_for_papers.end_date,
'schedule_link' => Rails.application.routes.url_helpers.conference_schedule_url(
'schedule_link' => Rails.application.routes.url_helpers.schedule_conference_url(
conference.short_title, host: CONFIG['url_for_emails'])
}

View file

@ -13,11 +13,18 @@ class TicketPurchase < ActiveRecord::Base
scope: :ticket_id,
message: 'already bought this ticket!'
delegate :title, to: :ticket
delegate :description, to: :ticket
delegate :price, to: :ticket
delegate :price_cents, to: :ticket
delegate :price_currency, to: :ticket
def self.purchase(conference, user, purchases)
errors = []
ActiveRecord::Base.transaction do
conference.tickets.each do |ticket|
quantity = purchases[ticket.id.to_s].to_i
# if the user bought the ticket, just update the quantity
if ticket.bought?(user)
purchase = update_quantity(conference, quantity, ticket, user)
else

View file

@ -4,6 +4,7 @@ class Track < ActiveRecord::Base
has_many :events
before_create :generate_guid
validates :name, presence: true
validates :name, presence: true

View file

@ -59,13 +59,17 @@ class User < ActiveRecord::Base
self.subscriptions.find_by(conference_id: conference.id).present?
end
# Returns the ticket purchased ticket
# Returns the purchased ticket
# ====Returns
# * +TicketUser::ActiveRecord_Relation+ -> user
def ticket(id)
ticket_purchases.where(ticket_id: id).first
end
def supports? conference
ticket_purchases.find_by(conference_id: conference.id).present?
end
def self.for_ichain_username(username, attributes)
user = find_by(username: username)

View file

@ -17,7 +17,7 @@ class Venue < ActiveRecord::Base
after_update :send_mail_notification
def address
"#{conference.venue.street}, #{conference.venue.postalcode} #{conference.venue.city}, #{conference.venue.country}"
"#{street}, #{city}, #{country_name}"
end
def country_name

View file

@ -4,7 +4,7 @@
%h1 Commercials
%p.text-muted
Conference commercials will be displayed on the events in the
= link_to "schedule,", conference_schedule_path(@conference.short_title)
= link_to "schedule,", schedule_conference_path(@conference.short_title)
if the event speaker didn't add an event commercial.
- @commercials.each_slice(3) do |slice|
.row

View file

@ -30,4 +30,4 @@
method: :delete, class: 'btn btn-danger', data: { confirm: "Do you really want to delete #{difficulty_level.title}?" }
.row
.col-md-12.text-right
= link_to 'Add Difficulty Level', new_admin_conference_difficulty_level_path(@conference.short_title), class: 'btn btn-primary'
= link_to 'Add Difficulty Level', new_admin_conference_difficulty_level_path(@conference.short_title), class: 'btn btn-primary'

View file

@ -10,6 +10,7 @@
%table.table.table-hover#tickets
%thead
%th Title
%th Description
%th Price
%th Sold
%th Actions
@ -19,6 +20,8 @@
%td
= link_to(admin_conference_ticket_path(@conference.short_title, ticket.id)) do
= ticket.title
%td
= truncate(ticket.description)
%td
= humanized_money_with_symbol ticket.price
%td

View file

@ -10,25 +10,26 @@
%small
%b
= date_string(conference.start_date, conference.end_date)
- if conference.venue
%p
= "#{conference.venue.city}/#{conference.venue.country_name}"
.col-md-2
.btn-group-vertical
- if current_user.nil? || !current_user.subscribed?(conference)
= link_to 'Subscribe', conference_subscriptions_path(conference.short_title), method: :post, class: 'btn btn-info btn-group-vertical'
- else
= link_to 'Unsubscribe', conference_subscriptions_path(conference.short_title), method: :delete, class: 'btn btn-danger btn-group-vertical'
- if !@conference || @conference != conference
- if conference.splashpage && conference.splashpage.public
= link_to "View Conference", conference_path(conference.short_title), :class =>"btn btn-default"
- if conference.call_for_paper and conference.call_for_paper.schedule_public
= link_to "Schedule", schedule_conference_path(conference.short_title), :class =>"btn btn-default"
- if conference.registration_open?
- if conference.user_registered?(current_user)
= link_to "Modify Registration", edit_conference_conference_registrations_path(conference.short_title), :class =>"btn btn-default"
= link_to "My Registration", conference_conference_registrations_path(conference.short_title), :class =>"btn btn-default"
- else
= link_to "Register", new_conference_conference_registrations_path(conference.short_title), :class =>"btn btn-success"
= link_to "Schedule", conference_schedule_path(conference.short_title), :class =>"btn btn-default" if conference.call_for_paper and conference.call_for_paper.schedule_public
= link_to "Register", new_conference_conference_registrations_path(conference.short_title), :class =>"btn btn-default"
- if !current_user.nil? && current_user.proposal_count(conference) > 0
= link_to "View My Proposals", conference_proposal_index_path(conference.short_title), :class =>"btn btn-default"
= link_to "My Proposals", conference_proposal_index_path(conference.short_title), :class =>"btn btn-default"
- elsif conference.cfp_open?
= link_to "Submit Proposal", conference_proposal_index_path(conference.short_title), :class =>"btn btn-default"
- if !current_user.nil? && conference.tickets.any?
= link_to 'Support', conference_tickets_path(conference.short_title), class: 'btn btn-default'
- if current_user.nil? || !current_user.subscribed?(conference)
= link_to 'Subscribe', conference_subscriptions_path(conference.short_title), method: :post, class: 'btn btn-default'
- else
= link_to 'Unsubscribe', conference_subscriptions_path(conference.short_title), method: :delete, class: 'btn btn-default'

View file

@ -1,9 +1,13 @@
.container
.row
.col-md-12.text-center
%h2 Accommodation Deals
%h2
Where to stay
- if @conference.venue
in
= @conference.venue.city
%p.lead
We have prepared affordable accommodation deals for your visit.
We recommend these affordable lodging accommodations for your visit.
- @conference.lodgings.each_slice(3) do |slice|
.row.row-centered
- slice.each do |lodging|

View file

@ -21,7 +21,7 @@
.row
.col-md-12
%p.cta-button.text-center
= link_to(conference_schedule_path(@conference.short_title), class: 'btn btn-default btn-lg') do
= link_to(schedule_conference_path(@conference.short_title), class: 'btn btn-default btn-lg') do
Full Schedule
- #FIXME: Display keynotes https://github.com/openSUSE/osem/issues/544

View file

@ -3,7 +3,7 @@
%a.smoothscroll{ href: '#venue' } Venue
-if !@conference.venue.latitude.blank? and !@conference.venue.longitude.blank?
#map{style: "height: 500px;" }
- popup = "<h3>#{@conference.venue.name}</h3><br>#{@conference.venue.street}<br>#{@conference.venue.postalcode}, #{@conference.venue.city}<br>#{@conference.venue.country_name}"
- popup = "<h3>#{@conference.venue.name}</h3><br>#{@conference.venue.street}<br>#{@conference.venue.city}<br>#{@conference.venue.country_name}"
- content_for(:script_body) do
:javascript
// create a map in the "map" div, set the view to a given place and zoom
@ -17,6 +17,25 @@
L.marker([#{@conference.venue.longitude}, #{@conference.venue.latitude}]).addTo(map)
.bindPopup("#{popup}")
.openPopup();
// Turn scrollwheel on when user clicks
map.on('focus', function(e) {
map.scrollWheelZoom.enable();
});
// Turn scrollwheel off once the map isn't in the viewport anymore
$('#map').waypoint({
handler: function(direction) {
map.scrollWheelZoom.disable();
//alert('map');
},
offset: '90%'
});
$('#map').waypoint({
handler: function(direction) {
map.scrollWheelZoom.disable();
//alert('map');
},
offset: '10%'
});
-else
.container
.row

View file

@ -18,12 +18,13 @@
- @antiquated.each do |conference|
= render :partial => "conference_details", :locals => {:conference => conference}
:javascript
$('#antiquated').on('hidden.bs.collapse', function () {
$( ".fa-chevron-down" ).hide();
$( ".fa-chevron-right" ).show();
})
$('#antiquated').on('shown.bs.collapse', function () {
$( ".fa-chevron-down" ).show();
$( ".fa-chevron-right" ).hide();
})
-content_for :script_body do
:javascript
$('#antiquated').on('hidden.bs.collapse', function () {
$( ".fa-chevron-down" ).hide();
$( ".fa-chevron-right" ).show();
})
$('#antiquated').on('shown.bs.collapse', function () {
$( ".fa-chevron-down" ).show();
$( ".fa-chevron-right" ).hide();
})

View file

@ -1,4 +0,0 @@
$('#gallery .carousel-inner').append("#{escape_javascript(render('gallery_photo', photos: @photos)) }");
$('#gallery .modal-body').children('.fa').remove();
$('#gallery #carousel-example-generic').css('visibility','visible');

View file

@ -45,7 +45,7 @@
%section#lodging
= render 'lodging'
- if @conference.tickets.any? and @conference.splashpage.include_tickets
- if @conference.tickets.any? and @conference.splashpage.include_tickets and @conference.pending?
%section#tickets
= render 'tickets'

View file

@ -1,23 +1,23 @@
.container
.row
.col-md-12
.page-header
%h1
Registration for
= @conference.title
.row
.col-md-8
= semantic_form_for(@registration, url: conference_conference_registrations_path(@conference.short_title)) do |f|
.tabbable
%ul.nav.nav-tabs
%li.active
= link_to 'Register', '#register-content', 'data-toggle' => 'tab'
- if @conference.use_volunteers
%li
= link_to 'Volunteer', '#volunteer-content', 'data-toggle' => 'tab'
.tab-content
#register-content.tab-pane.active
= render 'conference_registrations/registration', f: f
- if @conference.use_volunteers
#volunteer-content.tab-pane
= render 'conference_registrations/volunteer', f: f
- if @conference.user_registered?(current_user)
= f.action :submit, button_html: { value: 'Update Registration', class: 'btn btn-primary' }
= link_to 'Unregister', conference_conference_registrations_path(@conference.short_title),
method: :delete, class: 'btn btn-danger', confirm: 'Are you sure you want to unregister?'
- else
= f.action :submit, button_html: { value: 'Register', class: 'btn btn-primary', id: 'register' }
- if @conference.questions.any?
= render partial: 'questions', locals: { f: f }
- if @conference.events.workshops.any?
=f.inputs 'Register to Workshops' do
= f.input :events, as: :check_boxes, label: false, collection: @conference.events.workshops
= f.inputs 'Your Travel Info' do
= f.input :arrival, as: :string, label: 'Your arrival time', input_html: { value: (f.object.arrival.to_formatted_s(:db_without_seconds) unless f.object.arrival.nil?), id: 'registration-arrival-datepicker', readonly: 'readonly' }
= f.input :departure, as: :string, label: 'Your departure time', input_html: { value: (f.object.departure.to_formatted_s(:db_without_seconds) unless f.object.departure.nil?), id: 'registration-departure-datepicker', readonly: 'readonly' }
%p.pull-right
- if @conference.user_registered?(current_user)
= f.action :submit, button_html: { value: 'Update Registration', class: 'btn btn-primary' }
- else
= f.action :submit, button_html: { value: 'Register', class: 'btn btn-primary', id: 'register' }

View file

@ -1,10 +1,8 @@
- @conference.questions.each do |q|
%h5
= "Q: #{q.title}"
- if q.question_type.id == 1 || q.question_type.id == 2 # yes/no or single choice
= f.input :qanswers, :collection => q.qanswers, :as => :select, :input_html => { :multiple => false }, :label => false, :include_blank => "Please make your choice",
:member_label => Proc.new {|a| a.answer.title}
- if q.question_type.id == 3 # multiple choice
= f.input :qanswers, :collection => q.qanswers, :as => :check_boxes, :label => false,
:member_label => Proc.new {|a| a.answer.title}
= f.inputs 'Additional Info' do
- if q.question_type.id == 1 || q.question_type.id == 2 # yes/no or single choice
= f.input :qanswers, :collection => q.qanswers, :as => :select, :input_html => { :multiple => false, class: 'col-sm-10' }, label: q.title, :include_blank => "Please make your choice",
:member_label => Proc.new {|a| a.answer.title}
- if q.question_type.id == 3 # multiple choice
= f.input :qanswers, :collection => q.qanswers, :as => :check_boxes, :input_html => { class: 'col-sm-2' }, :label => false,
:member_label => Proc.new {|a| a.answer.title}

View file

@ -1,24 +0,0 @@
.row
.col-md-12
%h3
Registration for
= @conference.title
- if current_user.proposal_count(@conference) > 0
.row
.col-md-12
%i
Please note: Registration is not automatically performed for speakers. If you're scheduled to speak at the conference, you still need to register!
.row
.col-md-12
= f.inputs name: 'Your Details' do
- if @conference.questions
= render partial: 'questions', locals: { f: f }
%br
- if @conference.events.workshops.any?
=f.inputs 'Register to Workshops' do
= f.input :events, as: :check_boxes, label: false, collection: @conference.events.workshops
= f.inputs 'Travel Info' do
= f.input :arrival, as: :string, input_html: { value: (f.object.arrival.to_formatted_s(:db_without_seconds) unless f.object.arrival.nil?), id: 'registration-arrival-datepicker', readonly: 'readonly' }
= f.input :departure, as: :string, input_html: { value: (f.object.departure.to_formatted_s(:db_without_seconds) unless f.object.departure.nil?), id: 'registration-departure-datepicker', readonly: 'readonly' }

View file

@ -1,46 +0,0 @@
%table.table.table-hover
%thead
%tr
%th Ticket
%th.text-center Quantity
%th.text-center Price
%th.text-center Total
%th
%tbody
- tickets.each do |ticket|
%tr
%td.col-sm-8.col-md-6
.media
.media-body
%h4.media-heading
= ticket.title
%h5.media-heading
-if !ticket.description.blank?
= markdown(ticket.description)
%td.col-sm-1.col-md-1.text-center
= ticket.quantity_bought_by(current_user)
%td.col-sm-1.col-md-1.text-center
= humanized_money_with_symbol ticket.price
%td.col-sm-1.col-md-1.text-center
%strong
= ticket.total_price(current_user)
%td.col-sm-1.col-md-1.text-center
= link_to conference_ticket_purchase_path(@conference.short_title, ticket.id),
method: :delete, class: 'btn btn-danger',
data: { confirm: "Do you really want to delete the #{ticket.title} for #{@conference.title}?" } do
Delete
%i.fa.fa-trash-o
%tr
%td
%td
%td
%td.col-sm-1.col-md-1.text-center
%h4
Total
%td.col-sm-1.col-md-1.text-center
%h4
%strong
- if @total_price.cents == -1
not-calculable
- else
= humanized_money_with_symbol Ticket.total_price(@conference, current_user)

View file

@ -3,117 +3,136 @@
.col-md-12
.page-header
%h1
Registration for
= @conference.title
%p.text-muted
-if @conference.venue
at
%strong
= "#{@conference.venue.name},"
= "#{@conference.venue.street},"
= "#{@conference.venue.city} / #{@conference.venue.country_name}."
%small
= date_string(@conference.start_date, @conference.end_date)
.row
.col-md-6
- if @conference.venue
%p
%small
at
= link_to @conference.venue.name
,
= link_to @conference.venue.address, "http://maps.google.com/maps?q=#{@conference.venue.address}"
.col-md-4
- if @conference.contact.facebook.present?
= link_to "#{ @conference.contact.facebook }" do
%i.fa.fa-facebook-square
- if @conference.contact.twitter.present?
= link_to "#{ @conference.contact.twitter }" do
%i.fa.fa-twitter
- if @conference.contact.instagram.present?
= link_to "#{ @conference.contact.instagram }" do
%i.fa.fa-instagram
- if @conference.contact.googleplus.present?
= link_to "#{ @conference.contact.googleplus }" do
%i.fa.fa-google-plus-square
.row
.col-md-12
- if @conference.tickets.any?
%h3
Tickets
- if current_user.tickets.any?
%p
You have purchased the following tickets:
= render partial: 'tickets_bought', locals: { tickets: current_user.tickets }
- else
= render partial: 'tickets', locals: { tickets: @conference.tickets }
%p
If you would like to buy more tickets, please click
= link_to 'here', conference_tickets_path(@conference.short_title)
.row
.col-md-12
- if @conference.speakers.confirmed.any?
%h3
= pluralize(@conference.speakers.confirmed.count, 'Speaker')
- @conference.speakers.confirmed.limit(12).each_slice(4) do |slice|
.row
- slice.each do |speaker|
.col-md-3
.row
.col-md-3
= image_tag(speaker.gravatar_url(size: '25'),
title: "Yo #{speaker.name}!",
alt: '', 'class' => 'img-circle img-responsive text-center')
.col-md-9
%h4
= speaker.name
.row
.col-md-12
- if @conference.events.confirmed.any?
%h3
= pluralize(@conference.events.confirmed.count, 'Event')
%ul.list-unstyled
- @conference.events.confirmed.limit(10).each do |event|
%li
%h4
= link_to event.title, conference_proposal_path(@conference.short_title, event.id)
%strong
presented by
= event.speaker_names
.row
.col-md-12
- if @conference.participants.any?
%h3
= pluralize(@conference.participants.count, 'Participant')
- @conference.participants.limit(36).each_slice(12) do |slice|
.row
- slice.each do |participant|
.col-md-1
= image_tag(participant.gravatar_url(size: '25'),
title: "Yo #{participant.name}!",
alt: '', 'class' => 'img-circle img-responsive text-center')
.row
.col-md-12
- if @registration
%h2
Congratulations! You are now registered for
= "#{@conference.title}!"
- if @conference.questions.any?
%h3 Your answers to the registrations questions are:
%h4
%span.fa-stack
%i.fa.fa-square-o.fa-stack-2x
%i.fa.fa-plane.fa-stack-1x
Travel Schedule
%ul
%li
- if @registration.arrival.present?
arrive at
%strong
= @registration.arrival.strftime('%A, %B %-d. %Y %H:%m')
- else
You haven't scheduled your arrival
%li
- if @registration.departure.present?
depart at
%strong
= @registration.departure.strftime('%A, %B %-d. %Y %H:%m')
- else
You haven't scheduled your departure
- if @conference.questions.any?
.row
.col-md-12
%h4
%span.fa-stack
%i.fa.fa-square-o.fa-stack-2x
%i.fa.fa-info.fa-stack-1x
Additional Info
%ul
- @conference.questions.each do |q|
%p
%b Question:
= q.title
%b Your Answer:
- @registration.qanswers.where(:question_id => q.id).each do |qa|
= qa.answer.title
- if @workshops.any?
%h3 You are registered for the following workshops:
%ul.list-unstyled
- @workshops.each do |workshop|
%li
%h4
= link_to workshop.title, conference_proposal_path(@conference.short_title, workshop.id)
%strong
presented by
= workshop.speaker_names
%div
= link_to 'Modify your Registration', edit_conference_conference_registrations_path(@conference.short_title), class: 'btn btn-success'
%li
%strong
= q.title
- if @registration.qanswers.any?
- @registration.qanswers.where(:question_id => q.id).each do |qa|
= qa.answer.title
- else
You haven't answered
- if @workshops.any?
.row
.col-md-12
%h4
%span.fa-stack
%i.fa.fa-square-o.fa-stack-2x
%i.fa.fa-check.fa-stack-1x
Event Registrations
%ul
- @workshops.each do |workshop|
%li
= link_to workshop.title, conference_proposal_path(@conference.short_title, workshop.id)
- if @conference.tickets.any?
.row
.col-md-12
%h4
%span.fa-stack
%i.fa.fa-square-o.fa-stack-2x
%i.fa.fa-ticket.fa-stack-1x
Tickets
-if @tickets.any?
= "(#{@total_price} #{@tickets.first.price.symbol})"
%ul
- @tickets.each do |ticket|
%li
= ticket.quantity
= ticket.title
= word_pluralize(ticket.quantity, 'Ticket')
for
= humanized_money ticket.price
= ticket.price.symbol
= link_to conference_ticket_purchase_path(@conference.short_title, ticket.id), method: :delete,
id: "ticket-#{ticket.id}-delete",
class: 'btn btn-danger btn-xs',
data: { confirm: "Do you really want to delete the #{ticket.title} ticket for #{@conference.title}?" } do
%i.fa.fa-trash-o
%li
- if @tickets.any?
= link_to 'Buy more tickets', conference_tickets_path(@conference.short_title)
- else
You haven't bought any tickets.
= link_to 'Please buy some tickets to support us!', conference_tickets_path(@conference.short_title)
.row
.col-md-12
-if @registration
.btn-group-vertical.pull-right
= link_to 'Edit your Registration', edit_conference_conference_registrations_path(@conference.short_title), class: 'btn btn-success'
= link_to 'Unregister', conference_conference_registrations_path(@conference.short_title),
method: :delete, class: 'btn btn-danger', confirm: 'Are you sure you want to unregister?'
method: :delete, class: 'btn btn-danger btn-xs', confirm: 'Are you sure you want to unregister?'
- else
%p.lead
= "Unfortunately you are not registered for #{@conference.title}. If you want to register click"
= link_to 'here.', new_conference_conference_registrations_path(@conference.short_title)
= link_to 'Register', new_conference_conference_registrations_path(@conference.short_title), class: 'btn btn-success btn-lg pull-right'
.row
.col-md-12
.page-header
%h1
%small
Meet the people of
= @conference.short_title
.col-md-4
- if @conference.participants.any?
%h4
%span.fa-stack
%i.fa.fa-square-o.fa-stack-2x
%i.fa.fa-group.fa-stack-1x
= @conference.participants.count
Registered
= word_pluralize(@conference.participants.count, 'Attendees')
- @conference.participants.each do |participant|
= image_tag(participant.gravatar_url(size: '25'), title: "#{participant.name}!", class: 'img-circle')
.col-md-4.col-md-offset-2
- if @conference.speakers.any?
%h4
%span.fa-stack
%i.fa.fa-square-o.fa-stack-2x
%i.fa.fa-microphone.fa-stack-1x
= @conference.speakers.count
Confirmed
= word_pluralize(@conference.speakers.count, 'Speaker')
- @conference.speakers.each do |speaker|
= image_tag(speaker.gravatar_url(size: '25'), title: "#{speaker.name}!", class: 'img-circle')

View file

@ -7,7 +7,7 @@
= link_to(edit_user_path(current_user.id)) do
%span.fa.fa-user
Edit Profile
-if @conference and @conference.id
-if @conference and @conference.call_for_paper
%li
= link_to(conference_proposal_index_path(@conference.short_title)) do
%span.fa.fa-comment

View file

@ -7,7 +7,7 @@
%br
%small
= @event.subtitle
= link_to "Schedule", conference_schedule_path(@conference.short_title), :class =>"btn btn-success pull-right"
= link_to "Schedule", schedule_conference_path(@conference.short_title), :class =>"btn btn-success pull-right"
- if can? :edit, @event
= link_to "Edit", edit_admin_conference_event_path(@conference.short_title, @event), :class => "btn btn-mini btn-primary pull-right"
.row

View file

@ -10,10 +10,10 @@
%td.col-sm-1.col-md-1
- if ticket.bought?(current_user)
= text_field_tag("tickets[][#{ticket.id}]", ticket.quantity_bought_by(current_user),
type: 'number', min: 0, class: 'form-control quantity', 'data-id' => ticket.id)
type: 'number', min: 0, class: "form-control quantity", 'data-id' => ticket.id)
- else
= text_field_tag("tickets[][#{ticket.id}]", 0, type: 'number', min: 0,
class: 'form-control quantity', 'data-id' => ticket.id)
class: "form-control quantity", 'data-id' => ticket.id)
%td.col-sm-1.col-md-1.text-center
= ticket.price.symbol
%span{id: "price_#{ticket.id}"}

View file

@ -5,9 +5,10 @@
%h1
Tickets
%p.lead
Please buy a ticket to support
%b
Support
%strong
= @conference.title
by buying a ticket
=form_tag(conference_ticket_purchases_path, method: :post) do |f|
%table.table.table-hover
%thead
@ -17,8 +18,8 @@
%th Price
%th Total
%tbody
- @conference.tickets.each do |ticket|
= render partial: 'ticket', f: f, locals: {ticket: ticket}
- @conference.tickets.each do |ticket|
= render partial: 'ticket', f: f, locals: {ticket: ticket}
%tr
%td
%td
@ -28,11 +29,18 @@
%td.col-sm-1.col-md-1.text-center
%h4
%strong
= @conference.tickets.first.price.symbol
%span{id: 'total_price'}
0
.pull-right
= button_tag(type: 'submit', class: 'btn btn-success btn-lg') do
Support
%i.fa.fa-shopping-cart
= link_to 'Continue without a Ticket!', conference_conference_registrations_path(@conference.short_title),
class: 'btn btn-danger btn-sm'
.btn-group-vertical
= button_tag(type: 'submit', class: 'btn btn-success btn-lg') do
Support
%i.fa.fa-shopping-cart
= link_to 'Continue without a Ticket!', conference_conference_registrations_path(@conference.short_title),
class: 'btn btn-danger btn-sm'
.row
.col-md-13
%p.text-muted.text-center
%small
Checkout will be at the conference registration

View file

@ -58,10 +58,10 @@ Osem::Application.routes.draw do
resources :emails, only: [:show, :update, :index]
resources :sponsorship_levels, except: [:show] do
member do
patch :up
patch :down
end
member do
patch :up
patch :down
end
end
resources :questions do
@ -101,12 +101,8 @@ Osem::Application.routes.draw do
resources :ticket_purchases, only: [:create, :destroy]
resource :subscriptions, only: [:create, :destroy]
resource :schedule, only: [] do
get '/' => 'schedule#index'
end
member do
get 'gallery_photos'
get :schedule
end
end

View file

@ -3,6 +3,5 @@ FactoryGirl.define do
title 'Business Ticket'
price_cents 1000
price_currency 'USD'
conference
end
end

View file

@ -16,6 +16,7 @@ feature 'Has correct abilities' do
let(:user) { create(:user, role_ids: [role_organizer.id, role_cfp.id, role_info_desk.id, role_volunteer_coordinator.id]) }
scenario 'when user is organizer' do
user.is_admin = false
sign_in user
visit admin_conference_path(conference1.short_title)
@ -86,6 +87,7 @@ feature 'Has correct abilities' do
end
scenario 'when user is cfp' do
user.is_admin = false
sign_in user
visit admin_conference_path(conference2.short_title)
@ -156,6 +158,7 @@ feature 'Has correct abilities' do
end
scenario 'when user is info desk' do
user.is_admin = false
sign_in user
visit admin_conference_path(conference3.short_title)
@ -227,6 +230,7 @@ feature 'Has correct abilities' do
end
scenario 'when user is volunteer coordinator' do
user.is_admin = false
sign_in user
visit admin_conference_path(conference4.short_title)

View file

@ -18,21 +18,22 @@ feature Registration do
scenario 'updates conference registration', feature: true, js: true do
visit root_path
click_link 'Modify Registration'
click_link 'My Registration'
expect(current_path).to eq(conference_conference_registrations_path(conference.short_title))
click_link 'Edit your Registration'
expect(current_path).to eq(edit_conference_conference_registrations_path(conference.short_title))
click_button 'Update Registration'
click_button 'Update Registration'
expect(conference.user_registered?(participant)).to be(true)
end
scenario 'unregisters for a conference', feature: true, js: true do
visit root_path
click_link 'Modify Registration'
click_link 'My Registration'
expect(current_path).to eq(conference_conference_registrations_path(conference.short_title))
expect(current_path).to eq(edit_conference_conference_registrations_path(conference.short_title))
click_link 'Unregister'
expect(conference.user_registered?(participant)).to be(false)
end
end

View file

@ -26,7 +26,7 @@ feature DifficultyLevel do
within('table#difficulty_levels') do
expect(page.has_content?('Hard')).to be true
expect(page.has_content?('Life is the hardest')).to be true
expect(page.assert_selector('tr', count: 2)).to be true
expect(page.assert_selector('tr', count: 5)).to be true
end
end
@ -38,12 +38,17 @@ feature DifficultyLevel do
conference_id: conference.short_title)
# Remove difficulty level
click_link 'Delete'
within('table tr:nth-of-type(4)') do
click_link 'Delete'
end
# Validations
expect(flash).to eq('Difficulty level successfully deleted.')
within('table#difficulty_levels') do
expect(page.assert_selector('tr', count: 1)).to be true
expect(page.assert_selector('tr', count: 4)).to be true
expect(page.has_content?('Easy Events')).to be true
expect(page.has_content?('Medium Events')).to be true
expect(page.has_content?('Hard Events')).to be true
end
end

View file

@ -2,7 +2,7 @@ require 'spec_helper'
feature Registration do
let!(:ticket) { create(:ticket) }
let!(:conference) { create(:conference, title: 'ExampleCon', tickets: [ticket]) }
let!(:conference) { create(:conference, title: 'ExampleCon', tickets: [ticket], registration_period: create(:registration_period, start_date: 3.days.ago)) }
let!(:participant) { create(:user) }
context 'as a participant' do
@ -18,7 +18,10 @@ feature Registration do
scenario 'purchases a ticket', feature: true, js: true do
visit root_path
click_link 'Support'
click_link 'Register'
expect(current_path).to eq(new_conference_conference_registrations_path(conference.short_title))
click_button 'Register'
fill_in "tickets__#{ticket.id}", with: '2'
expect(current_path).to eq(conference_tickets_path(conference.short_title))
@ -29,20 +32,18 @@ feature Registration do
expect(purchase.quantity).to eq(2)
expect(current_path).to eq(conference_conference_registrations_path(conference.short_title))
expect(flash).
to eq('Congratulations, you have successfully purchased a ticket! You can pay for it in cash when you arrive! Thank you for supporting ExampleCon!')
expect(page.has_content?('Business Ticket')).to be true
to eq("Thank you for supporting #{conference.title} by purchasing a ticket.")
expect(page.has_content?('2 Business Ticket Tickets for 10')).to be true
end
scenario 'deletes a purchased ticket', feature: true, js: true do
create(:ticket_purchase,
user_id: participant.id,
ticket_id: ticket.id,
quantity: 2)
create(:registration, conference: conference, user: participant)
create(:ticket_purchase, conference: conference, user: participant, ticket: ticket, quantity: 4)
visit conference_conference_registrations_path(conference.short_title)
expect(page.has_content?('Business Ticket')).to be true
expect(page.has_content?('4 Business Ticket Tickets for 10')).to be true
click_link 'Delete'
click_link "ticket-#{ticket.id}-delete"
expect(flash).to eq('Ticket successfully deleted.')
expect(TicketPurchase.count).to eq(0)
end

View file

@ -7,7 +7,7 @@ describe 'User' do
let!(:first_user) { create(:user) } # automatically becomes admin
let(:user){ nil }
let(:conference_not_public) { create(:conference, splashpage: create(:splashpage, public: false)) }
let(:conference_public) { create(:conference, splashpage: create(:splashpage, public: true)) }
let(:conference_public) { create(:conference, splashpage: create(:splashpage, public: true), call_for_paper: create(:call_for_paper, schedule_public: true)) }
let(:event_confirmed) { create(:event, state: 'confirmed') }
let(:someevent) { create(:event) }
@ -19,7 +19,7 @@ describe 'User' do
it{ should be_able_to(:show, event_confirmed)}
it{ should_not be_able_to(:show, someevent)}
it{ should be_able_to(:index, :schedule)}
it{ should be_able_to(:schedule, conference_public)}
it{ should_not be_able_to(:create, Event)}
it{ should_not be_able_to(:manage, Event)}