How do I post a photo to a Facebook event? - facebook-php-sdk

I've been using the PHP SDK to try and post photos to a test user's event on Facebook:
$c = 0;
$facebook->setFileUploadSupport(true);
while ($c < count($_FILES['file']['name'])) {
aFile = $_FILES['file'];
//..other code for ui and validity checks..//
$real = realpath($aFile["tmp_name"][$c]);
$attachment = array('message' => 'Test upload');
$attachment['image'] = '#'.$real; //also tried other keys. Please see below...
try {
$result = $facebook->api('/'.$event_id.'/photos?access_token='.$access_token, 'post', $attachment);
}
catch (FacebookApiException $e) {
echo('Could not post image to Facebook:'.var_export($e));
}
$c++;
}
Unfortunately, all I get is errors:
'message' => 'An unexpected error has occurred. Please retry your request later.', 'type' => 'OAuthException', 'code' => 2
I've been trying for two days and this is the only upload code I have so I'm hardly overloading Facebook with (in my case) attempting to send a 500x500 jpg file every so often.
If I alter the ../photos?access_token=… to ../feed?access_token=.. then the text gets posted but not the image.
I've also tried handling the image with:
$attachment[basename($real)] = '#'.$real;
and
$attachment['source'] = '#'.$real;
and
$attachment['picture'] = '#'.$real;
All with no success for local files. But the last key option on $attachment will allow a post with a URL to a picture on a (random) website.
Could someone tell me whether I'm 'post'ing to the correct open graph edge or if there's a mistake in my code for handling the image's data?

I think the problem was a mixture of test user accounts being corrupted or old and of my file path's.
I deleted the test user and completely recreated new ones and that along with a dummy file placed in a place I knew I could get to worked with the facebook command:
$photo_return = $facebook->api($event_id.'/photos', 'POST', array('source' => '#dummy_file.jpg', 'message' => 'Photo post'));

Related

Populate Image & Image List Fields with Bolt CMS Programatically

I am attempting to create entries programaticaly in Bolt 4. I have managed to create basic text entries fine which covers the majority of fields I need to fill in however unsure how to go about image and image list types.
$content->setFieldValue('name', 'Test Name');
Works fine for most fields as stated but images field types looks like below in database and am unsure what the "Bolt / Symfony / Doctrine" way of generating below is:
{"media":11,"filename":"entity\/year\/month\/image.jpg","alt":"","0":""}
Which looks like some JSON formatted to contain a media ID, file path and an alt attribute. I'm guessing image lists are similar but with multiple of above but hoping there is a function I can use to generate this output as unsure how I would grab media ID etc.
Am assuming I may need to upload a file temporarily from an external URL and provide this to some function however cannot find any examples. Any help would be much appreciated.
Not quite sure on this but will answer anyway as it works and may help someone else, but would be good to clear some of the bits up.
Example of an Image:
//not sure what goes in media here but blank seemed to not work here but a 7 did as looked at example in database however worried this is wrong and should be an Id
$image = array('media' => '7', 'filename' => $filename, 'alt' => $image['Alt'], '0' => '');
$imageJSON = json_encode($image);
$content->setFieldValue('image', $image);
Example of an Image List:
$images = array();
foreach ($images as $image) {
$ImageId = $image['id'];
//images may be already on system but I had to download them here
if ($fileName = $this->downloadImage($propertyFile->{'url'}->__toString(), $ImageId)) {
//not sure what goes in media here but seemed to work blank for imagelist type and unsure what the 0 is on the end either
$image[] = array('media' => '', 'filename' => $filename, 'alt' => $image['Alt'], '0' => ''); }
}
$imageJSON = json_encode($image);
$content->setFieldValue('gallery', $image);

Laravel 5.2 PHPunit form submit create new record

I have the following code.
Login user and validation works, returns true.
The problem is seePageIs, it returns an error. But after posting the respons has to go to company lister page. So if i change the ->seePageIs('admin/company') to ->seePageIs('admin/company/create') it works.
What's wrong?
Error:
Failed asserting that two strings are equal.
Expected :'http://localhost/admin/company'
Actual :'http://localhost/admin/company/create'
Test:
public function testExample()
{
$this->be(User::find(4));
$rules = array(
'companyname' => 'required',
'email' => 'required|email',
);
$data = [
'companyname' => 'aa',
'email' => 'aaaa#aa.nl']
;
$v = $this->app['validator']->make( $data, $rules);
$this->visit('admin/company/create')
->press('Create')
->assertTrue($v->passes())
->seePageIs('admin/company');
}
I'm not sure I understand your code very well and your question, but maybe it is this line:
$this->visit('admin/company/create')
->press('Create')
->assertTrue($v->passes())
->seePageIs('admin/company/create');
I just changed the line seePageIs() to add create at the end. Maybe that is the problem?
Try it.
If not, can you show your code for assertEquals(...)... Thanks!
EDIT #2
Hi there Bas; based on your comments and what I think you are trying to do:
If after creating the company, then you want to check it, then you probably need to do this instead:
$this->visit('admin/company')
->seePageIs('admin/company');
Which just goes to the company page an verifies that is the correct page.

Symfony2 get validation message

I have a few validation messages in the file validators.en.yml:
soporte.nombre.not_blank: The name cannot be empty
soporte.price.is_integer: The price should be integer
soporte.not_repeat: soporte cannot be repeat
I valid making a query to the database:
$validarPorNombreAndTipo = $this->crud->findOneBy(
$soporte, array('nombre' => $soporte->getNombre(),
'tipo' => $object->tipo
)
);
if ($validarPorNombreAndTipo){
$error= //here i need to get soporte.not_repeat that is on file validators.en.yml;
}
excuse me for my bad English, I used the translator.
If you just want to get the translation, this code should work :
$validarPorNombreAndTipo = $this->crud->findOneBy(
$soporte, array('nombre' => $soporte->getNombre(),
'tipo' => $object->tipo
)
);
if ($validarPorNombreAndTipo){
$error= $this->get('translator')->trans('soporte.not_repeat', array(), 'validators');
// Third param is the translation domain (first part of the translation file)
}
However, i suggest you to read this : http://symfony.com/doc/2.3/cookbook/validation/custom_constraint.html
If you want to follow Symfony best practices, you should create a custom constraint class/Validator and move your validation logic into it.
Best regards

Programmatically insert a file in Drupal's file system?

I am grabbing a file using CURL, and want to save it locally, but so it plugs into Drupal's file system. Would I have to do a manual insert or is there a better way? The closest I could find was:
function image_field_insert($entity_type, $entity, $field, $instance, $langcode, &$items);
But I don't know how to use this. Any better suggestions?
There are a couple of ways but the easiest would be to use file_save_upload():
$source = '/path/to/file.ext';
$dest = 'public://uploads/'; // Or whatever
$file = file_save_upload($source, array(), $dest, FILE_EXISTS_RENAME);
if ($file) {
// file_save_upload marks the file as temporary, we need to mark as permanent or it will be cleared from the database in a few hours
$file->status = FILE_STATUS_PERMANENT;
file_save($file);
}
This just about drove me out of my mind and in trying to figure out a simple way to circumvent the form API expectations I came across this question. Clive's answer was the beginning of how I figured it out because it looks like you can provide a source as the first arg for file_save_upload() but as it turns out, you cannot. At least not in the current drupal 7.30.
I checked the link he provided and deconstructed the function. Basically, even if you pass a full source path it still looks for the first arg in the $_FILES array way down the path. Who knows why, but what a dumb idea. So I did this:
//after verifying all post fields are set...
$node = new stdClass();
$node->type = 'user_files';
node_object_prepare($node);
$node->title = $_POST['title'];
$node->author = 2; //hardcoded because only admin uses this form
$node->uid = 2;
$node->language = LANGUAGE_NONE;
$node->body[$node->language][0]['value'] = $_POST['body'];
$node->body[$node->language][0]['summary'] = text_summary($_POST['body']);
$node->body[$node->language][0]['format'] = 'filtered_html';
$node->field_first_name[$node->language][0]['value'] = $_POST['first_name'];
$node->field_last_name[$node->language][0]['value'] = $_POST['last_name'];
node_save($node);
if(isset($_FILES['file']['tmp_name']) && $_FILES['file']['name'] != '')
{
//upload file
$file = new stdClass();
$file->uid = 2;
$file->status = 0;
$file->filename = trim(drupal_basename($_FILES['file']['name']), '.');
$file->uri = $_FILES['file']['name'];
$file->filemime = file_get_mimetype($file->filename);
$file->filesize = $_FILES['file']['size'];
$file->filename = file_munge_filename($file->filename, 'jpg jpeg gif png doc docx pdf');
$file->destination = file_destination('private://' . $file->filename, FILE_EXISTS_RENAME);
$file->uri = $file->destination;
if (!drupal_move_uploaded_file($_FILES['file']['tmp_name'], $file->uri)) {
return false; //set errors or do whatever you want on error
}
else
{
drupal_chmod($file->uri);
$existing_files = file_load_multiple(array(), array('uri' => $file->uri));
if (count($existing_files)) {
$existing = reset($existing_files);
$file->fid = $existing->fid;
}
if ($file = file_save($file)) {
// Add file to the cache.
$node->field_file[$node->language][0]['uri'] = 'private://'.$file->filename;
$node->field_file[$node->language][0]['fid'] = $file->fid;
$node->field_file[$node->language][0]['display'] = 1;
$node->field_file[$node->language][0]['description'] = 'User uploaded file';
node_save($node);
//do we notify someone?
}
}
}
What this does is creates a node of a specified type. In this case, user_files, then if a file is uploaded, add the file to the media table, or file table or whatever it's called. Then adds the association to the newly created node.
Is it ugly? Yeah. Why didn't I use the internal drupal form API? Shut up, that's why. We don't always have a choice, so when a client asks for a basic form that just emails them, it is quick and easy to make a simple field that sends through a mail chimp API or something. Then later they add that they want files. Then later they want it to add to the drupal back end and suddenly you have a snowball of horror that would have been a lot easier if it was done with the form API to begin with. I don't know who started it, but I had to end it and I did it with this ghastly hack. Hope it helps you or whoever else is currently spinning in the drupal vortex of horror.
I was able to do the following to add a file from the public:// Drupal folder (typically sites/default/files or sites/DOMAIN/files) using file_save.
$uri = 'public://filename.pdf';
$file = file_save((object) array(
'filename' => basename($uri),
'uri' => $uri,
'status' => FILE_STATUS_PERMANENT,
'filemime' => file_get_mimetype($uri),
));
This adds the appropriate entry into the Drupal core file_managed table. If you have the File Entity module installed, an entry is created there as well.
The returned file object will include the database ID as $file->fid.

Preventing form_token from rendering in Drupal "GET" forms

Drupal inserts a form_token as a hidden field when it renders forms. The form_token is then checked on form submission to prevent cross-site request forgery attacks. The form data that is submitted is guaranteed to have come from the original form rendered by Drupal.
However, forms using the "GET" method shouldn't need this token. All it does is lengthen and uglify the resulting URL.
Is there any way of suppressing it?
Yes, there is a way, but use it consciously (see warning below):
If you create the form yourself, adding
$form['#token'] = FALSE;
to the form definition array should prevent a token from being generated in the first place.
If you are dealing with an existing form, you can bypass the token validation process by unsetting the '#token' element on hook_form_alter:
// Example for removal of token validation from login (NOTE: BAD IDEA!)
function yourmodule_form_alter(&$form, &$form_state, $form_id) {
if ($form_id == 'user_login_block') {
unset($form['#token']);
}
}
Warning: Given your question, I think there is a slight misconception concerning the difference (better, the lack of a difference) between GET and POST requests.
... on forms using the "GET" method
shouldn't need this token. All it does
is lengthen and uglify the resulting
URL.
This is wrong! GET and POST are just two different, but mostly equivalent methods of transmitting data from the client to the server. Since POST is better suited to transfer large amounts of data (or difficult formatted data), it is the established standard for submitting forms, but it is in no way safer/unsafer or more/less secure than GET requests. Both type of requests can be tampered with by malicious users in the same ways, hence both types should use the same protection mechanisms.
With a GET request, the token does exactly the same as with a POST request - it proves to the server that the submitted data comes from the same Browser on the same machine as the request he build the form for! So you should only remove it if you are sure that the request can not be misused via XSRF.
This worked for me. I had to unset all the form api elements and set the #token property to false. Notice the after_build function for unsetting the other properties.
function mymodule_form(&$form_state){
$form['name'] = array(
'#type' => 'textfield',
'#title' => 'name',
'#value' => 'name',
);
$form['#method'] = 'get';
$form['#action'] = url('someurl');
$form['submit'] = array('#type' => 'submit', '#value' => 'go');
$form['#token'] = false;
$form['#after_build'] = array('mymodule_unset_default_form_elements');
return $form;
}
function mymodule_unset_default_form_elements($form){
unset($form['#build_id'], $form['form_build_id'], $form['form_id']);
return $form;
}
The site I work on uses the Drupal 6 form API for custom search forms, so by removing the token and build id we were able to cache the results in memcache. Now we've moved to Acquia hosting, it's cached using Varnish.
To remove the form_token and form_build_id from your form and submit it as a GET request, use the following method:
<?php
function module_example_form($form_state, $form_id = NULL) {
// Form root settings.
$form = array();
// Set the submission callback for this form.
$form['#submit'][] = __FUNCTION__ . '_submit';
// Set the request method for this form to GET instead of the default
// of POST.
$form['#method'] = 'get';
// Remove unique form token so request can be cached. This is accompanied by
// code in hook_form_alter to ignore the token and remove the build_id.
$form['#token'] = FALSE;
// Submit button.
$form['go'] = array(
'#type' => 'submit',
'#value' => t('Go!'),
);
return $form;
}
/**
* Implements hook_form_alter().
*/
function module_form_alter(&$form, $form_state, $form_id) {
// Changes to the 'module_example_form' form.
if ($form_id == 'module_example_form') {
// Unset the hidden token field and form_build_id field.
unset($form['#token'], $form['form_build_id'], $form['#build_id']);
}
}
?>
I find that simply throwing away the CSRF token is not an option. We solved it using hook_theme_registry_alter() to overwrite the Drupal core theme_hidden() function so that the hidden form element 'form_token' is rendered as an <esi /> tag. The tag will cause Varnish to make a call to a PHP file which we allow to pass through the cache. This file will calculate the proper form token for the current user and will then output the HTML code for the hidden field. You can calculate this token without a Drupal bootstrap, but you will need a single DB query to fetch the *drupal_private_key* for your site, which is stored in the variable table.

Resources