Topic-icon Redirect JFBC User Registrations to akeeba subscriptions

Active Subscriptions:

None
Hi Alex,

Further to my last post, I believe that I will need to use the protected function createUser($profileData) & protected function saveProfileField($fieldId, $value) methods, right?

Just in case, I've updated those methods in my plugin class and added a couple of other methods that I'd previously used in my system plugin to transform the country, state & gender strings from facebook. Ultimately, these will allow me to populate the akeeba subs form, correctly selecting the gender, state and country select lists.

For note, I do this by:
1. including the Akeebasubs component helper in the plugin
2. Getting the states and countries arrays (properties) from the helper
3. Initialising states and countries properties in my SocialProfile to lookup later

So, now my createUser & saveProfileField methods look like:
protected function createUser($profileData)
{
    $query = "INSERT IGNORE INTO #__akeebasubs_users (userid) VALUES (" . $this->db->quote($this->joomlaId) . ")";
    $this->db->setQuery($query);
    $this->db->execute();
    
    // Custom fields stored as params
    $akCustomFieldKeys = array("agreetotos", "dob", "phone", "mobile","gender");
    $akCustomFields = '';

    // 
    foreach ($this->getProfileFields() as $name => $displayName){
        // Regular fields
        if(!in_array($name, $akCustomFieldKeys)){
            if($name == "state"){ // We need to pass the country too
                $this->saveProfileField($name, array($profileData->getFieldWithUserState($name), $profileData->getFieldWithUserState("country")));
            }else{
               $this->saveProfileField($name, $profileData->getFieldWithUserState($name)); 
            }
            
        }else{ // custom (params) fields
            // concatenate string of values
            $akCustomFields .= '"' . $name . '":"' . $profileData->getFieldWithUserState($name) . '"';
        }
    }
    
    // Check if any custom (params) fields included
    if(is_string($akCustomFields) && strlen($akCustomFields) > 5){
        $akCustomFields = '{' . $akCustomFields . '}';
        // Save the field
        $this->saveProfileField('params', $akCustomFields);
    }
}

protected function saveProfileField($fieldId, $value)
{
    $addStateToAddress2 = false; // flag - used to determine if state should be appended to address2 field

    // Load the language file for gender used below
    $lang = JFactory::getLanguage();
    $lang->load('com_jfbconnect');

    switch ($fieldId)
    {
        case "dob":
            //$value = date('m/d/Y',strtotime($value)); // - This is the original from system plugin
            $value = new JDate($value);
            $value = $value->toSql();
            break;
            
        case "gender":
            $value = $this->getGenderIdFromString($value);
            break;
            
        case "state":
            $originalValue = $value[0]; // The state
            $value = $this->getLocationIdFromName('states', $value[0], $value[1]); // $value[1] is the country
            if(trim($value[0]) == ""){
                $addStateToAddress2 = true;
                $state = $originalValue;
            }
            break;
        
        case "country":
            $value = $this->getLocationIdFromName('countries', $value);
            break;
            
    }
    
    $this->db->setQuery("UPDATE #__akeebasubs_users SET `" . $fieldId . "` = " . $this->db->quote($value) . " WHERE userid=" . $this->joomlaId);
    $this->db->execute();
    
    // If a state was present from FB but doesn't exist in the Akeeba States 
    if($addStateToAddress2){
        // Check that the state isn't already part of address2
        $this->db->setQuery("SELECT address2 FROM #__akeebasubs_users WHERE userid =" . $this->joomlaId);
        $result = $this->db->execute();
        
        // If the state isn't found in address2 field
        if(!strpos($state, $result)){
            //append it to the value
            $this->db->setQuery("UPDATE #__akeebasubs_users SET address2 = CONCAT(address2, ', " . $state . "') WHERE userid=" . $this->joomlaId);
            $this->db->execute();
        }
    } 
}
And, the following methods that that transform the FB string data to populate select boxes in Akeeba subs:
/**
 * Method to get the Country/State ID from name of the state/country
 * 
 * @param   string  $type       The type to get - 'countries' or 'states'
 * @param   string  $name       The name of the country/state
 * @param   string  $country    The name of country - ONLY used in case of state lookup
 * @return  sting   $returnID   Either 2 letter ISO Country/State code or nothing if not found
 */
public function getLocationIdFromName($type, $name, $country="")
{
    // Valid types to check against
    $types = array('countries', 'states');
    
    // To return
    $returnID = ""; // Default
    
    // Check we have values to search on
    if((!is_string($name) || strlen($name) <= 0) || 
        (!in_array($type, $types) || !is_string($type)))
        return $returnID;
    
    switch($type){
        case "countries":
            if(is_array($this->countries) && count($this->countries) > 0)
                $returnID = (string)array_search($name, $this->$type);
            break;
            
        case "states":
            // Bail out if no country is passed or if no states present for the country
            if(is_string($country) && strlen($country) > 1 && array_key_exists($country, $this->$type)){
                $states = $this->$type;
                $returnID = (string)array_search($name, $states[$country]);
            }
            break;
            
        default:
            break;
    }
    
    // return
    return $returnID;
}

/**
 * Method to get the gender ID from string
 * Transforms the 'male'/'female' strings from facebook
 * to populate the subscription form with relevant numeric ID
 * 
 * @param   string  $string     "male" or "female" - anything else returns 0
 * @return  int     $genderID   0 = not specified, 1 = male, 2 = female
 */
public function getGenderIdFromString($string)
{
    $genderID = 0; //Default
    
    // Check there was a string passed in
    if(!is_string($string) || strlen($string) <= 1)
        return $genderID; // return the default
    
    switch($string){
        case "male":
            $genderID = 1;
            break;
        case "female":
            $genderID = 2;
            break;
        default:
            $genderID = 0;
            break;
        
    }
    // return ID
    return $genderID;
}
Questions
What do I need to update in the following method to ensure that the fields are displayed/not displayed according to my Akeebasubs social plugin?
/**
* Get field names and inputs to request additional information from users on registration
* @return string HTML of form fields to display to user on registration
*/
public function socialProfilesOnShowRegisterForm($network)
{
    $this->loadSettings($network);
    $profileData = $this->fetchProfileFromFieldMap(false);
    $html = $this->getRegistrationForm($profileData);
    return $html;
}
Do I also need to override the protected function getRegistrationForm($profileData) method in my plugin to achieve this?

Thanks again!!!

Gez
The topic has been locked.
Active Subscriptions:

None
Hi Alex,

I think that I must be missing something here...

If I want to show/hide fields on the akeeba registration page according to the my social profile plugin's config, how do I 'tell' JFBC/social plugin which is the 'registration' form.
Also, please see questions in previous post.

Many thanks,

Gez
The topic has been locked.
Support Specialist
Gez,
You're using the Automatic Registration functionality, so you don't need to implement anything for the socialProfilesOnShowRegisterForm(xx) function. That function is only necessary when using the "Normal" registration mode of JFBConnect to add additional fields to JFBConnect's Registration form. Since you're bypassing our registration form (automatic mode) that function will never get called.

For hiding fields in the Akeeba Subs registration form, you wouldn't modify JFBConnect for that at all. What you'd want to do is create a template override for their /views/tmpl/level/default_fields.php file. In there, simply 'hide' any fields that already have data. For example, the country code looks like:
<div class="control-group <?php echo $group_classes['country'] ?>">
                <label for="country" class="control-label">
                        * <?php echo JText::_('COM_AKEEBASUBS_LEVEL_FIELD_COUNTRY')?>
                </label>
                <div class="controls">
                        <?php echo AkeebasubsHelperSelect::countries($field_data['country'], 'country', array('id'=>'country', 'show' => $cparamShowCountries, 'hide' => $cparamHideCountries)) ?>
                        <span id="country_empty" class="help-inline" <?php if($group_classes['country'] != 'error'):?>style="display:none"<?php endif?>>
                                <?php echo JText::_('COM_AKEEBASUBS_LEVEL_ERR_REQUIRED')?>
                        </span>
                </div>
        </div>
Simply update that to:
<?php      if ($field_data['country'] != null)
             $style = 'style="display:none"';
       else
             $style = "";
       ?>
       <div class="control-group <?php echo $group_classes['country'] ?>" <?php echo $style ?> >
                <label for="country" class="control-label">
                        * <?php echo JText::_('COM_AKEEBASUBS_LEVEL_FIELD_COUNTRY')?>
                </label>
                <div class="controls">
                        <?php echo AkeebasubsHelperSelect::countries($field_data['country'], 'country', array('id'=>'country', 'show' => $cparamShowCountries, 'hide' => $cparamHideCountries)) ?>
                        <span id="country_empty" class="help-inline" <?php if($group_classes['country'] != 'error'):?>style="display:none"<?php endif?>>
                                <?php echo JText::_('COM_AKEEBASUBS_LEVEL_ERR_REQUIRED')?>
                        </span>
                </div>
        </div>
With that, you're checking if there is already data for the user for that field, and if so, hiding it. We do the same on this site already to make for as small of a form as possible.

I hope that helps, but if you have questions, let me know.

Thanks,
Alex
The topic has been locked.
Active Subscriptions:

None
Hi Alex,

Great, thanks for this!

A couple more questions if I may...
1. How is JFBC/SCProfile handling updating the user's profile - I mean, which methods? Obviously, I need the saveProfileField()
2. Do I need to override them to have the users' fields Ito be updated at login based on my profile plugin's config? && Will this be effected by the fact that I'm using auto crate users

Sorry I should have mentioned that I'd already prepared duplicates of all of the views I want to override in my templates html dir. I was wondering more if there was a method to get/hide the fields' display setting from the config, provide the form for which you wish to apply it too (i.e. akeeba subs subscriptions level form) and somehow - a system plugin & JFBC event/trigger of sorts - the parameters take effect to show/hide fields that are populated by facebook data by detecting the view, component etc... Obviously, I was not correct in my assumption. Maybe that kind of implementation could be a cool feature. I'm just thinking that it could really 'open the door' for developers to extend profile integration even further.

Anyway, II'll give this a shot and see how I go with it but, I hope to write some additional methods in my social plugin to dynamically render some jquery toggling of form elements and their wrapping divs. I would aim to do this based on social profile plugin default settings or overrides pumped into the view=level subscription form via an akeeba subs plugin.

In this plugin, I would give the user the ability to override fields/permissions requests for each subscription level, mapping of each default and level-specific field to facebook fields via same dropdowns as in the main configuration and possibly a checkbox for each to specify whether it should be hidden or not. In fact, maybe one 'hide on profile edit' and another 'hide on registration' checkbox for each.

I'll just need to write a few methods to get the fields from akeeba subs- firstly for the main plugin config and secondly, from each specific configured subscription level. This would make it completely dynamic. I'll also need another method to dynamically prepare and inject the javascript according to the config/subscription form and maybe some addition to my system plugin.

Does that all sound relatively sensible?

Are there any 'hidden gem' methods existing in any of the SCProfiles/JFBC classes that you think would be wise for me to use relating to above?

Anyway, thanks again for your time and help with this!

Much appreciated!

Gez
The topic has been locked.
Support Specialist

1. How is JFBC/SCProfile handling updating the user's profile - I mean, which methods? Obviously, I need the saveProfileField()

First, you save the mapping of the fields in the admin area. JFBConnect stores this 'mapping' in the database. Then, on login or registration, we automatically fetch any fields that you've requested about the user and automatically call that saveProfileField($fieldId, $value) function. The $fieldId parameter would be the Akeeba Subs field Id that is having data imported into it. The $value field is the data from Facebook to import. Basically, JFBConnect takes care of all the logic of fetching the profile and doing a lot of that stuff (if you already have the configuration page working), so all you have to do is decide how to save that data.

2. Do I need to override them to have the users' fields Ito be updated at login based on my profile plugin's config? && Will this be effected by the fact that I'm using auto crate users

In the constructor of your plugin, there should be a line like $this->defaultSettings->set('import_always', '0'); If you set that value to '1', then the profile will be imported on every login. If it's '0', then it will only happen on registration.

There isn't anything in JFBConnect to alter other extension forms. You'll need to decide if you want to do that in those extensions. Most of what you're talking about above is showing/hiding fields on the Akeeba Subs registration form, from what I can tell. Nothing in the Social Profile plugin will be able to help you do that. Those plugins are only used on our registration page (which you're not using, since you're using automatic registration) and during login to automatically import a user's data. They are not triggered on other pages of the site, and wouldn't have any effect on Akeeba Subs pages.

I hope that helps explain. Mainly, I think you'll want to use the Social Profile plugins to automatically import a user's profile into the Akeeba Subs database tables.. then, when the Akeeba Subs subscription page is shown, choose to show or hide fields depending on if there is already data filled out (or imported) for that field for that user.

Best of luck,
Alex
The topic has been locked.
Active Subscriptions:

None
Great thanks Alex!!! I'll let you know how it goes!

Gez
The topic has been locked.
Support Specialist
Sounds good. Keep us posted!

Thanks,
Alex
The topic has been locked.
Active Subscriptions:

None
hi @alzander,

An update on this so far:
Everything in Social profile working fine ;) except that, since I'm not in the default JFBC loginregister view, I don't have access to the plugin's settings.

How I'm doing this:
I'm getting and mapping data from facebook in a system plugin and setting my custom 'mapped' data in a state var. This is all working fine. Then, I loop through all of the Akeeba subscriptions form data ($view->cache) and replace the values of any data from facebook using my state var. As this loop runs, If this data is present from facebook, the field ID is stored in array which is later pushed into the $view. This then allows me to determine which fields have been populated by facebook and therefore, which ones to hide. Again this is all working fine.

I know that I'm probably missing something simple here, but how can I get the plugin's settings (as per my configuration page in profiles tab) in order to show/hide fields grabbed by facebook?

I want to do this before my loop runs since, if the administrator has decided not to hide facebook fields, I won't bother adding field IDs related to data retrieved from facebook as it would be unnecessary.

Many thanks!!!

Gez
The topic has been locked.
Active Subscriptions:

None
Hi @alzander,

further to my last post, I just wanted to clarify that, in my akeeba subs default_fields.php, I am checking for the existence of $this->fbFields that is set in the onBeforeAkeebasubsControllerLevelRead method (see #3 below). Essentially, what I need to do in this method is get the configuration options from my social profile plugin before that var/property is set...

Anyway, here's some of the code that I'm using in my system plugin that might help clarify things further:
1. Here is the method that retrieves the state var I create when a user logs in with facebook:
public function getJFBCState()
{
    //default return
    $return = false;
    
    // Get app
    $app = JFactory::getApplication();
    
    // Get the state var
    $jfbc_state = $app->getUserState('plg_socialprofiles.facebook.akeebasubs', null);
    
    // If the state var exists
    if(!is_null($jfbc_state)){
        // JSON decode it
        $jfbc_state = json_decode($jfbc_state);
        
        // map the 'raw' fb profile to a custom session
        $this->fbProfile = $this->mapJFBCProfileToAkeebasubs($jfbc_state);
        
        // If the mapped profile empty, return
        if(empty($this->fbProfile) || is_null($this->fbProfile))
            return $return;
        
        // set custom state var. This will be used to pre-fill forms with the user's FB profile data 
        $app->setUserState('mybo.elite.mapped.fb.profile', $this->fbProfile);   
        
        // set flag
        $this->jfbcProfileExists = true;
        
        //Return
        $return = true;
    }
    return $return;
}

2. here is the method in my system plugin that gets passed the 'raw' FB data that's retrieved when a user logs in/connects with FB and decodes it to be akeeba subs-ready:
/**
  * Method to encode/decode facebook profile data from JFBC
  * and map it to Akeeba subscription cache so that the 
  * subscription form is pre-populated
  * 
  * @param  object  $profileData    The profile object returned from facebook login
  * @return array   $fbDataCheck    Associative array containing Akeebasubs-ready data to pre-populate subscription form       
  */
 public function mapJFBCProfileToAkeebasubs($profileData)
 {
     // To return
     $fbDataCheck = array();
     
     if(!is_object($profileData) || is_null($profileData))
        return $fbDataCheck;
    
     // location properties to check
     $locationFields = array('country', 'state', 'city');
    
     // Loop through all of the fbFields 'keys' to check/transform them for akeeba
     foreach($profileData as $f => $v){
        //If the field & value exist
        if($v){
            switch($f){
                    
                //Location related object
                case 'current_location':
                    foreach($locationFields as $l){
                            
                        //Check that the property exists in the location object & there's a value
                        if(property_exists($profileData->$f, $l) && $v){
                            switch($l){
                                //Country property
                                case "country": 
                                    //$fbDataCheck[$l] = $this->getLocationIdFromName('countries', $profileData->current_location->country);
                                    $fbDataCheck[$l] = $this->getLocationIdFromName('countries', $v->$l);
                                    break;
                                
                                //State property    
                                case "state":
                                    // Check if the state exists in akeeba subs
                                    $state = $this->getLocationIdFromName('states', $v->$l, $profileData->current_location->country);
                                    
                                    // set state to 2 char ISO code if getLocationIdFromName() not empty OR to original string from FB id 
                                    $fbDataCheck[$l] = (!is_null($state) && strlen($state) >= 3) ? $state : $v->$l;
                                    break;
                                
                                //Other properties     
                                default:
                                    $fbDataCheck[$l] = $v->$l;
                                    break;
                            }// End switch $l
                        }// End if property_exists    
                    }// End foreach $locationFields
                    break;
                
                //DOB
                case 'birthday':
                    $fbDataCheck['dob'] = date('m/d/Y',strtotime($v));
                    break;
                
                // GENDER
                case 'sex':
                    $fbDataCheck['gender'] = $this->getGenderIdFromString($v);
                    break;
                
                // Anything else    
                default:
                    $fbDataCheck[$f] = $v;
                    break;
            }// End switch 
        }//End if
     }// End foreach
     
     return $fbDataCheck;
 }

3. Here is the onBeforeAkeebasubsControllerLevelRead method I'm using to override the $view->cache:
public function onBeforeAkeebasubsControllerLevelRead(FOFController &$controller, FOFInput &$input)
{
    // Get JFBCProfile Plugin
    $plugin = JPluginHelper::getPlugin('socialprofiles', 'akeebasubs');
    
    /* I NEED TO GET THE PLUGIN SETTINGS HERE BEFORE PROCEEDING 
     * This will allow me to show/hide fields populated by facebook 
     * according to the configuration of the akeebasubs social profile
     */
    
    // Get the view 
    $view = $controller->getThisView();
    $viewCache = (array)$view->cache;
    
    if(!$this->jfbcLoaded || !$this->jfbcProfileExists)
        return;
    
    // To store akeeba field ids of fields filled by FB
    $facebookData = array(); 
    
    // Loop through each view->cache
    foreach($viewCache as $k => $v){
        // If a match found in FB data
        if((array_key_exists($k, $this->fbProfile) && ($viewCache[$k] || $this->fbProfile[$k])) || $k == "custom"){
            switch($k){
                // Custom akeeba fields
                case "custom":
                    // Check its an array
                    if(is_array($v) && !empty($v)){
                        //loop through
                        foreach($v as $key => $val){
                            // both vals
                            if($val && $this->fbProfile[$key]){
                                $viewCache[$k][$key] = $this->fbProfile[$key];
                                $facebookData[] = $key; 
                            }
                            // just facebook val
                            elseif(!$val && $this->fbProfile[$key]){
                                $viewCache[$k][$key] = $this->fbProfile[$key];
                                $facebookData[] = $key; 
                            }
                        }//end foreach
                        
                        
                    }else die('not array');
                    break;
                
                // All other fields
                default:
                    //if both vals
                    if($v && $this->fbProfile[$k]){
                        $viewCache[$k] = $this->fbProfile[$k];
                        $facebookData[] = $k;   
                    }
                    // Just FB
                    elseif(!$v && $this->fbProfile[$k]){
                        $viewCache[$k] = $this->fbProfile[$k];
                        $facebookData[] = $k;
                    }
                    
                    break;
            }
        }
    }
    
    //Set cache and fbFields properties of the view
    $view->set('fbFields', $facebookData);
    $view->set('cache', $viewCache);
}
Thanks once again for your help!!!

Gez
The topic has been locked.
Active Subscriptions:

None
Hi Alex,

just wondered whether you've had chance to look at my last 2 posts yet?

Many thank,

Gez
The topic has been locked.