Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

Welcome to Portal SDK. This document is intended to familiarize you with the HTML, Javascript and Server Script components of the SDK.

Other Documents

Get Started

For more resources, visit these sites:

To build web UI components, Portal is using Bootstrap:

To build app UI components, Portal is using Ionic V1:

A big part of Portal SDK uses AngularJS (version 1.x) to organize javascript and to make elements respond to user actions:

The editor that you see within the SDK is Code Mirror. If you would like to use a plugin for it that is not there, let us know. How to use the editor’s search/replace features: http://codemirror.net/demo/search.html

Available useful libraries

General Overview

Widget development in Portal is done through a web interface system called Portal SDK. The system allows you to create widget projects and use an online code editor to design and run your widget within portal.

In order to use Portal SDK you will first need to have access. If you do not have the "Develop" option in your main menu bar in Portal, it means you have not been grated access to the SDK.

Once you press this menu item, you will be taken to the SDK Home page that will contain the list of your projects and a button to create new projects.

To create a project, press the large "+" button and fill in the form that appears (shown above).

Note: you will not be able to change these fields once your project is created.

Project Title: the title of the project as it will appear in the list of projects on the SDK home page. Project Unique Identifier: a unique alphanumeric identifier of your project that will be used to prefix all your angular modules, css classes and other other instances where there is identifier ambiguity. Project Description: description of the project as it will appear in the SDK projects list

Use the Put on GitHub option to place your project to GitHub if you would like to collaborate with other individuals. After you tick the checkbox you will be given an option to add team membmers who will have access to the project through the Portal SDK (you can adjust this list later).

Once you have created a project, you will be presented with the editor screen where you can begin development:

Image Added

Hot Keys

Portal SDK

Windows Hot KeysCommandMac Hot Keys
Ctrl + 1Show HTML viewCtrl + 1
Ctrl + 2Show Javascript viewCtrl + 2
Ctrl + 3Show CSS viewCtrl + 3
Ctrl + 4Show Server Script viewCtrl + 4
Ctrl + 5Show SQL Console viewCtrl + 5
Ctrl + sSave your changes in the editor to Portal ServerCtrl + s
Ctrl + Shift + sSave your changes and refresh the previewCtrl + Shift + s

Editor

Windows Hot KeysCommandMac Hot Keys
Ctrl + SpaceAutocompleteCtrl + Space
Ctrl + bBeautify code Ctrl + b
F11Toggle fullscreen modeCommand + Fn + F11
Ctrl + /Comment/uncomment selectionCtrl + /
Ctrl + qCollapse/expand code sectionCtrl + q
Ctrl + aSelect allCtrl + a
Ctrl + dDelete lineCommand + d
Ctrl + zUndoCommand + z
Ctrl + Shift + zRedoCommand + Shift + z
Ctrl + yRedoCommand + y
Ctrl + HomeGo doc startCommand + up
Ctrl + EndGo doc endCommand + down
Ctrl + LeftGo word leftOption + Left
Ctrl + RightGo word rightOption + Right
Alt + LeftGo line startCommand + Left
Alt + RightGo line endCommand + Right
Ctrl + BackspaceDelete word beforeOption + Delete
Ctrl + DeleteDelete word afterOption + d
Ctrl + fFindCommand + f
Ctrl + gFind nextCommand + g
Ctrl + Shift + gFind previousCommand + Shift + g
Ctrl + Shift + fReplaceCommand + Option + f
Ctrl + Shift + rReplace allCommand + Option + Shift + f
Ctrl + [Indent lessCommand + [
Ctrl + ]Indent moreCommand + ]

SQL Console

Windows Hot KeysCommandMac Hot Keys
Ctrl + Enterexecute inputted scriptCtrl + Enter
Ctrl + Up / Ctrl + Downtraverse command history
Ctrl + Deldelete command from historyCommand + Up / Command + Down
Escclear commandEsc

HTML Components

Use the HTML component to create the interface of your widget.

Note: Refer to Portal HTML Directives for a list of available predefined components. To ensure that your widget fits well into Portal, it is strongly recommended (but not required) that you use predefined components for common tasks. Feel free to adjust the components to your needs using the CSS tab of the SDK.

Server Script

Note: Refer to the Portal SDK API Reference for an API list of Portal server script tab.

Passing Arguments from Javascript to Server Script

Server script has same syntax as normal JS, but with few modifications and additions. When you pass arguments to the Server Script, don't put parameters into function signatures. They are available as args.Get("ArgName"):


Code Block
languagejs
linenumberstrue
function myFunction(myArg){  //myArg is optional
    args.Get("myArg");
}


SQL arguments get injected as well, available as @ArgName, e.g.:



Code Block
languagesql
linenumberstrue
"SELECT * FROM tableName WHERE id=@id"


Access Student Information

Some useful information about the current user is injected for you as reserved arguments. You cannot use args to access these arguments. Use the @ symbol in SQL or the Portal object.

  • currentUser - Currently logged in UW user id, e.g. "j23doe"
  • uwId - Currently logged in student number, e.g. "20330000"

Debugging

Server Script Logging

You can use console.log() in your Server Script and then use $scope.portalHelpers.invokeServerFunction() in your JavaScript to extract the log information to your console.

Example

Server Script



Code Block
languagejs
linenumberstrue
function consoleTest(){
    console.log('hello');
    console.log('hello2');
    return { actual : "stuff" };
}


JavaScript



Code Block
languagejs
linenumberstrue
$scope.portalHelpers.invokeServerFunction('consoleTest').then(function (result) {
    console.log('console test', result);
});


This example writes the following information to your browser console.

  hello
hello2
console test Object {actual: "stuff"}

Return JSON String

One way to debug Server Script on the server is to construct an object or a string that contains program output and then return it so it can be examined on the client. If your server script returns an object, the Portal SDK automatically converts it to a JSON string. For example: In Server Script:

Code Block
languagejs
linenumberstrue
function myFunction(){
    var debug = {};
    // run some code
    debug.status = "ok";
    return debug;
}

Then on the client side in Javascript:

Code Block
languagejs
linenumberstrue
 $scope.portalHelpers.invokeServerFunction('myFunction').then(function(debug){
    console.log(debug);
});

You can see the output in the JS console of your browser. Since Server Script is basically Javascript, it is also suggested to test parts of code that don’t require server components on the client either using browser's developer console or through the "Javascript" window of Portal SDK before attempting to run the code on the server.

Additionally, if the script encounters an exception, your server script function would return a JSON block that contains an "Error" field with the description of the error.

JavaScript

Note: Refer to the Portal SDK API Reference for an API list of Portal JavaScript tab.

Code Structure

When the project is first created, your javascript will be preset with necessary code required to run the example. You can notice several major sections: controller, factory, directive and filter. First two sections, controller and factory are unique per project. The other two, directive and filter can exist in multiples.

Controller and Service

The purpose of the controller is to contain all code required for the user to interact with the widget such as click actions and watch expressions. The idea here is to place only the code that is specific to the widget view into the controller, whilst placing any reusable logic, such as data manipulation into the service. This allows to create multiple controllers that use the same service. While currently unavailable in SDK (but coming soon!), this will allow you to create nowlets (small components that appear on your home screen and show a small bit of your widget) as well as to create "widget APIs" where other widgets could load your service/directives and use them in their own widget. The diagram below shows how widgets share their code with nowlets.


You can notice that controller imports your service:

Code Block
languagejs
linenumberstrue
.controller('testCtrl', ['$scope', '$http', '$q', 'testFactory', function($scope, $http, $q, testFactory) {}

To read more about dependency injection, see Angular Dependendency Injection documentation. All components should be prefixed with the unique name identifier that you have selected during project setup (in this case ‘test’).

Following this declaration, you can import any functionality and variables from the service into the controller scope:

Code Block
languagejs
linenumberstrue
$scope.loading = testFactory.loading;

While storing functions inside a service helps with reusability, storing data inside the service makes it persistent between views, so if a user opens your widget, navigates to another page and then comes back, the service will have all necessary data to render the view immediately.

Following this declaration, you can import any functionality and variables from the service into the controller scope:

Code Block
languagejs
linenumberstrue
testFactory.init($scope);

While this function is going be invoked every time your controller initializes, you will notice that within it there is logic to prevent it from running more than once. Place any code that needs to run the first time user opens your widget here (for example fetching data).

Adding Multiple JavaScript Files

On the JavaScript tab, you can create multiple JavaScript files in the Portal SDK to manage your code.

Click the Add button to add a new JavaScript file. Click the Delete button to remove the current JavaScript file. Use the dropdown menu to select a JavaScript file.

After creating new JavaScript files, you need to add an imports array to your javascript.js to import the new files.

Code Block
languagejs
linenumberstrue
var imports = ["example1.js", "example2.js", "example3.js"];

Access Student Information in JavaScript

The Portal SDK provides a homeSettings.user object that contains information of the current user. For example, you can add the following code to your JavaScript to list all available information in your browser console.

Code Block
languagejs
linenumberstrue
console.log("homeSettings.user")

Managing Private Data

The Portal SDK uses GitHub API integration to manage code. Do not store any private data in your source code, such as API keys or passwords because your source code will be uploaded to GitHub and become publicly available. You can store your private information in the database.

Storing Private Data Manually

Complete the following steps to add new private data to the database.

  1. Open your widget project in your browser
  2. Select Tools>>Private Data from the top menu. The Private Data dialog box appears.
  3. In the dialog box, enter the name and the value of your private data to the Key and Value fields. The maximum length of Key is 200 characters. The maximum length of Value is 500 characters. For example:
  4. Click the Save button to save the private data.

Accessing Private Data

After adding private data to the database, you can access the data in your source code with privateDataService.Get() server script function.

Example

Server Script

Code Block
languagejs
linenumberstrue
function privDataRead(){
    return privateDataService.Get('API Key');
}

JavaScript

Code Block
languagejs
linenumberstrue
$scope.portalHelpers.invokeServerFunction('privDataRead').then(function(result) {
    console.log('priv read result', result);
});

This example returns the value of API Key to the console.

Clearing Private Data

You can remove a private data entry from the database using the privateDataService.Clear() server script function.

Example

Server Script

Code Block
languagejs
linenumberstrue
function clearPrivData(){
    return privateDataService.Clear('API Key');
}

JavaScript

Code Block
languagejs
linenumberstrue
$scope.portalHelpers.invokeServerFunction('clearPrivData').then(function (result) {
    console.log('priv clear result', result);
});

Storing Private Data Programmatically

Warning: This approach may leave hardcoded private data value in your source code. Ensure you know what you are doing before using this approach. You assign a new value to an existing private data entry using the privateDataService.Put() server script function.

Code Block
languagejs
linenumberstrue
function privDataWrite(){
    return privateDataService.Put('API Key', 'aDifferentValue');
}

JavaScript

Code Block
languagejs
linenumberstrue
$scope.portalHelpers.invokeServerFunction('privDataWrite').then(function (result) {
    console.log('priv write result', result);
});

This example update the value of API Key to "aDifferentValue". Please note that the new private data value is in the source code and could get published to GitHub when you upload your code.

Portal SDK adds your private to the 'privateData' table of the Database. You may access the table directly using the db object.

Web Scraping

Tips: During the Hackathon, consider using mock-up data to build your prototype widget and UI. You can replace the mock-up data with real data later when you have time. You can use external websites as data sources by scraping their pages in the Portal SDK. Use the $http service and jQuery to achieve this.

Note: Ensure the domain is whitelisted by Portal staff before using this function. To see available domains press External Sources on the top menu of the SDK.

1 . Create a request to the web page using endpoints on Portal: * POST request: use the /Develop/PostProxy endpoint.

Code Block
languagejs
linenumberstrue
$http.post("/Develop/PostProxy", {
    values : postParams,
    url : "https://yourURL.com"
}).success(function(response) {
    // Code to run on success
});
  • GET request: use the /Develop/GetProxy endpoint.
Code Block
languagejs
linenumberstrue
$http.get( '/Develop/GetProxy?url=yourUrl').success(function(response){
    // Code to run on success
});

2 . Create an HTML document to store the response.

Code Block
languagejs
linenumberstrue
.success(function(response){
    var doc = document.implementation.createHTMLDocument("");
    doc.documentElement.innerHTML = response;
});

3 . Pass the document to jQuery for parsing. You can use jQuery selectors to parse the document.

Code Block
languagejs
linenumberstrue
.success(function(response){
    var doc = document.implementation.createHTMLDocument("");
    doc.documentElement.innerHTML = response;
    jq = $(doc);
    var resultObjects = jq.find('your_jQuery_selector');
});

You can then bind the parsed result to your scope and display the value on your view.

Widget Menu

By default, widget menu will contain only one item: Add/Remove from favourites.

Note: You will see this menu option only when you’re in Test mode or in production, not in widget preview


If you would like to customize the options that appear in this menu, first create a template that will be placed into the menu. Here, you can add any additional syntax needed to control your menu:

Code Block
languagejs
linenumberstrue
<script type="text/ng-template" id="widgetMenu.html">
    <li ng-if="loggedIn">
        <a ng-click="logout()">
            Logout
        </a>
    </li>
</script>

Finally, at the top of your javascript, specify that you would like to use your template as part of the menu:

Code Block
languagejs
linenumberstrue
$scope.portalHelpers.config = {"widgetMenu" : "widgetMenu.html"};

This will result in an updated menu:

SQL

  • Portal is using Microsoft SQL Server and to run SQL statements you must use a type of SQL code called Transact SQL (TSQL).
  • The SQL Server has been configured to provide a restricted set of operations that are allowed to be performed (basic create/read/update/delete). If there are any extra features that you would like to see available, let us know. ## Getting id of the row being inserted

To get an auto-incremented id of the row being inserted, use the following syntax when inserting your row:

Code Block
languagesql
linenumberstrue
INSERT INTO yourTable (columnName1, columnName2) OUTPUT INSERTED.ID values ('value1', 'value2')

Directives & Filters

Dates and Times - "momentDate" Filter

To display dates Portal uses momentJs. We created a filter that wraps that functionality in order to allow users’ time settings preference to take effect (24h format vs 12). Therefore it is recommended to use this filter when working with dates in Portal SDK. The syntax is as following:

In template:

Code Block
languagexml
linenumberstrue
<div>{{object.yourDate | momentDate : ‘MMMM DD YYYY, h:mm’}}</div>

The filter will automatically modify your format string to be in either am/pm format or 24 hour format depending on user preferences.

Collaboration

Portal uses GitHub API integration to manage code contributed by various team members. To enable GitHub integration, the user who created the project must select "Collaboration" from the SDK Editor window and pick "Put on GitHub...". In the modal that appears enter UW User Ids of users who may have access to the project through the SDK and press "Create Repository". Portal will perform these actions:

  1. Create a repository and a team under the "UWaterloo" organization on GitHub
  2. Upload files from the Portal Server to the master branch (make sure you saved all your changes)
  3. Create a branch for each team member from the master branch
  4. Give Portal access to the project to users specified in the dialog Once the repository is created, all users who access the project will gain the ability to contribute to the project using simplified flow.

Important: Do not store any private data in your source code, such as API keys. Since the code will be uploaded to GitHub it will become publicly available. Store any private data in the database.

Simplified Flow

Simplified Flow implies an abstraction from normal Feature Branch Git flow built with future complexity in mind. Simplified flow consists of two simple actions, available under the Collaboration menu: Get Latest and Check In.

  • Get Latest: this action synchronizes users copy of the project to include any outstanding changes that were made to the master branch. To do this, Portal first saves your project to the Portal Server and then pushes your changes to your branch on GitHub (step 1). Then, Portal merges master branch to your branch, resulting in your branch picking up all the new changes from the master branch, while preserving your changes. This new merged branch then gets pulled back into Portal and to the editor.
  • Check In: use this action if you would like to share your changes with your team members. This action will save any of your outstanding edits to the Portal server, which will then push files from your workspace to your branch. Finally, your branch gets merged into the master branch, so that when another user uses the "Get Latest" action in their workspace, they will receive your changes.

Fine Controls

In addition to the simplified flow, you can take more control over how your code gets updated using fine controls. The name of the action and the direction of code flow are shown in diagram below. Note that "Save" action no longer runs automatically when you push to your branch as it does for "Get Latest" and "Check In" actions from the Simplified Flow.

Dealing With Conflicts

When multiple team members performed conflicting changes, users may begin encountering errors when trying to run "Get Latest" and "Check In" actions. If you see these errors, it means that your branch contains changes that are incompatible with the code present on the master branch.

To avoid conflicts, coordinate tasks with your team so that different members work on different aspects of the project. This way users will not edit the same parts of the project and GitHub will merge all changes seamlessly. Have one person create the project and set up areas of work before other uses begin to contribute to the project. It is best that users edit different files entirely or teals edit unrelated areas of files.

Use Desktop Git Tools

If you have git installed on your computer, you can perform merging using the command line tool:

  1. git clone https://… - clone repository to your computer, the url of the repository can be found on the GitHub page, on the right hand side.
  2. git checkout uwid-branch - switch to your branch
  3. git merge master - attempt to merge your branch to master
  4. git status - to see which files are causing issues If you open the file right now, you will see additional syntax that git added to indicate where the conflicts are occurring. Edit the file until there no lines starting with "<<<<<<<", ">>>>>>>" or "=======". Once finished:

  5. git commit -am "resolving conflict"

  6. git push You can then use "Pull From My Branch" menu action in Portal SDK to get the branch with resolved conflict into Portal or merge your branch into master.

Resetting Branch

If you do not use any of the desktop git tools and have a conflict that you cannot eliminate through normal editing procedures, you can reset your branch using the button from the "Collaborate" menu. This will remove your branch on GitHub and recreate it, making it identical to the master branch. To preserve your changes, you will have to re-implement them manually after reset. To ease that process, follow steps below.

Note: resetting your branch will erase any outstanding changes that you have against master branch. To preserve your changes, you will have to re-implement them manually after reset. To ease that process, follow steps below.

Before resetting your branch, it would be wise to see what changed have you made to your branch which make it incompatible with master so that you can re-implement your changes once the branch is reset. For this, go to github repository of the project and select your branch from the dropdown:

Press the Compare button to see the changes between your branch and master:


Keep the compare page open as a reference to what changes you will have to re-implement once you have reset your branch:

Once you branch is reset and is in sync with master, you can re-implement your changes into the code in an incremental fashion by making small changes and checking in often. Use the compare page you have open as reference to copy/paste any changes.

Other Documents