Published
Sunday, June 15, 2014 - 14:25
Written by

I was accepted into the Google Summer of Code program this year to write the Drupal 8 integration modules for CiviCRM and work has progressed well so far. Drupal 8 is on track for a release this year and hopefully CiviCRM will be Drupal 8 ready about the time it goes final.

The integration is quite a large project and in the planning/proposal phase I separated the work into 6 phases. The first 3 phases are broadly concerned with allowing CiviCRM to boot and to be able to access the bits of Drupal (ie. users) that it needs to. The final 3 phases were to implement the opposite: they are about enabling Drupal to interact with CiviCRM data, for example via Views or Rules.

To run through them briefly, phase 1 is a bare bootstrap, with only as much functionality implemented so that CiviCRM can run simple pages. For this phase, anything that requires CiviCRM to ask Drupal about users, or permissions, etc. would fail. Phase 2 involves fleshing out this missing functionality primarily by subclassing CRM_Utils_System_Base. And phase 3 involves finishing the full range of functionality of the core Drupal module that we had in Drupal 7.

Currently, these 3 phases are in a rough stage of completion.

The final 3 phases have changed somewhat since my first proposal. Initially, I had planned on implementing the core CiviCRM objects (eg. contacts, events, groups, relationships…) as native Drupal entities. I was under the impression that entities integration would get me a lot of ‘free’ functionality, in particular in the form of Views and Rules working out of the box. Unfortunately, this ‘free’ functionality won’t be available when Drupal 8 is first released. Views and Rules will have to implemented manually, which may take up the majority of the remaining time available for GSoC.

I had also not factored in the installation process in my initial planning. As it turns out, the current installer is strongly hardcoded for the various CRMs that CiviCRM supports and in a bit of a bad state, so with the guidance of my mentors we’ve chosen an alternative route: create some reusable installer components and run the install process from within Drupal itself. This has the nice side effect that for the majority of use cases, CiviCRM will be installable on Drupal by simply enabling the module. This work is rather finicky and at the moment still in progress.

In the course of this work I’ve taken up some strong opinions with how CiviCRM should orient itself in the future with regards to the CMSs it supports. Some of these probably deserve full blog posts, but bear with me a moment:

  • CiviCRM needs to be strictly CMS agnostic (there is currently too much CMS-conditional code in core!).
  • CiviCRM needs to position itself as a client of the CMS - I think a lot of the code is still written from the days when CiviCRM was standalone.
  • Related to the above: trust in the CMS to make calls for CSS, JS, page contents, breadcrumbs, page title, permissions, etc. at the time when the CMS deems it appropriate. At the moment, CiviCRM tries to set all of these as a side-effect of being asked for the page contents and at least for Drupal 8, this process doesn’t work anymore.
  • Don’t ever attempt to bootstrap the CMS — if the CMS needs to be bootstrapped, then go in via the front door first.

For the second half of the GSoC term, my top priority is finishing the installer so that I can put out a alpha release for people to play with, and then turn to the Views and Rules integration work.

Filed under

Comments

I think the last 4 items in Torrance's blog are worth quite a lot more discussion. I'd like to see some examples of how the code is & how the code should be. WRT the 1st point - since 4.1 we've had a clear intention to work towards CMS specific code being in the CMS specific classes - I'm not 100% sure whether people are aware of this though.  The rule of thumb is that the words 'Drupal', 'Joomla' & 'Wordpress' should not appear in the codebase outside the CMS specific files

 

Here is an example of what the code should NOT look like

 

switch ($config->userFramework) {
  case 'Drupal':
    $this->assign('ufAccessURL', CRM_Utils_System::url('admin/people/permissions'));
    break;

  case 'Drupal6':
    $this->assign('ufAccessURL', CRM_Utils_System::url('admin/user/permissions'));
    break;

  case 'Joomla':
    //condition based on Joomla version; <= 2.5 uses modal window; >= 3.0 uses full page with return value
    if( version_compare(JVERSION, '3.0', 'lt') ) {
      JHTML::_('behavior.modal');
      $url = $config->userFrameworkBaseURL . 'index.php?option=com_config&view=component&component=com_civicrm&tmpl=component';
      $jparams = 'rel="{handler: \'iframe\', size: {x: 875, y: 550}, onClose: function() {}}" class="modal"';

      $this->assign('ufAccessURL', $url);
      $this->assign('jAccessParams', $jparams);
    }
    else {
      $uri = (string) JUri::getInstance();
      $return = urlencode(base64_encode($uri));
      $url = $config->userFrameworkBaseURL . 'index.php?option=com_config&view=component&component=com_civicrm&return=' . $return;

      $this->assign('ufAccessURL', $url);
      $this->assign('jAccessParams', '');
    }
    break;

  case 'WordPress':
    $this->assign('ufAccessURL', CRM_Utils_System::url('civicrm/admin/access/wp-permissions', 'reset=1'));
    break;
}
return parent::run();

 

In fact that function should look like

 

$this->assign('ufAccessURL', $config->userSystem->getCMSPermissionsURL());

 

Each of the base classes should have a function e.g

 

CRM_Utils_System_WordPress

/**
 * Get url for user permission page
 * 
 * @return string url for CMS specific permission page
 *//

getCMSPermissionsURL() {

  return CRM_Utils_System::url('civicrm/admin/access/wp-permissions', 'reset=1';
}

 

 

This example also throws up the fact that in some cases Joomla! needs additional params. It's unclear without some digging as to whether there is a better way to deal with the jparams (it does seem quite cludgey) but one option would be to add

 

$extraParams = $config->userSystem->getCMSPermissionsParams());

foreach ($extraParams as $key => $value) {

  $this->assign($key, $value):

}

 

Although I suspect we should mark getCMSPermissionsParams as deprecated from the get-go per comments above.

 

 

Torrance - would love to see some specific examples relative to your other points

 

$extraParams = $config->userSystem->getCMSSpecificParams($pageName, $context));



Arguably we could add this part to CRM_Core_Form rather than have it in the relevant page

 

$extraParams = $config->userSystem->getCMSPermissionsParams());

foreach ($extraParams as $key => $value) {

  $this->assign($key, $value):

}

 

Of course it's worth noting that ideally we'd get the 'Joomla!' out of the templates as well. Or we could use the fact that Smarty actually allows templates to extend other templates in the way that classes extend other classes to add in CMS specific. We haven't got a defined approach for templates in the way we do for PHP

Being CMS independent is a great feature of CiviCRM, please keep it that way. It would make no sense to tie core CiviCRM to any CMS's, or have CIviCRM act as a 'CMS client' of any particular CMS. Instead the approach advocated by Eileen make a lot greater sense: isolate CMS-specific behaviours in a class, and subclass it for every CMS we need to integtrate with.

That way, we can expand our CMS's coverage over time (MODX, Typo3, Concrete 5, dotnetnuke have all been asked for in the forums), and therefore expand the reach of CiviCRM to new horizons.

Hi,

We have a few "non main entrance" bootstraps (extern/* and bin/*) mostly.

I'm not sure the historical reasons, but one side benefit used to be performances (boostraping D7 is dog slow). Because of a lot of side effects, we know bootstrap pretty much each of these backdoor entrances so it's more trouble and zero benefits. If we started it now, we would probably not have these indeed.

This being said, quite a few urls (rest.php, ipn.php...) are probably all hardcoded in the various clients that need it. Do you have any suggestion to move back to normal frontdoor without breaking everything?

 

I think we definitely need to keep the 'backdoors' open: whether this is for running the CiviCRM cron, posting back an IPN from Paypal, registering a bounce from an email blaster or a myriad other uses cases, there is absolutely no need for the CMS to be involved.

So bootstrapping the CMS in these cases has absolutely no value, will impact performance, and can only bring side effect and hard-to-debug errors with each CMS's different bootstrapping process & requirements in an otherwise simple process.

More and more of these alternate entries do bootstrap the CMS (or some variation of a proper bootstrap), and it was introduced to avoid other side effects of not having the CMS layer bootstrapped.

Some of the civicore is expecting to have a "normal" CMS session with the user id set properly and otehr stuff the CMS boostrap initialise and not having them created weird results. For instance, one of the reasons we boostraped the CMS for the api was the relationship api. Somewhere in the creation code, the current user is tested and when it wasn't setup properly, it would do weird stuff that took quite a while to identify.

X+

Although I would like to move the extern/ipn.php url to using the same format as the more recent payment processors (which does bootstrap CiviCRM) we will have to deprecate rather than remove that URL at that point - as people will have it configured in their Paypal administration.

 

I think the question is not what would it take to be able to remove these files / alternate 'ins' but what would it take to be able to deprecate them.

 

However, I'm not advocating moving CMS specific code to CMS specific classes INSTEAD of deprecating the direct path to ipns / extern / bin files.

 

I'd like to hear Torrance flesh out more about what he means about being a client. Torrance, Nicolas (CiviDesk) has resurrected the standalone version & delivers a SaaS using it - so obviously has an interest in that. It seems to me that the Standalone version *should* have it's own CMS files eg.  CRM_Utils_System_StandAlone - although perhaps the base class proxies for that? If that were in place I suspect that a bunch of functionalities that are currently making other integrations more complex could be moved to the standalone integration - does that sound righ?

Torrance,

How did GSoC and the Drupal 8 Integration project go this year? I've been poking around periodically to see how things have been going but there don't seem to be a lot of postings about it.

Thanks,
Andrew