I was looking through the swagger documentation trying to figure out how to add media to a region of a layout. I did not see a way to do that yet. Am I correct that this has just not been documented yet?
I went through the documentation again and it looks like a playlist needs to be created, then the playlist assigned to a region and the region assigned to a layout. If you take that one step farther, you can then assign the layout to a campaign.
Does that mean that Regions can be assigned to more than one Layout? Also, does that mean that a playlist can be assigned to multiple regions?
Update: Answered the question about assigning a region to multiple layouts, as seen in the swagger documentation under:
POST /playlist/library/assign/{playlistId}
It looks like it is possible
But I do not see a way to remove content from a playlist. Does this mean that when you want to make a change to a playlist that you will need to recreate the playlist/upload all the data for the playlist again?
Update 2: When trying to add media to a playlist I get:
( ! ) Fatal error: Uncaught exception 'GuzzleHttp\Exception\ClientException' with message 'Client error: 422' in C:\wamp\www\xibo_interface_c\3rdParty\oauth2-xibo-cms-master\vendor\guzzlehttp\guzzle\src\Middleware.php on line 69
( ! ) GuzzleHttp\Exception\ClientException: Client error: 422 in C:\wamp\www\xibo_interface_c\3rdParty\oauth2-xibo-cms-master\vendor\guzzlehttp\guzzle\src\Middleware.php on line 69
Call Stack
# Time Memory Function Location
1 0.0009 214152 {main}( ) ..\index.php:0
2 0.0152 824648 include( 'C:\wamp\www\xibo_interface_c\client_portal.php' ) ..\index.php:605
3 0.0246 854192 include( 'C:\wamp\www\xibo_interface_c\active_media.php' ) ..\client_portal.php:127
4 0.0294 930432 updateContractMedia( ) ..\active_media.php:120
5 0.0410 944712 postPlaylistLibrary( ) ..\database_insert_request.php:414
6 0.0422 945504 callService( ) ..\index.php:564
7 0.0588 1299624 GuzzleHttp\Client->request( ) ..\index.php:357
8 0.0825 1617808 GuzzleHttp\Promise\Promise->wait( ) ..\Client.php:129.
$playListId:
int 3
$mediaArray:
array (size=4)
0 => int 57
1 => int 57
2 => int 57
3 => int 61
Code index.php, Line 564 is the last line. (Same code structure works fine on all other api calls):
$params = array(
'api_method' => 'POST',
'api_path' => 'api/playlist/library/assign/'.$playListId,
'api_data' => [
'form_params' =>[
'media' => $mediaArray
]
]
);
callService($params, true);
The following code is working fine for all other api calls:
indent preformatted text by 4 spaces
function callService($params, $echo = false) {
if(isset($_SESSION['user_id']) && isset($params['api_path'])){
$client = new Client();
if(!empty($params['info'])) {
$request = $client->request($params['api_method'], SERVER_BASE.'/'.$params['api_path'], [
'headers' => [
'Authorization' => 'Bearer '.$_SESSION['client_access_token']
],
'multipart' => [
[
'name' => 'name[]',
'contents' => basename($params['info']['uri'])
],
[
'name' => 'files[]',
'contents' => fopen($params['info']['uri'], 'r')
]
]
]
);
}else{
$request = $client->request($params['api_method'], SERVER_BASE.'/'.$params['api_path'], [
'headers' => [
'Authorization' => 'Bearer '.$_SESSION['client_access_token']
],
$params['api_data']
]
);
}
$full_response = $request;
$GLOBALS['statusCode'] = $request->getStatusCode();
$GLOBALS['responseMessage'] = $request->getStatusCode().' '.$request->getReasonPhrase();
$GLOBALS['return'] = $full_response->getBody();
}
}
I will attempt to explain
Layouts/Regions are one to many - one layout can have many regions, but a region can only be on one layout.
In 1.7 media was assigned to a region, in 1.8 media is assigned to a playlist and a playlist assigned to a region. Regions/Playlists are many to many - but the UI only supports 1 to 1 (one region and one playlist), which mimics the original behaviour.
We hope to have a full implementation of regions/playlists available in 1.8.0-alpha2.
POST /playlist/library/assign/{playlistId}
is used to assign library items to a Playlist - the UI behaviour for this is when you are Editing a Timeline and you select library, pick a number of files and click assign.
Assigning anything to a playlist creates a widget
which is an instance of a module
.
Removing something from a playlist is deleting a widget, editing is editing a widget, etc. Likewise adding some text, ticker, etc is adding a widget.
The API does support adding widgets and its actually quite simple, but I am not sure how to document it at the moment and hence it doesnât appear in swagger.json
.
It is difficult to document because while each operation goes through the same API route, the parameters that are required are different for each type of module.
The routes available are:
POST /playlist/widget/{moduleType}/{playlistId}
PUT /playlist/widget/{widgetId}
DELETE /playlist/widget/{widgetId}
PUT /playlist/widget/transition/{moduleType}/{widgetId}
As you can see, once you have posted a new widget to a playlist you then interact with it using the widgetId
. The return from the post gives you this widgetId
.
Once we have implemented the full set of features for playlists there will be API calls in layout
for assigning, ordering and unassigning playlists.
Regarding your error - 422 means it thinks one of the arguments is invalid - this should be logged CMS side?
Forgive me if I am not understanding this. I see in the swagger file that the response to POST /playlist/library/assign/{playlistId} shows that it can return an array of :
Playlist {
regions {
regionId,
layoutId,
ownerId,
name,
width,
height,
top,
left,
zIndex,
playlists(array),
regionOptions(array),
permissions(array),
displayOrder,
duration
}
}
I see a playlist array shown on the return. Am I just looking at a future use option? Or is this some how tied to what is to become the ability to assign a region/playlist to multiple layouts?
I understand⌠No Worries
Need some sort of categorized widget handling
Overall it sounds like we just need to be patient We love what we are seeing and are grateful for your work.
Yeah I would think so too base on the log saying âPlease provide Media to Assignâ:
12 e22a8ea 2015-11-05 21:44 API POST DEBUG /playlist/library/assign/:id Storage rollback.
11 e22a8ea 2015-11-05 21:44 API POST DEBUG /playlist/library/assign/:id Please provide Media to Assign
10 e22a8ea 2015-11-05 21:44 API POST DEBUG /playlist/library/assign/:id SQL = SELECT playlist.* FROM `playlist` WHERE 1 = 1 AND playlistId = :playlistId . Params = array ( 'playlistId' => 3, ).
9 e22a8ea 2015-11-05 21:44 API POST DEBUG /playlist/library/assign/:id SQL = SELECT `group`.group, `group`.groupId, `group`.isUserSpecific, `group`.isEveryone, `group`.libraryQuota FROM `group` WHERE 1 = 1 AND `group`.groupId IN (SELECT groupId FROM `lkusergroup` WHERE userId = :userId) AND isUserSpecific = :isUserSpecific AND isEveryone = :isEveryone . Params = array ( 'userId' => 1, 'isUserSpecific' => 0, 'isEveryone' => 0, ).
8 e22a8ea 2015-11-05 21:44 API POST DEBUG /playlist/library/assign/:id Loading 1. All Objects = 0
7 e22a8ea 2015-11-05 21:44 API POST DEBUG /playlist/library/assign/:id SQL = SELECT `user`.userId, userName, userTypeId, loggedIn, email, `user`.homePageId, pages.title AS homePage, lastAccessed, newUserWizard, retired, CSPRNG, UserPassword AS password, group.groupId, group.group, IFNULL(group.libraryQuota, 0) AS libraryQuota FROM `user` INNER JOIN lkusergroup ON lkusergroup.userId = user.userId INNER JOIN `group` ON `group`.groupId = lkusergroup.groupId AND isUserSpecific = 1 LEFT OUTER JOIN `pages` ON pages.pageId = `user`.homePageId WHERE 1 = 1 AND user.userId = :userId ORDER BY userName. Params = array ( 'userId' => 1, ).
The media was provided, but the code doesnât seem to know about it. It is suppose to be an array of the media idâs stored in the Xibo Media database correct? (As shown above)
So far I have only been able to trace this back to:
\Lib\Controller\Playlist.php Line 306:
$media = Sanitize::getIntArray('media');
I have not been able to figure out yet how to trace the Media variable from it being sent to the API to where the API picks it up.
From what I can tell we are passing what the swagger doc says and in the format it says.
If we replace line 306 with say this:
$media = array(11);
It works. So I guess for us the problem is either in the format we are passing the data, or somewhere in the sanitization.php the arrayâs format is changed to something it doesnât like.
It doesnât look like âmediaâ has been specified.
It returns you the entire Playlist object - i.e. this is the current structure of the playlist - as it happens it might be necessary to change that as the response will grow and grow as things are added to the playlist.
The regions are the regions that the playlist is linked to - at the moment this will only ever be one (until we complete the functionality).
I would imagine it is the data format - the UI passes it like this:
$.ajax({
type: "post",
url: url,
dataType: "json",
data: {media: media},
success: XiboSubmitResponse
});
I must admit, I canât see anything wrong with your code - according to the Guzzle docs:
form_params: (array) Associative array of form field names to values
where each value is a string or array of strings. Sets the Content-Type
header to application/x-www-form-urlencoded when no Content-Type header
is already present.
You are (in effect) providing an array of strings in this case, which will be passed through intval
in the CMS.
Perhaps you can capture a more general log at the CMS end? Add the below into Playlist.php
, just before it tries to get the media ids
Log::debug(var_export($this->getApp()->request()->params(), true));
Does not look to help much, except to show the array is empty:
13 6ff5ac2 2015-11-10 08:39 API POST DEBUG /playlist/library/assign/:id Storage rollback.
12 6ff5ac2 2015-11-10 08:39 API POST DEBUG /playlist/library/assign/:id Please provide Media to Assign
11 6ff5ac2 2015-11-10 08:39 API POST DEBUG /playlist/library/assign/:id array ( )
10 6ff5ac2 2015-11-10 08:39 API POST DEBUG /playlist/library/assign/:id SET @playlistId=4; SELECT playlist.* FROM `playlist` WHERE 1 = 1 AND playlistId = @playlistId
9 6ff5ac2 2015-11-10 08:39 API POST DEBUG /playlist/library/assign/:id SET @userId=1; SET @isUserSpecific=0; SET @isEveryone=0; SELECT `group`.group, `group`.groupId, `group`.isUserSpecific, `group`.isEveryone, `group`.libraryQuota FROM `group` WHERE 1 = 1 AND `group`.groupId IN (SELECT groupId FROM `lkusergroup` WHERE userId = @userId) AND isUserSpecific = @isUserSpecific AND isEveryone = @isEveryone
8 6ff5ac2 2015-11-10 08:39 API POST DEBUG /playlist/library/assign/:id Loading 1. All Objects = 0
7 6ff5ac2 2015-11-10 08:39 API POST DEBUG /playlist/library/assign/:id SET @userId=1; SELECT `user`.userId, userName, userTypeId, loggedIn, email, `user`.homePageId, pages.title AS homePage, lastAccessed, newUserWizard, retired, CSPRNG, UserPassword AS password, group.groupId, group.group, IFNULL(group.libraryQuota, 0) AS libraryQuota FROM `user` INNER JOIN lkusergroup ON lkusergroup.userId = user.userId INNER JOIN `group` ON `group`.groupId = lkusergroup.groupId AND isUserSpecific = 1 LEFT OUTER JOIN `pages` ON pages.pageId = `user`.homePageId WHERE 1 = 1 AND user.userId = @userId ORDER BY userName
Dan, can you please tell me where the CMS is populating âmediaâ in this line?
Line 306 from \Lib\Controller\Playlist.php
From what I can tell it is just the media from params(âmediaâ).
We found that if we try to pass form_params via multipart we do see the params(âmediaâ) but it is still blank, and the documentation for Guzzle says that method is not supported.
Setting the debug option to true on the guzzle request gave us this:
Warning: curl_setopt_array(): cannot represent a stream of type Output as a STDIO FILE* in C:\wamp\www\xibo_interface_c\3rdParty\oauth2-xibo-cms-master\vendor\guzzlehttp\guzzle\src\Handler\CurlFactory.php on line 57
Not sure what that means yet.
Updated: Searched Google for the error message and found exactly 1 match, and even that did not look promising. I am pulling my hair out on this one. I even tried to update guzzle to the dev version to see if that made a difference. Just not sure what to think here. The form_params are passed, but params() always comes up empty. I think this is a Guzzle problem, but we do not have the needed expertise to document this correctly and submit it to Guzzle.
Iâve been through a worked example at this end and put the code here. This looks like the same as what you are doing, but there must be something your end not quite right (very hard to spot from snips).
I didnât need to make any changes to the CMS, etc⌠it worked as is. Probably not what you wanted to hear!
This is populated from $_POST
by the Slim framework - but I suspect you will find $_POST
is empty.
Dan,
Thank you for taking the time to make the example code. It does look a lot like ours. The only thing that initially sticks out is that you are not using SSL I wonder if the SSL has something to do with our problem. When I get a moment I will try changing to non-ssl and see what happens.
Yeah it isâŚ
Update: So⌠We found that using a 3rd level multidimensional array seems to be a problem when forming a request for Guzzle. (I thought this was fixed a while back in Guzzle , then maybe something else)When we trim it down to a 2nd level multidimensional array, we can then get the form_params to pass at least to the point where the extra logging you suggested is showing the media array being passed as shown here:
27157 c1835af 2015-11-11 21:56 API POST DEBUG /playlist/library/assign/:id array ( 'media' => array ( 0 => '13', ), )
Also changing to non-SSL had no affect on the problem.
After a few checks⌠⌠Ok I am good. Thank you Dan, we now have layouts that we can add media to.
Fantastic, iâm glad the example helped point you in the right direction
All of this debugging is useful for us, as iâm adding and refining the api as we go - so no wasted time (at least from my perspective! )
Now that we can add media to a playlist as a widget, we need to know how to record the widget id we added so that later we can remove it from the playlist.
Looking at the API response, I only see the ability to get all the widgets assigned to a playlist, after adding media to the playlist. How do we go about getting the widget id that is created when we add the media?
We could pull the last âwidgetsâ and assume that is the one, but I am not sure that will always returned the desired result.
You are quite right of course - it is difficult to determine which IDs are added at present. Iâve submitted an issue for it:
DELETE /playlist/widget/{widgetId}
The response returns an empty array.
It does remove the widgetId
Correct - it returns a 204 (success but empty response) - all DELETEâs do the same
Thank you Dan for the information.