By: Stichting Magneetbar
Email: support@magneetfestival.nl

This documentation explains the process we went through realizing a crowdsourcing platform for a festival.


Table of Contents


Summary of goalstop

The successful Magneetbar, proved the idea that the time is right for participation of the public in festivals. The idea was to build a platform that can be used year around to collaborate on events with the public. We thought out the main functionality as follows:


Child themingtop

We followed the child theming guidelines as much as possible. We have created a BuddyPress child theme named Magneet, and only overrided the template files necessary. Be prepared though, BuddyPress adds quite some template files and uses a different template hierarchy than WordPress. Also the separation of form and function isn’t as clean. To create a custom design, there is also a lot of css rules to override in your custom files.


Organizing with poststop

Our first approach for creating two different group-types (ideas and projectgroups) was to use the built in group meta filter. The problem with this scenario was realizing our program page and role management. The BuddyPress loops aren’t as flexible as we expected and the lack of extensive documentation made us consider other options. We decided to look into the scenario of using WordPress posts for ideas and projects, and attach certain BuddyPress group functionality to these ‘idea and project posts’. This way we could use the well documented wp_query for the program page, and use the categories combined with a custom taxonomy for the ‘type’ (idea or project).

The image below shows the custom taxonomy in the edit post page. This area can only be seen and edited by WordPress admins.


Program category magictop

The program page is divided into two sections, left projects right the ideas. On the image below ‘Location’ is one of the subcategories of this specific event. Clicking on Projects shows you all projects. Clicking on ideas shows you all ideas. Clicking on Location, shows you al projects within this event within the crowdsourcing subject Location.

We used a custom taxonomy named ‘event-type’ to define whether a post is an idea or a project.

query_posts('cat='.$cat->cat_ID.'&posts_per_page=3&taxonomy=event-type&term=projects');

After separating ideas and projects, we decided to also split ideas into two sections newest additions and most popular.

query_posts($query_string . '&posts_per_page=3&taxonomy=event-type&term=ideas');

We hooked thumbs up votes on ideas to a specific custom field in each idea. Thanks to this, we could sort popular posts using post custom field (post_meta) filtering. More about the voting script here.

query_posts($query_string .
'&orderby=meta_value_num&meta_key=votes_up&order=DESC
&posts_per_page=3&taxonomy=event-type&term=ideas');

Projects are separated by category and ordered by newest. In a foreach we loop trough all the categories and query posts for that category.

$categories = get_categories('child_of='.$curr_cat->cat_ID.''); foreach ($categories as $cat)
query_posts('cat='.$cat->cat_ID.'&posts_per_page=3&taxonomy=event-type&term=projects');
The following image shows part of the category structure. Events are a child category of Program,  crowdsourcing subjects are children of events. New categories are automatically added to the program page and submit idea form. The category descriptions are used for the program and event pages.


Attaching groups to poststop

In the first chapter, we explained that we went for the scenario of attaching BuddyPress groups to idea and project posts in the background. This way we would have the flexibility and maturity of post functionality vs the limitations BP groups have considering our architecture, but can still use the membership and activity features BP has to offer.

BuddyPress loops often use ‘slugs’, instead of ID’s. For our convenience, we used the WordPress post ID as the Group slug when creating the group:

slug=$post_id”;

The Group was then attached to the specific post by creating a custom field. Below a screenshot of the custom field area of an idea-post.

update_post_meta($post_id, "group_id", $groupid);

The Group ID is defined when someone submits an idea:

$groupid = groups_create_group($args);

Redirecting group access to attached posttop

Since we implemented BuddyPress functionality inside WordPress templates, we could avoid duplicate content by redirecting the original Buddypress Group URL’s.

Thankfully, as mentioned earlier, we used the corresponding post id as Group slug:

header("Location: " . get_permalink($slug) );

The downside of this redirect was that the Group admin functions couldn’t be reached anymore, necessary for managing group members and roles. The solution for this was adding admin group functions to the post template:

if (current_user_can('manage_options')) {

	$bp->current_action = 'admin';
	$bp->current_component = 'groups';
	$bp->action_variables[0] = 'manage-members';
	if ( bp_is_group_admin_screen( 'manage-members' ) ) :
// Admin options for managing members

Integrating group components in single post templatestop

The following image shows a single post WordPress template with information from an automatically attached BuddyPress group. i.e. followers, activity update form and crew count. WordPress post and group content are mixeed together. The image is the featured_image, the creative commons license a custom field, the followers count and update form are buddypress.

A single post WordPress template with information grabbed from an automatically attached BuddyPress group. i.e. followers, activity update, form and crew.

To integrate these group components we setup a buddypress loop inside the wordpress loop or created a buddypress loop on it’s own:

if ( bp_has_groups(array( 'type' => 'single-group', 'slug' => $post_id)) ) {
while ( bp_groups() ) {
	bp_the_group();
	// do stuff here with buddypress group
}
}

On the idea or project page we implement buddypress functionality like the groups join button and show the last activity.

if ( bp_has_activities( 'primary_id='.$group_id ) ) :
	while ( bp_activities() ) :
bp_the_activity();
// The group activity is shown here
endwhile;
endif;

We can also get the full activity just by passing the group ID as primary_id. The group ID is a custom field of a post, so on the idea and project page we get the value of the custom field and use this to show the Buddypress activity.


Thumbs up thumbs down votingtop

For the thumbs up thumbs down functionality we used a stand alone script to build upon. First, we made it WordPress compatible. Then we realized the functionality to track votes, only allow participators to vote and built the functionality to sort on thumbs up on the program page.

Allowing voting on ideas
We used the post ID, so we could easily see which votes belonged to which post. A built in function creates a votable object when an idea is published.

AJAX requests
Voting is done by an AJAX request. Because we wanted to have WordPress functionality with the callback function we had to change the Javascript a bit. The AJAX URL had to be changed pointing to a WordPress AJAX include file. This way we could use the WordPress callback function with all the globals needed. One problem was that the thumbs up script echoed a JSON object on AJAX access. There was not a get function so we had to buffer the output and place it in a variable:

ob_start();
include ('/home/admin/domains/magneetfestival.nl/public_html/dev/thumbsup/init.php');
$json_return = ob_get_contents();
ob_flush();

Then we could pass this JSON object to our own function that registered the thumbsup action.

User must be logged in to vote
To check if the user is logged in or has already voted there was another callback function that returned the user ID.

global $current_user;
return $current_user->ID;

This returns 0 if the user isn’t logged in and returned the user ID if the user is logged in.

Thumbs up registration

To register the thumbs up and thumbs down on each idea we put the thumbs up count and thumbs down count in a custom field.

update_post_meta($post_id, 'votes_up', $votes_up_meta);

We also wanted to know which users had voted on the specific post. We made a function that added the user ID in a comma separated string.

if ($user_ids = get_post_meta($post_id, 'user_ids', TRUE))
	$user_ids = $user_ids . "," . $current_user->ID;
else
	$user_ids = $current_user->ID;
update_post_meta($post_id, 'user_ids', $user_ids);

Positive votes on users profile

update_user_meta( $current_user->ID, 'thumbsups', $thumbsups );

Then if we needed the post ID’s we explod the string and we get an array of post ID’s.

The image below shows the thumbs up given on a profile page.

Activity

Because the vote ID was the same as the post ID, we could add activity for the specific post. Same principle as seen before.

bp_activity_add( array($fields));
//'action' => sprintf( __( '%s gave thumbs up on %s:', 'buddypress')

Sorting thumbs up

Using a custom field for registering votes, we could use that for sorting:

query_posts($query_string . '&orderby=meta_value_num&meta_key=votes_up&order=DESC);

Image below showing the popular ideas area on the program page. Sorted using the custom field.


Submitting ideastop

The following actions are required creating what we call an ‘idea’.

Create the post

$post_id = wp_insert_post($post);

Where $post is an array containing all the necessary variables (like the title, category and author)

Set the post custom taxonomy

Our custom taxonomy, used for defining either idea or project status is named ‘event-type’.

wp_set_object_terms( $post_id, 'Idea', 'event-type');

$post_id is the returning value of wp_insert_post.

Set the post categories

The post categories are used for attaching an idea to an event (child category of Program) and specific stage (child category of event).

This is done in the function wp_insert_post (see above).

Upload an image

move_uploaded_file ( $_FILES['image']['tmp_name'], $dir_base.$safe_filename);

$_FILES['image']['tmp_name'] is the image that has been uploaded. $dir_base.$safe_filename is the directory structure and filename where the file is moved to.

Set image as featured image (post thumbnail)

$attach_id = wp_insert_attachment( $attachment, $safe_filename, $post_id );

wp_insert_attachment attached the file to the post as regular image.

update_post_meta($post_id, "_thumbnail_id", $attachment_id);
update_post_meta sets the image as the featured image (“_thumbnail_id”).


Create BuddyPress group

$groupid = groups_create_group($group);

Where $group is an array containing all the necessary variables (like the title, slug and author)

Attach group as custom field

update_post_meta($post_id, "group_id", $groupid);

Delete default activity

Because the default activity points to the buddypress group, and we use posts where BuddyPress group info is included, the default activity has to be deleted.

bp_activity_delete( array('item_id' => '1', 'secondary_item_id' => $post_id) );

Add custom activity

bp_activity_add( array($fields));

Where $fields are the user id, action and other values.

Demote group admin to group moderator

Because we don’t want initiators to have total control over the group, we demote the group creator to group moderator.

$member = new BP_Groups_Member($bp->loggedin_user->id, $groupid);
$member->is_admin = 0;
$member->is_mod = 1;
$member->user_title = __( 'Initiator', 'buddypress' );
$member->date_modified = gmdate('Y-m-d H:i:s');
$member->save();

The member is put in an object. The is_admin is set to 0 and the is_mod is set to 1. The user_title is set to “initiator” so we can see which group moderator is the original one.

In our setup, we named groupmoderators Crew.

Set user id as author

To allow an ‘initiator’ to edit his/her idea, we need to promote them to the WordPress role of ‘Author’.

$user = new WP_User($bp->loggedin_user->id);
$user->set_role('author');

This meant that initiators could themselves promote ideas to projects by updating the custom taxonomy they have access to using the WordPress backend. We limited this, by checking level and removing specific areas from the backend:

remove_meta_box($box, $type, $context);

For the custom taxonomy box we used:

$box = 'event-typediv'

Adding social media to ideas and projectstop

We wanted to allow initiators to add social media embeds to their ideas and projects. For example a YouTube movie, Flickr picture, Flickr slideshow or Vimeo video.

To attach social media, we created a custom field which holds the URL. If this custom field is set we can show this through the WordPress oEmbed filter:

echo wp_oembed_get(get_post_meta($post->ID, 'media', true), array('width'=>400));

get_post_meta gets the value of media where the post id is $post->ID.

Because the third parameter is set to true, not an array is returned but just the custom value as variable. The wp_oembed_get function returns the corresponding oembed code which we echo.

To add or edit the custom field, we used a custom function for the front end editor.

editable_post_meta(get_the_ID(), 'media', 'input');

Then when you double click on the URL or on “[insert here]”, you can add your social media URL.

Double clicking the button shows a form.

After entering a valid oEmbed provider URL and refreshing the page, media is embedded with the proper html. The Add button is replaced for a Edit button.


URL structure additionstop

To realize pretty url’s around our program page and for the ideas and projects of each author, we used the add_rewrite_rules WordPress function.

Some examples:

/projects/ leads to event-type > projects and /projects/author/ leads to the projects of the author.

'projects/([^/]+)/?$' => 'index.php?author_name=$matches[1]&event-type=projects',

/category/program/event/ideas/ leads to all the ideas for a specific event.

'category/program/([^/]+)/ideas/?$' => 'index.php?category_name=$matches[1]&event-type=ideas',

Hacking into the activity feedtop

Using WordPress posts and not BuddyPress groups for presenting data and functionality, we had to change the activity. Activity updates normally point to the buddypress groups.

Activity can use custom meta fields, we used that to override default values:

bp_activity_update_meta( $item_id, 'action', $action );

Where $item_id is the activity ID and $action is the meta value of meta key ‘action’. To override the default action of the activity, we made a change in the template where the action is showed. If there is a meta value set then we show the custom meta value instead of the default one.

Later in our progress we found a better solution for this problem, so we didn’t have to change our template. First we delete the activity item and then add a new one.

We upgraded this method later, to avoid having to change the core template. As mentioned in the submitting ideas section, we first delete the activity, and then add a new one:

bp_activity_delete_by_activity_id($activity_id);

The activity_id is used with a hook. For example:

add_action( 'groups_join_group', 'add_action_activity_meta_join', 10, 4);
function add_action_activity_meta ($content, $user_id, $activity_id, $item_id);

The parameters of the function are the variables you get from the hook.

bp_activity_add($fields);

Where $fields is an array with the different variables like group ID and action.

Please also see the Thumbs up thumbs down voting section for information how we added votes to the activity feed.


Development of the Magneet Festival platform was supported by Stichting DigitalePioniers (Digital Pioneers Foundation).

Stichting Magneetbarhttp://www.magneetfestival.nl

Go To Table of Contents