Error: A required parameter (id) was missing when redirect URL in form Moodle - block

Moodle 3, developing a block
I'm getting the following error: 'A required parameter (id) was missing'.
It happens in $mform, only when I'm using the array('id' => $instance->id) in the 'Else if' statement in the redirect($url).
Surprisingly, because when I'm using the same code in a button with a redirect url, the code works correct.
I've tried several things, but nothing helps. What could be the problem?
Here is some code:
$id = required_param('id', PARAM_INT);
$instance = $DB->get_record('block_instances', array('id' => $id), '*', MUST_EXIST);
$context = \context_block::instance($instance->id);
$mform = new newlink();
if ($mform->is_cancelled()) {
$url = new moodle_url('/my');
redirect($url);
} else if ($fromform = $mform->get_data()) {
// data from form
....
$url = new moodle_url('/blocks/name_of_block/links.php', array('id' => $instance->id)); // HERE IS THE PROBLEM
(Note: when I'm using here the block instance id number 123 directly, the redirect is working correct:
$url = new moodle_url('/blocks/name_of_block/links.php?id=123';)
redirect ($url);
} else {
//Set default data (if any)
$mform->set_data($toform);
//displays the form
$mform->display();
}
$url = new moodle_url('/blocks/name_of_block/links.php', array('id' => $instance->id))
echo $OUTPUT->single_button($url, get_string('button:links', 'block_name_of_block')); // THIS IS WORKING CORRECT

Is it when you submit the form rather than the redirect?
In your form do you have:
$mform->addElement('hidden', 'id');
$mform->setType('id', PARAM_INT);
and in the code above do you have:
$toform->id = $id;
$mform->set_data($toform);

Related

Testing updating user profile data

I make http tests in my Laravel 5.7 application with profile page(which has "Profile View" page and "Profile Details" editor ), like :
$newUser->save();
$testing_user_id = $newUser->id;
$newUserGroup = new UserGroup();
$newUserGroup->group_id = USER_ACCESS_USER;
$newUserGroup->user_id = $newUser->id;
$userGroup= Group::find($newUserGroup->group_id);
$newUserSessionData = [
[
'loggedUserAccessGroups' => ['group_id' => $newUserGroup->group_id, 'group_name' => !empty($userGroup) ? $userGroup->name : ''],
'logged_user_ip' => '0',
]
];
$newUserGroup->save();
// 3. OPEN PROFILE PAGE BLOCK START
$response = $this->actingAs($newUser)
->withSession($newUserSessionData)
->get('/profile/view');
// 3. OPEN PROFILE PAGE BLOCK END
// 4. MAKING CHECKING PROFILE FOR USER CREATED AT 2) BLOCK START
$response->assertStatus(200); // to use HTTP_RESPONSE_OK
$response->assertSee(htmlspecialchars("Profile : " . $newUser->username, ENT_QUOTES));
// 4. MAKING CHECKING PROFILE FOR USER CREATED AT 2) BLOCK END
// 5. OPEN PROFILE DETAILS VIEW PAGE BLOCK START
$response = $this->actingAs($newUser)
->withSession($newUserSessionData)
->get('profile/view');
$response->assertStatus(200);
$response->assertSee(htmlspecialchars("Profile : " . $newUser->username, ENT_QUOTES));
// 5. OPEN PROFILE DETAILS VIEW PAGE BLOCK END
// 6. OPEN PROFILE DETAILS EDITOR PAGE BLOCK START
$response = $this->actingAs($newUser)
->withSession($newUserSessionData)
->get('profile/edit-details'); // http://local-votes.com/profile/edit-details
$response->assertStatus(HTTP_RESPONSE_OK);
$response->assertSee(htmlspecialchars("Profile : Details"));
// 6. OPEN PROFILE DETAILS EDITOR PAGE BLOCK END
// 7. MODIFY PROFILE DETAILS PAGE BLOCK START
$response = $this->actingAs($newUser)
->withSession($newUserSessionData)
->post('profile/edit-details-post', [
'first_name' => 'Modified : ' . $newUser->first_name,
'last_name' => 'Modified : ' . $newUser->last_name,
'phone' => 'Modified : ' . $newUser->phone,
'website' => 'Modified : ' . $newUser->website,
'_token' => $csrf_token
]);
// $response->assertStatus(205); // ???
// 7. MODIFY PROFILE DETAILS PAGE BLOCK END
////////////////////////
// 8. OPEN PROFILE DETAILS VIEW PAGE AFTER MODIFICATIONS BLOCK START
$response = $this->actingAs($newUser)
->withSession($newUserSessionData)
->get('profile/view');
$response->assertStatus(200);
$response->assertSee( htmlspecialchars('Modified : ' . $newUser->last_name) );
// 8. OPEN PROFILE DETAILS VIEW PAGE AFTER MODIFICATIONS BLOCK END
New user is added and Profile View page is opened ok, but there are problems at step // 7. MODIFY PROFILE DETAILS PAGE BLOCK START
as I see in sql trace new user is inserted but not updated.
In my control app/Http/Controllers/ProfileController.php update method is defined with validation request :
public function update_details(ProfileUserDetailsRequest $request)
{
$userProfile = Auth::user();
$requestData = $request->all();
$userProfile->first_name = $requestData['first_name'];
$userProfile->last_name = $requestData['last_name'];
$userProfile->phone = $requestData['phone'];
$userProfile->website = $requestData['website'];
$userProfile->updated_at = now();
$userProfile->save();
$this->setFlashMessage('Profile updated successfully !', 'success', 'Profile');
return Redirect::route('profile-view');
} // public function update_details(ProfileUserDetailsRequest $request)
1) Can the reason be in ProfileUserDetailsRequest and how to deal this ?
My profile details editor works ok.
My route defined as :
Route::group(array('prefix' => 'profile', 'middleware' => ['auth', 'isVerified']), function(){
Route::post('edit-details-post', array(
'as' => 'profile-edit-details-post',
'uses' => 'ProfileController#update_details'
));
At first I tried PUT, but after that I tried POST - the same without results.
2) Can you advice some proper way of User Profile details checking on // 7. MODIFY PROFILE DETAILS PAGE BLOCK START step ?
MODIFIED BLOCK # 2 :
I tried patch method, but it does not work anyway.
I have debugging method in my app/Http/Controllers/ProfileController.php
public function update_details(ProfileUserDetailsRequest $request)
{
method and when test is run I see that it is not triggered.
I use the same update_details method updating my form in brauser and it work ok(I see debugging info too).
I suppose that could be csrf issue and in header of my test file I wrote:
<?php
namespace Tests\Feature;
use Tests\TestCase;
use DB;
use Illuminate\Foundation\Testing\WithFaker;
use Illuminate\Foundation\Testing\WithoutMiddleware; // Prevent all middleware from being executed for this test class.
public function testProfilePage()
{
$csrf_token = csrf_token();
...
$response = $this->actingAs($newUser)
->withSession($newUserSessionData)
->patch('profile/edit-details-post', [
'first_name' => 'Modified : ' . $newUser->first_name,
'last_name' => 'Modified : ' . $newUser->last_name,
'phone' => 'Modified : ' . $newUser->phone,
'website' => 'Modified : ' . $newUser->website,
// '_token' => $csrf_token / I TRIED TO UNCOMMENT THIS LINE TOO
]);
$response->assertStatus(205); // making this check I see that code 419 was returned
Can decision be to add in file app/Http/Middleware/VerifyCsrfToken.php
class VerifyCsrfToken extends Middleware
{
/**
* The URIs that should be excluded from CSRF verification.
*
* #var array
*/
protected $except = [
// ???
];
If in command line I run tests as
vendor/bin/phpunit tests/Feature/ProfilepageTest.php
What have I to add in except?
Thanks!
Other than the PATCH instead of POST following are my recommendations to test your code.
Instead of ProfileUserDetailsRequest try to use simple Request.
Try to log $request variable in your update function and check if the _token and _method are available, method should be PATCH and the request variables are posted correctly.
Make a model of user table and try to update the user with that model using
$user = User::find(Auth::user()->id);
…
$user->save();
Sessions are initiated with a middleware, i think if you are disabling the middleware, session will not work.
If you are using an IDE like Visual Studio Code to run your code then try using a debugger like xdebug. Place a breakpoint in your update_details method and see if it is getting to save and save is executing. If it is then we know save is not updating correctly.
It looks like what you are doing is correct according to the documentation for update:
https://laravel.com/docs/5.7/eloquent#updates example from docs below:
$flight = App\Flight::find(1);
$flight->name = 'New Flight Name';
$flight->save();
Off the top of my head my only guess as to why it would fail is maybe getting the user off of the auth object causes problems. Take the id from the user in the auth object and use find() to grab the user then update its values and save and see if that works.

backdrop cms module-created blocks deltas not working

(tagging with drupal7 because i cannot create a tag for backdrop)
i have a fresh backdrop install, with a new theme duped from bartik, and a new layout duped from moscone_flipped. no code changes to those yet.
i have a module that creates 2 simple blocks, mostly just some html. i have implemented hook_block_info() and hook_block_view(). i can place blocks into regions in the layout using the admin ui. i can see each block on the front end page when i place either one of them. but not both. when i have both blocks placed in the layout, for some reason both regions display the output from the same block. and i have verified that it is always the first block defined in the array returned from hook_block_info(). i have cleared caches, checked code, etc.
has anyone seen this before?
btw, i just applied the recent security upgrade, and the behavior is the same both before and after the upgrade.
i will paste in the module code below, in case i have missed something.
thanks for any help anyone can provide.
// implements hook_block_info()
function mbr_block_info()
{
$info = array();
$info['rate-tables'] = array(
'info' => 'Rate Tables (Buttons)',
'description' => 'The displays the rate table links for the sidebar',
);
$info['mbr-footer'] = array(
'info' => 'MBR Footer',
'description' => 'Displays footer links, disclaimer, copyright',
);
return($info);
}
// implements hook_block_view()
function mbr_block_view($delta = '', $settings = array(), $contexts = array())
{
$block = array();
switch($delta)
{
case 'mbr-footer':
$subject = null;
$mbrFooter = getMBRFooterBlock();
$block = array('subject' => $subject, 'content' => $mbrFooter);
case 'rate-tables':
$subject = null;
$rateTables = getRateTablesBlock();
$block = array('subject' => $subject, 'content' => $rateTables);
}
return($block);
}

BOX-API upload file form

i'm trying to upload a file in BOX_API with php and Zend framework. But i'm missing somethin. It's the first time that i use an interface like this o i've read the manual. But it is confusig for me. My question are two:
-First, why do you have to pass to the post call only the name of the file and not the whole file with the right header for file upload? File upload in a form is not like passing the name of the file through a post call;
-secondly and consequently, do i have to make a form for file upload or simply a textarea where to write the name of the file to be passed to the BOX-API?
UPDATE:
This is the code of my upload form:
$form = new Zend_Form;
$form->setAction('/imball-reagens/public/upload')
->setMethod('post');
$file = new Zend_Form_Element_File('file');
$file->setLabel('Choose a file to upload:');
$file->addValidator('alnum');
$file->setRequired(true);
$form->addElement($file);
$access_token = new Zend_Form_Element_Hidden(array('name' => 'access_token', 'value' => $result->access_token));
$form->addElement($access_token);
$refresh_token = new Zend_Form_Element_Hidden(array('name' => 'refresh_token', 'value' => $result->refresh_token));
$form->addElement($refresh_token);
$form->addElement('submit', 'upload', array('label' => 'Upload File'));
echo $form;
And this s the POST cal to the box API which comes after the form:
$access_token= $this->getRequest()->getParam('access_token');
$client = new Zend_Http_Client('https://upload.box.com/api/2.0/files/content');
$client->setMethod(Zend_Http_Client::POST);
$client->setHeaders('Authorization: Bearer '.$access_token);
$data = $_FILES["file"]["name"];
$client->setParameterPost(array(
'filename' => '#'.$data,
'parent_id' => '0'
));
$response = $client->request()->getBody();
$this->view->response= $response;
$result = json_decode($response);
The error it throws is below:
{"type":"error","status":400,"code":"invalid_request_parameters","help_url":"http:\/\/developers.box.com\/docs\/#errors","message":"Invalid input parameters in request","request_id":"172518183652dcf2a16af73"}
Tricky to debug without seeing all of the code, but in the bit you pasted it looks like you are passing $_FILES["file"]["name"] to the API - this only contains the original name of the file which was uploaded by the user - you need to pass the location to the file on the server which is sending the data to the Box API client so that it can grab it and send it to the Box server - this should be stored in $_FILES["file"]["tmp_name"].
I would recommend changing the code to this and trying again:
$access_token= $this->getRequest()->getParam('access_token');
$client = new Zend_Http_Client('https://upload.box.com/api/2.0/files/content');
$client->setMethod(Zend_Http_Client::POST);
$client->setHeaders('Authorization: Bearer '.$access_token);
$data = $_FILES["file"]["tmp_name"];
$client->setParameterPost(array(
'parent_id' => '0'
));
$client->setFileUpload($data, 'filename');
$response = $client->request()->getBody();
$this->view->response= $response;
$result = json_decode($response);

Rendering links in tweet when using Get Statuses API 1.1

I'm using the Twitter API 1.1 Get statuses method to return the latest tweet from an account on the client's website. This is working fine but I can't find any clear documentation on how to render any links that may be included (Both included usernames and included links) as clickable links?
I can see in the JSON response that any included links are in the XML but it's not clear to me how to go about adding clickable links into the rendered output. The documentation around the new API seems to be lacking practical examples.
Can anyone advise?
The code I'm using the pull out the latest tweet is as follows:
$token = 'TOKEN HERE';
$token_secret = 'TOKEN SECRET HERE';
$consumer_key = 'CONSUMER KEY HERE';
$consumer_secret = 'CONSUMER SECRET HERE';
$host = 'api.twitter.com';
$method = 'GET';
$path = '/1.1/statuses/user_timeline.json'; // api call path
$query = array( // query parameters
'screen_name' => 'SCREEN NAME HERE',
'count' => '1'
);
$oauth = array(
'oauth_consumer_key' => $consumer_key,
'oauth_token' => $token,
'oauth_nonce' => (string)mt_rand(), // a stronger nonce is recommended
'oauth_timestamp' => time(),
'oauth_signature_method' => 'HMAC-SHA1',
'oauth_version' => '1.0'
);
$oauth = array_map("rawurlencode", $oauth); // must be encoded before sorting
$query = array_map("rawurlencode", $query);
$arr = array_merge($oauth, $query); // combine the values THEN sort
asort($arr); // secondary sort (value)
ksort($arr); // primary sort (key)
// http_build_query automatically encodes, but our parameters
// are already encoded, and must be by this point, so we undo
// the encoding step
$querystring = urldecode(http_build_query($arr, '', '&'));
$url = "https://$host$path";
// mash everything together for the text to hash
$base_string = $method."&".rawurlencode($url)."&".rawurlencode($querystring);
// same with the key
$key = rawurlencode($consumer_secret)."&".rawurlencode($token_secret);
// generate the hash
$signature = rawurlencode(base64_encode(hash_hmac('sha1', $base_string, $key, true)));
// this time we're using a normal GET query, and we're only encoding the query params
// (without the oauth params)
$url .= "?".http_build_query($query);
$oauth['oauth_signature'] = $signature; // don't want to abandon all that work!
ksort($oauth); // probably not necessary, but twitter's demo does it
// also not necessary, but twitter's demo does this too
function add_quotes($str) { return '"'.$str.'"'; }
$oauth = array_map("add_quotes", $oauth);
// this is the full value of the Authorization line
$auth = "OAuth " . urldecode(http_build_query($oauth, '', ', '));
// if you're doing post, you need to skip the GET building above
// and instead supply query parameters to CURLOPT_POSTFIELDS
$options = array( CURLOPT_HTTPHEADER => array("Authorization: $auth"),
//CURLOPT_POSTFIELDS => $postfields,
CURLOPT_HEADER => false,
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_SSL_VERIFYPEER => false);
// do our business
$feed = curl_init();
curl_setopt_array($feed, $options);
$json = curl_exec($feed);
curl_close($feed);
$twitter_data = json_decode($json);
Thanks a lot for your response. I actually found a solution thanks to this blog post from the guys at Asheville - http://www.appliedtns.com/blog/tag/twitter/
It works fine for me.
// Parse any links found in our tweet
$formatted_text = preg_replace('/(\b(www\.|http\:\/\/)\S+\b)/', "<a target='_blank' href='$1'>$1</a>", $post->text);
$formatted_text = preg_replace('/\#(\w+)/', "<a target='_blank' href='http://search.twitter.com/search?q=$1'>#$1</a>", $formatted_text);
$formatted_text = preg_replace('/\#(\w+)/', "<a target='_blank' href='http://twitter.com/$1'>#$1</a>", $formatted_text);
Not sure if this exactly what you need but I am using the tmhOAuth library for my application, see https://github.com/themattharris/tmhOAuth-examples. Using code from Matt Harris' examples I loop through the response and build the output as in the code below. The links in the tweets are created by the library function entify_with_options($tweet).
// Decode response
$timeline = json_decode($this->tmhOAuth->response['response'], true);
if(!$timeline){
throw new Exception('Error: No response was found.');
}
else{
// Start building the output
foreach ($timeline as $tweet) :
... start of response processing
// Format and set tweet text
$tw_entified_tweet = tmhUtilities::entify_with_options($tweet);
// Format and set creation date for permalink
$tw_created_at_formatted = is_twitterlist_format_date($tweet['created_at']);
// Format and set permalink
$tw_permalink = str_replace(
array(
'%screen_name%',
'%id%',
'%created_at%'
),
array(
$tweet['user']['screen_name'],
$tweet['id_str'],
$tw_created_at_formatted,
),
'%created_at%'
);
... end response processing
endforeach;
}
The date format function is:
function is_twitterlist_format_date($created_date)
{
if ( is_null($created_date)) {
return '';
}
else{
// Format: March 4th, 9:19 am
return date('F jS, g:i a', strtotime($created_date));
}
}
Hope this is useful.

Yii: in a view _view, ho to add a related grid [sortable and searchable]?

I've Client [1->N] Delivery
In _view of Client I want Delivery related to my client
This is in my ClientController
public function actionView($id)
{
$client = $this->loadModel($id);
$delivery_provider = new CActiveDataProvider(
'Delivery',
array (
'criteria' => array (
'condition' => 'client_id = :c_id',
'params' => array (':c_id' => $client->id),
), // fine dei criteri
) // fine array di definizione cactiveprovider
); // fine del CActive provider
$this->render('view',array(
'model'=> $client,
'delivery_provider' => $delivery_provider,
));
}
Then modules/admin/views/client/_view.php I add my CGridView. ... but ... it's not searchable and not sortable (but pagination works...)
How to proceed ?
Since Delivery is a model it's better to use the CActiveRecord::search() instead. This method is automatically generated for you if you used Gii.
For searching you have to capture the results of the search form / filters using $this->setAttributes($_GET['Delivery']); assuming your inputs have names of the form Delivery[attribute_name].
public function actionView($id){
$client = $this->loadModel($id);
$delivery = new Delivery('search');
if(isset($_GET['Delivery']))
$delivery->setAttributes($_GET['Delivery']);
$delivery->client_id=$id;
$this->render('view',array(
'model'=> $client,
'delivery_provider' => $delivery->search(),
));
}