Upload a File to a Task on Cerb API - php

At the moment I am adding a task to Cerb using the API (Docs: https://cerb.ai/docs/api/). I can add and edit comments on the task.
An extension is to add file uploads. The API documentation seems to be extremely limited in terms of file uploads and isn't helping much. I have based my current code on https://cerb.ai/docs/api/endpoints/records/#update, using a custom field linked to the task and posting directly to the API.
The process is as follows:
1 - User inserts information on a Laravel form
2 - Form is submitted, task is created
3 - If the user entered a comment or description, the controller then uses the ID from the newly created task toa dd the comment
4 - If the user adds a file, the controller should then use the same ID from the task to attach the file
Task and comment adding has been done through models, which were previously done directly through the API and reformatted once they worked.
Add Task:
$params = [
'title' => $request->input('title'),
'links' => ['project:'.$request->input('project')],
'priority' => $request->input('priority'),
'status_id' => 2
];
$due_date = $request->input('due');
if(!empty($due_date)){
$params['due'] = strtotime($due_date);
}
$success_task = $task->addUpdateRecord('tasks.add', $params);
Add Comment:
$params = [
'author__context' => 'bot',
'author_id' => 3,
'comment' => empty($comment_input) ? request()->comment : $comment_input,
'target__context' => 'task',
'target_id' => $task_id,
'author' => Auth::user()->name // custom field
];
$success = $comment->addUpdateRecord('comments.add', $params);
The addUpdateRecord() essentially does the same thing as the file upload as below:
$cerb = new CerbApi();
$out = $cerb->put($root.'records/file_bundle/1054.json?expand=custom_', $putfields);
$putfields contains a normal file upload object.
I'm getting the following error on request:
array:2 [▼
"__status" => "error"
"message" => "Unknown command (root/records/task/1054.json)"
]
(root removed for confidentiality)
Has anyone worked with this? Anyone able to assist?

Ended up getting it right by using the attachments record type https://cerb.ai/docs/records/types/attachment/ and creating a model for it with its own addUpdateRecord() method.
I then used the records API to do the post for the file as the first step instead of the last one:
$file_upload = new File;
// Get file contents
$data = file_get_contents($file);
// Base64 encode contents
$data = base64_encode($data);
$file_params = [
'name' => $file->getClientOriginalName(),
'content' => $data
];
// Do upload
$file_success = $file_upload->addUpdateRecord('files.add', $file_params);
// Only need to return the file ID to link it to the task
return $file_success["data"]["id"];
And then using the ID that was being returned to link it to the task:
$task = new Task();
$params = [
'title' => $request->input('title'),
'links' => [
'project:'.$request->input('project'),
'file:'.$file_id
],
'priority' => $request->input('priority'),
'status_id' => 2
];
I hope this helps someone in the future.
Note: Cerb API Version 9.2.0

Related

Docusign PHP SDK - Fill Template pre-defined data label value

UPDATE:
As it turns out, I need to enable this setting for data to show up, and using tabs is the correct thing to do.
When an envelope is sent, write the initial value of the field for all recipients
=================================================================
Not sure why this one is not mentioned in API properly ... but how does one go about filling template's custom data label with template?
So, I create a template like this:
$envelope_definition = new EnvelopeDefinition([
'status' => 'sent',
'template_id' => $args['template_id'],
]);
then I create a signer:
$signer = new TemplateRole([
'email' => $args['signer_email'],
'name' => $args['signer_name'],
'role_name' => 'signer',
]);
Here is where the disconnect happened, where do I add a pre-defined template value? I tried two things so far:
1. Add tabs to $signer like so, but by doing so, it ereases all field value in the final document,
new Tabs([
"text_tabs" => [
new Text([
"tab_label" => "price",
"value" => "123456789",
]),
],
]),
Call $envelope_definition->setCustomFields() , like this :
$envelope_definition->setCustomFields(new CustomFields([
'text_custom_fields' => [
'price' => new Text([
'tab_label' => 'price',
'custom_tab_id' => 'price',
'value' => '123456789',
]),
],
]));
It will throw me a C# error, which I don't understand at all:
Error while requesting server, received a non successful HTTP code [400] with response Body: O:8:\"stdClass\":2:{s:9:\"errorCode\";s:20:\"INVALID_REQUEST_BODY\";s:7:\"message\";s:718:\"The request body is missing or improperly formatted. Cannot deserialize the current JSON object (e.g. {\"name\":\"value\"}) into type 'System.Collections.Generic.List`1[API_REST.Models.v2_1.textCustomField]' because the type requires a JSON array (e.g. [1,2,3]) to deserialize correctly.\r\nTo fix this error either change the JSON to a JSON array (e.g. [1,2,3]) or change the deserialized type so that it is a normal .NET type (e.g. not a primitive type like integer, not a collection type like an array or List<T>) that can be deserialized from a JSON object. JsonObjectAttribute can also be added to the type to force it to deserialize from a JSON object.\r\nPath 'customFields.textCustomFields.price', line 1, position 45.\";}"
API docs seems to be focusing on creating template and values adhoc ... anyone have something that works? Thanks a lot!
You can find valid PHP example here which shows how to prepopulate template tab values while creating an envelope.
You didn't follow the example correctly.
You didn't create a \DocuSign\eSign\Model\TextCustomField object.
Here it is from Amit's link:
# Create an envelope custom field to save the our application's
# data about the envelope
$custom_field = new \DocuSign\eSign\Model\TextCustomField([
'name' => 'app metadata item',
'required' => 'false',
'show' => 'true', # Yes, include in the CoC
'value' => '1234567']);
$custom_fields = new \DocuSign\eSign\Model\CustomFields([
'text_custom_fields' => [$custom_field]]);
$envelope_definition->setCustomFields($custom_fields);

Cake PHP 3.5 multiple pagination

I have a media library where i load images and videos, both should have a pagination. So far it works and i can load e.g. the page 2,3... with ajax and replace the content for each entity.
Now the problem is the first page + the first- and prev- buttons if they point to the first page from each entity. Why?
The created links look like :
working one : http://www.xyz.de/media?image[page]=2
not working : http://www.xyz.de/.media
This occurs in the image- and video-paginaton for the first page.
What I am doing wrong? I read the Paginator-Component and the Paginator-Helper Doku 100 times and can firure it out.
I have my MediaController where I set up my paginator as follows :
public $paginate = [
'Images' => ['limit' => 10, 'scope' => 'image'],
'Videos' => ['limit' => 8, 'scope' => 'video'],
'limit' => 12
];
than in my controller-action i set the values :
$images = $this->paginate($imageTable->find('all',$image_options), ['scope' => 'image']);
$videos = $this->paginate($videosTable->find('all', $video_options), ['scope' => 'video']);
$this->set(compact('images', 'videos'));
in my view I use the PaginatorHelper to set e.g. the numbers like so
$paginator->numbers(['model' => 'Images'])

CakePHP HABTM Save Single Header Record, linked against multiple existing children

I am using CakePHP 2.9.1, have 2 tables in a HABTM relationship, I want to save the header record and save many associations in one go, but the child record is already existing, I seem to be able to save the data using a hardcoded list but not using a variable, I'm sure it's something silly I'm doing, I'm running inside a plugin, the model I want to link too is in the main code.
The child record must not be altered because it's handled by the main system, I'm just extending it's functionality by linking to it in our plugin.
// Header Record
class templatedoc extends TemplateModuleAppModel
{
public $useTable = 'templatedocs';
public $hasAndBelongsToMany = [
'Servicetemplate' => [
'className' => 'Servicetemplate',
'joinTable' => 'templatedocs_to_servicetemplates',
'foreignKey' => 'templatedoc_id',
'associationForeignKey' => 'servicetemplate_id',
'unique' => true,
'dependent' => false, // We don't want to delete the Service Template by mistake!
]
];
}
Here is my save, this works:
$this->templatedoc->create ();
$data = [
'templatedoc' => [
'warning_adjust' => $prioritywarn,
'summary' => $summary,
],
'Servicetemplate' => [
1,2,3,10 // Dynamic data needed here!
]
];
$result = $this->templatedoc->SaveAll ($data);
But I can't have the "Servicetemplate" hardcoded, I've tried passing an array inside that array and removing it, imploding an array to make a comma separated list all I end up with is the header 'templatedoc' record being created but nothing in my link table
Only thing I can think is a difference is that the hardcoded list are int values, by data is coming from the database so will be an int inside a string, but I can't see that being an issue.
Any ideas what I'm doing wrong? Sorry if it's something completely stupid.
What I had to do in the end was store the items forcing an int, when associating in my foreach loop.
$items[] = (int) $id
Then this worked:
$this->templatedoc->create ();
$data = [
'templatedoc' => [
'warning_adjust' => $prioritywarn,
'summary' => $summary,
],
'Servicetemplate' => [
$items // Dynamic data needed here!
]
];
$result = $this->templatedoc->SaveAll ($data);
Seems very fussy it has to be type int, adding here in case someone else gets caught out, would love to know if there is a better solution.

CakePHP 2.0 LinkedIn Plugin - Post a share?

I am using this LinkedIn plugins for CakePHP 2.0.4: https://github.com/ProLoser/CakePHP-LinkedIn
I setup everything to work fine with login and get user's profiles like first name, last name,... using this syntax: $this->Linkedin->find("all",...). I renamed the default name,"Linkedin", of Linkedin datasource to "LinkedinSource", hence I can call my model "Linkedin" just for my convenience.
I followed the file /Plugin/Linkedin/Model/LinkedinMessage.php to create this function in my Linkedin model:
function updateStatus($message) {
$request = $this->request;
$request['uri']['path'] = 'people/~/shares';
$this->request = $request;
//Above 3 lines are used to bypass the error "changing value of overloaded object property has no effect if I use $this->request["uri"]["path"] = "..."
$data = array(
'comment' => $message
, 'visibility' => array('code' => 'anyone')
);
//Above settings follow this: https://developer.linkedin.com/documents/share-api
$saved = $this->save($data);
}
When I run above code using my own LinkedIn account (connected & authorized), the value of $saved is just TRUE but NO status/share is posted to my LinkedIn account when I open my account on a browser (Google Chrome)
I tried to change the URI Path to
$request['uri']['path'] = 'people/~/person-activities';
and request data to:
$data = array(
'content-type' => "linkedin-html"
, 'body' => $message
);
as in https://developer.linkedin.com/documents/post-network-update but still no better result.
I also change these lines in /Plugin/Linkedin/Config/LinkedinSource.php:
$config['Apis']['LinkedinSource']['write'] = array(
// http://developer.linkedin.com/docs/DOC-1044
'mailbox' => array(
'people/~/mailbox' => array(
'subject',
'body',
'recipients',
),
),
);
to
$config['Apis']['LinkedinSource']['write'] = array(
// http://developer.linkedin.com/docs/DOC-1044
'mailbox' => array(
'people/~/mailbox' => array(
'subject',
'body',
'recipients',
),
),
//https://developer.linkedin.com/documents/share-api
'shares' => array(
'people/~/shares' => array(
'comment',
'content',
'visibility',
),
),
);
but still no better result.
Just be noted I added these lines in /Plugin/Linkedin/Model/LinkedinMessage.php
public $startQuote;
public $endQuote;
to avoid these errors:
Undefined property: LinkedinSource::$startQuote [CORE/Cake/Model/Model.php, line 1269]
Undefined property: LinkedinSource::$endQuote [CORE/Cake/Model/Model.php, line 1269]
I dont know if that can cause my problem or not but I want to list all details here.
Please help as I spent almost one day to make this work but still can not :(
Found the problem! The Content-Type MUST be application/json in order to work! I thought x-li-format: json is enough but it is NOT.

Custom field in Drupal 7 with multiple file fields

I'm making a custom "video" field that is supposed to accept several files (for different video formats) and a caption. So far the schema is fine, but I can't get it to upload and store the actual files.
My code in hook_field_widget_form looks like this (only pasting relevant bits):
$element['mp4'] = array(
'#type' => 'file',
'#title' => 'MP4 file',
'#delta' => $delta,
);
$element['ogg'] = ... /* similar to the mp4 one */
$element['caption'] = array(
'#type' => 'textfield',
'#title' => 'Caption',
'#delta' => $delta,
);
Also, in my .install file:
function customvideofield_field_schema($field) {
return array(
'columns' => array(
'mp4' => array(
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
'default' => 0,
),
'ogg' => ... /* similar to mp4 */
'caption' => array(
'type' => 'varchar',
'length' => 255,
),
)
);
}
And the error I'm getting is when I try to store data. I get the form ok, and the database looks fine (the fields Drupal generates at least), but when it tries to do an INSERT, it fails because the value it tries to get into those integer fields is an empty string.
From what I understand, they have to be integers, right? (fids?) But I'm guessing the files are not being uploaded, even though I do get the right interface for uploading files.
Drupal shows you the INSERT query it tries to do, which is too long to post here, but I can see there that the value for the caption field (which is just a text field), is fine in the query, so it's only a problem with the file fields.
You probably want to use the managed_file field type instead, it handles uploading the file and registering it in the managed_files table for you. Then you would just add a submit function to your widget form and put the following code (from the FAPI page linked to above):
// Load the file via file.fid.
$file = file_load($form_state['values']['mp4']);
// Change status to permanent.
$file->status = FILE_STATUS_PERMANENT;
// Save.
file_save($file);
// Record that the module (in this example, user module) is using the file.
file_usage_add($file, 'customvideofield', 'customvideofield', $file->fid);
Hope that helps
EDIT
The core file module handles the actual submission for this using hook_field_presave(), my best guess is that this code would work:
function customvideofield_field_presave($entity_type, $entity, $field, $instance, $langcode, &$items) {
// Make sure that each file which will be saved with this object has a
// permanent status, so that it will not be removed when temporary files are
// cleaned up.
foreach ($items as $item) {
$file = file_load($item['mp4']);
if (!$file->status) {
$file->status = FILE_STATUS_PERMANENT;
file_save($file);
}
}
}
That assumes the file ID column for your field is the one called mp4.
Remember to clear Drupal's caches when you implement the new hook or it won't be registered.
I haven't tried file uploading in my Drupal modules yet, but can you check does your form tag have the attribute enctype
="multipart/form-data"?
I would expect Drupal ought to include this automatically, but without it the file fields won't work, which seems to be what you're experiencing.
James

Categories