This summary explains how we are using webform to allow multiple people to be registered for multiple events - with each person registering for any variation of events
Blogs
A customer recently requested that the little contact search box in the top left corner be made available on all their drupal pages and that it link to the contact's Drupal page.
Re-using the Civi-search box seemed a bit too difficult but there was a fairly easy solution to this request using existing drupal modules - finder + views (plus features to deploy).
Finder module will create an autocomplete box off any view. I created a basic View based on drupal users. After Pete had finished with it it had 3 fields: contact display name - linked to CiviCRM contact, Drupal user name - linked to Drupal contact & drupal email linked to a mail to. It was also filtered to 'Is Primary Email'. It looks like this (filtered to a single record).

Next up I installed Finder module along with finder_views and finder_autocomplete. Finder appears under site building when installed. I chose to add a Views Finder and selected my View. I selected both to be able to view the Finder as form and as a block. I then went to 'add element' and chose an autocomplete textfield. There is a 'find items by field' box and I chose the same 3 fields as above. In order to reduce the load I chose the drupal email which is in the same table as the drupal username rather than the Civi email. I'll see if I get a request for the CiviCRM email to be used...
Those few steps were enough that my Finder page now looks like the below. It will autocomplete on username, email or CiviCRM display name.

The block sits on the left hand side & currently looks like this.

After a bit of tinkering when it seemed OK I packaged the view into a Feature which can be downloaded & installed from the fuzion svn (accept the certificate - it's self-signed). One of my biggest gripes about the Finder module is that it isn't possible to export finders as a Feature. The maintainer seems fairly opposed for some reason I can't make sense of. Anyway, I did find I could add the Finder module to the install hook on my Feature so it was possible to package it with the view although it's not possible to maintain it in code going forwards like you can with a Feature.
So, to use our implementation - you need a bunch of modules; Views, Features, Finder, CTools. Once you have all of them enabled just enable civifinder and there will be a block available for you to enable wherever you want on your site
I recently had a requirement to set up a CiviCRM install so that whenever an event (or specific types) was created an organic group would also be created. When someone registered for that event they would be added to the organic group and if they were registered as a teacher they would become the manager of the organic group.
The focus of this blog is on the code implementation of this requirement using rules integration. I wanted to see if it could be done by rules & if that would set a useful precedent. The two aspects of Rules that where the crux of what I was trying to do were not mplemented in the CiviCRM Rules module so I will describe them in some detail.
The way in which the CiviCRM rules module works is that has functions for the various drupal hooks. When one is called CiviCRM Rules evaluates whether or not CiviCRM Rules integration exists for that hook for the specific entity and option.
In our case the focus is on the POST hook. The post hook is called once a CiviCRM entity is created (an entity could be a Contact, an Event, a Group etc). I wanted to expose the POST hook on Event & Participant - but also to do it in a way that I could add other entities easily so I added a drupal admin settings form to provide an interface to allow the site administrator to specify when to call it. At the moment there is no code way of generating a list of entities that call the POST hook but we have talked about introducing that in CiviCRM 4.1. (at the moment available entities are just coded as an array).
So, once the civicrm POST hook is called it calls 'RULES' & gives it the 'event' - so when a contact is created RULES is called with 'contact_create'. I felt that with such things as groups and events there was a risk of confusion with events exposed by other drupal modules so for the additional objects I called rules with a prefix ie. 'civicrm_event_create'. Note that I will try to use 'rules event' & civicrm event in this blog to distinguish the two.
So, what is involved
1) register the event types. You register the types of events that should be available from your module to rules. So, I registered create, view, delete, edit for each enabled entity. I did this by calling a function in a separate file as I wanted to keep my code as separate as possible (as it may wind up being a separate module).
The rules function that registers the events is called
function civicrm_rules_event_info()
(My additions are in function civicrm_rules_entities_event_info())
So, registering my function means that when you go to set up a rule you can choose the rules event you want
2) Arguments: Once I have created a rules event I need to be able to do something with it. In this case I wanted to create a node. It turns out you can't create a node using Rules unless you have a User object available to your events. Being able to access alternate objects is called an argument in Rules parlance.
In order to create the argument for the CiviCRM Event I declared this argument when I registered the event.
return array(
'cms_user' => array(
'type' => 'user',
'label' => t('User that edited the event'),
'handler' => 'civicrm_rules_events_argument_civicrm_event',
) ,)
The handler is a function that is called & must return an object of the defined type - in this case 'user' .
This 'argument' - the user object - becomes available for all drupal actions (like creating content - or organic groups in this case) where a user object is required. This might also include adding a role to user or making them inactive.It would be pretty easy to also add a contact object argument here so you could carry out rules actions on that contact like adding them to a group.
Conditions & Arguments
So, I wanted to be able to say
"WHEN the event that I have just created event is of type x, y,z"
Is of type xyz is a condition. I struggled with this for a bit but basically it's a case of declaring the condition
function civicrm_rules_entities_condition_info() {
return array(
'civicrm_rules_condition_participant_role' => array(
'label' => t('Participant Role'),
'arguments' => civicrm_rules_entities_conditions( t( 'Participant Role' ),'civicrm_participant' ),
'module' => 'CiviCRM participant',
),
'civicrm_rules_condition_event_type' => array(
'label' => t('Event Type'),
'module' => 'CiviCRM event',
),
);
For the civicrm_rules_condition_event_type I wanted to allow the admin to use checkboxes to choose the type - I took a while to figure out how to do that but it turned out I just needed a function with _form on the end:
/**
* Condition: Check for content types - Configuration form
*/
function civicrm_rules_condition_event_type_form($settings, &$form) {
$form['settings']['civicrm_event_type'] = array(
'#type' => 'checkboxes',
'#title' => t('CiviCRM Event Types'),
'#options' => civicrm_rules_get_options( 'event_type'),
'#multiple' => TRUE,
'#default_value' => isset($settings['type'] ? $settings['type'] : array(),
'#required' => TRUE,
);
}
AARGH _ wrong arguments currently being passed in! - CHECKING FIXING
If the administrator specifies this condition as part of their rule it calls the function which is the array key from the 'info' function
/**
* Condition civicrm_event type
*/
function civicrm_rules_condition_event_type(&$entityobj,$types,$field) {
return array_key_exists($event['values'][$entityobj>id]['event_type_id'],$types) ? true : false;
}
So, conditions + arguments
It took me a while to figure out conditions & arguments . Here is the next part of the workflow broken up in Rules-speak
- When someone registers for an event (rules event : civicrm_participant_create)
- If their Role is '1' (rules condition : civicrm_rules_condition_participant_role)
- Subscribe 'the user who signed up' (argument) to 'the node related to the event' (argument)
Loading up the 'node related to the event' seemed to me to require a cck field against the event & turned out to be an odessey of it's own. In the end I have just implemented code that will load up a node based on the value in a cck field called 'civicrm_event' matching the event ID.
CODE
https://svn.fuzion.co.nz/repos/fuzion-code/trunk/drupal/modules/custom/civicrm_rules
In order to deploy the rules I packaged them as a feature. The feature is not good to go out of the box for someone else as it also includes views, and migrate tasks in order to create organic groups from existing events
https://svn.fuzion.co.nz/repos/egw/trunk/sites/all/modules/egwc_custom_modules/ogevents
The rule itself is defined in
Where to go:
I think it's fairly easy to extend the POST hook side of rules with more arguments & conditions & ideally offer civicrm entities as arguments.
I think deciding whether to present civicrm_entities with the 'civicrm_' prefix is a
I kept my code mostly in a separate file & referred to it as entities but I suspect all the generic 'stuff' should be in one file & there should be another file for entity specific arguments / conditions (per entity) although sometimes a file for conditions & one for arguments appeal. Whatever way .. it needs structure.
Doing this integration highlighted to me the usefulness of some things we have talked about implementing :
1) Having the POST hooks defined in Code. Lobo, Xavier & I have talked about moving the POST hooks to the DAO and having a function somewhere that defines which DAO actually call it - since we probably don't need to call it for all DAO
2) A major reason I didn't go with cck field was that I couldn't make it do autocomplete on event using the existing API - the API should accept '%blah%' arguments on all fields - another one for the long to-do list.
Table Wizard offers a quick & dirty way to get a csv into a Drupal mysql DB as a table. You need to start by downloading & enabling tablewizard and schema. Make sure you choose to enable the delimited import part.
You should then be able to navigate to admin/content/tw via the content menu and upload your csv file.
If you want the table available in views, However you need to access MySQL (e.g via PHPMyAdmin)
and add a primary key column e.g.
ALTER TABLE `contacts_import` ADD `twid` INT( 12 ) NOT NULL AUTO_INCREMENT PRIMARY KEY FIRST ;
The table name will be the same as your view. Once you have done that flush caches & 'analyse' the table in the table wizard dashboard- you need to re-analyze & save. You can also comment on and exclude fields. Make sure to set any fields that connect to other tables as available keys
The joy of CiviMigrate is when you have a complicated destination for your data. In our example we have a simple csv with 6 fields but we want to create a Contact (see previous blog), a membership for that contact and an attached contribution record for the payment received. This blog will describe how to do a membership import and then a contribution import off the same data set.
This blog follows on very closely from the previous blog and continues the same example.
Prequisites
Your data needs to be already in a view with a unique key - here is one way to get it there from csv
Civimigrate module installed & enabled. (see previous blog)
Your view needs to have a contact_id field already (see previous blog)
Getting started
TBC - need to write getting data in with TW first
The Migrate module is a powerful tool for importing data into Drupal and Drupal modules. It is also an approach we have been using for large, complicated or recurring imports into CiviCRM. Some advantages of using Migrate module for imports:
- You can specify how many (and which) records you wish to import in order to do a small test.
- You can then make changes and remove / re-import the records until you are happy
- You can manipulate data on the way in using hooks
- You can automate the import using drush
- You can run the update on a regular basis via cron & new additions will be picked up (this is a technique we are using to get 3 click csv uploads into multiple Civi types
- It is easy to visualise what is happening & come back to it over a period of time
- Advantages of CiviMigrate are the ability to import related entities - eg memberships with related contributions from one source
In this blog we will look at a very basic contact import & then in the following one will look at creating memberships for those contacts based on the same csv. The csv we are using is (will be) attached.
CiviMigrate does not replace existing CiviCRM migrate methods. It is good for different things. In general you need to set up a migration step per entity so if you wish to create a contact and a related contact and a relationship between them you need three steps.
Prequisites
Your data needs to be already in a view with a unique key - here is one way to get it there from csv
migrate module installed & enabled (drush dl migrate / drush en migrate )
Note that migrate module has table wizard as a pre-requisite but will not in the future. I have mostly switched to using Data module with it now. Schema is a pre-requisite of Table Wizard. You may wish to supress schema warnings @ admin/build/schema/settings
Civimigrate module installed & enabled. Civimigrate is currently just being hosted in the Fuzion repository although after (considerable) tidy-up it will hopefully eventually reside on drupal.org. For now you can get it by
svn co https://svn.fuzion.co.nz/repos/fuzion-code/trunk/drupal/modules/custom/civimigrate
drush en civimigrate
Getting started
Start at the migrate dashboard - admin/content/migrate/dashboard or navigate via 'content' from the admin menu.
Choose to add a set. Choose your view as the source and CiviCRM Contact as the destination. I recommend you set a weight on all steps & use a numbering scheme like 10, 20, 30 to enable you to re-order steps more easily later.
Setting up a mapping
In this next step you match your views fields to CiviCRM fields. Make sure you set the Unique ID first if necessary - in this case we are using system ID. CiviMigrate will create a table which maps this ID to the civicrm contact_id which is important for us to import related items (like contributions).
CiviMigrate uses the CiviCRM API to import data and generally the fields available for you to map to are the fields from the relevant tables with some manual additions & subtractions. In this case we will choose to map:
first_name to first_name
last_name to last_name
SystemID to external_identifier
phone to phone
optional: phone_type_id - we are going to set this to 1 - see civicrm/admin/options/phone_type&group=phone_type&reset=1 for the values. (without this it will still import but phone type won't be set).
optional: source - something to identify our import
Running the Import
Once you have saved the mappings you run the import by ticking the box next to it and opening up the execute section. You can specify to only import one record.under sample size and click run.
When I run the above I find that '0' records successfully import and I can see there is 1 error. Clicking on the error does not, however, show me the error - what's happening? When the migrate ran it created two new tables
migrate_map_contacts (my migration is called contacts)
migrate_map_msgs
The code also allows for the creation of views so we can see them but before we can access them the first time we need to flush all caches (well, I'm not sure which one so blunt hammer here). Now that I can access the table in the migrate_map_messages I see
88 Contact Type not specified
88 is the system ID for the first contact it attempted and Contact Type not Specified tells me I need to go back to my migrate map & enter 'Individual' against contact_type.
I re-do my import with another '1' record and the next record updates fine. My first record won't be touched until I choose the update existing option - but read the section on that below before you go there.
Looking at what has happened
Go to views and filter on 'tw' - you will see the migrate_map table for your import. It has a sourceid - your unique ID and a destid - the CiviCRM contact ID, and a 'needs_update' field. It can be very helpful to edit the destid field to output as link - use this link and it will link to your CiviCRM Contact
civicrm/contact/view?reset=1&cid=[destid]
When you next run an import it will look for any records in your source view that are not in your migrate_map or that are in your migrate map & have needs_update=1 against them and run the update command on them.
dupe matching
Civimigrate will always treat an external identifier match as a match - whether it's in your dedupe rule or not.
If your contact matches an existing CiviCRM contact based on your default de-dupe rule Civimigrate currently has 2 possible behaviours (only because that's all we've needed so far as we have mostly used an external_identifier for our contact import matches)
By default CiviMigrate will treat your imported contact as a match for the CiviCRM contact with the lowest ID and create any that don't exist.
If you choose 'Only update (don't create) and only if ONLY one match is found' CiviCRM will only update matches and not create any new contacts (existing ID is used). To choose this put 1 in the default for this field. This option is often valid when you don't want to import contacts at all - just contributions but you need to map up the contact_id first - see below for that.
Not hugely useful but you can tell CiviCRM to consider the logged in users permissions when determining a match by choosing "Do you want the current users's permissions used to limit dupe matching?(default = False)"
Changing your import - deletes
You can use 'clear' to delete your records if you wish to re-do them. Most probably if you are using a 3.2 version of CiviCRM this will cause the contact to be soft-deleted but 3.1 & 3.3 should cause hard deletes (desired in this case). Be careful if using an existing data set as contacts that have been matched by de-dupe will be deleted!
Updates & mapping contact ids
If you wish to do an update there is a very important step to do first (this is less important for contacts where matches can be done based on dedupe & external identifiers than it is for other civimigrate entities but let's do it here as good practice and prevents 'odd' things happening).
A word first about table wizard & data. Table Wizard is deprecated but I have been unable to create the joins described here using Data module with Migrate 1. I believe it is because Table Wizard manages the migrate_map tables and you need Data to manage them in order to use Data
Table Wizard Relationship
Go to the Table Wizard dashboard admin/content/tw - choose Add existing tables and add your source table. Then go to 'Relationships' admin/content/tw/relationships. Add a relationship from (the top field / base table) the unique ID of your source table to (bottom field) the equivalent of migrate_map_contacts.sourceid. The Automatic setting is fine.
Each time you accidentally connect to the migrate_msg_contacts instead log onto CiviCRM & make a small donation - this will save you time in the long run.
Once the relationship is added sort out your view
Now go back to your source view (the one based on your data table) stopping only to clear your views cache. Now try to add fields. You should now see the fields from the migrate_map_contacts table. Remember, your destid is your CiviCRM contact_id. Add migrate_map_contacts.destid to the view and label it contact_id (and probably link it to civicrm/contact/view?reset=1&cid=[destid] )
Update your migrate map
Finally, go back to your migrate job map and map this new contact_id field to the contact_id field. When you update your records now there will be no danger of a new contact being created. For contributions mapping the id field is absolutely essential
Updating records
Once you have your contact ID mapped up you can change your mapping and then click 'update existing' to update previously migrated records with your new settings. This is especially useful if you have changed your mind about where to map a particular value to.
Caution selecting 'update previously-imported content' sets the value needs_update to 1 for ALL records in the migrate_map table. Even if you have set your import to update 1 record next time you run it it will try to update all the others as needs_update is set to 1
Where to?
These were the basics of importing a contact using CiviMigrate but the real power is related transactions. The next blog will explain how to import our next 2 fields 'amount' & date as a membership & a related contribution.
NOTE: This approach has been superceded by our work developing the bridge module for using the Drupal Migrate module to migrate legacy data in to CivICRM. Read about it here
Often data that you need to import into CiviCRM is presented to you in an Excel or CSV format. Of course there are various ways to do this but the Data / Feeds module combo provides a nice method if you are likely to be uploading a similar csv more than once, or if the person actually uploading doesn't have the access or knowledge to directly edit the database. This method is also really nice for nightly uploads from another system because the most technical thing you have to do is write a script to ftp your csv onto the server. Follow on blogs from this will describe how to migrate data from these imports into CiviCRM & UberCart
Feeds allows you to get data from a range of formats (csv, RSS etc) and put them into a range of formats (nodes, mySQL table) but here the focus is csv to MySQL table.
Prerequisites:
CSV of the data you want to upload
Schema module installed (drush dl schema / drush en schema)
Data module installed (drush dl data / drush en data, drush dl data_ui / drush en data_ui )
Feeds module installed (drush dl feeds / drush en feeds, drush dl feeds_ui / drush en feeds_ui)
Views module installed (drush dl views / drush en views, drush dl views_ui / drush en views_ui)
Set up the feed config
Start at this URL (under structure/ feeds importers / New Importer on the admin menu or site building/ feeds importers / New Importer)
/admin/build/feeds/create
Give your feed a sensible machine name (pref lower case with underscores but no spaces, not too long) and a descriptive name & click create.
Settings
Many of the settings are self-explanatory - I'm just going to mention those you should change / consider or those that took me a while to make sense of
1) Select a Fetcher - click on 'Select' next to 'File Upload'
2) Optional - check 'Supply path to file directly' - this is good when you are ftping files up to the server (e.g by script) & you want them to be uploaded automatically. There isn't s downside to this setting
3) Parser - select csv Parser. You can then edit the default separator typeif you wish it can also be changed at the point of import
4) Processor - select 'Data Processor'
5) Under data processor settings consider whether you want the records created to be removed from your table after a period. I recommend you do select 'Replace Existing Records' which will allow you to do updates as well as inserts
6) Mapping- For each field in your csv enter the field's name in the source and choose the field type under target. If you choose integer or VarChar you will be able to make the field unique. You should select unique for any fields that uniquely identify the record in your csv. You don't have to create mappings for fields you aren't interested in
Note: Leave 'Attach to content type' as standalone form - this setting is really for when you want to have a content type like 'Bob's blog feed and use it to create blog content items from an RSS feed.
That's it to set it up!
Importing your data
Go to /import and click on the feed you set up. From this URL you can select & upload your csv. Any columns in your csv that match will be imported. If you are regularly importing this is the ONLY step you need to keep doing. One URL, 3 clicks.
Note that you can look at the csv that was used if you want to edit it and re-upload. There is also a 'delete items' option
Viewing your data
A View will have been created for you - you can navigate to it by selecting structure / site configuration in the admin menu/ data tables/ overview. Click on the link to 'visit the Data table Content Page' (I always overlook this link and go round a loop getting there)
You will now be at /admin/content/data. Click on View and you will be in your table's view. This is a normal view which you can edit.
Using your data
I will look at importing the data into CiviCRM in a separate blog
Open Source projects often suffer from development ideas not getting sufficient resources to implement properly, with the outcome being lots of users continue to use work arounds or poor fixes.
Eileen has put some time in to setting up CiviCRM with a Make it Happen facility so that clients and consultants can make donations to particular projects. If the particular project does not get sufficient funding, then the donor has the option of a refund, or to shift it to another project.
Check out the options here: Make it Happen - support your favorite Civi project!
Nice to see some praise for the Aus Greens site. From the ABC website http://www.abc.net.au/unleashed/stories/s2955511.htm
"The Australian Greens have by far the best online presence. The smell of a (non-)oily rag has produced a more vibrant site. Candidates and MPs have livelier and more personal blogs and there's a sense of a frequently updated and topical site.
The Greens are also much better marketers: there are frequent and prominent calls to action sprinkled throughout the site."