Welcome to Portal SDK. This document is intended to familiarize you with the HTML, Javascript and Server Script components of the SDK.
- Get Started
- General Overview
- Hot Keys
- HTML Components
- Server Script
- JavaScript
- SQL
- Directives & Filters
- Collaboration
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:
- http://www.w3schools.com/angular/
- https://docs.angularjs.org/api Angular-filter - a collection of useful AngularJS filters
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
- Linq.js - manipulate JSON data using lambda statements (client side only)
- Moment.js and Moment-Timezone.js - date time manipulation
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:
Hot Keys
Portal SDK
Windows Hot Keys | Command | Mac Hot Keys |
---|---|---|
Ctrl + 1 | Show HTML view | Ctrl + 1 |
Ctrl + 2 | Show Javascript view | Ctrl + 2 |
Ctrl + 3 | Show CSS view | Ctrl + 3 |
Ctrl + 4 | Show Server Script view | Ctrl + 4 |
Ctrl + 5 | Show SQL Console view | Ctrl + 5 |
Ctrl + s | Save your changes in the editor to Portal Server | Ctrl + s |
Ctrl + Shift + s | Save your changes and refresh the preview | Ctrl + Shift + s |
Editor
Windows Hot Keys | Command | Mac Hot Keys |
---|---|---|
Ctrl + Space | Autocomplete | Ctrl + Space |
Ctrl + b | Beautify code Ctrl + b | |
F11 | Toggle fullscreen mode | Command + Fn + F11 |
Ctrl + / | Comment/uncomment selection | Ctrl + / |
Ctrl + q | Collapse/expand code section | Ctrl + q |
Ctrl + a | Select all | Ctrl + a |
Ctrl + d | Delete line | Command + d |
Ctrl + z | Undo | Command + z |
Ctrl + Shift + z | Redo | Command + Shift + z |
Ctrl + y | Redo | Command + y |
Ctrl + Home | Go doc start | Command + up |
Ctrl + End | Go doc end | Command + down |
Ctrl + Left | Go word left | Option + Left |
Ctrl + Right | Go word right | Option + Right |
Alt + Left | Go line start | Command + Left |
Alt + Right | Go line end | Command + Right |
Ctrl + Backspace | Delete word before | Option + Delete |
Ctrl + Delete | Delete word after | Option + d |
Ctrl + f | Find | Command + f |
Ctrl + g | Find next | Command + g |
Ctrl + Shift + g | Find previous | Command + Shift + g |
Ctrl + Shift + f | Replace | Command + Option + f |
Ctrl + Shift + r | Replace all | Command + Option + Shift + f |
Ctrl + [ | Indent less | Command + [ |
Ctrl + ] | Indent more | Command + ] |
SQL Console
Windows Hot Keys | Command | Mac Hot Keys |
---|---|---|
Ctrl + Enter | execute inputted script | Ctrl + Enter |
Ctrl + Up / Ctrl + Down | traverse command history | |
Ctrl + Del | delete command from history | Command + Up / Command + Down |
Esc | clear command | Esc |
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. Server Script is a term used by the Portal team to denote JavaScript used for database and server interactions; it should not be confused as a separate programming language.
When you pass arguments to the Server Script, don't put parameters into function signatures. They are available as args.Get("ArgName"):
Code Block | ||||
---|---|---|---|---|
| ||||
function myFunction(myArg){ //myArg is optional
args.Get("myArg");
} |
SQL arguments get injected as well, available as @ArgName
, e.g.:
Code Block | ||||
---|---|---|---|---|
| ||||
"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 | ||||
---|---|---|---|---|
| ||||
function consoleTest(){
console.log('hello');
console.log('hello2');
return { actual : "stuff" };
} |
JavaScript
Code Block | ||||
---|---|---|---|---|
| ||||
$scope.portalHelpers.invokeServerFunction({uniqueNameId:'YOUR_UNQIUE_WIDGET_NAME_ID', functionName:'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 | ||||
---|---|---|---|---|
| ||||
function myFunction(){
var debug = {};
// run some code
debug.status = "ok";
return debug;
} |
Then on the client side in Javascript:
Code Block | ||||
---|---|---|---|---|
| ||||
$scope.portalHelpers.invokeServerFunction({uniqueNameId:'YOUR_UNQIUE_WIDGET_NAME_ID', functionName:'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.
Why Angular
Two way data binding
Two way data binding in Angular refers to the co-existence and relationship between the model and view. In short, any changes you make to variables in JavaScript (model) will be reflected in the HTML (view) files instantly, and vice-versa. $scope, which you will see soon, allows for variables to be accessed in HTML.
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 | ||||
---|---|---|---|---|
| ||||
.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 | ||||
---|---|---|---|---|
| ||||
$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 | ||||
---|---|---|---|---|
| ||||
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 | ||||
---|---|---|---|---|
| ||||
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 | ||||
---|---|---|---|---|
| ||||
console.log(homeSettings.user); |
Note: The browser console can be accessed by right clicking anywhere on the page containing your widget code, and clicking on "Inspect". The console should be one of the tabs on the top of the corresponding window.
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.
- Open your widget project in your browser
- Select Tools>>Private Data from the top menu. The Private Data dialog box appears.
- 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:
- 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 | ||||
---|---|---|---|---|
| ||||
function privDataRead(){
return privateDataService.Get('API Key');
} |
JavaScript
Code Block | ||||
---|---|---|---|---|
| ||||
$scope.portalHelpers.invokeServerFunction({uniqueNameId:'YOUR_UNQIUE_WIDGET_NAME_ID', functionName: '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 | ||||
---|---|---|---|---|
| ||||
function clearPrivData(){
return privateDataService.Clear('API Key');
} |
JavaScript
Code Block | ||||
---|---|---|---|---|
| ||||
$scope.portalHelpers.invokeServerFunction({uniqueNameId:'YOUR_UNQIUE_WIDGET_NAME_ID', functionName: '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 | ||||
---|---|---|---|---|
| ||||
function privDataWrite(){
return privateDataService.Put('API Key', 'aDifferentValue');
} |
JavaScript
Code Block | ||||
---|---|---|---|---|
| ||||
$scope.portalHelpers.invokeServerFunction({uniqueNameId:'YOUR_UNQIUE_WIDGET_NAME_ID', functionName : '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 | ||||
---|---|---|---|---|
| ||||
$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 | ||||
---|---|---|---|---|
| ||||
$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 | ||||
---|---|---|---|---|
| ||||
.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 | ||||
---|---|---|---|---|
| ||||
.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 | ||||
---|---|---|---|---|
| ||||
<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 | ||||
---|---|---|---|---|
| ||||
$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 | ||||
---|---|---|---|---|
| ||||
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 | ||||
---|---|---|---|---|
| ||||
<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:
- Create a repository and a team under the "UWaterloo" organization on GitHub
- Upload files from the Portal Server to the master branch (make sure you saved all your changes)
- Create a branch for each team member from the master branch
- 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:
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.git checkout
uwid-branch - switch to your branchgit merge master
- attempt to merge your branch to mastergit 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:git commit -am "resolving conflict"
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.