Drupal 7 load profile2 programmatically - drupal-7

I have two profile2 profiles defined - main and customer_profile. Also, I have a node type called Customer.
When creating a new Customer node, I would like to load the custom_profile form. The idea is to create a node and a profile simultaneously.
I know it's definately a hook_form_alter solution but can someone tell me how to programmatically load a profile while creating or editing a Customer node.

You can load profile type and data by using these function
$types = profile2_get_types();
profile2_load_by_user($account, $type_name = NULL)
For Example :
$types = profile2_get_types();
if (!empty($types)) {
foreach ($types as $type) {
$profile = profile2_load_by_user($uid, $type->type);
}
}

Even if you are able to load the customer_profile form, you will need to handle the values separately as they are two different nodes.
I would suggest capturing those fields in the customer node form, and then create a customer_profile programmatically from the values.
If you want to get the profile2 form itself then you can use something like
module_load_include('inc', 'profile2_page', 'profile2_page');
$profile2 = profile2_by_uid_load($uid, 'seeker_profile');
$entity_form = entity_ui_get_form('profile2', $profile2, 'edit');
and then add that to the form you want to place it in.

You can load full profile data using profile2_load_by_user();
params like:-
profile2_load_by_user($account,$type_name)
$account: The user account to load profiles for, or its uid.
$type_name: To load a single profile, pass the type name of the profile to load
So code like bellow
$account->uid = $existingUser->uid;
$type_name = 'user_about';
$profile = profile2_load_by_user($account, $type_name);
//$profile variable have full data of profile fields
//changing data profile2 fields
if(isset($_POST['field_user_first_name'])&& !empty($_POST['field_user_first_name'])){
$profile->field_user_first_name['und'][0]['value'] = $_POST['field_user_first_name'];
}
profile2_save($profile);

Well When creating a new Profile , Profile2 fields are not visible until a manual save is done.
To Automatically create the profile2 object , We use rules Module
Step
1) Go to Drupal admin/config/workflow/rules
2) create new rule
3) Give a name and Select in react/event "After saving a new user account"
4) Action,>> Add Action >> Execute custom PHP code
5) insert php code
$profile = profile_create(array('type' => 'profile2 type machine name', 'uid' => $account->uid));
profile2_save($profile);
6)Save >> Save changes.
This will create profile2 field when a new user is Created.

I had a similar need for creating a custom tab at the user page and loading the user profile2 form in it.
Here is a snap code of how I managed to accomplish just that:
MYMODULE.module https://gist.github.com/4223234
MYMODULE_profile2_MYPROFILE2TYPE.inc https://gist.github.com/4223201
Hope it helps.

Related

Adding multiple owners for Google App Maker records

I need help with Google App Maker data model security. I want to set multiple owners for a single record. Like the current user + the assigned admin + super admin.
I need this because all records can have different owners and super-owners/admins.
I know that we can point google app maker to a field containing record owner's email and we can set that field to the current user at the time of the creation of the record.
record.Owner = Session.getActiveUser().getEmail();
I want to know if it is possible to have field owners or have multiple fields like owner1, owner2 and then assign access levels to owner1, owner2...
Or how can we programmatically control the access/security/permissions of records?
The solution I'd use for this one definitely involves a field on the record that contains a comma separated string of all the users who should have access to it. I've worked on the following example to explain better what I have in mind.
I created a model and is called documents and looks like this:
In a page, I have a table and a button to add new document records. The page looks like this:
When I click on the Add Document button, a dialog pops up and looks like this:
The logic on the SUBMIT button on the form above is the following:
widget.datasource.item.owners = app.user.email;
widget.datasource.createItem(function(){
app.closeDialog();
});
That will automatically assign the creator of the record the ownership. To add additional owners, I do it on an edit form. The edit form popus up when I click the edit button inside the record row. It looks like this:
As you can see, I'm using a list widget to control who the owners are. For that, it is necessary to use a <List>String custom property in the edit dialog and that will be the datasource of the list widget. In this case, I've called it owners. I've applied the following to the onClick event of the edit button:
var owners = widget.datasource.item.owners;
owners = owners ? owners.split(",") : [];
app.pageFragments.documentEdit.properties.owners = owners;
app.showDialog(app.pageFragments.documentEdit);
The add button above the list widget has the following logic for the onClick event handler:
widget.root.properties.owners.push("");
The TextBox widget inside the row of the list widget has the following logic for the onValueEdit event handler:
widget.root.properties.owners[widget.parent.childIndex] = newValue;
And the CLOSE button has the following logic for the onClick event handler:
var owners = widget.root.properties.owners || [];
if(owners && owners.length){
owners = owners.filter(function(owner){
return owner != false; //jshint ignore:line
});
}
widget.datasource.item.owners = owners.join();
app.closeDialog();
Since I want to create a logic that will load records only for authorized users, then I had to use a query script in the datasource that will serve that purpose. For that I created this function on a server script:
function getAuthorizedRecords(){
var authorized = [];
var userRoles = app.getActiveUserRoles();
var allRecs = app.models.documents.newQuery().run();
if(userRoles.indexOf(app.roles.Admins) > -1){
return allRecs;
} else {
for(var r=0; r<allRecs.length; r++){
var rec = allRecs[r];
if(rec.owners && rec.owners.indexOf(Session.getActiveUser().getEmail()) > -1){
authorized.push(rec);
}
}
return authorized;
}
}
And then on the documents datasource, I added the following to the query script:
return getAuthorizedRecords();
This solution will load all records for admin users, but for non-admin users, it will only load records where their email is located in the owners field of the record. This is the most elegant solution I could come up with and I hope it serves your purpose.
References:
https://developers.google.com/appmaker/models/datasources#query_script
https://developers.google.com/appmaker/ui/binding#custom_properties
https://developers.google.com/appmaker/ui/logic#events
https://developers-dot-devsite-v2-prod.appspot.com/appmaker/scripting/api/client#Record

Listing Form Templates within another Controller view

I have a form_templates table and a forms table. They're connected by form_template_id. I want to be able to list the form_templates that have been created by title within a select.ctp file I have created within the Forms controller. Just wanting some direction on how to do this with cakephp?
At the moment I have the following code within my FormsController:
public function select()
{
$this->set('page_heading', 'Current Forms');
$contain = [];
$formTemplate = $this->FormTemplates->Forms->find('list', ['order' => 'title'])->where(['active'=>true]);
$forms = $this->paginate($this->Forms->FormTemplates);
$this->set(compact('forms', 'formTemplate'));
}
But I am getting a Call to a member function find() on null error.
Any help on how to tackle this would be greatly appreciated. I know it would be simple but I am new to cakephp.
In your FormsController only FormsTable is loaded automatically, and you are trying to access model that is not currently loaded:
$formTemplate = $this->FormTemplates->Forms->find(...
To get what you want, you should access associated FormTemplatesTable like this:
$formTemplate = $this->Forms->FormTemplates->find(...

How to update the database after transaction?

I have a Drupal 7 site that has a customized theme installed on it and we have also added some new tables and extended some others.
Right now I have set up Ubercart for selling our products based on the taxonomy. When the purchase is complete I need to update a custom table in MySQL so I made a proc to do that.
In MySQL the created a proc that will do the updating of the tables that I need, all I need to pass into the proc is the uid (same as from the users table) and an id of the taxonomy that was selected during purchase.
I have created the following code to make the call but I am not sure what the best way to pass in the uid and tid to the proc? Should I be using rules within Drupal?
<?php
$mysqli = new mysqli("mysite.com", "user", "password", "db1");
if ($mysqli->connect_errno) {
echo "Failed to connect to MySQL: (" . $mysqli->connect_errno . ") " . $mysqli->connect_error;
}
if (!$mysqli->query("CALL UpdateUserDestList(uID, tID")) {
echo "CALL failed: (" . $mysqli->errno . ") " . $mysqli->error;
}
?>
You can fire your code after a purchase using Ubercart's hook_uc_checkout_complete(). You will want to define that in a custom module, not from within the theme since this is business logic not display information. From within the hook you will have access to both the order and the user account that placed the order.
When working with Drupal, or any other framework really, you should leverage the built-in tools when you can to benefit from the structures they provide.
// Get the Drupal database connection and change the statement class to PDOStatement.
// Save the current class for cleanup later.
$conn = Database::getConnection();
$saved_class = $conn->getAttribute(PDO::ATTR_STATEMENT_CLASS);
$conn->setAttribute(PDO::ATTR_STATEMENT_CLASS, array('PDOStatement'));
// Prepare the statement and bind params
$statement = $conn->prepare("Call GetNodeList(?,?)");
$op_status = $statement->bindParam(1, $node_type, PDO::PARAM_STR | PDO::PARAM_INPUT_OUTPUT, 25);
$op_status = $statement->bindParam(2, $publish_state, PDO::PARAM_INT | PDO::PARAM_INPUT_OUTPUT);
// Execute the statement and reset the connection's statement class to the original.
$exec_result = $statement->execute();
$conn->setAttribute(PDO::ATTR_STATEMENT_CLASS, $saved_class);
// Get your data
while ($row = $statement->fetchColumn(0)) {
// ...
}
Code taken from: https://drupal.stackexchange.com/questions/32708/how-to-execute-stored-procedures-in-drupal which in turn cites: http://public-action.org/content/stored-procedures-and-drupal-7

Adding a 1 to many file upload to CRUD

My app has sales listing functionality that will allow the user to add 1 or more photos for the product that they want to sell.
I'm attempting to use the upload/filestore_image of ATK with a Join table to create the relationship - my models:
class Model_Listing extends Model_Table {
public $entity_code='listing';
function init(){
parent::init();
$this->addField('name');
$this->addField('body')->type('text');
$this->addField('status');
$this->addField('showStatus')->calculated(true);
}
function calculate_showStatus(){
return ($this->status == 1) ? "Sold" : "For Sale" ;
}
}
class Model_listingimages extends Model_Table {
public $entity_code='listing_images';
function init(){
parent::init();
$this->addField('listing_id')->refModel('Model_Listing');
$this->addField('filestore_image_id')->refModel('Model_Filestore_Image');
}
}
In my page manager class I have added the file upload to the crud:
class page_manager extends Page {
function init(){
parent::init();
$tabs=$this->add('Tabs');
$s = $tabs->addTab('Sales')->add('CRUD');
$s->setModel('Listing',array('name','body','status'),array('name','status'));
if ($s->form) {
$f = $s->form;
$f->addField('upload','Add Photos')->setModel('Filestore_Image');
$f->add('FileGrid')->setModel('Filestore_Image');
}
}
}
My questions:
I am getting a "Unable to include FileGrid.php" error - I want the user to be able to see the images that they have uploaded and hoped that this would be the best way to do so - by adding the file grid to bottom of the form. - EDIT - ignore this question, I created a FileGrid class based on the code in the example link below - that fixed the issue.
How do I make the association between the CRUD form so that a submit will save the uploaded files and create entries in the join table?
I have installed the latest release of ATK4, added the 4 filestore tables to the db and referenced the following page in the documentation http://codepad.agiletoolkit.org/image
TIA
PG
By creating model based on Filestore_File
You need to specify a proper model. By proper I mean:
It must be extending Model_Filestore_File
It must have MasterField set to link it with your entry
In this case, however you must know the referenced ID when the images are being uploaded, so it won't work if you upload image before creating record. Just to give you idea the code would look
$mymodel=$this->add('Model_listingimages');
$mymodel->setMasterField('listing_id',$listing_id);
$upload_field->setModel($mymodel);
$upload_field->allowMultiple();
This way all the images uploaded through the field will automatically be associated with your listing. You will need to inherit model from Model_Filestore_File. The Model_Filestore_Image is a really great example which you can use. You should add related entity (join) and define fields in that table.
There is other way too:
By doing some extra work in linking images
When form is submitted, you can retrieve list of file IDs by simply getting them.
$form->get('add_photos')
Inside form submission handler you can perform some manual insertion into listingimages.
$form->onSubmit(function($form) uses($listing_id){
$photos = explode(',',$form->get('add_photos'));
$m=$form->add('Model_listingimages');
foreach($photos as $photo_id){
$m->unloadDdata()->set('listing_id',$listing_id)
->set('filestore_image_id',$photo_id)->update();
}
}); // I'm not sure if this will be called by CRUD, which has
// it's own form submit handler, but give it a try.
You must be careful, through, if you use global model inside the upload field without restrictions, then user can access or delete images uploaded by other users. If you use file model with MVCGrid you should see what files they can theoretically get access to. That's normal and that's why I recommend using the first method described above.
NOTE: you should not use spaces in file name, 2nd argument to addField, it breaks javascript.

Drupal 7: Multiple pages for one node

We list events on our Drupal 7 site, but we'd like our users to be able to register for these events via a simple form. We're using Pathauto to generate URL aliases for events using the following pattern: events/[node:title]. We would like to have another page with the alias events/register/[node:title] which would present the registration form. We would also like to use tpl.php files for creating the templates, like we do for the rest of the site.
Any ideas on how we might accomplish this? Thanks.
You can try the Signup module. It's still in development, but over 7000 sites are using it:
I would put a register button on the event/ page via the node.tpl.php file. If all your nodes are not registerable, then you can check the node by getting the $nid with $node->nid and run a db_query on the url_alias table to see if current node qualifies.
<?php
$nid = $node->nid;
$result = db_query('SELECT alias FROM {url_alias} WHERE source = :source,
array(':source' => 'node/'.$nid));
foreach ($result as $r) {
$alias = $r->alias;
}
if (strpos($alias, 'events')) {
?> <input... or <button...
Have your register button redirect to events/register/$node->title page and make sure you pass the node. There's a few different ways to go from here. If you have questions about this part open another thread.

Resources