GSoC Second evaluation

Reading time ~5 minutes

Implementing the concept of event organizer in TSP

During the second phase of GSoC, one of my tasks was to implement the event organizer feature. Now with the help of this tsp members can easily assign event organizers for an event. An event consists of various organizers to manage certain attributes for that event such as checking if the attendees have signed up or not, checking the state of their requests, reminding them about important dates and timings. Before this feature, event organizers always had to bug someone for these details.

Github link for this issue -> event organizer feature.

This feature was requested by KDE e.V. Travel Cost Reimbursement initiative .

Creating the event organizer role

TSP has various roles defined such as tsp, supervisor, administrative, requester, etc which are all global roles. But event organizer is not a global role. An event organizer has the ability to see the participants of an event and manage event email.

So to define this role we created a many to many association between a user and an event.

create_table "event_organizers", force: true do |t|
  t.integer "event_id"
  t.integer "user_id"
end

And now a user can be defined as a event organizer for various events.

# Event organizer for events
has_many :event_organizers
# Events for which the user is an event organizer
has_many :events, through: :event_organizers

Defining the abilities of an event organizer

Starting by writing some tests for the event organizer`s abilities. In the tests below there are two users wedge and luke in which luke is an event organizer. So signing in with wedge we are not allowed to access the event email feature on the other hand we can access the event email for the party event for which luke is defined as an event organizer but he cannot do the same for the hoth hackathon event.

scenario 'When logged in as a requester' do
  sign_in_as_user(users(:wedge))

  visit event_event_emails_path(events(:party))
  page.should have_content 'You are not allowed to access this page'

  visit event_event_emails_path(events(:hoth_hackaton))
  page.should have_content 'You are not allowed to access this page'
end
 
scenario 'When logged in as an event organizer' do
  sign_in_as_user(users(:luke))

  visit event_event_emails_path(events(:party))
  page.should have_content "Event email Emails for Death Star's destruction celebration"

  visit event_event_emails_path(events(:hoth_hackaton))
  page.should have_content 'You are not allowed to access this page'
end

Now defining the abilities for an event organizer, TSP uses cancancan for authorization. Cancancan uses an Ability class to define user permissions. TSP uses ActiveHash for user roles. So you can easily define the abilities for a particular role very easily, like done in the Ability file of TSP. But we have not created the event organizer as a global role, so how to defined the abilities for this role. Here comes the interesting part, cancancan supports Nested Resources so with the help of this we can define the abilities in the following way:

user.events.each do |e|
  can :manage, EventEmail, event: { id: e.id }
end

What does the above code do? It provides the ability to manage the Event Email to a user who is an event organizer of that event.

Adding/Removing an event organizer for an event

Tsp members can easily add event organizers by searching the users with the help of their email address.

Index
Searching with the help of auto complete and adding an event organizer

For the auto complete function we are using `rails4-autocomplete’ gem. This gem uses the jQuery-ui Autocomplete and is very easy to use. You just have to define some custom css to make the dropdown look better ;), if you want you can have a look here for the css we are using in TSP.

Event organizers for an event are listed on /events/:event_id/organizers

Index
Index view of event organizers

A remove button is provided in every row so that the tsp members can also easily remove the event organizer for an event.

Event Organizers controller:

class EventOrganizersController < InheritedResources::Base
  autocomplete :user, :email
  belongs_to :event
  actions :all, except: [:show, :edit, :update]

  def create
    @event_organizer.user = User.find_by(email: params[:event_organizer][:user_email])
    create!(notice: 'Event Organizer Added')
  end

  def permitted_params
    params.permit(event_organizer: [:user_email])
  end
end

The autocomplete method is from the rails4_autocomplete gem to generate user email’s. And as we are using Inherited resources we have defined the nesting with the help of belongs_to. In the create action we are searching a user with the help of his email and then the user is added as an event organizer.

The new action view to add the event organizers:

%h3 Add an event organizer for #{@event.name}
.col-md-3
  = simple_form_for(resource, url: event_event_organizers_path) do |f|
    = f.error :user_id
    = f.input :user_email, url: autocomplete_user_email_event_event_organizers_path, as: :autocomplete, label: false, :input_html => {class: 'form-control', placeholder: 'Search by email'}
    .form-group  
      = f.submit "Add", class: 'btn btn-primary btn-sm'

For the autocomplete, as we are using simple form we can easily do it with the help of as: :autocomplete, also there is an error field for custom error messages.

Other tasks during the second evaluation

Implementing the markdown preview feature in the event emails

We are using the Redcarpet gem for markdown in TSP which was implemented during the event email feature you can read about it in my previous post, if you have already read it then its awesome :)

How the feature looks:

Markdown
Filling in the body field

We have used bootstrap tabs here for the Body and Preview tabs.

Preview
Markdown Preview

As Redcarpet is a ruby library we can’t use it on the client side. The first thing which comes to mind is Ajax isn’t it? Then lets just use ajax here. This is how the ajax request looks

$('a[href=#preview]').click ->
  $("#preview_screen").html("<h3>Loading...</h3>")
  $.ajax
   url: '/events/' + $(this).data('event-id') + '/event_emails/preview'
   type: 'POST'
   data: content: $('#event_email_body').val()

The above requests is written in coffee script, as you can see we are using a post type request and passing the body field value as the data. Then we collect the data in an instance variable in the preview action defined in the event_emails controller and then we can just use the markdown method which we created with the help of redcarpet to process the data. Remember to create a route for the following request in the routes.rb file.

// In preview.js.erb
$('#preview_screen').html('<%= j markdown(@content) %>');

The above code will fill our preview section with the converted html. That’s it, simple wasn’t it :)

Testing the above code, we can do it by just checking if the preview section contains the converted html or not.

scenario 'Viewing the markdown preview', js: true do
  sign_in_as_user(users(:tspmember))
  visit new_event_event_email_path(events(:party))

  fill_in 'event_email_body', with: "# Death Star's destruction celebration"
  click_link 'Preview'
  within(:xpath, '//*[@id="preview_screen"]') do
    page.should have_css('h1', text: "Death Star's destruction celebration")
  end
end

For the implementation of these features you can have a look at the following PR’s:

And of course I would have not been able to implement these features without the help of my amazing mentors Björn Geuken and Christian Buckmayer :)

To know more about Travel Support Program visit TSP

GSoC product submission

Details about new features added in TSP during GSoC Continue reading

Second task of GSoC

Published on June 20, 2017

Migrate to the latest version of Bootstrap

Published on June 05, 2017