Posted Feb 12 by Jason Ibrahim.
Updated Mar 23 by Simon Day.

In this article, we go through the development process of a proof of concept app that pulls and displays assets from media manager using a custom angularjs service that enables us to consume of media manager api's. NOTE: This Article applies to AppWorks 1.x NOT AppWorks 16.x

Last activity Mar 23 by Simon Day.
5176 views. 1 comment.

Introduction - Media Manager CMO Dashboard

We will be putting together a simple app to interface with OpenText Media Manager that will allow us to view assets on the go on our mobile devices using the AppWorks client. This app will use a few excellent frameworks and libraries that will aid us in delivering an app that is slick, functional, and easy to use: the Ionic framework to give us the look and feel of a native app, angularjs to provide us with power and flexibility in communicating with our backend server, and an angularjs media manager service to make api consumption a cinch.

A computer would deserve to be called intelligent if it could deceive a human into believing that it was human.
Alan Turing

Caveats

To fully follow along in this article, you will need some experience with angularjs. Both ionic and the media manager service are written in angularjs, so I recommend taking a look at some tutorials online before starting. Some of the ionic code may be difficult to follow along with if you have not had thorough experience with angularjs. That being said, one should be able to capture the essence of what the code is doing by carefully reading through the description of each block.

If you are unfamiliar with angularJS, please take the time to look at some tutorials online. There are several. Here is one.

Getting Started With Ionic

We will start by generating a template using the Ionic framework. Before we begin, you will need to have nodejs installed on your machine. Verify you do by running the following command:

$ node

If you see the command prompt, we can proceed to the next step. If not, please download and install nodejs from nodejs.org.

Next we will install ionic using npm. Run the following command (it may be slightly different if you are on windows):

$ sudo npm install –g cordova ionic

Once that is finished, we are ready to generate a starter template using ionic. Run the following command:

$ ionic start cmo_dashboard sidemenu

This will generate an app named media_manager using the pre-made ionic template sidemenu. Now you can emulate the app using the iOS emulator (assuming you have xCode installed), or view it in the browser. To emulate using the iOS emulator run the following:

$ cd cmo_dashboard
$ ionic platform add ios
$ ionic build ios
$ ionic emulate ios

If you would simply like to view it in the browser, run the following:

$ ionic serve

This will launch a browser window with the app running inside of it.

At this point you should see a skeleton app generated by ionic with a side menu that contains playlists and a radio among other things. We are going to gut this app and put in some of our own templates and angular code, whilst maintaining the layout and feel of the starter.

MediaManager & AngularJS

The next step is to include an angularjs service written specifically to aid in the consumption opentext api’s via appworks. At the time of this writing, the only api that is supported is media manager. Support for other api’s is being rolled out during the course of the next few months.

Technical details

This angular service uses, as a dependency, an angular module called ngResource to give us the power of using a variable as a resource that we can call REST methods on. This makes querying, updating, saving, or destroying a record straightforward and simple, and reduces the amount of code we need to write to perform basic tasks.

Using ngResource, the angular-appworks service will fill in the details of communicating with our media manager backend. All the developer needs to do is ensure there is a reverse proxy mapping from ‘media_manager’ to the address of their media manager instance.

If you are interested in what the exact proxy mapping looks like in the angular-appworks service, here is an example:

Suppose we have a media manager instance set up to listen to api calls at the following url:

http://some-media-manager.com:8009/otmmapi/

In our gateway, we have a reverse proxy mapping that takes on the following form:

Allowed path patterns:

media_manager/*

Proxy mappings:

media_manager=some-media-manager.com:11090

The angular-appworks service will make calls to our resources found at:

http://some-media-manager.com:8009/otmmapi/v1/(*resource)

Since ngResource is already bundled with Ionic, all we need to do is include the appworks service as a dependency in our app.

Download angular-appworks from here and stick it in lib/opentext/js/angular-appworks.js. The directory structure should look like this:

…
lib/
    ionic
    opentext/
        js/
            angular-appworks.js
…

Next, open up index.html and add the following line:

<script src="lib/opentext/js/angular-appworks.js"></script></code></pre>

Finally, open up controllers.js and include the service as an angularjs dependency:

angular.module('starter.controllers', ['appworks'])

.controller('AppCtrl', function($scope, $ionicModal, $timeout) {
  // Form data for the login modal
  $scope.loginData = {};

…

With that, we are ready to use the $MediaManager service in our angular code. This will make fetching our assets a breeze.

Controllers

Our app is structured in the following way: when the user opens it up, he will be presented with the folders that are present on the media manager instance. When the user enters a folder, he will be presented with the assets contained within that folder. When the user enters an asset, he will be able to view or stream that asset using the url for that asset.

To make this possible, we will have two controllers: a folders controller to handle the display of folders loaded in from media manager, and an asset controller to handle the display of a single asset.

Open up controllers.js in the js folder. Delete all of the existing controllers in this file except for the one named ‘AppCtrl’. Your controllers.js file should look like this:

angular.module('starter.controllers', ['appworks'])

.controller('AppCtrl', function($scope, $ionicModal, $timeout) {
  // Form data for the login modal
  $scope.loginData = {};

  // Create the login modal that we will use later
  $ionicModal.fromTemplateUrl('templates/login.html', {
    scope: $scope
  }).then(function(modal) {
    $scope.modal = modal;
  });

  // Triggered in the login modal to close it
  $scope.closeLogin = function() {
    $scope.modal.hide();
  };

  // Open the login modal
  $scope.login = function() {
    $scope.modal.show();
  };

  // Perform the login action when the user submits the login form
  $scope.doLogin = function() {
    console.log('Doing login', $scope.loginData);

    // Simulate a login delay. Remove this and replace with your login
    // code if using a login system
    $timeout(function() {
      $scope.closeLogin();
    }, 1000);
  };
})

Lets start with our folders controller. We will name it ‘FolderCtrl’. Add the following code to your controllers.js file:

.controller('FoldersCtrl', function ($scope, $stateParams, $MediaManager) {

    if ($stateParams.asset_id) {

        $MediaManager.Folder.get({id: $stateParams.asset_id}, function (folder) {
            $scope.folder = folder.folder_resource.folder;
        });

        $MediaManager.Folder.children.get({id: $stateParams.asset_id}, function (folders) {
            $scope.folders = folders.folder_children.asset_list
        });

    } else {

        $scope.showNavicon = true;

        $MediaManager.Folder.rootfolders(function (folders) {
            $scope.folders = folders.folders_resource.folder_list;
        });
    }
})

Lets go through what this code is doing. First see that we have included $stateParams and $MediaManager as dependencies into our app. The first thing that happens is we use $stateParams to grab the asset id from the path. If there is not asset id in the path, then asset_id is nil and we grab the root folders from the server.

This is where you see a call to $MediaManager.Folder.get. $MediaManager is our service, and Folder is a variable that maps to a folder resource on media manager. If we have an asset id, we make a call to grab the folder with that particular id by passing it to the call to get(). If there is no asset id, we use the Folder resource to make a call to rootfolders(), which will return us the root folders on our media manager instance. We also grab the folder children if we are dealing with an instance of a folder; this is where the call to Folder.children.get() is made.

When this call returns from the server, we assign it to a variable called folders on our $scope. This will give us the power to display and modify the folders in our view using angularjs data binding.

Next, we will create a controller for displaying an asset. As in the previous example, we will leave most of the heavy lifting to our $MediaManager service, and assign a variable on our scope to bind to our view. Paste in the following code to controllers.js:

.controller('AssetCtrl', function ($scope, $stateParams, $MediaManager) {

    $MediaManager.Asset.get({id: $stateParams.asset_id}, function (asset) {
        $scope.asset = asset.asset_resource.asset;
    });

});

In this block, we use the Asset resource on our $MediaManager service to grab the asset with the asset id we grab from the url using $stateParams. We then assign the asset to our scope so we can view and manipulate it in the view.

With that complete, we can move on to the fun part: the view.

Views

We are going to create two view templates for our app. One to display folders and one to display an asset. Each view corresponds directly to one of the controllers we created in the previous step.

Create two new files in the templates folder: folders.html and asset.html. The views are relatively simple thanks to ionic. Here is folders.html:

<ion-view title="{{folder.name || 'Folders'}}">
    <ion-nav-buttons side="left">
        <button menu-toggle="left" class="button button-icon icon ion-navicon" ng-show="showNavicon === true"></button>
    </ion-nav-buttons>
    <ion-content class="has-header">
        <div class="container-fluid">
            <div class="row">
                <div class="col col-sm-4" ng-repeat="folder in folders">
                    <a class="item item-icon-left" ng-href="#/app/folders/{{folder.asset_id}}">
                        <i ng-attr-class="{{ (folder.content_type === 'NONE' || folder.content_type === undefined) && 'icon ion-folder' || 'icon ion-images' }}"></i>
                        {{folder.name}}
                    </a>
                    <a 
                        ng-href="#/app/assets/{{folder.asset_id}}"
                        class="item item-image"
                        ng-show="folder.rendition_content">
                        <img ng-src="{{otagServer + '/media_manager' + folder.rendition_content.thumbnail_content.url}}">
                    </a>
                </div>
            </div>
        </div>

    </ion-content>
</ion-view>

We are using two way data binding to display variables in our template inside of double curly brackets {{}}. Recall that we used the $MediaManager service to fetch the folder from the server and assigned the children attribute to a $scope variable named ‘folders’. In this view template we iterate over each folder in the list using ng-repeat and display the name of the folder as well as an in-app url to access that particular folder, using the asset_id attribute that is a part of each folder instance. We also do a quick check to see if the folder is a folder in the general sense of the word, or an asset. We do this because architecturally speaking, everything in media manager is an asset, even folders. We simply check for a content_type attribute and this is enough (for this example) to tell us whether the asset we have is a folder or some media.

<ion-view title="{{asset.name}}">
    <ion-nav-buttons side="left">
    </ion-nav-buttons>
    <ion-content class="has-header">

        <div class="list-card">
            <div class="item item-avatar">
                <img ng-src="{{otagServer + '/media_manager/' + asset.rendition_content.thumbnail_content.url}}">
                <h2>{{asset.name}}</h2>
            </div>
            <div class="item item-image">
                <a ng-href="{{asset.streaming_url || otagServer + '/media_manager/' + asset.rendition_content.preview_content.url}}">
                    <img ng-src="{{otagServer + '/media_manager/' + asset.rendition_content.thumbnail_content.url}}">
                </a>
            </div>
        </div>
    </ion-content>
</ion-view>

This view is straightforward as well. Again, we are using angular’s two way data binding to display variables we set in the controller here in our view. We display a thumbnail, which is contained inside the asset as an attribute. We also construct a url to stream the media in the browser. We do this by again through the magic that our reverse proxy provides. We point to our otag server, and append ‘/media_manager/’ + the path of the asset, which is contained in the attribute asset.rendition_content.preview_content.url. Now when a user clicks the asset, it will be streamed from the media manager server using the device’s built in html5 capabilities. Note: This will not work unless the reverse proxy is configured correctly.

Routing

There is one last thing we must do in order to make sure our app works correctly, and that is provide the proper mappings to our routes. Open up app.js and edit the config function to make it look like this:

.config(function($stateProvider, $urlRouterProvider) {
  $stateProvider

    .state('app', {
      url: "/app",
      abstract: true,
      templateUrl: "templates/menu.html",
      controller: 'AppCtrl'
    })

    .state('app.search', {
      url: "/search",
      views: {
        'menuContent' :{
          templateUrl: "templates/search.html"
        }
      }
    })

    .state('app.browse', {
      url: "/browse",
      views: {
        'menuContent' :{
          templateUrl: "templates/browse.html"
        }
      }
    })

    .state('app.folders', {
      url: "/folders",
      views: {
        'menuContent' :{
          templateUrl: "templates/folders.html",
          controller: 'FoldersCtrl'
        }
      }
    })

    .state('app.folder', {
      url: "/folders/:asset_id",
      views: {
        'menuContent' :{
          templateUrl: "templates/folders.html",
          controller: 'FoldersCtrl'
        }
      }
    })

    .state('app.asset', {
      url: "/assets/:asset_id",
      views: {
        'menuContent' :{
          templateUrl: "templates/asset.html",
          controller: 'AssetCtrl'
        }
      }
    });
  // if none of the above states are matched, use this as the fallback
  $urlRouterProvider.otherwise('/app/folders');
});

Here we have added three routes: one for mapping requests to /folders to our folders controller and folders.html template, one for mapping requests to /folders/:asset_id to our folders controller and folders.html, providing as asset id to the controller to fetch a particular folder from the server, and finally, one for mapping requests to /assets/:asset_id to AssetCtrl and our asset.html view, passing the id of the asset to display. The last bit is to display /folders as a fallback in case any other route is not found.

With that we are done.

Testing It Out

Now that we have our app, we are ready to package it up and deploy it as an appworks app. Take the contents of the entire project and compress it as a file named mobile.zip. Next, create a file called app.properties and enter in the following:

displayName=CMO Dashboard
description=OpenText Media Manager Dashboard on mobile
releaseNumber=1
minimumClientVersion=1
type=app

Now take mobile.zip, app.properties and an icon and compress that into a file named media_manager_0.0.0.zip. You now have an install package you can deploy on your gateway.

Install the package on your gateway. Make sure you have the reverse proxy service installed and are mapping ‘media_manager’ to your media manager instance as exampled above.

You should be all set. If you have any questions, please drop a line over in the developer forum.

Screenshots




Github

https://github.com/jasonaibrahim/cmodashboard

1 Comment

0

Please note this article applies to AppWorks 1.2.x and NOT AppWorks 16.x


Table of Contents

Your comment

To leave a comment, please sign in.