setting persistent plugin parameters in Joomla 3 - php

I'm developing a Joomla 3.x plugin, and want to be able to change the plugin parameter set in the plugin's manifest file programmatically. I believe I need to use a JRegistry object, but I'm not sure about the syntax.
Here's the issue:
// token A is set in plugin params as defined in plugin's XML manifest
var_dump($this->params->get('token')); // prints token "A" as expected
// do some stuff to get a fresh access token, called token "B"
$tokenB = $function_to_get_fresh_token();
// set the new token
if ($tokenB) $this->params->set('token', $tokenB);
var_dump($this->params->get('apptoken')); // prints token "B" as expected
the problem is that on subsequent page reloads, the token reverts to tokenA rather than what I assumed would be the stored value of tokenB.
How do I store the tokenB value in the plugin's parameters in the database?

This is a working example of how to change plugin params from within the plugin (J! 3.4):
// Load plugin called 'plugin_name'
$table = new JTableExtension(JFactory::getDbo());
$table->load(array('element' => 'plugin_name'));
// Params can be changed like this
$this->params->set('new_param', 'new value'); // if you are doing change from a plugin
$table->set('params', $this->params->toString());
// Save the change
$table->store();
Note: If new params are added by plugin dynamically and the plugin is saved afterwards, these new params gets deleted. So one way to deal with it is to add those params as hidden fields to plugin's config XML.

This is just an outline, but something along these lines
$extensionTable = new JtableExtension();
$pluginId = $extensionTable->find('element', 'my_plugin');
$pluginRow = $extensionTable->load($pluginId);
// Do the jregistry work that is needed
// do some stuff to get a fresh access token, called token "B"
$tokenB = $function_to_get_fresh_token();
// set the new token
if ($tokenB) $this->params->set('token', $tokenB);
// more stuff
$extensionTable->save($pluginRow);

I spent a lot of time googling and reading and found no real answer to this. Oddly enough this doesn't seem to have been provided for in Joomla. So here's what I ended up doing:
1) build a function to get your plugin ID, since it will change from one installation to another
private function getPlgId(){
// stupid hack since there doesn't seem to be another way to get plugin id
$db = JFactory::getDBO();
$sql = 'SELECT `extension_id` FROM `#__extensions` WHERE `element` = "my_plugin" AND `folder` = "my_plugin_folder"'; // check the #__extensions table if you don't know your element / folder
$db->setQuery($sql);
if( !($plg = $db->loadObject()) ){
return false;
} else {
return (int) $plg->extension_id;
}
}
2) use the plugin id to load the table object:
$extension = new JTableExtension($db);
$ext_id = $this->getPlgId();
// get the existing extension data
$extension->load($ext_id);
3) when you're ready to store the value, add it to the params, then store it:
$this->params->set('myvalue', $newvalue);
$extension->bind( array('params' => $this->params->toString()) );
// check and store
if (!$extension->check()) {
$this->setError($extension->getError());
return false;
}
if (!$extension->store()) {
$this->setError($extension->getError());
return false;
}
If anyone knows a better way to do this please let me know!

Related

How do I use the Cohort form in my plugin

How do I use the Form for creating a Cohort in Moodle in my own plugin?
I want to use the form, create the Cohort and return to a URL specified by with the Cohort id as a GET parameter ie http://myip/moodle/myplugin/myscrip.php?cohort_id=0
I've taken a look at the moodle files for the form but it's all chinese for me as i'm a complete novice as it comes to moodle development. What is the 'nice' way of using it?
The forms take a bit of getting used to, they are based on quickforms.
https://docs.moodle.org/dev/Form_API
https://docs.moodle.org/dev/lib/formslib.php_Usage
https://docs.moodle.org/dev/lib/formslib.php_Form_Definition
Usually there is a file named edit.php which uses the class in edit_form.php - but the file names can be anything.
You could take a copy of /cohort/edit.php and put it into your local plugin. If you are only creating cohorts and not updating them, then remove the update and delete code and keep the add cohort code:
$cohortid = cohort_add_cohort($data);
if ($usetags) {
if (isset($data->otags)) {
tag_set('cohort', $cohortid, tag_get_name($data->otags));
} else {
tag_set('cohort', $cohortid, array());
}
}
//update textarea
$data = file_postupdate_standard_editor($data, 'description', $editoroptions, $context, 'cohort', 'cohort', $cohortid);
$DB->set_field('cohort', 'description', $data->description, array('id' => $cohortid));
Then redirect to your link
$url = new moodle_url('/myplugin/myscrip.php', array('cohort_id' => $cohortid));
redirect($url);

Use "Self enrolment" in "Add a new course"

Im new for PHP and Moodle..., I want set "Max enrolled users" in the page "Add a new course".
In this moment i did this code in archive(moodle\course\edit_form.php):
$mform->addElement('text', 'customint3', get_string('maxenrolled', 'enrol_self'));
$mform->addHelpButton('customint3', 'maxenrolled', 'enrol_self');
$mform->setType('customint3', PARAM_INT);
but i know miss something for use Self Enrolment in this page... like active the table "enrol_self"...
For use "Self Enrolment" , first need to get "CourseID". Because in the page of "Self Enrolment" need a foreign key for updating the format of a course.
So... how can I do in the code "edit_form.php" for creating a course and updating Enrolment Self(maxenrolled) immediately after click the button?? Have function of moodle for easy edit it??
Need your advice please!! And thanks!!
You can set the default for max enrolled in the plugin settings. Go to site admin -> plugins -> enrolment -> self enrolment
or go directly to yoursite.com/admin/settings.php?section=enrolsettingsself
If you want to use a different value each time, then in /enrol/self/edit.php look for this code (this is the code in Moodle 2.5 it might be slightly different in other versions):
} else {
require_capability('moodle/course:enrolconfig', $context);
// No instance yet, we have to add new instance.
navigation_node::override_active_url(new moodle_url('/enrol/instances.php', array('id'=>$course->id)));
$instance = (object)$plugin->get_instance_defaults();
$instance->id = null;
$instance->courseid = $course->id;
$instance->status = ENROL_INSTANCE_ENABLED; // Do not use default for automatically created instances here.
}
and insert this line
$instance->courseid = $course->id;
$instance->customint3 = $yourdefault; // Your new default.
$instance->status = ENROL_INSTANCE_ENABLED;

Preventing Infinite Redirects in Access Control Library

I am working on an Open Source Role Based Access Control Library for PHP called PHP-Bouncer. PHP-Bouncer allows the user to define a list of roles, which pages each role provides access to, and each role may also define a list of pages which override other pages (so going to the overridden page will redirect you to the overriding page). Here is an example of how this would work (From the Access Managed Example in the documentation):
$bouncer = new Bouncer();
// Add a role Name, Array of pages role provides
$bouncer->addRole("Public", array("index.php", "about.php", "fail.php"));
// Add a role Name, Array of pages role provides
$bouncer->addRole("Registered User", array("myaccount.php", "editaccount.php", "viewusers.php"));
// Add a role Name, Array of pages role provides List of pages that are overridden by other pages
$bouncer->addRole("Admin", array("stats.php", "manageusers.php"), array("viewusers.php" => "manageusers.php"));
// Here we add some users. The user class here extends the BouncerUser class, so it can still do whatever you
// would normally create a user class to do..
$publicUser = new User();
$registeredUser = new User();
$adminUser = new User();
$registeredAndAdmin = new User();
$publicUser->addRole("Public");
$registeredUser->addRole("Public"); // We add the public group to all users since they need it to see index.php
$registeredUser->addRole("Registered User");
$adminUser->addRole("Public"); // We add the public group to all users since they need it to see index.php
$adminUser->addRole("Admin");
$registeredAndAdmin->addRole("Public"); // We add the public group to all users since they need it to see index.php
$registeredAndAdmin->addRole("Registered User");
$registeredAndAdmin->addRole("Admin");
$bouncer->manageAccess($publicUser->getRoles(), substr($_SERVER["PHP_SELF"], 1), "fail.php");
Here's the problem I am having: In the manageAccess function you see above, everything works well as long as the roles are defined sanely, and all users have access to fail.php (or fail.php does not implement the $bouncer object). As soon as someone creates a role which has an inherent conflict (such as overriding a page with itself) or fails to give all users access to the failure page, the manageAccess function results in an infinite loop. Since this is bad, I would like to fix it. I am not sure however, what would be the best approach for allowing a few redirects (it is feasible that redirecting up to two or three times could be desired behaviour) while preventing an infinite loop. Here is the manageAccess function:
/**
* #param array $roleList
* #param string $url
* #param string $failPage
*/
public function manageAccess($roleList, $url, $failPage = "index.php"){
$granted = false;
foreach($roleList as $role){
if(array_key_exists($role, $this->roles)){
$obj = $this->roles[$role];
/** #var $obj BouncerRole */
$response = $obj->verifyAccess($url);
if($response->getIsOverridden()){ // If access to the page is overridden forward the user to the overriding page
$loc = ($obj->getOverridingPage($url) !== false) ? $obj->getOverridingPage($url) : $failPage;
$locationString = "Location: ".$loc;
header($locationString);
// I broke something in the last commit, perhaps this comment will help?
}
if($response->getIsAccessible()){ // If this particular role contains access to the page set granted to true
$granted = true; // We don't return yet in case another role overrides.
}
}
}
// If we are here, we know that the page has not been overridden
// so let's check to see if access has been granted by any of our roles.
// If not, the user doesn't have access so we'll forward them on to the failure page.
if(!$granted){
$locationString = "Location: ".$failPage."?url=".urlencode($url)."&roles=".urlencode(serialize($roleList));
header($locationString);
}
}
Any Suggestions?
Since your desired functionality is to allow "a few redirects", the best way I can think of approaching this is creating either a $_SESSION (or $_GET I suppose would work) parameter that you increment every time you issue a redirect. I should point out that from a user standpoint this would be pretty annoying to deal with, but it is your website.
Let's assume we named the $_SESSION parameter num_redirects:
Before we redirect, check to see if $_SESSION['num_redirects'] is set. If it is not, set it to 0.
Get the current value of $_SESSION['num_redirects'], and see if it is above the redirect threshold.
If it is above the threshold, do not redirect, simply die();
If it is not above the threshold, increment $_SESSION['num_redirects'], store it back, and redirect.
So, that code would look like this (I've also improved your URL query string with a call to http_build_query():
Somewhere, we need to define the threshold:
define( 'MAX_NUM_REDIRECTS', 3);
Then, we can do:
// Assuming session_start(); has already occurred
if(!$granted) {
if( !isset( $_SESSION['num_redirects'])) {
$_SESSION['num_redirects'] = 0;
}
// Check if too many redirects have occurred
if( $_SESSION['num_redirects'] >= MAX_NUM_REDIRECTS) {
die( "Severe Error: Misconfigured roles - Maximum number of redirects reached\n");
}
// If we get here, we can redirect the user, just add 1 to the redirect count
$_SESSION['num_redirects'] += 1;
$query_string = http_build_query( array(
'url' => $url,
'roles' => serialize($roleList)
));
$locationString = "Location: " . $failPage . '?' . $query_string;
header($locationString);
exit(); // Probably also want to kill the script here
}
That's it! Note that you'll have to add the same logic for the first call to header(). If you're going to repeat this functionality elsewhere, it might be worthwhile to encapsulate redirection within a helper function:
function redirect( $url, array $params = array()) {
if( !isset( $_SESSION['num_redirects'])) {
$_SESSION['num_redirects'] = 0;
}
// Check if too many redirects have occurred
if( $_SESSION['num_redirects'] >= MAX_NUM_REDIRECTS) {
die( "Severe Error: Maximum number of redirects reached\n");
}
// If we get here, we can redirect the user, just add 1 to the redirect count
$_SESSION['num_redirects'] += 1;
$query_string = http_build_query( $params);
$locationString = "Location: " . $url . ( !empty( $query_string) ? ('?' . $query_string) : '');
header($locationString);
exit(); // Probably also want to kill the script here
}
Then, you can call it like this:
if(!$granted){
redirect( $failPage, array(
'url' => $url,
'roles' => serialize($roleList)
));
}
This way you can encapsulate all of the logic necessary to redirecting within that one function.

passing parameters securely in Zend

I am in service controller, login action (/service/login) and I want to pass the name,email and company to profile controller, register action(/profile/register).
Currently I am doing this way
$this->_redirect('/profile/register/?name='.$name.'&emailid='.$email.'&companyid='.$company);
But I want to pass this info to /profile/register in a way such that the user can't see these in the url.
I tried with $this->_forward like this
$params = array(
name=>$name,
emailid=>$email,
companyid=>$company
);
$this->_forward('register','profile',null,$params);
But this isn't working. Is there any other way I can do this?
To pass parameters securely, you should store the data in a session, or if you cannot use sessions, then in the database.
You could use Zend_Session to store the data temporarily until the next page view where you can retrieve it and it will be deleted (or you can let it persist).
$s = new Zend_Session_Namespace('registrationData');
$s->setExpirationHops(1); // expire the namespace after 1 page view
$s->name = $name;
$s->email = $email;
$s->companyId = $company;
// ...
return $this->_redirect('/profile/register);
And then in profile/register:
$s = new Zend_Session_Namespace('registrationData');
$name = $s->name;
$email = $s->email;
$companyId = $s->companyId;
// when this request terminates, the data will be deleted if you leave
// setExpirationHops as 1.
See also Zend_Session - namespace expiration

Add attributeSet but based on an existing set - Magento

Ok, its possible to add a new attribute set in magento using something like the following:
$entitySetup = new Mage_Eav_Model_Entity_Setup;
$entitySetup->addAttributeSet('catalog_product', $setName);
But how can i base the set on an existing set like the default. This option is available in the admin section so its possible.
I did this 6 months ago, I do not have the code anymore, but I know you have to use the initFromSkeleton() method on your attribute set. You can search Magento's code for calls to this function, there are very few calls (just one perhaps). It will show you its usage.
EDIT: I remember I had the very same problem you are talking about, and I mailed about it. Here is the usage I was advised:
$attrSet = Mage::getModel('eav/entity_attribute_set');
$attrSet->setAttributeSetName('MyAttributeSet');
$attrSet->setEntityTypeId(4);//You can look into the db what '4' corresponds to, I think it is for products.
$attrSet->initFromSkeleton($attrSetId);
$attrSet->save();
The initialization is done before the save.
// create attribute set
$entityTypeId = Mage::getModel('eav/entity')
->setType('catalog_product')
->getTypeId(); // 4 - Default
$newSet = Mage::getModel('eav/entity_attribute_set');
$newSet->setEntityTypeId($entityTypeId);
$newSet->setAttributeSetName(self::ATTRIBUTE_SET_NAME);
$newSet->save();
$newSet->initFromSkeleton($entityTypeId);
$newSet->save();
This is what worked for me.
$i_duplicate_attribut_set_id = 10; // ID of Attribut-Set you want to duplicate
$object = new Mage_Catalog_Model_Product_Attribute_Set_Api();
$object->create('YOUR_ATTRIBUT_SET_NAME', $i_duplicate_attribut_set_id);
Alex
Here you can find useful information about working with attribute sets.
Here:
$entityTypeId = Mage::getModel('eav/entity')
->setType('catalog_product') // This can be any eav_entity_type code
->getTypeId();
$attrSet = Mage::getModel('eav/entity_attribute_set');
$attrSetCollection = $attrSet->getCollection();
$attrSetCollection
->addFieldToFilter('entity_type_id', array('eq' => $entityTypeId))
->addFieldToFilter('attribute_set_name', array('eq' => 'Default')); // This can be any attribute set you might want to clone
$defaultAttrSet = $attrSetCollection->getFirstItem();
$defaultAttrSetId = $defaultAttrSet->getAttributeSetId();
$attrSet->setAttributeSetName('Assinaturas'); // This is the new attribute set name
$attrSet->setEntityTypeId($entityTypeId);
$attrSet->initFromSkeleton($defaultAttrSetId);
$attrSet->save();

Categories